Ejemplo n.º 1
0
class GameVoting(dict):
    def __init__(self):
        super().__init__()

        self._votes_received = 0
        self._max_players = -1
        self._timeout_delay = None
        self._popup = None
        self.result = None

    def select_callback(self, popup, index, option):
        self._votes_received += 1

        if option.value != "WONT_PLAY":
            self[option.value] = self.get(option.value, 0) + 1

            player = player_manager[index]
            broadcast(strings_module['player_voted'].tokenize(
                player=player.name, choice=option.value.caption))

        if self._votes_received >= self._max_players:
            self.finish()

    def start(self, popup, targets, timeout):
        self._popup = popup
        self._max_players = len(targets)
        self._timeout_delay = Delay(timeout, self.finish)

        popup.send(*[player.index for player in targets])

    def finish(self):
        if self._popup is None:
            return

        if self._timeout_delay is not None:
            if self._timeout_delay.running:
                self._timeout_delay.cancel()

            self._timeout_delay = None

        self._popup.close()
        self._popup = None

        if not self:
            broadcast(strings_module['result no_game'])
            InternalEvent.fire('jail_games_voting_result', result=None)
            return

        top_games = sorted(
            self.keys(), key=lambda key: self[key], reverse=True)

        top_score = self[top_games[0]]
        top_games = list(filter(lambda key: self[key] == top_score, top_games))
        winner = choice(top_games)

        self.result = winner
        broadcast(strings_module['result chosen_game'], caption=winner.caption)
        InternalEvent.fire('jail_games_voting_result', result=winner)

        self.clear()
Ejemplo n.º 2
0
class _EffectHandler:
    """Natural way for using effects directly through a player instance.

    Instead of:

        PlayerClass.my_effect.enable_for(player_instance)

    You simply write:

        player_instance.my_effect()

    This will return a handler instance with a cancel method to cancel
    the effect:

        effect = player_instance.my_effect()
        effect.cancel()

    Handler also allows a duration to be applied on an effect via
    an additional duration parameter. Effects with a duration can
    also be cancelled manually via the cancel method:

        freeze = player.freeze(duration=5)  # Keywording is optional
        freeze.cancel()  # Cancel manually before 5 seconds has passed
    """

    def __init__(self, effect, player):
        """Initialize a handler which links an effect and a player."""
        self.effect = effect
        self.player = player
        self._delay = None

    def __call__(self, duration=None):
        """Enable the effect for the player.

        If a duration is passed, the effect will get automatically
        cancelled after the duration has ended.
        """
        self.effect.enable_for(self.player)
        if duration is not None:
            self._delay = Delay(duration, self.effect.disable_for, self.player)
        return self

    def cancel(self):
        """Cancel the enabled effect.

        Also cancels the delay if a duration was passed when enabled.
        """
        if self._delay is not None:
            self._delay.cancel()
            self._delay = None
        self.effect.disable_for(self.player)

    def is_enabled(self):
        """Check if the effect is enabled for the player."""
        return self.effect.is_enabled_for(self.player)
Ejemplo n.º 3
0
class _PlayerEffect(object):
    """Class for player effects like freeze and burn."""

    def __init__(self, descriptor_obj, player):
        """Initialize a new effect for a player.

        This doesn't enable the effect yet, it simply prepares it.
        The effect can be enabled via the _enable method,
        or via parenthesis which invokes the __call__.
        """
        self._descriptor_obj = descriptor_obj
        self._player = player
        self._delay = None

    def _enable(self, duration=None, *args, **kwargs):
        """Enable the effect.

        Add the new effect to a player's effects and call
        the descriptor object's on function with the provided
        arguments and keyword arguments.

        If duration is a positive integer, adds a delay which
        automatically disables the effect after the duration.
        """
        if isinstance(duration, int) and duration > 0:
            self._delay = Delay(duration, self._disable)
        self._player._effects[self._descriptor_obj].append(self)
        self._descriptor_obj._on_f(self._player, *args, **kwargs)

    def __call__(self, duration=None, *args, **kwargs):
        """Override () to call the _enable method."""
        self._enable(duration, *args, **kwargs)
        return self

    def _disable(self, *args, **kwargs):
        """Disable the effect.

        Remove the effect from a player's effects and if there are
        no more effects of this type, calls the descriptor object's
        off function with the provided arguments.
        """
        self._player._effects[self._descriptor_obj].remove(self)
        if not self._player._effects[self._descriptor_obj]:
            self._descriptor_obj._off_f(self._player, *args, **kwargs)

    def cancel(self, *args, **kwargs):
        """Cancel the tick_delay and disable the effect."""
        if self._delay is not None:
            self._delay.cancel()
            self._delay = None
        self._disable(*args, **kwargs)
Ejemplo n.º 4
0
class _MultiLevelPlayer(Player):
    """Class used to give/remove multi-level for a player."""

    spark_entity = None
    delay = None

    def __init__(self, index):
        """Give the player multi-level."""
        super(_MultiLevelPlayer, self).__init__(index)
        self.sound = sound_manager.emit_sound('multi_level', index)
        self.start_gravity = self.gravity
        self.start_speed = self.speed
        self.gravity = gravity.get_int() / 100
        self.speed = speed.get_int() / 100
        self.give_spark_entity()
        duration = 10 if self.sound is None else self.sound.duration
        self.delay = Delay(
            delay=duration,
            callback=multi_level_manager.__delitem__,
            args=(self.userid,),
        )

    def give_spark_entity(self):
        """Give the player an env_spark effect."""
        entity = self.spark_entity = Entity.create('env_spark')
        entity.spawn_flags = 896
        entity.angles = QAngle(-90, 0, 0)
        entity.magnitude = 8
        entity.trail_length = 3
        entity.set_parent(self, -1)
        entity.origin = self.origin
        entity.start_spark()

    def remove_multi_level(self):
        """Remove multi-level from the player."""
        if self.delay is not None and self.delay.running:
            self.delay.cancel()
        self.delay = None
        self.gravity = self.start_gravity
        self.speed = self.start_speed
        if self.sound is not None:
            self.sound.stop(self.index)
        self.remove_spark_entity()

    def remove_spark_entity(self):
        """Remove the env_spark from the player."""
        self.spark_entity.stop_spark()
        self.spark_entity.remove()
Ejemplo n.º 5
0
    def protect(self, seconds):
        if self._counter is not None:
            raise ValueError(
                "Player {} already protected".format(self.player.userid)
            )

        self._counter = self.p_player.new_counter(
            health=0, display=strings_module['health falldmg_protection'])

        def sub_hook(counter, info):
            self.p_player.delete_counter(counter)
            self.p_player.unset_protected()

            self._counter = None

            Shake(
                FALL_PROT_SHAKE_MAGNITUDE, FALL_PROT_SHAKE_TIME
            ).send(self.player.index)

            self._delay.cancel()

            return False

        self._counter.hook_hurt = get_hook('W', next_hook=sub_hook)

        def delay_callback():
            self.p_player.delete_counter(self._counter)
            self.p_player.unset_protected()

            self._counter = None

        self._delay = Delay(seconds, delay_callback)
        self.p_player.set_protected()
