/* * Copyright (c) 2002 DarkGod * Copyright (c) 2012 Bardur Arantsson * * This software may be copied and distributed for educational, research, and * not for profit purposes provided that this copyright and statement are * included in all such copies. */ #include "squeltch.hpp" #include "cave_type.hpp" #include "files.hpp" #include "loadsave.hpp" #include "lua_bind.hpp" #include "object1.hpp" #include "object2.hpp" #include "object_kind.hpp" #include "object_type.hpp" #include "player_type.hpp" #include "tome/squelch/tree_printer.hpp" #include "tome/squelch/condition.hpp" #include "tome/squelch/condition_metadata.hpp" #include "tome/squelch/rule.hpp" #include "tome/squelch/cursor.hpp" #include "tome/squelch/object_status.hpp" #include "tome/squelch/automatizer.hpp" #include "util.hpp" #include "util.h" #include "variable.h" #include "variable.hpp" #include #include #include #include #include #include #include using squelch::action_type; using squelch::action_mapping; using squelch::status_type; using squelch::Rule; using squelch::DestroyRule; using squelch::PickUpRule; using squelch::InscribeRule; using squelch::Condition; using squelch::AndCondition; using squelch::TvalCondition; using squelch::SvalCondition; using squelch::NameCondition; using squelch::StatusCondition; static squelch::Automatizer *automatizer = nullptr; void squeltch_grid(void) { if (!automatizer_enabled) { return; } // Copy list of objects since we may modify it auto const object_idxs(cave[p_ptr->py][p_ptr->px].o_idxs); // Scan the pile of objects for (auto const this_o_idx: object_idxs) { // Acquire object object_type * o_ptr = &o_list[this_o_idx]; // We've now seen one of these if (!k_info[o_ptr->k_idx].flavor) { object_aware(o_ptr); } // Apply rules automatizer->apply_rules(o_ptr, -this_o_idx); } } void squeltch_inventory(void) { if (!automatizer_enabled) { return; } bool changed = true; for (int num_iter = 0; changed && (num_iter < 100); num_iter++) { // No changes on this iteration. changed = false; // Traverse inventory for (int i = 0; i < INVEN_PACK; i++) { object_type *o_ptr = &p_ptr->inventory[i]; if ((o_ptr->k_idx > 0) && automatizer->apply_rules(o_ptr, i)) { // We have changes changed = true; // Re-traverse inventory break; } } } // If we reached the iteration limit, "changed" will be true if (changed) { cmsg_format(TERM_VIOLET, "'apply_rules' ran too often."); } } static int create_new_rule() { char name[20] = { '\0' }; int wid = 0, hgt = 0; Term_get_size(&wid, &hgt); sprintf(name, "%s", "No name"); if (!input_box("Name?", hgt / 2, wid / 2, name, sizeof(name))) { return -1; } char typ = lua_msg_box("[D]estroy, [P]ickup, [I]nscribe?"); std::shared_ptr rule; switch (typ) { case 'd': case 'D': rule = std::make_shared(name, game_module_idx, nullptr); break; case 'p': case 'P': rule = std::make_shared(name, game_module_idx, nullptr); break; case 'i': case 'I': { cptr i = lua_input_box("Inscription?", 79); if ((i == nullptr) || (strlen(i) == 0)) { return -1; } rule = std::make_shared( name, game_module_idx, nullptr, std::string(i)); break; } default: return -1; } return automatizer->append_rule(rule); } static void automatizer_save_rules() { char name[30] = { '\0' }; char buf[1025]; char ch; int hgt, wid; Term_get_size(&wid, &hgt); sprintf(name, "%s.atm", player_name); if (!input_box("Save name?", hgt / 2, wid / 2, name, sizeof(name))) { return; } // Build the filename path_build(buf, 1024, ANGBAND_DIR_USER, name); if (file_exist(buf)) { c_put_str(TERM_WHITE, "File exists, continue?[y/n]", hgt / 2, wid / 2 - 14); ch = inkey(); if ((ch != 'Y') && (ch != 'y')) { return; } } // Write to file { auto rules_json = automatizer->to_json(); int status = json_dump_file(rules_json.get(), buf, JSON_INDENT(2) | JSON_SORT_KEYS); if (status == 0) { c_put_str(TERM_WHITE, "Saved rules in file ", hgt / 2, wid / 2 - 14); } else { c_put_str(TERM_WHITE, "Saving rules failed! ", hgt / 2, wid / 2 - 14); } // Wait for keypress inkey(); } } static void rename_rule(Rule *rule) { char name[16]; int wid, hgt; assert(rule != nullptr); Term_get_size(&wid, &hgt); sprintf(name, "%s", rule->get_name()); if (input_box("New name?", hgt / 2, wid / 2, name, sizeof(name))) { rule->set_name(name); } } #define ACTIVE_LIST 0 #define ACTIVE_RULE 1 void do_cmd_automatizer() { int wid = 0, hgt = 0; int active = ACTIVE_LIST; cptr keys; cptr keys2; cptr keys3; std::vector rule_names; Term_get_size(&wid, &hgt); if (!automatizer_enabled) { if (msg_box("Automatizer is currently disabled, enable it? (y/n)", hgt / 2, wid / 2) == 'y') { automatizer_enabled = TRUE; } else return; } screen_save(); automatizer->reset_view(); while (1) { Term_clear(); Term_get_size(&wid, &hgt); automatizer->get_rule_names(&rule_names); display_list(0, 0, hgt - 1, 15, "Rules", rule_names.data(), automatizer->rules_count(), automatizer->rules_begin(), automatizer->selected_rule(), (active == ACTIVE_LIST) ? TERM_L_GREEN : TERM_GREEN); draw_box(0, 15, hgt - 4, wid - 1 - 15); if (active == ACTIVE_RULE) { keys = "#Bup#W/#Bdown#W/#Bleft#W/#Bright#W to navitage rule, #B9#W/#B3#W/#B7#W/#B1#W to scroll"; keys2 = "#Btab#W for switch, #Ba#Wdd clause, #Bd#Welete clause/rule"; keys3 = "#G?#W for Automatizer help"; } else { keys = "#Bup#W/#Bdown#W to scroll, #Btab#W to switch to the rule window"; keys2 = "#Bu#W/#Bd#W to move rules, #Bn#Wew rule, #Br#Wename rule, #Bs#Wave rules"; keys3 = "#Rk#W to #rdisable#W the automatizer, #G?#W for Automatizer help"; } display_message(17, hgt - 3, strlen(keys), TERM_WHITE, keys); display_message(17, hgt - 2, strlen(keys2), TERM_WHITE, keys2); display_message(17, hgt - 1, strlen(keys3), TERM_WHITE, keys3); automatizer->show_current(); char c = inkey(); if (c == ESCAPE) break; if (active == ACTIVE_LIST) { if (c == '?') { screen_save(); show_file("automat.txt", "Automatizer help"); screen_load(); } else if (c == '8') { if (!automatizer->rules_count()) continue; automatizer->select_rule( automatizer->selected_rule() - 1); } else if (c == '2') { if (!automatizer->rules_count()) continue; automatizer->select_rule( automatizer->selected_rule() + 1); } else if (c == 'u') { int sel = automatizer->selected_rule(); if (sel > 0) { automatizer->swap_rules(sel-1, sel); automatizer->select_rule(sel-1); } } else if (c == 'd') { if (!automatizer->rules_count()) continue; int sel = automatizer->selected_rule(); if (sel < automatizer->rules_count() - 1) { automatizer->swap_rules(sel, sel+1); automatizer->select_rule(sel+1); } } else if (c == 'n') { int i = create_new_rule(); if (i >= 0) { automatizer->select_rule(i); active = ACTIVE_RULE; } } else if (c == 's') { automatizer_save_rules(); } else if (c == 'r') { if (!automatizer->rules_count()) continue; rename_rule(automatizer->current_rule().get()); continue; } else if (c == 'k') { automatizer_enabled = FALSE; break; } else if (c == '\t') { if (!automatizer->rules_count()) continue; active = ACTIVE_RULE; } } else if (active == ACTIVE_RULE) { if (c == '?') { screen_save(); show_file("automat.txt", "Automatizer help"); screen_load(); } else if (c == '8') { automatizer->move_up(); } else if (c == '2') { automatizer->move_down(); } else if (c == '6') { automatizer->move_right(); } else if (c == '4') { automatizer->move_left(); } else if (c == '9') { automatizer->scroll_up(); } else if (c == '3') { automatizer->scroll_down(); } else if (c == '7') { automatizer->scroll_left(); } else if (c == '1') { automatizer->scroll_right(); } else if (c == 'a') { automatizer->add_new_condition( squelch::new_condition_interactive); } else if (c == 'd') { if (!automatizer->rules_count()) { continue; } int new_sel = automatizer->remove_current_selection(); if (new_sel == -1) { active = ACTIVE_LIST; } } else if (c == '\t') { active = ACTIVE_LIST; } } } screen_load(); } enum class add_rule_mode { TVAL, TSVAL, NAME }; static void easy_add_rule(add_rule_mode mode, bool do_status, object_type *o_ptr) { std::shared_ptr condition; switch (mode) { case add_rule_mode::TVAL: condition = std::make_shared(o_ptr->tval); break; case add_rule_mode::TSVAL: { auto andCondition = std::make_shared(); andCondition->add_condition( std::make_shared(o_ptr->tval)); andCondition->add_condition( std::make_shared(o_ptr->sval, o_ptr->sval)); condition = andCondition; break; } case add_rule_mode::NAME: { char buf[128]; object_desc(buf, o_ptr, -1, 0); condition = std::make_shared(buf); break; } } // Use object status? if (do_status) { status_type status = squelch::object_status(o_ptr); auto andCondition = std::make_shared(); andCondition->add_condition( std::shared_ptr(condition)); // cycle andCondition->add_condition( std::make_shared(status)); // Replace condition; breaks cycle condition = andCondition; } // Rule name auto rule_name = action_mapping().stringify(action_type::AUTO_DESTROY); // Append to list of rules automatizer->append_rule( std::make_shared( rule_name, game_module_idx, condition)); msg_print("Rule added. Please go to the Automatizer screen (press = then T)"); msg_print("to save the modified ruleset."); } void automatizer_add_rule(object_type *o_ptr) { bool do_status = false; while (true) { char ch; if (!get_com(format("Destroy all of the same [T]ype, [F]amily or [N]ame, also use [S]tatus (%s)? ", (do_status) ? "Yes" : "No"), &ch)) { break; } if (ch == 'S' || ch == 's') { do_status = !do_status; continue; } if (ch == 'T' || ch == 't') { easy_add_rule(add_rule_mode::TSVAL, do_status, o_ptr); break; } if (ch == 'F' || ch == 'f') { easy_add_rule(add_rule_mode::TVAL, do_status, o_ptr); break; } if (ch == 'N' || ch == 'n') { easy_add_rule(add_rule_mode::NAME, do_status, o_ptr); break; } } } /** * Initialize the automatizer. */ void automatizer_init() { // Only permit single initialization. assert(automatizer == nullptr); // Set up dependencies auto tree_printer(std::make_shared()); auto cursor(std::make_shared()); // Initialize automatizer = new squelch::Automatizer(tree_printer, cursor); } /** * Load automatizer file. Returns true iff automatizer * rules were loaded successfully. */ bool automatizer_load(boost::filesystem::path const &path) { assert(automatizer != NULL); // Does the path exist? if (!boost::filesystem::exists(path)) { return false; // Not fatal; just skip } // Parse file json_error_t error; std::shared_ptr rules_json( json_load_file(path.c_str(), 0, &error), &json_decref); if (rules_json == nullptr) { msg_format("Error parsing automatizer rules from '%s'.", path.c_str()); msg_format("Line %d, Column %d", error.line, error.column); msg_print(nullptr); return false; } // Load rules automatizer->load_json(rules_json.get()); return true; }