def do_attack(attacker, defender, defending_team, surprise=''): if attacker in team_1: Narrator().cut() attacker.busy = True attacker.reveal() defender.busy = True defender.reveal() if attacker.hit(defender, 1 + initiative[attacker] - initiative[defender]): Narrator().add([ attacker.name, verbs_1 + 'kills', defender.name, surprise, at_area, weapon_name[attacker] ]) drops.extend(player_2.drops) defending_team.remove(defender) else: verb = 'attacks' if fight_round == 1 and attacker in team_1 else 'fights' Narrator().add([ attacker.name, verbs_1 + verb, defender.name, at_area, weapon_name[attacker] ]) Narrator().apply_stock() if defender.is_alive and random( ) > defender.courage and defender.can_flee(): w = defender.weapon defender.flee(panic=True) if defender.weapon != w: drops.append(w) defending_team.remove(defender)
def loot_weapon(self, weapon: Optional[Weapon] = None, retry=True): if weapon is None: weapon = Map().pick_weapon(self.current_area) if weapon is None or (weapon.damage_mult <= self.weapon.damage_mult and (not weapon.small or Context().time == STARTER or self.bag is None)): if retry: return self.loot(take_a_break=False) else: Narrator().add([ self.name, 'tries to find a weapon', self.current_area.at, 'but can\'t find anything good' ]) return if weapon.name == self.weapon.name: self.weapon.long_name.replace('\'s', '\'s old') Narrator().add([ self.name, 'picks up', f'a better {weapon.name}', self.current_area.at ]) else: Narrator().add([ self.name, 'picks up', weapon.long_name, self.current_area.at ]) self.get_weapon(weapon)
def loot(self, take_a_break=True): items: List[Item] = [] for _ in self.acting_players: b = Map().pick_item(self) if b is not None: items.append(b) items.sort(key=lambda x: random()) self.acting_players.sort(key=lambda x: random()) empty_handed: List[Player] = [] for player in self.acting_players: if len(items): item = items.pop(0) Narrator().add([ player.name, 'picks up', item.long_name, self.current_area.at ]) player.get_item(item) else: empty_handed.append(player) if len(empty_handed): verb = 'try' if len(empty_handed) > 1 else 'tries' Narrator().add([ player_names(empty_handed), verb, 'to find loot', self.current_area.at, 'but can\'t find anything good' ])
def check_bag(self): if 'unchecked bag' in self.status: self.status.remove('unchecked bag') bags = [e for e in self._equipment if isinstance(e, Bag)] if not len(bags): return stuff = [e.name for bag in bags for e in bag.content] if len(stuff): Narrator().new([ self.name, 'checks', self.his, 'bags,' if len(bags) > 1 else 'bag,', 'finds', format_list(stuff) ]) Narrator().cut() else: Narrator().new([ self.name, 'checks', self.his, 'bags,' if len(bags) > 1 else 'bag,', 'finds', 'they are' if len(bags) > 1 else 'it is', 'empty' ]) Narrator().cut() for i in range(1, len(bags)): extra_bag = bags[i] self._equipment.remove(extra_bag) bags[0].content.extend(extra_bag.content) bag_weapons = [ i for i in bags[0].content if isinstance(i, Weapon) and i.damage_mult > self.weapon.damage_mult ] bag_weapons.sort(key=lambda x: -x.damage_mult) if len(bag_weapons): bags[0].content.remove(bag_weapons[0]) self.get_weapon(bag_weapons[0])
def fill_bottles(self): self.water_upkeep() if Map().has_water(self): total_water = self.water + sum(b.fill for b in self.bottles) self._water = max(1.5, self.water) for b in self.bottles: b.fill = 1 new_total_water = self.water + sum(b.fill for b in self.bottles) if new_total_water > total_water + 1: if len(self.bottles): Narrator().add([ self.name, 'fills', self.his, 'bottles' if len(self.bottles) > 1 else 'bottle', self.current_area.at ]) else: Narrator().add([self.name, 'drinks', self.current_area.at]) else: water = random() amount = min(self.thirst, water) self._water += amount water -= amount for b in self.bottles: amount = min(1 - b.fill, water) b.fill += amount water -= amount
def _apply(self, name, player): if player.be_damaged(random() * 5, 'fire'): Narrator().new([player.name, 'blows up', 'on', f'{name}!']) else: Narrator().new([player.name, 'steps', 'on', f'{name}!']) if not Narrator().has_stock: Narrator().add([player.name, 'is', 'wounded']) Narrator().apply_stock()
def _apply(self, name, player): Narrator().new( [player.first_name, 'gets', 'ensnared into', f'{name}!']) if random() > 0.5: player.status.append('leg wound') Narrator().add([player.name, 'wounds', player.his, 'leg']) else: player.status.append(TRAPPED) take_advantage_of_trap(self, player)
def go_to_sleep(self, stock=False): if self.energy < 0.2: Narrator().add([self.name, 'is exhausted'], stock=stock) self.add_health(self.energy * (1 - self.health)) self.add_energy(self.sleep * (1 - self.energy)) self.add_sleep(1) verb = 'sleeps' if self.energy > 0 else 'collapses' Narrator().add([self.name, verb, self.current_area.at], stock=stock) self.status.append(SLEEPING)
def _apply(self, name, player): if player.be_damaged(random(), 'trident'): Narrator().new([ player.name, 'impales', f'{player.him}self', 'on', f'{name}!' ]) else: Narrator().new([player.name, 'falls', f'into', f'{name}!']) if not Narrator().has_stock: Narrator().add([player.name, 'is', 'lightly wounded']) Narrator().apply_stock()
def apply_strat(self, strategy: Optional[Strategy]): Narrator().cut() if self.acted or self.busy: return if strategy is None: strategy = self.strategy try: strategy.apply(self) except AttributeError as e: raise AttributeError( f'{self.name}({self.current_area.at}) has no strat ({self.strategy})' ) from e Narrator().cut()
def go_get_drop(self, area: Area): self.end_ambush() out = self.go_to(area) if out is not None: Narrator().add([self.name, f'goes {out.to} to get loot']) else: Narrator().cut() if self.check_for_ambush_and_traps(): return seen_neighbors = [ p for p in Map().potential_players(self) if self.can_see(p) and p != self ] free_neighbors = [ p for p in seen_neighbors if p.current_area == self.current_area and not p.busy ] potential_danger = sum([p.dangerosity for p in seen_neighbors]) actual_danger = sum([p.dangerosity for p in free_neighbors]) if potential_danger > self.dangerosity and potential_danger > self.courage: Narrator().add([ self.name, 'sees', format_list([p.name for p in seen_neighbors]) ]) self.flee(filtered_areas=[area]) elif actual_danger > self.dangerosity and actual_danger > self.courage: Narrator().add([ self.name, 'sees', format_list([p.name for p in free_neighbors]) ]) self.flee(filtered_areas=[area]) elif actual_danger > 0: # enemy present -> fight them Narrator().cut() self.attack_at_random() elif potential_danger > 0 and actual_danger == 0: # enemy busy but incoming if self.dangerosity > potential_danger: # attack before the other(s) arrive Narrator().cut() self.attack_at_random() else: # loot and go/get your load and hit the road Narrator().add([ self.name, 'avoids', format_list([p.name for p in seen_neighbors]) ]) self.loot(take_a_break=False) self.flee(filtered_areas=[area]) else: # servez-vous self.loot() if len(Narrator().current_sentence) == 0: Narrator().add([ self.name, f'potential_danger={potential_danger}', f'actual_danger={actual_danger}', f'dangerosity={self.dangerosity}', f'courage={self.courage}', ])
def choose_between(self, player_a, player_b): p_a = self._ally_potential(player_a) p_b = self._ally_potential(player_b) if p_a > p_b and p_a > 0: Narrator().add([self.name, 'sides with', player_a.name]) player_a.relationship(self).add_trust(0.25) self.new_ally(player_a) elif p_b > 0: Narrator().add([self.name, 'sides with', player_b.name]) player_b.relationship(self).add_trust(0.25) self.new_ally(player_b) else: Narrator().add([self.name, 'chooses to go on', self.his, 'own']) if self.can_flee(): self.flee()
def rest(self, stock=False): if self.current_area.name != START_AREA: self.stealth += random() * (1 - self.stealth) Narrator().add([self.name, 'hides', self.current_area.at], stock=stock) self.take_a_break() wounds = self.wounds if BLEEDING in wounds: self.patch(BLEEDING, stock=stock) elif len(wounds): self.patch(choice(wounds), stock=stock) else: self.add_health(max(self.energy, random()) * (self.max_health - self.health)) self.add_energy(max(self.sleep, random()) * (1 - self.energy)) Narrator().add([self.name, 'rests', self.current_area.at], stock=stock)
def trigger_event(self): possible_events = [cls for cls in self.event_classes if cls.can_happen()] if not len(possible_events): return self._event_gauge = 0 event = choice(possible_events)() areas = format_list(list(set([f'the {area.name}' for area in event.areas]))) Narrator().new(['EVENT:', event.name.upper(), f'at {areas}']) Narrator().new(['EVENT:', event.name.upper(), f'at {event.areas}']) Narrator().cut() event.trigger() Narrator().new(' ') Narrator().cut() self._players_at_last_event = len(self.alive_players) self._time_since_last_event = 0
def loot_bag(self, take_a_break=True): item = Map().pick_bag(self.current_area) if item is None: return self.loot(take_a_break=take_a_break) Narrator().add( [self.name, 'picks up', item.long_name, self.current_area.at]) self.get_item(item)
def upkeep(self): self.destination = None self.stop_running() dehydratation = 0.5 if BURN_WOUND in self.status else 0.3 self._water -= dehydratation self.water_upkeep() energy_upkeep = -random() * 0.1 # loses energy while being awake sleep_upkeep = max(random(), random()) * 0.1 food_upkeep = max(random(), random()) * 0.2 if self.thirst > 1: self.status.append(THIRSTY) energy_upkeep *= self.thirst if SLEEPING in self.status: self.status.remove(SLEEPING) sleep_upkeep = 0 energy_upkeep = 0 food_upkeep /= 2 energy_upkeep += min(sleep_upkeep, self.sleep) # compensates with sleep reserves energy_upkeep += min(food_upkeep, self.stomach) # compensates with food reserves self.add_sleep(-sleep_upkeep * 2) # consumes sleep reserves self.consume_nutriments(-food_upkeep) # consumes food reserves self.add_energy(energy_upkeep + sleep_upkeep + food_upkeep) if BLEEDING in self.status: if self.be_damaged(max(0.05, self.health / 5)): Narrator().add([self.name, 'bleeds', 'to death']) for poison in self.active_poisons: poison.upkeep(self) self._rage = 0
def set_up_ambush(self): self.stealth += (random() / 2 + 0.5) * (1 - self.stealth) self.end_ambush() Map().add_ambusher(self, self) self._ambush = True Narrator().add( [self.name, 'sets up', 'an ambush', self.current_area.at])
def pillage(self, stuff): if len([p for p in Context().alive_players if p.is_alive]) == 1: return if Map().players_count(self) > 1: return looted = [] for item in stuff: if item not in Map().loot(self.current_area): continue if isinstance(item, Weapon): if item.damage_mult > self.weapon.damage_mult: looted.append(item) Map().remove_loot(item, self.current_area) else: looted.append(item) Map().remove_loot(item, self.current_area) if not len(looted): return Narrator().add( [self.name, 'loots', format_list([e.long_name for e in looted])]) for item in looted: if isinstance(item, Weapon): self.get_weapon(item) else: self.get_item(item)
def hit(self, target: LivingEntity, mult=1) -> bool: if SLEEPING in target.status: target.status.remove(SLEEPING) mult *= 2 if self.energy < 0.1: mult /= 2 self._rage = -1 else: self.add_energy(-0.1) hit_chance = mult if mult > 1 else 0.6 * mult if ARM_WOUND in self.status: hit_chance -= 0.2 if TRAPPED in target.status: hit_chance += 0.3 if random() < hit_chance: self._rage += 0.1 if self.weapon.poison is not None and\ self.weapon.poison.long_name not in [p.long_name for p in target.active_poisons]: if random() > 0.3: target.add_poison(copy(self.weapon.poison)) self.weapon.poison.amount -= 1 if self.weapon.poison.amount == 0: self.weapon.poison = None return target.be_damaged(self.damage() * mult, weapon=self.weapon.name, attacker_name=self.name) else: # Miss self._rage -= 0.1 Narrator().stock([self.name, 'misses']) return False
def flee(self, area=None, panic=False, drop_verb='drops', stock=False, filtered_areas=None): if area is None: if filtered_areas is None: filtered_areas = [] filtered_areas = [*Context().forbidden_areas, *filtered_areas] self.status.append(FLEEING) if panic and random() > self.courage + 0.5: self.drop_weapon(verbose=True, drop_verb=drop_verb) available_areas = [ area for area in Map().areas if area not in filtered_areas ] available_areas.sort(key=lambda x: self._flee_value(x)) if available_areas: area = available_areas[-1] out = None self.reveal() if area is not None: out = self.go_to(area) if out is None: self.hide(panic=panic, stock=stock) else: Narrator().add([self.name, f'flees {out.to}'], stock=stock) self.check_for_ambush_and_traps()
def ask_to_ally(self, player): self.reveal() if random() < player.want_to_ally(self): Narrator().new([ self.name, 'proposes', 'an alliance to', player.name, 'and', player.he, 'accepts!' ]) self.new_ally(player) player.new_ally(self) else: Narrator().new([ self.name, 'proposes', 'an alliance to', player.name, 'but', player.he, 'refuses' ]) self.relationship(player).add_friendship(-0.5) self.relationship(player).add_trust(-2) self.relationship(player).add_trust(1) # trust = 0
def eat(self, food: Food, quantifier='', verbose=True): if verbose: Narrator().add([ self.name, 'eats', quantifier, food.name, self.current_area.at ]) self.consume_nutriments(food.value) if food.is_poisonous: self._poisons.append(copy(food.poison)) while food in self.equipment: self.remove_item(food) if verbose: Narrator().new([ f'The {food.name}', 'are' if food.name[-1] == 's' else 'is', 'poisonous!' ]) Narrator().cut() return 'poison'
def loot_weapon(self, weapon: Optional[Union[Weapon, List[Weapon]]] = None): weapons: List[Weapon] = [] if isinstance(weapon, Weapon): weapons.append(weapon) if isinstance(weapon, list): weapons = weapon if weapon is None: for _ in self.acting_players: weapons.append(Map().pick_weapon(self)) weapons = [w for w in weapons if w is not None] weapons.sort(key=lambda x: -x.damage_mult) self.acting_players.sort(key=lambda x: -x.weapon.damage_mult) empty_handed: List[Player] = [] for player in self.acting_players: picked = False for weapon in weapons: if weapon.damage_mult > player.weapon.damage_mult: if weapon.name == player.weapon.name: player.weapon.long_name.replace('\'s', '\'s old') Narrator().add([ player.name, 'picks up', f'a better {weapon.name}', self.current_area.at ]) else: Narrator().add([ player.name, 'picks up', weapon.long_name, self.current_area.at ]) if player.weapon != HANDS: weapons.append(player.weapon) player.get_weapon(weapon) weapons.remove(weapon) picked = True break if not picked: empty_handed.append(player) if len(empty_handed): verb = 'try' if len(empty_handed) > 1 else 'tries' Narrator().add([ player_names(empty_handed), verb, 'to find weapons', self.current_area.at, 'but can\'t find anything good' ])
def upkeep(self, player: LivingEntity): if self.amount == 0: player.remove_poison(self) return self.amount -= 1 live = player.is_alive player.add_health(-self.damage) if live and not player.is_alive: Narrator().new([player.name, 'succumbs', 'to', self.long_name])
def set_up_ambush(self): self.stealth += (random() / 2 + 0.5) * (1 - self.stealth) if AMBUSH not in self.status: self.status.append(AMBUSH) Map().add_ambusher(self, self) Narrator().add( [self.name, 'sets up', 'an ambush', self.current_area.at]) else: self._waiting += 1 if self._waiting < 2: Narrator().add( [self.name, 'keeps', 'hiding', self.current_area.at]) else: Narrator().add([ self.name, 'gets', 'tired of hiding', self.current_area.at ]) self.end_ambush() self.pursue()
def death(self, dead_player): try: self.map.remove_player(dead_player) except ValueError as e: Narrator().tell() raise ValueError( f'{dead_player.name} has {dead_player.health}hp, is_alive={dead_player.is_alive}, ' f'is_in_players={dead_player in self.alive_players}' ) from e
def drop_weapon(self, verbose=True, drop_verb='drops'): if self.weapon != HANDS: if verbose: Narrator().add([ self.name, drop_verb, f'{self.his} {self.weapon.name}', self.current_area.at ]) Map().add_loot(self.weapon, self.current_area) self._weapon = HANDS
def hide(self, panic=False, stock=False): if panic: Narrator().add([ player_names(self.acting_players), 'hides', self.current_area.at ], stock=stock) return for player in self.acting_players: player.hide(stock=stock)
def build_trap(player, trap_class: Type[Trap]): player.reveal() if not trap_class.can_be_built(player): return for ingredient in trap_class.ingredients: item = [i for i in player.equipment if i.name == ingredient][0] player.remove_item(item) trap = trap_class(player, random() / 2 + 0.5) Narrator().add( [player.name, 'builds', 'a', trap.name, f'at {player.current_area}'])
def dine(self): self.take_a_break() food_owner: Dict[Food, Player] = {} for player in self.acting_players: for e in player.equipment: if isinstance(e, Food): food_owner[e] = player if not len(food_owner): Narrator().add([self.name, 'does not have', 'anything to eat']) else: food = list(food_owner.keys()) food.sort(key=lambda x: x.value) diner = {p: [] for p in self.acting_players} poison = {p: None for p in self.acting_players} eaters = copy(self.acting_players) eaters.sort(key=lambda x: -x.hunger) while len(eaters) and len(food): eaters_this_round = copy(eaters) for eater in eaters_this_round: try: meal = food.pop() except IndexError: break food_owner[meal].remove_item(meal) diner[eater].append(meal.name) if eater.eat(meal, verbose=False) == 'poison': poison[eater] = meal eaters.remove(eater) elif not eater.hunger: eaters.remove(eater) for player in self.acting_players: if len(diner[player]): Narrator().add([ player.name, 'eats', player.his, format_list(diner[player]), self.current_area.at ]) if poison[player] is not None: Narrator().new( [f'the {poison[player].name}', 'is', 'poisonous!']) Narrator().cut() player.consume_antidote()