Ejemplo n.º 6
0
    def _on_player_ultimate(self, player, **kwargs):
        if self.level == 0:
            return
            
        _cooldown = self.cooldowns['ultimate']
        if _cooldown <= 0:
            self._godmode = True
            player.stuck = True
            Delay(self.duration, self.__setattr__, args=('_godmode', False))
            Delay(self.duration, setattr, args=(player, 'stuck', False))

            self.effect.create(life_time=self.duration + 0.5, origin=player.origin)

            send_wcs_saytext_by_index(self._msg_a.format(time=self.duration), player.index)
            Delay(self.duration - 0.5, send_wcs_saytext_by_index, args=(self._msg_b, player.index))

            self.cooldowns['ultimate'] = (30 - self.level)
        else:
            send_wcs_saytext_by_index(self._msg_c.format(time=_cooldown), player.index)
Ejemplo n.º 7
0
    def request_input(self, callback, return_menu=None):
        assert self.data.get('_internal_input_callback') is None

        self.data['_internal_input_callback'] = callback
        self.data['_internal_input_menu'] = return_menu
        self.data['_internal_input_repeat'] = Repeat(self._request_update)
        self.data['_internal_input_repeat'].start(0.25, 39)
        self.data['_internal_input_delay'] = Delay(10, self._request_end)

        return input_menu
Ejemplo n.º 8
0
    def __call__(self, duration=None):
        """Enable the effect for the player.

        If a duration is passed, the effect will get automatically
        cancelled after the duration has ended.
        """
        self.effect.enable_for(self.player)
        if duration is not None:
            self._delay = Delay(duration, self.effect.disable_for, self.player)
        return self
Ejemplo n.º 9
0
    def take_delayed_damage(self, damage, attacker, skip_hooks=True):
        # TODO: This method should not have been called if the victim is already dead
        assert not self.player.dead

        attacker = userid_from_index(attacker)

        delay = Delay(0, self._take_delayed_damage, args=(damage, attacker, skip_hooks))
        delay.args += (delay, )

        _delays[self.userid].add(delay)
Ejemplo n.º 10
0
    def shift_property(self, prop_name, shift, duration=None):
        """Shifts player's property's value.

        If duration is a positive integer, automatically cancel
        the shift after the duration has ended.
        """
        old_value = getattr(self, prop_name)
        setattr(self, prop_name, old_value + shift)
        if isinstance(duration, int) and duration > 0:
            return Delay(duration, self.shift_property, prop_name, -shift)
Ejemplo n.º 11
0
    def stage_winreward_entry(self):
        winner, loser = self._results['winner'], self._results['loser']

        loser.speed = config_manager['loser_speed']

        def timeout_callback():
            self.set_stage_group('winreward-timed-out')

        self._delays.append(Delay(config_manager['duration'],
                                  timeout_callback))
Ejemplo n.º 12
0
class _MultiLevelPlayer(Player):
    """"""

    spark_entity = None
    delay = None

    def __init__(self, index):
        super(_MultiLevelPlayer, self).__init__(index)
        self.sound = sound_manager.emit_sound('multi_level', index)
        self.start_gravity = self.gravity
        self.start_speed = self.speed
        self.gravity = gravity.get_int() / 100
        self.speed = speed.get_int() / 100
        self.give_spark_entity()
        self.delay = Delay(
            delay=self.sound.duration,
            callback=multi_level_manager.__delitem__,
            args=(self.userid, ),
        )

    def give_spark_entity(self):
        entity = self.spark_entity = Entity.create('env_spark')
        entity.spawn_flags = 896
        entity.angles = Vector(-90, 0, 0)
        entity.magnitude = 8
        entity.trail_length = 3
        entity.set_parent(self, -1)
        entity.origin = self.origin
        entity.start_spark()

    def remove_multi_level(self):
        if self.delay is not None and self.delay.running:
            self.delay.cancel()
        self.delay = None
        self.gravity = self.start_gravity
        self.speed = self.start_speed
        self.sound.stop(self.index)
        self.remove_spark_entity()

    def remove_spark_entity(self):
        self.spark_entity.stop_spark()
        self.spark_entity.remove()
Ejemplo n.º 13
0
def take_damage_hook(stack_data):
    take_damage_info = make_object(TakeDamageInfo, stack_data[1])
    victim = make_object(Entity, stack_data[0])
    if victim.index in entity_health:
        damage = take_damage_info.damage
        if entity_health[victim.index] <= 0:
            Delay(0.1, victim.remove)
        else:
            entity_health[victim.index] -= damage
    else:
        return
Ejemplo n.º 14
0
def wcs_setfx_jetpack_command(command_info, player:convert_userid_to_player, operator:valid_operators('='), value:int, time:float=0):
    if player is None:
        return

    if value:
        player.move_type = MoveType.FLY
    else:
        player.move_type = MoveType.WALK

    if time > 0:
        Delay(time, validate_userid_after_delay, (wcs_setfx_jetpack_command, player.userid, '=', not value))
Ejemplo n.º 15
0
 def select(menu, index, choice):
     """Change map"""
     Delay(3, engine_server.change_level, (
         choice.value,
         None,
     ))
     for player in PlayerIter('human'):
         SayText2(messages['Change Map'].get_string(player.language[:2],
                                                    name=choice.value,
                                                    duration=3)).send()
     return menu
Ejemplo n.º 16
0
    def _on_weapon_fire_competitive(self, game_event):
        player = player_manager.get_by_userid(game_event['userid'])

        if player not in self._players:
            return

        opponent = self.prisoner if player == self.guard else self.guard

        self._shots_fired += 1
        self._delays.append(
            Delay(BULLET_TRAVEL_TIME, self._competitive_bullet_hit, opponent))
Ejemplo n.º 17
0
def play_flawless_effects(players):
    def callback():
        if config_manager['flawless_sound'] is not None:
            config_manager['flawless_sound'].play(
                *[player_.index for player_ in players])

        if config_manager['flawless_material'] != "":
            for player in players:
                show_overlay(player, config_manager['flawless_material'], 3)

    _flawless_effects_delays.append(Delay(1.5, callback))
Ejemplo n.º 18
0
def do_poison_smoke(position, userid, range, damage, delay, duration):
    attacker = Player.from_userid(int(userid))
    duration = duration - delay
    for player in PlayerIter('all'):
        if player.origin.get_distance(position) <= range:
            player.take_damage(damage,
                               attacker_index=attacker.index,
                               weapon_index=None)
    if duration > 0:
        Delay(delay, do_poison_smoke,
              (position, userid, range, damage, delay, duration))
