DFHack / dfhack

Memory hacking library for Dwarf Fortress and a set of tools that use it
Other
1.87k stars 475 forks source link

Feature Req: New Sortable/Filterable Work Order Display Window #3555

Open wbschultz opened 1 year ago

wbschultz commented 1 year ago

I'm a relatively new player on the steam version, but even with just a meager fort, I find it painful to check if I'm regularly producing certain goods, if my output is high enough, etc. I would really like a way to view all existing work orders in an easier-to-use window, with some sort of sorting and filters available.

User story: As a user, I want to have access to view, sort, and filter existing work orders so that I can easily monitor which jobs are recurring, output levels, etc.

The ability to tag and filter by tag, or organize orders into folders within this special display would be incredible so that I could see orders by various industries.

myk002 commented 1 year ago

thoughts from u/wheatleygone on Reddit:

It might be simpler if rather than allowing custom user-created folders, work orders were just categorized by the workshop they're performed at. The relative position of a "Cut ten gems" order to a "Make three wooden buckets" order does not matter, because they're performed at different workshops. It doesn't meaningfully change anything to separate the two out into different lists visually. Being able to collapse all of my mason orders or just view my glass forge orders would still be pretty great for keeping things organized, compared to the way it is now. It would also be quite intuitive since when you add work orders, the list is categorized by workshop.

Off the top of my head, the only thing that would complicate this is if there are multiple vastly different workshops that partially overlap in tasks, but I don't believe this is the case? There's overlap between the forges and their magma variants, and mill/quern, but those are cases where all of the tasks are shared so it's perfectly convenient for them to share a category.

I'm sure there are probably issues I'm not anticipating, but I think this could allow for a much better work order screen that sidesteps the issues with the list ordering.

Halifay commented 1 year ago

Here is the code to sort by material or job type. It was written for orders.cpp file.

static bool compare_type(df::manager_order *a, df::manager_order *b)
{
    // Goal: Sort orders to easily find them in the list and to see dupclicated orders.
    // Sorting by job types

    // Divide orders by frequency first
    if (a->frequency != b->frequency)
    {
        return compare_freq(a, b);
    }

    // Determine if only one order has reaction_name
    bool a_has_reaction_name = !a->reaction_name.empty();
    bool b_has_reaction_name = !b->reaction_name.empty();

    if (a_has_reaction_name != b_has_reaction_name)
    {
        return a_has_reaction_name;
    }
    else if (a_has_reaction_name && b_has_reaction_name)
    {
        if (a->reaction_name != b->reaction_name)
        {
            return a->reaction_name < b->reaction_name;
        }
    }

    // Compare job_type
    if (enum_item_key(a->job_type) != enum_item_key(b->job_type))
    {
        return enum_item_key(a->job_type) < enum_item_key(b->job_type);
    }

    // Compare item subtype
    bool a_has_item_subtype = (a->item_subtype != -1);
    bool b_has_item_subtype = (b->item_subtype != -1);

    if (a_has_item_subtype != b_has_item_subtype)
    {
        return a_has_item_subtype;
    }
    else if (a_has_item_subtype && b_has_item_subtype)
    {
        if (a->item_subtype != b->item_subtype)
        {
            return a->item_subtype < b->item_subtype;
        }
    }

    // By default orders are the same
    return false;
}

static command_result orders_sort_type_command(color_ostream & out)
{
    CoreSuspender suspend;
    if (!std::is_sorted(world->manager_orders.begin(),
                        world->manager_orders.end(),
                        compare_type))
    {
        std::stable_sort(world->manager_orders.begin(),
                         world->manager_orders.end(),
                         compare_type);
        out << "Manager orders are sorted by job type." << std::endl;
    }

    return CR_OK;
}

static bool compare_material(df::manager_order *a, df::manager_order *b)
{
    // Goal: Sort orders to easily find them in the list and to see dupclicated orders.
    // Sorting by materials

    // Divide orders by frequency first
    if (a->frequency != b->frequency)
    {
        return compare_freq(a, b);
    }

    // Determine if only one of the orders has mat_type
    bool a_has_material = (a->mat_type != -1 || a->mat_index != -1);
    bool b_has_material = (b->mat_type != -1 || b->mat_index != -1);

    if (a_has_material != b_has_material)
    {
        return a_has_material;
    }
    else if (a_has_material && b_has_material)
    {
        // Compare mat_type using MaterialInfo
        if (MaterialInfo(a).getToken() != MaterialInfo(b).getToken())
        {
            return MaterialInfo(a).getToken() < MaterialInfo(b).getToken();
        }
    }

    // Determine if only one order has material_category
    bool a_has_material_category = (a->material_category.whole != 0);
    bool b_has_material_category = (b->material_category.whole != 0);

    if (a_has_material_category != b_has_material_category)
    {
        return a_has_material_category;
    }
    else if (a_has_material_category && b_has_material_category)
    {
        std::vector<std::string> a_mats, b_mats;
        bitfield_to_string(&a_mats, a->material_category);
        bitfield_to_string(&b_mats, b->material_category);

        // Checking that mats are not empty just in case
        if (!a_mats.empty() && !b_mats.empty() && a_mats[0] != b_mats[0])
        {
            return a_mats[0] < b_mats[0];
        }
    }

    // Fall back to material sort
    return compare_type(a, b);
}

static command_result orders_sort_material_command(color_ostream & out)
{
    CoreSuspender suspend;
    if (!std::is_sorted(world->manager_orders.begin(),
                        world->manager_orders.end(),
                        compare_material))
    {
        std::stable_sort(world->manager_orders.begin(),
                         world->manager_orders.end(),
                         compare_material);
        out << "Manager orders are sorted by material." << std::endl;
    }

    return CR_OK;
}