Example #1
0
def parse_file(args):
    """Reads a file specified by arguments, and returns an entry list"""

    entries = []
    started = False
    encounter_sets = []
    with args.file as file:
        # If searching for encounters, divert and find start/end first.
        if args.search_fights:
            encounter_sets = e_tools.find_fights_in_file(file)
            # If all we want to do is list encounters, stop here and give to the user.
            if args.search_fights < 0:
                return [f'{i + 1}. {" ".join(e_info)}' for i, e_info in enumerate(encounter_sets)]
            elif args.search_fights > len(encounter_sets):
                raise Exception('Selected fight index not in selected ACT log.')

        start_time, end_time = e_tools.choose_fight_times(args, encounter_sets)
        # Scan the file until the start timestamp
        for line in file:

            if not started and start_time != line[14:26]:
                continue

            if end_time == line[14:26]:
                break

            # We're at the start of the encounter now.
            if not started:
                started = True
                last_ability_time = e_tools.parse_event_time(line)

            # We're looking for enemy casts
            # These lines will start with 21 or 22, and have an NPC ID (400#####)
            # If this isn't one, skip the line
            if not (line[0:2] == '21' or line[0:2] == '22') or not line[37:40] == '400':
                continue

            # At this point, we have a combat line for the timeline.
            line_fields = line.split('|')
            entry = {
                'time': e_tools.parse_event_time(line),
                'combatant': line_fields[3],
                'ability_id': line_fields[4],
                'ability_name': line_fields[5],
            }

            # Unknown abilities should be hidden sync lines by default.
            if line_fields[5].startswith('Unknown_'):
                entry['ability_name'] = '--sync--'

            entries.append(entry)

    if not started:
        raise Exception('Fight start not found')

    return entries, last_ability_time
Example #2
0
def run_file(args, timelist):
    """Runs a timeline against a specified file"""
    state = {
        "file": True,
        "last_entry": False,
        "last_sync_position": 0,
        "last_jump": 0,
        "branch": 1,
        "timeline_stopped": True,
    }
    started = False
    encounter_sets = []

    with args.file as file:
        # If searching for encounters, divert and find start/end first.
        if args.search_fights:
            encounter_sets = e_tools.find_fights_in_file(file)
            # If all we want to do is list encounters, stop here and give to the user.
            if args.search_fights == -1:
                return [
                    f'{i + 1}. {" ".join(e_info)}'
                    for i, e_info in enumerate(encounter_sets)
                ]
            elif args.search_fights > len(
                    encounter_sets) or args.search_fights < -1:
                raise Exception(
                    "Selected fight index not in selected ACT log.")

        start_time, end_time = e_tools.choose_fight_times(args, encounter_sets)
        # Scan the file until the start timestamp
        for line in file:

            # Scan the file until the start timestamp
            if not started and line[14:26] != start_time:
                continue

            if line[14:26] == end_time:
                break

            # We're at the start of the encounter now.
            if not started:
                started = True
                state["last_sync_timestamp"] = e_tools.parse_event_time(line)

            state = check_event(line, timelist, state)
    if not started:
        raise Exception("Fight start not found")
Example #3
0
def check_event(event, timelist, state):
    # Get amount of time that's passed since last sync point
    if state["timeline_stopped"]:
        time_progress_seconds = 0
    else:
        event_time = e_tools.parse_event_time(event)
        if event_time > state["last_sync_timestamp"]:
            time_progress_delta = e_tools.parse_event_time(event) - state["last_sync_timestamp"]
            time_progress_seconds = (
                time_progress_delta.seconds + time_progress_delta.microseconds / 1000000
            )
        else:
            # battle logs have out of order parsed times because their
            # microseconds are zero.  Just pretend this is 0.
            time_progress_seconds = 0

    # Get where the timeline would be at this time
    timeline_position = state["last_sync_position"] + time_progress_seconds

    # Search timelist for matches
    for entry in timelist:
        match = test_match(event, entry)
        if match and timeline_position >= entry["start"] and timeline_position <= entry["end"]:
            # Flag with current branch
            if state["last_entry"] == entry and time_progress_seconds < 2.5:
                continue

            entry["branch"] = state["branch"]
            state["last_entry"] = entry

            # Check the timeline drift for anomolous timings
            drift = entry["time"] - timeline_position
            print(
                "{:.3f}: Matched entry: {} {} ({:+.3f}s)".format(
                    timeline_position, entry["time"], entry["label"], drift
                )
            )

            if time_progress_seconds > 30:
                print("    Warning: {:.3f}s since last sync".format(time_progress_seconds))

            # Find any syncs before this one that were passed without syncing
            if not state["timeline_stopped"]:
                for other_entry in timelist:
                    if (
                        "regex" in other_entry
                        and other_entry["time"] > state["last_jump"]
                        and other_entry["time"] < entry["time"]
                        and other_entry["branch"] < entry["branch"]
                    ):
                        if "last" in other_entry and drift < 999:
                            print(
                                "    Missed sync: {} at {} (last seen at {})".format(
                                    other_entry["label"], other_entry["time"], other_entry["last"]
                                )
                            )
                        elif drift < 999:
                            print(
                                "    Missed sync: {} at {}".format(
                                    other_entry["label"], other_entry["time"]
                                )
                            )
                        # If this is a sync from a large window, ignore missed syncs
                        other_entry["branch"] = state["branch"]

            # Carry out the sync to make this the new baseline position
            if state["timeline_stopped"]:
                state["last_jump"] = entry["time"]
            state["timeline_stopped"] = False
            state["last_sync_timestamp"] = e_tools.parse_event_time(event)

            # Jump to new time, stopping if necessary
            if "jump" in entry:
                if entry["jump"] == 0:
                    print("---!Resetting encounter from {}!---".format(state["last_sync_position"]))
                    state["timeline_stopped"] = True
                else:
                    print("    Jumping to {:.3f}".format(entry["jump"]))
                state["last_jump"] = entry["jump"]
                state["last_sync_position"] = entry["jump"]
                state["branch"] += 1
            else:
                state["last_sync_position"] = entry["time"]

        # Record last seen data if it matches but outside window
        elif match:
            entry["last"] = timeline_position

    return state
