1 #include "stdafx.h"
2 #include "campaign_builder.h"
3 #include "options.h"
4 #include "campaign_type.h"
5 #include "player_role.h"
6 #include "view.h"
7 #include "enemy_factory.h"
8 #include "villain_type.h"
9 #include "creature.h"
10 #include "creature_name.h"
11 #include "retired_games.h"
12 #include "view_object.h"
13 #include "name_generator.h"
14 #include "creature_factory.h"
15
16
considerStaticPlayerPos(const Campaign & campaign)17 optional<Vec2> CampaignBuilder::considerStaticPlayerPos(const Campaign& campaign) {
18 switch (campaign.type) {
19 case CampaignType::CAMPAIGN:
20 case CampaignType::QUICK_MAP:
21 case CampaignType::SINGLE_KEEPER:
22 return campaign.sites.getBounds().middle();
23 default:
24 return none;
25 }
26 }
27
setCountLimits(Options * options)28 static void setCountLimits(Options* options) {
29 #ifdef RELEASE
30 options->setLimits(OptionId::MAIN_VILLAINS, 1, 4);
31 #else
32 options->setLimits(OptionId::MAIN_VILLAINS, 0, 4);
33 #endif
34 options->setLimits(OptionId::LESSER_VILLAINS, 0, 6);
35 options->setLimits(OptionId::ALLIES, 0, 4);
36 options->setLimits(OptionId::INFLUENCE_SIZE, 3, 6);
37 }
38
getSecondaryOptions(CampaignType type) const39 vector<OptionId> CampaignBuilder::getSecondaryOptions(CampaignType type) const {
40 switch (type) {
41 case CampaignType::QUICK_MAP:
42 case CampaignType::CAMPAIGN:
43 return {};
44 case CampaignType::ENDLESS:
45 return {OptionId::LESSER_VILLAINS, OptionId::ALLIES};
46 case CampaignType::FREE_PLAY:
47 if (playerRole == PlayerRole::KEEPER)
48 return {OptionId::MAIN_VILLAINS, OptionId::LESSER_VILLAINS, OptionId::ALLIES, OptionId::GENERATE_MANA};
49 else
50 return {OptionId::MAIN_VILLAINS, OptionId::LESSER_VILLAINS, OptionId::ALLIES};
51 case CampaignType::SINGLE_KEEPER:
52 return {};
53 }
54 }
55
getPlayerNameOptionId() const56 OptionId CampaignBuilder::getPlayerNameOptionId() const {
57 switch (playerRole) {
58 case PlayerRole::KEEPER:
59 return OptionId::KEEPER_NAME;
60 case PlayerRole::ADVENTURER:
61 return OptionId::ADVENTURER_NAME;
62 }
63 }
64
getPlayerTypeOptionId() const65 OptionId CampaignBuilder::getPlayerTypeOptionId() const {
66 switch (playerRole) {
67 case PlayerRole::KEEPER:
68 return OptionId::KEEPER_TYPE;
69 case PlayerRole::ADVENTURER:
70 return OptionId::ADVENTURER_TYPE;
71 }
72 }
73
getPlayerTribeId() const74 TribeId CampaignBuilder::getPlayerTribeId() const {
75 switch (playerRole) {
76 case PlayerRole::KEEPER:
77 return TribeId::getKeeper();
78 case PlayerRole::ADVENTURER:
79 return TribeId::getAdventurer();
80 }
81 }
82
getPrimaryOptions() const83 vector<OptionId> CampaignBuilder::getPrimaryOptions() const {
84 return {getPlayerTypeOptionId(), getPlayerNameOptionId()};
85 }
86
getCampaignTypeDescription(CampaignType type)87 static vector<string> getCampaignTypeDescription(CampaignType type) {
88 switch (type) {
89 case CampaignType::CAMPAIGN:
90 return {
91 "the main competitive mode"
92 };
93 case CampaignType::FREE_PLAY:
94 return {
95 "retired dungeons of other players",
96 "custom starting point and villains",
97 "highscores not recorded",
98 };
99 case CampaignType::SINGLE_KEEPER:
100 return {
101 "everyone on one big map",
102 "retiring not possible"
103 };
104 case CampaignType::ENDLESS:
105 return {
106 "conquest not mandatory",
107 "recurring enemy waves",
108 "survive as long as possible"
109 };
110 default:
111 return {};
112 }
113 }
114
getAvailableTypes() const115 vector<CampaignType> CampaignBuilder::getAvailableTypes() const {
116 switch (playerRole) {
117 case PlayerRole::KEEPER:
118 return {
119 CampaignType::CAMPAIGN,
120 CampaignType::FREE_PLAY,
121 CampaignType::SINGLE_KEEPER,
122 CampaignType::ENDLESS,
123 #ifndef RELEASE
124 CampaignType::QUICK_MAP,
125 #endif
126 };
127 case PlayerRole::ADVENTURER:
128 return {
129 CampaignType::CAMPAIGN,
130 CampaignType::FREE_PLAY,
131 };
132 }
133 }
134
getSiteChoiceTitle(CampaignType type) const135 optional<string> CampaignBuilder::getSiteChoiceTitle(CampaignType type) const {
136 switch (type) {
137 case CampaignType::FREE_PLAY:
138 case CampaignType::ENDLESS:
139 switch (playerRole) {
140 case PlayerRole::KEEPER: return "Choose the location of your base:"_s;
141 case PlayerRole::ADVENTURER: return "Choose a location to start your adventure:"_s;
142 }
143 break;
144 default:
145 return none;
146 }
147 }
148
getMainVillains()149 vector<Campaign::VillainInfo> CampaignBuilder::getMainVillains() {
150 switch (playerRole) {
151 case PlayerRole::KEEPER:
152 return {
153 {ViewId::DUKE, EnemyId::KNIGHTS, "Knights", VillainType::MAIN},
154 {ViewId::ELF_LORD, EnemyId::ELVES, "Elves", VillainType::MAIN},
155 {ViewId::DWARF_BARON, EnemyId::DWARVES, "Dwarves", VillainType::MAIN},
156 {ViewId::RED_DRAGON, EnemyId::RED_DRAGON, "Red dragon", VillainType::MAIN},
157 {ViewId::ELEMENTALIST, EnemyId::ELEMENTALIST, "Elementalist", VillainType::MAIN},
158 {ViewId::GREEN_DRAGON, EnemyId::GREEN_DRAGON, "Green dragon", VillainType::MAIN},
159 {ViewId::LIZARDLORD, EnemyId::LIZARDMEN, "Lizardmen", VillainType::MAIN},
160 {ViewId::SHAMAN, EnemyId::WARRIORS, "Warriors", VillainType::MAIN},
161 {ViewId::DEMON_LORD, EnemyId::DEMON_DEN, "Demon den", VillainType::MAIN},
162 };
163 case PlayerRole::ADVENTURER:
164 return {
165 {ViewId::RED_DRAGON, EnemyId::RED_DRAGON, "Red dragon", VillainType::MAIN},
166 {ViewId::GREEN_DRAGON, EnemyId::GREEN_DRAGON, "Green dragon", VillainType::MAIN},
167 {ViewId::SHELOB, EnemyId::SHELOB, "Giant spider", VillainType::MAIN},
168 {ViewId::ANT_QUEEN, EnemyId::ANTS_OPEN, "Ants", VillainType::MAIN},
169 {ViewId::DARK_ELF_LORD, EnemyId::DARK_ELVES, "Dark elves", VillainType::MAIN},
170 {ViewId::ORC_CAPTAIN, EnemyId::ORC_VILLAGE, "Greenskin village", VillainType::MAIN},
171 {ViewId::DEMON_LORD, EnemyId::DEMON_DEN, "Demon den", VillainType::MAIN},
172 };
173 }
174 }
175
getLesserVillains()176 vector<Campaign::VillainInfo> CampaignBuilder::getLesserVillains() {
177 switch (playerRole) {
178 case PlayerRole::KEEPER:
179 return {
180 {ViewId::ENT, EnemyId::ENTS, "Tree spirits", VillainType::LESSER},
181 {ViewId::DRIAD, EnemyId::DRIADS, "Driads", VillainType::LESSER},
182 {ViewId::CYCLOPS, EnemyId::CYCLOPS, "Cyclops", VillainType::LESSER},
183 {ViewId::SHELOB, EnemyId::SHELOB, "Giant spider", VillainType::LESSER},
184 {ViewId::HYDRA, EnemyId::HYDRA, "Hydra", VillainType::LESSER},
185 {ViewId::UNICORN, EnemyId::UNICORN_HERD, "Unicorn herd", VillainType::LESSER},
186 {ViewId::ANT_QUEEN, EnemyId::ANTS_OPEN, "Ants", VillainType::LESSER},
187 {ViewId::ZOMBIE, EnemyId::CEMETERY, "Zombies", VillainType::LESSER},
188 };
189 case PlayerRole::ADVENTURER:
190 return {
191 {ViewId::BANDIT, EnemyId::BANDITS, "Bandits", VillainType::LESSER},
192 {ViewId::CYCLOPS, EnemyId::CYCLOPS, "Cyclops", VillainType::LESSER},
193 {ViewId::HYDRA, EnemyId::HYDRA, "Hydra", VillainType::LESSER},
194 {ViewId::ZOMBIE, EnemyId::CEMETERY, "Zombies", VillainType::LESSER},
195 {ViewId::OGRE, EnemyId::OGRE_CAVE, "Ogres", VillainType::LESSER},
196 {ViewId::HARPY, EnemyId::HARPY_CAVE, "Harpies", VillainType::LESSER},
197 };
198 }
199 }
200
getAllies()201 vector<Campaign::VillainInfo> CampaignBuilder::getAllies() {
202 switch (playerRole) {
203 case PlayerRole::KEEPER:
204 return {
205 // {ViewId::UNKNOWN_MONSTER, EnemyId::OGRE_CAVE, "Unknown", VillainType::ALLY},
206 // {ViewId::UNKNOWN_MONSTER, EnemyId::HARPY_CAVE, "Unknown", VillainType::ALLY},
207 {ViewId::UNKNOWN_MONSTER, EnemyId::SOKOBAN, "Unknown", VillainType::ALLY},
208 {ViewId::DARK_ELF_LORD, EnemyId::DARK_ELVES, "Dark elves", VillainType::ALLY},
209 {ViewId::GNOME_BOSS, EnemyId::GNOMES, "Gnomes", VillainType::ALLY},
210 {ViewId::ORC_CAPTAIN, EnemyId::ORC_VILLAGE, "Greenskin village", VillainType::ALLY},
211 };
212 case PlayerRole::ADVENTURER:
213 return {
214 {ViewId::DUKE, EnemyId::KNIGHTS, "Knights", VillainType::ALLY},
215 {ViewId::ELF_LORD, EnemyId::ELVES, "Elves", VillainType::ALLY},
216 {ViewId::DWARF_BARON, EnemyId::DWARVES, "Dwarves", VillainType::ALLY},
217 {ViewId::LIZARDLORD, EnemyId::LIZARDMEN, "Lizardmen", VillainType::ALLY},
218 };
219 }
220 }
221
getIntroText() const222 const char* CampaignBuilder::getIntroText() const {
223 switch (playerRole) {
224 case PlayerRole::KEEPER:
225 return
226 "Welcome to the campaign mode! "
227 "The world, which you see below, is made up of smaller maps. You will build your base on one of them. "
228 "There are hostile and friendly tribes around you. You have to conquer all villains marked as \"main\" "
229 "to win the game."
230 "You can travel to other sites by creating a team and using the travel command.\n\n"
231 "The highlighted tribes are in your influence zone, which means that you can currently interact with them "
232 "(trade, recruit, attack or be attacked). "
233 "As you conquer more enemies, your influence zone grows.\n\n";
234 case PlayerRole::ADVENTURER:
235 return
236 "Welcome to the campaign mode! "
237 "The world, which you see below, is made up of smaller maps. Your adventure will start on one of them. "
238 "There are hostile and friendly tribes around you. You have to conquer all villains marked as \"main\" "
239 "to win the game."
240 "You can travel to other sites by using the travel command.\n\n"
241 "The highlighted tribes are in your influence zone, which means that you can currently travel there. "
242 "As you conquer more enemies, your influence zone grows.\n\n";
243 }
244 }
245
setPlayerPos(Campaign & campaign,Vec2 pos,WConstCreature player)246 void CampaignBuilder::setPlayerPos(Campaign& campaign, Vec2 pos, WConstCreature player) {
247 switch (playerRole) {
248 case PlayerRole::KEEPER:
249 if (campaign.playerPos)
250 campaign.clearSite(*campaign.playerPos);
251 campaign.playerPos = pos;
252 campaign.sites[*campaign.playerPos].dweller =
253 Campaign::SiteInfo::Dweller(Campaign::KeeperInfo{player->getViewObject().id()});
254 break;
255 case PlayerRole:: ADVENTURER:
256 campaign.playerPos = pos;
257 break;
258 }
259
260 }
261
getPlayerCreature()262 PCreature CampaignBuilder::getPlayerCreature() {
263 PCreature ret = CreatureFactory::fromId(options->getCreatureId(getPlayerTypeOptionId()), getPlayerTribeId());
264 auto name = options->getStringValue(getPlayerNameOptionId());
265 if (!name.empty())
266 ret->getName().setFirst(name);
267 ret->getName().useFullTitle();
268 return ret;
269 }
270
271
getTerrain(RandomGen & random,Vec2 size,int numBlocked)272 static Table<Campaign::SiteInfo> getTerrain(RandomGen& random, Vec2 size, int numBlocked) {
273 Table<Campaign::SiteInfo> ret(size, {});
274 for (Vec2 v : ret.getBounds())
275 ret[v].viewId.push_back(ViewId::GRASS);
276 vector<Vec2> freePos = ret.getBounds().getAllSquares();
277 for (int i : Range(numBlocked)) {
278 Vec2 pos = random.choose(freePos);
279 freePos.removeElement(pos);
280 ret[pos].setBlocked();
281 }
282 return ret;
283 }
284
285 struct VillainCounts {
286 int numMain;
287 int numLesser;
288 int numAllies;
289 int maxRetired;
290 };
291
getVillainCounts(CampaignType type,Options * options)292 static VillainCounts getVillainCounts(CampaignType type, Options* options) {
293 switch (type) {
294 case CampaignType::FREE_PLAY: {
295 return {
296 options->getIntValue(OptionId::MAIN_VILLAINS),
297 options->getIntValue(OptionId::LESSER_VILLAINS),
298 options->getIntValue(OptionId::ALLIES),
299 10000
300 };
301 }
302 case CampaignType::CAMPAIGN:
303 return {4, 6, 2, 1};
304 case CampaignType::ENDLESS:
305 return {
306 0,
307 options->getIntValue(OptionId::LESSER_VILLAINS),
308 options->getIntValue(OptionId::ALLIES),
309 0
310 };
311 case CampaignType::QUICK_MAP:
312 case CampaignType::SINGLE_KEEPER:
313 return {0, 0, 0, 0};
314 }
315 }
316
CampaignBuilder(View * v,RandomGen & rand,Options * o,PlayerRole r)317 CampaignBuilder::CampaignBuilder(View* v, RandomGen& rand, Options* o, PlayerRole r)
318 : view(v), random(rand), playerRole(r), options(o) {
319 }
320
getNewIdSuffix()321 static string getNewIdSuffix() {
322 vector<char> chars;
323 for (char c : Range(128))
324 if (isalnum(c))
325 chars.push_back(c);
326 string ret;
327 for (int i : Range(4))
328 ret += Random.choose(chars);
329 return ret;
330 }
331
332 struct VillainPlacement {
333 function<bool(int)> xPredicate;
334 optional<Vec2> firstLocation;
335 };
336
placeVillains(Campaign & campaign,vector<Campaign::SiteInfo::Dweller> villains,const VillainPlacement & placement,int count)337 void CampaignBuilder::placeVillains(Campaign& campaign, vector<Campaign::SiteInfo::Dweller> villains,
338 const VillainPlacement& placement, int count) {
339 random.shuffle(villains.begin(), villains.end());
340 if (villains.size() > count)
341 villains.resize(count);
342 vector<Vec2> freePos;
343 for (Vec2 v : campaign.sites.getBounds())
344 if (!campaign.sites[v].blocked && campaign.sites[v].isEmpty() && placement.xPredicate(v.x))
345 freePos.push_back(v);
346 freePos = random.permutation(freePos);
347 if (auto& pos = placement.firstLocation)
348 freePos = concat({*pos}, freePos);
349 for (int i : All(villains))
350 campaign.sites[freePos[i]].dweller = villains[i];
351 }
352
getVillainPlacement(const Campaign & campaign,VillainType type)353 VillainPlacement CampaignBuilder::getVillainPlacement(const Campaign& campaign, VillainType type) {
354 VillainPlacement ret { [&campaign](int x) { return campaign.sites.getBounds().getXRange().contains(x);}, none };
355 switch (campaign.getType()) {
356 case CampaignType::CAMPAIGN:
357 switch (type) {
358 case VillainType::LESSER:
359 ret.xPredicate = [](int x) { return x >= 5 && x < 12; };
360 break;
361 case VillainType::MAIN:
362 ret.xPredicate = [](int x) { return (x >= 1 && x < 5) || (x >= 12 && x < 16) ; };
363 break;
364 case VillainType::ALLY:
365 if (campaign.getPlayerRole() == PlayerRole::ADVENTURER)
366 ret.firstLocation = *considerStaticPlayerPos(campaign);
367 break;
368 default:
369 break;
370 }
371 break;
372 default:
373 break;
374 }
375 return ret;
376 }
377
378 using Dweller = Campaign::SiteInfo::Dweller;
379
380 template <typename T>
shuffle(RandomGen & random,vector<T> v)381 vector<Dweller> shuffle(RandomGen& random, vector<T> v) {
382 random.shuffle(v.begin(), v.end());
383 return v.transform([](const T& t) { return Dweller(t); });
384 }
385
placeVillains(Campaign & campaign,const VillainCounts & counts,const optional<RetiredGames> & retired)386 void CampaignBuilder::placeVillains(Campaign& campaign, const VillainCounts& counts,
387 const optional<RetiredGames>& retired) {
388 int numRetired = retired ? min(retired->getNumActive(), min(counts.numMain, counts.maxRetired)) : 0;
389 placeVillains(campaign, shuffle(random, getMainVillains()), getVillainPlacement(campaign, VillainType::MAIN),
390 counts.numMain - numRetired);
391 placeVillains(campaign, shuffle(random, getLesserVillains()), getVillainPlacement(campaign, VillainType::LESSER),
392 counts.numLesser);
393 placeVillains(campaign, shuffle(random, getAllies()), getVillainPlacement(campaign, VillainType::ALLY),
394 counts.numAllies);
395 if (retired) {
396 placeVillains(campaign, retired->getActiveGames().transform(
397 [](const RetiredGames::RetiredGame& game) -> Dweller {
398 return Campaign::RetiredInfo{game.gameInfo, game.fileInfo};
399 }), getVillainPlacement(campaign, VillainType::MAIN), numRetired);
400 }
401 }
402
getMenuWarning(CampaignType type)403 static optional<View::CampaignOptions::WarningType> getMenuWarning(CampaignType type) {
404 switch (type) {
405 case CampaignType::CAMPAIGN:
406 return View::CampaignOptions::OTHER_MODES;
407 case CampaignType::SINGLE_KEEPER:
408 return View::CampaignOptions::NO_RETIRE;
409 default:
410 return none;
411 }
412 }
413
autoConfirm(CampaignType type)414 static bool autoConfirm(CampaignType type) {
415 switch (type) {
416 case CampaignType::QUICK_MAP:
417 return true;
418 default:
419 return false;
420 }
421 }
422
getIntroMessages(CampaignType type,string worldName)423 static vector<string> getIntroMessages(CampaignType type, string worldName) {
424 vector<string> ret = {
425 "Welcome to KeeperRL Alpha23! A lot of gameplay changes have arrived with this update. Below is a very short "
426 "summary, and we encourage you to check out the full change log at www.keeperrl.com.\n \n"
427 "Mana is no longer generated at the library, and instead you only receive it for defeating enemies. "
428 "Many features, including construction and crafting costs, have been rebalanced to accommodate this change. "
429 "You will only need mana to research new technology, and increase the population limit.\n \n"
430 "If you miss the old ways, you can enable mana regeneration when playing the 'free play' game mode.\n \n"
431 "When commanding a team, you can choose to control every team member directly. "
432 "We encourage you to use this feature during combat, as it's extremely useful. You can toggle it using the "
433 "shortcut [G] or by going into the [Commands] menu.\n \n"
434 "From now on the vampire lord will be hostile when you wake him up, so be careful!"
435 };
436 if (type == CampaignType::ENDLESS)
437 ret.push_back(
438 "Welcome to the new endless mode! Your task here is to survive as long as possible, while "
439 "defending your dungeon from incoming enemy waves. The enemies don't come from any specific place and "
440 "will just appear at the edge of the map. You will get mana for defeating each wave. "
441 "Note that there are also traditional enemy villages scattered around and they may also attack you.\n \n"
442 "The endless mode is a completely new feature and we are very interested in your feedback on how "
443 "it can be developed further. Please drop by on the forums at keeperrl.com or on Steam and let us know!"
444 );
445
446 return ret;
447 }
448
prepareCampaign(function<optional<RetiredGames> (CampaignType)> genRetired,CampaignType type)449 optional<CampaignSetup> CampaignBuilder::prepareCampaign(function<optional<RetiredGames>(CampaignType)> genRetired,
450 CampaignType type) {
451 Vec2 size(17, 9);
452 int numBlocked = 0.6 * size.x * size.y;
453 Table<Campaign::SiteInfo> terrain = getTerrain(random, size, numBlocked);
454 auto retired = genRetired(type);
455 View::CampaignMenuState menuState { true, false};
456 setCountLimits(options);
457 options->setChoices(OptionId::KEEPER_TYPE, {CreatureId::KEEPER, CreatureId::KEEPER_F});
458 options->setChoices(OptionId::ADVENTURER_TYPE, {CreatureId::ADVENTURER, CreatureId::ADVENTURER_F});
459 while (1) {
460 PCreature player = getPlayerCreature();
461 Campaign campaign(terrain, type, playerRole, NameGenerator::get(NameGeneratorId::WORLD)->getNext());
462 if (auto pos = considerStaticPlayerPos(campaign)) {
463 campaign.clearSite(*pos);
464 setPlayerPos(campaign, *pos, player.get());
465 }
466 placeVillains(campaign, getVillainCounts(type, options), retired);
467 while (1) {
468 bool updateMap = false;
469 campaign.influenceSize = options->getIntValue(OptionId::INFLUENCE_SIZE);
470 campaign.refreshInfluencePos();
471 CampaignAction action = autoConfirm(type) ? CampaignActionId::CONFIRM
472 : view->prepareCampaign({
473 campaign,
474 (retired && type == CampaignType::FREE_PLAY) ? optional<RetiredGames&>(*retired) : none,
475 player.get(),
476 getPrimaryOptions(),
477 getSecondaryOptions(type),
478 getSiteChoiceTitle(type),
479 getIntroText(),
480 getAvailableTypes().transform([](CampaignType t) -> View::CampaignOptions::CampaignTypeInfo {
481 return {t, getCampaignTypeDescription(t)};}),
482 getMenuWarning(type)
483 }, options, menuState);
484 switch (action.getId()) {
485 case CampaignActionId::REROLL_MAP:
486 terrain = getTerrain(random, size, numBlocked);
487 updateMap = true;
488 break;
489 case CampaignActionId::UPDATE_MAP:
490 updateMap = true;
491 break;
492 case CampaignActionId::CHANGE_TYPE:
493 type = action.get<CampaignType>();
494 retired = genRetired(type);
495 updateMap = true;
496 break;
497 case CampaignActionId::UPDATE_OPTION:
498 switch (action.get<OptionId>()) {
499 case OptionId::KEEPER_NAME:
500 case OptionId::ADVENTURER_NAME:
501 case OptionId::KEEPER_TYPE:
502 case OptionId::ADVENTURER_TYPE:
503 player = getPlayerCreature();
504 if (campaign.playerPos) {
505 setPlayerPos(campaign, *campaign.playerPos, player.get());
506 }
507 break;
508 case OptionId::GENERATE_MANA:
509 case OptionId::INFLUENCE_SIZE: break;
510 default: updateMap = true; break;
511 }
512 break;
513 case CampaignActionId::CANCEL:
514 return none;
515 case CampaignActionId::CHOOSE_SITE:
516 if (!considerStaticPlayerPos(campaign))
517 setPlayerPos(campaign, action.get<Vec2>(), player.get());
518 break;
519 case CampaignActionId::CONFIRM:
520 if (!retired || retired->getNumActive() > 0 || playerRole != PlayerRole::KEEPER ||
521 retired->getAllGames().empty() ||
522 view->yesOrNoPrompt("The imps are going to be sad if you don't add any retired dungeons. Continue?")) {
523 string name = *player->getName().first();
524 string gameIdentifier = name + "_" + campaign.worldName + getNewIdSuffix();
525 string gameDisplayName = name + " of " + campaign.worldName;
526 return CampaignSetup{campaign, std::move(player), gameIdentifier, gameDisplayName,
527 options->getBoolValue(OptionId::GENERATE_MANA) &&
528 getSecondaryOptions(type).contains(OptionId::GENERATE_MANA),
529 getIntroMessages(type, campaign.getWorldName())};
530 }
531 }
532 if (updateMap)
533 break;
534 }
535 }
536 }
537
getEmptyCampaign()538 CampaignSetup CampaignBuilder::getEmptyCampaign() {
539 Campaign ret(Table<Campaign::SiteInfo>(1, 1), CampaignType::SINGLE_KEEPER, PlayerRole::KEEPER, "");
540 return CampaignSetup{ret, PCreature(nullptr), "", ""};
541 }
542