async def broadcast(self, content: str): if self.last_comms + COMMS_COOLDOWN > now(): return False my_pos = self.sub.movement.get_position() for subname in get_subs(): if subname == self.sub._name: continue sub = get_sub(subname) dist = diagonal_distance(my_pos, sub.movement.get_position()) garbled = self.garble(content, dist) if garbled is not None: await sub.send_message( f"**Message received from {self.sub.name()}**:\n`{garbled}`\n**END MESSAGE**", "captain") for npcid in get_npcs(): npc = get_npc(npcid) dist = diagonal_distance(my_pos, npc.get_position()) garbled = self.garble(content, dist) if garbled is not None: await npc.send_message( f"**Message received from {self.sub.name()}**:\n`{garbled}`\n**END MESSAGE**", "") self.last_comms = now() return True
def hits(self, x: int, y: int) -> Dict[str, List[Entity]]: # Returns a list of indirect and direct hits. indirect = [] direct = [] for subname in get_subs(): sub = get_sub(subname) pos = sub.movement.get_position() distance = diagonal_distance(pos, (x, y)) if distance == 0: direct.append(sub) elif distance == 1: indirect.append(sub) for npcid in get_npcs(): npc = get_npc(npcid) pos = npc.get_position() distance = diagonal_distance(pos, (x, y)) if distance == 0: direct.append(npc) elif distance == 1: indirect.append(npc) shuffle(indirect) shuffle(direct) return {"indirect": indirect, "direct": direct}
def get_team(channel : discord.TextChannel) -> Optional[str]: """ Gets the name of the category channel of the channel the message was sent in. """ category_channel = bot.get_channel(channel.category_id) if category_channel: team = category_channel.name.lower() if team in get_subs(): return team return None
async def explode(pos: Tuple[int, int], power: int, sub_exclusions: List[str] = [], npc_exclusions: List[int] = []): """ Makes an explosion in pos, dealing power damage to the centre square, power-1 to the surrounding ones, power-2 to those that surround and so on. """ from ALTANTIS.subs.state import get_subs, get_sub from ALTANTIS.npcs.npc import get_npcs, get_npc for subname in get_subs(): if subname in sub_exclusions: continue sub = get_sub(subname) sub_pos = sub.movement.get_position() sub_dist = diagonal_distance(pos, sub_pos) damage = power - sub_dist if damage > 0: await sub.send_message(f"Explosion in {pos}!", "captain") sub.damage(damage) for npcid in get_npcs(): if npcid in npc_exclusions: continue npc_obj = get_npc(npcid) npc_pos = npc_obj.get_position() npc_dist = diagonal_distance(pos, npc_pos) damage = power - npc_dist if damage > 0: await npc_obj.send_message(f"Explosion in {pos}!", "captain") npc_obj.damage(damage)
async def perform_timestep(counter: int): """ Does all time-related stuff, including movement, power changes and so on. Called at a time interval, when allowed. """ global NO_SAVE NO_SAVE = True print(f"Running turn {counter}.") def is_active_sub(subname): sub = get_sub(subname) if not sub: return False return sub.power.activated() # Get all active subs. (Can you tell I'm a functional programmer?) # Note: we still collect all messages for all subs, as there are some # messages that inactive subs should receive. subsubset: List[str] = list(filter(is_active_sub, get_subs())) submessages: Dict[str, Dict[str, str]] = { i: { "engineer": "", "captain": "", "scientist": "" } for i in get_subs() } message_opening: str = f"---------**TURN {counter}**----------\n" # Emergency messaging for subname in subsubset: sub = get_sub(subname) if sub.power.total_power == 1: emergency_message = f"EMERGENCY!!! {random.choice(emergencies)}\n" submessages[subname]["captain"] += emergency_message submessages[subname]["scientist"] += emergency_message submessages[subname]["engineer"] += emergency_message # Power management for subname in subsubset: sub = get_sub(subname) power_message = sub.power.apply_power_schedule() if power_message: power_message = f"{power_message}\n" submessages[subname]["captain"] += power_message submessages[subname]["engineer"] += power_message # Weapons for subname in subsubset: sub = get_sub(subname) weapons_message = sub.weapons.weaponry_tick() if weapons_message: weapons_message = f"{weapons_message}\n" submessages[subname]["captain"] += weapons_message # NPCs await npc_tick() # Map map_tick() # The crane for subname in subsubset: sub = get_sub(subname) crane_message = await sub.inventory.crane_tick() if crane_message: crane_message = f"{crane_message}\n" submessages[subname]["scientist"] += crane_message # Movement, trade and puzzles for subname in subsubset: sub = get_sub(subname) move_message, trade_messages = await sub.movement.movement_tick() if move_message: move_message = f"{move_message}\n" submessages[subname]["captain"] += move_message for target in trade_messages: submessages[target]["captain"] += trade_messages[target] + "\n" # Scanning (as we enter a new square only) for subname in subsubset: sub = get_sub(subname) scan_message = sub.scan.scan_string() if scan_message != "": submessages[subname]["captain"] += scan_message submessages[subname]["scientist"] += scan_message # Postponed events for subname in subsubset: sub = get_sub(subname) await sub.upgrades.postponed_tick() # Damage for subname in get_subs(): sub = get_sub(subname) damage_message = await sub.power.damage_tick() if damage_message: damage_message = f"{damage_message}\n" submessages[subname]["captain"] += damage_message submessages[subname]["engineer"] += damage_message submessages[subname]["scientist"] += damage_message for subname in get_subs(): messages = submessages[subname] sub = get_sub(subname) if messages["captain"] == "": if subname not in subsubset: messages[ "captain"] = "Your submarine is deactivated so nothing happened.\n" else: messages[ "captain"] = "Your submarine is active, but there is nothing to notify you about.\n" await sub.send_message(f"{message_opening}{messages['captain'][:-1]}", "captain") if messages["engineer"] != "": await sub.send_message( f"{message_opening}{messages['engineer'][:-1]}", "engineer") if messages["scientist"] != "": await sub.send_message( f"{message_opening}{messages['scientist'][:-1]}", "scientist") NO_SAVE = False save_game()
def explore_submap(pos: Tuple[int, int], dist: int, sub_exclusions: Collection[str] = (), npc_exclusions: Collection[int] = (), with_distance: bool = False) -> List[str]: """ Explores the area centered around pos = (cx, cy) spanning distance dist. Returns all outward_broadcast events (as a list) formatted for output. Ignores any NPCs or subs with a name included in exclusions. """ events = [] (cx, cy) = pos # First, map squares. for i in range(-dist, dist + 1): x = cx + i if x < 0 or x >= X_LIMIT: continue for j in range(-dist, dist + 1): y = cy + j if y < 0 or y >= Y_LIMIT: continue this_dist = diagonal_distance((0, 0), (i, j)) event = get_square(x, y).outward_broadcast(dist - this_dist) if event != "": direction = determine_direction((cx, cy), (x, y)) if direction is None: event = f"{event} - in your current square!" else: distance_measure = "" if with_distance: distance_measure = f" at a distance of {this_dist} away" event = f"{event} - in direction {direction.upper()}{distance_measure}!" events.append(event) # Then, submarines. for subname in get_subs(): if subname in sub_exclusions: continue sub = get_sub(subname) sub_pos = sub.movement.get_position() sub_dist = diagonal_distance(pos, sub_pos) # If out of range, drop it. if sub_dist > dist: continue event = sub.scan.outward_broadcast(dist - sub_dist) direction = determine_direction(pos, sub_pos) if direction is None: event = f"{event} in your current square!" else: event = f"{event} in direction {direction.upper()}!" events.append(event) # Finally, NPCs. for npcid in get_npcs(): if npcid in npc_exclusions: continue npc_obj = get_npc(npcid) npc_pos = npc_obj.get_position() npc_dist = diagonal_distance(pos, npc_pos) if npc_dist > dist: continue event = npc_obj.outward_broadcast(dist - npc_dist) direction = determine_direction(pos, npc_pos) if direction is None: event = f"{event} in your current square!" else: event = f"{event} in direction {direction.upper()}!" events.append(event) return events