Example #4
0
def parse_file(args):
    """Reads a file specified by arguments, and returns an entry list"""

    entries = []
    started = False
    encounter_sets = []
    with args.file as file:
        # If searching for encounters, divert and find start/end first.
        if args.search_fights:
            encounter_sets = e_tools.find_fights_in_file(file)
            # If all we want to do is list encounters, stop here and give to the user.
            if args.search_fights < 0:
                return [
                    f'{i + 1}. {" ".join(e_info)}'
                    for i, e_info in enumerate(encounter_sets)
                ]
            elif args.search_fights > len(encounter_sets):
                raise Exception(
                    "Selected fight index not in selected ACT log.")

        start_time, end_time = e_tools.choose_fight_times(args, encounter_sets)
        # Scan the file until the start timestamp
        for line in file:

            if not started and start_time != line[14:26]:
                continue

            if end_time == line[14:26]:
                break

            # We're at the start of the encounter now.
            if not started:
                started = True
                last_ability_time = e_tools.parse_event_time(line)

            # We're looking for enemy casts or enemies becoming targetable/untargetable.
            # These lines will start with 21, 22, or 34, and have an NPC ID (400#####)
            # If this isn't one, skip the line

            if not (line[0:2] in ["21", "22", "34"
                                  ]) or not line[37:40] == "400":
                continue
            line_fields = line.split("|")
            # We aren't including targetable lines unless the user explicitly says to.
            if line[0:2] == "34" and not line_fields[
                    3] in args.include_targetable:
                continue

            # At this point, we have a combat line for the timeline.
            entry = make_entry({
                "line_type": line_fields[0],
                "time": e_tools.parse_event_time(line),
                "combatant": line_fields[3],
            })
            if line[0:2] in ["21", "22"]:
                entry["ability_id"] = line_fields[4]
                entry["ability_name"] = line_fields[5]

            # Unknown abilities should be hidden sync lines by default.
            if line_fields[5].startswith("Unknown_"):
                entry["ability_name"] = "--sync--"
            else:
                entry["targetable"] = ("--targetable--" if line_fields[6]
                                       == "01" else "--untargetable--")
            entries.append(entry)

    if not started:
        raise Exception("Fight start not found")

    return entries, last_ability_time
Example #5
0
def parse_file(args):
    """Reads a file specified by arguments, and returns an entry list"""

    entries = []
    started = False
    encounter_sets = []
    with args.file as file:
        # If searching for encounters, divert and find start/end first.
        if args.search_fights:
            encounter_sets = e_tools.find_fights_in_file(file)
            # If all we want to do is list encounters, stop here and give to the user.
            if args.search_fights < 0:
                return e_tools.list_fights_in_file(args, encounter_sets)
        start_time, end_time = e_tools.choose_fight_times(args, encounter_sets)
        # Scan the file until the start timestamp
        for line in file:

            if not started and start_time != line[14:26]:
                continue

            if end_time == line[14:26]:
                break

            # We're at the start of the encounter now.
            if not started:
                started = True
                last_ability_time = e_tools.parse_event_time(line)

            # We cull non-useful lines before potentially more expensive operations.
            if not line[0:2] in ["00", "21", "22", "34"]:
                continue

            line_fields = line.split("|")

            # If it's a zone seal, we want to make a special entry.
            if e_tools.is_zone_seal(line_fields):
                entry = make_entry(
                    {
                        "line_type": "zone_seal",
                        "time": e_tools.parse_event_time(line),
                        "zone_message": line_fields[4].split(" will be sealed off")[0],
                    }
                )
                entries.append(entry)
                continue

            # We're looking for enemy casts or enemies becoming targetable/untargetable.
            # These lines will start with 21, 22, or 34, and have an NPC ID (400#####)
            # If none of these apply, skip the line

            if not line[0:2] in ["21", "22", "34"] or not line[37:40] == "400":
                continue

            # We aren't including targetable lines unless the user explicitly says to.
            if line[0:2] == "34" and not line_fields[3] in args.include_targetable:
                continue
            # At this point, we have a combat line for the timeline.
            entry = make_entry(
                {
                    "line_type": line_fields[0],
                    "time": e_tools.parse_event_time(line),
                    "combatant": line_fields[3],
                }
            )
            if line[0:2] in ["21", "22"]:
                entry["ability_id"] = line_fields[4]
                entry["ability_name"] = line_fields[5]

            # Unknown abilities should be hidden sync lines by default.
            if line_fields[5].startswith(("Unknown_", "unknown_")):
                entry["ability_name"] = "--sync--"
            else:
                entry["targetable"] = (
                    "--targetable--" if line_fields[6] == "01" else "--untargetable--"
                )
            entries.append(entry)

    if not started:
        raise Exception("Fight start not found")

    return entries, last_ability_time