Ejemplo n.º 19
0
    def _nade_thrown(self, weapon_classname):
        if self._ammo_refill_delay is None:
            return

        if weapon_classname not in self.infinite_weapons:
            return

        self._nade_refill_delay = Delay(
            PROJECTILE_REFILL_DELAY,
            self.player.give_named_item, weapon_classname
        )
Ejemplo n.º 20
0
class Regeneration_Suit(Item):
    "Regenerate health up to 100 when out of combat for 5 seconds."
    authors = ("Mahi",)
    category = "DEBUG"
    cost = 2500
    limit = 1

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self._repeat = TickRepeat(self._tick)
        self._delay = None

    def _tick(self, player):
        if player.health < 100:
            player.health = min(player.health + 3, 100)

    def player_spawn(self, player, **eargs):
        self._repeat.args = (player,)
        self._repeat.start(1, 0)

    def player_death(self, **eargs):
        if self._delay is not None:
            self._delay.cancel()
            self._delay = None
        self._repeat.stop()

    def player_attack(self, **eargs):
        if self._delay is not None:
            self._delay.cancel()
        self._repeat.pause()
        self._delay = Delay(5, self._cancel_pause)

    def player_victim(self, **eargs):
        if self._delay is not None:
            self._delay.cancel()
        self._repeat.pause()
        self._delay = Delay(5, self._cancel_pause)

    def _cancel_pause(self):
        self._repeat.resume()
        self._delay = None
Ejemplo n.º 21
0
 def give_spawn_protection(self):
     delay = spawn_protection.get_float()
     if delay <= 0:
         return
     self.in_spawn_protection = True
     self.godmode = True
     self.color = self.color.with_alpha(100)
     self._protect_delay = Delay(
         delay=delay,
         callback=self.remove_spawn_protection,
         kwargs={'from_delay': True},
     )
    def _on_player_run_command(self, player, usercmd, **kwargs):
        if (usercmd.buttons & PlayerButtons.FORWARD
                or usercmd.buttons & PlayerButtons.BACK
                or usercmd.buttons & PlayerButtons.MOVELEFT
                or usercmd.buttons & PlayerButtons.MOVERIGHT
                or usercmd.buttons & PlayerButtons.JUMP):
            if self.state == player.move_type:
                return

            if self.state == MoveType.LADDER and self.level > 0:
                Delay(0.5, self.reduce_gravity, args=(player, self.reduction))
            self.state = player.move_type
Ejemplo n.º 23
0
    def _on_player_pre_attack(self, attacker, victim, info, **kwargs):
        if victim.dead or randint(0, 101) > 25 or self.level == 0:
            return

        electric_sound.index = victim.index
        electric_sound.origin = victim.origin
        electric_sound.play()
        Delay(0.5, electric_sound.stop)
        damage = randint(2, self.extra_damage)
        info.damage += damage
        send_wcs_saytext_by_index(self._msg_a.format(damage=damage, name=victim.name), attacker.index)
        self.effect.create(origin=victim.origin)
Ejemplo n.º 24
0
def doCommand(userid):
    player_entity = Player(index_from_userid(userid))
    if userid not in resetskills:
        resetskills[userid] = 0
    if resetskills[userid] == 1 or player_entity.dead:
        player_entity.client_command("kill", True)
        race = wcs.wcs.wcsplayers[userid].currace
        raceinfo = wcs.wcs.racedb.getRace(race)
        level = wcs.wcs.wcsplayers[userid].all_races[race]['level']
        unused = wcs.wcs.wcsplayers[userid].all_races[race]['unused']
        nol = raceinfo['numberoflevels']
        nos = int(raceinfo['numberofskills'])
        if ('|') in nol:
            nol = nol.split('|')
        if len(nol) == 1:
            maxunused = int(nol) * nos
        else:
            maxunused = 0
            for x in nol:
                maxunused += int(x)

        v = 0
        for x in wcs.wcs.wcsplayers[userid].all_races[race]['skills'].split(
                '|'):
            v += int(x)
        unused += v
        if unused > maxunused:
            unused = maxunused

        skills = []
        for x in range(1, 10):
            skill = 'skill' + str(x)
            if skill in wcs.wcs.racedb.races[race]:
                skills.append('0')
        skillst = '|'.join(skills)

        wcs.wcs.wcsplayers[userid].all_races[race]['unused'] = unused
        wcs.wcs.wcsplayers[userid].skills = skillst
        wcs.wcs.wcsplayers[userid].all_races[race]['skills'] = skillst
        wcs.wcs.wcsplayers[userid].save()

        wcs.wcs.tell(
            userid,
            "\x04[WCS] \x05Your skills has been reset. Type \x04'spendskills' \x05to spend your \x04%s \x05unused skill points."
            % wcs.wcs.wcsplayers[userid].all_races[race]['unused'])
    else:
        resetskills[userid] = 1
        wcs.wcs.tell(
            userid,
            '\x04[WCS] \x05Type \x04resetskills \x05again to continue. You will \x04die!'
        )
        Delay(3.0, resetskills_dict, (userid, ))
Ejemplo n.º 25
0
def wcs_setfx_noblock_command(command_info,
                              player: convert_userid_to_player,
                              operator: valid_operators('='),
                              value: int,
                              time: float = 0):
    if player is None:
        return

    player.noblock = value

    if time > 0:
        Delay(time, validate_userid_after_delay,
              (wcs_setfx_noblock_command, player.userid, '=', not value))
Ejemplo n.º 26
0
 def __init__(self, index):
     super(_MultiLevelPlayer, self).__init__(index)
     self.sound = sound_manager.emit_sound('multi_level', index)
     self.start_gravity = self.gravity
     self.start_speed = self.speed
     self.gravity = gravity.get_int() / 100
     self.speed = speed.get_int() / 100
     self.give_spark_entity()
     self.delay = Delay(
         delay=self.sound.duration,
         callback=multi_level_manager.__delitem__,
         args=(self.userid, ),
     )
Ejemplo n.º 27
0
def _pre_knife_steal(game_event):
    previous_attacker_level = game_event['attacker_level'] - 1
    previous_victim_level = game_event['victim_level'] + 1
    difference = previous_victim_level - previous_attacker_level

    if difference <= 1:
        return EventAction.CONTINUE

    levels = abs(difference) - 1
    killer = player_dictionary[game_event['attacker']]
    victim = player_dictionary[game_event['victim']]

    Delay(
        delay=0,
        callback=killer.increase_level,
        kwargs={
            'levels': levels,
            'reason': 'swap',
            'victim': victim.userid,
            'sound_name': 'swapped_up',
        },
    )
    Delay(delay=0,
          callback=victim.decrease_level,
          kwargs={
              'levels': levels,
              'reason': 'swap',
              'attacker': killer.userid,
              'sound_name': 'swapped_down',
          })
    Delay(delay=0,
          callback=_fire_swap_event,
          kwargs={
              'killer': killer.userid,
              'killer_level': previous_victim_level,
              'victim': victim.userid,
              'victim_level': previous_attacker_level,
          })
    return EventAction.CONTINUE
