Merge branch 'master' of https://github.com/angavrilov/dfhack
						commit
						0e4df55364
					
				
											
												
													File diff suppressed because it is too large
													Load Diff
												
											
										
									
								@ -1 +1 @@
 | 
				
			|||||||
Subproject commit fcacacce7cf09cf70f011fea87b5be416da73457
 | 
					Subproject commit 4b2124957e282683480eaf05922e63c353364ec1
 | 
				
			||||||
@ -0,0 +1,704 @@
 | 
				
			|||||||
 | 
					#include <modules/Screen.h>
 | 
				
			||||||
 | 
					#include <modules/Translation.h>
 | 
				
			||||||
 | 
					#include <modules/Units.h>
 | 
				
			||||||
 | 
					#include <MiscUtils.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <VTableInterpose.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//#include "df/viewscreen_petst.h"
 | 
				
			||||||
 | 
					#include "df/viewscreen_storesst.h"
 | 
				
			||||||
 | 
					#include "df/viewscreen_tradegoodsst.h"
 | 
				
			||||||
 | 
					#include "df/viewscreen_unitlistst.h"
 | 
				
			||||||
 | 
					#include "df/interface_key.h"
 | 
				
			||||||
 | 
					#include "df/interfacest.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					using std::set;
 | 
				
			||||||
 | 
					using std::vector;
 | 
				
			||||||
 | 
					using std::string;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					using namespace DFHack;
 | 
				
			||||||
 | 
					using namespace df::enums;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					using df::global::gps;
 | 
				
			||||||
 | 
					using df::global::gview;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					Search Plugin
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					A plugin that adds a "Search" hotkey to some screens (Units, Trade and Stocks)
 | 
				
			||||||
 | 
					that allows filtering of the list items by a typed query.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Works by manipulating the vector(s) that the list based viewscreens use to store
 | 
				
			||||||
 | 
					their items. When a search is started the plugin saves the original vectors and
 | 
				
			||||||
 | 
					with each keystroke creates a new filtered vector off the saves for the screen
 | 
				
			||||||
 | 
					to use.
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void OutputString(int8_t color, int &x, int y, const std::string &text)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    Screen::paintString(Screen::Pen(' ', color, 0), x, y, text);
 | 
				
			||||||
 | 
					    x += text.length();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static bool is_live_screen(const df::viewscreen *screen)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    for (df::viewscreen *cur = &gview->view; cur; cur = cur->child)
 | 
				
			||||||
 | 
					        if (cur == screen)
 | 
				
			||||||
 | 
					            return true;
 | 
				
			||||||
 | 
					    return false;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// START: Base Search functionality
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Parent class that does most of the work
 | 
				
			||||||
 | 
					template <class S, class T, class V = void*>
 | 
				
			||||||
 | 
					class search_parent
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
					    // Called each time you enter or leave a searchable screen. Resets everything.
 | 
				
			||||||
 | 
					    void reset_all()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        reset_search();
 | 
				
			||||||
 | 
					        valid = false;
 | 
				
			||||||
 | 
					        sort_list1 = NULL;
 | 
				
			||||||
 | 
					        sort_list2 = NULL;
 | 
				
			||||||
 | 
					        viewscreen = NULL;
 | 
				
			||||||
 | 
					        select_key = 's';
 | 
				
			||||||
 | 
					        track_secondary_values = false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    bool reset_on_change()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        if (valid && is_live_screen(viewscreen))
 | 
				
			||||||
 | 
					            return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        reset_all();
 | 
				
			||||||
 | 
					        return true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // A new keystroke is received in a searchable screen
 | 
				
			||||||
 | 
					    virtual bool process_input(set<df::interface_key> *input)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        // If the page has two search options (Trade screen), only allow one to operate
 | 
				
			||||||
 | 
					        // at a time
 | 
				
			||||||
 | 
					        if (lock != NULL && lock != this)
 | 
				
			||||||
 | 
					            return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Allows custom preprocessing for each screen
 | 
				
			||||||
 | 
					        if (!should_check_input(input))
 | 
				
			||||||
 | 
					            return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        bool key_processed = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (entry_mode)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            // Query typing mode
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            df::interface_key last_token = *input->rbegin();
 | 
				
			||||||
 | 
					            if (last_token >= interface_key::STRING_A032 && last_token <= interface_key::STRING_A126)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                // Standard character
 | 
				
			||||||
 | 
					                search_string += last_token - ascii_to_enum_offset;
 | 
				
			||||||
 | 
					                do_search();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            else if (last_token == interface_key::STRING_A000)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                // Backspace
 | 
				
			||||||
 | 
					                if (search_string.length() > 0)
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    search_string.erase(search_string.length()-1);
 | 
				
			||||||
 | 
					                    do_search();
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            else if (input->count(interface_key::SELECT) || input->count(interface_key::LEAVESCREEN))
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                // ENTER or ESC: leave typing mode
 | 
				
			||||||
 | 
					                end_entry_mode();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            else if  (input->count(interface_key::CURSOR_UP) || input->count(interface_key::CURSOR_DOWN)
 | 
				
			||||||
 | 
					                || input->count(interface_key::CURSOR_LEFT) || input->count(interface_key::CURSOR_RIGHT))
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                // Arrow key pressed. Leave entry mode and allow screen to process key
 | 
				
			||||||
 | 
					                end_entry_mode();
 | 
				
			||||||
 | 
					                key_processed = false;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        // Not in query typing mode
 | 
				
			||||||
 | 
					        else if (input->count(select_token))
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            // Hotkey pressed, enter typing mode
 | 
				
			||||||
 | 
					            start_entry_mode();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        else if (input->count((df::interface_key) (select_token + shift_offset)))
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            // Shift + Hotkey pressed, clear query
 | 
				
			||||||
 | 
					            clear_search();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        else
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            // Not a key for us, pass it on to the screen
 | 
				
			||||||
 | 
					            key_processed = false;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return key_processed || entry_mode; // Only pass unrecognized keys down if not in typing mode
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Called if the search should be redone after the screen processes the keystroke.
 | 
				
			||||||
 | 
					    // Used by the stocks screen where changing categories should redo the search on
 | 
				
			||||||
 | 
					    // the new category.
 | 
				
			||||||
 | 
					    virtual void do_post_update_check()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        if (redo_search)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            do_search();
 | 
				
			||||||
 | 
					            redo_search = false;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    static search_parent<S,T,V> *lock;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					protected:
 | 
				
			||||||
 | 
					    const S *viewscreen;
 | 
				
			||||||
 | 
					    vector <T> saved_list1, reference_list;
 | 
				
			||||||
 | 
					    vector <V> saved_list2;
 | 
				
			||||||
 | 
					    vector <int> saved_indexes;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    bool valid;
 | 
				
			||||||
 | 
					    bool redo_search;
 | 
				
			||||||
 | 
					    bool track_secondary_values;
 | 
				
			||||||
 | 
					    string search_string;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    search_parent() : ascii_to_enum_offset(interface_key::STRING_A048 - '0'), shift_offset('A' - 'a')
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        reset_all();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    virtual void init(int *cursor_pos, vector <T> *sort_list1, vector <V> *sort_list2 = NULL, char select_key = 's')
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        this->cursor_pos = cursor_pos;
 | 
				
			||||||
 | 
					        this->sort_list1 = sort_list1;
 | 
				
			||||||
 | 
					        this->sort_list2 = sort_list2;
 | 
				
			||||||
 | 
					        this->select_key = select_key;
 | 
				
			||||||
 | 
					        select_token = (df::interface_key) (ascii_to_enum_offset + select_key);
 | 
				
			||||||
 | 
					        track_secondary_values = false;
 | 
				
			||||||
 | 
					        valid = true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    bool is_entry_mode()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return entry_mode;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void start_entry_mode()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        entry_mode = true;
 | 
				
			||||||
 | 
					        lock = this;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    void end_entry_mode()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        entry_mode = false;
 | 
				
			||||||
 | 
					        lock = NULL;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void reset_search()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        end_entry_mode();
 | 
				
			||||||
 | 
					        search_string = "";
 | 
				
			||||||
 | 
					        saved_list1.clear();
 | 
				
			||||||
 | 
					        saved_list2.clear();
 | 
				
			||||||
 | 
					        reference_list.clear();
 | 
				
			||||||
 | 
					        saved_indexes.clear();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // If the second vector is editable (i.e. Trade screen vector used for marking). then it may
 | 
				
			||||||
 | 
					    // have been edited while the list was filtered. We have to update the original unfiltered
 | 
				
			||||||
 | 
					    // list with these values. Uses a stored reference vector to determine if the list has been 
 | 
				
			||||||
 | 
					    // reordered after filtering, in which case indexes must be remapped.
 | 
				
			||||||
 | 
					    void update_secondary_values()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        if (sort_list2 != NULL && track_secondary_values)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            bool list_has_been_sorted = (sort_list1->size() == reference_list.size()
 | 
				
			||||||
 | 
					                && *sort_list1 != reference_list);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            for (size_t i = 0; i < saved_indexes.size(); i++)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                int adjusted_item_index = i;
 | 
				
			||||||
 | 
					                if (list_has_been_sorted)
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    for (size_t j = 0; j < sort_list1->size(); j++)
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        if ((*sort_list1)[j] == reference_list[i])
 | 
				
			||||||
 | 
					                        {
 | 
				
			||||||
 | 
					                            adjusted_item_index = j;
 | 
				
			||||||
 | 
					                            break;
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                saved_list2[saved_indexes[i]] = (*sort_list2)[adjusted_item_index];
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            saved_indexes.clear();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Store a copy of filtered list, used later to work out if filtered list has been sorted after filtering
 | 
				
			||||||
 | 
					    void store_reference_values()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        if (track_secondary_values)
 | 
				
			||||||
 | 
					            reference_list = *sort_list1;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Shortcut to clear the search immediately
 | 
				
			||||||
 | 
					    void clear_search()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        if (saved_list1.size() > 0)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            *sort_list1 = saved_list1;
 | 
				
			||||||
 | 
					            if (sort_list2 != NULL) 
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                update_secondary_values();
 | 
				
			||||||
 | 
					                *sort_list2 = saved_list2;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            saved_list1.clear();
 | 
				
			||||||
 | 
					            saved_list2.clear();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        store_reference_values();
 | 
				
			||||||
 | 
					        search_string = "";
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // The actual sort
 | 
				
			||||||
 | 
					    void do_search()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        if (search_string.length() == 0)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            clear_search();
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (saved_list1.size() == 0)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            // On first run, save the original list
 | 
				
			||||||
 | 
					            saved_list1 = *sort_list1;
 | 
				
			||||||
 | 
					            if (sort_list2 != NULL)
 | 
				
			||||||
 | 
					                saved_list2 = *sort_list2;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        else
 | 
				
			||||||
 | 
					            update_secondary_values(); // Update original list with any modified values
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Clear viewscreen vectors
 | 
				
			||||||
 | 
					        sort_list1->clear();
 | 
				
			||||||
 | 
					        if (sort_list2 != NULL)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            sort_list2->clear();
 | 
				
			||||||
 | 
					            saved_indexes.clear();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        string search_string_l = toLower(search_string);
 | 
				
			||||||
 | 
					        for (size_t i = 0; i < saved_list1.size(); i++ )
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            T element = saved_list1[i];
 | 
				
			||||||
 | 
					            string desc = toLower(get_element_description(element));
 | 
				
			||||||
 | 
					            if (desc.find(search_string_l) != string::npos)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                sort_list1->push_back(element);
 | 
				
			||||||
 | 
					                if (sort_list2 != NULL)
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    sort_list2->push_back(saved_list2[i]);
 | 
				
			||||||
 | 
					                    if (track_secondary_values)
 | 
				
			||||||
 | 
					                        saved_indexes.push_back(i); // Used to map filtered indexes back to original, if needed
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        store_reference_values(); //Keep a copy, in case user sorts new list
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        *cursor_pos = 0;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    virtual bool should_check_input(set<df::interface_key> *input)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Display hotkey message
 | 
				
			||||||
 | 
					    void print_search_option(int x, int y = -1) const
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        if (y == -1)
 | 
				
			||||||
 | 
					            y = gps->dimy - 2;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        OutputString((entry_mode) ? 4 : 12, x, y, string(1, select_key));
 | 
				
			||||||
 | 
					        OutputString((entry_mode) ? 10 : 15, x, y, ": Search");
 | 
				
			||||||
 | 
					        if (search_string.length() > 0 || entry_mode)
 | 
				
			||||||
 | 
					            OutputString(15, x, y, ": " + search_string);
 | 
				
			||||||
 | 
					        if (entry_mode)
 | 
				
			||||||
 | 
					            OutputString(10, x, y, "_");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    virtual string get_element_description(T element) const = 0;
 | 
				
			||||||
 | 
					    virtual void render () const = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					private:
 | 
				
			||||||
 | 
					    vector <T> *sort_list1;
 | 
				
			||||||
 | 
					    vector <V> *sort_list2;
 | 
				
			||||||
 | 
					    int *cursor_pos;
 | 
				
			||||||
 | 
					    char select_key;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    bool entry_mode;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    df::interface_key select_token;
 | 
				
			||||||
 | 
					    const int ascii_to_enum_offset;
 | 
				
			||||||
 | 
					    const int shift_offset;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					template <class S, class T, class V> search_parent<S,T,V> *search_parent<S,T,V> ::lock = NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Parent struct for the hooks
 | 
				
			||||||
 | 
					template <class T, class V, typename D = void>
 | 
				
			||||||
 | 
					struct search_hook : T
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    typedef T interpose_base;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    static V module;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    DEFINE_VMETHOD_INTERPOSE(void, feed, (set<df::interface_key> *input))
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        if (!module.init(this))
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            INTERPOSE_NEXT(feed)(input);
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (!module.process_input(input))
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            INTERPOSE_NEXT(feed)(input);
 | 
				
			||||||
 | 
					            module.do_post_update_check();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    DEFINE_VMETHOD_INTERPOSE(void, render, ())
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        bool ok = module.init(this);
 | 
				
			||||||
 | 
					        INTERPOSE_NEXT(render)();
 | 
				
			||||||
 | 
					        if (ok)
 | 
				
			||||||
 | 
					            module.render();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					template <class T, class V, typename D> V search_hook<T, V, D> ::module;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// END: Base Search functionality
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// START: Stocks screen search
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					class stocks_search : public search_parent<df::viewscreen_storesst, df::item*>
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    virtual void render() const
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        if (!viewscreen->in_group_mode)
 | 
				
			||||||
 | 
					            print_search_option(2);
 | 
				
			||||||
 | 
					        else
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            int x = 2;
 | 
				
			||||||
 | 
					            OutputString(15, x, gps->dimy - 2, "Tab to enable Search");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    virtual void do_post_update_check()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        if (viewscreen->in_group_mode)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            // Disable search if item lists are grouped
 | 
				
			||||||
 | 
					            clear_search();
 | 
				
			||||||
 | 
					            reset_search();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        else
 | 
				
			||||||
 | 
					            search_parent::do_post_update_check();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    bool init(df::viewscreen_storesst *screen)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        if (screen != viewscreen && !reset_on_change())
 | 
				
			||||||
 | 
					            return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (!valid)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            viewscreen = screen;
 | 
				
			||||||
 | 
					            search_parent::init(&screen->item_cursor, &screen->items);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					private:
 | 
				
			||||||
 | 
					    virtual string get_element_description(df::item *element) const
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return Items::getDescription(element, 0, true);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    virtual bool should_check_input(set<df::interface_key> *input) 
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        if (viewscreen->in_group_mode)
 | 
				
			||||||
 | 
					            return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if ((input->count(interface_key::CURSOR_UP) || input->count(interface_key::CURSOR_DOWN)) && !viewscreen->in_right_list)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            // Redo search if category changes
 | 
				
			||||||
 | 
					            saved_list1.clear();
 | 
				
			||||||
 | 
					            end_entry_mode();
 | 
				
			||||||
 | 
					            if (search_string.length() > 0)
 | 
				
			||||||
 | 
					                redo_search = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return false;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typedef search_hook<df::viewscreen_storesst, stocks_search> stocks_search_hook;
 | 
				
			||||||
 | 
					template<> IMPLEMENT_VMETHOD_INTERPOSE(stocks_search_hook, feed);
 | 
				
			||||||
 | 
					template<> IMPLEMENT_VMETHOD_INTERPOSE(stocks_search_hook, render);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// END: Stocks screen search
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// START: Unit screen search
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					class unitlist_search : public search_parent<df::viewscreen_unitlistst, df::unit*, df::job*>
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    virtual void render() const
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        print_search_option(28);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    bool init(df::viewscreen_unitlistst *screen)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        if (screen != viewscreen && !reset_on_change())
 | 
				
			||||||
 | 
					            return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (!valid)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            viewscreen = screen;
 | 
				
			||||||
 | 
					            search_parent::init(&screen->cursor_pos[viewscreen->page], &screen->units[viewscreen->page], &screen->jobs[viewscreen->page]);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					private:
 | 
				
			||||||
 | 
					    virtual string get_element_description(df::unit *element) const
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        string desc = Translation::TranslateName(Units::getVisibleName(element), false);
 | 
				
			||||||
 | 
					        desc += ", " + Units::getProfessionName(element); // Check animal type too
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return desc;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    virtual bool should_check_input(set<df::interface_key> *input) 
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        if (input->count(interface_key::CURSOR_LEFT) || input->count(interface_key::CURSOR_RIGHT) || input->count(interface_key::CUSTOM_L))
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            if (!is_entry_mode())
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                // Changing screens, reset search
 | 
				
			||||||
 | 
					                clear_search();
 | 
				
			||||||
 | 
					                reset_all();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            else
 | 
				
			||||||
 | 
					                input->clear(); // Ignore cursor keys when typing
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return false;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typedef search_hook<df::viewscreen_unitlistst, unitlist_search> unitlist_search_hook;
 | 
				
			||||||
 | 
					template<> IMPLEMENT_VMETHOD_INTERPOSE_PRIO(unitlist_search_hook, feed, 100);
 | 
				
			||||||
 | 
					template<> IMPLEMENT_VMETHOD_INTERPOSE_PRIO(unitlist_search_hook, render, 100);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// END: Unit screen search
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// TODO: Animals screen search
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// END: Animals screen search
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// START: Trade screen search
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					class trade_search_base : public search_parent<df::viewscreen_tradegoodsst, df::item*, char>
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					private:
 | 
				
			||||||
 | 
					    virtual string get_element_description(df::item *element) const
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return Items::getDescription(element, 0, true);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    virtual bool should_check_input(set<df::interface_key> *input)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        if (is_entry_mode())
 | 
				
			||||||
 | 
					            return true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (input->count(interface_key::TRADE_TRADE) ||
 | 
				
			||||||
 | 
					            input->count(interface_key::TRADE_OFFER) ||
 | 
				
			||||||
 | 
					            input->count(interface_key::TRADE_SEIZE))
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            // Block the keys if were searching
 | 
				
			||||||
 | 
					            if (!search_string.empty())
 | 
				
			||||||
 | 
					                input->clear();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Trying to trade, reset search
 | 
				
			||||||
 | 
					            clear_search();
 | 
				
			||||||
 | 
					            reset_all();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return false;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class trade_search_merc : public trade_search_base
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
					    virtual void render() const
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        print_search_option(2, 26);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    bool init(df::viewscreen_tradegoodsst *screen)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        if (screen != viewscreen && !reset_on_change())
 | 
				
			||||||
 | 
					            return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (!valid)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            viewscreen = screen;
 | 
				
			||||||
 | 
					            search_parent::init(&screen->trader_cursor, &screen->trader_items, &screen->trader_selected, 'q');
 | 
				
			||||||
 | 
					            track_secondary_values = true;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typedef search_hook<df::viewscreen_tradegoodsst, trade_search_merc, int> trade_search_merc_hook;
 | 
				
			||||||
 | 
					template<> IMPLEMENT_VMETHOD_INTERPOSE(trade_search_merc_hook, feed);
 | 
				
			||||||
 | 
					template<> IMPLEMENT_VMETHOD_INTERPOSE(trade_search_merc_hook, render);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class trade_search_fort : public trade_search_base
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
					    virtual void render() const
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        print_search_option(42, 26);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    bool init(df::viewscreen_tradegoodsst *screen)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        if (screen != viewscreen && !reset_on_change())
 | 
				
			||||||
 | 
					            return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (!valid)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            viewscreen = screen;
 | 
				
			||||||
 | 
					            search_parent::init(&screen->broker_cursor, &screen->broker_items, &screen->broker_selected, 'w');
 | 
				
			||||||
 | 
					            track_secondary_values = true;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typedef search_hook<df::viewscreen_tradegoodsst, trade_search_fort, char> trade_search_fort_hook;
 | 
				
			||||||
 | 
					template<> IMPLEMENT_VMETHOD_INTERPOSE(trade_search_fort_hook, feed);
 | 
				
			||||||
 | 
					template<> IMPLEMENT_VMETHOD_INTERPOSE(trade_search_fort_hook, render);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// END: Trade screen search
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					DFHACK_PLUGIN("search");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					DFhackCExport command_result plugin_init ( color_ostream &out, vector <PluginCommand> &commands)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    if (!gps || !gview ||
 | 
				
			||||||
 | 
					        !INTERPOSE_HOOK(unitlist_search_hook, feed).apply() ||
 | 
				
			||||||
 | 
					        !INTERPOSE_HOOK(unitlist_search_hook, render).apply() ||
 | 
				
			||||||
 | 
					        !INTERPOSE_HOOK(trade_search_merc_hook, feed).apply() ||
 | 
				
			||||||
 | 
					        !INTERPOSE_HOOK(trade_search_merc_hook, render).apply() ||
 | 
				
			||||||
 | 
					        !INTERPOSE_HOOK(trade_search_fort_hook, feed).apply() ||
 | 
				
			||||||
 | 
					        !INTERPOSE_HOOK(trade_search_fort_hook, render).apply() ||
 | 
				
			||||||
 | 
					        !INTERPOSE_HOOK(stocks_search_hook, feed).apply() ||
 | 
				
			||||||
 | 
					        !INTERPOSE_HOOK(stocks_search_hook, render).apply())
 | 
				
			||||||
 | 
					        out.printerr("Could not insert Search hooks!\n");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return CR_OK;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					DFhackCExport command_result plugin_shutdown ( color_ostream &out )
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    INTERPOSE_HOOK(unitlist_search_hook, feed).remove();
 | 
				
			||||||
 | 
					    INTERPOSE_HOOK(unitlist_search_hook, render).remove();
 | 
				
			||||||
 | 
					    INTERPOSE_HOOK(trade_search_merc_hook, feed).remove();
 | 
				
			||||||
 | 
					    INTERPOSE_HOOK(trade_search_merc_hook, render).remove();
 | 
				
			||||||
 | 
					    INTERPOSE_HOOK(trade_search_fort_hook, feed).remove();
 | 
				
			||||||
 | 
					    INTERPOSE_HOOK(trade_search_fort_hook, render).remove();
 | 
				
			||||||
 | 
					    INTERPOSE_HOOK(stocks_search_hook, feed).remove();
 | 
				
			||||||
 | 
					    INTERPOSE_HOOK(stocks_search_hook, render).remove();
 | 
				
			||||||
 | 
					    return CR_OK;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					DFhackCExport command_result plugin_onstatechange ( color_ostream &out, state_change_event event )
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    switch (event) {
 | 
				
			||||||
 | 
					    case SC_VIEWSCREEN_CHANGED:
 | 
				
			||||||
 | 
					        unitlist_search_hook::module.reset_on_change();
 | 
				
			||||||
 | 
					        trade_search_merc_hook::module.reset_on_change();
 | 
				
			||||||
 | 
					        trade_search_fort_hook::module.reset_on_change();
 | 
				
			||||||
 | 
					        stocks_search_hook::module.reset_on_change();
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    default:
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return CR_OK;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -1 +1 @@
 | 
				
			|||||||
Subproject commit 75df766263b23182820a1e07b330e64f87d5c9b7
 | 
					Subproject commit cb97cf308c6e09638c0de94894473c9bd0f561fd
 | 
				
			||||||
@ -0,0 +1,141 @@
 | 
				
			|||||||
 | 
					-- Shows populations of animals in the region, and allows tweaking them.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					local utils = require 'utils'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					local function sort_keys(tab)
 | 
				
			||||||
 | 
					    local kt = {}
 | 
				
			||||||
 | 
					    for k,v in pairs(tab) do table.insert(kt,k) end
 | 
				
			||||||
 | 
					    table.sort(kt)
 | 
				
			||||||
 | 
					    return ipairs(kt)
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					local is_plant_map = {
 | 
				
			||||||
 | 
					    Animal = false, Vermin = false, VerminInnumerable = false,
 | 
				
			||||||
 | 
					    ColonyInsect = false, Tree = true, Grass = true, Bush = true
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function enum_populations()
 | 
				
			||||||
 | 
					    local stat_table = {
 | 
				
			||||||
 | 
					        plants = {},
 | 
				
			||||||
 | 
					        creatures = {},
 | 
				
			||||||
 | 
					        any = {}
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for i,v in ipairs(df.global.world.populations) do
 | 
				
			||||||
 | 
					        local typeid = df.world_population_type[v.type]
 | 
				
			||||||
 | 
					        local is_plant = is_plant_map[typeid]
 | 
				
			||||||
 | 
					        local id, obj, otable, idtoken
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if is_plant then
 | 
				
			||||||
 | 
					            id = v.plant
 | 
				
			||||||
 | 
					            obj = df.plant_raw.find(id)
 | 
				
			||||||
 | 
					            otable = stat_table.plants
 | 
				
			||||||
 | 
					            idtoken = obj.id
 | 
				
			||||||
 | 
					        else
 | 
				
			||||||
 | 
					            id = v.race
 | 
				
			||||||
 | 
					            obj = df.creature_raw.find(id)
 | 
				
			||||||
 | 
					            otable = stat_table.creatures
 | 
				
			||||||
 | 
					            idtoken = obj.creature_id
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        local entry = otable[idtoken]
 | 
				
			||||||
 | 
					        if not entry then
 | 
				
			||||||
 | 
					            entry = {
 | 
				
			||||||
 | 
					                obj = obj, token = idtoken, id = id, records = {},
 | 
				
			||||||
 | 
					                count = 0, known_count = 0,
 | 
				
			||||||
 | 
					                known = false, infinite = false
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            otable[idtoken] = entry
 | 
				
			||||||
 | 
					            stat_table.any[idtoken] = entry
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        table.insert(entry.records, v)
 | 
				
			||||||
 | 
					        entry.known = entry.known or v.known
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if v.quantity < 10000001 then
 | 
				
			||||||
 | 
					            entry.count = entry.count + v.quantity
 | 
				
			||||||
 | 
					            if v.known then
 | 
				
			||||||
 | 
					                entry.known_count = entry.known_count + v.quantity
 | 
				
			||||||
 | 
					            end
 | 
				
			||||||
 | 
					        else
 | 
				
			||||||
 | 
					            entry.infinite = true
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return stat_table
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function list_poptable(entries, all, pattern)
 | 
				
			||||||
 | 
					    for _,k in sort_keys(entries) do
 | 
				
			||||||
 | 
					        local entry = entries[k]
 | 
				
			||||||
 | 
					        if (all or entry.known) and (not pattern or string.match(k,pattern)) then
 | 
				
			||||||
 | 
					            local count = entry.known_count
 | 
				
			||||||
 | 
					            if all then
 | 
				
			||||||
 | 
					                count = entry.count
 | 
				
			||||||
 | 
					            end
 | 
				
			||||||
 | 
					            if entry.infinite then
 | 
				
			||||||
 | 
					                count = 'innumerable'
 | 
				
			||||||
 | 
					            end
 | 
				
			||||||
 | 
					            print(string.format('%-40s %s', entry.token, count))
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function list_populations(stat_table, all, pattern)
 | 
				
			||||||
 | 
					    print('Plants:')
 | 
				
			||||||
 | 
					    list_poptable(stat_table.plants, true, pattern)
 | 
				
			||||||
 | 
					    print('\nCreatures and vermin:')
 | 
				
			||||||
 | 
					    list_poptable(stat_table.creatures, all, pattern)
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function boost_population(entry, factor, boost_count)
 | 
				
			||||||
 | 
					    for _,v in ipairs(entry.records) do
 | 
				
			||||||
 | 
					        if v.quantity < 10000001 then
 | 
				
			||||||
 | 
					            boost_count = boost_count + 1
 | 
				
			||||||
 | 
					            v.quantity = math.floor(v.quantity * factor)
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					    return boost_count
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					local args = {...}
 | 
				
			||||||
 | 
					local pops = enum_populations()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if args[1] == 'list' or args[1] == 'list-all' then
 | 
				
			||||||
 | 
					    list_populations(pops, args[1] == 'list-all', args[2])
 | 
				
			||||||
 | 
					elseif args[1] == 'boost' or args[1] == 'boost-all' then
 | 
				
			||||||
 | 
					    local factor = tonumber(args[3])
 | 
				
			||||||
 | 
					    if not factor or factor < 0 then
 | 
				
			||||||
 | 
					        qerror('Invalid boost factor.')
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    local count = 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if args[1] == 'boost' then
 | 
				
			||||||
 | 
					        local entry = pops.any[args[2]] or qerror('Unknown population token.')
 | 
				
			||||||
 | 
					        count = boost_population(entry, factor, count)
 | 
				
			||||||
 | 
					    else
 | 
				
			||||||
 | 
					        for k,entry in pairs(pops.any) do
 | 
				
			||||||
 | 
					            if string.match(k, args[2]) then
 | 
				
			||||||
 | 
					                count = boost_population(entry, factor, count)
 | 
				
			||||||
 | 
					            end
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    print('Updated '..count..' populations.')
 | 
				
			||||||
 | 
					else
 | 
				
			||||||
 | 
					    print([[
 | 
				
			||||||
 | 
					Usage:
 | 
				
			||||||
 | 
					  region-pops list [pattern]
 | 
				
			||||||
 | 
					    Lists encountered populations of the region, possibly restricted by pattern.
 | 
				
			||||||
 | 
					  region-pops list-all [pattern]
 | 
				
			||||||
 | 
					    Lists all populations of the region.
 | 
				
			||||||
 | 
					  region-pops boost <TOKEN> <factor>
 | 
				
			||||||
 | 
					    Multiply all populations of TOKEN by factor.
 | 
				
			||||||
 | 
					    If the factor is greater than one, increases the
 | 
				
			||||||
 | 
					    population, otherwise decreases it.
 | 
				
			||||||
 | 
					  region-pops boost-all <pattern> <factor>
 | 
				
			||||||
 | 
					    Same as above, but match using a pattern acceptable to list.
 | 
				
			||||||
 | 
					]])
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
		Loading…
	
		Reference in New Issue