diff --git a/Lua API.html b/Lua API.html index 3d019f986..f0af74012 100644 --- a/Lua API.html +++ b/Lua API.html @@ -1160,6 +1160,26 @@ the container itself.

  • dfhack.gui.getSelectedBuilding([silent])

    Returns the building selected via 'q', 't', 'k' or 'i'.

  • +
  • dfhack.gui.writeToGamelog(text)

    +

    Writes a string to gamelog.txt without doing an announcement.

    +
  • +
  • dfhack.gui.makeAnnouncement(type,flags,pos,text,color[,is_bright])

    +

    Adds an announcement with given announcement_type, text, color, and brightness. +The is_bright boolean actually seems to invert the brightness.

    +

    The announcement is written to gamelog.txt. The announcement_flags +argument provides a custom set of announcements.txt options, +which specify if the message should actually be displayed in the +announcement list, and whether to recenter or show a popup.

    +

    Returns the index of the new announcement in df.global.world.status.reports, or -1.

    +
  • +
  • dfhack.gui.addCombatReport(unit,slot,report_index)

    +

    Adds the report with the given index (returned by makeAnnouncement) +to the specified group of the given unit. Returns true on success.

    +
  • +
  • dfhack.gui.addCombatReportAuto(unit,flags,report_index)

    +

    Adds the report with the given index to the appropriate group(s) +of the given unit, as requested by the flags.

    +
  • dfhack.gui.showAnnouncement(text,color[,is_bright])

    Adds a regular announcement with given text, color, and brightness. The is_bright boolean actually seems to invert the brightness.

    @@ -1170,9 +1190,9 @@ The is_bright boolean actually seems to invert the brightness.

  • dfhack.gui.showPopupAnnouncement(text,color[,is_bright])

    Pops up a titan-style modal announcement window.

  • -
  • dfhack.gui.showAutoAnnouncement(type,pos,text,color[,is_bright])

    -

    Uses the type to look up options from announcements.txt, and calls the -above operations accordingly. If enabled, pauses and zooms to position.

    +
  • dfhack.gui.showAutoAnnouncement(type,pos,text,color[,is_bright,unit1,unit2])

    +

    Uses the type to look up options from announcements.txt, and calls the above +operations accordingly. The units are used to call addCombatReportAuto.

  • diff --git a/Lua API.rst b/Lua API.rst index 27445be45..90e36213f 100644 --- a/Lua API.rst +++ b/Lua API.rst @@ -862,6 +862,32 @@ Gui module Returns the building selected via *'q'*, *'t'*, *'k'* or *'i'*. +* ``dfhack.gui.writeToGamelog(text)`` + + Writes a string to gamelog.txt without doing an announcement. + +* ``dfhack.gui.makeAnnouncement(type,flags,pos,text,color[,is_bright])`` + + Adds an announcement with given announcement_type, text, color, and brightness. + The is_bright boolean actually seems to invert the brightness. + + The announcement is written to gamelog.txt. The announcement_flags + argument provides a custom set of announcements.txt options, + which specify if the message should actually be displayed in the + announcement list, and whether to recenter or show a popup. + + Returns the index of the new announcement in ``df.global.world.status.reports``, or -1. + +* ``dfhack.gui.addCombatReport(unit,slot,report_index)`` + + Adds the report with the given index (returned by makeAnnouncement) + to the specified group of the given unit. Returns *true* on success. + +* ``dfhack.gui.addCombatReportAuto(unit,flags,report_index)`` + + Adds the report with the given index to the appropriate group(s) + of the given unit, as requested by the flags. + * ``dfhack.gui.showAnnouncement(text,color[,is_bright])`` Adds a regular announcement with given text, color, and brightness. @@ -875,10 +901,10 @@ Gui module Pops up a titan-style modal announcement window. -* ``dfhack.gui.showAutoAnnouncement(type,pos,text,color[,is_bright])`` +* ``dfhack.gui.showAutoAnnouncement(type,pos,text,color[,is_bright,unit1,unit2])`` - Uses the type to look up options from announcements.txt, and calls the - above operations accordingly. If enabled, pauses and zooms to position. + Uses the type to look up options from announcements.txt, and calls the above + operations accordingly. The units are used to call ``addCombatReportAuto``. Job module diff --git a/library/LuaApi.cpp b/library/LuaApi.cpp index b63e58dbf..622b3f286 100644 --- a/library/LuaApi.cpp +++ b/library/LuaApi.cpp @@ -1307,6 +1307,10 @@ static const LuaWrapper::FunctionReg dfhack_gui_module[] = { WRAPM(Gui, getSelectedUnit), WRAPM(Gui, getSelectedItem), WRAPM(Gui, getSelectedBuilding), + WRAPM(Gui, writeToGamelog), + WRAPM(Gui, makeAnnouncement), + WRAPM(Gui, addCombatReport), + WRAPM(Gui, addCombatReportAuto), WRAPM(Gui, showAnnouncement), WRAPM(Gui, showZoomAnnouncement), WRAPM(Gui, showPopupAnnouncement), diff --git a/library/include/modules/Gui.h b/library/include/modules/Gui.h index 794cb89ff..6acb0ce08 100644 --- a/library/include/modules/Gui.h +++ b/library/include/modules/Gui.h @@ -35,6 +35,8 @@ distribution. #include "df/init.h" #include "df/ui.h" #include "df/announcement_type.h" +#include "df/announcement_flags.h" +#include "df/unit_report_type.h" namespace df { struct viewscreen; @@ -97,13 +99,20 @@ namespace DFHack DFHACK_EXPORT bool any_building_hotkey(df::viewscreen *top); DFHACK_EXPORT df::building *getSelectedBuilding(color_ostream &out, bool quiet = false); + // Low-level API that gives full control over announcements and reports + DFHACK_EXPORT void writeToGamelog(std::string message); + + DFHACK_EXPORT int makeAnnouncement(df::announcement_type type, df::announcement_flags mode, df::coord pos, std::string message, int color = 7, bool bright = true); + DFHACK_EXPORT bool addCombatReport(df::unit *unit, df::unit_report_type slot, int report_index); + DFHACK_EXPORT bool addCombatReportAuto(df::unit *unit, df::announcement_flags mode, int report_index); + // Show a plain announcement, or a titan-style popup message DFHACK_EXPORT void showAnnouncement(std::string message, int color = 7, bool bright = true); DFHACK_EXPORT void showZoomAnnouncement(df::announcement_type type, df::coord pos, std::string message, int color = 7, bool bright = true); DFHACK_EXPORT void showPopupAnnouncement(std::string message, int color = 7, bool bright = true); // Show an announcement with effects determined by announcements.txt - DFHACK_EXPORT void showAutoAnnouncement(df::announcement_type type, df::coord pos, std::string message, int color = 7, bool bright = true); + DFHACK_EXPORT void showAutoAnnouncement(df::announcement_type type, df::coord pos, std::string message, int color = 7, bool bright = true, df::unit *unit1 = NULL, df::unit *unit2 = NULL); /* * Cursor and window coords diff --git a/library/modules/Gui.cpp b/library/modules/Gui.cpp index 04a453c06..bb9ce6676 100644 --- a/library/modules/Gui.cpp +++ b/library/modules/Gui.cpp @@ -87,6 +87,8 @@ using namespace DFHack; #include "df/announcements.h" #include "df/stop_depart_condition.h" #include "df/route_stockpile_link.h" +#include "df/game_mode.h" +#include "df/unit.h" using namespace df::enums; using df::global::gview; @@ -97,6 +99,7 @@ using df::global::world; using df::global::selection_rect; using df::global::ui_menu_width; using df::global::ui_area_map_width; +using df::global::gamemode; static df::layer_object_listst *getLayerList(df::viewscreen_layer *layer, int idx) { @@ -1080,13 +1083,26 @@ df::building *Gui::getSelectedBuilding(color_ostream &out, bool quiet) // -static void doShowAnnouncement( - df::announcement_type type, df::coord pos, std::string message, int color, bool bright -) { +DFHACK_EXPORT void Gui::writeToGamelog(std::string message) +{ + if (message.empty()) + return; + + std::ofstream fseed("gamelog.txt", std::ios::out | std::ios::app); + if(fseed.is_open()) + fseed << message << std::endl; + fseed.close(); +} + +DFHACK_EXPORT int Gui::makeAnnouncement(df::announcement_type type, df::announcement_flags flags, df::coord pos, std::string message, int color, bool bright) +{ using df::global::world; using df::global::cur_year; using df::global::cur_year_tick; + if (message.empty()) + return -1; + int year = 0, year_time = 0; if (cur_year && cur_year_tick) @@ -1102,6 +1118,31 @@ static void doShowAnnouncement( year_time = last->time; } + // Apply the requested effects + writeToGamelog(message); + + if (flags.bits.DO_MEGA || flags.bits.PAUSE || flags.bits.RECENTER) + { + resetDwarfmodeView(flags.bits.DO_MEGA || flags.bits.PAUSE); + + if (flags.bits.RECENTER && pos.isValid()) + revealInDwarfmodeMap(pos, true); + + if (flags.bits.DO_MEGA) + showPopupAnnouncement(message, color, bright); + } + + bool display = false; + + if (gamemode == NULL) + display = flags.bits.A_DISPLAY || flags.bits.D_DISPLAY; + else if (*gamemode == game_mode::ADVENTURE) + display = flags.bits.A_DISPLAY; + else + display = flags.bits.D_DISPLAY; + + // Generate the report objects + int report_idx = world->status.reports.size(); bool continued = false; while (!message.empty()) @@ -1117,7 +1158,6 @@ static void doShowAnnouncement( new_rep->time = year_time; new_rep->flags.bits.continuation = continued; - new_rep->flags.bits.announcement = true; int size = std::min(message.size(), (size_t)73); new_rep->text = message.substr(0, size); @@ -1129,20 +1169,114 @@ static void doShowAnnouncement( new_rep->id = world->status.next_report_id++; world->status.reports.push_back(new_rep); - world->status.announcements.push_back(new_rep); - world->status.display_timer = 2000; + + if (display) + { + new_rep->flags.bits.announcement = true; + world->status.announcements.push_back(new_rep); + world->status.display_timer = 2000; + } } + + return report_idx; +} + + +bool Gui::addCombatReport(df::unit *unit, df::unit_report_type slot, int report_index) +{ + using df::global::world; + + CHECK_INVALID_ARGUMENT(is_valid_enum_item(slot)); + + auto &vec = world->status.reports; + auto report = vector_get(vec, report_index); + + if (!unit || !report) + return false; + + // Check that it is a new report + auto &rvec = unit->reports.log[slot]; + if (!rvec.empty() && rvec.back() >= report->id) + return false; + + // Add the report + rvec.push_back(report->id); + + unit->reports.last_year[slot] = report->year; + unit->reports.last_year_tick[slot] = report->time; + + switch (slot) { + case unit_report_type::Combat: + world->status.flags.bits.combat = true; + break; + case unit_report_type::Hunting: + world->status.flags.bits.hunting = true; + break; + case unit_report_type::Sparring: + world->status.flags.bits.sparring = true; + break; + } + + // And all the continuation lines + for (size_t i = report_index+1; i < vec.size() && vec[i]->flags.bits.continuation; i++) + rvec.push_back(vec[i]->id); + + return true; +} + +bool Gui::addCombatReportAuto(df::unit *unit, df::announcement_flags mode, int report_index) +{ + using df::global::world; + + auto &vec = world->status.reports; + auto report = vector_get(vec, report_index); + + if (!unit || !report) + return false; + + bool ok = false; + + if (mode.bits.UNIT_COMBAT_REPORT) + { + if (unit->flags2.bits.sparring) + ok |= addCombatReport(unit, unit_report_type::Sparring, report_index); + else if (unit->job.current_job && unit->job.current_job->job_type == job_type::Hunt) + ok |= addCombatReport(unit, unit_report_type::Hunting, report_index); + else + ok |= addCombatReport(unit, unit_report_type::Combat, report_index); + } + + if (mode.bits.UNIT_COMBAT_REPORT_ALL_ACTIVE) + { + FOR_ENUM_ITEMS(unit_report_type, slot) + { + if (!unit->reports.log[slot].empty() && + unit->reports.last_year[slot] == report->year && + (report->time - unit->reports.last_year_tick[slot]) <= 500) + { + ok |= addCombatReport(unit, slot, report_index); + } + } + } + + return ok; } void Gui::showAnnouncement(std::string message, int color, bool bright) { - doShowAnnouncement(df::announcement_type(0), df::coord(), message, color, bright); + df::announcement_flags mode(0); + mode.bits.D_DISPLAY = mode.bits.A_DISPLAY = true; + + makeAnnouncement(df::announcement_type(0), mode, df::coord(), message, color, bright); } void Gui::showZoomAnnouncement( df::announcement_type type, df::coord pos, std::string message, int color, bool bright ) { - doShowAnnouncement(type, pos, message, color, bright); + df::announcement_flags mode(0); + mode.bits.D_DISPLAY = mode.bits.A_DISPLAY = true; + + makeAnnouncement(type, mode, pos, message, color, bright); } void Gui::showPopupAnnouncement(std::string message, int color, bool bright) @@ -1157,26 +1291,21 @@ void Gui::showPopupAnnouncement(std::string message, int color, bool bright) } void Gui::showAutoAnnouncement( - df::announcement_type type, df::coord pos, std::string message, int color, bool bright + df::announcement_type type, df::coord pos, std::string message, int color, bool bright, + df::unit *unit1, df::unit *unit2 ) { using df::global::announcements; - df::announcement_flags flags; + df::announcement_flags flags(0); + flags.bits.D_DISPLAY = flags.bits.A_DISPLAY = true; + if (is_valid_enum_item(type) && announcements) flags = announcements->flags[type]; - doShowAnnouncement(type, pos, message, color, bright); - - if (flags.bits.DO_MEGA || flags.bits.PAUSE || flags.bits.RECENTER) - { - resetDwarfmodeView(flags.bits.DO_MEGA || flags.bits.PAUSE); - - if (flags.bits.RECENTER && pos.isValid()) - revealInDwarfmodeMap(pos, true); - } + int id = makeAnnouncement(type, flags, pos, message, color, bright); - if (flags.bits.DO_MEGA) - showPopupAnnouncement(message, color, bright); + addCombatReportAuto(unit1, flags, id); + addCombatReportAuto(unit2, flags, id); } df::viewscreen *Gui::getCurViewscreen(bool skip_dismissed)