Ejemplo n.º 28
0
    def _get_player_level(self, userid):
        """Return the current dialog message level for the given player."""
        # Get the player's next message level
        level = max(list(_player_levels[userid]) + [0]) + 1

        # Increase the player's level
        _player_levels[userid].add(level)

        # Remove the level after the time is up
        Delay(self.time, _player_levels[userid].remove, level)

        # Return the level
        return level
Ejemplo n.º 29
0
    def delay(self,
              delay,
              callback,
              args=(),
              kwargs=None,
              cancel_on_level_end=False):
        """Create the delay which will be stopped after removing the entity.

        :param float delay:
            The delay in seconds.
        :param callback:
            A callable object that should be called after the delay expired.
        :param tuple args:
            Arguments that should be passed to the callback.
        :param dict kwargs:
            Keyword arguments that should be passed to the callback.
        :param bool cancel_on_level_end:
            Whether or not to cancel the delay at the end of the map.
        :raise ValueError:
            If the given callback is not callable.
        :return:
            The delay instance.
        :rtype: Delay
        """
        # Get the index of the entity
        index = self.index

        # TODO: Ideally, we want to subclass Delay and cleanup on cancel() too
        #   in case the caller manually cancel the returned Delay.
        def _callback(*args, **kwargs):
            """Called when the delay is executed."""
            # Remove the delay from the global dictionary...
            _entity_delays[index].remove(delay)

            # Was this the last pending delay for the entity?
            if not _entity_delays[index]:

                # Remove the entity from the dictionary...
                del _entity_delays[index]

            # Call the callback...
            callback(*args, **kwargs)

        # Get the delay instance...
        delay = Delay(delay, _callback, args, kwargs, cancel_on_level_end)

        # Add the delay to the dictionary...
        _entity_delays[index].add(delay)

        # Return the delay instance...
        return delay
Ejemplo n.º 30
0
def _reincarnate_delay(wcsplayer, counter, vector):
    if counter:
        reincarnation_counter_message.send(wcsplayer.index, count=counter)

        counter -= 1

        _delays[wcsplayer] = Delay(1, _reincarnate_delay, (wcsplayer, counter, vector))
    else:
        reincarnation_complete_message.send(wcsplayer.index)

        player = wcsplayer.player

        player.spawn()
        player.teleport(origin=vector)
Ejemplo n.º 31
0
 def give_spawn_protection(self):
     """Give the player spawn protection."""
     delay = spawn_protection.get_float()
     if delay <= 0:
         return
     self.in_spawn_protection = True
     self.godmode = True
     self.color = self.color.with_alpha(100)
     self._protect_delay = Delay(
         delay=delay,
         callback=self.remove_spawn_protection,
         kwargs={'from_delay': True},
         cancel_on_level_end=True,
     )
Ejemplo n.º 32
0
    def __call__(self, key, delay, callback, args=(), call_on_cancel=False):
        """Add the delay object and reference it by `key`."""
        # Format the delay key
        key = self._format_key(key)

        # Cancel the delay for the key, if it is running
        self.cancel(key)

        # Store whether the callback should be called on cancel
        self._call_on_cancel[key] = call_on_cancel

        # Add the delay if delays are enabled
        if self.delays_enabled:
            self[key] = Delay(delay, callback, args)
Ejemplo n.º 33
0
    def _on_player_attack(self, attacker, victim, **kwargs):
        if victim.dead or victim.is_slowed or randint(
                0, 101) > 10 or self.level == 0:
            return

        current_speed = victim.speed
        victim.speed = 0.8
        victim.is_slowed = True
        victim.delay(self.duration,
                     victim.__setattr__,
                     args=('speed', current_speed))
        Delay(self.duration, victim.__setattr__, args=('is_slowed', False))
        send_wcs_saytext_by_index(self._msg_a.format(name=victim.name),
                                  attacker.index)
Ejemplo n.º 34
0
def sp_map(source, command):
    if command.get_arg_count() == 1:
        source.message(
            "c=(white)[c=(purple)SPc=(white)] Usage: $c=(purple)sp_map $c=(white)<map>"
        )
        return CommandReturn.BLOCK
    level = command[1]
    if not engine_server.is_map_valid(level):
        source.message("c=(white)[c=(purple)SPc=(white)] Map not found")
        return CommandReturn.BLOCK
    Delay(3, change_map, level)
    source.message(
        "c=(white)[c=(purple)SPc=(white)] Changing map to {} in 3 seconds".
        format(level))
Ejemplo n.º 35
0
    def check_files(self):
        self.delay = None

        if not net_chan_is_file_in_waiting_list(self.net_channel, self.file):
            if self.index not in sv_allowupload:
                return self.callback(self, True)
            else:
                return self.callback(self, False)

        if self.transfer_time >= self.time_limit:
            return self.callback(self, False)

        self.transfer_time += 1
        self.delay = Delay(1, self.check_files, cancel_on_level_end=True)
Ejemplo n.º 36
0
    def _enable(self, duration=None, *args, **kwargs):
        """Enable the effect.

        Add the new effect to a player's effects and call
        the descriptor object's on function with the provided
        arguments and keyword arguments.

        If duration is a positive integer, adds a delay which
        automatically disables the effect after the duration.
        """
        if isinstance(duration, int) and duration > 0:
            self._delay = Delay(duration, self._disable)
        self._player._effects[self._descriptor_obj].append(self)
        self._descriptor_obj._on_f(self._player, *args, **kwargs)
Ejemplo n.º 37
0
    def stage_chatgame_ask(self):
        timeout = max(1, config_manager_games['answer_timeout'])

        def timeout_callback():
            self.set_stage_group('chatgame-timed-out')

        self._delays.append(Delay(timeout, timeout_callback))

        broadcast(self._question)

        if config_manager_games['sound'] is not None:
            indexes = [player.index for player in self._players]
            config_manager_games['sound'].play(*indexes)

        self._receiving_answers = True
Ejemplo n.º 38
0
    def stage_chatgame_print_rules(self):
        if self._rules_print == len(self.rules):
            self.set_stage_group('chatgame-start')

        else:
            rule = self.rules[self._rules_print]
            broadcast(rule)

            self._rules_print += 1

            def callback():
                self.set_stage_group('chatgame-print-rules')

            self._delays.append(
                Delay(config_manager_games['rules_print_interval'], callback))
Ejemplo n.º 39
0
            def countdown(ticks_left):
                if (ticks_left > 3 or ticks_left < 1 or config_manager[
                        'countdown_{}_material'.format(ticks_left)] == ""):

                    TextMsg(str(ticks_left)).send(*indexes)

                else:
                    for player in self._players:
                        show_overlay(player, config_manager[
                            'countdown_{}_material'.format(ticks_left)], 1)

                if config_manager['countdown_sound'] is not None:
                    config_manager['countdown_sound'].play(*indexes)

                self._prepare_countdown = Delay(1.0, countdown, ticks_left - 1)
