From f06bbd13dd87d3b776f12eb2d95d1a6fb5934e20 Mon Sep 17 00:00:00 2001 From: JT Traub Date: Tue, 12 Dec 2023 08:46:46 -0800 Subject: [PATCH] Write the text reports from json (#161) Rather than writing the text reports and the json reports from the internal structures, this changes the code to build the json reports once, and then use that to output both the json report (just dump the json) and the text reports (utilize the json data to output the existing textual report format). This does change one small thing which is that now the templates are written within the WriteReports function rather than via a second pass. Since the Json already contains the unit order info, this was the cleanest way to do it. This requires updating the snapshots of the turn run output since the lines about writing templates are now gone. --- Makefile | 4 +- aregion.cpp | 452 +--------- aregion.h | 14 +- battle.cpp | 15 +- battle.h | 3 +- faction.cpp | 336 +------ faction.h | 11 +- game.cpp | 70 +- game.h | 7 +- indenter.hpp | 7 + object.cpp | 143 +-- object.h | 3 +- .../neworigins_turns/turn_0/engine-output.txt | 2 - .../neworigins_turns/turn_1/engine-output.txt | 2 - .../turn_10/engine-output.txt | 2 - .../turn_11/engine-output.txt | 2 - .../turn_12/engine-output.txt | 2 - .../turn_13/engine-output.txt | 2 - .../neworigins_turns/turn_2/engine-output.txt | 2 - .../neworigins_turns/turn_3/engine-output.txt | 2 - .../neworigins_turns/turn_4/engine-output.txt | 2 - .../neworigins_turns/turn_5/engine-output.txt | 2 - .../neworigins_turns/turn_6/engine-output.txt | 2 - .../neworigins_turns/turn_7/engine-output.txt | 2 - .../neworigins_turns/turn_8/engine-output.txt | 2 - .../neworigins_turns/turn_9/engine-output.txt | 2 - snapshot-tests/turns/turn_0/engine-output.txt | 2 - snapshot-tests/turns/turn_1/engine-output.txt | 2 - .../turns/turn_10/engine-output.txt | 2 - .../turns/turn_11/engine-output.txt | 2 - .../turns/turn_12/engine-output.txt | 2 - .../turns/turn_13/engine-output.txt | 2 - snapshot-tests/turns/turn_2/engine-output.txt | 2 - snapshot-tests/turns/turn_3/engine-output.txt | 2 - snapshot-tests/turns/turn_4/engine-output.txt | 2 - snapshot-tests/turns/turn_5/engine-output.txt | 2 - snapshot-tests/turns/turn_6/engine-output.txt | 2 - snapshot-tests/turns/turn_7/engine-output.txt | 2 - snapshot-tests/turns/turn_8/engine-output.txt | 2 - snapshot-tests/turns/turn_9/engine-output.txt | 2 - template.cpp | 587 ------------ text_report_generator.cpp | 846 ++++++++++++++++++ text_report_generator.hpp | 45 + unit.cpp | 237 +---- unit.h | 6 +- unittest/faction_test.cpp | 12 - unittest/json_report_test.cpp | 16 +- unittest/testhelper.hpp | 6 + 48 files changed, 1033 insertions(+), 1843 deletions(-) delete mode 100644 template.cpp create mode 100644 text_report_generator.cpp create mode 100644 text_report_generator.hpp diff --git a/Makefile b/Makefile index f1c8fe77..c001952b 100644 --- a/Makefile +++ b/Makefile @@ -19,9 +19,9 @@ ENGINE_OBJECTS = alist.o aregion.o army.o astring.o battle.o economy.o \ edit.o faction.o game.o gamedata.o gamedefs.o gameio.o \ genrules.o i_rand.o items.o main.o market.o modify.o monthorders.o \ npc.o object.o orders.o parseorders.o production.o quests.o runorders.o \ - shields.o skills.o skillshows.o specials.o spells.o template.o unit.o \ + shields.o skills.o skillshows.o specials.o spells.o unit.o \ events.o events-battle.o events-assassination.o mapgen.o simplex.o namegen.o \ - indenter.o + indenter.o text_report_generator.o UNITTEST_SRC = unittest/main.cpp unittest/testhelper.cpp $(wildcard unittest/*_test.cpp) UNITTEST_OBJECTS = $(patsubst unittest/%.cpp,unittest/obj/%.o,$(UNITTEST_SRC)) diff --git a/aregion.cpp b/aregion.cpp index e531a5e5..13cfbf84 100644 --- a/aregion.cpp +++ b/aregion.cpp @@ -1196,29 +1196,6 @@ int ARegion::CanMakeAdv(Faction *fac, int item) return 0; } -void ARegion::WriteProducts(ostream& f, Faction *fac, int present) -{ - Production *p = get_production_for_skill(I_SILVER, S_ENTERTAINMENT); - if (p) { - f << "Entertainment available: $" - << (((Globals->TRANSIT_REPORT & GameDefs::REPORT_SHOW_ENTERTAINMENT) || present) ? p->amount : 0) - << ".\n"; - } - - f << "Products: "; - bool has = false; - for (const auto& p : products) { - if (p->itemtype == I_SILVER) continue; - // Advanced items are special.. Skip unless we are allowed to see them. - if (ItemDefs[p->itemtype].type & IT_ADVANCED && !(CanMakeAdv(fac, p->itemtype) || fac->is_npc)) continue; - // For normal items, skip if we aren't allowed to see them. - if (!present && !(Globals->TRANSIT_REPORT & GameDefs::REPORT_SHOW_RESOURCES)) continue; - f << (has ? ", " : "") << p->WriteReport(); - has = true; - } - f << (has ? "." : "none.") << '\n'; -} - int ARegion::HasItem(Faction *fac, int item) { forlist(&objects) { @@ -1233,74 +1210,6 @@ int ARegion::HasItem(Faction *fac, int item) return 0; } -void ARegion::WriteMarkets(ostream &f, Faction *fac, int present) -{ - f << "Wanted: "; - int has = 0; - for (const auto& m : markets) { - if (!m->amount) continue; - if (!present && !(Globals->TRANSIT_REPORT & GameDefs::REPORT_SHOW_MARKETS)) continue; - if (m->type == M_SELL) { - if (ItemDefs[m->item].type & IT_ADVANCED) { - if (!Globals->MARKETS_SHOW_ADVANCED_ITEMS) { - if (!HasItem(fac, m->item)) { - continue; - } - } - } - f << (has ? ", " : "") << m->Report().const_str(); - has = 1; - } - } - f << (has ? "." : "none.") << '\n'; - - f << "For Sale: "; - has = 0; - for (const auto& m : markets) { - if (!m->amount) continue; - if (!present && !(Globals->TRANSIT_REPORT & GameDefs::REPORT_SHOW_MARKETS)) continue; - if (m->type == M_BUY) { - f << (has ? ", " : "") << m->Report().const_str(); - has = 1; - } - } - f << (has ? "." : "none.") << '\n'; -} - -void ARegion::WriteEconomy(ostream& f, Faction *fac, int present) -{ - f << indent::incr; - - if ((Globals->TRANSIT_REPORT & GameDefs::REPORT_SHOW_WAGES) || present) { - f << "Wages: " << WagesForReport().const_str() << ".\n"; - } else { - f << "Wages: $0.\n"; - } - - WriteMarkets(f, fac, present); - - WriteProducts(f, fac, present); - - f << indent::decr << '\n'; -} - -void ARegion::WriteExits(ostream& f, ARegionList *pRegs, int *exits_seen) -{ - f << "Exits:\n"; - f << indent::incr; - int y = 0; - for (int i=0; iPrint(pRegs) << ".\n"; - y = 1; - } - } - if (!y) f << "none\n"; - f << indent::decr; - f << '\n'; -} - json ARegion::basic_json_data(ARegionList *regions) { json j; j["terrain"] = TerrainDefs[type].name; @@ -1309,6 +1218,26 @@ json ARegion::basic_json_data(ARegionList *regions) { string label = (level->strName ? level->strName->const_str() : "surface"); j["coordinates"] = { { "x", xloc }, { "y", yloc }, { "z", zloc }, { "label", label } }; + // in order to support games with different UW settings, we need to put a bit more information in the JSON to + // make the text report easier.. exact depth being hidden is really stupid, but that's the way the text report is. + if (!Globals->EASIER_UNDERWORLD) { + string z_prefix = ""; + if (zloc >= 2 && zloc < Globals->UNDERWORLD_LEVELS + 2) { + for (int i = zloc; i > 3; i--) { + z_prefix += "very "; + } + z_prefix += "deep "; + } else if ((zloc > Globals->UNDERWORLD_LEVELS + 2) && + (zloc < Globals->UNDERWORLD_LEVELS + + Globals->UNDERDEEP_LEVELS + 2)) { + for (int i = zloc; i > Globals->UNDERWORLD_LEVELS + 3; i--) { + z_prefix += "very "; + } + z_prefix += "deep "; + } + if (!z_prefix.empty()) j["coordinates"]["depth_prefix"] = z_prefix; + } + j["province"] = name->const_str(); if (town) { j["settlement"] = { { "name", town->name->const_str() }, { "size", TownString(town->TownType()).const_str() } }; @@ -1316,15 +1245,16 @@ json ARegion::basic_json_data(ARegionList *regions) { return j; } -void ARegion::write_json_report(json& j, Faction *fac, int month, ARegionList *regions) { +void ARegion::build_json_report(json& j, Faction *fac, int month, ARegionList *regions) { Farsight *farsight = GetFarsight(&farsees, fac); Farsight *passer = GetFarsight(&passers, fac); - int present = Present(fac) || fac->is_npc; + bool present = (Present(fac) == 1) || fac->is_npc; // this faction cannot see this region, why are we even here? if (!farsight && !passer && !present) return; j = basic_json_data(regions); + j["present"] = present && !fac->is_npc; if (Population() && (present || farsight || (Globals->TRANSIT_REPORT & GameDefs::REPORT_SHOW_PEASANTS))) { j["population"] = { { "amount", Population() } }; @@ -1353,7 +1283,7 @@ void ARegion::write_json_report(json& j, Faction *fac, int month, ARegionList *r Production *p = get_production_for_skill(I_SILVER, -1); double wages = p ? p->productivity / 10.0 : 0; auto max_wages = p ? p->amount : 0; - j["wages"] = (p && ((Globals->TRANSIT_REPORT & GameDefs::REPORT_SHOW_WAGES) || present)) + j["wages"] = (p && ((Globals->TRANSIT_REPORT & GameDefs::REPORT_SHOW_WAGES) || present || farsight)) ? json{ { "amount", wages }, { "max", max_wages } } : json{ { "amount", 0 } }; @@ -1361,7 +1291,7 @@ void ARegion::write_json_report(json& j, Faction *fac, int month, ARegionList *r json for_sale = json::array(); for (const auto& m : markets) { if (!m->amount) continue; - if (!present && !(Globals->TRANSIT_REPORT & GameDefs::REPORT_SHOW_MARKETS)) continue; + if (!present && !farsight && !(Globals->TRANSIT_REPORT & GameDefs::REPORT_SHOW_MARKETS)) continue; ItemType itemdef = ItemDefs[m->item]; json item = { @@ -1384,8 +1314,10 @@ void ARegion::write_json_report(json& j, Faction *fac, int month, ARegionList *r j["markets"] = { { "wanted", wanted }, { "for_sale", for_sale } }; p = get_production_for_skill(I_SILVER, S_ENTERTAINMENT); - j["entertainment"] = p && (present || (Globals->TRANSIT_REPORT & GameDefs::REPORT_SHOW_ENTERTAINMENT)) - ? p->amount : 0; + if (p) { + j["entertainment"] = + (present || farsight || (Globals->TRANSIT_REPORT & GameDefs::REPORT_SHOW_ENTERTAINMENT)) ? p->amount : 0; + } j["products"] = json::array(); for (const auto& p : products) { @@ -1394,7 +1326,7 @@ void ARegion::write_json_report(json& j, Faction *fac, int month, ARegionList *r // Advanced items have slightly different rules, so call CanMakeAdv (poorly named) to see if we can see them. if (itemdef.type & IT_ADVANCED && !(CanMakeAdv(fac, p->itemtype) || fac->is_npc)) continue; // If it's a normal resource, and we aren't here or not showing it, skip it. - if (!present && !(Globals->TRANSIT_REPORT & GameDefs::REPORT_SHOW_RESOURCES)) continue; + if (!present && !farsight && !(Globals->TRANSIT_REPORT & GameDefs::REPORT_SHOW_RESOURCES)) continue; json item = { { "name", itemdef.name }, { "plural", itemdef.names }, { "tag", itemdef.abr }, }; @@ -1516,329 +1448,7 @@ void ARegion::write_json_report(json& j, Faction *fac, int month, ARegionList *r { forlist (&objects) { Object *o = (Object *) elem; - o->write_json_report(j, fac, obs, truesight, detfac, passobs, passtrue, passdetfac, present || farsight); - } - } -} - -void ARegion::write_text_report(ostream &f, Faction *fac, int month, ARegionList *pRegions) -{ - Farsight *farsight = GetFarsight(&farsees, fac); - Farsight *passer = GetFarsight(&passers, fac); - int present = Present(fac) || fac->is_npc; - - if (farsight || passer || present) { - AString temp = Print(pRegions); - if (Population() && - (present || farsight || - (Globals->TRANSIT_REPORT & GameDefs::REPORT_SHOW_PEASANTS))) { - temp += AString(", ") + Population() + " peasants"; - if (Globals->RACES_EXIST) { - temp += AString(" (") + ItemDefs[race].names + ")"; - } - if (present || farsight || - Globals->TRANSIT_REPORT & GameDefs::REPORT_SHOW_REGION_MONEY) { - temp += AString(", $") + wealth; - } else { - temp += AString(", $0"); - } - } - temp += "."; - f << temp.const_str() << "\n"; - f << "------------------------------------------------------------\n"; - - f << indent::incr; - if (Globals->WEATHER_EXISTS) { - temp = "It was "; - if (clearskies) temp += "unnaturally clear "; - else { - if (weather == W_BLIZZARD) temp = "There was an unnatural "; - else if (weather == W_NORMAL) temp = "The weather was "; - temp += SeasonNames[weather]; - } - temp += " last month; "; - int nxtweather = pRegions->GetWeather(this, (month + 1) % 12); - temp += "it will be "; - temp += SeasonNames[nxtweather]; - temp += " next month."; - f << temp.const_str() << "\n"; - } - - // Freezing effect - if (weather == W_BLIZZARD && !clearskies) { - temp = "There was an unnatural freezing last month."; - f << temp.const_str() << "\n"; - } - -#if 0 - f << "\nElevation is " << elevation << ".\n"; - f << "Humidity is " << humidity << ".\n"; - f << "Temperature is " << temperature << ".\n"; -#endif - - if (type == R_NEXUS) { - f << '\n' << Globals->WORLD_NAME << " Nexus is a magical place: the entryway " - << "to the world of " << Globals->WORLD_NAME << ". Enjoy your stay; " - << "the city guards should keep you safe as long as you should choose " - << "to stay. However, rumor has it that once you have left the Nexus, " - << "you can never return.\n\n"; - } - - f << indent::decr; - - WriteEconomy(f, fac, present || farsight); - - int exits_seen[NDIRS]; - if (present || farsight || - (Globals->TRANSIT_REPORT & GameDefs::REPORT_SHOW_ALL_EXITS)) { - for (int i = 0; i < NDIRS; i++) - exits_seen[i] = 1; - } else { - // This is just a transit report and we're not showing all - // exits. See if we are showing used exits. - - // Show none by default. - int i; - for (i = 0; i < NDIRS; i++) - exits_seen[i] = 0; - // Now, if we should, show the ones actually used. - if (Globals->TRANSIT_REPORT & GameDefs::REPORT_SHOW_USED_EXITS) { - forlist(&passers) { - Farsight *p = (Farsight *)elem; - if (p->faction == fac) { - for (i = 0; i < NDIRS; i++) { - exits_seen[i] |= p->exits_used[i]; - } - } - } - } - } - - WriteExits(f, pRegions, exits_seen); - - if (Globals->GATES_EXIST && gate && gate != -1) { - int sawgate = 0; - if (fac->is_npc) - sawgate = 1; - if (Globals->IMPROVED_FARSIGHT && farsight) { - forlist(&farsees) { - Farsight *watcher = (Farsight *)elem; - if (watcher && watcher->faction == fac && watcher->unit) { - if (watcher->unit->GetSkill(S_GATE_LORE)) { - sawgate = 1; - } - } - } - } - if (Globals->TRANSIT_REPORT & GameDefs::REPORT_USE_UNIT_SKILLS) { - forlist(&passers) { - Farsight *watcher = (Farsight *)elem; - if (watcher && watcher->faction == fac && watcher->unit) { - if (watcher->unit->GetSkill(S_GATE_LORE)) { - sawgate = 1; - } - } - } - } - forlist(&objects) { - Object *o = (Object *) elem; - forlist(&o->units) { - Unit *u = (Unit *) elem; - if (!sawgate && - ((u->faction == fac) && - u->GetSkill(S_GATE_LORE))) { - sawgate = 1; - } - } - } - if (sawgate) { - if (gateopen) { - f << "There is a Gate here (Gate " << gate; - if (!Globals->DISPERSE_GATE_NUMBERS) { - f << " of " << pRegions->numberofgates; - } - f << ").\n\n"; - } else if (Globals->SHOW_CLOSED_GATES) { - f << "There is a closed Gate here.\n\n"; - } - } - } - - int obs = GetObservation(fac, 0); - int truesight = GetTrueSight(fac, 0); - int detfac = 0; - - int passobs = GetObservation(fac, 1); - int passtrue = GetTrueSight(fac, 1); - int passdetfac = detfac; - - if (fac->is_npc) { - obs = 10; - passobs = 10; - } - - forlist (&objects) { - Object *o = (Object *) elem; - forlist(&o->units) { - Unit *u = (Unit *) elem; - if (u->faction == fac && u->GetSkill(S_MIND_READING) > 1) { - detfac = 1; - } - } - } - if (Globals->IMPROVED_FARSIGHT && farsight) { - forlist(&farsees) { - Farsight *watcher = (Farsight *)elem; - if (watcher && watcher->faction == fac && watcher->unit) { - if (watcher->unit->GetSkill(S_MIND_READING) > 1) { - detfac = 1; - } - } - } - } - - if ((Globals->TRANSIT_REPORT & GameDefs::REPORT_USE_UNIT_SKILLS) && - (Globals->TRANSIT_REPORT & GameDefs::REPORT_SHOW_UNITS)) { - forlist(&passers) { - Farsight *watcher = (Farsight *)elem; - if (watcher && watcher->faction == fac && watcher->unit) { - if (watcher->unit->GetSkill(S_MIND_READING) > 1) { - passdetfac = 1; - } - } - } - } - - { - forlist (&objects) { - ((Object *) elem)->write_text_report(f, fac, obs, truesight, detfac, - passobs, passtrue, passdetfac, - present || farsight); - } - f << '\n'; - } - } -} - -// DK -void ARegion::WriteTemplate(ostream& f, Faction *fac, ARegionList *pRegs, int month) -{ - int header = 0, order; - AString temp, *token; - - forlist (&objects) { - Object *o = (Object *) elem; - forlist(&o->units) { - Unit *u = (Unit *) elem; - if (u->faction == fac) { - if (!header) { - // DK - if (fac->temformat == TEMPLATE_MAP) { - WriteTemplateHeader(f, fac, pRegs, month); - } else { - temp = AString("*** ") + Print(pRegs) + AString(" ***"); - f << '\n'; - f << indent::comment << temp.const_str() << '\n'; - } - header = 1; - } - - f << "\nunit " << u->num << '\n'; - // DK - if (fac->temformat == TEMPLATE_LONG || - fac->temformat == TEMPLATE_MAP) { - f << indent::comment << u->TemplateReport() << '\n'; - } - int gotMonthOrder = 0; - forlist(&(u->oldorders)) { - temp = *((AString *) elem); - temp.getat(); - token = temp.gettoken(); - if (token) { - order = Parse1Order(token); - delete token; - } else - order = NORDERS; - switch (order) { - case O_MOVE: - case O_SAIL: - case O_TEACH: - case O_STUDY: - case O_BUILD: - case O_PRODUCE: - case O_ENTERTAIN: - case O_WORK: - gotMonthOrder = 1; - break; - case O_TAX: - case O_PILLAGE: - if (Globals->TAX_PILLAGE_MONTH_LONG) - gotMonthOrder = 1; - break; - } - if (order == O_ENDTURN || order == O_ENDFORM) - f << indent::decr; - f << *((AString *) elem) << '\n'; - if (order == O_TURN || order == O_FORM) - f << indent::incr; - } - f << indent::set_indent(0); - u->oldorders.DeleteAll(); - - int firstMonthOrder = gotMonthOrder; - if (u->turnorders.First()) { - TurnOrder *tOrder; - forlist(&u->turnorders) { - tOrder = (TurnOrder *)elem; - if (firstMonthOrder) { - f << (tOrder->repeating ? "@" : "") << "TURN\n"; - f << indent::incr; - } - forlist(&tOrder->turnOrders) { - temp = *((AString *) elem); - temp.getat(); - token = temp.gettoken(); - if (token) { - order = Parse1Order(token); - delete token; - } else - order = NORDERS; - if (order == O_ENDTURN || order == O_ENDFORM) - f << indent::decr; - f << *((AString *) elem) << '\n'; - if (order == O_TURN || order == O_FORM) - f << indent::incr; - } - if (firstMonthOrder) { - f << indent::decr << "ENDTURN\n"; - } - firstMonthOrder = 1; - f << indent::set_indent(0); - } - tOrder = (TurnOrder *) u->turnorders.First(); - if (tOrder->repeating && !gotMonthOrder) { - f << "@TURN\n"; - f << indent::incr; - forlist(&tOrder->turnOrders) { - temp = *((AString *) elem); - temp.getat(); - token = temp.gettoken(); - if (token) { - order = Parse1Order(token); - delete token; - } else - order = NORDERS; - if (order == O_ENDTURN || order == O_ENDFORM) - f << indent::decr; - f << *((AString *) elem) << '\n'; - if (order == O_TURN || order == O_FORM) - f << indent::incr; - } - f << indent::set_indent(0) << "ENDTURN\n"; - } - } - u->turnorders.DeleteAll(); - } + o->build_json_report(j, fac, obs, truesight, detfac, passobs, passtrue, passdetfac, present || farsight); } } } diff --git a/aregion.h b/aregion.h index cfb244ef..003a5c4f 100644 --- a/aregion.h +++ b/aregion.h @@ -197,17 +197,7 @@ class ARegion : public AListElem int CanMakeAdv(Faction *, int); int HasItem(Faction *, int); - void WriteProducts(ostream& f, Faction *, int); - void WriteMarkets(ostream& f, Faction *, int); - void WriteEconomy(ostream& f, Faction *, int); - void WriteExits(ostream& f, ARegionList *pRegs, int *exits_seen); - void write_text_report(ostream& f, Faction *fac, int month, ARegionList *pRegions); - json basic_json_data(ARegionList *pRegions); - void write_json_report(json& j, Faction *fac, int month, ARegionList *pRegions); - // DK - void WriteTemplate(ostream& f, Faction *, ARegionList *, int); - void WriteTemplateHeader(ostream& f, Faction *, ARegionList *, int); - void GetMapLine(char *, int, ARegionList *); + void build_json_report(json& j, Faction *fac, int month, ARegionList *pRegions); AString ShortPrint(ARegionList *pRegs); AString Print(ARegionList *pRegs); @@ -385,6 +375,8 @@ class ARegion : public AListElem std::vector GetPossibleLairs(); void SetupHabitat(TerrainType* terrain); void SetupEconomy(); + + json basic_json_data(ARegionList *pRegions); }; class ARegionArray diff --git a/battle.cpp b/battle.cpp index 9cefe425..9b4b07f9 100644 --- a/battle.cpp +++ b/battle.cpp @@ -732,26 +732,17 @@ void Battle::WriteSides(ARegion * r, AddLine(""); } -void Battle::write_json_report(json& j, Faction *fac) { +void Battle::build_json_report(json& j, Faction *fac) { if(assassination == ASS_SUCC && fac != attacker) { j["type"] = "assassination"; - j["report"] = asstext; + j["report"] = json::array(); + j["report"].push_back(asstext); return; } j["type"] = "battle"; j["report"] = text; } -void Battle::write_text_report(ostream& f,Faction * fac) { - if (assassination == ASS_SUCC && fac != attacker) { - f << asstext << "\n"; - return; - } - for (const auto& line: text) { - f << line << '\n'; - } -} - void Battle::AddLine(const AString & s) { text.push_back(s.const_str()); } diff --git a/battle.h b/battle.h index 3f7bb11b..074a79b8 100644 --- a/battle.h +++ b/battle.h @@ -57,8 +57,7 @@ class Battle : public AListElem Battle(); ~Battle(); - void write_text_report(ostream& f, Faction *fac); - void write_json_report(json &j, Faction *fac); + void build_json_report(json &j, Faction *fac); void AddLine(const AString &); int Run(Events* events, ARegion *, Unit *, AList *, Unit *, AList *, int ass, diff --git a/faction.cpp b/faction.cpp index e5ae75f7..1d355a32 100644 --- a/faction.cpp +++ b/faction.cpp @@ -251,34 +251,6 @@ void Faction::SetAddress(AString &strNewAddress) address = new AString(strNewAddress); } -AString Faction::FactionTypeStr() -{ - AString temp; - if (is_npc) return AString("NPC"); - - if (Globals->FACTION_LIMIT_TYPE == GameDefs::FACLIM_UNLIMITED) { - return (AString("Unlimited")); - } else if (Globals->FACTION_LIMIT_TYPE == GameDefs::FACLIM_MAGE_COUNT) { - return(AString("Normal")); - } else if (Globals->FACTION_LIMIT_TYPE == GameDefs::FACLIM_FACTION_TYPES) { - bool comma = false; - - for (auto &ft : *FactionTypes) { - auto value = type[ft]; - if (value) { - if (comma) { - temp += ", "; - } else { - comma = true; - } - temp += AString(ft) + " " + value; - } - } - if (!comma) return AString("none"); - } - return temp; -} - vector Faction::compute_faction_statistics(Game *game, size_t **citems) { vector stats; // To allow testing, just return an empty vector if we don't have any item arrays inbound @@ -342,50 +314,7 @@ static inline GmData collect_gm_data() { return data; } -void Faction::write_text_gm_report(ostream& f, Game *game) { - GmData data = collect_gm_data(); - - bool need_header = true; - for (auto &skillshow : data.skills) { - if(need_header) { f << "Skill reports:\n"; need_header = false; } - AString *string = skillshow.Report(this); - if (string) { - f << '\n' << string->const_str() << '\n'; - delete string; - } - } - if(!need_header) f << '\n'; - - need_header = true; - for (const auto& itemshow : data.items) { - if(need_header) { f << "Item reports:\n"; need_header = false; } - f << '\n' << itemshow << '\n'; - } - if (!need_header) f << '\n'; - - need_header = true; - for (const auto& objectshow : data.objects) { - if(need_header) { f << "Object reports:\n"; need_header = false; } - f << '\n' << objectshow << '\n'; - } - if (!need_header) f << '\n'; - - present_regions.clear(); - forlist(&(game->regions)) { - ARegion *reg = (ARegion *)elem; - present_regions.push_back(reg); - } - for (const auto& reg: present_regions) { - reg->write_text_report(f, this, game->month, &(game->regions)); - } - present_regions.clear(); - - errors.clear(); - events.clear(); - battles.clear(); -} - -void Faction::write_json_gm_report(json& j, Game *game) { +void Faction::build_gm_json_report(json& j, Game *game) { GmData data = collect_gm_data(); json skills = json::array(); @@ -410,7 +339,7 @@ void Faction::write_json_gm_report(json& j, Game *game) { json regions = json::array(); for (const auto& reg: present_regions) { json region; - reg->write_json_report(region, this, game->month, &(game->regions)); + reg->build_json_report(region, this, game->month, &(game->regions)); regions.push_back(region); } j["regions"] = regions; @@ -421,9 +350,9 @@ void Faction::write_json_gm_report(json& j, Game *game) { battles.clear(); } -void Faction::write_json_report(json& j, Game *game, size_t **citems) { +void Faction::build_json_report(json& j, Game *game, size_t **citems) { if (gets_gm_report(game)) { - write_json_gm_report(j, game); + build_gm_json_report(j, game); return; } @@ -441,18 +370,21 @@ void Faction::write_json_report(json& j, Game *game, size_t **citems) { string s = name->const_str(); j["name"] = s.substr(0, s.find(" (")); // remove the faction number from the name for json output j["number"] = num; - j["email"] = address->const_str(); - j["password"] = password->const_str(); if (Globals->FACTION_LIMIT_TYPE == GameDefs::FACLIM_FACTION_TYPES) { j["type"] = json::object(); for (auto &ft : *FactionTypes) { string factype = ft; - transform(factype.begin(), factype.end(), factype.begin(), ::tolower); + std::transform(factype.begin(), factype.end(), factype.begin(), ::tolower); if (type[ft]) j["type"][factype] = type[ft]; } } - j["times_sent"] = (times != 0); - j["password_unset"] = (!password || *password == "none"); + j["administrative"]["times_sent"] = (times != 0); + bool password_unset = (!password || *password == "none"); + j["administrative"]["password_unset"] = password_unset; + j["administrative"]["email"] = address->const_str(); + j["administrative"]["show_unit_attitudes"] = (showunitattitudes != 0); + + if(!password_unset) j["administrative"]["password"] = password->const_str(); if(Globals->MAX_INACTIVE_TURNS) { int cturn = game->TurnNumber() - lastorders; if ((cturn >= (Globals->MAX_INACTIVE_TURNS - 3)) && !is_npc) { @@ -468,19 +400,24 @@ void Faction::write_json_report(json& j, Game *game, size_t **citems) { { "year", game->year } }; - if (Globals->FACTION_LIMIT_TYPE == GameDefs::FACLIM_MAGE_COUNT) { + if ((Globals->FACTION_LIMIT_TYPE == GameDefs::FACLIM_MAGE_COUNT) + || (Globals->FACTION_LIMIT_TYPE == GameDefs::FACLIM_FACTION_TYPES)) { j["status"]["mages"] = { { "current", nummages }, { "allowed", game->AllowedMages(this) } }; if (Globals->APPRENTICES_EXIST) { + string name = Globals->APPRENTICE_NAME; + name[0] = toupper(name[0]); j["status"]["apprentices"] = { { "current", numapprentices }, - { "allowed", game->AllowedApprentices(this) } + { "allowed", game->AllowedApprentices(this) }, + { "name", name } }; } - } else if (Globals->FACTION_LIMIT_TYPE == GameDefs::FACLIM_FACTION_TYPES) { + } + if (Globals->FACTION_LIMIT_TYPE == GameDefs::FACLIM_FACTION_TYPES) { if (Globals->FACTION_ACTIVITY != FactionActivityRules::DEFAULT) { int currentCost = GetActivityCost(FactionActivity::TAX); int maxAllowedCost = game->AllowedMartial(this); @@ -511,16 +448,6 @@ void Faction::write_json_report(json& j, Game *game, size_t **citems) { { "allowed", game->AllowedTacticians(this) } }; } - j["status"]["mages"] = { - { "current", nummages }, - { "allowed", game->AllowedMages(this) } - }; - if (Globals->APPRENTICES_EXIST) { - j["status"]["apprentices"] = { - { "current", numapprentices }, - { "allowed", game->AllowedApprentices(this) } - }; - } } if (!errors.empty()) { @@ -534,7 +461,7 @@ void Faction::write_json_report(json& j, Game *game, size_t **citems) { for (const auto& battle: battles) { json jbattle = json::object(); // we will obviously want to make this into json-y output - battle->write_json_report(jbattle, this); + battle->build_json_report(jbattle, this); jbattles.push_back(jbattle); } j["battles"] = jbattles; @@ -548,12 +475,12 @@ void Faction::write_json_report(json& j, Game *game, size_t **citems) { /* Attitudes */ j["attitudes"] = json::object(); string defattitude = AttitudeStrs[defaultattitude]; - transform(defattitude.begin(), defattitude.end(), defattitude.begin(), ::tolower); + std::transform(defattitude.begin(), defattitude.end(), defattitude.begin(), ::tolower); j["attitudes"]["default"] = defattitude; for (int i=0; iwrite_json_report(region, this, game->month, &(game->regions)); + reg->build_json_report(region, this, game->month, &(game->regions)); regions.push_back(region); } j["regions"] = regions; } -void Faction::write_text_report(ostream& f, Game *pGame, size_t ** citems) -{ - // make the output automatically wrap at 70 characters - f << indent::wrap; - - if(gets_gm_report(pGame)) { - write_text_gm_report(f, pGame); - return; - } - - if (Globals->FACTION_STATISTICS) { - f << ";Treasury:\n"; - f << ";\n"; - f << ";Item Rank Max Total\n"; - f << ";=====================================================================\n"; - for (const auto& stat : compute_faction_statistics(pGame, citems)) { - f << ';' << stat << '\n'; - } - f << '\n'; - } - - f << "Atlantis Report For:\n"; - if ((Globals->FACTION_LIMIT_TYPE == GameDefs::FACLIM_MAGE_COUNT) || - (Globals->FACTION_LIMIT_TYPE == GameDefs::FACLIM_UNLIMITED)) { - f << name->const_str() << '\n'; - } else if (Globals->FACTION_LIMIT_TYPE == GameDefs::FACLIM_FACTION_TYPES) { - f << name->const_str() << " (" << FactionTypeStr().const_str() << ")\n"; - } - f << MonthNames[pGame->month] << ", Year " << pGame->year << "\n\n"; - - f << "Atlantis Engine Version: " << ATL_VER_STRING(CURRENT_ATL_VER) << '\n'; - f << Globals->RULESET_NAME << ", Version: " << ATL_VER_STRING(Globals->RULESET_VERSION) << "\n\n"; - - if (!times) { - f << "Note: The Times is not being sent to you.\n\n"; - } - - if (!password || (*password == "none")) { - f << "REMINDER: You have not set a password for your faction!\n\n"; - } - - if (Globals->MAX_INACTIVE_TURNS != -1) { - int cturn = pGame->TurnNumber() - lastorders; - if ((cturn >= (Globals->MAX_INACTIVE_TURNS - 3)) && !is_npc) { - cturn = Globals->MAX_INACTIVE_TURNS - cturn; - f << "WARNING: You have " << cturn - << " turns until your faction is automatically removed due to inactivity!\n\n"; - } - } - - if (!exists) { - if (quit == QUIT_AND_RESTART) { - f << "You restarted your faction this turn. This faction " - << "has been removed, and a new faction has been started " - << "for you. (Your new faction report will come in a " - << "separate message.)\n"; - } else if (quit == QUIT_GAME_OVER) { - f << "I'm sorry, the game has ended. Better luck in " - << "the next game you play!\n"; - } else if (quit == QUIT_WON_GAME) { - f << "Congratulations, you have won the game!\n"; - } else { - f << "I'm sorry, your faction has been eliminated.\n" - << "If you wish to restart, please let the " - << "Gamemaster know, and you will be restarted for " - << "the next available turn.\n"; - } - f << '\n'; - } - - f << "Faction Status:\n"; - if (Globals->FACTION_LIMIT_TYPE == GameDefs::FACLIM_MAGE_COUNT) { - f << "Mages: " << nummages << " (" << pGame->AllowedMages(this) << ")\n"; - - if (Globals->APPRENTICES_EXIST) { - string name = Globals->APPRENTICE_NAME; - name[0] = toupper(name[0]); - f << name << "s: " << numapprentices << " (" << pGame->AllowedApprentices(this) << ")\n"; - } - } - else if (Globals->FACTION_LIMIT_TYPE == GameDefs::FACLIM_FACTION_TYPES) { - if (Globals->FACTION_ACTIVITY != FactionActivityRules::DEFAULT) { - int currentCost = GetActivityCost(FactionActivity::TAX); - int maxAllowedCost = pGame->AllowedMartial(this); - - bool isMerged = Globals->FACTION_ACTIVITY == FactionActivityRules::MARTIAL_MERGED; - f << (isMerged ? "Regions: " : "Activity: ") << currentCost << " (" << maxAllowedCost << ")\n"; - } else { - int taxRegions = GetActivityCost(FactionActivity::TAX); - int tradeRegions = GetActivityCost(FactionActivity::TRADE); - f << "Tax Regions: " << taxRegions << " (" << pGame->AllowedTaxes(this) << ")\n"; - f << "Trade Regions: " << tradeRegions << " (" << pGame->AllowedTrades(this) << ")\n"; - } - - if (Globals->TRANSPORT & GameDefs::ALLOW_TRANSPORT) { - f << "Quartermasters: " << numqms << " (" << pGame->AllowedQuarterMasters(this) << ")\n"; - } - - if (Globals->TACTICS_NEEDS_WAR) { - f << "Tacticians: " << numtacts << " (" << pGame->AllowedTacticians(this) << ")\n"; - } - - f << "Mages: " << nummages << " (" << pGame->AllowedMages(this) << ")\n"; - - if (Globals->APPRENTICES_EXIST) { - string name = Globals->APPRENTICE_NAME; - name[0] = toupper(name[0]); - f << name << "s: " << numapprentices << " (" << pGame->AllowedApprentices(this) << ")\n"; - } - } - f << '\n'; - - if (!errors.empty()) { - f << "Errors during turn:\n"; - for (const auto& error : errors) f << error << '\n'; - f << '\n'; - } - - if (!battles.empty()) { - f << "Battles during turn:\n"; - for (const auto& battle: battles) { - battle->write_text_report(f, this); - } - } - - if (!events.empty()) { - f << "Events during turn:\n"; - for (const auto& event: events) f << event << '\n'; - f << '\n'; - } - - if (!shows.empty()) { - f << "Skill reports:\n"; - for (const auto& skillshow : shows) { - AString *string = skillshow.Report(this); - if (string) { - f << '\n' << string->const_str() << '\n'; - delete string; - } - } - f << '\n'; - } - - if (!itemshows.empty()) { - f << "Item reports:\n"; - for (const auto& itemshow : itemshows) f << '\n' << itemshow << '\n'; - f << '\n'; - } - - if (!objectshows.empty()) { - f << "Object reports:\n"; - for (const auto& objectshow : objectshows) f << '\n' << objectshow << '\n'; - f << '\n'; - } - - /* Attitudes */ - f << "Declared Attitudes (default " << AttitudeStrs[defaultattitude] << "):\n"; - for (int i = 0; i < NATTITUDES; i++) { - int j = 0; - f << AttitudeStrs[i] << " : "; - for (const auto& attitude: attitudes) { - if (attitude.attitude == i) { - if (j) f << ", "; - f << GetFaction(&(pGame->factions), attitude.factionnum)->name->const_str(); - j = 1; - } - } - if (!j) f << "none"; - f << ".\n"; - } - f << '\n'; - - f << "Unclaimed silver: " << unclaimed << ".\n\n"; - - for (const auto& reg: present_regions) { - reg->write_text_report(f, this, pGame->month, &(pGame->regions)); - } - f << '\n'; -} - -// LLS - write order template -void Faction::WriteTemplate(ostream& f, Game *pGame) -{ - AString temp; - if (temformat == TEMPLATE_OFF) return; - if (is_npc) return; - - f << indent::wrap; - f << '\n'; - switch (temformat) { - case TEMPLATE_SHORT: - f << "Orders Template (Short Format):\n"; - break; - case TEMPLATE_LONG: - f << "Orders Template (Long Format):\n"; - break; - case TEMPLATE_MAP: - f << "Orders Template (Map Format):\n"; - break; - } - f << '\n'; - f << "#atlantis " << num; - if (!(*password == "none")) { - f << " \"" << *password << "\""; - } - f << '\n'; - - for (const auto& reg: present_regions) { - reg->WriteTemplate(f, this, &(pGame->regions), pGame->month); - } - - f << "\n#end\n\n"; -} - void Faction::WriteFacInfo(ostream &f) { f << "Faction: " << num << '\n'; diff --git a/faction.h b/faction.h index ccd5448c..0633f7f2 100644 --- a/faction.h +++ b/faction.h @@ -161,12 +161,8 @@ class Faction : public AListElem void error(const string &s); void event(const string &s); - AString FactionTypeStr(); - void write_text_report(ostream &f, Game *pGame, size_t **citems); - void write_json_report(json &j, Game *pGame, size_t **citems); + void build_json_report(json& j, Game *pGame, size_t **citems); - // LLS - write order template - void WriteTemplate(ostream &f, Game *pGame); void WriteFacInfo(ostream &f); void set_attitude(int faction_id, int attitude); // attitude -1 clears it @@ -256,10 +252,7 @@ class Faction : public AListElem private: vector compute_faction_statistics(Game *game, size_t **citems); void gm_report_setup(Game *game); - - // Split the gm report from the normal report to make the code more readable. - void write_text_gm_report(ostream &f, Game *game); - void write_json_gm_report(json &j, Game *game); + void build_gm_json_report(json& j, Game *game); }; Faction *GetFaction(AList *, int); diff --git a/game.cpp b/game.cpp index 70c4a1c7..825f1cdf 100644 --- a/game.cpp +++ b/game.cpp @@ -29,17 +29,18 @@ #define F_OK 0 #endif +#include +#include +#include #include -#include "game.h" -#include "unit.h" #include "astring.h" +#include "game.h" #include "gamedata.h" -#include "quests.h" #include "indenter.hpp" -#include -#include -#include +#include "text_report_generator.hpp" +#include "quests.h" +#include "unit.h" #include "external/nlohmann/json.hpp" using json = nlohmann::json; @@ -1066,10 +1067,7 @@ int Game::RunGame() Awrite("Writing the Report File..."); WriteReport(); Awrite(""); - // LLS - write order templates - Awrite("Writing order templates..."); - WriteTemplates(); - Awrite(""); + battles.clear(); EmptyHell(); @@ -1209,21 +1207,35 @@ void Game::WriteReport() forlist(&factions) { Faction *fac = (Faction *) elem; - string str = "report." + to_string(fac->num); + string report_file = "report." + to_string(fac->num); + string template_file = "template." + to_string(fac->num); if (!fac->is_npc || fac->gets_gm_report(this)) { + // Generate the report in JSON format and then write it to whatever formats we want + json json_report; + bool show_region_depth = Globals->EASIER_UNDERWORLD + && (Globals->UNDERWORLD_LEVELS + Globals->UNDERDEEP_LEVELS > 1); + fac->build_json_report(json_report, this, citems); + if (Globals->REPORT_FORMAT & GameDefs::REPORT_FORMAT_TEXT) { - ofstream f(str, ios::out|ios::ate); + TextReportGenerator text_report; + ofstream f(report_file, ios::out | ios::ate); if (f.is_open()) { - fac->write_text_report(f, this, citems); + text_report.output(f, json_report, show_region_depth); + } + if (!fac->is_npc && fac->temformat != TEMPLATE_OFF) { + // even factions which get a gm report do not get a template. + ofstream f(template_file, ios::out | ios::ate); + if (f.is_open()) { + text_report.output_template(f, json_report, fac->temformat, show_region_depth); + } } } + if (Globals->REPORT_FORMAT & GameDefs::REPORT_FORMAT_JSON) { - ofstream jsonf(str + ".json", ios::out | ios::ate); + ofstream jsonf(report_file + ".json", ios::out | ios::ate); if (jsonf.is_open()) { - json json_obj; - fac->write_json_report(json_obj, this, citems); - jsonf << json_obj.dump(2); + jsonf << json_report.dump(2); } } } @@ -1237,24 +1249,6 @@ void Game::WriteReport() } } -// LLS - write order templates for factions -void Game::WriteTemplates() -{ - forlist(&factions) { - Faction *fac = (Faction *) elem; - if (!fac->is_npc) { - string str = "template." + to_string(fac->num); - ofstream f(str.c_str(), ios::out|ios::ate); - if (f.is_open()) { - fac->WriteTemplate(f, this); - } - fac->present_regions.clear(); - } - Adot(); - } -} - - void Game::DeleteDeadFactions() { forlist(&factions) { @@ -1958,15 +1952,9 @@ void Game::CreateCityMon(ARegion *pReg, int percent, int needmage) } u->SetFlag(FLAG_HOLDING,1); u->MoveUnit(pReg->GetDummy()); - /* - Awrite(AString(*u->BattleReport(3))); - */ if ((!Globals->LEADERS_EXIST) && (pReg->type != R_NEXUS)) { u2->SetFlag(FLAG_HOLDING,1); u2->MoveUnit(pReg->GetDummy()); - /* - Awrite(AString(*u2->BattleReport(3))); - */ } if (AC && Globals->START_CITY_MAGES && needmage) { diff --git a/game.h b/game.h index ec8830c2..1b194960 100644 --- a/game.h +++ b/game.h @@ -151,13 +151,8 @@ class Game void ClearOrders(Faction *); void MakeFactionReportLists(); void CountAllSpecialists(); - // void CountAllMages(); - // void CountAllApprentices(); - // void CountAllQuarterMasters(); - // void CountAllTacticians(); + void WriteReport(); - // LLS - write order templates - void WriteTemplates(); void DeleteDeadFactions(); diff --git a/indenter.hpp b/indenter.hpp index 8e0ce292..e7271f4d 100644 --- a/indenter.hpp +++ b/indenter.hpp @@ -1,3 +1,8 @@ +#pragma once + +#ifndef __INDENTER_HPP__ +#define __INDENTER_HPP__ + #include #include #include @@ -210,3 +215,5 @@ namespace indent { return os; } } + +#endif diff --git a/object.cpp b/object.cpp index 7b34e86e..62ae1c84 100644 --- a/object.cpp +++ b/object.cpp @@ -281,7 +281,7 @@ Unit *Object::GetOwner() return(owner); } -void Object::write_json_report(json& j, Faction *fac, int obs, int truesight, +void Object::build_json_report(json& j, Faction *fac, int obs, int truesight, int detfac, int passobs, int passtrue, int passdetfac, int present) { // Exit early if no observable. @@ -302,11 +302,12 @@ void Object::write_json_report(json& j, Faction *fac, int obs, int truesight, if (describe) container["description"] = describe->const_str(); if (IsFleet()) { + container["fleet"] = true; if((GetOwner() && fac == GetOwner()->faction) || (obs > 9)){ container["load"] = FleetLoad(); container["capacity"] = FleetCapacity(); container["sailors"] = FleetSailingSkill(1); - container["fleetsize"] = GetFleetSize(); + container["fleet_size"] = GetFleetSize(); container["max_speed"] = GetFleetSpeed(1); container["damage_percent"] = incomplete; } @@ -330,8 +331,8 @@ void Object::write_json_report(json& j, Faction *fac, int obs, int truesight, } } } else { - if (incomplete) container["incomplete"] = incomplete; - container["inner_location"] = inner != -1; + if (incomplete > 0) container["incomplete"] = incomplete; + if (inner != -1) container["inner_location"] = true; if (runes) container["warding_runes"] = true; if (!(ob.flags & ObjectType::CANENTER)) container["closed"] = true; if (Globals->DECAY && !(ob.flags & ObjectType::NEVERDECAY) && incomplete < 1) { @@ -352,15 +353,15 @@ void Object::write_json_report(json& j, Faction *fac, int obs, int truesight, json unit = json::object(); int attitude = fac->get_attitude(u->faction->num); if (u->faction == fac) { - u->write_json_report(unit, -1, 1, 1, 1, attitude, fac->showunitattitudes); + u->build_json_report(unit, -1, 1, 1, 1, attitude, fac->showunitattitudes); } else { if (present) { - u->write_json_report(unit, obs, truesight, detfac, type != O_DUMMY, attitude, fac->showunitattitudes); + u->build_json_report(unit, obs, truesight, detfac, type != O_DUMMY, attitude, fac->showunitattitudes); } else { if (((type == O_DUMMY) && (Globals->TRANSIT_REPORT & GameDefs::REPORT_SHOW_OUTDOOR_UNITS)) || ((type != O_DUMMY) && (Globals->TRANSIT_REPORT & GameDefs::REPORT_SHOW_INDOOR_UNITS)) || ((u->guard == GUARD_GUARD) && (Globals->TRANSIT_REPORT & GameDefs::REPORT_SHOW_GUARDS))) { - u->write_json_report(unit, passobs, passtrue, passdetfac, type != O_DUMMY, attitude, fac->showunitattitudes); + u->build_json_report(unit, passobs, passtrue, passdetfac, type != O_DUMMY, attitude, fac->showunitattitudes); } } } @@ -373,134 +374,6 @@ void Object::write_json_report(json& j, Faction *fac, int obs, int truesight, } } -void Object::write_text_report(ostream& f, Faction *fac, int obs, int truesight, - int detfac, int passobs, int passtrue, int passdetfac, int present) -{ - ObjectType *ob = &ObjectDefs[type]; - - if ((type != O_DUMMY) && !present) { - if (IsFleet() && - !(Globals->TRANSIT_REPORT & GameDefs::REPORT_SHOW_SHIPS)) { - // This is a ship and we don't see ships in transit - return; - } - if (IsBuilding() && - !(Globals->TRANSIT_REPORT & GameDefs::REPORT_SHOW_BUILDINGS)) { - // This is a building and we don't see buildings in transit - return; - } - if (IsRoad() && - !(Globals->TRANSIT_REPORT & GameDefs::REPORT_SHOW_ROADS)) { - // This is a road and we don't see roads in transit - return; - } - } - - /* Fleet Report */ - if (IsFleet()) { - AString temp = AString("+ ") + *name + " : " + FleetDefinition(); - /* report ships: - for (int item=0; item 0) { - if (num > 1) { - temp += AString(", ") + num + " " + ItemDefs[item].names; - } else { - temp += AString(", ") + num + " " +ItemDefs[item].name; - } - } - } - */ - if ((GetOwner() && fac == GetOwner()->faction) || (obs > 9)){ - temp += ";"; - if (incomplete > 0) { - temp += AString(" ") + incomplete + "% damaged;"; - } - temp += AString(" Load: ") + FleetLoad() + "/" + FleetCapacity() + ";"; - temp += AString(" Sailors: ") + FleetSailingSkill(1) + "/" + GetFleetSize() + ";"; - temp += AString(" MaxSpeed: ") + GetFleetSpeed(1); - } - if ((Globals->PREVENT_SAIL_THROUGH) && - (!Globals->ALLOW_TRIVIAL_PORTAGE)) { - if ((flying < 1) && - (TerrainDefs[region->type].similar_type != R_OCEAN)) { - int dir = 0; - int first = 1; - temp += AString("; Sail directions: "); - for (dir = 0; dir < NDIRS; dir++) { - if (SailThroughCheck(dir) == 1) { - if (first == 1) first = 0; - else temp += AString(", "); - - temp += DirectionAbrs[dir]; - } - } - } - } - if (describe) { - temp += AString("; ") + *describe; - } - temp += "."; - f << temp << '\n'; - f << indent::incr; - } else if (type != O_DUMMY) { - AString temp = AString("+ ") + *name + " : " + ob->name; - if (incomplete > 0) { - temp += AString(", needs ") + incomplete; - } else if (Globals->DECAY && - !(ob->flags & ObjectType::NEVERDECAY) && incomplete < 1) { - if (incomplete > (0 - ob->maxMonthlyDecay)) { - temp += ", about to decay"; - } else if (incomplete > (0 - ob->maxMaintenance/2)) { - temp += ", needs maintenance"; - } - } - if (inner != -1) { - temp += ", contains an inner location"; - } - if (runes) { - temp += ", engraved with Runes of Warding"; - } - if (describe) { - temp += AString("; ") + *describe; - } - if (!(ob->flags & ObjectType::CANENTER)) { - temp += ", closed to player units"; - } - temp += "."; - f << temp << '\n'; - f << indent::incr; - } - - forlist ((&units)) { - Unit *u = (Unit *) elem; - int attitude = fac->get_attitude(u->faction->num); - if (u->faction == fac) { - u->write_text_report(f, -1, 1, 1, 1, attitude, fac->showunitattitudes); - } else { - if (present) { - u->write_text_report(f, obs, truesight, detfac, type != O_DUMMY, attitude, fac->showunitattitudes); - } else { - if (((type == O_DUMMY) && - (Globals->TRANSIT_REPORT & - GameDefs::REPORT_SHOW_OUTDOOR_UNITS)) || - ((type != O_DUMMY) && - (Globals->TRANSIT_REPORT & - GameDefs::REPORT_SHOW_INDOOR_UNITS)) || - ((u->guard == GUARD_GUARD) && - (Globals->TRANSIT_REPORT & - GameDefs::REPORT_SHOW_GUARDS))) { - u->write_text_report(f, passobs, passtrue, passdetfac, - type != O_DUMMY, attitude, fac->showunitattitudes); - } - } - } - } - if (type != O_DUMMY) { - f << indent::decr; - } - f << '\n'; -} void Object::SetPrevDir(int newdir) { diff --git a/object.h b/object.h index c0c9938f..d426d0d3 100644 --- a/object.h +++ b/object.h @@ -91,8 +91,7 @@ class Object : public AListElem void Readin(istream& f, AList *); void Writeout(ostream& f); - void write_text_report(ostream& f, Faction *, int, int, int, int, int, int, int); - void write_json_report(json& j, Faction *, int, int, int, int, int, int, int); + void build_json_report(json& j, Faction *, int, int, int, int, int, int, int); void SetName(AString *); void SetDescribe(AString *); diff --git a/snapshot-tests/neworigins_turns/turn_0/engine-output.txt b/snapshot-tests/neworigins_turns/turn_0/engine-output.txt index 6263ce2f..38817705 100644 --- a/snapshot-tests/neworigins_turns/turn_0/engine-output.txt +++ b/snapshot-tests/neworigins_turns/turn_0/engine-output.txt @@ -42,8 +42,6 @@ Players have visited 0 regions; 96 unvisited. Writing world events... Writing the Report File... ... -Writing order templates... -... Writing Playerinfo File... Removing Dead Factions... done diff --git a/snapshot-tests/neworigins_turns/turn_1/engine-output.txt b/snapshot-tests/neworigins_turns/turn_1/engine-output.txt index 7dcc4e03..01ec5556 100644 --- a/snapshot-tests/neworigins_turns/turn_1/engine-output.txt +++ b/snapshot-tests/neworigins_turns/turn_1/engine-output.txt @@ -42,8 +42,6 @@ Players have visited 1 regions; 95 unvisited. Writing world events... Writing the Report File... ... -Writing order templates... -... Writing Playerinfo File... Removing Dead Factions... done diff --git a/snapshot-tests/neworigins_turns/turn_10/engine-output.txt b/snapshot-tests/neworigins_turns/turn_10/engine-output.txt index 97fcb9bf..6f2eb8dd 100644 --- a/snapshot-tests/neworigins_turns/turn_10/engine-output.txt +++ b/snapshot-tests/neworigins_turns/turn_10/engine-output.txt @@ -42,8 +42,6 @@ Players have visited 6 regions; 90 unvisited. Writing world events... Writing the Report File... ... -Writing order templates... -... Writing Playerinfo File... Removing Dead Factions... done diff --git a/snapshot-tests/neworigins_turns/turn_11/engine-output.txt b/snapshot-tests/neworigins_turns/turn_11/engine-output.txt index f14d78cd..6d2b7e89 100644 --- a/snapshot-tests/neworigins_turns/turn_11/engine-output.txt +++ b/snapshot-tests/neworigins_turns/turn_11/engine-output.txt @@ -42,8 +42,6 @@ Players have visited 11 regions; 85 unvisited. Writing world events... Writing the Report File... ... -Writing order templates... -... Writing Playerinfo File... Removing Dead Factions... done diff --git a/snapshot-tests/neworigins_turns/turn_12/engine-output.txt b/snapshot-tests/neworigins_turns/turn_12/engine-output.txt index f0d94dd0..c8da2512 100644 --- a/snapshot-tests/neworigins_turns/turn_12/engine-output.txt +++ b/snapshot-tests/neworigins_turns/turn_12/engine-output.txt @@ -42,8 +42,6 @@ Players have visited 20 regions; 76 unvisited. Writing world events... Writing the Report File... ... -Writing order templates... -... Writing Playerinfo File... Removing Dead Factions... done diff --git a/snapshot-tests/neworigins_turns/turn_13/engine-output.txt b/snapshot-tests/neworigins_turns/turn_13/engine-output.txt index a567354a..addf27cf 100644 --- a/snapshot-tests/neworigins_turns/turn_13/engine-output.txt +++ b/snapshot-tests/neworigins_turns/turn_13/engine-output.txt @@ -52,8 +52,6 @@ Quest reward: mithril x 35. Writing world events... Writing the Report File... ... -Writing order templates... -... Writing Playerinfo File... Removing Dead Factions... done diff --git a/snapshot-tests/neworigins_turns/turn_2/engine-output.txt b/snapshot-tests/neworigins_turns/turn_2/engine-output.txt index 8d983a7e..56c16d5f 100644 --- a/snapshot-tests/neworigins_turns/turn_2/engine-output.txt +++ b/snapshot-tests/neworigins_turns/turn_2/engine-output.txt @@ -42,8 +42,6 @@ Players have visited 2 regions; 94 unvisited. Writing world events... Writing the Report File... ... -Writing order templates... -... Writing Playerinfo File... Removing Dead Factions... done diff --git a/snapshot-tests/neworigins_turns/turn_3/engine-output.txt b/snapshot-tests/neworigins_turns/turn_3/engine-output.txt index 8d983a7e..56c16d5f 100644 --- a/snapshot-tests/neworigins_turns/turn_3/engine-output.txt +++ b/snapshot-tests/neworigins_turns/turn_3/engine-output.txt @@ -42,8 +42,6 @@ Players have visited 2 regions; 94 unvisited. Writing world events... Writing the Report File... ... -Writing order templates... -... Writing Playerinfo File... Removing Dead Factions... done diff --git a/snapshot-tests/neworigins_turns/turn_4/engine-output.txt b/snapshot-tests/neworigins_turns/turn_4/engine-output.txt index 8d983a7e..56c16d5f 100644 --- a/snapshot-tests/neworigins_turns/turn_4/engine-output.txt +++ b/snapshot-tests/neworigins_turns/turn_4/engine-output.txt @@ -42,8 +42,6 @@ Players have visited 2 regions; 94 unvisited. Writing world events... Writing the Report File... ... -Writing order templates... -... Writing Playerinfo File... Removing Dead Factions... done diff --git a/snapshot-tests/neworigins_turns/turn_5/engine-output.txt b/snapshot-tests/neworigins_turns/turn_5/engine-output.txt index 8d983a7e..56c16d5f 100644 --- a/snapshot-tests/neworigins_turns/turn_5/engine-output.txt +++ b/snapshot-tests/neworigins_turns/turn_5/engine-output.txt @@ -42,8 +42,6 @@ Players have visited 2 regions; 94 unvisited. Writing world events... Writing the Report File... ... -Writing order templates... -... Writing Playerinfo File... Removing Dead Factions... done diff --git a/snapshot-tests/neworigins_turns/turn_6/engine-output.txt b/snapshot-tests/neworigins_turns/turn_6/engine-output.txt index 8d983a7e..56c16d5f 100644 --- a/snapshot-tests/neworigins_turns/turn_6/engine-output.txt +++ b/snapshot-tests/neworigins_turns/turn_6/engine-output.txt @@ -42,8 +42,6 @@ Players have visited 2 regions; 94 unvisited. Writing world events... Writing the Report File... ... -Writing order templates... -... Writing Playerinfo File... Removing Dead Factions... done diff --git a/snapshot-tests/neworigins_turns/turn_7/engine-output.txt b/snapshot-tests/neworigins_turns/turn_7/engine-output.txt index 8d983a7e..56c16d5f 100644 --- a/snapshot-tests/neworigins_turns/turn_7/engine-output.txt +++ b/snapshot-tests/neworigins_turns/turn_7/engine-output.txt @@ -42,8 +42,6 @@ Players have visited 2 regions; 94 unvisited. Writing world events... Writing the Report File... ... -Writing order templates... -... Writing Playerinfo File... Removing Dead Factions... done diff --git a/snapshot-tests/neworigins_turns/turn_8/engine-output.txt b/snapshot-tests/neworigins_turns/turn_8/engine-output.txt index 8d983a7e..56c16d5f 100644 --- a/snapshot-tests/neworigins_turns/turn_8/engine-output.txt +++ b/snapshot-tests/neworigins_turns/turn_8/engine-output.txt @@ -42,8 +42,6 @@ Players have visited 2 regions; 94 unvisited. Writing world events... Writing the Report File... ... -Writing order templates... -... Writing Playerinfo File... Removing Dead Factions... done diff --git a/snapshot-tests/neworigins_turns/turn_9/engine-output.txt b/snapshot-tests/neworigins_turns/turn_9/engine-output.txt index 37a6dd6c..ea699f1a 100644 --- a/snapshot-tests/neworigins_turns/turn_9/engine-output.txt +++ b/snapshot-tests/neworigins_turns/turn_9/engine-output.txt @@ -42,8 +42,6 @@ Players have visited 4 regions; 92 unvisited. Writing world events... Writing the Report File... ... -Writing order templates... -... Writing Playerinfo File... Removing Dead Factions... done diff --git a/snapshot-tests/turns/turn_0/engine-output.txt b/snapshot-tests/turns/turn_0/engine-output.txt index 4d8a6cf2..7b62304e 100644 --- a/snapshot-tests/turns/turn_0/engine-output.txt +++ b/snapshot-tests/turns/turn_0/engine-output.txt @@ -39,8 +39,6 @@ Assessing Maintenance costs... Post-Turn Processing... Writing the Report File... ... -Writing order templates... -... Writing Playerinfo File... Removing Dead Factions... done diff --git a/snapshot-tests/turns/turn_1/engine-output.txt b/snapshot-tests/turns/turn_1/engine-output.txt index 4d8a6cf2..7b62304e 100644 --- a/snapshot-tests/turns/turn_1/engine-output.txt +++ b/snapshot-tests/turns/turn_1/engine-output.txt @@ -39,8 +39,6 @@ Assessing Maintenance costs... Post-Turn Processing... Writing the Report File... ... -Writing order templates... -... Writing Playerinfo File... Removing Dead Factions... done diff --git a/snapshot-tests/turns/turn_10/engine-output.txt b/snapshot-tests/turns/turn_10/engine-output.txt index 4d8a6cf2..7b62304e 100644 --- a/snapshot-tests/turns/turn_10/engine-output.txt +++ b/snapshot-tests/turns/turn_10/engine-output.txt @@ -39,8 +39,6 @@ Assessing Maintenance costs... Post-Turn Processing... Writing the Report File... ... -Writing order templates... -... Writing Playerinfo File... Removing Dead Factions... done diff --git a/snapshot-tests/turns/turn_11/engine-output.txt b/snapshot-tests/turns/turn_11/engine-output.txt index 4d8a6cf2..7b62304e 100644 --- a/snapshot-tests/turns/turn_11/engine-output.txt +++ b/snapshot-tests/turns/turn_11/engine-output.txt @@ -39,8 +39,6 @@ Assessing Maintenance costs... Post-Turn Processing... Writing the Report File... ... -Writing order templates... -... Writing Playerinfo File... Removing Dead Factions... done diff --git a/snapshot-tests/turns/turn_12/engine-output.txt b/snapshot-tests/turns/turn_12/engine-output.txt index 4d8a6cf2..7b62304e 100644 --- a/snapshot-tests/turns/turn_12/engine-output.txt +++ b/snapshot-tests/turns/turn_12/engine-output.txt @@ -39,8 +39,6 @@ Assessing Maintenance costs... Post-Turn Processing... Writing the Report File... ... -Writing order templates... -... Writing Playerinfo File... Removing Dead Factions... done diff --git a/snapshot-tests/turns/turn_13/engine-output.txt b/snapshot-tests/turns/turn_13/engine-output.txt index 4d8a6cf2..7b62304e 100644 --- a/snapshot-tests/turns/turn_13/engine-output.txt +++ b/snapshot-tests/turns/turn_13/engine-output.txt @@ -39,8 +39,6 @@ Assessing Maintenance costs... Post-Turn Processing... Writing the Report File... ... -Writing order templates... -... Writing Playerinfo File... Removing Dead Factions... done diff --git a/snapshot-tests/turns/turn_2/engine-output.txt b/snapshot-tests/turns/turn_2/engine-output.txt index 4d8a6cf2..7b62304e 100644 --- a/snapshot-tests/turns/turn_2/engine-output.txt +++ b/snapshot-tests/turns/turn_2/engine-output.txt @@ -39,8 +39,6 @@ Assessing Maintenance costs... Post-Turn Processing... Writing the Report File... ... -Writing order templates... -... Writing Playerinfo File... Removing Dead Factions... done diff --git a/snapshot-tests/turns/turn_3/engine-output.txt b/snapshot-tests/turns/turn_3/engine-output.txt index 4d8a6cf2..7b62304e 100644 --- a/snapshot-tests/turns/turn_3/engine-output.txt +++ b/snapshot-tests/turns/turn_3/engine-output.txt @@ -39,8 +39,6 @@ Assessing Maintenance costs... Post-Turn Processing... Writing the Report File... ... -Writing order templates... -... Writing Playerinfo File... Removing Dead Factions... done diff --git a/snapshot-tests/turns/turn_4/engine-output.txt b/snapshot-tests/turns/turn_4/engine-output.txt index 4d8a6cf2..7b62304e 100644 --- a/snapshot-tests/turns/turn_4/engine-output.txt +++ b/snapshot-tests/turns/turn_4/engine-output.txt @@ -39,8 +39,6 @@ Assessing Maintenance costs... Post-Turn Processing... Writing the Report File... ... -Writing order templates... -... Writing Playerinfo File... Removing Dead Factions... done diff --git a/snapshot-tests/turns/turn_5/engine-output.txt b/snapshot-tests/turns/turn_5/engine-output.txt index 4d8a6cf2..7b62304e 100644 --- a/snapshot-tests/turns/turn_5/engine-output.txt +++ b/snapshot-tests/turns/turn_5/engine-output.txt @@ -39,8 +39,6 @@ Assessing Maintenance costs... Post-Turn Processing... Writing the Report File... ... -Writing order templates... -... Writing Playerinfo File... Removing Dead Factions... done diff --git a/snapshot-tests/turns/turn_6/engine-output.txt b/snapshot-tests/turns/turn_6/engine-output.txt index 4d8a6cf2..7b62304e 100644 --- a/snapshot-tests/turns/turn_6/engine-output.txt +++ b/snapshot-tests/turns/turn_6/engine-output.txt @@ -39,8 +39,6 @@ Assessing Maintenance costs... Post-Turn Processing... Writing the Report File... ... -Writing order templates... -... Writing Playerinfo File... Removing Dead Factions... done diff --git a/snapshot-tests/turns/turn_7/engine-output.txt b/snapshot-tests/turns/turn_7/engine-output.txt index 4d8a6cf2..7b62304e 100644 --- a/snapshot-tests/turns/turn_7/engine-output.txt +++ b/snapshot-tests/turns/turn_7/engine-output.txt @@ -39,8 +39,6 @@ Assessing Maintenance costs... Post-Turn Processing... Writing the Report File... ... -Writing order templates... -... Writing Playerinfo File... Removing Dead Factions... done diff --git a/snapshot-tests/turns/turn_8/engine-output.txt b/snapshot-tests/turns/turn_8/engine-output.txt index 4d8a6cf2..7b62304e 100644 --- a/snapshot-tests/turns/turn_8/engine-output.txt +++ b/snapshot-tests/turns/turn_8/engine-output.txt @@ -39,8 +39,6 @@ Assessing Maintenance costs... Post-Turn Processing... Writing the Report File... ... -Writing order templates... -... Writing Playerinfo File... Removing Dead Factions... done diff --git a/snapshot-tests/turns/turn_9/engine-output.txt b/snapshot-tests/turns/turn_9/engine-output.txt index 4d8a6cf2..7b62304e 100644 --- a/snapshot-tests/turns/turn_9/engine-output.txt +++ b/snapshot-tests/turns/turn_9/engine-output.txt @@ -39,8 +39,6 @@ Assessing Maintenance costs... Post-Turn Processing... Writing the Report File... ... -Writing order templates... -... Writing Playerinfo File... Removing Dead Factions... done diff --git a/template.cpp b/template.cpp deleted file mode 100644 index 5a1dda9c..00000000 --- a/template.cpp +++ /dev/null @@ -1,587 +0,0 @@ -// START A3HEADER -// -// This source file is part of the Atlantis PBM game program. -// Copyright (C) 1995-1999 Geoff Dunbar -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program, in the file license.txt. If not, write -// to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, -// Boston, MA 02111-1307, USA. -// -// See the Atlantis Project web page for details: -// http://www.prankster.com/project -// -// END A3HEADER - -#include "game.h" -#include "gamedata.h" -#include "indenter.hpp" -#include -#ifdef WIN32 -#include -#endif -#include - -#define LINE_WIDTH 70 -#define MAP_WIDTH 23 -#define TMPL_MAP_WIDTH 20 -#define TMPL_MAP_OFS 1 -#define FILL_SIZE 6 -#define TEMPLATE_MAX_LINES 13 - -static char const *TemplateMap[] = { - //12345678901234567890 - " ____ ", // 1 - "nw / \\ ne", // 2 - " ____/ \\____ ", // 3 - " / \\ / \\ ", // 4 - "/ \\____/ \\", // 5 - "\\ / \\ /", // 6 - " \\____/ \\____/ ", // 7 - " / \\ / \\ ", // 8 - "/ \\____/ \\", // 9 - "\\ / \\ /", // 10 - " \\____/ \\____/ ", // 11 - " \\ / ", // 12 - "sw \\____/ se" // 13 -}; - -static int dircrd[] = { - // X Y - 8-1, 7-1, // center - 8-1, 3-1, // N - 14-1, 5-1, // NE - 14-1, 9-1, // SE - 8-1, 11-1, // S - 2-1, 9-1, // SW - 2-1, 5-1 // NW -}; - - -static char const *ter_fill[] = { - // block - " #### ", - " #### ", - - // ocean - " ~ ~ ", - " ~ ~ ", - - // plain - " ", - " ", - - // forest - " ^ ^ ", - " ^ ^ ", - - // mountain - " /\\/\\ ", - "/ \\ \\", - - // swamp - " v v ", - " v v ", - - // jungle - " @ @ ", - " @ @ ", - - // desert - " . . ", - " . . ", - - // tundra - " ' ' ", - " ' ' ", - - // cavern - " . . ", - " . . ", - - // underforest - " ^ ^ ", - " ^ ^ ", - - // tunnels - " ", - " ", - - // nexus - " !!!! ", - " !!!! ", - - // For conquest - // Island Plain - " ", - " ", - - // Island swamp - " v v ", - " v v ", - - // Island mountain - " /\\/\\ ", - "/ \\ \\", - - // For Ceran terrains - - // plain1 - " ", - " ", - // plain2 - " ", - " ", - // plain3 - " ", - " ", - - // forest1 - " ^ ^ ", - " ^ ^ ", - // forest2 - " ^ ^ ", - " ^ ^ ", - // forest3 - " ^ ^ ", - " ^ ^ ", - - // mystforest - " ` ` ", - " ` ` ", - // mystforest1 - " ` ` ", - " ` ` ", - // mystforest2 - " ` ` ", - " ` ` ", - - // mountain1 - " /\\/\\ ", - "/ \\ \\", - // mountain2 - " /\\/\\ ", - "/ \\ \\", - // mountain3 - " /\\/\\ ", - "/ \\ \\", - - // hill - " * * ", - " * * ", - // hill1 - " * * ", - " * * ", - // hill2 - " * * ", - " * * ", - - // swamp1 - " v v ", - " v v ", - // swamp2 - " v v ", - " v v ", - // swamp3 - " v v ", - " v v ", - - // jungle1 - " @ @ ", - " @ @ ", - // jungle2 - " @ @ ", - " @ @ ", - // jungle3 - " @ @ ", - " @ @ ", - - // desert1 - " . . ", - " . . ", - // desert2 - " . . ", - " . . ", - // desert3 - " . . ", - " . . ", - - // wasteland - " ; ; ", - " ; ; ", - // wasteland1 - " ; ; ", - " ; ; ", - - // lake - " ~ ~ ", - " ~ ~ ", - - // tundra1 - " ' ' ", - " ' ' ", - // tundra2 - " ' ' ", - " ' ' ", - // tundra2 - " ' ' ", - " ' ' ", - - // cavern1 - " . . ", - " . . ", - // cavern2 - " . . ", - " . . ", - // cavern3 - " . . ", - " . . ", - - // underforest1 - " ^ ^ ", - " ^ ^ ", - // underforest2 - " ^ ^ ", - " ^ ^ ", - // underforest3 - " ^ ^ ", - " ^ ^ ", - - // tunnels1 - " ", - " ", - // tunnels2 - " ", - " ", - - // grotto - " . . ", - " . . ", - // deepforest - " ^ ^ ", - " ^ ^ ", - // chasm - " ", - " ", - // grotto1 - " . . ", - " . . ", - // deepforest1 - " ^ ^ ", - " ^ ^ ", - // chasm1 - " ", - " ", - - // volcano - " /\\/\\ ", - "/ \\ \\", - - // lake - " ~ ~ ", - " ~ ~ ", -}; - -// NEW FUNCTION DK 2000.03.07, -// converted WriteReport -// -void ARegion::WriteTemplateHeader(ostream& f, Faction *fac, - ARegionList *pRegs, int month) -{ - - f << '\n' << indent::comment << "-----------------------------------------------------------\n"; - - // plain (X,Y) in Blah, contains Blah - f << indent::comment << Print(pRegs) << '\n'; - - char buffer[LINE_WIDTH+1]; - string temp; - char *data = buffer + MAP_WIDTH; - int line = 0; - - Production *prod; - int any; - - // ---------------------------------------------------------------- - GetMapLine(buffer, line, pRegs); - temp = buffer; - temp.erase(temp.find_last_not_of(' ') + 1); - f << indent::comment << temp << '\n'; - line++; - - // ---------------------------------------------------------------- - - if (Globals->WEATHER_EXISTS) { - GetMapLine(buffer, line, pRegs); - - char const *nextWeather = ""; - int nxtweather = pRegs->GetWeather(this, (month + 1) % 12); - if (nxtweather == W_WINTER) - nextWeather = "winter"; - if (nxtweather == W_MONSOON) - nextWeather = "monsoon"; - if (nxtweather == W_NORMAL) - nextWeather = "clear"; - snprintf(data, LINE_WIDTH - MAP_WIDTH, "Next %s", nextWeather); - temp = buffer; - temp.erase(temp.find_last_not_of(' ') + 1); - f << indent::comment << temp << '\n'; - line++; - } - - // ---------------------------------------------------------------- - GetMapLine(buffer, line, pRegs); - snprintf(data, LINE_WIDTH - MAP_WIDTH, "Tax %5i", wealth); - temp = buffer; - temp.erase(temp.find_last_not_of(' ') + 1); - f << indent::comment << temp << '\n'; - line++; - - // ---------------------------------------------------------------- - prod = get_production_for_skill(I_SILVER, S_ENTERTAINMENT); - if (prod) { - GetMapLine(buffer, line, pRegs); - snprintf(data, LINE_WIDTH - MAP_WIDTH, "Ente %5i", prod->amount); - temp = buffer; - temp.erase(temp.find_last_not_of(' ') + 1); - f << indent::comment << temp << '\n'; - line++; - } - - // ---------------------------------------------------------------- - prod = get_production_for_skill(I_SILVER, -1); - if (prod) { - GetMapLine(buffer, line, pRegs); - snprintf(data, LINE_WIDTH - MAP_WIDTH, "Wage %5i.%1i (max %i)", (prod->productivity/10), (prod->productivity%10), prod->amount); - temp = buffer; - temp.erase(temp.find_last_not_of(' ') + 1); - f << indent::comment << temp << '\n'; - line++; - } - - // ---------------------------------------------------------------- - any = 0; - for (const auto& m : markets) { - if (!m->amount) continue; - if (m->type == M_SELL) { - - if (ItemDefs[m->item].type & IT_ADVANCED) { - if (!HasItem(fac, m->item)) { - continue; - } - } - - if (!any) { - GetMapLine(buffer, line, pRegs); - temp = buffer; - temp.erase(temp.find_last_not_of(' ') + 1); - f << indent::comment << temp << '\n'; - line++; - } - - GetMapLine(buffer, line, pRegs); - - if (m->amount == -1) { - snprintf(data, LINE_WIDTH - MAP_WIDTH, "%s unlim %4s @ %3i", - (any ? " " : "Want"), - ItemDefs[m->item].abr, - m->price); - } else { - snprintf(data, LINE_WIDTH - MAP_WIDTH, "%s %5i %4s @ %3i", - (any ? " " : "Want"), - m->amount, - ItemDefs[m->item].abr, - m->price); - } - temp = buffer; - temp.erase(temp.find_last_not_of(' ') + 1); - f << indent::comment << temp << '\n'; - line++; - any = 1; - } - } - - // ---------------------------------------------------------------- - any = 0; - for (const auto& m : markets) { - if (!m->amount) continue; - if (m->type == M_BUY) { - - if (!any) { - GetMapLine(buffer, line, pRegs); - temp = buffer; - temp.erase(temp.find_last_not_of(' ') + 1); - f << indent::comment << temp << '\n'; - line++; - } - - GetMapLine(buffer, line, pRegs); - - if (m->amount == -1) { - snprintf(data, LINE_WIDTH - MAP_WIDTH, "%s unlim %4s @ %3i", - (any ? " " : "Sell"), - ItemDefs[m->item].abr, - m->price); - } else { - snprintf(data, LINE_WIDTH - MAP_WIDTH, "%s %5i %4s @ %3i", - (any ? " " : "Sell"), - m->amount, - ItemDefs[m->item].abr, - m->price); - } - temp = buffer; - temp.erase(temp.find_last_not_of(' ') + 1); - f << indent::comment << temp << '\n'; - line++; - any = 1; - } - } - - // ---------------------------------------------------------------- - any = 0; - for (const auto& p : products) { - if (ItemDefs[p->itemtype].type & IT_ADVANCED) { - if (!CanMakeAdv(fac, p->itemtype)) { - continue; - } - } else { - if (p->itemtype == I_SILVER) { - continue; - } - } - - if (!any) { - GetMapLine(buffer, line, pRegs); - temp = buffer; - temp.erase(temp.find_last_not_of(' ') + 1); - f << indent::comment << temp << '\n'; - line++; - } - - GetMapLine(buffer, line, pRegs); - - if (p->amount == -1) { - snprintf(data, LINE_WIDTH - MAP_WIDTH, "%s unlim %4s", - (any ? " " : "Prod"), - ItemDefs[p->itemtype].abr); - } else { - snprintf(data, LINE_WIDTH - MAP_WIDTH, "%s %5i %4s", - (any ? " " : "Prod"), - p->amount, - ItemDefs[p->itemtype].abr); - } - temp = buffer; - temp.erase(temp.find_last_not_of(' ') + 1); - f << indent::comment << temp << '\n'; - line++; - any = 1; - } - - // ---------------------------------------------------------------- - - if (Globals->GATES_EXIST && gate) { - int sawgate = 0; - forlist(&objects) { - Object *o = (Object *) elem; - forlist(&o->units) { - Unit *u = (Unit *) elem; - if (!sawgate && u->faction == fac && - u->GetSkill(S_GATE_LORE)) { - GetMapLine(buffer, line, pRegs); - temp = buffer; - temp.erase(temp.find_last_not_of(' ') + 1); - f << indent::comment << temp << '\n'; - line++; - - GetMapLine(buffer, line, pRegs); - snprintf(data, LINE_WIDTH - MAP_WIDTH, "Gate %4i", gate); - temp = buffer; - temp.erase(temp.find_last_not_of(' ') + 1); - f << indent::comment << temp << '\n'; - line++; - - sawgate = 1; - } - } - } - } - - // ---------------------------------------------------------------- - while (line < TEMPLATE_MAX_LINES) { - GetMapLine(buffer, line, pRegs); - temp = buffer; - temp.erase(temp.find_last_not_of(' ') + 1); - f << indent::comment << temp << '\n'; - line++; - } -} - - -// NEW FUNCTION DK 2000.03.07, -// converted WriteExits -// -void ARegion::GetMapLine(char *buffer, int line, ARegionList *pRegs) -{ - - for (int m=0; m= TEMPLATE_MAX_LINES) - return; - - char *dest = buffer+TMPL_MAP_OFS; - memcpy(dest, TemplateMap[line], TMPL_MAP_WIDTH); - - ARegion *r = this; - int x, y, t, i = 0; - char *name; - - t = (r->type + 1) * 2; - name = (r->town ? r->town->name->Str() : NULL); - - for (;;) { - - x = dircrd[i*2]; - y = dircrd[i*2+1]; - - if (y == line || y+1 == line) { - if (y == line) { - if (name) { - int len = strlen(name); - if (len > FILL_SIZE) len = FILL_SIZE; - memcpy(dest + x, name, len); - } else { - memcpy(dest + x, ter_fill[t], FILL_SIZE); - } - } else { - t++; - memcpy(dest + x, ter_fill[t], FILL_SIZE); - } - } - - if (i >= NDIRS) break; - - ARegion *r = neighbors[i]; - if (r) { - t = (r->type + 1) * 2; - name = (r->town ? r->town->name->Str() : NULL); - } else { - t = 0; - name = NULL; - } - - i++; - } -} diff --git a/text_report_generator.cpp b/text_report_generator.cpp new file mode 100644 index 00000000..2a22bfb8 --- /dev/null +++ b/text_report_generator.cpp @@ -0,0 +1,846 @@ +#include + +#include "astring.h" +#include "faction.h" +#include "gamedefs.h" +#include "indenter.hpp" +#include "text_report_generator.hpp" + +#include "external/nlohmann/json.hpp" +using json = nlohmann::json; + +using namespace std; + +const int TextReportGenerator::line_width = 70; +const int TextReportGenerator::map_width = 23; +const size_t TextReportGenerator::template_fill_size = 6; +const int TextReportGenerator::template_max_lines = 13; + + +// Initialize the string constants +const string TextReportGenerator::template_map[] = { +// 01234567890123456789012 + " ____ ", // 1 + " nw / \\ ne ", // 2 + " ____/ \\____ ", // 3 + " / \\ / \\ ", // 4 + " / \\____/ \\ ", // 5 + " \\ / \\ / ", // 6 + " \\____/ \\____/ ", // 7 + " / \\ / \\ ", // 8 + " / \\____/ \\ ", // 9 + " \\ / \\ / ", // 10 + " \\____/ \\____/ ", // 11 + " \\ / ", // 12 + " sw \\____/ se " // 13 +}; + +const map> TextReportGenerator::direction_offsets = { + {"center", { 8, 7-1 }}, + {"north", { 8, 3-1 }}, + {"northeast", { 14, 5-1 }}, + {"southeast", { 14, 9-1 }}, + {"south", { 8, 11-1 }}, + {"southwest", { 2, 9-1 }}, + {"northwest", { 2, 5-1 }} +}; + +const map> TextReportGenerator::terrain_fill = { + { "block", { + "####", + "####" } + }, + { "ocean", { + " ~ ~ ", + " ~ ~ " } + }, + { "plain", { + " ", + " " } + }, + { "forest", { + " ^ ^ ", + " ^ ^ " } + }, + { "mountain", { + " /\\/\\ ", + "/ \\ \\" } + }, + { "swamp", { + " v v ", + " v v " } + }, + { "jungle", { + " @ @ ", + " @ @ " } + }, + { "desert", { + " . . ", + " . . " } + }, + { "tundra", { + " ' ' ", + " ' ' " } + }, + { "cavern", { + " . . ", + " . . " } + }, + { "underforest", { + " ^ ^ ", + " ^ ^ " } + }, + { "tunnels", { + " ", + " " } + }, + { "nexus", { + " !!!! ", + " !!!! " } + }, + { "mystforest", { + " ` ` ", + " ` ` " } + }, + { "hill", { + " * * ", + " * * " } + }, + { "wasteland", { + " ; ; ", + " ; ; " } + }, + { "lake", { + " ~ ~ ", + " ~ ~ " } + }, + { "grotto", { + " . . ", + " . . " } + }, + { "deepforest", { + " ^ ^ ", + " ^ ^ " } + }, + { "chasm", { + " ", + " " } + }, + { "volcano", { + " /\\/\\ ", + "/ \\ \\" } + }, +}; + +string TextReportGenerator::to_s(const json& j) { + string s; + if (j.is_string()) return j.template get(); + // If we somehow get something that isn't a string, just dump it as json. + return j.dump(); +} + +void TextReportGenerator::output_region_header(ostream&f, const json& region, bool show_region_depth) { + f << to_s(region["terrain"]) << " (" << region["coordinates"]["x"] << "," << region["coordinates"]["y"]; + if (region["coordinates"]["label"] != "surface") { + int z = region["coordinates"]["z"]; + f << ","; + if (show_region_depth) f << z << " <"; + if (region["coordinates"].contains("depth_prefix")) { + f << to_s(region["coordinates"]["depth_prefix"]) << " "; + } + f << to_s(region["coordinates"]["label"]); + if (show_region_depth) f << ">"; + } + f << ")"; + f << " in " << to_s(region["province"]); + if (region.contains("settlement")) { + f << ", contains " << to_s(region["settlement"]["name"]) << " [" << to_s(region["settlement"]["size"]) << "]"; + } +} + +void TextReportGenerator::output_item(ostream& f, const json& item, bool assume_singular, bool show_single_amt) { + if (item.contains("unlimited")) { + f << "unlimited " << to_s(item["plural"]); + } else { + int amount = item.value("amount", 0); + if ((amount > 1) || (show_single_amt && (amount == 1))) f << amount << " "; + if (item.contains("unfinished")) f << "unfinished "; + if (assume_singular) { + f << to_s(item["name"]); + } else { + f << plural(item["amount"], item["name"], item["plural"]); + } + } + + f << " [" << to_s(item["tag"]) << "]"; + if (item.contains("needs")) f << " (needs " << item["needs"] << ")"; + if (item.contains("illusion")) f << " (illusion)"; + if (item.contains("price")) f << " at $" << item["price"]; +} + +void TextReportGenerator::output_items(ostream& f, const json& item_list, bool assume_singular, bool show_single_amt) { + if (item_list.empty()) return; + bool comma = false; + for(const auto& item : item_list) { + if (comma) f << ", "; + output_item(f, item, assume_singular, show_single_amt); + comma = true; + } +} + +void TextReportGenerator::output_item_list(ostream& f, const json& item_list, string header) { + f << header << ": "; + if (item_list.empty()) { + f << "none.\n"; + return; + } + output_items(f, item_list); + f << ".\n"; +} + +void TextReportGenerator::output_unit_summary(ostream& f, const json& unit, bool show_faction) { + f << to_s(unit["name"]) << " (" << unit["number"] << ")"; + // display guard flag *before* the faction + if (unit.contains("flags") && unit["flags"]["guard"]) f << ", on guard"; + if (unit.contains("faction") && show_faction) { + f << ", " << to_s(unit["faction"]["name"]) << " (" << unit["faction"]["number"] << ")"; + } + // and then the rest of the flags (which might or might not be present) + if (unit["flags"].contains("avoid") && unit["flags"]["avoid"]) f << ", avoiding"; + if (unit["flags"].contains("behind") && unit["flags"]["behind"]) f << ", behind"; + if (unit["flags"].contains("reveal")) { + if (unit["flags"]["reveal"] == "unit") f << ", revealing unit"; + if (unit["flags"]["reveal"] == "faction") f << ", revealing faction"; + } + if (unit["flags"].contains("holding") && unit["flags"]["holding"]) f << ", holding"; + if (unit["flags"].contains("taxing") && unit["flags"]["taxing"]) f << ", taxing"; + if (unit["flags"].contains("no_aid") && unit["flags"]["no_aid"]) f << ", receiving no aid"; + if (unit["flags"].contains("sharing") && unit["flags"]["sharing"]) f << ", sharing"; + if (unit["flags"].contains("consume")) { + if (unit["flags"]["consume"] == "unit") f << ", consuming unit's food"; + if (unit["flags"]["consume"] == "faction") f << ", consuming faction's food"; + } + if (unit["flags"].contains("no_cross_water") && unit["flags"]["no_cross_water"]) f << ", won't cross water"; + // All spoils is the default, so we don't need to output it. + if (unit["flags"].contains("spoils") && unit["flags"]["spoils"] != "all") { + f << ", " << unit["flags"]["spoils"] << " battle spoils"; + } + + f << ", "; + output_items(f, unit["items"]); + if (unit.contains("weight")) f << ". Weight: " << unit["weight"]; + if (unit.contains("capacity")) { + f << ". Capacity: " << unit["capacity"]["flying"] << "/" << unit["capacity"]["riding"] << "/" + << unit["capacity"]["walking"] << "/" << unit["capacity"]["swimming"]; + } + + if (unit.contains("skills")) { + f << ". Skills: "; + if (unit["skills"].contains("known") && !unit["skills"]["known"].empty()) { + bool comma = false; + for(const auto& skill : unit["skills"]["known"]) { + if (comma) f << ", "; + f << to_s(skill["name"]) << " [" << to_s(skill["tag"]) << "]" << " " << skill["level"] << " (" + << skill["skill_days"]; + if (skill.contains("study_rate")) { + f << "+" << skill["study_rate"]; + } + f << ")"; + comma = true; + } + } else { + f << "none"; + } + } + + if (unit.contains("combat_spell")) { + f << ". Combat spell: " << to_s(unit["combat_spell"]["name"]) + << " [" << to_s(unit["combat_spell"]["tag"]) << "]"; + } + + // Readied items + if (unit.contains("readied")) { + if (unit["readied"].contains("weapons")) { + int count = unit["readied"]["weapons"].size(); + f << ". Ready " << plural(count, "weapon", "weapons") << ": "; + output_items(f, unit["readied"]["weapons"], true); + } + if (unit["readied"].contains("armor")) { + f << ". Ready armor: "; + output_items(f, unit["readied"]["armor"], true); + } + if (unit["readied"].contains("item")) { + f << ". Ready item: "; + output_item(f, unit["readied"]["item"], true); + } + } + + // Studyable skills + if (unit.contains("skills") && unit["skills"].contains("can_study")) { + f << ". Can Study: "; + bool comma = false; + for(const auto& skill : unit["skills"]["can_study"]) { + if (comma) f << ", "; + f << to_s(skill["name"]) << " [" << to_s(skill["tag"]) << "]"; + comma = true; + } + } + + if (unit.contains("visited") && !unit["visited"].empty()) { + f << ". Has visited "; + for (auto it = unit["visited"].begin(); it != unit["visited"].end(); it++) { + if (it != unit["visited"].begin()) { + if (it == unit["visited"].end() - 1) + f << " and "; + else + f << ", "; + } + f << to_s(*it); + } + } + + if (unit.contains("description")) f << "; " << to_s(unit["description"]); + f << ".\n"; +} + +void TextReportGenerator::output_unit(ostream& f, const json& unit, bool show_unit_attitudes) { + if (unit.contains("own_unit")) { + f << "* "; + } else if (!show_unit_attitudes) { + f << "- "; + } else if (unit.contains("attitude")) { + if(unit["attitude"] == "ally") f << "= "; + if(unit["attitude"] == "friendly") f << ": "; + if(unit["attitude"] == "neutral") f << "- "; + if(unit["attitude"] == "unfriendly") f << "% "; + if(unit["attitude"] == "hostile") f << "! "; + } else { + f << "- "; + } + output_unit_summary(f, unit); +} + +void TextReportGenerator::output_structure(ostream& f, const json& structure, bool show_unit_attitudes) { + f << "+ " << to_s(structure["name"]) << " [" << structure["number"] << "] : "; + if (structure.contains("ships")) { + output_items(f, structure["ships"], false, true); + if (structure.contains("damage_percent")) f << "; " << structure["damage_percent"] << "% damaged; "; + if (structure.contains("load")) + f << "; " << "Load: " << structure["load"] << "/" << structure["capacity"] << "; "; + if (structure.contains("sailors")) + f << "; " << "Sailors: " << structure["sailors"] << "/" << structure["fleet_size"] << "; "; + if (structure.contains("max_speed")) f << "; " << "MaxSpeed: " << structure["max_speed"] << "; "; + if (structure.contains("sail_directions")) { + f << "; " << "Sail directions: "; + bool comma = false; + for(const auto& direction : structure["sail_directions"]) { + if (comma) f << ", "; + f << to_s(direction); + comma = true; + } + } + if (structure.contains("description")) f << "; " << to_s(structure["description"]); + f << ".\n"; + } else { + f << to_s(structure["type"]); + if (structure.contains("incomplete")) f << ", needs " << structure["incomplete"]; + if (structure.contains("decay")) f << ", about to decay"; + if (structure.contains("needs_maintenance")) f << ", needs maintenance"; + if (structure.contains("inner_location")) f << ", contains an inner location"; + if (structure.contains("runes")) f << ", engraved with Runes of Warding"; + if (structure.contains("description")) f << "; " << to_s(structure["description"]); + if (structure.contains("closed")) f << ", closed to player units"; + f << ".\n"; + } + f << indent::incr; + if (structure.contains("units") && !structure["units"].empty()) { + for(const auto& unit : structure["units"]) { + output_unit(f, unit, show_unit_attitudes); + } + } + f << indent::decr; + f << '\n'; +} + +void TextReportGenerator::output_region( + ostream& f, const json& region, bool show_unit_attitudes, bool show_region_depth +) { + output_region_header(f, region, show_region_depth); + if (region.contains("population")) { + f << ", " << region["population"]["amount"] << " peasants"; + if (region["population"]["race"] != "men") { + f << " (" << to_s(region["population"]["race"]) << ")"; + } + if (region.contains("tax")) { + f << ", $" << region["tax"]; + } + } + f << ".\n"; + f << "------------------------------------------------------------\n"; + + f << indent::incr; + if (region.contains("weather")) { + if (region["weather"]["current"] == "clear") { + f << "The weather was clear last month; "; + } else if (region["weather"]["current"] == "blizzard") { + f << "There was an unnatural blizzard last month; "; + } else { + f << "It was " << to_s(region["weather"]["current"]) << " last month; "; + } + f << "it will be " << to_s(region["weather"]["next"]) << " next month.\n"; + } + if (region.contains("description")) f << '\n' << to_s(region["description"]) << "\n\n"; + + if (region.contains("wages")) { + f << "Wages: $" << region["wages"]["amount"]; + if (region["wages"].contains("max")) f << " (Max: $" << region["wages"]["max"] << ")"; + f << ".\n"; + } + + if (region.contains("markets")) { + output_item_list(f, region["markets"]["wanted"], "Wanted"); + output_item_list(f, region["markets"]["for_sale"], "For Sale"); + } + + if (region.contains("entertainment")) f << "Entertainment available: $" << region["entertainment"] << ".\n"; + + output_item_list(f, region["products"], "Products"); + f << indent::decr << '\n'; + + f << "Exits:\n" << indent::incr; + if (region["exits"].empty()) { + f << "none\n"; + } else { + for (const auto& exit : region["exits"]) { + f << to_s(exit["direction"]) << " : "; + output_region_header(f, exit["region"], show_region_depth); + f << ".\n"; + } + } + f << indent::decr << '\n'; + + if (region.contains("gate")) { + if (region["gate"].contains("open")) { + f << "There is a Gate here (Gate " << region["gate"]["number"]; + if (region["gate"].contains("total")) { + f << " of " << region["gate"]["total"]; + } + f << ").\n\n"; + } else { + f << "There is a closed Gate here.\n\n"; + } + } + + if (region.contains("units") && !region["units"].empty()) { + for(const auto& unit : region["units"]) { + output_unit(f, unit, show_unit_attitudes); + } + } + f << '\n'; + + if (region.contains("structures") && !region["structures"].empty()) { + for(const auto& structure : region["structures"]) { + output_structure(f, structure, show_unit_attitudes); + } + f << '\n'; + } else { + f << '\n'; + } +} + +void TextReportGenerator::output(ostream& f, const json& report, bool show_region_depth) { + f << indent::wrap; + + if (report.contains("statistics")) { + f << ";Treasury:\n"; + f << ";\n"; + f << ";Item Rank Max Total\n"; + f << ";=====================================================================\n"; + for (const auto& stat : report["statistics"]) { + f << ';' << left << setw(42) << to_s(stat["item_name"]) << setw(6) << stat.value("rank", 0) + << setw(11) << stat.value("max", 0) << stat.value("total", 0) << right << '\n'; + } + f << '\n'; + } + + // Player reports have an extra newline. GM reports don't. This is silly, but for now we will emulate that + // behavior. + bool final_newline = false; + + if (report.contains("name")) { + // Only player reports generate the report header + final_newline = true; + + f << "Atlantis Report For:\n"; + f << to_s(report["name"]) << " (" << report["number"] << ")"; + if (report.contains("type")) { + f << " ("; + bool comma = false; + // right now there are 4 possible faction types. + // martial, war, trade, magic. + // while it's not legal to have martial along with war and trade, for now we will just check and output + // all of them. + string types[] = {"martial", "war", "trade", "magic"}; + for (const auto& type : types) { + string key = type; + key[0] = toupper(key[0]); + if (report["type"].contains(type)) { + if (comma) f << ", "; + f << key << " " << report["type"][type]; + comma = true; + } + } + f << ")"; + } + f << '\n'; + f << to_s(report["date"]["month"]) << ", Year " << report["date"]["year"] << "\n\n"; + } + + if (report.contains("engine")) { + f << "Atlantis Engine Version: " << to_s(report["engine"]["version"]) << '\n'; + f << to_s(report["engine"]["ruleset"]) << ", Version: " << to_s(report["engine"]["ruleset_version"]) << "\n\n"; + } + + bool show_unit_attitudes = false; + if (report.contains("administrative")) { + if (!report["administrative"].contains("times_sent")) + f << "Note: The Times is not being sent to you.\n\n"; + if (report["administrative"].contains("password_unset")) + f << "REMINDER: You have not set a password for your faction!\n\n"; + if (report["administrative"].contains("inactivity_deletion_turns")) { + f << "WARNING: You have " << report["administrative"]["inactivity_deletion_turns"] + << " turns until your faction is automatically removed due to inactivity!\n\n"; + } + if (report["administrative"].contains("quit")) { + string quit = to_s(report["administrative"]["quit"]); + if (quit == "quit and restart") { + f << "You restarted your faction this turn. This faction has been removed, and a new faction has " + << "been started for you. (Your new faction report will come in a separate message.)\n"; + } else if (quit == "game over") { + f << "I'm sorry, the game has ended. Better luck in the next game you play!\n"; + } else if (quit == "won game") { + f << "Congratulations, you have won the game!\n"; + } else { + f << "I'm sorry, your faction has been eliminated.\n" << "If you wish to restart, please let the " + << "Gamemaster know, and you will be restarted for the next available turn.\n"; + } + f << '\n'; + } + if (report["administrative"].contains("show_unit_attitudes")) { + show_unit_attitudes = report["administrative"]["show_unit_attitudes"]; + } + } + + if (report.contains("status")) { + auto status = report["status"]; + f << "Faction Status:\n"; + if (status.contains("regions")) { + f << "Regions: " << status["regions"]["current"] << " (" << status["regions"]["allowed"] << ")\n"; + } + if (status.contains("activity")) { + f << "Activity: " << status["activity"]["current"] << " (" << status["activity"]["allowed"] << ")\n"; + } + if (status.contains("tax_regions")) { + f << "Tax Regions: " << status["tax_regions"]["current"] << " (" + << status["tax_regions"]["allowed"] << ")\n"; + } + if (status.contains("trade_regions")) { + f << "Trade Regions: " << status["trade_regions"]["current"] << " (" + << status["trade_regions"]["allowed"] << ")\n"; + } + if (status.contains("quartermasters")) { + f << "Quartermasters: " << status["quartermasters"]["current"] << " (" + << status["quartermasters"]["allowed"] << ")\n"; + } + if (status.contains("tacticians")) { + f << "Tacticians: " << status["tacticians"]["current"] << " (" + << status["tacticians"]["allowed"] << ")\n"; + } + if (status.contains("mages")) { + f << "Mages: " << status["mages"]["current"] << " (" << status["mages"]["allowed"] << ")\n"; + } + if (status.contains("apprentices")) { + f << to_s(status["apprentices"]["name"]) << "s: " << status["apprentices"]["current"] << " (" + << status["apprentices"]["allowed"] << ")\n"; + } + f << '\n'; + } + + if (report.contains("errors") && !report["errors"].empty()) { + f << "Errors during turn:\n"; + for (const auto& error : report["errors"]) f << to_s(error) << '\n'; + f << '\n'; + } + + if (report.contains("battles") && !report["battles"].empty()) { + f << "Battles during turn:\n"; + for (const auto& battle: report["battles"]) { + for (const auto& line: battle["report"]) { + f << to_s(line) << '\n'; + } + } + } + + if (report.contains("events") && !report["events"].empty()) { + f << "Events during turn:\n"; + for (const auto& event: report["events"]) f << to_s(event) << '\n'; + f << '\n'; + } + + if(report.contains("skill_reports") && !report["skill_reports"].empty()) { + f << "Skill reports:\n"; + for (const auto& skillshow : report["skill_reports"]) f << '\n' << to_s(skillshow) << '\n'; + f << '\n'; + } + + if (report.contains("item_reports") && !report["item_reports"].empty()) { + f << "Item reports:\n"; + for (const auto& itemshow : report["item_reports"]) f << '\n' << to_s(itemshow) << '\n'; + f << '\n'; + } + + if (report.contains("object_reports") && !report["object_reports"].empty()) { + f << "Object reports:\n"; + for (const auto& objectshow : report["object_reports"]) f << '\n' << to_s(objectshow) << '\n'; + f << '\n'; + } + + if (report.contains("attitudes") && !report["attitudes"].empty()) { + string default_attitude = to_s(report["attitudes"]["default"]); + default_attitude[0] = toupper(default_attitude[0]); + f << "Declared Attitudes (default " << default_attitude << "):\n"; + string available_attitudes[] = { "hostile", "unfriendly", "neutral", "friendly", "ally" }; + for (const auto& attitude : available_attitudes) { + if (report["attitudes"].contains(attitude)) { + string attitude_name = attitude; + attitude_name[0] = toupper(attitude_name[0]); + f << attitude_name << " : "; + if (report["attitudes"][attitude].empty()) { + f << "none"; + } else { + bool comma = false; + for (const auto& faction : report["attitudes"][attitude]) { + if (comma) f << ", "; + f << to_s(faction["name"]) << " (" << faction["number"] << ")"; + comma = true; + } + } + f << ".\n"; + } + } + f << '\n'; + } + + if(report.contains("unclaimed_silver")) { + f << "Unclaimed silver: " << report["unclaimed_silver"] << ".\n\n"; + } + + if (report.contains("regions") && !report["regions"].empty()) { + for (const auto& region : report["regions"]) + output_region(f, region, show_unit_attitudes, show_region_depth); + } + if (final_newline) f << '\n'; +} + +void TextReportGenerator::output_unit_orders(ostream& f, const json& orders) { + for (const auto& order : orders) { + f << to_s(order["order"]) << '\n'; + if (order.contains("nested") && !order["nested"].empty()) { + f << indent::incr; + output_unit_orders(f, order["nested"]); + f << indent::decr; + } + } +} + +void TextReportGenerator::output_unit_template(ostream& f, const json& unit, int template_type) { + f << "\nunit " << unit["number"] << '\n'; + if (template_type == TEMPLATE_LONG || template_type == TEMPLATE_MAP) { + f << indent::comment; + output_unit_summary(f, unit, false); + } + if (unit.contains("orders") && !unit["orders"].empty()) { + output_unit_orders(f, unit["orders"]); + } +} + +string TextReportGenerator::next_map_header_line(int line, const json& region) { + if (line >= template_max_lines) return string(map_width, ' '); + + string result = template_map[line]; + + // See if there is anything special to fill on for this line. + for (const auto& offset: direction_offsets) { + int x = offset.second[0]; + int y = offset.second[1]; + + if (line == y || line == y + 1) { + if (offset.first == "center") { + string value = ( + region.contains("settlement") && line == y + ? to_s(region["settlement"]["name"]) + : terrain_fill.at(to_s(region["terrain"]))[line - y] + ); + int len = min(value.length(), template_fill_size); + result.replace(x, len, value.substr(0, len)); + } else { + if (region.contains("exits")) { + for (const auto& exit : region["exits"]) { + string lower_dir = exit["direction"]; + transform(lower_dir.begin(), lower_dir.end(), lower_dir.begin(), ::tolower); + if (lower_dir == offset.first) { + string value = ( + exit["region"].contains("settlement") && line == y + ? to_s(exit["region"]["settlement"]["name"]) + : terrain_fill.at(to_s(exit["region"]["terrain"]))[line - y] + ); + int len = min(value.length(), template_fill_size); + result.replace(x, len, value.substr(0, len)); + } + } + } + } + } + } + return result; +} + +void TextReportGenerator::output_region_map_header_line(ostream& f, string line) { + string out = line.substr(0, line_width); + out.erase(out.find_last_not_of(' ') + 1); + f << indent::comment << out << '\n'; +} + +void TextReportGenerator::output_region_map_header(ostream& f, const json& region, bool show_region_depth) { + f << '\n' << indent::comment << "-----------------------------------------------------------\n"; + f << indent::comment; + output_region_header(f, region, show_region_depth); + f << '\n'; + + int line = 0; + output_region_map_header_line(f, next_map_header_line(line++, region)); + + if (region.contains("weather")) { + string weather = "Next " + to_s(region["weather"]["next"]); + // strip off ' season' if it's there (for monsoon) + if (weather.ends_with(" season")) weather = weather.substr(0, weather.length() - 7); + output_region_map_header_line(f, next_map_header_line(line++, region) + weather); + } + + stringstream ss; + ss << "Tax " << setw(5) << right << region.value("tax", 0); + output_region_map_header_line(f, next_map_header_line(line++, region) + ss.str()); + stringstream().swap(ss); + + if (region.contains("entertainment")) { + ss << "Ente " << setw(5) << right << region.value("entertainment", 0); + output_region_map_header_line(f, next_map_header_line(line++, region) + ss.str()); + stringstream().swap(ss); + } + + if (region.contains("wages")) { + ss << "Wage " << setprecision(1) << fixed << setw(7) << right << region["wages"].value("amount", 0.0) + << " (max " << region["wages"]["max"] << ")"; + output_region_map_header_line(f, next_map_header_line(line++, region) + ss.str()); + stringstream().swap(ss); + } + + bool first = true; + if (region.contains("markets")) { + for (const auto& item : region["markets"]["wanted"]) { + if (first) output_region_map_header_line(f, next_map_header_line(line++, region)); + ss << (first ? "Want " : " "); + if (item.value("unlimited", false)) ss << "unlim"; + else ss << setw(5) << right << item.value("amount", 0); + ss << " " << setw(4) << to_s(item["tag"]) << " @ " << setw(3) << right << item.value("price", 0); + output_region_map_header_line(f, next_map_header_line(line++, region) + ss.str()); + stringstream().swap(ss); + first = false; + } + + first = true; + for (const auto& item : region["markets"]["for_sale"]) { + if (first) output_region_map_header_line(f, next_map_header_line(line++, region)); + ss << (first ? "Sell " : " "); + if (item.value("unlimited", false)) ss << "unlim"; + else ss << setw(5) << right << item.value("amount", 0); + ss << " " << setw(4) << to_s(item["tag"]) << " @ " << setw(3) << right << item.value("price", 0); + output_region_map_header_line(f, next_map_header_line(line++, region) + ss.str()); + stringstream().swap(ss); + first = false; + } + } + + first = true; + for (const auto& item : region["products"]) { + if (first) output_region_map_header_line(f, next_map_header_line(line++, region)); + ss << (first ? "Prod " : " "); + if (item.value("unlimited", false)) ss << "unlim"; + else ss << setw(5) << right << item.value("amount", 0); + ss << " " << setw(4) << to_s(item["tag"]); + output_region_map_header_line(f, next_map_header_line(line++, region) + ss.str()); + stringstream().swap(ss); + first = false; + } + + if (region.contains("gate")) { + if (region["gate"].contains("open")) { + ss << "Gate " << setw(4) << right << region["gate"].value("number", 0); + output_region_map_header_line(f, next_map_header_line(line++, region) + ss.str()); + stringstream().swap(ss); + } else { + output_region_map_header_line(f, next_map_header_line(line++, region) + "Gate closed"); + } + } + + while (line < template_max_lines) { + output_region_map_header_line(f, next_map_header_line(line++, region)); + } +} + +void TextReportGenerator::output_region_template( + ostream& f, const json& region, int template_type, bool show_region_depth +) { + if (template_type == TEMPLATE_MAP) output_region_map_header(f, region, show_region_depth); + else { + f << "\n" << indent::comment << "*** "; + output_region_header(f, region, show_region_depth); + f << " ***\n"; + } + + if (region.contains("units")) { + for(auto& unit: region["units"]) { + if (unit.contains("own_unit")) output_unit_template(f, unit, template_type); + } + } + + if (region.contains("structures")) { + for (auto& structure : region["structures"]) { + for (auto &unit : structure["units"]) { + if (unit.contains("own_unit")) output_unit_template(f, unit, template_type); + } + } + } +} + +void TextReportGenerator::output_template(ostream& f, const json& report, int template_type, bool show_region_depth) { + f << indent::wrap; + f << "\n"; + f << "Orders Template ("; + f << (template_type == TEMPLATE_SHORT ? "Short" : (template_type == TEMPLATE_LONG ? "Long" : "Map")); + f << " Format):\n\n"; + f << "#atlantis " << report["number"]; + if (!report["administrative"].contains("password_unset")) { + // Since the json output will quote for strings, just use it as is + f << " " << report["administrative"]["password"]; + } + f << "\n"; + + if (report.contains("regions") && !report["regions"].empty()) { + for(const auto& region : report["regions"]) { + // Only output the region template if you have actual units in the region. + if (region.contains("present")) output_region_template(f, region, template_type, show_region_depth); + } + } + f << "\n#end\n\n"; +} diff --git a/text_report_generator.hpp b/text_report_generator.hpp new file mode 100644 index 00000000..fe6a6ad3 --- /dev/null +++ b/text_report_generator.hpp @@ -0,0 +1,45 @@ +#pragma once + +#ifndef __TEXT_REPORT_GENERATOR_HPP__ +#define __TEXT_REPORT_GENERATOR_HPP__ + +#include + +#include "external/nlohmann/json.hpp" +using json = nlohmann::json; + +class TextReportGenerator { +public: + void output(std::ostream& f, const json& report, bool show_region_depth); + void output_template(std::ostream& f, const json& report, int template_type, bool show_region_depth); +private: + void output_region(std::ostream& f, const json& region, bool show_unit_attitudes, bool show_region_depth); + void output_region_header(std::ostream& f, const json& region, bool show_region_depth); + void output_item_list(std::ostream& f, const json& item_list, string header); + void output_items(std::ostream& f, const json& item_list, bool assume_singular = false, bool show_single_amt = false); + void output_item(std::ostream& f, const json& item, bool assume_singular = false, bool show_single_amt = false); + void output_structure(std::ostream& f, const json& structure, bool show_unit_attitudes); + void output_unit(std::ostream& f, const json& unit, bool show_unit_attitudes); + void output_unit_summary(std::ostream& f, const json& unit, bool show_faction = true); + void output_region_template(std::ostream& f, const json& region, int template_type, bool show_region_depth); + void output_region_map_header(std::ostream& f, const json& region, bool show_region_depth); + void output_region_map_header_line(std::ostream& f, std::string line); + std::string next_map_header_line(int line, const json& region); + void output_unit_template(std::ostream& f, const json& unit, int template_type); + void output_unit_orders(std::ostream& f, const json& orders); + + // Without this utility function for strings, we end up printing "" instead of since the + // item is a json element at that point, so we have a nice utility function here. + std::string to_s(const json& item); + + // Some private data members to help with output formatting of the map template + static const int line_width; + static const int map_width; + static const size_t template_fill_size; + static const int template_max_lines; + + static const std::string template_map[]; + static const std::map> terrain_fill; + static const std::map> direction_offsets; +}; +#endif diff --git a/unit.cpp b/unit.cpp index d0cfd730..5cef02e5 100644 --- a/unit.cpp +++ b/unit.cpp @@ -559,7 +559,7 @@ json Unit::write_json_orders() return container; } -void Unit::write_json_report(json& j, int obs, int truesight, int detfac, int autosee, int attitude, int showattitudes) +void Unit::build_json_report(json& j, int obs, int truesight, int detfac, int autosee, int attitude, int showattitudes) { int stealth = GetAttribute("stealth"); bool my_unit = (obs == -1); @@ -609,7 +609,7 @@ void Unit::write_json_report(json& j, int obs, int truesight, int detfac, int au j["flags"]["sharing"] = (GetFlag(FLAG_SHARING) ? true : false); if (GetFlag(FLAG_CONSUMING_UNIT)) j["flags"]["consume"] = "unit"; if (GetFlag(FLAG_CONSUMING_FACTION)) j["flags"]["consume"] = "faction"; - j["flags"]["cross_water"] = (GetFlag(FLAG_NOCROSS_WATER) ? true : false); + j["flags"]["no_cross_water"] = GetFlag(FLAG_NOCROSS_WATER) ? true : false; // Only one of these is allowed to be true at a time. if (GetFlag(FLAG_NOSPOILS)) j["flags"]["spoils"] = "weightless"; @@ -643,6 +643,7 @@ void Unit::write_json_report(json& j, int obs, int truesight, int detfac, int au int rate = StudyRateAdjustment(man_days, exp); skill["study_rate"] = rate; } + j["skills"]["known"].push_back(skill); } for (auto i = 0; i < NSKILLS; i++) { if (SkillDefs[i].depends[0].skill != NULL) { @@ -692,6 +693,8 @@ void Unit::write_json_report(json& j, int obs, int truesight, int detfac, int au } + j["items"] = json::array(); + // Clear any marks on the item list since we want to report items in a specific order. // Not sure this is necessary with json output, but we are going to hold it for now so that the array in json // is in the same order the items would be listed in the normal report. @@ -700,7 +703,7 @@ void Unit::write_json_report(json& j, int obs, int truesight, int detfac, int au // now, report the items in the specific order (men, monsters, weapons, mounts, wagons, other, silver) // items will be marked when they are reported to make sure they don't get reported twice if they fit // multiple categories. - for (auto type = 0; type < 7; type++) { + for (auto output_phase = 0; output_phase < 7; output_phase++) { forlist(&items) { Item *item = (Item *)elem; if (item->checked) continue; @@ -712,13 +715,13 @@ void Unit::write_json_report(json& j, int obs, int truesight, int detfac, int au (item->type == I_WAGON) || (item->type == I_MWAGON) || (item->type == I_SILVER) ); - if (phase == 0 && !(def.type & IT_MAN)) continue; - if (phase == 1 && !(def.type & IT_MONSTER)) continue; - if (phase == 2 && !combat_item) continue; - if (phase == 3 && !(def.type & IT_MOUNT)) continue; - if (phase == 4 && !((item->type == I_WAGON) || (item->type == I_MWAGON))) continue; - if (phase == 5 && item_reported_in_other_phase) continue; - if (phase == 6 && !(item->type == I_SILVER)) continue; + if (output_phase == 0 && !(def.type & IT_MAN)) continue; + if (output_phase == 1 && !(def.type & IT_MONSTER)) continue; + if (output_phase == 2 && !combat_item) continue; + if (output_phase == 3 && !(def.type & IT_MOUNT)) continue; + if (output_phase == 4 && !((item->type == I_WAGON) || (item->type == I_MWAGON))) continue; + if (output_phase == 5 && item_reported_in_other_phase) continue; + if (output_phase == 6 && !(item->type == I_SILVER)) continue; item->checked = 1; if (my_unit || (def.weight != 0)) { @@ -736,220 +739,6 @@ void Unit::write_json_report(json& j, int obs, int truesight, int detfac, int au } } -void Unit::write_text_report(ostream& f, int obs, int truesight, int detfac, - int autosee, int attitude, int showattitudes) -{ - int stealth = GetAttribute("stealth"); - if (obs==-1) { - /* The unit belongs to the Faction writing the report */ - obs = 2; - } else { - if (obs < stealth) { - /* The unit cannot be seen */ - if (reveal == REVEAL_FACTION) { - obs = 1; - } else { - if (guard == GUARD_GUARD || reveal == REVEAL_UNIT || autosee) { - obs = 0; - } else { - return; - } - } - } else { - if (obs == stealth) { - /* Can see unit, but not Faction */ - if (reveal == REVEAL_FACTION) { - obs = 1; - } else { - obs = 0; - } - } else { - /* Can see unit and Faction */ - obs = 1; - } - } - } - - /* Setup True Sight */ - if (obs == 2) { - truesight = 1; - } else { - if (GetSkill(S_ILLUSION) > truesight) { - truesight = 0; - } else { - truesight = 1; - } - } - - if (detfac && obs != 2) obs = 1; - - /* Write the report */ - AString temp; - if (obs == 2) { - temp += AString("* ") + *name; - } else { - if (showattitudes) { - switch (attitude) { - case A_ALLY: - temp += AString("= ") +*name; - break; - case A_FRIENDLY: - temp += AString(": ") +*name; - break; - case A_NEUTRAL: - temp += AString("- ") +*name; - break; - case A_UNFRIENDLY: - temp += AString("% ") +*name; - break; - case A_HOSTILE: - temp += AString("! ") +*name; - break; - } - } else { - temp += AString("- ") + *name; - } - } - - if (guard == GUARD_GUARD) temp += ", on guard"; - if (obs > 0) { - temp += AString(", ") + *faction->name; - if (guard == GUARD_AVOID) temp += ", avoiding"; - if (GetFlag(FLAG_BEHIND)) temp += ", behind"; - } - - if (obs == 2) { - if (reveal == REVEAL_UNIT) temp += ", revealing unit"; - if (reveal == REVEAL_FACTION) temp += ", revealing faction"; - if (GetFlag(FLAG_HOLDING)) temp += ", holding"; - if (GetFlag(FLAG_AUTOTAX)) temp += ", taxing"; - if (GetFlag(FLAG_NOAID)) temp += ", receiving no aid"; - if (GetFlag(FLAG_SHARING)) temp += ", sharing"; - if (GetFlag(FLAG_CONSUMING_UNIT)) temp += ", consuming unit's food"; - if (GetFlag(FLAG_CONSUMING_FACTION)) - temp += ", consuming faction's food"; - if (GetFlag(FLAG_NOCROSS_WATER)) temp += ", won't cross water"; - temp += SpoilsReport(); - } - - temp += items.Report(obs, truesight, 0); - - if (obs == 2) { - temp += ". Weight: "; - temp += AString(items.Weight()); - temp += ". Capacity: "; - temp += AString(FlyingCapacity()); - temp += "/"; - temp += AString(RidingCapacity()); - temp += "/"; - temp += AString(WalkingCapacity()); - temp += "/"; - temp += AString(SwimmingCapacity()); - temp += ". Skills: "; - temp += skills.Report(GetMen()); - } - - if (obs == 2 && (type == U_MAGE || type == U_GUARDMAGE)) { - temp += MageReport(); - } - - if (obs == 2) { - temp += ReadyItem(); - temp += StudyableSkills(); - if (visited.size() > 0) { - set::iterator it; - unsigned int count; - - count = 0; - temp += ". Has visited "; - for (it = visited.begin(); - it != visited.end(); - it++) { - count++; - if (count > 1) { - if (count == visited.size()) - temp += " and "; - else - temp += ", "; - } - temp += it->c_str(); - } - } - } - - if (describe) { - temp += AString("; ") + *describe; - } - temp += "."; - f << temp << '\n'; -} - -AString Unit::TemplateReport() -{ - /* Write the report */ - AString temp; - temp = *name; - - if (guard == GUARD_GUARD) temp += ", on guard"; - if (guard == GUARD_AVOID) temp += ", avoiding"; - if (GetFlag(FLAG_BEHIND)) temp += ", behind"; - if (reveal == REVEAL_UNIT) temp += ", revealing unit"; - if (reveal == REVEAL_FACTION) temp += ", revealing faction"; - if (GetFlag(FLAG_HOLDING)) temp += ", holding"; - if (GetFlag(FLAG_AUTOTAX)) temp += ", taxing"; - if (GetFlag(FLAG_NOAID)) temp += ", receiving no aid"; - if (GetFlag(FLAG_SHARING)) temp += ", sharing"; - if (GetFlag(FLAG_CONSUMING_UNIT)) temp += ", consuming unit's food"; - if (GetFlag(FLAG_CONSUMING_FACTION)) temp += ", consuming faction's food"; - if (GetFlag(FLAG_NOCROSS_WATER)) temp += ", won't cross water"; - temp += SpoilsReport(); - - temp += items.Report(2, 1, 0); - temp += ". Weight: "; - temp += AString(items.Weight()); - temp += ". Capacity: "; - temp += AString(FlyingCapacity()); - temp += "/"; - temp += AString(RidingCapacity()); - temp += "/"; - temp += AString(WalkingCapacity()); - temp += "/"; - temp += AString(SwimmingCapacity()); - temp += ". Skills: "; - temp += skills.Report(GetMen()); - - if (type == U_MAGE || type == U_GUARDMAGE) { - temp += MageReport(); - } - temp += ReadyItem(); - temp += StudyableSkills(); - if (visited.size() > 0) { - set::iterator it; - unsigned int count; - - count = 0; - temp += ". Has visited "; - for (it = visited.begin(); - it != visited.end(); - it++) { - count++; - if (count > 1) { - if (count == visited.size()) - temp += " and "; - else - temp += ", "; - } - temp += it->c_str(); - } - } - - if (describe) { - temp += AString("; ") + *describe; - } - temp += "."; - return temp; -} - AString *Unit::BattleReport(int obs) { AString *temp = new AString(""); diff --git a/unit.h b/unit.h index aa9cf5b8..53573341 100644 --- a/unit.h +++ b/unit.h @@ -133,16 +133,14 @@ class Unit : public AListElem AString SpoilsReport(void); int CanGetSpoil(Item *i); - void write_text_report(ostream& f, int obs, int truesight, int detfac, int autosee, int attitude, int showattitudes); - void write_json_report(json& j, int obs, int truesight, int detfac, int autosee, int attitude, int showattitudes); + void build_json_report(json& j, int obs, int truesight, int detfac, int autosee, int attitude, int showattitudes); json write_json_orders(); AString GetName(int); AString MageReport(); AString ReadyItem(); AString StudyableSkills(); AString * BattleReport(int); - AString TemplateReport(); - + void ClearOrders(); void ClearCastOrders(); void DefaultOrders(Object *); diff --git a/unittest/faction_test.cpp b/unittest/faction_test.cpp index 1d40111a..206f745c 100644 --- a/unittest/faction_test.cpp +++ b/unittest/faction_test.cpp @@ -51,18 +51,6 @@ ut::suite<"Faction"> faction_suite = [] expect(eq(current, expected)); }; - "FactionTypeStr returns correct string"_test = [faction] - { - // Mock up that a faction had set their types to 2 war and 3 magic. - faction->type[F_WAR] = 2; - faction->type[F_TRADE] = 0; - faction->type[F_MAGIC] = 3; - - string current = faction->FactionTypeStr().Str(); - string expected = "War 2, Magic 3"; - expect(eq(current, expected)); - }; - // No memory leaks. delete faction; diff --git a/unittest/json_report_test.cpp b/unittest/json_report_test.cpp index eecb7602..a8224cfc 100644 --- a/unittest/json_report_test.cpp +++ b/unittest/json_report_test.cpp @@ -33,7 +33,7 @@ ut::suite<"JSON Report"> json_report_suite = [] // Generate just this single factions json object. Game &game = helper.game_object(); json json_report; - faction->write_json_report(json_report, &game, nullptr); + faction->build_json_report(json_report, &game, nullptr); // pick some of the data out of the report for checking string data_name = json_report["name"]; @@ -66,7 +66,7 @@ ut::suite<"JSON Report"> json_report_suite = [] faction->error("This is an error"); json json_report; - faction->write_json_report(json_report, &helper.game_object(), nullptr); + faction->build_json_report(json_report, &helper.game_object(), nullptr); auto count = json_report["errors"].size(); expect(count == 1_ul); @@ -85,7 +85,7 @@ ut::suite<"JSON Report"> json_report_suite = [] for (auto i = 0; i < 1003; i++) faction->error("This is error #" + to_string(i+1)); json json_report; - faction->write_json_report(json_report, &helper.game_object(), nullptr); + faction->build_json_report(json_report, &helper.game_object(), nullptr); auto count = json_report["errors"].size(); expect(count == 1001_ul); @@ -109,7 +109,7 @@ ut::suite<"JSON Report"> json_report_suite = [] faction->event("This is event 2"); json json_report; - faction->write_json_report(json_report, &helper.game_object(), nullptr); + faction->build_json_report(json_report, &helper.game_object(), nullptr); auto count = json_report["events"].size(); expect(count == 2_ul); @@ -139,7 +139,7 @@ ut::suite<"JSON Report"> json_report_suite = [] ARegionList *regions = helper.get_regions(); json json_report; - region->write_json_report(json_report, faction, helper.get_month(), regions); + region->build_json_report(json_report, faction, helper.get_month(), regions); string expected_provice("Testing Wilds"); // name given in the unit test setup string province = json_report["province"]; @@ -239,8 +239,8 @@ ut::suite<"JSON Report"> json_report_suite = [] // Get a report for each region so we can verify that fleet data is correct for owners and non-owners. json json_report_1; json json_report_2; - region->write_json_report(json_report_1, faction, helper.get_month(), regions); - region->write_json_report(json_report_2, faction2, helper.get_month(), regions); + region->build_json_report(json_report_1, faction, helper.get_month(), regions); + region->build_json_report(json_report_2, faction2, helper.get_month(), regions); // Verify that owner sees additional data auto capacity = json_report_1["structures"][0]["capacity"]; @@ -282,7 +282,7 @@ ut::suite<"JSON Report"> json_report_suite = [] // Get a report for each region so we can verify that fleet data is correct for owners and non-owners. json json_unit_report; - leader->write_json_report(json_unit_report, -1, 1, 1, 1, A_ALLY, 1); + leader->build_json_report(json_unit_report, -1, 1, 1, 1, A_ALLY, 1); string name = json_unit_report["name"]; string expected_name = "My Leader"; diff --git a/unittest/testhelper.hpp b/unittest/testhelper.hpp index 1eb17e79..996e65ea 100644 --- a/unittest/testhelper.hpp +++ b/unittest/testhelper.hpp @@ -1,3 +1,7 @@ +#pragma once +#ifndef __UNIT_TEST_HELPER_HPP__ +#define __UNIT_TEST_HELPER_HPP__ + #include "game.h" #include @@ -57,3 +61,5 @@ class UnitTestHelper { streambuf *cout_streambuf; Game game; }; + +#endif