Exemple #1
0
async def print_map(team: str,
                    options: Sequence[str] = ("w", "d", "s", "a", "m", "e"),
                    show_hidden: bool = False) -> DiscordAction:
    """
    Prints the map from the perspective of one submarine, or all if team is None.
    """
    subs = []
    max_options = ["w", "d", "s", "t", "n", "a", "j", "m", "e"]
    if options is True:
        options = MAX_OPTIONS
    options = list(filter(lambda v: v in max_options, options))
    if team is None:
        subs = get_sub_objects()
    else:
        sub = get_sub(team)
        if sub is None:
            return FAIL_REACT
        else:
            subs = [sub]
    map_string, map_arr = draw_map(subs, list(options), show_hidden)
    map_json = json.dumps(map_arr)
    async with httpx.AsyncClient() as client:
        url = MAP_DOMAIN + "/api/map/"
        res = await client.post(url,
                                data={
                                    "map": map_string,
                                    "key": MAP_TOKEN,
                                    "names": map_json
                                })
        if res.status_code == 200:
            final_url = MAP_DOMAIN + res.json()['url']
            return Message(f"The map is visible here: {final_url}")
    return FAIL_REACT
Exemple #2
0
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_sub_objects
    from ALTANTIS.npcs.npc import get_npc_objects
    for sub in get_sub_objects():
        if sub._name in sub_exclusions:
            continue

        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 npc in get_npc_objects():
        if npc.id in npc_exclusions:
            continue

        npc_pos = npc.get_position()
        npc_dist = diagonal_distance(pos, npc_pos)
        damage = power - npc_dist

        if damage > 0:
            await npc.send_message(f"Explosion in {pos}!", "captain")
            npc.damage(damage)
Exemple #3
0
    def hits(self, x: int, y: int) -> Dict[str, List[Entity]]:
        # Returns a list of indirect and direct hits.
        indirect: List[Entity] = []
        direct: List[Entity] = []
        for sub in get_sub_objects():
            pos = sub.movement.get_position()
            distance = diagonal_distance(pos, (x, y))
            if distance == 0:
                direct.append(sub)
            elif distance == 1:
                indirect.append(sub)

        for npc in get_npc_objects():
            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}
Exemple #4
0
    async def broadcast(self, content : str) -> str:
        if self.last_comms + COMMS_COOLDOWN > now():
            return f"The comms system is still cooling down! (Requires {int(self.last_comms + COMMS_COOLDOWN - now())}s more.)"

        if self.sub.power.get_power("comms") == 0:
            return "Cannot use comms system that isn't powered!"
        
        my_pos = self.sub.movement.get_position()
        for sub in get_sub_objects():
            if sub._name == self.sub._name:
                continue

            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 npc in get_npc_objects():
            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 "Transmitted message."
Exemple #5
0
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(sub):
        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[Submarine] = list(filter(is_active_sub, get_sub_objects()))
    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 sub in subsubset:
        if sub.power.total_power == 1:
            subname = sub._name
            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 sub in subsubset:
        power_message = sub.power.apply_power_schedule()
        if power_message:
            subname = sub._name
            power_message = f"{power_message}\n"
            submessages[subname]["captain"] += power_message
            submessages[subname]["engineer"] += power_message

    # Weapons
    for sub in subsubset:
        weapons_message = sub.weapons.weaponry_tick()
        if weapons_message:
            weapons_message = f"{weapons_message}\n"
            submessages[sub._name]["captain"] += weapons_message

    # NPCs
    await npc_tick()
    # Map
    map_tick()

    # The crane
    for sub in subsubset:
        crane_message = await sub.inventory.crane_tick()
        if crane_message:
            crane_message = f"{crane_message}\n"
            submessages[sub._name]["scientist"] += crane_message

    # Movement, trade and puzzles
    for sub in subsubset:
        move_message, trade_messages = await sub.movement.movement_tick()
        if move_message:
            move_message = f"{move_message}\n"
            submessages[sub._name]["captain"] += move_message
        for target in trade_messages:
            submessages[target]["captain"] += trade_messages[target] + "\n"

    # Scanning (as we enter a new square only)
    for sub in subsubset:
        scan_message = sub.scan.scan_string()
        if scan_message != "":
            subname = sub._name
            submessages[subname]["captain"] += scan_message
            submessages[subname]["scientist"] += scan_message

    # Postponed events
    for sub in subsubset:
        await sub.upgrades.postponed_tick()

    # Damage
    for sub in get_sub_objects():
        damage_message = await sub.power.damage_tick()
        if damage_message:
            subname = sub._name
            damage_message = f"{damage_message}\n"
            submessages[subname]["captain"] += damage_message
            submessages[subname]["engineer"] += damage_message
            submessages[subname]["scientist"] += damage_message

    for sub in get_sub_objects():
        messages = submessages[sub._name]
        if messages["captain"] == "":
            if sub._name not in map(lambda s: s._name, subsubset):
                if sub.power.total_power <= 0:
                    messages[
                        "captain"] = "Your submarine is **dead** so nothing happened.\n"
                else:
                    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()
Exemple #6
0
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))
            sq = get_square(x, y)
            if sq is None:
                continue
            event = sq.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 sub in get_sub_objects():
        if sub._name in sub_exclusions:
            continue

        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 npc in get_npc_objects():
        if npc.id in npc_exclusions:
            continue
        
        npc_pos = npc.get_position()
        npc_dist = diagonal_distance(pos, npc_pos)

        if npc_dist > dist:
            continue

        event = npc.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