Ejemplo n.º 40
0
    def _on_ability(self, player, **kwargs):
        if self.level == 0:
            return

        if len(self._wards) > 1:
            send_wcs_saytext_by_index(self._msg_f, player.index)
            return

        location = player.origin.copy()
        self._wards.append(location)
        self._players_hit.clear()
        self._repeater.start(0.2)

        send_wcs_saytext_by_index(self._msg_a, player.index)

        Delay(self.duration, self._delete_ward, args=(location, ))
Ejemplo n.º 41
0
def wcs_setfx_gravity_command(command_info, player:convert_userid_to_player, operator:valid_operators(), value:float, time:float=0):
    if player is None:
        return

    if operator == '=':
        old_value = player.gravity
        player.gravity = value
        value = old_value - value
    elif operator == '+':
        player.gravity += value
        value *= -1
    else:
        player.gravity = max(player.gravity - value, 0)

    if time > 0:
        Delay(time, validate_userid_after_delay, (wcs_setfx_gravity_command, player.userid, '+', value))
Ejemplo n.º 42
0
def on_entity_created(base_entity):
    try:
        index = base_entity.index
    except ValueError:
        return

    if 'flashbang_projectile' in base_entity.classname:
        # Get the owner of the flashbang.
        owner_handle = base_entity.owner_handle
        # No owner? (invalid inthandle)
        if owner_handle == -1:
            return

        # Delay the call by a single frame, otherwise the flashbang won't be
        # properly initialized.
        Delay(0, OnFlashbangCreated.manager.notify, (index, owner_handle))
Ejemplo n.º 43
0
def wcs_setfx_health_command(command_info, player:convert_userid_to_player, operator:valid_operators(), value:int, time:float=0):
    if player is None:
        return

    if operator == '=':
        old_value = player.health
        player.health = value
        value = old_value - value
    elif operator == '+':
        player.health += value
        value *= -1
    else:
        # TODO: Minimum 1 health?
        player.health -= value

    if time > 0:
        Delay(time, validate_userid_after_delay, (wcs_setfx_health_command, player.userid, '+', value))
Ejemplo n.º 44
0
    def stage_prepare_prepare_to_ask(self):
        def ask_callback():
            self.set_stage_group('chatgame-ask')

        min_delay = max(0, config_manager_games['ask_delay_min'])
        max_delay = min(0, config_manager_games['ask_delay_min'])

        if min_delay > max_delay:
            self.set_stage_group('chatgame-improperly-configured')
            return

        if min_delay == max_delay:
            delay = min_delay
        else:
            delay = randrange(min_delay, max_delay)

        self._delays.append(Delay(delay, ask_callback))
Ejemplo n.º 45
0
    def infinite_on(self):
        if (self._ammo_refill_delay is not None or
                self._nade_refill_delay is not None):

            raise ValueError("Infinite equipment is already turned on")

        maxed_weapon_classnames = map(
            lambda weapon: weapon.classname,
            self.max_ammo(self.infinite_weapons)
        )

        self.infinite_weapons = list(filter(
            lambda classname:
                classname in maxed_weapon_classnames or
                classname in PROJECTILE_CLASSNAMES,
            self.infinite_weapons
        ))

        self._ammo_refill_delay = Delay(
            INFINITE_AMMO_REFILL_INTERVAL, self._refill_infinite_ammo)
