diff --git a/aregion.cpp b/aregion.cpp index 3d6e794c..b7f7a016 100644 --- a/aregion.cpp +++ b/aregion.cpp @@ -161,6 +161,15 @@ void ARegion::SetName(char const *c) name = new AString(c); } +Production *ARegion::get_production_for_skill(int item, int skill) { + if (products.size() == 0) return nullptr; + auto p = find_if( + products.begin(), + products.end(), + [item, skill](Production *p) { return p->itemtype == item && p->skill == skill; } + ); + return (p != products.end()) ? *p : nullptr; +} int ARegion::IsNativeRace(int item) { @@ -193,7 +202,7 @@ int ARegion::GetNearestProd(int item) ARegion *r = ((ARegionPtr *) elem)->ptr; AString skname = ItemDefs[item].pSkill; int sk = LookupSkill(&skname); - if (r->products.GetProd(item, sk)) { + if (r->get_production_for_skill(item, sk)) { regs.DeleteAll(); regs2.DeleteAll(); return i; @@ -1046,8 +1055,10 @@ void ARegion::Writeout(ostream& f) f << xloc << '\n' << yloc << '\n' << zloc << '\n'; f << visited << '\n'; - products.Writeout(f); - markets.Writeout(f); + f << products.size() << '\n'; + for (const auto& product : products) product->Writeout(f); + f << markets.size() << '\n'; + for (const auto& market : markets) market->Writeout(f); f << objects.Num() << '\n'; forlist ((&objects)) ((Object *) elem)->Writeout(f); @@ -1110,14 +1121,25 @@ void ARegion::Readin(istream &f, AList *facs) f >> zloc; f >> visited; - products.Readin(f); - markets.Readin(f); + f >> n; + products.reserve(n); + for (int i = 0; i < n; i++) { + Production *p = new Production(); + p->Readin(f); + products.push_back(p); + } - int i; - f >> i; + f >> n; + markets.reserve(n); + for (int i = 0; i < n; i++) { + Market *m = new Market(); + m->Readin(f); + markets.push_back(m); + } + f >> n; buildingseq = 1; - for (int j = 0; j < i; j++) { + for (int j = 0; j < n; j++) { Object *temp = new Object(this); temp->Readin(f, facs); if (temp->num >= buildingseq) @@ -1176,47 +1198,25 @@ int ARegion::CanMakeAdv(Faction *fac, int item) void ARegion::WriteProducts(ostream& f, Faction *fac, int present) { - AString temp = "Products: "; - int has = 0; - forlist((&products)) { - Production *p = ((Production *) elem); - if (ItemDefs[p->itemtype].type & IT_ADVANCED) { - if (CanMakeAdv(fac, p->itemtype) || fac->is_npc) { - if (has) { - temp += AString(", ") + p->WriteReport(); - } else { - has = 1; - temp += p->WriteReport(); - } - } - } else { - if (p->itemtype == I_SILVER) { - if (p->skill == S_ENTERTAINMENT) { - if ((Globals->TRANSIT_REPORT & - GameDefs::REPORT_SHOW_ENTERTAINMENT) || present) { - f << "Entertainment available: $" << p->amount << ".\n"; - } else { - f << "Entertainment available: $0.\n"; - } - } - } else { - if (!present && - !(Globals->TRANSIT_REPORT & - GameDefs::REPORT_SHOW_RESOURCES)) - continue; - if (has) { - temp += AString(", ") + p->WriteReport(); - } else { - has = 1; - temp += p->WriteReport(); - } - } - } + 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"; } - if (has==0) temp += "none"; - temp += "."; - f << temp.const_str() << "\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) @@ -1237,12 +1237,9 @@ void ARegion::WriteMarkets(ostream &f, Faction *fac, int present) { f << "Wanted: "; int has = 0; - forlist(&markets) { - Market *m = (Market *) elem; + for (const auto& m : markets) { if (!m->amount) continue; - if (!present && - !(Globals->TRANSIT_REPORT & GameDefs::REPORT_SHOW_MARKETS)) - 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) { @@ -1251,38 +1248,23 @@ void ARegion::WriteMarkets(ostream &f, Faction *fac, int present) } } } - if (has) { - f << ", "; - } else { - has = 1; - } - f << m->Report().const_str(); + f << (has ? ", " : "") << m->Report().const_str(); + has = 1; } } - if (!has) f << "none"; - f << ".\n"; + f << (has ? "." : "none.") << '\n'; f << "For Sale: "; has = 0; - { - forlist(&markets) { - Market *m = (Market *) elem; - if (!m->amount) continue; - if (!present && - !(Globals->TRANSIT_REPORT & GameDefs::REPORT_SHOW_MARKETS)) - continue; - if (m->type == M_BUY) { - if (has) { - f << ", "; - } else { - has = 1; - } - f << m->Report().const_str(); - } + 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; } } - if (!has) f << "none"; - f << ".\n"; + f << (has ? "." : "none.") << '\n'; } void ARegion::WriteEconomy(ostream& f, Faction *fac, int present) @@ -1319,15 +1301,8 @@ void ARegion::WriteExits(ostream& f, ARegionList *pRegs, int *exits_seen) f << '\n'; } -void ARegion::write_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; - - // this faction cannot see this region, why are we even here? - if (!farsight && !passer && !present) return; - - // The region is visible to the faction +json ARegion::basic_json_data(ARegionList *regions) { + json j; j["terrain"] = TerrainDefs[type].name; ARegionArray *level = regions->pRegionArrays[zloc]; @@ -1338,6 +1313,18 @@ void ARegion::write_json_report(json& j, Faction *fac, int month, ARegionList *r if (town) { j["settlement"] = { { "name", town->name->const_str() }, { "size", TownString(town->TownType()).const_str() } }; } + return j; +} + +void ARegion::write_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; + + // this faction cannot see this region, why are we even here? + if (!farsight && !passer && !present) return; + + j = basic_json_data(regions); if (Population() && (present || farsight || (Globals->TRANSIT_REPORT & GameDefs::REPORT_SHOW_PEASANTS))) { j["population"] = { { "amount", Population() } }; @@ -1363,49 +1350,92 @@ void ARegion::write_json_report(json& j, Faction *fac, int month, ARegionList *r j["description"] = desc.str(); } - // hold off on writing out other stuff for now.. Just the top level region info. - /* - WriteEconomy(f, fac, present || farsight); + 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)) + ? json{ { "amount", wages }, { "max", max_wages } } + : json{ { "amount", 0 } }; - 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. + json wanted = json::array(); + json for_sale = json::array(); + for (const auto& m : markets) { + if (!m->amount) continue; + if (!present && !(Globals->TRANSIT_REPORT & GameDefs::REPORT_SHOW_MARKETS)) continue; - // 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]; - } + ItemType itemdef = ItemDefs[m->item]; + json item = { + { "name", itemdef.name }, { "plural", itemdef.names }, { "abbr", itemdef.abr }, { "price", m->price } + }; + if (m->amount != -1) item["amount"] = m->amount; + else item["unlimited"] = true; + + if (m->type == M_SELL) { + if (ItemDefs[m->item].type & IT_ADVANCED) { + if (!Globals->MARKETS_SHOW_ADVANCED_ITEMS) { + if (!HasItem(fac, m->item)) continue; + } + } + wanted.push_back(item); + } else { + for_sale.push_back(item); + } + } + 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; + + j["products"] = json::array(); + for (const auto& p : products) { + ItemType itemdef = ItemDefs[p->itemtype]; + if (p->itemtype == I_SILVER) continue; // wages and entertainment handled seperately. + // 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; + json item = { + { "name", itemdef.name }, { "plural", itemdef.names }, { "abbr", itemdef.abr }, + }; + // I don't think resources can be unlimited, but just in case, we will handle it. + if (p->amount != -1) item["amount"] = p->amount; + else item["unlimited"] = true; + j["products"].push_back(item); + } + + bool default_state = (present || farsight || (Globals->TRANSIT_REPORT & GameDefs::REPORT_SHOW_ALL_EXITS)); + bool exits_seen[NDIRS]; + std::fill_n(exits_seen, NDIRS, default_state); + // If we are only showing used exits, we need to walk the list of whomever passed through and update it with ones + // our units used. + if (Globals->TRANSIT_REPORT & GameDefs::REPORT_SHOW_USED_EXITS) { + forlist(&passers) { + Farsight *p = (Farsight *)elem; + if (p->faction == fac) { + for (auto i = 0; i < NDIRS; i++) { + exits_seen[i] |= (bool)p->exits_used[i]; } } } } - WriteExits(f, pRegions, exits_seen); + j["exits"] = json::array(); + for (int i=0; ibasic_json_data(regions) } } + ); + } if (Globals->GATES_EXIST && gate && gate != -1) { - int sawgate = 0; - if (fac->is_npc) - sawgate = 1; + bool can_see_gate = false; + if (fac->is_npc) can_see_gate = true; 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 (watcher->unit->GetSkill(S_GATE_LORE)) can_see_gate = true; } } } @@ -1413,9 +1443,7 @@ void ARegion::write_json_report(json& j, Faction *fac, int month, ARegionList *r forlist(&passers) { Farsight *watcher = (Farsight *)elem; if (watcher && watcher->faction == fac && watcher->unit) { - if (watcher->unit->GetSkill(S_GATE_LORE)) { - sawgate = 1; - } + if (watcher->unit->GetSkill(S_GATE_LORE)) can_see_gate = true; } } } @@ -1423,22 +1451,18 @@ void ARegion::write_json_report(json& j, Faction *fac, int month, ARegionList *r Object *o = (Object *) elem; forlist(&o->units) { Unit *u = (Unit *) elem; - if (!sawgate && - ((u->faction == fac) && - u->GetSkill(S_GATE_LORE))) { - sawgate = 1; - } + if ((u->faction == fac) && u->GetSkill(S_GATE_LORE)) can_see_gate = true; } } - if (sawgate) { + + // Ok, someone from this faction can see the gate. + if (can_see_gate) { + j["gate"]["open"] = gateopen; if (gateopen) { - f << "There is a Gate here (Gate " << gate; + j["gate"]["number"] = gate; if (!Globals->DISPERSE_GATE_NUMBERS) { - f << " of " << pRegions->numberofgates; + j["gate"]["total"] = regions->numberofgates; } - f << ").\n\n"; - } else if (Globals->SHOW_CLOSED_GATES) { - f << "There is a closed Gate here.\n\n"; } } } @@ -1488,15 +1512,13 @@ void ARegion::write_json_report(json& j, Faction *fac, int month, ARegionList *r } } + // extra block because of freaking AList. { forlist (&objects) { - ((Object *) elem)->Report(f, fac, obs, truesight, detfac, - passobs, passtrue, passdetfac, - present || farsight); + Object *o = (Object *) elem; + o->write_json_report(j, fac, obs, truesight, detfac, passobs, passtrue, passdetfac, present || farsight); } - f << '\n'; } - */ } void ARegion::write_text_report(ostream &f, Faction *fac, int month, ARegionList *pRegions) @@ -1683,7 +1705,7 @@ void ARegion::write_text_report(ostream &f, Faction *fac, int month, ARegionList { forlist (&objects) { - ((Object *) elem)->Report(f, fac, obs, truesight, detfac, + ((Object *) elem)->write_text_report(f, fac, obs, truesight, detfac, passobs, passtrue, passdetfac, present || farsight); } @@ -1968,8 +1990,7 @@ Unit *ARegion::ForbiddenByAlly(Unit *u) Object *obj = (Object *) elem; forlist ((&obj->units)) { Unit *u2 = (Unit *) elem; - if (u->faction->GetAttitude(u2->faction->num) == A_ALLY && - u2->Forbids(this, u)) return u2; + if (u->faction->get_attitude(u2->faction->num) == A_ALLY && u2->Forbids(this, u)) return u2; } } return 0; @@ -2952,7 +2973,7 @@ struct WaterBody { }; int findWaterBody(std::vector& waterBodies, ARegion* reg) { - for (auto water : waterBodies) { + for (const auto& water : waterBodies) { if (water->includes(reg)) { return water->name; } @@ -2995,7 +3016,7 @@ bool isNearWaterBody(ARegion* reg, WaterBody* wb) { } bool isNearWaterBody(ARegion* reg, std::vector& list) { - for (auto wb : list) { + for (const auto& wb : list) { if (isNearWaterBody(reg, wb)) { return true; } @@ -3045,7 +3066,7 @@ void makeRivers(Map* map, ARegionArray* arr, std::vector& waterBodie wb->name = waterBodyName++; waterBodies.push_back(wb); - for (auto kv : result) { + for (const auto& kv : result) { wb->add(kv.first); } } @@ -3064,7 +3085,7 @@ void makeRivers(Map* map, ARegionArray* arr, std::vector& waterBodie } } - for (auto water : waterBodies) { + for (const auto& water : waterBodies) { std::cout << "WATER BODY " << water->name << std::endl; graph.setInclusion([ water, &innerWater ](ARegion* current, ARegion* next) { @@ -3072,13 +3093,13 @@ void makeRivers(Map* map, ARegionArray* arr, std::vector& waterBodie return !water->includes(loc) && innerWater.find(loc) == innerWater.end(); }); - for (auto loc : water->regions) { + for (const auto& loc : water->regions) { if (innerWater.find(loc) != innerWater.end()) { continue; } auto result = graphs::breadthFirstSearch(graph, loc); - for (auto kv : result) { + for (const auto& kv : result) { int newDist = kv.second.distance + 1; int otherWater = findWaterBody(waterBodies, graph.get(kv.first)); @@ -3187,7 +3208,7 @@ void makeRivers(Map* map, ARegionArray* arr, std::vector& waterBodie graphs::Location2D riverEnd; int riverCost = INT32_MAX; - for (auto start : source->regions) { + for (const auto& start : source->regions) { if (innerWater.find(start) != innerWater.end()) { continue; } @@ -4253,22 +4274,16 @@ void ARegionList::ResoucesStatistics() { forlist(this) { ARegion* reg = (ARegion*) elem; - { - forlist (®->products) { - Production* p = (Production*) elem; - resources[p->itemtype] += p->amount; - } + for (const auto& p : reg->products) { + resources[p->itemtype] += p->amount; } - { - forlist (®->markets) { - Market* m = (Market*) elem; - if (m->type == M_BUY) { - forSale[m->item] += m->amount; - } - else { - wanted[m->item] += m->amount; - } + for (const auto& m : reg->markets) { + if (m->type == M_BUY) { + forSale[m->item] += m->amount; + } + else { + wanted[m->item] += m->amount; } } } diff --git a/aregion.h b/aregion.h index 63f07a68..cfb244ef 100644 --- a/aregion.h +++ b/aregion.h @@ -202,6 +202,7 @@ class ARegion : public AListElem 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); @@ -353,8 +354,8 @@ class ARegion : public AListElem AList farsees; // List of units which passed through the region AList passers; - ProductionList products; - MarketList markets; + std::vector products; + std::vector markets; int xloc, yloc, zloc; int visited; @@ -362,6 +363,8 @@ class ARegion : public AListElem int distance; ARegion *next; + // find a production for a certain skill. + Production *get_production_for_skill(int item, int skill); // Editing functions void UpdateEditRegion(); void SetupEditRegion(); diff --git a/basic/world.cpp b/basic/world.cpp index d09b9023..68542de5 100644 --- a/basic/world.cpp +++ b/basic/world.cpp @@ -2515,44 +2515,43 @@ void ARegion::MakeStartingCity() town->dev = TownDevelopment(); float ratio; + + for (auto& m : markets) delete m; // Free the allocated object + markets.clear(); // empty the vector. + Market *m; - markets.DeleteAll(); if (Globals->START_CITIES_START_UNLIMITED) { for (int i=0; iBASE_MAN_COST * 10); // hack: include wage factor of 10 in float calculation above - m=new Market(M_BUY,race,(int)(Wages()*4*ratio),-1, 5000,5000,-1,-1); - markets.Add(m); + m = new Market(M_BUY,race,(int)(Wages()*4*ratio),-1, 5000,5000,-1,-1); + markets.push_back(m); if (Globals->LEADERS_EXIST) { ratio=ItemDefs[I_LEADERS].baseprice/((float)Globals->BASE_MAN_COST * 10); // hack: include wage factor of 10 in float calculation above - m = new Market(M_BUY,I_LEADERS,(int)(Wages()*4*ratio), - -1,5000,5000,-1,-1); - markets.Add(m); + m = new Market(M_BUY,I_LEADERS,(int)(Wages()*4*ratio), -1,5000,5000,-1,-1); + markets.push_back(m); } } else { SetupCityMarket(); ratio = ItemDefs[race].baseprice / ((float)Globals->BASE_MAN_COST * 10); // hack: include wage factor of 10 in float calculation above /* Setup Recruiting */ - m = new Market( M_BUY, race, (int)(Wages()*4*ratio), - Population()/5, 0, 10000, 0, 2000 ); - markets.Add(m); + m = new Market( M_BUY, race, (int)(Wages()*4*ratio), Population()/5, 0, 10000, 0, 2000 ); + markets.push_back(m); if ( Globals->LEADERS_EXIST ) { ratio=ItemDefs[I_LEADERS].baseprice/((float)Globals->BASE_MAN_COST * 10); // hack: include wage factor of 10 in float calculation above - m = new Market( M_BUY, I_LEADERS, (int)(Wages()*4*ratio), - Population()/25, 0, 10000, 0, 400 ); - markets.Add(m); + m = new Market( M_BUY, I_LEADERS, (int)(Wages()*4*ratio), Population()/25, 0, 10000, 0, 400 ); + markets.push_back(m); } } } diff --git a/battle.cpp b/battle.cpp index d9af070c..9cefe425 100644 --- a/battle.cpp +++ b/battle.cpp @@ -747,7 +747,7 @@ void Battle::write_text_report(ostream& f,Faction * fac) { f << asstext << "\n"; return; } - for(auto line: text) { + for (const auto& line: text) { f << line << '\n'; } } diff --git a/economy.cpp b/economy.cpp index 0e1bef7d..0c975049 100644 --- a/economy.cpp +++ b/economy.cpp @@ -80,7 +80,7 @@ int ARegion::Wages() AString ARegion::WagesForReport() { - Production *p = products.GetProd(I_SILVER, -1); + Production *p = get_production_for_skill(I_SILVER, -1); if (p) { return AString("$") + (p->productivity / 10) + "." + (p->productivity % 10) + " (Max: $" + p->amount + ")"; @@ -207,22 +207,20 @@ void ARegion::SetupEconomy() { e->amount += wbonus / Globals->ENTERTAIN_FRACTION; e->baseamount += wbonus / Globals->ENTERTAIN_FRACTION; } - products.Add(w); - products.Add(e); + products.push_back(w); + products.push_back(e); float ratio = ItemDefs[race].baseprice / ((float)Globals->BASE_MAN_COST * 10); // hack: include wage factor of 10 in float assignment above // Setup Recruiting - Market *m = new Market(M_BUY, race, (int)(Wages()*4*ratio), - Population()/25, 0, 10000, 0, 2000); - markets.Add(m); + Market *m = new Market(M_BUY, race, (int)(Wages()*4*ratio), Population()/25, 0, 10000, 0, 2000); + markets.push_back(m); if (Globals->LEADERS_EXIST) { ratio = ItemDefs[I_LEADERS].baseprice / ((float)Globals->BASE_MAN_COST * 10); // hack: include wage factor of 10 in float assignment above - m = new Market(M_BUY, I_LEADERS, (int)(Wages()*4*ratio), - Population()/125, 0, 10000, 0, 400); - markets.Add(m); + m = new Market(M_BUY, I_LEADERS, (int)(Wages()*4*ratio), Population()/125, 0, 10000, 0, 400); + markets.push_back(m); } } @@ -294,12 +292,12 @@ void ARegion::SetIncome() } int maxwages = (int) ((float) (pp * (Wages() - 10 * Globals->MAINTENANCE_COST) /50)); if (maxwages < 0) maxwages = 0; - Production * w = products.GetProd(I_SILVER,-1); + Production * w = get_production_for_skill(I_SILVER,-1); // In some cases (ie. after products.DeleteAll() in EditGameRegionTerrain) // I_SILVER is not in ProductionList if( !w ) { w = new Production; - products.Add(w); + products.push_back(w); } w->itemtype = I_SILVER; w->amount = maxwages / Globals->WORK_FRACTION; @@ -316,12 +314,12 @@ void ARegion::SetIncome() } int maxent = (int) ((float) (ep * ((Wages() - 10 * Globals->MAINTENANCE_COST) + 10) /50)); if (maxent < 0) maxent = 0; - Production * e = products.GetProd(I_SILVER,S_ENTERTAINMENT); + Production * e = get_production_for_skill(I_SILVER,S_ENTERTAINMENT); // In some cases (ie. after products.DeleteAll() in EditGameRegionTerrain) // I_SILVER is not in ProductionList if( !e ) { e = new Production; - products.Add(e); + products.push_back(e); } e->itemtype = I_SILVER; e->amount = maxent / Globals->ENTERTAIN_FRACTION; @@ -472,9 +470,8 @@ void ARegion::SetupCityMarket() cap = (citymax * 3/4) - 5000; if (cap < 0) cap = citymax/2; - Market * m = new Market (M_SELL, i, price, amt, population, - population+cap, amt, amt*2); - markets.Add(m); + Market * m = new Market (M_SELL, i, price, amt, population, population+cap, amt, amt*2); + markets.push_back(m); } else if (i == I_FOOD) { // Add foodstuffs directly to market int amt = Globals->CITY_MARKET_NORMAL_AMT; @@ -489,9 +486,8 @@ void ARegion::SetupCityMarket() cap = (citymax * 3/4) - 5000; if (cap < 0) cap = citymax/2; - Market * m = new Market (M_BUY, i, price, amt, population, - population+2*cap, amt, amt*5); - markets.Add(m); + Market * m = new Market (M_BUY, i, price, amt, population, population+2*cap, amt, amt*5); + markets.push_back(m); } else if (ItemDefs[i].pInput[0].item == -1) { // Basic resource // Add to supply? @@ -553,9 +549,8 @@ void ARegion::SetupCityMarket() if (cap < citymax/2) cap = citymax / 2; offset = citymax / 8; if (cap+offset < citymax) { - Market * m = new Market (M_SELL, i, price, amt/6, population+cap+offset, - population+citymax, 0, amt); - markets.Add(m); + Market * m = new Market (M_SELL, i, price, amt/6, population+cap+offset, population+citymax, 0, amt); + markets.push_back(m); } } } @@ -585,9 +580,8 @@ void ARegion::SetupCityMarket() cap = (citymax *3/4) - 5000; if (cap < citymax/2) cap = citymax / 2; offset = (citymax/20) + ((citymax/5) * 2); - Market * m = new Market (M_SELL, i, price, amt/6, population+cap, - population+citymax, 0, amt); - markets.Add(m); + Market * m = new Market (M_SELL, i, price, amt/6, population+cap, population+citymax, 0, amt); + markets.push_back(m); } } @@ -620,9 +614,8 @@ void ARegion::SetupCityMarket() cap = (citymax/4); offset = - (citymax/20) + ((5-num) * citymax * 3/40); - Market * m = new Market (M_SELL, i, price, amt/6, - population+cap+offset, population+citymax, 0, amt); - markets.Add(m); + Market * m = new Market (M_SELL, i, price, amt/6, population+cap+offset, population+citymax, 0, amt); + markets.push_back(m); demand[i] = 0; num--; } @@ -657,10 +650,8 @@ void ARegion::SetupCityMarket() cap = (citymax/4); offset = ((3-num) * citymax * 3 / 40); if (supply[i] < 4) offset += citymax / 20; - Market * m = new Market (M_BUY, i, price, 0, - population+cap+offset, population+citymax, - 0, amt); - markets.Add(m); + Market * m = new Market (M_BUY, i, price, 0, population+cap+offset, population+citymax, 0, amt); + markets.push_back(m); supply[i] = 0; num--; } @@ -719,9 +710,8 @@ void ARegion::SetupCityMarket() tradesell++; offset = - (citymax/20) + tradesell * (tradesell * tradesell * citymax/40); if (cap + offset < citymax) { - Market * m = new Market (M_SELL, i, price, amt/5, cap+population+offset, - citymax+population, 0, amt); - markets.Add(m); + Market * m = new Market (M_SELL, i, price, amt/5, cap+population+offset, citymax+population, 0, amt); + markets.push_back(m); } } @@ -743,9 +733,8 @@ void ARegion::SetupCityMarket() cap = (citymax/2); offset = tradebuy++ * (citymax/6); if (cap+offset < citymax) { - Market * m = new Market (M_BUY, i, price, amt/6, cap+population+offset, - citymax+population, 0, amt); - markets.Add(m); + Market * m = new Market (M_BUY, i, price, amt/6, cap+population+offset, citymax+population, 0, amt); + markets.push_back(m); } } } @@ -776,7 +765,7 @@ void ARegion::SetupProds(double weight) p = new Production(I_FISH, typer->economy); break; } - products.Add(p); + products.push_back(p); } } @@ -788,7 +777,7 @@ void ARegion::SetupProds(double weight) if (!(ItemDefs[item].flags & ItemType::DISABLED) && (getrandom(100) < chance)) { p = new Production(item, amt); - products.Add(p); + products.push_back(p); } } } @@ -906,29 +895,24 @@ void ARegion::UpdateEditRegion() { // redo markets and entertainment/tax income for extra people. SetIncome(); - markets.PostTurn(Population(), Wages()); + for (auto& m : markets) m->PostTurn(Population(), Wages()); //Replace man selling - forlist(&markets) { - Market *m = (Market *) elem; - if (ItemDefs[m->item].type & IT_MAN) { - markets.Remove(m); - delete m; - } - } + markets.erase( + remove_if(markets.begin(), markets.end(), [](const Market * m) { return ItemDefs[m->item].type & IT_MAN; }), + markets.end() + ); float ratio = ItemDefs[race].baseprice / (float) (Globals->BASE_MAN_COST * 10); // hack: include wage factor of 10 in float calculation above - Market *m = new Market(M_BUY, race, (int)(Wages()*4*ratio), - Population()/25, 0, 10000, 0, 2000); - markets.Add(m); + Market *m = new Market(M_BUY, race, (int)(Wages()*4*ratio), Population()/25, 0, 10000, 0, 2000); + markets.push_back(m); if (Globals->LEADERS_EXIST) { ratio = ItemDefs[I_LEADERS].baseprice / (float) (Globals->BASE_MAN_COST * 10); // hack: include wage factor of 10 in float calculation above - m = new Market(M_BUY, I_LEADERS, (int)(Wages()*4*ratio), - Population()/125, 0, 10000, 0, 400); - markets.Add(m); + m = new Market(M_BUY, I_LEADERS, (int)(Wages()*4*ratio), Population()/125, 0, 10000, 0, 400); + markets.push_back(m); } } @@ -1039,23 +1023,20 @@ void ARegion::SetupEditRegion() float ratio = ItemDefs[race].baseprice / ((float)Globals->BASE_MAN_COST * 10); // hack: include wage factor of 10 in float assignment above // Setup Recruiting - Market *m = new Market(M_BUY, race, (int)(Wages()*4*ratio), - Population()/25, 0, 10000, 0, 2000); - markets.Add(m); + Market *m = new Market(M_BUY, race, (int)(Wages()*4*ratio), Population()/25, 0, 10000, 0, 2000); + markets.push_back(m); if (Globals->LEADERS_EXIST) { ratio = ItemDefs[I_LEADERS].baseprice / ((float)Globals->BASE_MAN_COST * 10); // hack: include wage factor of 10 in float assignment above - m = new Market(M_BUY, I_LEADERS, (int)(Wages()*4*ratio), - Population()/125, 0, 10000, 0, 400); - markets.Add(m); + m = new Market(M_BUY, I_LEADERS, (int)(Wages()*4*ratio), Population()/125, 0, 10000, 0, 400); + markets.push_back(m); } } void ARegion::UpdateProducts() { - forlist (&products) { - Production *prod = (Production *) elem; + for (auto& prod : products) { int lastbonus = prod->baseamount / 2; int bonus = 0; @@ -1102,10 +1083,8 @@ int ARegion::BaseDev() int ARegion::ProdDev() { int basedev = BaseDev(); - forlist(&products) { - Production *p = (Production *) elem; - if (ItemDefs[p->itemtype].type & IT_NORMAL && - p->itemtype != I_SILVER) { + for (const auto& p : products) { + if (ItemDefs[p->itemtype].type & IT_NORMAL && p->itemtype != I_SILVER) { basedev += p->activity; } } @@ -1211,8 +1190,7 @@ int ARegion::TownGrowth() // Calculate target population from market activity int amt = 0; int tot = 0; - forlist(&markets) { - Market *m = (Market *) elem; + for (const auto& m : markets) { if (Population() > m->minpop) { if (ItemDefs[m->item].type & IT_TRADE) { if (m->type == M_BUY) { @@ -1288,10 +1266,8 @@ void ARegion::Grow() // Check resource production activity int activity = 0; int amount = 0; - forlist(&products) { - Production *p = (Production *) elem; - if (ItemDefs[p->itemtype].type & IT_NORMAL && - p->itemtype != I_SILVER) { + for (const auto& p : products) { + if (ItemDefs[p->itemtype].type & IT_NORMAL && p->itemtype != I_SILVER) { activity += p->activity; // bonuses for productivity are calculated from // the _baseamount_ of all resources. @@ -1464,7 +1440,7 @@ int ARegion::MigrationAttractiveness(int homedev, int range, int round) mdev -= 8 * range; if (mdev <= homedev) return 0; /* available entertainment */ - Production *p = products.GetProd(I_SILVER, S_ENTERTAINMENT); + Production *p = get_production_for_skill(I_SILVER, S_ENTERTAINMENT); int entertain = p->activity / 20; /* available space */ float space = 1 / 2; @@ -1585,28 +1561,25 @@ void ARegion::PostTurn(ARegionList *pRegs) if (IsStartingCity() && !HasCityGuard() && !Globals->SAFE_START_CITIES) { // Make sure we haven't already been modified. int done = 1; - forlist(&markets) { - Market *m = (Market *)elem; + for (const auto& m : markets) { if (m->minamt == -1) { done = 0; break; } } + if (!done) { - markets.DeleteAll(); + for (auto& m : markets) delete m; // Free the allocated object + markets.clear(); // empty the vector. SetupCityMarket(); - float ratio = ItemDefs[race].baseprice / - (float) (Globals->BASE_MAN_COST * 10); + float ratio = ItemDefs[race].baseprice / (float) (Globals->BASE_MAN_COST * 10); // Setup Recruiting - Market *m = new Market(M_BUY, race, (int)(Wages()*4*ratio), - Population()/25, 0, 10000, 0, 2000); - markets.Add(m); + Market *m = new Market(M_BUY, race, (int)(Wages()*4*ratio), Population()/25, 0, 10000, 0, 2000); + markets.push_back(m); if (Globals->LEADERS_EXIST) { - ratio = ItemDefs[I_LEADERS].baseprice / - (float)Globals->BASE_MAN_COST; - m = new Market(M_BUY, I_LEADERS, (int)(Wages()*4*ratio), - Population()/125, 0, 10000, 0, 400); - markets.Add(m); + ratio = ItemDefs[I_LEADERS].baseprice / (float)Globals->BASE_MAN_COST; + m = new Market(M_BUY, I_LEADERS, (int)(Wages()*4*ratio), Population()/125, 0, 10000, 0, 400); + markets.push_back(m); } } } @@ -1617,9 +1590,9 @@ void ARegion::PostTurn(ARegionList *pRegs) } /* update markets */ - markets.PostTurn(Population(),Wages()); + for (auto& m : markets) m->PostTurn(Population(), Wages()); - /* update resources */ + /* update resources */ UpdateProducts(); // Set these guys to 0. diff --git a/edit.cpp b/edit.cpp index b7c7fae2..88966914 100644 --- a/edit.cpp +++ b/edit.cpp @@ -453,8 +453,7 @@ void Game::EditGameRegionTerrain( ARegion *pReg ) // write products AString temp = "Products: "; int has = 0; - forlist((&pReg->products)) { - Production * p = ((Production *) elem); + for (const auto& p : pReg->products) { if (ItemDefs[p->itemtype].type & IT_ADVANCED) { if (has) { temp += AString(", ") + p->WriteReport(); @@ -643,13 +642,13 @@ void Game::EditGameRegionTerrain( ARegion *pReg ) } else if (*pToken == "p") { SAFE_DELETE(pToken); - forlist((&pReg->products)) { - Production * p = ((Production *) elem); - if (p->itemtype!=I_SILVER) { - pReg->products.Remove(p); - delete p; - } - } + auto removes = remove_if( + pReg->products.begin(), + pReg->products.end(), + [](Production *p) { return p->itemtype != I_SILVER; } + ); + for_each (removes, pReg->products.end(), [](Production *p) mutable { delete p; }); + pReg->products.erase(removes, pReg->products.end()); pReg->SetupProds(1); } else if (*pToken == "g") { @@ -658,10 +657,12 @@ void Game::EditGameRegionTerrain( ARegion *pReg ) if (pReg->town) delete pReg->town; pReg->town = NULL; - pReg->products.DeleteAll(); + for (auto& p : pReg->products) delete p; // Free the allocated object + pReg->products.clear(); // empty the vector. pReg->SetupProds(1); - - pReg->markets.DeleteAll(); + + for (auto& m : pReg->markets) delete m; // Free the allocated object + pReg->markets.clear(); // empty the vector. pReg->SetupEditRegion(); pReg->UpdateEditRegion(); @@ -697,7 +698,8 @@ void Game::EditGameRegionTerrain( ARegion *pReg ) if (pReg->town) { *townname = *pReg->town->name; delete pReg->town; - pReg->markets.DeleteAll(); + for (auto& m : pReg->markets) delete m; // Free the allocated object + pReg->markets.clear(); // empty the vector. } pReg->AddTown(townname); @@ -708,7 +710,9 @@ void Game::EditGameRegionTerrain( ARegion *pReg ) if (pReg->town) { delete pReg->town; pReg->town = NULL; - pReg->markets.DeleteAll(); + for (auto& m : pReg->markets) delete m; // Free the allocated object + pReg->markets.clear(); // empty the vector. + pReg->UpdateEditRegion(); } } @@ -742,8 +746,7 @@ void Game::EditGameRegionMarkets( ARegion *pReg ) Awrite(AString("Market Format: ... price(base). minpop/maxpop. minamt/maxamt.")); Awrite("Wanted: "); - forlist(&pReg->markets) { - Market *m = (Market *) elem; + for (const auto &m : pReg->markets) { if (m->type == M_SELL) { AString temp = ItemString(m->item, m->amount) + " at $" + m->price + "(" + m->baseprice + ")."; temp += AString(" Pop: ") + m->minpop + "/" + m->maxpop + "."; @@ -751,9 +754,8 @@ void Game::EditGameRegionMarkets( ARegion *pReg ) Awrite(temp); } } - Awrite("For Sale: "); - forlist_reuse(&pReg->markets) { - Market *m = (Market *) elem; + Awrite("For Sale: "); + for (const auto &m : pReg->markets) { if (m->type == M_BUY) { AString temp = ItemString(m->item, m->amount) + " at $" + m->price + "(" + m->baseprice + ")."; temp += AString(" Pop: ") + m->minpop + "/" + m->maxpop + "."; @@ -762,7 +764,6 @@ void Game::EditGameRegionMarkets( ARegion *pReg ) } } - Awrite( "" ); Awrite( " [g] to regenerate all markets" ); @@ -790,7 +791,9 @@ void Game::EditGameRegionMarkets( ARegion *pReg ) if (*pToken == "g") { SAFE_DELETE(pToken); - pReg->markets.DeleteAll(); + for (auto& m : pReg->markets) delete m; // Free the allocated object + pReg->markets.clear(); // empty the vector. + pReg->SetupCityMarket(); pReg->UpdateEditRegion(); } @@ -826,8 +829,7 @@ void Game::EditGameRegionMarkets( ARegion *pReg ) int population = pReg->Population(); - forlist(&pReg->markets) { - Market *m = (Market *) elem; + for (auto& m : pReg->markets) { if (m->item == mitem) { m->minpop = minimum; m->maxpop = maximum; @@ -838,9 +840,9 @@ void Game::EditGameRegionMarkets( ARegion *pReg ) if (population >= m->maxpop) m->amount = m->maxamt; else { - m->amount = m->minamt + ((m->maxamt - m->minamt) * - (population - m->minpop)) / - (m->maxpop - m->minpop); + m->amount = m->minamt + + ((m->maxamt - m->minamt) * (population - m->minpop)) + / (m->maxpop - m->minpop); } } done = 1; @@ -848,11 +850,9 @@ void Game::EditGameRegionMarkets( ARegion *pReg ) } if (!done) { - int price = (ItemDefs[mitem].baseprice * (100 + getrandom(50))) / - 100; + int price = (ItemDefs[mitem].baseprice * (100 + getrandom(50))) / 100; Market *m = new Market(M_SELL, mitem, price, 0, minimum, maximum, 0, 0); - // m->PostTurn(pReg->Population(),pReg->Wages()); // updates amounts - pReg->markets.Add(m); + pReg->markets.push_back(m); } } @@ -888,8 +888,7 @@ void Game::EditGameRegionMarkets( ARegion *pReg ) int population = pReg->Population(); - forlist(&pReg->markets) { - Market *m = (Market *) elem; + for (auto& m: pReg->markets) { if (m->item == mitem) { m->minamt = minimum; m->maxamt = maximum; @@ -900,9 +899,9 @@ void Game::EditGameRegionMarkets( ARegion *pReg ) if (population >= m->maxpop) m->amount = m->maxamt; else { - m->amount = m->minamt + ((m->maxamt - m->minamt) * - (population - m->minpop)) / - (m->maxpop - m->minpop); + m->amount = m->minamt + + ((m->maxamt - m->minamt) * (population - m->minpop)) + / (m->maxpop - m->minpop); } } done = 1; @@ -910,11 +909,10 @@ void Game::EditGameRegionMarkets( ARegion *pReg ) } if (!done) { - int price = (ItemDefs[mitem].baseprice * (100 + getrandom(50))) / - 100; + int price = (ItemDefs[mitem].baseprice * (100 + getrandom(50))) / 100; int mamount = minimum + ( maximum * population / 5000 ); Market *m = new Market(M_SELL, mitem, price, mamount, 0, 5000, minimum, maximum); - pReg->markets.Add(m); + pReg->markets.push_back(m); } } @@ -947,10 +945,7 @@ void Game::EditGameRegionMarkets( ARegion *pReg ) } int done = 0; - - - forlist(&pReg->markets) { - Market *m = (Market *) elem; + for (auto& m: pReg->markets) { if (m->item == mitem) { m->price = price; m->baseprice = baseprice; @@ -961,7 +956,7 @@ void Game::EditGameRegionMarkets( ARegion *pReg ) if (!done) { Market *m = new Market(M_SELL, mitem, price, 0, 0, 5000, 0, 0); m->baseprice = baseprice; - pReg->markets.Add(m); + pReg->markets.push_back(m); } } @@ -980,21 +975,25 @@ void Game::EditGameRegionMarkets( ARegion *pReg ) } SAFE_DELETE( pToken ); - int done = 0; - forlist(&pReg->markets) { - Market *m = (Market *) elem; - if (m->item == mitem) { - if (!done) { - if (m->type == M_SELL) m->type = M_BUY; - else m->type = M_SELL; - done = 1; - } else { - pReg->markets.Remove(m); - delete m; - } - } - } - if (!done) Awrite("No such market"); + // remove all duplicate market items of the same type. + std::unordered_set s; + auto dupes = remove_if( + pReg->markets.begin(), + pReg->markets.end(), + [&s, mitem](const Market *m) { return m->item == mitem && !s.insert(m->item).second; } + ); + for_each (dupes, pReg->markets.end(), [](Market *m) mutable { delete m; }); + pReg->markets.erase(dupes, pReg->markets.end()); + auto m = find_if( + pReg->markets.begin(), + pReg->markets.end(), + [mitem](const Market *m) { return m->item == mitem; } + ); + if (m != pReg->markets.end()) { + (*m)->type = (*m)->type == M_SELL ? M_BUY : M_SELL; + } else { + Awrite("No such market"); + } } else if (*pToken == "d") { SAFE_DELETE(pToken); @@ -1011,16 +1010,22 @@ void Game::EditGameRegionMarkets( ARegion *pReg ) } SAFE_DELETE( pToken ); - int done = 0; - forlist(&pReg->markets) { - Market *m = (Market *) elem; - if (m->item == mitem) { - pReg->markets.Remove(m); - delete m; - done = 1; - } - } - if (!done) Awrite("No such market"); + auto m = find_if( + pReg->markets.begin(), + pReg->markets.end(), + [mitem](const Market *m) { return m->item == mitem; } + ); + if (m != pReg->markets.end()) { + auto dupes = remove_if( + pReg->markets.begin(), + pReg->markets.end(), + [mitem](const Market *m) { return m->item == mitem; } + ); + for_each (dupes, pReg->markets.end(), [](Market *m) mutable { delete m; }); + pReg->markets.erase(dupes, pReg->markets.end()); + } else { + Awrite("No such market"); + } } } while( 0 ); diff --git a/faction.cpp b/faction.cpp index 4c800d67..0836c582 100644 --- a/faction.cpp +++ b/faction.cpp @@ -83,53 +83,6 @@ int ParseAttitude(AString *token) return -1; } -FactionVector::FactionVector(int size) -{ - vector = new Faction *[size]; - vectorsize = size; - ClearVector(); -} - -FactionVector::~FactionVector() -{ - delete vector; -} - -void FactionVector::ClearVector() -{ - for (int i=0; i> factionnum; - f >> attitude; -} - Faction::Faction() { exists = 1; @@ -187,7 +140,7 @@ Faction::~Faction() if (name) delete name; if (address) delete address; if (password) delete password; - attitudes.DeleteAll(); + attitudes.clear(); } void Faction::Writeout(ostream& f) @@ -215,8 +168,8 @@ void Faction::Writeout(ostream& f) skills.Writeout(f); items.Writeout(f); f << defaultattitude << '\n'; - f << attitudes.Num() << '\n'; - forlist((&attitudes)) ((Attitude *) elem)->Writeout(f); + f << attitudes.size() << '\n'; + for (const auto& attitude: attitudes) f << attitude.factionnum << '\n' << attitude.attitude << '\n'; } void Faction::Readin(istream& f) @@ -254,10 +207,11 @@ void Faction::Readin(istream& f) int n; f >> n; for (int i = 0; i < n; i++) { - Attitude* a = new Attitude; - a->Readin(f); - if (a->factionnum == num) delete a; - else attitudes.Add(a); + int fnum, fattitude; + f >> fnum >> fattitude; + if (fnum == num) continue; + Attitude a = { .factionnum = fnum, .attitude = fattitude }; + attitudes.push_back(a); } } @@ -400,33 +354,28 @@ void Faction::write_text_gm_report(ostream& f, Game *game) { if(!need_header) f << '\n'; need_header = true; - for(auto &itemshow : data.items) { + 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(auto &objectshow : data.objects) { + 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.DeleteAll(); + present_regions.clear(); forlist(&(game->regions)) { ARegion *reg = (ARegion *)elem; - ARegionPtr *ptr = new ARegionPtr; - ptr->ptr = reg; - present_regions.Add(ptr); + present_regions.push_back(reg); } - { - // extra block because of foreach macro. - forlist(&present_regions) { - ((ARegionPtr*)elem)->ptr->write_text_report(f, this, game->month, &(game->regions)); - } + for (const auto& reg: present_regions) { + reg->write_text_report(f, this, game->month, &(game->regions)); } - present_regions.DeleteAll(); + present_regions.clear(); errors.clear(); events.clear(); @@ -450,24 +399,19 @@ void Faction::write_json_gm_report(json& j, Game *game) { j["item_reports"] = data.items; j["object_reports"] = data.objects; - present_regions.DeleteAll(); + present_regions.clear(); forlist(&(game->regions)) { ARegion *reg = (ARegion *)elem; - ARegionPtr *ptr = new ARegionPtr; - ptr->ptr = reg; - present_regions.Add(ptr); + present_regions.push_back(reg); } json regions = json::array(); - { - // extra block because of foreach macro. - forlist(&present_regions) { - json region; - ((ARegionPtr*)elem)->ptr->write_json_report(region, this, game->month, &(game->regions)); - regions.push_back(region); - } + for (const auto& reg: present_regions) { + json region; + reg->write_json_report(region, this, game->month, &(game->regions)); + regions.push_back(region); } j["regions"] = regions; - present_regions.DeleteAll(); + present_regions.clear(); errors.clear(); events.clear(); @@ -584,7 +528,7 @@ void Faction::write_json_report(json& j, Game *game, size_t **citems) { if (!battles.empty()) { json jbattles = json::array(); - for(auto battle: battles) { + 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); @@ -608,14 +552,13 @@ void Faction::write_json_report(json& j, Game *game, size_t **citems) { // how annoying that this is the easiest way to do this. transform(attitude.begin(), attitude.end(), attitude.begin(), ::tolower); j["attitudes"][attitude] = json::array(); // [] = json::array(); - forlist((&attitudes)) { - Attitude* a = (Attitude *) elem; - if (a->attitude == i) { + for (const auto& a: attitudes) { + if (a.attitude == i) { // Grab that faction so we can get it's number and name, and strip the " (num)" from the name for json - Faction *fac = GetFaction(&(game->factions), a->factionnum); + Faction *fac = GetFaction(&(game->factions), a.factionnum); string facname = fac->name->const_str(); facname = facname.substr(0, facname.find(" (")); - j["attitudes"][attitude].push_back({ { "name", facname }, { "number", a->factionnum } }); + j["attitudes"][attitude].push_back({ { "name", facname }, { "number", a.factionnum } }); } } // the array will be empty if this faction has declared no other factions with that specific attitude. @@ -638,13 +581,10 @@ void Faction::write_json_report(json& j, Game *game, size_t **citems) { // regions json regions = json::array(); - { - // extra block because of foreach macro. - forlist(&present_regions) { - json region; - ((ARegionPtr*)elem)->ptr->write_json_report(region, this, game->month, &(game->regions)); - regions.push_back(region); - } + for (const auto& reg: present_regions) { + json region; + reg->write_json_report(region, this, game->month, &(game->regions)); + regions.push_back(region); } j["regions"] = regions; } @@ -664,7 +604,7 @@ void Faction::write_text_report(ostream& f, Game *pGame, size_t ** citems) f << ";\n"; f << ";Item Rank Max Total\n"; f << ";=====================================================================\n"; - for(auto &stat : compute_faction_statistics(pGame, citems)) { + for (const auto& stat : compute_faction_statistics(pGame, citems)) { f << ';' << stat << '\n'; } f << '\n'; @@ -763,26 +703,26 @@ void Faction::write_text_report(ostream& f, Game *pGame, size_t ** citems) if (!errors.empty()) { f << "Errors during turn:\n"; - for(auto &error : errors) f << error << '\n'; + for (const auto& error : errors) f << error << '\n'; f << '\n'; } if (!battles.empty()) { f << "Battles during turn:\n"; - for(auto battle: battles) { + for (const auto& battle: battles) { battle->write_text_report(f, this); } } if (!events.empty()) { f << "Events during turn:\n"; - for(auto &event: events) f << event << '\n'; + for (const auto& event: events) f << event << '\n'; f << '\n'; } if (!shows.empty()) { f << "Skill reports:\n"; - for(auto &skillshow : shows) { + for (const auto& skillshow : shows) { AString *string = skillshow.Report(this); if (string) { f << '\n' << string->const_str() << '\n'; @@ -794,26 +734,25 @@ void Faction::write_text_report(ostream& f, Game *pGame, size_t ** citems) if (!itemshows.empty()) { f << "Item reports:\n"; - for(auto &itemshow : itemshows) f << '\n' << itemshow << '\n'; + for (const auto& itemshow : itemshows) f << '\n' << itemshow << '\n'; f << '\n'; } if (!objectshows.empty()) { f << "Object reports:\n"; - for(auto &objectshow : objectshows) f << '\n' << objectshow << '\n'; + for (const auto& objectshow : objectshows) f << '\n' << objectshow << '\n'; f << '\n'; } /* Attitudes */ f << "Declared Attitudes (default " << AttitudeStrs[defaultattitude] << "):\n"; - for (int i=0; iattitude == i) { + for (const auto& attitude: attitudes) { + if (attitude.attitude == i) { if (j) f << ", "; - f << GetFaction(&(pGame->factions), a->factionnum)->name->const_str(); + f << GetFaction(&(pGame->factions), attitude.factionnum)->name->const_str(); j = 1; } } @@ -824,9 +763,9 @@ void Faction::write_text_report(ostream& f, Game *pGame, size_t ** citems) f << "Unclaimed silver: " << unclaimed << ".\n\n"; - forlist(&present_regions) { - ((ARegionPtr *) elem)->ptr->write_text_report(f, this, pGame->month, &(pGame->regions)); - } + for (const auto& reg: present_regions) { + reg->write_text_report(f, this, pGame->month, &(pGame->regions)); + } f << '\n'; } @@ -857,8 +796,8 @@ void Faction::WriteTemplate(ostream& f, Game *pGame) } f << '\n'; - forlist((&present_regions)) { - ((ARegionPtr *) elem)->ptr->WriteTemplate(f, this, &(pGame->regions), pGame->month); + for (const auto& reg: present_regions) { + reg->WriteTemplate(f, this, &(pGame->regions), pGame->month); } f << "\n#end\n\n"; @@ -875,10 +814,10 @@ void Faction::WriteFacInfo(ostream &f) f << "SendTimes: " << times << '\n'; f << "Template: " << TemplateStrs[temformat] << '\n'; f << "Battle: na\n"; - forlist(&extraPlayers) { - f << ((AString *) elem)->const_str() << '\n'; + for (const auto& s: extra_player_data) { + f << s << '\n'; } - extraPlayers.DeleteAll(); + extra_player_data.clear(); } void Faction::CheckExist(ARegionList* regs) @@ -907,50 +846,34 @@ void Faction::event(const string& s) events.push_back(s); } -void Faction::RemoveAttitude(int f) -{ - forlist((&attitudes)) { - Attitude *a = (Attitude *) elem; - if (a->factionnum == f) { - attitudes.Remove(a); - delete a; - return; - } - } +void Faction::remove_attitude(int f) { + attitudes.erase( + remove_if(attitudes.begin(), attitudes.end(), [f](const Attitude& a) { return a.factionnum == f; }), + attitudes.end() + ); } -int Faction::GetAttitude(int n) +int Faction::get_attitude(int n) { if (n == num) return A_ALLY; - forlist((&attitudes)) { - Attitude *a = (Attitude *) elem; - if (a->factionnum == n) - return a->attitude; + for (const auto& attitude: attitudes) { + if (attitude.factionnum == n) return attitude.attitude; } return defaultattitude; } -void Faction::SetAttitude(int num, int att) +void Faction::set_attitude(int faction_id, int attitude) { - forlist((&attitudes)) { - Attitude *a = (Attitude *) elem; - if (a->factionnum == num) { - if (att == -1) { - attitudes.Remove(a); - delete a; - return; - } else { - a->attitude = att; - return; - } - } - } - if (att != -1) { - Attitude *a = new Attitude; - a->factionnum = num; - a->attitude = att; - attitudes.Add(a); + auto place = find_if( + attitudes.begin(), attitudes.end(), [faction_id](const Attitude& a) { return a.factionnum == faction_id; } + ); + if (place != attitudes.end()) { + place->attitude = attitude; + return; } + // we didn't find it. + Attitude a = { .factionnum = faction_id, .attitude = attitude }; + attitudes.push_back(a); } int Faction::CanCatch(ARegion *r, Unit *t) diff --git a/faction.h b/faction.h index 8fa152a1..ccd5448c 100644 --- a/faction.h +++ b/faction.h @@ -53,7 +53,8 @@ using json = nlohmann::json; using namespace std; -enum { +enum +{ A_HOSTILE, A_UNFRIENDLY, A_NEUTRAL, @@ -69,7 +70,8 @@ extern const std::string F_MARTIAL; // DK // LLS - make templates cleaner for save/restore -enum { +enum +{ TEMPLATE_OFF, TEMPLATE_SHORT, TEMPLATE_LONG, @@ -77,7 +79,8 @@ enum { NTEMPLATES }; -enum { +enum +{ QUIT_NONE, QUIT_BY_ORDER, QUIT_BY_GM, @@ -86,7 +89,7 @@ enum { QUIT_GAME_OVER, }; -extern char const ** AttitudeStrs; +extern char const **AttitudeStrs; extern std::vector *FactionTypes; // LLS - include strings for the template enum @@ -97,42 +100,27 @@ int ParseAttitude(AString *); int MagesByFacType(int); -class FactionVector { -public: - FactionVector(int); - ~FactionVector(); - - void ClearVector(); - void SetFaction(int, Faction *); - Faction *GetFaction(int); - - Faction **vector; - int vectorsize; -}; - -class Attitude : public AListElem { -public: - Attitude(); - ~Attitude(); - void Writeout(ostream& f); - void Readin(istream& f); - +struct Attitude +{ int factionnum; int attitude; }; -enum FactionActivity { - TAX = 1, - TRADE = 2 +enum FactionActivity +{ + TAX = 1, + TRADE = 2 }; -class FactionPtr : public AListElem { +class FactionPtr : public AListElem +{ public: - Faction * ptr; + Faction *ptr; }; // Collect the faction statistics for display in the report -struct FactionStatistic { +struct FactionStatistic +{ string item_name; size_t rank; size_t max; @@ -142,11 +130,13 @@ struct FactionStatistic { // objects/structs that require no additional parameters to be passed in. On the other hand, for simple // structures this is nice, and works with things like STL containers of structs to make the entire container // serializable to json. - friend void to_json(json& j, const FactionStatistic& s) { - j = json{{"item_name", s.item_name}, {"rank", s.rank }, {"max", s.max }, {"total", s.total}}; + friend void to_json(json &j, const FactionStatistic &s) + { + j = json{{"item_name", s.item_name}, {"rank", s.rank}, {"max", s.max}, {"total", s.total}}; }; - friend ostream& operator <<(ostream& os, const FactionStatistic& s) { + friend ostream &operator<<(ostream &os, const FactionStatistic &s) + { os << left << setw(42) << s.item_name << setw(6) << s.rank << setw(11) << s.max << s.total << right; return os; }; @@ -158,39 +148,38 @@ class Faction : public AListElem Faction(); Faction(int); ~Faction(); - - void Readin(istream& f); - void Writeout(ostream& f); + + void Readin(istream &f); + void Writeout(ostream &f); void View(); - + void SetName(AString *); - void SetNameNoChange( AString *str ); - void SetAddress( AString &strNewAddress ); - + void SetNameNoChange(AString *str); + void SetAddress(AString &strNewAddress); + void CheckExist(ARegionList *); - void error(const string& s); - void event(const string& s); - + 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 write_text_report(ostream &f, Game *pGame, size_t **citems); + void write_json_report(json &j, Game *pGame, size_t **citems); // LLS - write order template - void WriteTemplate(ostream& f, Game *pGame); - void WriteFacInfo(ostream& f); - - void SetAttitude(int,int); /* faction num, attitude */ - /* if attitude == -1, clear it */ - int GetAttitude(int); - void RemoveAttitude(int); - - int CanCatch(ARegion *,Unit *); + void WriteTemplate(ostream &f, Game *pGame); + void WriteFacInfo(ostream &f); + + void set_attitude(int faction_id, int attitude); // attitude -1 clears it + int get_attitude(int faction_id); + void remove_attitude(int faction_id); + + int CanCatch(ARegion *, Unit *); /* Return 1 if can see, 2 if can see faction */ - int CanSee(ARegion *,Unit *, int practice = 0); - + int CanSee(ARegion *, Unit *, int practice = 0); + void DefaultOrders(); void TimesReward(); - + bool is_npc = false; // by default factions are not NPCs void DiscoverItem(int item, int force, int full); @@ -208,9 +197,9 @@ class Faction : public AListElem int unclaimed; int bankaccount; int interest; // not written to game.out - AString * name; - AString * address; - AString * password; + AString *name; + AString *address; + AString *password; int times; int showunitattitudes; int temformat; @@ -218,7 +207,7 @@ class Faction : public AListElem char exists; int quit; int numshows; - + int nummages; int numapprentices; int numqms; @@ -234,18 +223,21 @@ class Faction : public AListElem bool gets_gm_report(Game *game); /* Used when writing reports */ - AList present_regions; - + vector present_regions; + int defaultattitude; // TODO: Convert this to a hashmap of > - AList attitudes; + // For now, just making it a vector of attitudes. More will come later. + vector attitudes; + + // These need to not be ALists wrapped with extra behavior at some point. SkillList skills; ItemList items; - - // - // Both are lists of AStrings - // - AList extraPlayers; + + // Extra lines/data from the players.in file for this faction. Stored and dumped, not used. + vector extra_player_data; + + // Errors and events during turn processing vector errors; vector events; @@ -264,14 +256,13 @@ 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); + // 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); }; -Faction * GetFaction(AList *,int); -Faction * GetFaction2(AList *,int); /*This AList is a list of FactionPtr*/ +Faction *GetFaction(AList *, int); +Faction *GetFaction2(AList *, int); /*This AList is a list of FactionPtr*/ #endif diff --git a/fracas/world.cpp b/fracas/world.cpp index 1111c59b..9d735d13 100644 --- a/fracas/world.cpp +++ b/fracas/world.cpp @@ -2518,44 +2518,43 @@ void ARegion::MakeStartingCity() town->dev = TownDevelopment(); float ratio; + + for (auto& m : markets) delete m; // Free the allocated object + markets.clear(); // empty the vector. + Market *m; - markets.DeleteAll(); if (Globals->START_CITIES_START_UNLIMITED) { for (int i=0; iBASE_MAN_COST * 10); // hack: include wage factor of 10 in float calculation above - m=new Market(M_BUY,race,(int)(Wages()*4*ratio),-1, 5000,5000,-1,-1); - markets.Add(m); + m = new Market(M_BUY,race,(int)(Wages()*4*ratio),-1, 5000,5000,-1,-1); + markets.push_back(m); if (Globals->LEADERS_EXIST) { ratio=ItemDefs[I_LEADERS].baseprice/((float)Globals->BASE_MAN_COST * 10); // hack: include wage factor of 10 in float calculation above - m = new Market(M_BUY,I_LEADERS,(int)(Wages()*4*ratio), - -1,5000,5000,-1,-1); - markets.Add(m); + m = new Market(M_BUY,I_LEADERS,(int)(Wages()*4*ratio), -1,5000,5000,-1,-1); + markets.push_back(m); } } else { SetupCityMarket(); ratio = ItemDefs[race].baseprice / ((float)Globals->BASE_MAN_COST * 10); // hack: include wage factor of 10 in float calculation above /* Setup Recruiting */ - m = new Market( M_BUY, race, (int)(Wages()*4*ratio), - Population()/5, 0, 10000, 0, 2000 ); - markets.Add(m); + m = new Market( M_BUY, race, (int)(Wages()*4*ratio), Population()/5, 0, 10000, 0, 2000 ); + markets.push_back(m); if ( Globals->LEADERS_EXIST ) { ratio=ItemDefs[I_LEADERS].baseprice/((float)Globals->BASE_MAN_COST * 10); // hack: include wage factor of 10 in float calculation above - m = new Market( M_BUY, I_LEADERS, (int)(Wages()*4*ratio), - Population()/25, 0, 10000, 0, 400 ); - markets.Add(m); + m = new Market( M_BUY, I_LEADERS, (int)(Wages()*4*ratio), Population()/25, 0, 10000, 0, 400 ); + markets.push_back(m); } } } diff --git a/game.cpp b/game.cpp index 56864882..70c4a1c7 100644 --- a/game.cpp +++ b/game.cpp @@ -291,7 +291,9 @@ int Game::NewGame() // // Seed the random number generator with a different value each time. // - seedrandomrandom(); + // init_random_seed() is a function reference that can be overwritten by the test suites to control the random seed + // so that tests are repeatable. + init_random_seed(); CreateWorld(); CreateNPCFactions(); @@ -997,9 +999,8 @@ int Game::ReadPlayersLine(AString *pToken, AString *pLine, Faction *pFac, } } } else { - pTemp = new AString(*pToken + *pLine); - pFac->extraPlayers.Add(pTemp); - pTemp = 0; + string temp = string(pToken->const_str()) + pLine->const_str(); + pFac->extra_player_data.push_back(temp); } if (pTemp) delete pTemp; @@ -1152,20 +1153,21 @@ void Game::ReadOrders() void Game::MakeFactionReportLists() { - FactionVector vector(factionseq); + vector facs (factionseq, nullptr); forlist(®ions) { - vector.ClearVector(); + // clear the temporary + fill(facs.begin(), facs.end(), nullptr); ARegion *reg = (ARegion *) elem; forlist(®->farsees) { Faction *fac = ((Farsight *) elem)->faction; - vector.SetFaction(fac->num, fac); + facs[fac->num] = fac; } { forlist(®->passers) { Faction *fac = ((Farsight *)elem)->faction; - vector.SetFaction(fac->num, fac); + facs[fac->num] = fac; } } { @@ -1174,17 +1176,13 @@ void Game::MakeFactionReportLists() forlist(&obj->units) { Unit *unit = (Unit *) elem; - vector.SetFaction(unit->faction->num, unit->faction); + facs[unit->faction->num] = unit->faction; } } } - for (int i=0; iptr = reg; - vector.GetFaction(i)->present_regions.Add(ptr); - } + for(const auto& fac: facs) { + if (fac) fac->present_regions.push_back(reg); } } } @@ -1250,7 +1248,7 @@ void Game::WriteTemplates() if (f.is_open()) { fac->WriteTemplate(f, this); } - fac->present_regions.DeleteAll(); + fac->present_regions.clear(); } Adot(); } @@ -1264,7 +1262,7 @@ void Game::DeleteDeadFactions() if (!fac->is_npc && !fac->exists) { factions.Remove(fac); forlist((&factions)) - ((Faction *) elem)->RemoveAttitude(fac->num); + ((Faction *) elem)->remove_attitude(fac->num); delete fac; } } @@ -2154,20 +2152,15 @@ int Game::CountItem (Faction * fac, int item) if (ItemDefs[item].type & IT_SHIP) return 0; size_t all = 0; - forlist (&(fac->present_regions)) - { - ARegionPtr * r = (ARegionPtr *) elem; - forlist (&r->ptr->objects) - { + for (const auto& r : fac->present_regions) { + forlist(&r->objects) { Object * obj = (Object *) elem; - forlist (&obj->units) - { - Unit * unit = (Unit *) elem; - if (unit->faction == fac) - all += unit->items.GetNum (item); + forlist(&obj->units) { + Unit * unit = (Unit *) elem; + if (unit->faction == fac) + all += unit->items.GetNum (item); } } } - return all; } diff --git a/game.h b/game.h index 05eeee35..ec8830c2 100644 --- a/game.h +++ b/game.h @@ -305,6 +305,16 @@ class Game int year; int month; + + // These hooks are meant to allow the game engine for a custom game to override some basic functions. + // For our existing case, these are going to be used by the unit test framework to ensure a consistent + // run (by overriding the seeding of the random number generator). In general, there should be *very* + // few of these hooks and they should exist for a very explicit purpose and be used with extreme care. + + // control the random number seed used for new game generation (by default it uses the existing + // seedrandomrandom function) which uses the current time. + std::function init_random_seed = seedrandomrandom; + enum { GAME_STATUS_UNINIT, diff --git a/havilah/extra.cpp b/havilah/extra.cpp index 5474b83c..07d5f888 100644 --- a/havilah/extra.cpp +++ b/havilah/extra.cpp @@ -100,7 +100,7 @@ int Game::SetupFaction( Faction *pFac ) if (Globals->LAIR_MONSTERS_EXIST || Globals->WANDERING_MONSTERS_EXIST) { // Try to auto-declare all player factions unfriendly // to Creatures, since all they do is attack you. - pFac->SetAttitude(monfaction, A_UNFRIENDLY); + pFac->set_attitude(monfaction, A_UNFRIENDLY); } return( 1 ); @@ -114,7 +114,6 @@ static void CreateQuest(ARegionList *regions, int monfaction) ARegion *r; Object *o; Unit *u; - Production *p; AString rname; map temples; map ::iterator it; @@ -194,8 +193,7 @@ static void CreateQuest(ARegionList *regions, int monfaction) continue; if (!r->visited) continue; - forlist(&r->products) { - p = (Production *) elem; + for (const auto& p : r->products) { if (p->itemtype != I_SILVER) count++; } @@ -208,8 +206,7 @@ static void CreateQuest(ARegionList *regions, int monfaction) continue; if (!r->visited) continue; - forlist(&r->products) { - p = (Production *) elem; + for (const auto& p : r->products) { if (p->itemtype != I_SILVER) { if (!count--) { q->type = Quest::HARVEST; @@ -882,7 +879,7 @@ Faction *Game::CheckVictory() message += ObjectDefs[q->building].name; message += "s in "; ucount = 0; - for(it2 = q->destinations.begin(); + for (it2 = q->destinations.begin(); it2 != q->destinations.end(); it2++) { ucount++; diff --git a/havilah/world.cpp b/havilah/world.cpp index a02be2f6..72c1f780 100644 --- a/havilah/world.cpp +++ b/havilah/world.cpp @@ -788,44 +788,42 @@ void ARegion::MakeStartingCity() town->dev = TownDevelopment(); float ratio; + for (auto& m : markets) delete m; // Free the allocated object + markets.clear(); // empty the vector. + Market *m; - markets.DeleteAll(); if (Globals->START_CITIES_START_UNLIMITED) { for (int i=0; iBASE_MAN_COST * 10); // hack: include wage factor of 10 in float calculation above - m=new Market(M_BUY,race,(int)(Wages()*4*ratio),-1, 5000,5000,-1,-1); - markets.Add(m); + m = new Market(M_BUY,race,(int)(Wages()*4*ratio),-1, 5000,5000,-1,-1); + markets.push_back(m); if (Globals->LEADERS_EXIST) { ratio=ItemDefs[I_LEADERS].baseprice/((float)Globals->BASE_MAN_COST * 10); // hack: include wage factor of 10 in float calculation above - m = new Market(M_BUY,I_LEADERS,(int)(Wages()*4*ratio), - -1,5000,5000,-1,-1); - markets.Add(m); + m = new Market(M_BUY,I_LEADERS,(int)(Wages()*4*ratio), -1,5000,5000,-1,-1); + markets.push_back(m); } } else { SetupCityMarket(); ratio = ItemDefs[race].baseprice / ((float)Globals->BASE_MAN_COST * 10); // hack: include wage factor of 10 in float calculation above /* Setup Recruiting */ - m = new Market( M_BUY, race, (int)(Wages()*4*ratio), - Population()/5, 0, 10000, 0, 2000 ); - markets.Add(m); + m = new Market( M_BUY, race, (int)(Wages()*4*ratio), Population()/5, 0, 10000, 0, 2000 ); + markets.push_back(m); if ( Globals->LEADERS_EXIST ) { - ratio=ItemDefs[I_LEADERS].baseprice/((float)Globals->BASE_MAN_COST * 10); + ratio = ItemDefs[I_LEADERS].baseprice/((float)Globals->BASE_MAN_COST * 10); // hack: include wage factor of 10 in float calculation above - m = new Market( M_BUY, I_LEADERS, (int)(Wages()*4*ratio), - Population()/25, 0, 10000, 0, 400 ); - markets.Add(m); + m = new Market( M_BUY, I_LEADERS, (int)(Wages()*4*ratio), Population()/25, 0, 10000, 0, 400 ); + markets.push_back(m); } } } diff --git a/items.cpp b/items.cpp index ed0dd80b..0bd5b919 100644 --- a/items.cpp +++ b/items.cpp @@ -136,7 +136,7 @@ ManType *FindRace(char const *abbr) int FindRaceItemIndex(int raceIndex) { auto man = ManDefs[raceIndex]; - for(int i = 0; i < NITEMS; i++) { + for (int i = 0; i < NITEMS; i++) { auto item = ItemDefs[i]; if (item.type == IT_MAN && AString(item.abr) == man.abbr) { return i; diff --git a/kingdoms/world.cpp b/kingdoms/world.cpp index f37ec864..557b4118 100644 --- a/kingdoms/world.cpp +++ b/kingdoms/world.cpp @@ -2515,44 +2515,43 @@ void ARegion::MakeStartingCity() town->dev = TownDevelopment(); float ratio; + + for (auto& m : markets) delete m; // Free the allocated object + markets.clear(); // empty the vector. + Market *m; - markets.DeleteAll(); if (Globals->START_CITIES_START_UNLIMITED) { for (int i=0; iBASE_MAN_COST * 10); // hack: include wage factor of 10 in float calculation above - m=new Market(M_BUY,race,(int)(Wages()*4*ratio),-1, 5000,5000,-1,-1); - markets.Add(m); + m = new Market(M_BUY,race,(int)(Wages()*4*ratio),-1, 5000,5000,-1,-1); + markets.push_back(m); if (Globals->LEADERS_EXIST) { ratio=ItemDefs[I_LEADERS].baseprice/((float)Globals->BASE_MAN_COST * 10); // hack: include wage factor of 10 in float calculation above - m = new Market(M_BUY,I_LEADERS,(int)(Wages()*4*ratio), - -1,5000,5000,-1,-1); - markets.Add(m); + m = new Market(M_BUY,I_LEADERS,(int)(Wages()*4*ratio), -1,5000,5000,-1,-1); + markets.push_back(m); } } else { SetupCityMarket(); ratio = ItemDefs[race].baseprice / ((float)Globals->BASE_MAN_COST * 10); // hack: include wage factor of 10 in float calculation above /* Setup Recruiting */ - m = new Market( M_BUY, race, (int)(Wages()*4*ratio), - Population()/5, 0, 10000, 0, 2000 ); - markets.Add(m); + m = new Market( M_BUY, race, (int)(Wages()*4*ratio), Population()/5, 0, 10000, 0, 2000 ); + markets.push_back(m); if ( Globals->LEADERS_EXIST ) { ratio=ItemDefs[I_LEADERS].baseprice/((float)Globals->BASE_MAN_COST * 10); // hack: include wage factor of 10 in float calculation above - m = new Market( M_BUY, I_LEADERS, (int)(Wages()*4*ratio), - Population()/25, 0, 10000, 0, 400 ); - markets.Add(m); + m = new Market( M_BUY, I_LEADERS, (int)(Wages()*4*ratio), Population()/25, 0, 10000, 0, 400 ); + markets.push_back(m); } } } diff --git a/market.cpp b/market.cpp index 41f496d4..9ddab4db 100644 --- a/market.cpp +++ b/market.cpp @@ -128,27 +128,3 @@ AString Market::Report() temp += ItemString(item, amount) + " at $" + price; return temp; } - -void MarketList::PostTurn(int population, int wages) -{ - forlist(this) { - ((Market *) elem)->PostTurn(population, wages); - } -} - -void MarketList::Writeout(ostream& f) -{ - f << Num() << '\n'; - forlist (this) ((Market *) elem)->Writeout(f); -} - -void MarketList::Readin(istream& f) -{ - int n; - f >> n;; - for (int i = 0; i < n; i++) { - Market *m = new Market; - m->Readin(f); - Add(m); - } -} diff --git a/market.h b/market.h index f5002eb9..45ba782b 100644 --- a/market.h +++ b/market.h @@ -62,8 +62,13 @@ class Market : public AListElem { AString Report(); }; -class MarketList : public AList { +class MarketList : std::vector { public: + // expose just the function on vector that we need. + using std::vector::push_back; + using std::vector::begin; + using std::vector::end; + void PostTurn(int, int); void Writeout(ostream& f); void Readin(istream& f); diff --git a/monthorders.cpp b/monthorders.cpp index a0c64a8b..28c7a9e1 100644 --- a/monthorders.cpp +++ b/monthorders.cpp @@ -440,7 +440,7 @@ void Game::Do1TeachOrder(ARegion * reg,Unit * unit) unit->Error("TEACH: No such unit."); delete id; } else { - if (target->faction->GetAttitude(unit->faction->num) < A_FRIENDLY) { + if (target->faction->get_attitude(unit->faction->num) < A_FRIENDLY) { unit->Error(AString("TEACH: ") + *(target->name) + " is not a member of a friendly faction."); order->targets.Remove(id); @@ -785,8 +785,7 @@ void Game::RunBuildHelpers(ARegion *r) continue; } // Make sure that unit considers you friendly! - if (target->faction->GetAttitude(u->faction->num) < - A_FRIENDLY) { + if (target->faction->get_attitude(u->faction->num) < A_FRIENDLY) { u->Error("BUILD: Unit you are helping rejects " "your help."); delete u->monthorders; @@ -1045,13 +1044,11 @@ void Game::RunMonthOrders() } } -void Game::RunUnitProduce(ARegion * r,Unit * u) +void Game::RunUnitProduce(ARegion *r, Unit *u) { - Production *p; ProduceOrder *o = (ProduceOrder *) u->monthorders; - forlist(&r->products) { - p = (Production *) elem; + for (const auto& p : r->products) { // PRODUCE orders for producing goods from the land // are shared among factions, and therefore handled // specially by the RunAProduction() function @@ -1219,8 +1216,7 @@ void Game::RunProduceOrders(ARegion * r) } } } - forlist_reuse(&r->products) - RunAProduction(r,(Production *) elem); + for (const auto& p : r->products) RunAProduction(r, p); } int Game::ValidProd(Unit * u,ARegion * r, Production * p) diff --git a/neworigins/extra.cpp b/neworigins/extra.cpp index b631a90a..871e6c93 100644 --- a/neworigins/extra.cpp +++ b/neworigins/extra.cpp @@ -109,7 +109,7 @@ int Game::SetupFaction( Faction *pFac ) if (Globals->LAIR_MONSTERS_EXIST || Globals->WANDERING_MONSTERS_EXIST) { // Try to auto-declare all player factions unfriendly // to Creatures, since all they do is attack you. - pFac->SetAttitude(monfaction, A_UNFRIENDLY); + pFac->set_attitude(monfaction, A_UNFRIENDLY); } return( 1 ); @@ -123,7 +123,6 @@ static void CreateQuest(ARegionList *regions, int monfaction) ARegion *r; Object *o; Unit *u; - Production *p; AString rname; map temples; map ::iterator it; @@ -247,8 +246,7 @@ static void CreateQuest(ARegionList *regions, int monfaction) // No need to check if quests do not require exploration if (!r->visited && QUEST_EXPLORATION_PERCENT != 0) continue; - forlist(&r->products) { - p = (Production *) elem; + for (const auto& p : r->products) { if (p->itemtype != I_SILVER) count++; } @@ -262,8 +260,7 @@ static void CreateQuest(ARegionList *regions, int monfaction) // No need to check if quests do not require exploration if (!r->visited && QUEST_EXPLORATION_PERCENT != 0) continue; - forlist(&r->products) { - p = (Production *) elem; + for (const auto& p : r->products) { if (p->itemtype != I_SILVER) { if (!count--) { q->type = Quest::HARVEST; @@ -690,7 +687,7 @@ Faction *Game::CheckVictory() message += ObjectDefs[q->building].name; message += "s in "; ucount = 0; - for(it2 = q->destinations.begin(); + for (it2 = q->destinations.begin(); it2 != q->destinations.end(); it2++) { ucount++; diff --git a/neworigins/world.cpp b/neworigins/world.cpp index fb389e05..c0c33070 100644 --- a/neworigins/world.cpp +++ b/neworigins/world.cpp @@ -818,44 +818,43 @@ void ARegion::MakeStartingCity() town->dev = TownDevelopment(); float ratio; + + for (auto& m : markets) delete m; // Free the allocated object + markets.clear(); // empty the vector. + Market *m; - markets.DeleteAll(); if (Globals->START_CITIES_START_UNLIMITED) { for (int i=0; iBASE_MAN_COST * 10); // hack: include wage factor of 10 in float calculation above - m=new Market(M_BUY,race,(int)(Wages()*4*ratio),-1, 5000,5000,-1,-1); - markets.Add(m); + m = new Market(M_BUY,race,(int)(Wages()*4*ratio),-1, 5000,5000,-1,-1); + markets.push_back(m); if (Globals->LEADERS_EXIST) { - ratio=ItemDefs[I_LEADERS].baseprice/((float)Globals->BASE_MAN_COST * 10); + ratio = ItemDefs[I_LEADERS].baseprice/((float)Globals->BASE_MAN_COST * 10); // hack: include wage factor of 10 in float calculation above - m = new Market(M_BUY,I_LEADERS,(int)(Wages()*4*ratio), - -1,5000,5000,-1,-1); - markets.Add(m); + m = new Market(M_BUY,I_LEADERS,(int)(Wages()*4*ratio), -1,5000,5000,-1,-1); + markets.push_back(m); } } else { SetupCityMarket(); ratio = ItemDefs[race].baseprice / ((float)Globals->BASE_MAN_COST * 10); // hack: include wage factor of 10 in float calculation above /* Setup Recruiting */ - m = new Market( M_BUY, race, (int)(Wages()*4*ratio), - Population()/5, 0, 10000, 0, 2000 ); - markets.Add(m); + m = new Market( M_BUY, race, (int)(Wages()*4*ratio), Population()/5, 0, 10000, 0, 2000 ); + markets.push_back(m); if ( Globals->LEADERS_EXIST ) { ratio=ItemDefs[I_LEADERS].baseprice/((float)Globals->BASE_MAN_COST * 10); // hack: include wage factor of 10 in float calculation above - m = new Market( M_BUY, I_LEADERS, (int)(Wages()*4*ratio), - Population()/25, 0, 10000, 0, 400 ); - markets.Add(m); + m = new Market( M_BUY, I_LEADERS, (int)(Wages()*4*ratio), Population()/25, 0, 10000, 0, 400 ); + markets.push_back(m); } } } diff --git a/object.cpp b/object.cpp index 5554b135..a34a2b65 100644 --- a/object.cpp +++ b/object.cpp @@ -279,7 +279,105 @@ Unit *Object::GetOwner() return(owner); } -void Object::Report(ostream& f, Faction *fac, int obs, int truesight, +void Object::write_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. + if ((type != O_DUMMY) && !present) { + if (IsFleet() && !(Globals->TRANSIT_REPORT & GameDefs::REPORT_SHOW_SHIPS)) return; + if (IsBuilding() && !(Globals->TRANSIT_REPORT & GameDefs::REPORT_SHOW_BUILDINGS)) return; + if (IsRoad() && !(Globals->TRANSIT_REPORT & GameDefs::REPORT_SHOW_ROADS)) return; + } + + json container = json::object(); + if (type != O_DUMMY) { + ObjectType& ob = ObjectDefs[type]; + + string s = name->const_str(); + container["name"] = s.substr(0, s.find(" [")); // remove the object number. + container["number"] = num; + container["type"] = ob.name; + if (describe) container["description"] = describe->const_str(); + + if (IsFleet()) { + if((GetOwner() && fac == GetOwner()->faction) || (obs > 9)){ + container["load"] = FleetLoad(); + container["capacity"] = FleetCapacity(); + container["sailors"] = FleetSailingSkill(1); + container["fleetsize"] = GetFleetSize(); + container["max_speed"] = GetFleetSpeed(1); + container["damage_percent"] = incomplete; + } + if ( + Globals->PREVENT_SAIL_THROUGH && !Globals->ALLOW_TRIVIAL_PORTAGE + && (flying < 1) && (TerrainDefs[region->type].similar_type != R_OCEAN) + ) { + for (int dir = 0; dir < NDIRS; dir++) { + if (SailThroughCheck(dir) == 1) container["sail_directions"][DirectionAbrs[dir]] = true; + } + } + forlist(&ships) { + Item *ship = (Item *) elem; + if (ship->type != -1 && ship->num > 0) { + ItemType item_def = ItemDefs[ship->type]; + if (item_def.flags & ItemType::DISABLED) continue; + if (!(item_def.type & IT_SHIP)) continue; + container["ships"].push_back( + { {"name", item_def.name}, {"number", ship->num}, { "plural", item_def.names } } + ); + } + } + } else { + if (incomplete) container["incomplete"] = incomplete; + container["inner_location"] = inner != -1; + if (runes) container["warding_runes"] = true; + if (!(ob.flags & ObjectType::CANENTER)) container["closed"] = true; + if (Globals->DECAY && !(ob.flags & ObjectType::NEVERDECAY) && incomplete < 1) { + if (incomplete > (0 - ob.maxMonthlyDecay)) { + container["decaying"] = true; + } else if (incomplete > (0 - ob.maxMaintenance/2)) { + container["needs_maintainence"] = true; + } + } + } + } + + //json& unit_container = (type == O_DUMMY) ? j["units"] : container["units"]; + + // Add units to container + /* + forlist ((&units)) { + Unit *u = (Unit *) elem; + int attitude = fac->get_attitude(u->faction->num); + if (u->faction == fac) { + u->WriteReport(f, -1, 1, 1, 1, attitude, fac->showunitattitudes); + } else { + if (present) { + u->WriteReport(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->WriteReport(f, passobs, passtrue, passdetfac, + type != O_DUMMY, attitude, fac->showunitattitudes); + } + } + } + } + */ + + if (type != O_DUMMY) { + j["structures"].push_back(container); + } +} + +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]; @@ -380,7 +478,7 @@ void Object::Report(ostream& f, Faction *fac, int obs, int truesight, forlist ((&units)) { Unit *u = (Unit *) elem; - int attitude = fac->GetAttitude(u->faction->num); + int attitude = fac->get_attitude(u->faction->num); if (u->faction == fac) { u->WriteReport(f, -1, 1, 1, 1, attitude, fac->showunitattitudes); } else { diff --git a/object.h b/object.h index ab64e2f9..c0c9938f 100644 --- a/object.h +++ b/object.h @@ -34,6 +34,9 @@ class Object; #include "items.h" #include +#include "external/nlohmann/json.hpp" +using json = nlohmann::json; + #define I_WOOD_OR_STONE -2 class ObjectType { @@ -88,7 +91,8 @@ class Object : public AListElem void Readin(istream& f, AList *); void Writeout(ostream& f); - void Report(ostream& f, Faction *, int, int, int, int, int, int, int); + 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 SetName(AString *); void SetDescribe(AString *); diff --git a/parseorders.cpp b/parseorders.cpp index 4b394fc6..e4ec2186 100644 --- a/parseorders.cpp +++ b/parseorders.cpp @@ -1935,7 +1935,7 @@ void Game::ProcessDeclareOrder(Faction *f, AString *o, OrdersCheck *pCheck) if (!token) { if (fac != -1) { if (!pCheck) { - f->SetAttitude(fac, -1); + f->remove_attitude(fac); } } return; @@ -1952,7 +1952,7 @@ void Game::ProcessDeclareOrder(Faction *f, AString *o, OrdersCheck *pCheck) if (fac == -1) { f->defaultattitude = att; } else { - f->SetAttitude(fac, att); + f->set_attitude(fac, att); } } } diff --git a/production.cpp b/production.cpp index c87743f2..1d9b27da 100644 --- a/production.cpp +++ b/production.cpp @@ -86,40 +86,3 @@ AString Production::WriteReport() AString temp = ItemString(itemtype, amount); return temp; } - -void ProductionList::Writeout(ostream& f) -{ - f << Num() << '\n'; - forlist(this) ((Production *) elem)->Writeout(f); -} - -void ProductionList::Readin(istream& f) -{ - int n; - f >> n; - for (int i = 0; i < n; i++) { - Production *p = new Production; - p->Readin(f); - Add(p); - } -} - -Production *ProductionList::GetProd(int t, int s) -{ - forlist(this) { - Production *p = (Production *) elem; - if (p->itemtype == t && p->skill == s) return p; - } - return 0; -} - -void ProductionList::AddProd(Production *p) -{ - Production *p2 = GetProd(p->itemtype, p->skill); - if (p2) { - Remove(p2); - delete p2; - } - - Add(p); -} diff --git a/production.h b/production.h index 17cf7174..989be9bd 100644 --- a/production.h +++ b/production.h @@ -52,13 +52,4 @@ class Production : public AListElem { int activity; }; -class ProductionList : public AList { -public: - Production * GetProd(int,int); /* item type, skill */ - void AddProd(Production *); - - void Writeout(ostream& f); - void Readin(istream& f); -}; - #endif diff --git a/runorders.cpp b/runorders.cpp index d1478928..22257636 100644 --- a/runorders.cpp +++ b/runorders.cpp @@ -286,7 +286,7 @@ void Game::Do1Assassinate(ARegion *r, Object *o, Unit *u) succ = 0; break; } - if (f->GetAttitude(tar->faction->num) == A_ALLY) { + if (f->get_attitude(tar->faction->num) == A_ALLY) { succ = 0; break; } @@ -360,7 +360,7 @@ void Game::Do1Steal(ARegion *r, Object *o, Unit *u) succ = 0; break; } - if (f->GetAttitude(tar->faction->num) == A_ALLY) { + if (f->get_attitude(tar->faction->num) == A_ALLY) { succ = 0; break; } @@ -1494,21 +1494,17 @@ void Game::RunSellOrders() { forlist((®ions)) { ARegion *r = (ARegion *) elem; - forlist((&r->markets)) { - Market *m = (Market *) elem; - if (m->type == M_SELL) - DoSell(r, m); + for (const auto& m : r->markets) { + if (m->type == M_SELL) DoSell(r, m); } - { - forlist((&r->objects)) { - Object *obj = (Object *) elem; - forlist((&obj->units)) { - Unit *u = (Unit *) elem; - forlist((&u->sellorders)) { - u->Error("SELL: Can't sell that."); - } - u->sellorders.DeleteAll(); + forlist((&r->objects)) { + Object *obj = (Object *) elem; + forlist((&obj->units)) { + Unit *u = (Unit *) elem; + forlist((&u->sellorders)) { + u->Error("SELL: Can't sell that."); } + u->sellorders.DeleteAll(); } } } @@ -1586,21 +1582,17 @@ void Game::RunBuyOrders() { forlist((®ions)) { ARegion *r = (ARegion *) elem; - forlist((&r->markets)) { - Market *m = (Market *) elem; - if (m->type == M_BUY) - DoBuy(r, m); - } - { - forlist((&r->objects)) { - Object *obj = (Object *) elem; - forlist((&obj->units)) { - Unit *u = (Unit *) elem; - forlist((&u->buyorders)) { - u->Error("BUY: Can't buy that."); - } - u->buyorders.DeleteAll(); + for (const auto& m : r->markets) { + if (m->type == M_BUY) DoBuy(r, m); + } + forlist((&r->objects)) { + Object *obj = (Object *) elem; + forlist((&obj->units)) { + Unit *u = (Unit *) elem; + forlist((&u->buyorders)) { + u->Error("BUY: Can't buy that."); } + u->buyorders.DeleteAll(); } } } @@ -2598,12 +2590,12 @@ int Game::DoGiveOrder(ARegion *r, Unit *u, GiveOrder *o) return 0; } if (!u->CanSee(r, t) && - (t->faction->GetAttitude(u->faction->num) < A_FRIENDLY)) { + (t->faction->get_attitude(u->faction->num) < A_FRIENDLY)) { u->Error(ord + ": Nonexistant target (" + o->target->Print() + ")."); return 0; } - if (t->faction->GetAttitude(u->faction->num) < A_FRIENDLY) { + if (t->faction->get_attitude(u->faction->num) < A_FRIENDLY) { u->Error(ord + ": Target is not a member of a friendly faction."); return 0; } @@ -2851,7 +2843,7 @@ int Game::DoGiveOrder(ARegion *r, Unit *u, GiveOrder *o) } // New RULE -- Must be able to see unit to give something to them! if (!u->CanSee(r, t) && - (t->faction->GetAttitude(u->faction->num) < A_FRIENDLY)) { + (t->faction->get_attitude(u->faction->num) < A_FRIENDLY)) { u->Error(ord + ": Nonexistant target (" + o->target->Print() + ")."); return 0; @@ -2880,7 +2872,7 @@ int Game::DoGiveOrder(ARegion *r, Unit *u, GiveOrder *o) } if (o->item != I_SILVER && - t->faction->GetAttitude(s->faction->num) < A_FRIENDLY) { + t->faction->get_attitude(s->faction->num) < A_FRIENDLY) { u->Error("GIVE: Target is not a member of a friendly faction."); return 0; } @@ -2944,7 +2936,7 @@ int Game::DoGiveOrder(ARegion *r, Unit *u, GiveOrder *o) } notallied = 1; - if (t->faction->GetAttitude(u->faction->num) == A_ALLY) { + if (t->faction->get_attitude(u->faction->num) == A_ALLY) { notallied = 0; } @@ -3173,8 +3165,7 @@ void Game::CheckTransportOrders() } // Make sure the target and unit are at least friendly - if (tar->unit->faction->GetAttitude(u->faction->num) < - A_FRIENDLY) { + if (tar->unit->faction->get_attitude(u->faction->num) < A_FRIENDLY) { u->Error(ordertype + ": Target " + AString(*tar->unit->name) + " is not a member of a friendly " "faction."); diff --git a/skills.h b/skills.h index 88dd8b07..89e1137e 100644 --- a/skills.h +++ b/skills.h @@ -124,7 +124,7 @@ struct ShowSkill { int skill; int level; - AString * Report(Faction *); + AString * Report(Faction *) const; }; class Skill : public AListElem { diff --git a/skillshows.cpp b/skillshows.cpp index 0bad047d..29b3ed04 100644 --- a/skillshows.cpp +++ b/skillshows.cpp @@ -84,7 +84,7 @@ static void DescribeEscapeParameters(AString *desc, int item) return; } -AString *ShowSkill::Report(Faction *f) +AString *ShowSkill::Report(Faction *f) const { if (SkillDefs[skill].flags & SkillType::DISABLED) return NULL; diff --git a/spells.cpp b/spells.cpp index 4acf3b78..e774e9a8 100644 --- a/spells.cpp +++ b/spells.cpp @@ -1538,13 +1538,9 @@ int Game::RunPhantasmalEntertainment(ARegion *r,Unit *u) if (level > r->phantasmal_entertainment) r->phantasmal_entertainment = level; - forlist((&r->products)) { - Production *p = ((Production *) elem); - if (p->itemtype == I_SILVER) { - if (p->skill == S_ENTERTAINMENT) { - max_entertainement = p->amount; - } - } + Production *p = r->get_production_for_skill(I_SILVER, S_ENTERTAINMENT); + if (p != NULL) { + max_entertainement = p->amount; } if (amt > max_entertainement) { @@ -1914,7 +1910,7 @@ int Game::RunPortalLore(ARegion *r,Object *o,Unit *u) return 0; } - if (tar->unit->faction->GetAttitude(u->faction->num) < A_FRIENDLY) { + if (tar->unit->faction->get_attitude(u->faction->num) < A_FRIENDLY) { u->Error("CAST: Target mage is not friendly."); return 0; } diff --git a/standard/extra.cpp b/standard/extra.cpp index 8a248e13..95486e8e 100644 --- a/standard/extra.cpp +++ b/standard/extra.cpp @@ -89,7 +89,7 @@ int Game::SetupFaction( Faction *pFac ) if (Globals->LAIR_MONSTERS_EXIST || Globals->WANDERING_MONSTERS_EXIST) { // Try to auto-declare all player factions unfriendly // to Creatures, since all they do is attack you. - pFac->SetAttitude(monfaction, A_UNFRIENDLY); + pFac->set_attitude(monfaction, A_UNFRIENDLY); } return( 1 ); diff --git a/standard/world.cpp b/standard/world.cpp index 36b2207f..5482cf43 100644 --- a/standard/world.cpp +++ b/standard/world.cpp @@ -2509,44 +2509,42 @@ void ARegion::MakeStartingCity() town->dev = TownDevelopment(); float ratio; + for (auto& m : markets) delete m; // Free the allocated object + markets.clear(); // empty the vector. + Market *m; - markets.DeleteAll(); if (Globals->START_CITIES_START_UNLIMITED) { for (int i=0; iBASE_MAN_COST * 10); // hack: include wage factor of 10 in float calculation above - m=new Market(M_BUY,race,(int)(Wages()*4*ratio),-1, 5000,5000,-1,-1); - markets.Add(m); + m = new Market(M_BUY,race,(int)(Wages()*4*ratio),-1, 5000,5000,-1,-1); + markets.push_back(m); if (Globals->LEADERS_EXIST) { ratio=ItemDefs[I_LEADERS].baseprice/((float)Globals->BASE_MAN_COST * 10); // hack: include wage factor of 10 in float calculation above - m = new Market(M_BUY,I_LEADERS,(int)(Wages()*4*ratio), - -1,5000,5000,-1,-1); - markets.Add(m); + m = new Market(M_BUY,I_LEADERS,(int)(Wages()*4*ratio), -1,5000,5000,-1,-1); + markets.push_back(m); } } else { SetupCityMarket(); ratio = ItemDefs[race].baseprice / ((float)Globals->BASE_MAN_COST * 10); // hack: include wage factor of 10 in float calculation above /* Setup Recruiting */ - m = new Market( M_BUY, race, (int)(Wages()*4*ratio), - Population()/5, 0, 10000, 0, 2000 ); - markets.Add(m); + m = new Market( M_BUY, race, (int)(Wages()*4*ratio), Population()/5, 0, 10000, 0, 2000 ); + markets.push_back(m); if ( Globals->LEADERS_EXIST ) { ratio=ItemDefs[I_LEADERS].baseprice/((float)Globals->BASE_MAN_COST * 10); // hack: include wage factor of 10 in float calculation above - m = new Market( M_BUY, I_LEADERS, (int)(Wages()*4*ratio), - Population()/25, 0, 10000, 0, 400 ); - markets.Add(m); + m = new Market( M_BUY, I_LEADERS, (int)(Wages()*4*ratio), Population()/25, 0, 10000, 0, 400 ); + markets.push_back(m); } } } diff --git a/template.cpp b/template.cpp index 18a1adbd..5a1dda9c 100644 --- a/template.cpp +++ b/template.cpp @@ -348,7 +348,7 @@ void ARegion::WriteTemplateHeader(ostream& f, Faction *fac, line++; // ---------------------------------------------------------------- - prod = products.GetProd(I_SILVER, S_ENTERTAINMENT); + 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); @@ -359,7 +359,7 @@ void ARegion::WriteTemplateHeader(ostream& f, Faction *fac, } // ---------------------------------------------------------------- - prod = products.GetProd(I_SILVER, -1); + 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); @@ -371,102 +371,51 @@ void ARegion::WriteTemplateHeader(ostream& f, Faction *fac, // ---------------------------------------------------------------- any = 0; - { - forlist(&markets) { - Market *m = (Market *) elem; - if (!m->amount) continue; - if (m->type == M_SELL) { - - if (ItemDefs[m->item].type & IT_ADVANCED) { - if (!HasItem(fac, m->item)) { - continue; - } - } + for (const auto& m : markets) { + if (!m->amount) continue; + if (m->type == M_SELL) { - if (!any) { - GetMapLine(buffer, line, pRegs); - temp = buffer; - temp.erase(temp.find_last_not_of(' ') + 1); - f << indent::comment << temp << '\n'; - line++; + if (ItemDefs[m->item].type & IT_ADVANCED) { + if (!HasItem(fac, m->item)) { + continue; } + } + if (!any) { 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; - { - forlist(&markets) { - Market *m = (Market *) elem; - 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); + 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; + 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; - { - forlist((&products)) { - Production *p = ((Production *) elem); - if (ItemDefs[p->itemtype].type & IT_ADVANCED) { - if (!CanMakeAdv(fac, p->itemtype)) { - continue; - } - } else { - if (p->itemtype == I_SILVER) { - continue; - } - } + for (const auto& m : markets) { + if (!m->amount) continue; + if (m->type == M_BUY) { if (!any) { GetMapLine(buffer, line, pRegs); @@ -478,15 +427,17 @@ void ARegion::WriteTemplateHeader(ostream& f, Faction *fac, GetMapLine(buffer, line, pRegs); - if (p->amount == -1) { - snprintf(data, LINE_WIDTH - MAP_WIDTH, "%s unlim %4s", - (any ? " " : "Prod"), - ItemDefs[p->itemtype].abr); + 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", - (any ? " " : "Prod"), - p->amount, - ItemDefs[p->itemtype].abr); + 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); @@ -496,6 +447,46 @@ void ARegion::WriteTemplateHeader(ostream& f, Faction *fac, } } + // ---------------------------------------------------------------- + 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) { diff --git a/unit.cpp b/unit.cpp index c5e8ff6d..eb5650b9 100644 --- a/unit.cpp +++ b/unit.cpp @@ -1944,7 +1944,7 @@ int Unit::AmtsPreventCrime(Unit *u) int Unit::GetAttitude(ARegion *r, Unit *u) { if (faction == u->faction) return A_ALLY; - int att = faction->GetAttitude(u->faction->num); + int att = faction->get_attitude(u->faction->num); if (att >= A_FRIENDLY && att >= faction->defaultattitude) return att; if (CanSee(r, u) == 2) diff --git a/unittest/json_report_test.cpp b/unittest/json_report_test.cpp index 8cb1888c..2355549d 100644 --- a/unittest/json_report_test.cpp +++ b/unittest/json_report_test.cpp @@ -37,7 +37,7 @@ ut::suite<"JSON Report"> json_report_suite = [] // pick some of the data out of the report for checking string data_name = json_report["name"]; - int data_num = json_report["number"]; + auto data_num = json_report["number"]; auto new_items = json_report["item_reports"].size(); auto new_skills = json_report["skill_reports"].size(); auto regions = json_report["regions"].size(); @@ -82,7 +82,7 @@ ut::suite<"JSON Report"> json_report_suite = [] helper.setup_turn(); Faction *faction = helper.create_faction("Test Faction"); - for(auto i = 0; i < 1003; i++) faction->error("This is error #" + to_string(i+1)); + 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); @@ -167,6 +167,92 @@ ut::suite<"JSON Report"> json_report_suite = [] string settlement_size = json_report["settlement"]["size"]; string expected_settlement_size("city"); expect(settlement_size == expected_settlement_size); + + auto wages = json_report["wages"]; + auto expected_wages = json{ {"amount", 15.2}, {"max", 1935} }; + expect(wages == expected_wages); + auto entertainment = json_report["entertainment"]; + expect(entertainment == 141_i); + + // verify the products and the markets + auto products = json_report["products"].size(); + expect(products == 1_ul); + auto expected_product = json{ {"abbr", "HORS"}, {"name", "horse"}, {"plural", "horses"}, {"amount", 21 } }; + auto first_product = json_report["products"][0]; + expect(first_product == expected_product); + + auto for_sale = json_report["markets"]["for_sale"].size(); + expect(for_sale == 4_ul); + + auto expected_sale = json{ + {"abbr", "IVOR"}, {"name", "ivory"}, {"plural", "ivory"}, {"amount", 13 }, { "price", 81 } + }; + auto first_sale = json_report["markets"]["for_sale"][0]; + expect(first_sale == expected_sale); + + auto wanted = json_report["markets"]["wanted"].size(); + expect(wanted == 9_ul); + auto expected_wanted = json{ + {"abbr", "GRAI"}, {"name", "grain"}, {"plural", "grain"}, {"amount", 51 }, { "price", 16 } + }; + auto first_wanted = json_report["markets"]["wanted"][0]; + expect(first_wanted == expected_wanted); + + auto exits = json_report["exits"].size(); + expect(exits == 3_ul); // for our 4 hex world, from this hex, it'll be se, s, and sw. + auto expected_exit_s = json{ + {"direction", "South" }, + { "region", + { + { "coordinates", { {"x", 0}, {"y", 2}, {"z", 0}, {"label", "surface"} } }, + { "province", "Testing Wilds" }, + { "terrain", "forest" } + } + } + }; + auto exit_s = json_report["exits"][1]; + expect(exit_s == expected_exit_s); + // The other 2 exits point to the same region due to wrapping. + auto exit_se_region = json_report["exits"][0]["region"]; + auto exit_sw_region = json_report["exits"][2]["region"]; + expect(exit_se_region == exit_sw_region); }; + "Region contains object data which includes correct details"_test = [] + { + UnitTestHelper helper; + helper.initialize_game(); + helper.setup_turn(); + + helper.setup_reports(); + + // Generate just this single regions json object. + Faction *faction = helper.create_faction("Test Faction"); + Faction *faction2 = helper.create_faction("Test Faction 2"); + ARegion *region = helper.get_region(0, 0, 0); + ARegionList *regions = helper.get_regions(); + Unit *leader = helper.get_first_unit(faction); + // Create a fleet in the region for the faction. **THIS MOVES THE UNIT INTO THE FLEET** + helper.create_fleet(region, leader, I_GALLEON, 3); // 3 galleons + helper.create_fleet(region, leader, I_LONGSHIP, 2); // 2 longships + + // 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); + + // Verify that owner sees additional data + auto capacity = json_report_1["structures"][0]["capacity"]; + expect(capacity == 8400_ul); + + // Verify that non-owner does not see additional data + auto capacity2 = json_report_2["structures"][0]["capacity"]; + expect(capacity2 == nullptr); + + // Verify they both see the same ships + auto ships = json_report_1["structures"][0]["ships"]; + auto ships2 = json_report_2["structures"][0]["ships"]; + expect(ships == ships2); + }; }; diff --git a/unittest/map.cpp b/unittest/map.cpp index 249fa6dd..b771f08d 100644 --- a/unittest/map.cpp +++ b/unittest/map.cpp @@ -136,11 +136,6 @@ void ARegionList::AssignTypes(ARegionArray *pArr) { ARegion *reg = pArr->GetRegion(x, y); if (!reg) continue; - // Add a town at the first loc (the plains) - if (loc == 0) { - reg->MakeStartingCity(); - } - reg->type = terrains[loc++]; reg->race = TerrainDefs[reg->type].races[0]; } diff --git a/unittest/testhelper.cpp b/unittest/testhelper.cpp index a9e9f51d..b9cfcb6e 100644 --- a/unittest/testhelper.cpp +++ b/unittest/testhelper.cpp @@ -1,7 +1,12 @@ #include "game.h" #include "testhelper.hpp" +static void seed_random() { seedrandom(0xdeadbeef); } + UnitTestHelper::UnitTestHelper() { + // install our own random seed function + game.init_random_seed = seed_random; + // Set up the output streams to capture the output. cout_streambuf = cout.rdbuf(); cout.rdbuf(cout_buffer.rdbuf()); @@ -39,6 +44,20 @@ Faction *UnitTestHelper::create_faction(string name) { return fac; } +Unit *UnitTestHelper::get_first_unit(Faction *faction) { + // This is pretty gross.. a faction should have a link to it's own units, but it doesn't. + for (size_t i = 0; i < game.maxppunits; i++) { + if (game.ppUnits[i] && game.ppUnits[i]->faction == faction) return game.ppUnits[i]; + } + return nullptr; +} + +void UnitTestHelper::create_fleet(ARegion *region, Unit *owner, int ship_type, int ship_count) { + for (auto i = 0; i < ship_count; i++) + game.CreateShip(region, owner, ship_type); + +} + ARegion *UnitTestHelper::get_region(int x, int y, int z) { ARegionArray *level = game.regions.pRegionArrays[z]; return level->GetRegion(x, y); diff --git a/unittest/testhelper.hpp b/unittest/testhelper.hpp index 7d279d37..46b2bf0c 100644 --- a/unittest/testhelper.hpp +++ b/unittest/testhelper.hpp @@ -39,6 +39,14 @@ class UnitTestHelper { ARegionList *get_regions() { return &game.regions; } // get the game month int get_month() { return game.month; } + // Find the first unit for a given faction + Unit *get_first_unit(Faction *faction); + // Create a fleet in the given region. + void create_fleet(ARegion *region, Unit *owner, int ship_type, int ship_count); + + + // dummy + int get_seed() { return getrandom(10000); }; // Get the contents of cout as a string. string cout_data(); diff --git a/unittest/unit_test_helper_test.cpp b/unittest/unit_test_helper_test.cpp index 6d9d004a..479b6f37 100644 --- a/unittest/unit_test_helper_test.cpp +++ b/unittest/unit_test_helper_test.cpp @@ -35,4 +35,17 @@ ut::suite<"UnitTestHelper"> unit_test_helper_suite = [] string expected = "Creating world\n...."; expect(current == expected); }; + + "UnitTestHelper overrides random number generation"_test = [] + { + // Create a game helper which will create a minimal game and provide us the ability to manipulate it for testing. + // We do this per-test so that we have independance between tests. + UnitTestHelper helper; + + // initialize the game which will generate a small bit of output + helper.initialize_game(); + auto seed = helper.get_seed(); + // As long as we keep the isaac rng (for now) this will always be the same. + expect(seed == 299_i); + }; }; diff --git a/unittest/world.cpp b/unittest/world.cpp index 5a801e90..e41334a9 100644 --- a/unittest/world.cpp +++ b/unittest/world.cpp @@ -38,6 +38,9 @@ void Game::CreateWorld() { regions.CreateLevels(1); // because of the way regions are numbered, if you want 4 hexes you need a height of 4 and a width of 2. regions.CreateSurfaceLevel(0, 2, 4, nullptr); + + ARegion *reg = regions.GetRegion(0,0,0); + reg->MakeStartingCity(); } int ARegionList::GetRegType( ARegion *pReg ) { return 0; } @@ -68,44 +71,42 @@ void ARegion::MakeStartingCity() { town->dev = TownDevelopment(); float ratio; + for (auto& m : markets) delete m; // Free the allocated object + markets.clear(); // empty the vector. + Market *m; - markets.DeleteAll(); if (Globals->START_CITIES_START_UNLIMITED) { for (int i=0; iBASE_MAN_COST * 10); // hack: include wage factor of 10 in float calculation above - m=new Market(M_BUY,race,(int)(Wages()*4*ratio),-1, 5000,5000,-1,-1); - markets.Add(m); + m = new Market(M_BUY,race,(int)(Wages()*4*ratio),-1, 5000,5000,-1,-1); + markets.push_back(m); if (Globals->LEADERS_EXIST) { ratio=ItemDefs[I_LEADERS].baseprice/((float)Globals->BASE_MAN_COST * 10); // hack: include wage factor of 10 in float calculation above - m = new Market(M_BUY,I_LEADERS,(int)(Wages()*4*ratio), - -1,5000,5000,-1,-1); - markets.Add(m); + m = new Market(M_BUY,I_LEADERS,(int)(Wages()*4*ratio), -1,5000,5000,-1,-1); + markets.push_back(m); } } else { SetupCityMarket(); ratio = ItemDefs[race].baseprice / ((float)Globals->BASE_MAN_COST * 10); // hack: include wage factor of 10 in float calculation above /* Setup Recruiting */ - m = new Market( M_BUY, race, (int)(Wages()*4*ratio), - Population()/5, 0, 10000, 0, 2000 ); - markets.Add(m); + m = new Market( M_BUY, race, (int)(Wages()*4*ratio), Population()/5, 0, 10000, 0, 2000 ); + markets.push_back(m); if ( Globals->LEADERS_EXIST ) { ratio=ItemDefs[I_LEADERS].baseprice/((float)Globals->BASE_MAN_COST * 10); // hack: include wage factor of 10 in float calculation above - m = new Market( M_BUY, I_LEADERS, (int)(Wages()*4*ratio), - Population()/25, 0, 10000, 0, 400 ); - markets.Add(m); + m = new Market( M_BUY, I_LEADERS, (int)(Wages()*4*ratio), Population()/25, 0, 10000, 0, 400 ); + markets.push_back(m); } } }