command_result AI::onupdate_register(color_ostream & out) { command_result res = CR_OK; if (res == CR_OK) res = pop.onupdate_register(out); if (res == CR_OK) res = plan.onupdate_register(out); if (res == CR_OK) res = stocks.onupdate_register(out); if (res == CR_OK) res = camera.onupdate_register(out); if (res == CR_OK) { status_onupdate = events.onupdate_register("df-ai status", 3 * 28 * 1200, 3 * 28 * 1200, [this](color_ostream & out) { debug(out, status()); }); time_paused = 0; pause_onupdate = events.onupdate_register_once("df-ai unpause", [this](color_ostream &) -> bool { if (!*pause_state && world->status.popups.empty()) { Gui::getViewCoords(last_good_x, last_good_y, last_good_z); time_paused = 0; return false; } time_paused++; if (time_paused == enabler->fps * 10) { timeout_sameview(10, [this](color_ostream &) { unpause(); }); time_paused = -enabler->fps; } return false; }); tag_enemies_onupdate = events.onupdate_register("df-ai tag_enemies", 1200, 1200, [this](color_ostream & out) { tag_enemies(out); }); events.onstatechange_register_once("world unload watcher", [this](color_ostream & out, state_change_event st) -> bool { if (st == SC_WORLD_UNLOADED) { debug(out, "world unloaded, disabling self"); onupdate_unregister(out); return true; } statechanged(out, st); return false; }); } return res; }
command_result AI::onupdate_register(color_ostream & out) { command_result res = CR_OK; if (res == CR_OK) res = pop->onupdate_register(out); if (res == CR_OK) res = plan->onupdate_register(out); if (res == CR_OK) res = stocks->onupdate_register(out); if (res == CR_OK) res = camera->onupdate_register(out); if (res == CR_OK) res = embark->onupdate_register(out); if (res == CR_OK) { status_onupdate = events.onupdate_register("df-ai status", 3*28*1200, 3*28*1200, [this](color_ostream & out) { debug(out, status()); }); last_unpause = std::time(nullptr); pause_onupdate = events.onupdate_register_once("df-ai unpause", [this](color_ostream & out) -> bool { if (std::time(nullptr) < last_unpause + 11) return false; if (*pause_state) { timeout_sameview(10, [](color_ostream & out) { AI::unpause(); }); last_unpause = std::time(nullptr); } return false; }); events.onstatechange_register_once([this](color_ostream & out, state_change_event st) -> bool { if (st == SC_WORLD_UNLOADED) { debug(out, "world unloaded, disabling self"); onupdate_unregister(out); return true; } statechanged(out, st); return false; }); } return res; }
void AI::statechanged(color_ostream & out, state_change_event st) { // automatically unpause the game (only for game-generated pauses) if (st == SC_PAUSED) { auto la = std::find_if(world->status.announcements.rbegin(), world->status.announcements.rend(), [](df::report *a) -> bool { return announcements->flags[a->type].bits.PAUSE; }); if (la != world->status.announcements.rend() && (*la)->year == *cur_year && (*la)->time == *cur_year_tick) { handle_pause_event(out, *la); } else { unpause(); debug(out, "pause without an event"); } } else if (st == SC_VIEWSCREEN_CHANGED) { df::viewscreen *curview = Gui::getCurViewscreen(true); df::viewscreen_textviewerst *view = strict_virtual_cast<df::viewscreen_textviewerst>(curview); if (view) { std::ostringstream text; for (auto it = view->formatted_text.begin(); it != view->formatted_text.end(); it++) { if ((*it)->text) { text << " " << (*it)->text; } } std::string stripped = text.str(); stripped.erase(std::remove(stripped.begin(), stripped.end(), ' '), stripped.end()); if (stripped.find("I" "am" "your" "liaison" "from" "the" "Mountainhomes." "Let's" "discuss" "your" "situation.") != std::string::npos || stripped.find("I" "look" "forward" "to" "our" "meeting" "next" "year.") != std::string::npos || stripped.find("A" "diplomat" "has" "left" "unhappy.") != std::string::npos || stripped.find("You" "have" "disrespected" "the" "trees" "in" "this" "area," "but" "this" "is" "what" "we" "have" "come" "to" "expect" "from" "your" "stunted" "kind." "Further" "abuse" "cannot" "be" "tolerated." "Let" "this" "be" "a" "warning" "to" "you.") != std::string::npos || stripped.find("Greetings" "from" "the" "woodlands." "We" "have" "much" "to" "discuss.") != std::string::npos || stripped.find("Although" "we" "do" "not" "always" "see" "eye" "to" "eye" "(ha!)," "I" "bid" "you" "farewell." "May" "you" "someday" "embrace" "nature" "as" "you" "embrace" "the" "rocks" "and" "mud.") != std::string::npos) { debug(out, "exit diplomat textviewerst:" + text.str()); timeout_sameview([](color_ostream & out) { AI::feed_key(interface_key::LEAVESCREEN); }); } else if (stripped.find("A" "vile" "force" "of" "darkness" "has" "arrived!") != std::string::npos || stripped.find("have" "brought" "the" "full" "forces" "of" "their" "lands" "against" "you.") != std::string::npos || stripped.find("The" "enemy" "have" "come" "and" "are" "laying" "siege" "to" "the" "fortress.") != std::string::npos || stripped.find("The" "dead" "walk." "Hide" "while" "you" "still" "can!") != std::string::npos) { debug(out, "exit siege textviewerst:" + text.str()); timeout_sameview([](color_ostream & out) { AI::feed_key(interface_key::LEAVESCREEN); unpause(); }); } else if (stripped.find("Your" "strength" "has" "been" "broken.") != std::string::npos || stripped.find("Your" "settlement" "has" "crumbled" "to" "its" "end.") != std::string::npos || stripped.find("Your" "settlement" "has" "been" "abandoned.") != std::string::npos) { debug(out, "you just lost the game:" + text.str()); debug(out, "Exiting AI"); onupdate_unregister(out); // get rid of all the remaining event handlers events.clear(); embark->register_restart_timer(out); // don't unpause, to allow for 'die' } else { debug(out, "[ERROR] paused in unknown textviewerst:" + text.str()); } } else if (strict_virtual_cast<df::viewscreen_topicmeetingst>(curview)) { debug(out, "exit diplomat topicmeetingst"); timeout_sameview([](color_ostream & out) { AI::feed_key(interface_key::OPTION1); }); } else if (strict_virtual_cast<df::viewscreen_topicmeeting_takerequestsst>(curview)) { debug(out, "exit diplomat topicmeeting_takerequestsst"); timeout_sameview([](color_ostream & out) { AI::feed_key(interface_key::LEAVESCREEN); }); } else if (strict_virtual_cast<df::viewscreen_requestagreementst>(curview)) { debug(out, "exit diplomat requestagreementst"); timeout_sameview([](color_ostream & out) { AI::feed_key(interface_key::LEAVESCREEN); }); } else if (strict_virtual_cast<df::viewscreen_movieplayerst>(curview)) { Screen::dismiss(curview); camera->check_record_status(); } else if (virtual_identity *ident = virtual_identity::get(curview)) { std::string cvname = ident->getName(); if (seen_cvname.insert(cvname).second) { debug(out, "[ERROR] paused in unknown viewscreen " + cvname); } } } }
void AI::handle_pause_event(color_ostream & out, df::report *announce) { // unsplit announce text std::string fulltext = announce->text; auto idx = std::find(world->status.announcements.rbegin(), world->status.announcements.rend(), announce); while (announce->flags.bits.continuation) { idx++; if (idx == world->status.announcements.rend()) break; announce = *idx; fulltext = announce->text + " " + fulltext; } debug(out, "pause: " + fulltext); switch (announce->type) { case announcement_type::MEGABEAST_ARRIVAL: { debug(out, "pause: uh oh, megabeast..."); bool found = false; for (auto it = world->units.active.rbegin(); it != world->units.active.rend(); it++) { if ((*it)->flags2.bits.visitor_uninvited) { pop->military_all_squads_attack_unit(out, *it); found = true; break; } } if (!found) { debug(out, "[ERROR] could not find megabeast"); } break; } case announcement_type::BERSERK_CITIZEN: debug(out, "pause: berserk"); break; case announcement_type::UNDEAD_ATTACK: debug(out, "pause: i see dead people"); break; case announcement_type::CAVE_COLLAPSE: debug(out, "pause: kevin?"); break; case announcement_type::DIG_CANCEL_DAMP: case announcement_type::DIG_CANCEL_WARM: camera->ignore_pause(); debug(out, "pause: lazy miners"); break; case announcement_type::BIRTH_CITIZEN: debug(out, "pause: newborn"); break; case announcement_type::BIRTH_ANIMAL: break; case announcement_type::D_MIGRANTS_ARRIVAL: case announcement_type::D_MIGRANT_ARRIVAL: case announcement_type::MIGRANT_ARRIVAL: case announcement_type::NOBLE_ARRIVAL: case announcement_type::FORT_POSITION_SUCCESSION: debug(out, "pause: more minions"); break; case announcement_type::DIPLOMAT_ARRIVAL: case announcement_type::LIAISON_ARRIVAL: case announcement_type::CARAVAN_ARRIVAL: case announcement_type::TRADE_DIPLOMAT_ARRIVAL: debug(out, "pause: visitors"); break; case announcement_type::STRANGE_MOOD: case announcement_type::MOOD_BUILDING_CLAIMED: case announcement_type::ARTIFACT_BEGUN: case announcement_type::MADE_ARTIFACT: debug(out, "pause: mood"); break; case announcement_type::FEATURE_DISCOVERY: case announcement_type::STRUCK_DEEP_METAL: debug(out, "pause: dig dig dig"); break; case announcement_type::TRAINING_FULL_REVERSION: debug(out, "pause: born to be wild"); break; case announcement_type::NAMED_ARTIFACT: debug(out, "pause: hallo"); break; default: { const static std::string prefix("AMBUSH"); std::string type(ENUM_KEY_STR(announcement_type, announce->type)); if (std::mismatch(prefix.begin(), prefix.end(), type.begin()).first == prefix.end()) { debug(out, "pause: an ambush!"); } else { debug(out, "pause: unhandled pausing event " + type); // return; } break; } } if (announcements->flags[announce->type].bits.DO_MEGA) { timeout_sameview([](color_ostream & out) { unpause(); }); } else { unpause(); } }