Ejemplo n.º 46
0
    def refresh(self):

        # We must cancel delay in case we were called from .count_vote
        if self._refresh_delay is not None and self._refresh_delay.running:
            self._refresh_delay.cancel()

        # Check our cvar - we do this this late to allow on-line
        # alteration of this cvar (during the vote)
        if not config_manager["votemap_show_progress"]:
            return

        # Send KeyHint
        if self._message:

            # Calculate seconds left
            timeleft = int(status.vote_start_time + config_manager["vote_duration"] - time())

            # Send HintText
            HintText(self._message.tokenize(timeleft="{:02d}:{:02d}".format(timeleft // 60, timeleft % 60))).send(
                *[user.player.index for user in user_manager.values()]
            )

        # Schedule next refresh
        self._refresh_delay = Delay(REFRESH_INTERVAL, self.refresh)
Ejemplo n.º 47
0
    def _refill_infinite_ammo(self):
        self.max_ammo(self.infinite_weapons)

        self._ammo_refill_delay = Delay(
            INFINITE_AMMO_REFILL_INTERVAL, self._refill_infinite_ammo)
Ejemplo n.º 48
0
 def player_victim(self, **eargs):
     if self._delay is not None:
         self._delay.cancel()
     self._repeat.pause()
     self._delay = Delay(5, self._cancel_pause)
Ejemplo n.º 49
0
class FallProtectedPlayer:
    def __init__(self, player):
        self.player = player
        self.p_player = protected_player_manager[player.index]

        self._delay = None
        self._counter = None

    def protect(self, seconds):
        if self._counter is not None:
            raise ValueError(
                "Player {} already protected".format(self.player.userid)
            )

        self._counter = self.p_player.new_counter(
            health=0, display=strings_module['health falldmg_protection'])

        def sub_hook(counter, info):
            self.p_player.delete_counter(counter)
            self.p_player.unset_protected()

            self._counter = None

            Shake(
                FALL_PROT_SHAKE_MAGNITUDE, FALL_PROT_SHAKE_TIME
            ).send(self.player.index)

            self._delay.cancel()

            return False

        self._counter.hook_hurt = get_hook('W', next_hook=sub_hook)

        def delay_callback():
            self.p_player.delete_counter(self._counter)
            self.p_player.unset_protected()

            self._counter = None

        self._delay = Delay(seconds, delay_callback)
        self.p_player.set_protected()

    def unprotect(self):
        if self._counter is None:
            raise ValueError(
                "Player {} is not protected yet".format(self.player.userid)
            )

        self.cancel_delay()

        self.p_player.delete_counter(self._counter)
        self.p_player.unset_protected()

        self._counter = None

    @property
    def protected(self):
        return self._counter is not None

    def cancel_delay(self):
        if self._delay is not None and self._delay.running:
            self._delay.cancel()
Ejemplo n.º 50
0
    def start(self, popup, targets, timeout):
        self._popup = popup
        self._max_players = len(targets)
        self._timeout_delay = Delay(timeout, self.finish)

        popup.send(*[player.index for player in targets])
Ejemplo n.º 51
0
class KeyHintProgress:
    def __init__(self):
        self._refresh_delay = None
        self._message = None
        self._users_total = 0
        self._users_voted = 0

    def count_vote(self, map_):
        self._users_voted += 1

        for exception in EXCLUDE_ENTRY_CLASSES:
            if isinstance(map_, exception):
                return

        maps = list(
            filter(lambda map_: map_.votes, sorted(map_manager.values(), key=lambda map_: map_.votes, reverse=True))
        )

        map_tokens = {}
        if len(maps) > 0:
            map_tokens["map1"] = strings_popups["vote_progress_map_with_votes"].tokenize(
                map=maps[0].name, votes=maps[0].votes
            )
            if len(maps) > 1:
                map_tokens["map2"] = strings_popups["vote_progress_map_with_votes"].tokenize(
                    map=maps[1].name, votes=maps[1].votes
                )
                if len(maps) > 2:
                    map_tokens["map3"] = strings_popups["vote_progress_map_with_votes"].tokenize(
                        map=maps[2].name, votes=maps[2].votes
                    )
                else:
                    map_tokens["map3"] = strings_popups["vote_progress_map_without_votes"]
            else:
                map_tokens["map2"] = strings_popups["vote_progress_map_without_votes"]
                map_tokens["map3"] = strings_popups["vote_progress_map_without_votes"]
        else:
            map_tokens["map1"] = strings_popups["vote_progress_map_without_votes"]
            map_tokens["map2"] = strings_popups["vote_progress_map_without_votes"]
            map_tokens["map3"] = strings_popups["vote_progress_map_without_votes"]

        self._message = strings_popups["vote_progress"].tokenize(
            users_voted=self._users_voted, users_total=self._users_total, **map_tokens
        )

        self.refresh()

    def refresh(self):

        # We must cancel delay in case we were called from .count_vote
        if self._refresh_delay is not None and self._refresh_delay.running:
            self._refresh_delay.cancel()

        # Check our cvar - we do this this late to allow on-line
        # alteration of this cvar (during the vote)
        if not config_manager["votemap_show_progress"]:
            return

        # Send KeyHint
        if self._message:

            # Calculate seconds left
            timeleft = int(status.vote_start_time + config_manager["vote_duration"] - time())

            # Send HintText
            HintText(self._message.tokenize(timeleft="{:02d}:{:02d}".format(timeleft // 60, timeleft % 60))).send(
                *[user.player.index for user in user_manager.values()]
            )

        # Schedule next refresh
        self._refresh_delay = Delay(REFRESH_INTERVAL, self.refresh)

    def start(self):
        self._users_total = len(user_manager)
        self._users_voted = 0

        self._message = strings_popups["vote_progress"].tokenize(
            users_voted=self._users_voted,
            users_total=self._users_total,
            map1=strings_popups["vote_progress_map_without_votes"],
            map2=strings_popups["vote_progress_map_without_votes"],
            map3=strings_popups["vote_progress_map_without_votes"],
        )

        self.refresh()

    def stop(self):
        if self._refresh_delay is not None and self._refresh_delay.running:
            self._refresh_delay.cancel()
Ejemplo n.º 52
0
class PrepareTime(JailGame):
    stage_groups = {
        'init': ["prepare-prepare", ],
        'destroy': [
            "prepare-cancel-delays",
            "unsend-popups",
            "cancel-delays",
            "destroy",
        ],
        'prepare-start': [
            'prepare-freeze',
            'prepare-register-event-handlers',
            'prepare-entry',
        ],
        'abort-prepare-interrupted': ["abort-prepare-interrupted", ],
        'prepare-continue': [
            "prepare-cancel-countdown",
            "prepare-undo-prepare-start",
            "register-event-handlers",
            "start-notify",
            "basegame-entry",
        ],
    }

    def __init__(self, leader_player, players, **kwargs):
        super().__init__(leader_player, players, **kwargs)

        self._prepare_delay = None
        self._prepare_countdown = None

    @stage('prepare-prepare')
    def stage_prepare_prepare(self):
        if self._settings.get('prepare', True):
            indexes = list(player.index for player in self._players)
            if self.leader.index not in indexes:
                indexes.append(self.leader.index)

            def callback():
                self.undo_stages('prepare-start')
                self.set_stage_group('prepare-continue')

            self._prepare_delay = Delay(
                config_manager['prepare_timeout'], callback)

            def countdown(ticks_left):
                if (ticks_left > 3 or ticks_left < 1 or config_manager[
                        'countdown_{}_material'.format(ticks_left)] == ""):

                    TextMsg(str(ticks_left)).send(*indexes)

                else:
                    for player in self._players:
                        show_overlay(player, config_manager[
                            'countdown_{}_material'.format(ticks_left)], 1)

                if config_manager['countdown_sound'] is not None:
                    config_manager['countdown_sound'].play(*indexes)

                self._prepare_countdown = Delay(1.0, countdown, ticks_left - 1)

            countdown(int(config_manager['prepare_timeout']))

            broadcast(strings_module['stage_prepare'])

            if config_manager['prepare_sound'] is not None:
                config_manager['prepare_sound'].play(*indexes)

            self.set_stage_group('prepare-start')

        else:
            self.set_stage_group('prepare-continue')

    def _prepare_event_handler_player_death(self, game_event):
        player = player_manager.get_by_userid(game_event['userid'])
        if player in self._players or player == self.leader:
            self.set_stage_group('abort-prepare-interrupted')

    def _prepare_event_handler_player_deleted(self, player):
        if player in self._players or player == self.leader:
            self.set_stage_group('abort-prepare-interrupted')

    def _prepare_event_handler_player_hurt(self, game_event):
        player = player_manager.get_by_userid(game_event['userid'])
        if player in self._players or player == self.leader:
            self.set_stage_group('abort-prepare-interrupted')

    @stage('prepare-register-event-handlers')
    def stage_prepare_register_event_handlers(self):
        event_manager.register_for_event(
            'player_death', self._prepare_event_handler_player_death)

        event_manager.register_for_event(
            'player_hurt', self._prepare_event_handler_player_hurt)

        internal_event_manager.register_event_handler(
            'player_deleted',
            self._prepare_event_handler_player_deleted
        )

    @stage('undo-prepare-register-event-handlers')
    def stage_undo_prepare_register_event_handlers(self):
        event_manager.unregister_for_event(
            'player_death', self._prepare_event_handler_player_death)

        event_manager.unregister_for_event(
            'player_hurt', self._prepare_event_handler_player_hurt)

        internal_event_manager.unregister_event_handler(
            'player_deleted',
            self._prepare_event_handler_player_deleted
        )

    @stage('prepare-cancel-delays')
    def stage_prepare_cancel_delays(self):
        for delay in (self._prepare_delay, self._prepare_countdown):
            if delay is not None and delay.running:
                delay.cancel()

    @stage('prepare-cancel-countdown')
    def stage_prepare_cancel_countdown(self):
        if self._prepare_countdown is not None:
            self._prepare_countdown.cancel()

    @stage('prepare-undo-prepare-start')
    def stage_prepare_undo_prepare_start(self):
        self.undo_stages('prepare-start')

    @stage('prepare-entry')
    def stage_prepare_entry(self):
        pass

    def _prepare_freeze_tick_handler(self):
        for player in self._players:
            weapon = player.active_weapon
            if weapon is None:
                continue

            weapon.next_attack += 1
            weapon.next_secondary_fire_attack += 1

    @stage('prepare-freeze')
    def stage_prepare_freeze(self):
        on_tick_listener_manager.register_listener(
            self._prepare_freeze_tick_handler)

        for player in self._players:
            player.stuck = True

    @stage('undo-prepare-freeze')
    def stage_undo_prepare_freeze(self):
        on_tick_listener_manager.unregister_listener(
            self._prepare_freeze_tick_handler)

        for player in self._players:
            player.stuck = False

            weapon = player.active_weapon
            if weapon is None:
                continue

            weapon.next_attack = 0
            weapon.next_secondary_fire_attack = 0

    @stage('abort-prepare-interrupted')
    def stage_abort_prepare_interrupted(self):
        broadcast(strings_module['abort_prepare_interrupted'])

        if config_manager['prepare_sound'] is not None:
            config_manager['prepare_sound'].stop()

        self.set_stage_group('destroy')
Ejemplo n.º 53
0
class SavedPlayer:
    def __init__(self, player):
        self._ammo_refill_delay = None
        self._nade_refill_delay = None
        self.player = player
        self.health = 0
        self.saved_weapons = []
        self.infinite_weapons = []

    def _nade_thrown(self, weapon_classname):
        if self._ammo_refill_delay is None:
            return

        if weapon_classname not in self.infinite_weapons:
            return

        self._nade_refill_delay = Delay(
            PROJECTILE_REFILL_DELAY,
            self.player.give_named_item, weapon_classname
        )

    def save_health(self):
        self.health = self.player.health

    def save_weapons(self):
        self.saved_weapons = []

        for weapon in self.player.weapons():
            weapon_dict = {
                'classname': weapon.classname,
                'subtype': weapon.get_property_int('m_iSubType'),
                'ammo': weapon.ammo if weapon.has_ammo() else None,
                'clip': weapon.ammo if weapon.has_clip() else None
            }

            self.saved_weapons.append(weapon_dict)

        self.strip()

    def save_all(self):
        self.save_health()
        self.save_weapons()

    def strip(self):
        for weapon in self.player.weapons():
            self.player.drop_weapon(weapon.pointer, NULL_VECTOR, NULL_VECTOR)
            weapon.remove()

    def restore_health(self):
        self.player.health = self.health

    def restore_weapons(self):
        self.strip()
        for weapon_dict in self.saved_weapons:
            weapon = Weapon.create(weapon_dict['classname'])
            weapon.teleport(self.player.origin, None, None)
            weapon.spawn()

            if weapon_dict['clip'] is not None:
                weapon.clip = weapon_dict['clip']

            if weapon_dict['ammo'] is not None:
                weapon.ammo = weapon_dict['ammo']

    def restore_all(self):
        self.restore_health()
        self.restore_weapons()

    def max_ammo(self, weapon_classnames):
        maxed_weapons = []

        for weapon in self.player.weapons():
            if weapon.classname not in weapon_classnames:
                continue

            if weapon.classname in PROJECTILE_CLASSNAMES:
                continue

            weapon_class = weapon_manager[weapon.classname]
            if not weapon.has_ammo() or weapon_class.maxammo <= 0:
                continue

            weapon.ammo = weapon_class.maxammo
            maxed_weapons.append(weapon)

        return maxed_weapons

    def _refill_infinite_ammo(self):
        self.max_ammo(self.infinite_weapons)

        self._ammo_refill_delay = Delay(
            INFINITE_AMMO_REFILL_INTERVAL, self._refill_infinite_ammo)

    def infinite_on(self):
        if (self._ammo_refill_delay is not None or
                self._nade_refill_delay is not None):

            raise ValueError("Infinite equipment is already turned on")

        maxed_weapon_classnames = map(
            lambda weapon: weapon.classname,
            self.max_ammo(self.infinite_weapons)
        )

        self.infinite_weapons = list(filter(
            lambda classname:
                classname in maxed_weapon_classnames or
                classname in PROJECTILE_CLASSNAMES,
            self.infinite_weapons
        ))

        self._ammo_refill_delay = Delay(
            INFINITE_AMMO_REFILL_INTERVAL, self._refill_infinite_ammo)

    def infinite_off(self):
        if self._ammo_refill_delay is None:
            raise ValueError("Infinite equipment is already turned off")

        self._ammo_refill_delay.cancel()
        self._ammo_refill_delay = None

        if self._nade_refill_delay is not None:
            if self._nade_refill_delay.running:
                self._nade_refill_delay.cancel()

            self._nade_refill_delay = None
Ejemplo n.º 54
0
class GunGamePlayer(Player):
    """Class used to interact directly with a specific player."""

    level = 0
    multi_kill = 0
    in_spawn_protection = False
    _protect_delay = None

    def __setattr__(self, attr, value):
        """Verify that the attribute's value should be set."""
        # Are there any pre-hooks for the attribute?
        if (
            attr in player_attributes and
            attr in attribute_pre_hooks and
            hasattr(self, attr)
        ):

            # Do any of the pre-hooks block the setting of the attribute?
            if not attribute_pre_hooks[attr].call_callbacks(self, value):

                # Block the attribute from being set
                return

        # Are there any post-hooks for the attribute?
        if not (
            attr in player_attributes and
            hasattr(self, attr) and
            attr in attribute_post_hooks
        ):

            # If not, simply set the attribute's value
            super().__setattr__(attr, value)
            return

        # Get the value prior to setting
        old_value = getattr(self, attr)

        # Set the attribute's value
        super().__setattr__(attr, value)

        # Call all of the attribute's post-hooks
        attribute_post_hooks[attr].call_callbacks(self, value, old_value)

    @property
    def unique_id(self):
        """Return the player's unique id."""
        return self.uniqueid

    @property
    def is_afk(self):
        """Return whether the player is AFK."""
        return is_client_idle(self.index)

    # =========================================================================
    # >> LEVEL FUNCTIONALITY
    # =========================================================================
    def increase_level(self, levels, reason, victim=0, sound_name='level_up'):
        """Increase the player's level by the given amount."""
        if GunGameStatus.MATCH is not GunGameMatchStatus.ACTIVE:
            return
        if not isinstance(levels, int) or levels < 1:
            raise ValueError(f'Invalid value given for levels "{levels}".')
        old_level = self.level
        new_level = old_level + levels
        if new_level > weapon_order_manager.max_levels:
            with GG_Win() as event:
                event.attacker = event.winner = self.userid
                event.userid = event.loser = victim
            return
        self.level = new_level
        if self.level != new_level:
            return
        self.multi_kill = 0
        self.play_sound(sound_name)

        with GG_Level_Up() as event:
            event.attacker = event.leveler = self.userid
            event.userid = event.victim = victim
            event.old_level = old_level
            event.new_level = new_level
            event.reason = reason

    def decrease_level(
        self, levels, reason, attacker=0, sound_name='level_down'
    ):
        """Decrease the player's level by the given amount."""
        if GunGameStatus.MATCH is not GunGameMatchStatus.ACTIVE:
            return
        if not isinstance(levels, int) or levels < 1:
            raise ValueError(f'Invalid value given for levels "{levels}".')
        old_level = self.level
        new_level = max(old_level - levels, 1)
        if self.level == new_level:
            return
        self.level = new_level
        if self.level != new_level:
            return
        self.multi_kill = 0
        self.play_sound(sound_name)

        with GG_Level_Down() as event:
            event.attacker = attacker
            event.leveler = event.userid = self.userid
            event.old_level = old_level
            event.new_level = new_level
            event.reason = reason

    # =========================================================================
    # >> WEAPON FUNCTIONALITY
    # =========================================================================
    @property
    def level_multi_kill(self):
        """Return the multi_kill value for the player's current level."""
        return weapon_order_manager.active[self.level].multi_kill

    @property
    def level_weapon(self):
        """Return the player's current level weapon."""
        return weapon_order_manager.active[self.level].weapon

    @property
    def level_weapon_classname(self):
        """Return the classname of the player's current level weapon."""
        return weapon_manager[self.level_weapon].name

    def strip_weapons(self, not_filters=None, remove_incendiary=False):
        """Strip weapons from the player."""
        base_not_filters = {'objective', 'tool'}
        if not_filters is None:
            not_filters = set()
        not_filters |= base_not_filters
        if self.level_weapon not in melee_weapons:
            not_filters |= {'melee'}
        for weapon in self.weapons():
            tags = weapon_manager[weapon.classname].tags
            if not_filters.intersection(tags):
                if not remove_incendiary or 'incendiary' not in tags:
                    continue
            if weapon.classname == self.level_weapon_classname:
                continue
            weapon.remove()

    def has_level_weapon(self):
        """Return whether or not the player has their level weapon."""
        for weapon in self.weapons():
            if weapon.classname == self.level_weapon_classname:
                return True
        return False

    def give_level_weapon(self):
        """Give the player the weapon of their current level."""
        if self.has_level_weapon():
            return self.get_weapon(self.level_weapon_classname)
        return make_object(
            Weapon,
            self.give_named_item(self.level_weapon_classname)
        )

    # =========================================================================
    # >> MESSAGE FUNCTIONALITY
    # =========================================================================
    def center_message(self, message='', **tokens):
        """Send a center message to the player."""
        message_manager.center_message(message, self.index, **tokens)

    def chat_message(self, message='', index=0, **tokens):
        """Send a chat message to the player."""
        message_manager.chat_message(message, index, self.index, **tokens)

    def echo_message(self, message='', **tokens):
        """Send an echo message to the player."""
        message_manager.echo_message(message, self.index, **tokens)

    def hint_message(self, message='', **tokens):
        """Send a hint message to the player."""
        message_manager.hint_message(message, self.index, **tokens)

    # pylint: disable=too-many-arguments
    def hud_message(
        self, message='', x=-1.0, y=-1.0, color1=WHITE,
        color2=WHITE, effect=0, fade_in=0.0, fade_out=0.0,
        hold=4.0, fx_time=0.0, channel=0, **tokens
    ):
        """Send a hud message to the player."""
        message_manager.hud_message(
            message, x, y, color1, color2, effect, fade_in,
            fade_out, hold, fx_time, channel, self.index, **tokens
        )

    def keyhint_message(self, message='', **tokens):
        """Send a keyhint message to the player."""
        message_manager.keyhint_message(message, self.index, **tokens)

    def motd_message(
        self, panel_type=2, title='', message='', visible=True, **tokens
    ):
        """Send a motd message to the player."""
        message_manager.motd_message(
            panel_type, title, message, visible, self.index, **tokens
        )

    def top_message(self, message='', color=WHITE, time=4, **tokens):
        """Send a toptext message to the player."""
        message_manager.top_message(message, color, time, self.index, **tokens)

    # =========================================================================
    # >> SPAWN PROTECT FUNCTIONALITY
    # =========================================================================
    def give_spawn_protection(self):
        """Give the player spawn protection."""
        delay = spawn_protection.get_float()
        if delay <= 0:
            return
        self.in_spawn_protection = True
        self.godmode = True
        self.color = self.color.with_alpha(100)
        self._protect_delay = Delay(
            delay=delay,
            callback=self.remove_spawn_protection,
            kwargs={'from_delay': True},
            cancel_on_level_end=True,
        )

    def remove_spawn_protection(self, from_delay=False):
        """Remove the player's spawn protection."""
        self.cancel_protect_delay(from_delay)
        self.godmode = False
        self.color = self.color.with_alpha(255)
        self.in_spawn_protection = False

    def cancel_protect_delay(self, from_delay=False):
        """Cancel the player's spawn protection delay."""
        if self._protect_delay is None:
            return
        if not from_delay:
            self._protect_delay.cancel()
        self._protect_delay = None

    # =========================================================================
    # >> SOUND FUNCTIONALITY
    # =========================================================================
    def play_sound(self, sound):
        """Play the sound to the player."""
        sound_manager.play_sound(sound, self.index)

    def emit_sound(self, sound):
        """Emit the sound from the player."""
        sound_manager.emit_sound(sound, self.index)

    def stop_sound(self, sound):
        """Stop the sound from emitting from the player."""
        sound_manager.stop_sound(sound, self.index)

    # =========================================================================
    # >> DATABASE FUNCTIONALITY
    # =========================================================================
    def update_time_stamp(self):
        """Update the player's time stamp."""
        if self.unique_id in winners_database:
            winners_database.update_player_time_stamp(self)

    @property
    def wins(self):
        """Return the number of wins for the player."""
        if self.unique_id in winners_database:
            return winners_database[self.unique_id].wins
        return 0

    @wins.setter
    def wins(self, wins):
        """Add a win for the player."""
        if not (self.is_fake_client() or 'BOT' in self.steamid):
            winners_database.set_player_wins(self, wins)

    @property
    def rank(self):
        """Return the player's rank on the server."""
        # If the player is not in the database, they have no wins
        if self.unique_id not in winners_database:
            return 0

        # Start with the base rank
        rank = 1

        # Get the number of wins for the player
        wins = self.wins

        # Create a list to store players tied with this player
        tied_players = list()

        # Loop through all players in the database
        for unique_id in winners_database:

            # Get the current players wins
            current_wins = winners_database[unique_id].wins

            # Does the current player have more wins than this player?
            if current_wins > wins:
                rank += 1

            # Is the current player tied with this player?
            if current_wins == wins:
                tied_players.append(unique_id)

        # Are there any tied players?
        if len(tied_players) > 1:

            # Sort the tied players by their last win
            sorted_ties = sorted(
                tied_players,
                key=lambda key: winners_database[key].last_win
            )

            # Get the final rank of the player
            rank += sorted_ties.index(self.unique_id)

        # Return the player's rank
        return rank