1 #include "stdafx.h"
2 #include "util.h"
3 #include "creature.h"
4 #include "collective_config.h"
5 #include "tribe.h"
6 #include "game.h"
7 #include "technology.h"
8 #include "collective_warning.h"
9 #include "item_type.h"
10 #include "resource_id.h"
11 #include "inventory.h"
12 #include "workshops.h"
13 #include "lasting_effect.h"
14 #include "item.h"
15 #include "view_id.h"
16 #include "furniture_type.h"
17 #include "spawn_type.h"
18 #include "minion_task.h"
19 #include "furniture_usage.h"
20 #include "creature_attributes.h"
21 #include "collective.h"
22 #include "zones.h"
23 #include "construction_map.h"
24 #include "item_class.h"
25 #include "villain_type.h"
26 #include "furniture.h"
27 #include "immigrant_info.h"
28 #include "tutorial_highlight.h"
29 #include "trap_type.h"
30 #include "spell_id.h"
31 #include "spell.h"
32 #include "creature_factory.h"
33 #include "resource_info.h"
34 #include "workshop_item.h"
35 
36 
37 template <class Archive>
serialize(Archive & ar,const unsigned int version)38 void CollectiveConfig::serialize(Archive& ar, const unsigned int version) {
39   ar(immigrantInterval, maxPopulation, populationIncreases, immigrantInfo);
40   ar(type, leaderAsFighter, spawnGhosts, ghostProb, guardianInfo, regenerateMana);
41 }
42 
43 SERIALIZABLE(CollectiveConfig);
44 SERIALIZATION_CONSTRUCTOR_IMPL(CollectiveConfig);
45 
46 template <class Archive>
serialize(Archive & ar,const unsigned int version)47 void AttractionInfo::serialize(Archive& ar, const unsigned int version) {
48   ar(types, amountClaimed);
49 }
50 
51 SERIALIZABLE(AttractionInfo);
52 SERIALIZATION_CONSTRUCTOR_IMPL(AttractionInfo);
53 
54 template <class Archive>
serialize(Archive & ar,const unsigned int version)55 void PopulationIncrease::serialize(Archive& ar, const unsigned int version) {
56   ar(type, increasePerSquare, maxIncrease);
57 }
58 
59 SERIALIZABLE(PopulationIncrease);
60 
61 template <class Archive>
serialize(Archive & ar,const unsigned int version)62 void GuardianInfo::serialize(Archive& ar, const unsigned int version) {
63   ar(creature, probability, minEnemies, minVictims);
64 }
65 
66 SERIALIZABLE(GuardianInfo);
67 
addBedRequirementToImmigrants()68 void CollectiveConfig::addBedRequirementToImmigrants() {
69   for (auto& info : immigrantInfo) {
70     PCreature c = CreatureFactory::fromId(info.getId(0), TribeId::getKeeper());
71     if (auto spawnType = c->getAttributes().getSpawnType()) {
72       AttractionType bedType = getDormInfo()[*spawnType].bedType;
73       bool hasBed = false;
74       info.visitRequirements(makeVisitor(
75           [&](const AttractionInfo& attraction) -> void {
76             for (auto& type : attraction.types)
77               if (type == bedType)
78                 hasBed = true;
79           },
80           [&](const auto&) {}
81       ));
82       if (!hasBed)
83         info.addRequirement(AttractionInfo(1, bedType));
84     }
85   }
86 }
87 
CollectiveConfig(int interval,const vector<ImmigrantInfo> & im,CollectiveType t,int maxPop,vector<PopulationIncrease> popInc)88 CollectiveConfig::CollectiveConfig(int interval, const vector<ImmigrantInfo>& im,
89     CollectiveType t, int maxPop, vector<PopulationIncrease> popInc)
90     : immigrantInterval(interval),
91     maxPopulation(maxPop), populationIncreases(popInc), immigrantInfo(im), type(t) {
92   if (type == KEEPER)
93     addBedRequirementToImmigrants();
94 }
95 
keeper(int immigrantInterval,int maxPopulation,bool regenerateMana,vector<PopulationIncrease> increases,const vector<ImmigrantInfo> & im)96 CollectiveConfig CollectiveConfig::keeper(int immigrantInterval, int maxPopulation, bool regenerateMana,
97     vector<PopulationIncrease> increases, const vector<ImmigrantInfo>& im) {
98   auto ret = CollectiveConfig(immigrantInterval, im, KEEPER, maxPopulation, increases);
99   ret.regenerateMana = regenerateMana;
100   return ret;
101 }
102 
withImmigrants(int interval,int maxPopulation,const vector<ImmigrantInfo> & im)103 CollectiveConfig CollectiveConfig::withImmigrants(int interval, int maxPopulation, const vector<ImmigrantInfo>& im) {
104   return CollectiveConfig(interval, im, VILLAGE, maxPopulation, {});
105 }
106 
noImmigrants()107 CollectiveConfig CollectiveConfig::noImmigrants() {
108   return CollectiveConfig(0, {}, VILLAGE, 10000, {});
109 }
110 
setLeaderAsFighter()111 CollectiveConfig& CollectiveConfig::setLeaderAsFighter() {
112   leaderAsFighter = true;
113   return *this;
114 }
115 
setGhostSpawns(double prob,int num)116 CollectiveConfig& CollectiveConfig::setGhostSpawns(double prob, int num) {
117   ghostProb = prob;
118   spawnGhosts = num;
119   return *this;
120 }
121 
getNumGhostSpawns() const122 int CollectiveConfig::getNumGhostSpawns() const {
123   return spawnGhosts;
124 }
125 
getImmigrantTimeout() const126 int CollectiveConfig::getImmigrantTimeout() const {
127   return 500;
128 }
129 
getGhostProb() const130 double CollectiveConfig::getGhostProb() const {
131   return ghostProb;
132 }
133 
isLeaderFighter() const134 bool CollectiveConfig::isLeaderFighter() const {
135   return leaderAsFighter;
136 }
137 
getManageEquipment() const138 bool CollectiveConfig::getManageEquipment() const {
139   return type == KEEPER;
140 }
141 
getFollowLeaderIfNoTerritory() const142 bool CollectiveConfig::getFollowLeaderIfNoTerritory() const {
143   return type == KEEPER;
144 }
145 
hasVillainSleepingTask() const146 bool CollectiveConfig::hasVillainSleepingTask() const {
147   return type != KEEPER;
148 }
149 
getRegenerateMana() const150 bool CollectiveConfig::getRegenerateMana() const {
151   return regenerateMana;
152 }
153 
hasImmigrantion(bool currentlyActiveModel) const154 bool CollectiveConfig::hasImmigrantion(bool currentlyActiveModel) const {
155   return type != KEEPER || currentlyActiveModel;
156 }
157 
getImmigrantInterval() const158 int CollectiveConfig::getImmigrantInterval() const {
159   return immigrantInterval;
160 }
161 
getStripSpawns() const162 bool CollectiveConfig::getStripSpawns() const {
163   return type == KEEPER;
164 }
165 
getFetchItems() const166 bool CollectiveConfig::getFetchItems() const {
167   return type == KEEPER;
168 }
169 
getEnemyPositions() const170 bool CollectiveConfig::getEnemyPositions() const {
171   return type == KEEPER;
172 }
173 
getWarnings() const174 bool CollectiveConfig::getWarnings() const {
175   return type == KEEPER;
176 }
177 
getConstructions() const178 bool CollectiveConfig::getConstructions() const {
179   return type == KEEPER;
180 }
181 
bedsLimitImmigration() const182 bool CollectiveConfig::bedsLimitImmigration() const {
183   return type == KEEPER;
184 }
185 
getMaxPopulation() const186 int CollectiveConfig::getMaxPopulation() const {
187   return maxPopulation;
188 }
189 
getImmigrantInfo() const190 const vector<ImmigrantInfo>& CollectiveConfig::getImmigrantInfo() const {
191   return immigrantInfo;
192 }
193 
getPopulationIncreases() const194 const vector<PopulationIncrease>& CollectiveConfig::getPopulationIncreases() const {
195   return populationIncreases;
196 }
197 
setGuardian(GuardianInfo info)198 CollectiveConfig& CollectiveConfig::setGuardian(GuardianInfo info) {
199   guardianInfo = info;
200   return *this;
201 }
202 
getGuardianInfo() const203 const optional<GuardianInfo>& CollectiveConfig::getGuardianInfo() const {
204   return guardianInfo;
205 }
206 
getDormInfo() const207 const EnumMap<SpawnType, DormInfo>& CollectiveConfig::getDormInfo() const {
208   static EnumMap<SpawnType, DormInfo> dormInfo ([](SpawnType type) -> DormInfo {
209       switch (type) {
210         case SpawnType::HUMANOID: return {FurnitureType::BED, CollectiveWarning::BEDS};
211         case SpawnType::UNDEAD: return {FurnitureType::GRAVE};
212         case SpawnType::BEAST: return {FurnitureType::BEAST_CAGE};
213         case SpawnType::DEMON: return {FurnitureType::DEMON_SHRINE};
214       }
215   });
216   return dormInfo;
217 }
218 
getRoomsNeedingLight() const219 const vector<FurnitureType>& CollectiveConfig::getRoomsNeedingLight() const {
220   static vector<FurnitureType> ret {
221     FurnitureType::WORKSHOP,
222     FurnitureType::FORGE,
223     FurnitureType::LABORATORY,
224     FurnitureType::JEWELER,
225     FurnitureType::STEEL_FURNACE,
226     FurnitureType::TRAINING_WOOD,
227     FurnitureType::TRAINING_IRON,
228     FurnitureType::TRAINING_STEEL,
229     FurnitureType::BOOKCASE_WOOD,
230     FurnitureType::BOOKCASE_IRON,
231     FurnitureType::BOOKCASE_GOLD,
232   };
233   return ret;
234 };
235 
getFloors()236 const vector<FloorInfo>& CollectiveConfig::getFloors() {
237   static vector<FloorInfo> ret = {
238     {FurnitureType::FLOOR_WOOD1, {CollectiveResourceId::WOOD, 2}, "Wooden", 0.02},
239     {FurnitureType::FLOOR_WOOD2, {CollectiveResourceId::WOOD, 2}, "Wooden", 0.02},
240     {FurnitureType::FLOOR_STONE1, {CollectiveResourceId::STONE, 2}, "Stone", 0.04},
241     {FurnitureType::FLOOR_STONE2, {CollectiveResourceId::STONE, 2}, "Stone", 0.04},
242     {FurnitureType::FLOOR_CARPET1, {CollectiveResourceId::GOLD, 2}, "Carpet", 0.06},
243     {FurnitureType::FLOOR_CARPET2, {CollectiveResourceId::GOLD, 2}, "Carpet", 0.06},
244   };
245   return ret;
246 }
247 
getEfficiencyBonus(FurnitureType type)248 double CollectiveConfig::getEfficiencyBonus(FurnitureType type) {
249   for (auto& info : getFloors())
250     if (info.type == type)
251       return info.efficiencyBonus;
252   switch (type) {
253     case FurnitureType::DUNGEON_WALL:
254       return 0.04;
255     default:
256       return 0;
257   }
258 }
259 
canBuildOutsideTerritory(FurnitureType type)260 bool CollectiveConfig::canBuildOutsideTerritory(FurnitureType type) {
261   switch (type) {
262     case FurnitureType::EYEBALL:
263     case FurnitureType::KEEPER_BOARD:
264     case FurnitureType::DUNGEON_WALL:
265     case FurnitureType::TORCH_N:
266     case FurnitureType::TORCH_E:
267     case FurnitureType::TORCH_S:
268     case FurnitureType::TORCH_W:
269     case FurnitureType::TUTORIAL_ENTRANCE:
270     case FurnitureType::BRIDGE: return true;
271     default: return false;
272   }
273 }
274 
getFurnitureStorage(FurnitureType t)275 static StorageDestinationFun getFurnitureStorage(FurnitureType t) {
276   return [t](WConstCollective col)->const set<Position>& { return col->getConstructions().getBuiltPositions(t); };
277 }
278 
getZoneStorage(ZoneId zone)279 static StorageDestinationFun getZoneStorage(ZoneId zone) {
280   return [zone](WConstCollective col)->const set<Position>& { return col->getZones().getPositions(zone); };
281 }
282 
getResourceInfo(CollectiveResourceId id)283 const ResourceInfo& CollectiveConfig::getResourceInfo(CollectiveResourceId id) {
284   static EnumMap<CollectiveResourceId, ResourceInfo> resourceInfo([](CollectiveResourceId id)->ResourceInfo {
285     switch (id) {
286       case CollectiveResourceId::MANA:
287         return { nullptr, none, ItemType::GoldPiece{}, "mana", ViewId::MANA};
288       case CollectiveResourceId::PRISONER_HEAD:
289         return { nullptr, none, ItemType::GoldPiece{}, "", ViewId::IMPALED_HEAD, true};
290       case CollectiveResourceId::GOLD:
291         return {getFurnitureStorage(FurnitureType::TREASURE_CHEST), ItemIndex::GOLD, ItemType::GoldPiece{}, "gold", ViewId::GOLD};
292       case CollectiveResourceId::WOOD:
293         return { getZoneStorage(ZoneId::STORAGE_RESOURCES), ItemIndex::WOOD, ItemType::WoodPlank{}, "wood", ViewId::WOOD_PLANK,
294             false, TutorialHighlight::WOOD_RESOURCE};
295       case CollectiveResourceId::IRON:
296         return { getZoneStorage(ZoneId::STORAGE_RESOURCES), ItemIndex::IRON, ItemType::IronOre{}, "iron", ViewId::IRON_ROCK};
297       case CollectiveResourceId::STEEL:
298         return { getZoneStorage(ZoneId::STORAGE_RESOURCES), ItemIndex::STEEL, ItemType::SteelIngot{}, "steel", ViewId::STEEL_INGOT};
299       case CollectiveResourceId::STONE:
300         return { getZoneStorage(ZoneId::STORAGE_RESOURCES), ItemIndex::STONE, ItemType::Rock{}, "granite", ViewId::ROCK};
301       case CollectiveResourceId::CORPSE:
302         return { getFurnitureStorage(FurnitureType::GRAVE), ItemIndex::REVIVABLE_CORPSE, ItemType::GoldPiece{}, "corpses", ViewId::BODY_PART, true};
303     }
304   });
305   return resourceInfo[id];
306 }
307 
unMarkedItems()308 static CollectiveItemPredicate unMarkedItems() {
309   return [](WConstCollective col, WConstItem it) { return !col->isItemMarked(it); };
310 }
311 
312 
getFetchInfo()313 const vector<ItemFetchInfo>& CollectiveConfig::getFetchInfo() {
314   static vector<ItemFetchInfo> ret {
315       {ItemIndex::CORPSE, unMarkedItems(), getFurnitureStorage(FurnitureType::GRAVE), true, CollectiveWarning::GRAVES},
316       {ItemIndex::GOLD, unMarkedItems(), getFurnitureStorage(FurnitureType::TREASURE_CHEST), false,
317           CollectiveWarning::CHESTS},
318       {ItemIndex::MINION_EQUIPMENT, [](WConstCollective col, WConstItem it)
319           { return it->getClass() != ItemClass::GOLD && !col->isItemMarked(it);},
320           getZoneStorage(ZoneId::STORAGE_EQUIPMENT), false, CollectiveWarning::EQUIPMENT_STORAGE},
321       {ItemIndex::WOOD, unMarkedItems(), getZoneStorage(ZoneId::STORAGE_RESOURCES), false,
322           CollectiveWarning::RESOURCE_STORAGE},
323       {ItemIndex::IRON, unMarkedItems(), getZoneStorage(ZoneId::STORAGE_RESOURCES), false,
324           CollectiveWarning::RESOURCE_STORAGE},
325       {ItemIndex::STEEL, unMarkedItems(), getZoneStorage(ZoneId::STORAGE_RESOURCES), false,
326           CollectiveWarning::RESOURCE_STORAGE},
327       {ItemIndex::STONE, unMarkedItems(), getZoneStorage(ZoneId::STORAGE_RESOURCES), false,
328           CollectiveWarning::RESOURCE_STORAGE},
329   };
330   return ret;
331 }
332 
MinionTaskInfo(Type t,const string & desc,optional<CollectiveWarning> w)333 MinionTaskInfo::MinionTaskInfo(Type t, const string& desc, optional<CollectiveWarning> w)
334     : type(t), description(desc), warning(w) {
335   CHECK(type != FURNITURE);
336 }
337 
MinionTaskInfo()338 MinionTaskInfo::MinionTaskInfo() {}
339 
MinionTaskInfo(FurnitureType type,const string & desc)340 MinionTaskInfo::MinionTaskInfo(FurnitureType type, const string& desc) : type(FURNITURE),
341   furniturePredicate([type](WConstCollective, WConstCreature, FurnitureType t) { return t == type;}),
342     description(desc) {
343 }
344 
MinionTaskInfo(UsagePredicate pred,const string & desc)345 MinionTaskInfo::MinionTaskInfo(UsagePredicate pred, const string& desc) : type(FURNITURE),
346   furniturePredicate(pred), description(desc) {
347 }
348 
MinionTaskInfo(UsagePredicate usagePred,ActivePredicate activePred,const string & desc)349 MinionTaskInfo::MinionTaskInfo(UsagePredicate usagePred, ActivePredicate activePred, const string& desc) :
350     type(FURNITURE), furniturePredicate(usagePred), activePredicate(activePred), description(desc) {
351 }
352 
__anonad4a5b400a02(WorkshopType type)353 static EnumMap<WorkshopType, WorkshopInfo> workshops([](WorkshopType type)->WorkshopInfo {
354   switch (type) {
355     case WorkshopType::WORKSHOP: return {FurnitureType::WORKSHOP, "workshop", SkillId::WORKSHOP};
356     case WorkshopType::FORGE: return {FurnitureType::FORGE, "forge", SkillId::FORGE};
357     case WorkshopType::LABORATORY: return {FurnitureType::LABORATORY, "laboratory", SkillId::LABORATORY};
358     case WorkshopType::JEWELER: return {FurnitureType::JEWELER, "jeweler", SkillId::JEWELER};
359     case WorkshopType::STEEL_FURNACE: return {FurnitureType::STEEL_FURNACE, "steel furnace", SkillId::FURNACE};
360   }});
361 
getWorkshopType(FurnitureType furniture)362 optional<WorkshopType> CollectiveConfig::getWorkshopType(FurnitureType furniture) {
363   static optional<EnumMap<FurnitureType, optional<WorkshopType>>> map;
364   if (!map) {
365     map.emplace();
366     for (auto type : ENUM_ALL(WorkshopType))
367       (*map)[workshops[type].furniture] = type;
368   }
369   return (*map)[furniture];
370 }
371 
getStartingResource() const372 map<CollectiveResourceId, int> CollectiveConfig::getStartingResource() const {
373   return map<CollectiveResourceId, int>{};
374 }
375 
getTrainingFurniture(ExperienceType type)376 const vector<FurnitureType>& CollectiveConfig::getTrainingFurniture(ExperienceType type) {
377   static EnumMap<ExperienceType, vector<FurnitureType>> ret(
378       [](ExperienceType expType) {
379         vector<FurnitureType> furniture;
380         for (auto type : ENUM_ALL(FurnitureType))
381           if (!!getTrainingMaxLevel(expType, type))
382             furniture.push_back(type);
383         return furniture;
384       });
385   return ret[type];
386 }
387 
getTrainingMaxLevel(ExperienceType experienceType,FurnitureType type)388 optional<int> CollectiveConfig::getTrainingMaxLevel(ExperienceType experienceType, FurnitureType type) {
389   switch (experienceType) {
390     case ExperienceType::MELEE:
391       switch (type) {
392         case FurnitureType::TRAINING_WOOD:
393           return 3;
394         case FurnitureType::TRAINING_IRON:
395           return 7;
396         case FurnitureType::TRAINING_STEEL:
397           return 12;
398         default:
399           return none;
400       }
401       break;
402     case ExperienceType::SPELL:
403       switch (type) {
404         case FurnitureType::BOOKCASE_WOOD:
405           return 3;
406         case FurnitureType::BOOKCASE_IRON:
407           return 7;
408         case FurnitureType::BOOKCASE_GOLD:
409           return 12;
410         default:
411           return none;
412       }
413       break;
414     default:
415       return none;
416   }
417 }
418 
getManaForConquering(const optional<VillainType> & type)419 int CollectiveConfig::getManaForConquering(const optional<VillainType>& type) {
420   if (type)
421     switch (*type) {
422       case VillainType::MAIN:
423         return 200;
424       case VillainType::LESSER:
425         return 100;
426       default:
427         break;;
428     }
429   return 50;
430 }
431 
432 CollectiveConfig::CollectiveConfig(const CollectiveConfig&) = default;
433 
~CollectiveConfig()434 CollectiveConfig::~CollectiveConfig() {
435 }
436 
getTrainingPredicate(ExperienceType experienceType)437 static auto getTrainingPredicate(ExperienceType experienceType) {
438   return [experienceType] (WConstCollective, WConstCreature c, FurnitureType t) {
439       if (auto maxIncrease = CollectiveConfig::getTrainingMaxLevel(experienceType, t))
440         return !c || (c->getAttributes().getExpLevel(experienceType) < *maxIncrease &&
441             !c->getAttributes().isTrainingMaxedOut(experienceType));
442       else
443         return false;
444     };
445 }
446 
447 template <typename Pred>
addManaGenerationPredicate(Pred p)448 static auto addManaGenerationPredicate(Pred p) {
449   return [p] (WConstCollective col, WConstCreature c, FurnitureType t) {
450     return (!!CollectiveConfig::getTrainingMaxLevel(ExperienceType::SPELL, t) &&
451             (!col || col->getConfig().getRegenerateMana()))
452         || p(col, c, t);
453   };
454 }
455 
getTaskInfo(MinionTask task)456 const MinionTaskInfo& CollectiveConfig::getTaskInfo(MinionTask task) {
457   static EnumMap<MinionTask, MinionTaskInfo> map([](MinionTask task) -> MinionTaskInfo {
458     switch (task) {
459       case MinionTask::WORKER: return {MinionTaskInfo::WORKER, "working"};
460       case MinionTask::TRAIN: return {getTrainingPredicate(ExperienceType::MELEE), "training"};
461       case MinionTask::SLEEP: return {FurnitureType::BED, "sleeping"};
462       case MinionTask::EAT: return {MinionTaskInfo::EAT, "eating"};
463       case MinionTask::GRAVE: return {FurnitureType::GRAVE, "sleeping"};
464       case MinionTask::LAIR: return {FurnitureType::BEAST_CAGE, "sleeping"};
465       case MinionTask::THRONE: return {FurnitureType::THRONE, "throne"};
466       case MinionTask::STUDY: return {addManaGenerationPredicate(getTrainingPredicate(ExperienceType::SPELL)),
467            "studying"};
468       case MinionTask::PRISON: return {FurnitureType::PRISON, "prison"};
469       case MinionTask::CROPS: return {FurnitureType::CROPS, "crops"};
470       case MinionTask::RITUAL: return {FurnitureType::DEMON_SHRINE, "rituals"};
471       case MinionTask::ARCHERY: return {MinionTaskInfo::ARCHERY, "archery range"};
472       case MinionTask::COPULATE: return {MinionTaskInfo::COPULATE, "copulation"};
473       case MinionTask::EXPLORE: return {MinionTaskInfo::EXPLORE, "spying"};
474       case MinionTask::SPIDER: return {MinionTaskInfo::SPIDER, "spider"};
475       case MinionTask::EXPLORE_NOCTURNAL: return {MinionTaskInfo::EXPLORE, "spying"};
476       case MinionTask::EXPLORE_CAVES: return {MinionTaskInfo::EXPLORE, "spying"};
477       case MinionTask::BE_WHIPPED: return {FurnitureType::WHIPPING_POST, "being whipped"};
478       case MinionTask::BE_TORTURED: return {FurnitureType::TORTURE_TABLE, "being tortured"};
479       case MinionTask::BE_EXECUTED: return {FurnitureType::GALLOWS, "being executed"};
480       case MinionTask::CRAFT: return {[](WConstCollective, WConstCreature c, FurnitureType t) {
481             if (auto type = getWorkshopType(t))
482               return !c || c->getAttributes().getSkills().getValue(getWorkshopInfo(*type).skill) > 0;
483             else
484               return false;
485           },
486           [](WConstCollective collective, FurnitureType t) {
487             if (auto type = getWorkshopType(t))
488               return !collective->getWorkshops().get(*type).isIdle();
489             else
490               return false;
491           },
492           "crafting"};
493     }
494   });
495   return map[task];
496 }
497 
getWorkshopInfo(WorkshopType type)498 const WorkshopInfo& CollectiveConfig::getWorkshopInfo(WorkshopType type) {
499   return workshops[type];
500 }
501 
502 
getWorkshops() const503 unique_ptr<Workshops> CollectiveConfig::getWorkshops() const {
504   return unique_ptr<Workshops>(new Workshops({
505       {WorkshopType::WORKSHOP, {
506           Workshops::Item::fromType(ItemType::LeatherArmor{}, 6, {CollectiveResourceId::WOOD, 20}),
507           Workshops::Item::fromType(ItemType::LeatherHelm{}, 1, {CollectiveResourceId::WOOD, 6}),
508           Workshops::Item::fromType(ItemType::LeatherBoots{}, 2, {CollectiveResourceId::WOOD, 10}),
509           Workshops::Item::fromType(ItemType::LeatherGloves{}, 1, {CollectiveResourceId::WOOD, 2}),
510           Workshops::Item::fromType(ItemType::Club{}, 3, {CollectiveResourceId::WOOD, 10})
511               .setTutorialHighlight(TutorialHighlight::SCHEDULE_CLUB),
512           Workshops::Item::fromType(ItemType::HeavyClub{}, 5, {CollectiveResourceId::WOOD, 20})
513                   .setTechId(TechId::TWO_H_WEAP),
514           Workshops::Item::fromType(ItemType::Bow{}, 13, {CollectiveResourceId::WOOD, 20}).setTechId(TechId::ARCHERY),
515           Workshops::Item::fromType(ItemType::WoodenStaff{}, 13, {CollectiveResourceId::WOOD, 20})
516                   .setTechId(TechId::MAGICAL_WEAPONS),
517           Workshops::Item::fromType(ItemType::TrapItem{TrapType::BOULDER}, 20, {CollectiveResourceId::STONE, 50})
518                   .setTechId(TechId::TRAPS),
519           Workshops::Item::fromType(ItemType::TrapItem{TrapType::POISON_GAS}, 10, {CollectiveResourceId::WOOD, 20})
520                   .setTechId(TechId::TRAPS),
521           Workshops::Item::fromType(ItemType::TrapItem{TrapType::ALARM}, 8, {CollectiveResourceId::WOOD, 20})
522                   .setTechId(TechId::TRAPS),
523           Workshops::Item::fromType(ItemType::TrapItem{TrapType::WEB}, 8, {CollectiveResourceId::WOOD, 20})
524                   .setTechId(TechId::TRAPS),
525           Workshops::Item::fromType(ItemType::TrapItem{TrapType::SURPRISE}, 8, {CollectiveResourceId::WOOD, 20})
526                   .setTechId(TechId::TRAPS),
527           Workshops::Item::fromType(ItemType::TrapItem{TrapType::TERROR}, 8, {CollectiveResourceId::WOOD, 20})
528                   .setTechId(TechId::TRAPS),
529       }},
530       {WorkshopType::FORGE, {
531           Workshops::Item::fromType(ItemType::Sword{}, 10, {CollectiveResourceId::IRON, 20}),
532           Workshops::Item::fromType(ItemType::SteelSword{}, 20, {CollectiveResourceId::STEEL, 20})
533                   .setTechId(TechId::STEEL_MAKING),
534           Workshops::Item::fromType(ItemType::ChainArmor{}, 30, {CollectiveResourceId::IRON, 40}),
535           Workshops::Item::fromType(ItemType::SteelArmor{}, 30, {CollectiveResourceId::STEEL, 40})
536                   .setTechId(TechId::STEEL_MAKING),
537           Workshops::Item::fromType(ItemType::IronHelm{}, 8, {CollectiveResourceId::IRON, 16}),
538           Workshops::Item::fromType(ItemType::IronBoots{}, 12, {CollectiveResourceId::IRON, 24}),
539           Workshops::Item::fromType(ItemType::WarHammer{}, 16, {CollectiveResourceId::IRON, 40})
540                   .setTechId(TechId::TWO_H_WEAP),
541           Workshops::Item::fromType(ItemType::BattleAxe{}, 22, {CollectiveResourceId::IRON, 50})
542                   .setTechId(TechId::TWO_H_WEAP),
543           Workshops::Item::fromType(ItemType::SteelBattleAxe{}, 22, {CollectiveResourceId::STEEL, 50})
544                   .setTechId(TechId::STEEL_MAKING),
545          Workshops::Item::fromType(ItemType::IronStaff{}, 20, {CollectiveResourceId::IRON, 40})
546                  .setTechId(TechId::MAGICAL_WEAPONS),
547       }},
548       {WorkshopType::LABORATORY, {
549           Workshops::Item::fromType(ItemType::Potion{Effect::Lasting{LastingEffect::SLOWED}}, 2,
550               {CollectiveResourceId::GOLD, 2}),
551           Workshops::Item::fromType(ItemType::Potion{Effect::Lasting{LastingEffect::SLEEP}}, 2,
552               {CollectiveResourceId::GOLD, 2}),
553           Workshops::Item::fromType(ItemType::Potion{
554               Effect::Lasting{LastingEffect::POISON_RESISTANT}}, 4, {CollectiveResourceId::GOLD, 6}),
555           Workshops::Item::fromType(ItemType::Potion{
556               Effect::Lasting{LastingEffect::SPEED}}, 4, {CollectiveResourceId::GOLD, 6}),
557           Workshops::Item::fromType(ItemType::Potion{
558               Effect::Lasting{LastingEffect::TELEPATHY}}, 4, {CollectiveResourceId::GOLD, 6}),
559           Workshops::Item::fromType(ItemType::Potion{
560               Effect::Lasting{LastingEffect::REGENERATION}}, 4, {CollectiveResourceId::GOLD, 8}),
561           Workshops::Item::fromType(ItemType::Potion{
562               Effect::Lasting{LastingEffect::POISON}}, 4, {CollectiveResourceId::GOLD, 8}),
563           Workshops::Item::fromType(ItemType::Potion{
564               Effect::Lasting{LastingEffect::FLYING}}, 4, {CollectiveResourceId::GOLD, 8}),
565           Workshops::Item::fromType(ItemType::Potion{Effect::Heal{}}, 4, {CollectiveResourceId::GOLD, 10})
566              .setTechId(TechId::ALCHEMY_ADV),
567           Workshops::Item::fromType(ItemType::Potion{
568               Effect::Lasting{LastingEffect::BLIND}}, 4, {CollectiveResourceId::GOLD, 15})
569                   .setTechId(TechId::ALCHEMY_ADV),
570           Workshops::Item::fromType(ItemType::Potion{
571               Effect::Lasting{LastingEffect::MELEE_RESISTANCE}}, 6, {CollectiveResourceId::GOLD, 20})
572                   .setTechId(TechId::ALCHEMY_ADV),
573           Workshops::Item::fromType(ItemType::Potion{
574               Effect::Lasting{LastingEffect::INVISIBLE}}, 6, {CollectiveResourceId::GOLD, 20})
575                   .setTechId(TechId::ALCHEMY_ADV),
576           //Alchemical conversion to and from gold
577           Workshops::Item::fromType(ItemType::GoldPiece{}, 5, {CollectiveResourceId::IRON, 30})
578               .setTechId(TechId::ALCHEMY_CONV).setBatchSize(10),
579           Workshops::Item::fromType(ItemType::WoodPlank{}, 5, {CollectiveResourceId::GOLD, 10})
580               .setTechId(TechId::ALCHEMY_CONV).setBatchSize(10),
581           Workshops::Item::fromType(ItemType::IronOre{}, 5, {CollectiveResourceId::GOLD, 10})
582               .setTechId(TechId::ALCHEMY_CONV).setBatchSize(10),
583          Workshops::Item::fromType(ItemType::Rock{}, 5, {CollectiveResourceId::GOLD, 10})
584              .setTechId(TechId::ALCHEMY_CONV).setBatchSize(10),
585       }},
586       {WorkshopType::JEWELER, {
587           Workshops::Item::fromType(ItemType::Ring{LastingEffect::POISON_RESISTANT}, 10,
588               {CollectiveResourceId::GOLD, 20}),
589           Workshops::Item::fromType(ItemType::Ring{LastingEffect::FIRE_RESISTANT}, 10,
590               {CollectiveResourceId::GOLD, 30}),
591           Workshops::Item::fromType(ItemType::Ring{LastingEffect::MAGIC_RESISTANCE}, 10,
592               {CollectiveResourceId::GOLD, 30}),
593           Workshops::Item::fromType(ItemType::Ring{LastingEffect::RESTED}, 10, {CollectiveResourceId::GOLD, 30}),
594           Workshops::Item::fromType(ItemType::Ring{LastingEffect::SATIATED}, 10, {CollectiveResourceId::GOLD, 30}),
595           Workshops::Item::fromType(ItemType::Amulet{LastingEffect::NIGHT_VISION}, 10, {CollectiveResourceId::GOLD, 20}),
596           Workshops::Item::fromType(ItemType::Amulet{LastingEffect::ELF_VISION}, 10, {CollectiveResourceId::GOLD, 20}),
597           Workshops::Item::fromType(ItemType::Amulet{LastingEffect::WARNING}, 10, {CollectiveResourceId::GOLD, 30}),
598           Workshops::Item::fromType(ItemType::DefenseAmulet{}, 10, {CollectiveResourceId::GOLD, 40}),
599           Workshops::Item::fromType(ItemType::Amulet{LastingEffect::REGENERATION}, 10, {CollectiveResourceId::GOLD, 60}),
600       }},
601       {WorkshopType::STEEL_FURNACE, {
602           Workshops::Item::fromType(ItemType::SteelIngot{}, 25, {CollectiveResourceId::IRON, 30}).setBatchSize(10),
603       }},
604   }));
605 }
606 
getInitialTech() const607 vector<Technology*> CollectiveConfig::getInitialTech() const {
608   if (type == KEEPER)
609     return {
610       Technology::get(TechId::GEOLOGY1),
611       Technology::get(TechId::SPELLS),
612     };
613   else
614     return {};
615 }
616