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)