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()
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)
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)
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()
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 _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)
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
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 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)
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)
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))
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()
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
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))
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
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))
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))
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))
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 )
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
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
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)
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, ))
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))
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 _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
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
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
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)
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 __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)
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)
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))
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)
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 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
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))
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)
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, ))
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))
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))
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))
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))
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 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 _refill_infinite_ammo(self): self.max_ammo(self.infinite_weapons) self._ammo_refill_delay = Delay( INFINITE_AMMO_REFILL_INTERVAL, self._refill_infinite_ammo)
def player_victim(self, **eargs): if self._delay is not None: self._delay.cancel() self._repeat.pause() self._delay = Delay(5, self._cancel_pause)
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()
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])
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()
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')
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
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