Refactors a lot of stuff out of sbf.
This commit is contained in:
@@ -183,6 +183,28 @@ cc_test(
|
|||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
cc_library(
|
||||||
|
name = "character_generator",
|
||||||
|
srcs = ["CharacterGenerator.cpp"],
|
||||||
|
hdrs = ["CharacterGenerator.h"],
|
||||||
|
deps = [
|
||||||
|
"archetypes",
|
||||||
|
"character",
|
||||||
|
"freebies",
|
||||||
|
"genders",
|
||||||
|
"menus",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
cc_test(
|
||||||
|
name = "character_generator_test",
|
||||||
|
srcs = ["CharacterGenerator_test.cpp"],
|
||||||
|
deps = [
|
||||||
|
":character_generator",
|
||||||
|
":tinytest",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
# cc_library(
|
# cc_library(
|
||||||
# name = "main",
|
# name = "main",
|
||||||
# srcs = ["main.cpp"],
|
# srcs = ["main.cpp"],
|
||||||
@@ -255,13 +277,7 @@ cc_binary(
|
|||||||
"sbf.h",
|
"sbf.h",
|
||||||
],
|
],
|
||||||
deps = [
|
deps = [
|
||||||
":abilities",
|
":character_generator",
|
||||||
":archetypes",
|
|
||||||
":attributes",
|
|
||||||
":backgrounds",
|
|
||||||
":character",
|
|
||||||
":clans",
|
|
||||||
":genders",
|
|
||||||
":menus",
|
":menus",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -28,11 +28,12 @@ CharacterType::CharacterType() {
|
|||||||
natureId = 0;
|
natureId = 0;
|
||||||
demeanorId = 0;
|
demeanorId = 0;
|
||||||
generation = 3;
|
generation = 3;
|
||||||
roadName = "";
|
road_name = "";
|
||||||
roadValue = 0;
|
road_value = 0;
|
||||||
willpower = 0;
|
willpower = 0;
|
||||||
bloodPool = 0;
|
bloodPool = 0;
|
||||||
derangementId = -1;
|
derangementId = -1;
|
||||||
|
freebie_points = 15;
|
||||||
|
|
||||||
// Virtues
|
// Virtues
|
||||||
selfControl = 1;
|
selfControl = 1;
|
||||||
@@ -250,10 +251,9 @@ int CharacterType::GetVirtueValue(int id) const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void CharacterType::FillVirtueValues(std::vector<int>& values) const {
|
void CharacterType::FillVirtueValues(std::vector<int>& values) const {
|
||||||
// TODO: This method sucks, but was needed in QBasic.
|
|
||||||
values.clear();
|
values.clear();
|
||||||
for (int id = 0; id <= kVirtuesCount; id++) {
|
for (int id = 0; id <= kVirtuesCount; id++) {
|
||||||
values[id] = GetVirtueValue(id);
|
values.push_back(GetVirtueValue(id));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -756,4 +756,28 @@ string CharacterType::GetAllDerangementsLine() const {
|
|||||||
}
|
}
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int CharacterType::GetFreebiePoints() const {
|
||||||
|
return freebie_points;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CharacterType::SetFreebiePoints(int value) {
|
||||||
|
freebie_points = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
string CharacterType::GetRoadName() const {
|
||||||
|
return road_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CharacterType::SetRoadName(string name) {
|
||||||
|
road_name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
int CharacterType::GetRoadValue() const {
|
||||||
|
return road_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CharacterType::SetRoadValue(int value) {
|
||||||
|
road_value = value;
|
||||||
|
}
|
||||||
} // End namespace SBF
|
} // End namespace SBF
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ int GetAttributePointsForRank(int rank_id);
|
|||||||
int GetBackgroundPoints();
|
int GetBackgroundPoints();
|
||||||
int GetDisciplinePoints();
|
int GetDisciplinePoints();
|
||||||
int GetVirtuePoints();
|
int GetVirtuePoints();
|
||||||
|
const int kInitialGeneration = 13;
|
||||||
|
|
||||||
class CharacterType {
|
class CharacterType {
|
||||||
public:
|
public:
|
||||||
@@ -47,9 +48,12 @@ class CharacterType {
|
|||||||
std::vector<int> GetBackgroundValues() const;
|
std::vector<int> GetBackgroundValues() const;
|
||||||
int GetDisciplineValue(int id) const;
|
int GetDisciplineValue(int id) const;
|
||||||
std::vector<int> GetDisciplineValues() const;
|
std::vector<int> GetDisciplineValues() const;
|
||||||
|
int GetFreebiePoints() const;
|
||||||
int GetKnowledgeValue(int id) const;
|
int GetKnowledgeValue(int id) const;
|
||||||
int GetMentalAttributeValue(int id) const;
|
int GetMentalAttributeValue(int id) const;
|
||||||
int GetPhysicalAttributeValue(int id) const;
|
int GetPhysicalAttributeValue(int id) const;
|
||||||
|
std::string GetRoadName() const;
|
||||||
|
int GetRoadValue() const;
|
||||||
int GetSkillValue(int id) const;
|
int GetSkillValue(int id) const;
|
||||||
int GetSocialAttributeValue(int id) const;
|
int GetSocialAttributeValue(int id) const;
|
||||||
int GetTalentValue(int id) const;
|
int GetTalentValue(int id) const;
|
||||||
@@ -59,13 +63,17 @@ class CharacterType {
|
|||||||
void SetAttributeValue(int group_id, int id, int value);
|
void SetAttributeValue(int group_id, int id, int value);
|
||||||
void SetBackgroundValue(int id, int value);
|
void SetBackgroundValue(int id, int value);
|
||||||
void SetDisciplineValue(int id, int value);
|
void SetDisciplineValue(int id, int value);
|
||||||
|
void SetFreebiePoints(int value);
|
||||||
void SetKnowledgeValue(int id, int value);
|
void SetKnowledgeValue(int id, int value);
|
||||||
void SetMentalAttributeValue(int id, int value);
|
void SetMentalAttributeValue(int id, int value);
|
||||||
void SetPhysicalAttributeValue(int id, int value);
|
void SetPhysicalAttributeValue(int id, int value);
|
||||||
|
void SetRoadName(std::string name);
|
||||||
|
void SetRoadValue(int value);
|
||||||
void SetSkillValue(int id, int value);
|
void SetSkillValue(int id, int value);
|
||||||
void SetSocialAttributeValue(int id, int value);
|
void SetSocialAttributeValue(int id, int value);
|
||||||
void SetTalentValue(int id, int value);
|
void SetTalentValue(int id, int value);
|
||||||
void SetVirtueValue(int id, int value);
|
void SetVirtueValue(int id, int value);
|
||||||
|
|
||||||
std::string name;
|
std::string name;
|
||||||
std::string player;
|
std::string player;
|
||||||
std::string chronicle;
|
std::string chronicle;
|
||||||
@@ -80,13 +88,16 @@ class CharacterType {
|
|||||||
int selfControl;
|
int selfControl;
|
||||||
int courage;
|
int courage;
|
||||||
int generation;
|
int generation;
|
||||||
std::string roadName;
|
|
||||||
int roadValue;
|
|
||||||
int willpower;
|
int willpower;
|
||||||
int bloodPool;
|
int bloodPool;
|
||||||
int derangementId;
|
int derangementId;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
// Scalars
|
||||||
|
int freebie_points;
|
||||||
|
std::string road_name;
|
||||||
|
int road_value;
|
||||||
|
|
||||||
// Disciplines
|
// Disciplines
|
||||||
int discipline_animalism;
|
int discipline_animalism;
|
||||||
int discipline_auspex;
|
int discipline_auspex;
|
||||||
|
|||||||
514
sbf-cpp/CharacterGenerator.cpp
Normal file
514
sbf-cpp/CharacterGenerator.cpp
Normal file
@@ -0,0 +1,514 @@
|
|||||||
|
#include "CharacterGenerator.h"
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "Archetypes.h"
|
||||||
|
#include "Character.h"
|
||||||
|
#include "Freebies.h"
|
||||||
|
#include "Genders.h"
|
||||||
|
#include "Menus.h"
|
||||||
|
#include "Random.h"
|
||||||
|
#include "Utils.h"
|
||||||
|
|
||||||
|
namespace SBF {
|
||||||
|
namespace {
|
||||||
|
using std::cout;
|
||||||
|
using std::endl;
|
||||||
|
using std::string;
|
||||||
|
using std::to_string;
|
||||||
|
using std::vector;
|
||||||
|
} // End namespace
|
||||||
|
|
||||||
|
void CGGetHeader(CharacterType& ch) {
|
||||||
|
MaybeClearScreen();
|
||||||
|
MenuStyle ms;
|
||||||
|
ch.name = GetString("What is the character's name?");
|
||||||
|
ch.player = GetString("Who is the player?");
|
||||||
|
ch.chronicle = GetString("What chronicle is the character going to be used for?");
|
||||||
|
ch.haven = GetString("What is the character's Haven?");
|
||||||
|
ch.concept = GetString("What is the character's concept?");
|
||||||
|
ch.age = GetString("How old is the character?");
|
||||||
|
vector<string> genders;
|
||||||
|
FillGenderLabels(genders);
|
||||||
|
ch.genderId = ChooseStringId(genders, ms, "What is the character's gender?");
|
||||||
|
vector<string> clans;
|
||||||
|
FillClanLabels(clans);
|
||||||
|
ch.clanId = ChooseStringId(clans, ms, "What clan is the character from?");
|
||||||
|
vector<string> archetypes;
|
||||||
|
FillArchetypeLabels(archetypes);
|
||||||
|
ch.natureId = ChooseStringId(archetypes, ms, "What is the character's nature?");
|
||||||
|
ch.demeanorId = ChooseStringId(archetypes, ms, "What is the character's demeanor?");
|
||||||
|
}
|
||||||
|
|
||||||
|
void CGGetDisciplines(CharacterType& ch) {
|
||||||
|
MenuStyle ms;
|
||||||
|
int discipline_points = GetDisciplinePoints();
|
||||||
|
vector<int> discipline_values;
|
||||||
|
vector<string> discipline_labels;
|
||||||
|
FillDisciplineLabels(discipline_labels);
|
||||||
|
while (discipline_points > 0) {
|
||||||
|
MaybeClearScreen();
|
||||||
|
discipline_values = ch.GetDisciplineValues();
|
||||||
|
int discipline_id = ChooseStringIdWithValues(
|
||||||
|
discipline_labels,
|
||||||
|
discipline_values,
|
||||||
|
ms,
|
||||||
|
"Which discipline do you want to spend 1 of your " + to_string(discipline_points) + " points on?");
|
||||||
|
ch.SetDisciplineValue(discipline_id, ch.GetDisciplineValue(discipline_id) + 1);
|
||||||
|
discipline_points--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CGGetAttributes(CharacterType& ch) {
|
||||||
|
MenuStyle ms_without_values;
|
||||||
|
MenuStyle ms_with_values;
|
||||||
|
// indexed by group_id - 1 holds the rank_id
|
||||||
|
vector<int> attribute_ranks(kAttributeGroupsCount);
|
||||||
|
|
||||||
|
// Attribute groups menu (physical/social/mental)
|
||||||
|
vector<MenuItem> attribute_groups_menu_items;
|
||||||
|
for (size_t i = 1; i <= kAttributeGroupsCount; i++) {
|
||||||
|
attribute_groups_menu_items.push_back(MenuItem(GetAttributeGroupLabel(i), i));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Choose attribute group priorities.
|
||||||
|
int group_sum = 0;
|
||||||
|
int rank_sum = 1;
|
||||||
|
for (size_t i = 1; i < kAttributeGroupsCount; i++) {
|
||||||
|
int next_group_id = ChooseMenuItemId(attribute_groups_menu_items,
|
||||||
|
ms_without_values,
|
||||||
|
"Choose your " + ToLower(GetRank(i).label) + " attribute?",
|
||||||
|
true);
|
||||||
|
int next_group_index = next_group_id - 1;
|
||||||
|
attribute_groups_menu_items.at(next_group_index).is_visible = false;
|
||||||
|
attribute_ranks[next_group_index] = i;
|
||||||
|
rank_sum += i + 1;
|
||||||
|
group_sum += next_group_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
// General formula for last choice given 1 to count based indexing is this. (Sum from 1 to count) - (Sum of all
|
||||||
|
// previous choice IDs). Sum(1..AllAttributesCount)-Sum(Choice[1..Choice[AllAttributesCount-1]).
|
||||||
|
int last_group = rank_sum - group_sum;
|
||||||
|
attribute_ranks[last_group - 1] = kAttributeGroupsCount;
|
||||||
|
|
||||||
|
// Spend attribute points
|
||||||
|
for (int group_id = 1; group_id <= kAttributeGroupsCount; group_id++) {
|
||||||
|
int group_index = group_id - 1;
|
||||||
|
vector<string> attribute_labels = GetAttributeLabelsInGroup(group_id);
|
||||||
|
int rank_id = attribute_ranks.at(group_index);
|
||||||
|
int attribute_points = GetAttributePointsForRank(rank_id);
|
||||||
|
while (attribute_points > 0) {
|
||||||
|
vector<int> values = ch.GetAttributeValuesInGroup(group_id);
|
||||||
|
string prompt = "Which " + ToLower(GetAttributeGroupLabel(group_id))
|
||||||
|
+ " attribute do you want to spend 1 of your " + to_string(attribute_points) + " points on?";
|
||||||
|
int attribute_id = ChooseStringIdWithValues(attribute_labels, values, ms_with_values, prompt);
|
||||||
|
ch.SetAttributeValue(group_id, attribute_id, ch.GetAttributeValue(group_id, attribute_id) + 1);
|
||||||
|
attribute_points--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CGGetBackgrounds(CharacterType& ch) {
|
||||||
|
// Spend background points
|
||||||
|
MenuStyle ms;
|
||||||
|
int background_points = GetBackgroundPoints();
|
||||||
|
vector<string> background_labels = GetBackgroundLabels();
|
||||||
|
while (background_points > 0) {
|
||||||
|
MaybeClearScreen();
|
||||||
|
vector<int> background_values = ch.GetBackgroundValues();
|
||||||
|
int background_id = ChooseStringIdWithValues(
|
||||||
|
background_labels,
|
||||||
|
background_values,
|
||||||
|
ms,
|
||||||
|
"Which background do you want to spend 1 of your " + to_string(background_points) + " points on?");
|
||||||
|
ch.SetBackgroundValue(background_id, ch.GetBackgroundValue(background_id) + 1);
|
||||||
|
background_points--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CGGetRoad(CharacterType& ch) {
|
||||||
|
// TODO: Update this to actually pick a road for VtDA.
|
||||||
|
ch.SetRoadName("Humanity");
|
||||||
|
}
|
||||||
|
|
||||||
|
void CGSpendVirtuePoints(CharacterType& ch) {
|
||||||
|
// Spend virtue points
|
||||||
|
MenuStyle ms;
|
||||||
|
int virtue_points = GetVirtuePoints();
|
||||||
|
vector<string> labels = GetVirtueLabels();
|
||||||
|
while (virtue_points > 0) {
|
||||||
|
vector<int> values = ch.GetVirtueValues();
|
||||||
|
int virtue_id = ChooseStringIdWithValues(
|
||||||
|
labels, values, ms, "Which virtue do you want to spend 1 of your " + to_string(virtue_points) + " points on?");
|
||||||
|
ch.SetVirtueValue(virtue_id, ch.GetVirtueValue(virtue_id) + 1);
|
||||||
|
virtue_points--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CGGetDerangement(CharacterType& ch) {
|
||||||
|
if (ch.clanId == kClanMalkavian) {
|
||||||
|
// If the clan is malkavian then pick a derangement.
|
||||||
|
MenuStyle ms;
|
||||||
|
ms.use_colors = true;
|
||||||
|
ch.derangementId =
|
||||||
|
ChooseStringIdWithColors(GetDerangementLabels(), GetDerangementColors(), ms, "Which derangement do you want?");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CGSpendFreebiePoints(CharacterType& ch) {
|
||||||
|
int freebie_points = ch.GetFreebiePoints();
|
||||||
|
MenuStyle ms;
|
||||||
|
while (freebie_points > 0) {
|
||||||
|
MaybeClearScreen();
|
||||||
|
// Build the menu
|
||||||
|
vector<FreebieType> available_freebies = GetAvailableFreebies(freebie_points);
|
||||||
|
vector<MenuItem> menu_items;
|
||||||
|
for (int freebie_index = 0; freebie_index < available_freebies.size(); freebie_index++) {
|
||||||
|
FreebieType freebie = available_freebies.at(freebie_index);
|
||||||
|
MenuItem mi(freebie.label, freebie.id);
|
||||||
|
if (freebie_index + 1 == kFreebieShowCharacterSheetId) {
|
||||||
|
mi.include_in_random = false;
|
||||||
|
}
|
||||||
|
menu_items.push_back(mi);
|
||||||
|
}
|
||||||
|
string prompt = "You have " + to_string(freebie_points)
|
||||||
|
+ " freebie points remaining. What would you like to spend the points on?";
|
||||||
|
int id = ChooseMenuItemId(menu_items, ms, prompt, true);
|
||||||
|
switch (id) {
|
||||||
|
case kFreebieDisciplineId:
|
||||||
|
CGSpendDisciplinePoint(ch);
|
||||||
|
break;
|
||||||
|
case kFreebieAttributeId:
|
||||||
|
CGSpendAttributePoint(ch);
|
||||||
|
break;
|
||||||
|
case kFreebieAbilityId:
|
||||||
|
CGSpendAbilityPoint(ch);
|
||||||
|
break;
|
||||||
|
case kFreebieVirtueId:
|
||||||
|
CGSpendVirtuePoint(ch);
|
||||||
|
break;
|
||||||
|
case kFreebieHumanityId:
|
||||||
|
CGSpendHumanityPoint(ch);
|
||||||
|
break;
|
||||||
|
case kFreebieBackgroundId:
|
||||||
|
CGSpendBackgroundPoint(ch);
|
||||||
|
break;
|
||||||
|
case kFreebieShowCharacterSheetId:
|
||||||
|
ShowCharacterSheet(ch);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
freebie_points = ch.GetFreebiePoints();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CGSpendDisciplinePoint(CharacterType& ch) {
|
||||||
|
MaybeClearScreen();
|
||||||
|
MenuStyle ms;
|
||||||
|
ms.show_cancel = true;
|
||||||
|
ms.cancel_item_id = kDisciplinesCount + 1;
|
||||||
|
vector<int> values = ch.GetDisciplineValues();
|
||||||
|
vector<string> labels;
|
||||||
|
FillDisciplineLabels(labels);
|
||||||
|
string prompt = "Which discipline would you like to add 1 dot to?";
|
||||||
|
int discipline_id = ChooseStringIdWithValues(labels, values, ms, prompt);
|
||||||
|
if (discipline_id != ms.cancel_item_id) {
|
||||||
|
ch.SetDisciplineValue(discipline_id, ch.GetDisciplineValue(discipline_id) + 1);
|
||||||
|
ch.SetFreebiePoints(ch.GetFreebiePoints() - kFreebieDisciplineCost);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct GroupedStatReference {
|
||||||
|
int id;
|
||||||
|
int group_id;
|
||||||
|
int item_id;
|
||||||
|
};
|
||||||
|
|
||||||
|
void CGSpendAttributePoint(CharacterType& ch) {
|
||||||
|
MaybeClearScreen();
|
||||||
|
MenuStyle ms;
|
||||||
|
ms.show_cancel = true;
|
||||||
|
int num_attributes = 0;
|
||||||
|
vector<int> num_attributes_in_group;
|
||||||
|
for (int group_id = 1; group_id <= kAttributeGroupsCount; group_id++) {
|
||||||
|
int count = GetNumAttributesInGroup(group_id);
|
||||||
|
num_attributes_in_group.push_back(count);
|
||||||
|
num_attributes += count;
|
||||||
|
}
|
||||||
|
|
||||||
|
vector<GroupedStatReference> attribute_refs;
|
||||||
|
vector<string> labels;
|
||||||
|
vector<int> values;
|
||||||
|
|
||||||
|
int attribute_id = 1;
|
||||||
|
for (int attribute_group_id = 1; attribute_group_id <= kAttributeGroupsCount; attribute_group_id++) {
|
||||||
|
int attribute_group_index = attribute_group_id - 1;
|
||||||
|
for (int id = 1; id <= num_attributes_in_group.at(attribute_group_index); id++) {
|
||||||
|
GroupedStatReference attribute_ref = {attribute_id, attribute_group_id, id};
|
||||||
|
attribute_refs.push_back(attribute_ref);
|
||||||
|
labels.push_back(GetAttributeLabel(attribute_group_id, id));
|
||||||
|
values.push_back(ch.GetAttributeValue(attribute_group_id, id));
|
||||||
|
attribute_id++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
string prompt = "Which attribute do you want to add one dot to?";
|
||||||
|
ms.cancel_item_id = num_attributes + 1;
|
||||||
|
int id = ChooseStringIdWithValues(labels, values, ms, prompt);
|
||||||
|
if (id == ms.cancel_item_id) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
GroupedStatReference ref = attribute_refs.at(id - 1);
|
||||||
|
ch.SetAttributeValue(ref.group_id, ref.item_id, ch.GetAttributeValue(ref.group_id, ref.item_id) + 1);
|
||||||
|
ch.SetFreebiePoints(ch.GetFreebiePoints() - kFreebieAttributeCost);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CGSpendAbilityPoint(CharacterType& ch) {
|
||||||
|
MenuStyle ms;
|
||||||
|
ms.show_cancel = true;
|
||||||
|
bool done = false;
|
||||||
|
while (!done) {
|
||||||
|
MaybeClearScreen();
|
||||||
|
ms.cancel_item_id = kAbilitiesCount;
|
||||||
|
int ability_group_id =
|
||||||
|
ChooseStringId(GetAbilityGroupPluralLabels(), ms, "What kind of ability would you like to add 1 dot to?");
|
||||||
|
if (ability_group_id == ms.cancel_item_id) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
AbilityType ability = GetAbility(ability_group_id);
|
||||||
|
|
||||||
|
vector<string> labels = GetAbilityLabelsForAbilityGroup(ability_group_id);
|
||||||
|
ms.cancel_item_id = labels.size() + 1;
|
||||||
|
int ability_id =
|
||||||
|
ChooseStringId(labels, ms, "What " + ToLower(ability.singular) + " would you like to add 1 dot to?");
|
||||||
|
if (ability_id != ms.cancel_item_id) {
|
||||||
|
ch.SetAbilityValue(ability_group_id, ability_id, ch.GetAbilityValue(ability_group_id, ability_id) + 1);
|
||||||
|
ch.SetFreebiePoints(ch.GetFreebiePoints() - kFreebieAbilityCost);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CGSpendVirtuePoint(CharacterType& ch) {
|
||||||
|
MenuStyle ms;
|
||||||
|
ms.show_cancel = true;
|
||||||
|
ms.cancel_item_id = kVirtuesCount + 1;
|
||||||
|
string prompt = "What virtue would you like to add 1 dot to?";
|
||||||
|
int id = ChooseStringIdWithValues(GetVirtueLabels(), ch.GetVirtueValues(), ms, prompt);
|
||||||
|
if (id != ms.cancel_item_id) {
|
||||||
|
ch.SetVirtueValue(id, ch.GetVirtueValue(id) + 1);
|
||||||
|
ch.SetFreebiePoints(ch.GetFreebiePoints() - kFreebieVirtueCost);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CGSpendHumanityPoint(CharacterType& ch) {
|
||||||
|
vector<string> labels = {"Yes", "No"};
|
||||||
|
string prompt = "Are you sure you want to add a dot to your " + ch.GetRoadName() + "?";
|
||||||
|
MenuStyle ms;
|
||||||
|
ms.show_random = false;
|
||||||
|
if (ChooseYesOrNo(prompt)) {
|
||||||
|
ch.SetRoadValue(ch.GetRoadValue() - 1);
|
||||||
|
ch.SetFreebiePoints(ch.GetFreebiePoints() - kFreebieHumanityCost);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CGSpendBackgroundPoint(CharacterType& ch) {
|
||||||
|
MenuStyle ms;
|
||||||
|
ms.show_cancel = true;
|
||||||
|
vector<string> labels = GetBackgroundLabels();
|
||||||
|
ms.cancel_item_id = labels.size() + 1;
|
||||||
|
string prompt = "Which background would you like to add 1 dot to?";
|
||||||
|
int id = ChooseStringId(labels, ms, prompt);
|
||||||
|
if (id != ms.cancel_item_id) {
|
||||||
|
ch.SetBackgroundValue(id, ch.GetBackgroundValue(id) + 1);
|
||||||
|
ch.SetFreebiePoints(ch.GetFreebiePoints() - kFreebieBackgroundCost);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
string FormatAttributeValue(const string& label, int value) {
|
||||||
|
return MakeFitC(label + MakeFitL(to_string(value), 2), 12);
|
||||||
|
}
|
||||||
|
|
||||||
|
string FormatAbilityWithValue(const string& label, int value) {
|
||||||
|
return MakeFitC(MakeFitL(label + ":", 14) + to_string(value), 24);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SaveCharacterSheet(CharacterType& ch) {
|
||||||
|
// TODO: Fill this in.
|
||||||
|
cout << "// TODO: SaveCharacterSheet(CharacterType&)" << endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShowCharacterSheet(CharacterType& ch) {
|
||||||
|
const int kLeftColumnWidth = 36;
|
||||||
|
const int kRightColumnWidth = 37;
|
||||||
|
vector<string> discipline_strings;
|
||||||
|
size_t index;
|
||||||
|
for (index = 1; index <= kDisciplinesCount; index++) {
|
||||||
|
int value = ch.GetDisciplineValue(index);
|
||||||
|
if (value > 0) {
|
||||||
|
string suffix = "";
|
||||||
|
if (value > 1) {
|
||||||
|
suffix = " x" + to_string(value);
|
||||||
|
}
|
||||||
|
discipline_strings.push_back(GetDisciplineLabel(index) + suffix);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while (discipline_strings.size() <= 3) {
|
||||||
|
discipline_strings.push_back(RepeatChar(kLeftColumnWidth, '_'));
|
||||||
|
}
|
||||||
|
vector<string> background_strings(5);
|
||||||
|
for (index = 1; index <= kBackgroundsCount; index++) {
|
||||||
|
int value = ch.GetBackgroundValue(index);
|
||||||
|
if (value > 0) {
|
||||||
|
string suffix = "";
|
||||||
|
if (value > 1) {
|
||||||
|
suffix = " x" + to_string(value);
|
||||||
|
}
|
||||||
|
background_strings.push_back(GetBackgroundLabel(index) + suffix);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while (background_strings.size() <= 5) {
|
||||||
|
background_strings.push_back(RepeatChar(kLeftColumnWidth, '_'));
|
||||||
|
}
|
||||||
|
string all_derangements_line = ch.GetAllDerangementsLine();
|
||||||
|
vector<string> derangement_strings = WordWrap(all_derangements_line, kLeftColumnWidth);
|
||||||
|
while (derangement_strings.size() <= 5) {
|
||||||
|
derangement_strings.push_back(RepeatChar(kLeftColumnWidth, '_'));
|
||||||
|
}
|
||||||
|
MaybeClearScreen();
|
||||||
|
cout << "╔══════════════════════════════════════╦═══════════════════════════════════════╗" << endl;
|
||||||
|
cout << "║ Name: " << MakeFitL(ch.name, 30) << " ║ Gender: " << MakeFitL(GetGenderLabel(ch.genderId), 14)
|
||||||
|
<< " Generation: " << MakeFitR(to_string(ch.generation), 2) << " ║" << endl;
|
||||||
|
cout << "║ Clan: " << MakeFitL(GetClanLabel(ch.clanId), 30) << " ║ Age: " << MakeFitL(ch.age, 32) << " ║" << endl;
|
||||||
|
cout << "╠══════════════════════════════════════╣ Player: " << MakeFitL(ch.player, 29) << " ║" << endl;
|
||||||
|
cout << "║ Attributes ║ Chronicle: " << MakeFitL(ch.chronicle, 26) << " ║" << endl;
|
||||||
|
cout << "║ " << MakeFitC("Physical", 12) << MakeFitC("Social", 12) << MakeFitC("Mental", 12)
|
||||||
|
<< " ║ Haven: " << MakeFitL(ch.haven, 30) + " ║" << endl;
|
||||||
|
cout << "║ " << FormatAttributeValue("Str. ", ch.GetPhysicalAttributeValue(kPhysicalAttributeStrengthId))
|
||||||
|
<< FormatAttributeValue("App. ", ch.GetSocialAttributeValue(kSocialAttributeAppearanceId))
|
||||||
|
<< FormatAttributeValue("Int. ", ch.GetMentalAttributeValue(kMentalAttributeIntelligenceId))
|
||||||
|
<< " ║ Concept: " << MakeFitL(ch.concept, 28) << " ║" << endl;
|
||||||
|
cout << "║ " << FormatAttributeValue("Dex. ", ch.GetPhysicalAttributeValue(kPhysicalAttributeDexterityId))
|
||||||
|
<< FormatAttributeValue("Cha. ", ch.GetSocialAttributeValue(kSocialAttributeCharismaId))
|
||||||
|
<< FormatAttributeValue("Per. ", ch.GetMentalAttributeValue(kMentalAttributePerceptionId))
|
||||||
|
<< " ╠═══════════════════════════════════════╣" << endl;
|
||||||
|
cout << "║ " << FormatAttributeValue("Sta. ", ch.GetPhysicalAttributeValue(kPhysicalAttributeStaminaId))
|
||||||
|
<< FormatAttributeValue("Man. ", ch.GetSocialAttributeValue(kSocialAttributeManipulationId))
|
||||||
|
<< FormatAttributeValue("Wit. ", ch.GetMentalAttributeValue(kMentalAttributeWitsId))
|
||||||
|
<< " ║ Derangements: ║" << endl;
|
||||||
|
cout << "╠══════════════════════════════════════╣ " << MakeFitL(derangement_strings[0], kRightColumnWidth, '_')
|
||||||
|
<< " ║" << endl;
|
||||||
|
cout << "║ Disciplines: ║ " << MakeFitL(derangement_strings[1], kRightColumnWidth, '_')
|
||||||
|
<< " ║" << endl;
|
||||||
|
cout << "║ " << MakeFitL(discipline_strings[0], kLeftColumnWidth) << " ║ "
|
||||||
|
<< MakeFitL(derangement_strings[2], kRightColumnWidth, '_') << " ║" << endl;
|
||||||
|
cout << "║ " << MakeFitL(discipline_strings[1], kLeftColumnWidth) << " ║ "
|
||||||
|
<< MakeFitL(derangement_strings[3], kRightColumnWidth, '_') << " ║" << endl;
|
||||||
|
cout << "║ " << MakeFitL(discipline_strings[2], kLeftColumnWidth) << " ║ "
|
||||||
|
<< MakeFitL(derangement_strings[4], kRightColumnWidth, '_') << " ║" << endl;
|
||||||
|
cout << "╠══════════════════════════════════════╬═══════════════════════════════════════╣" << endl;
|
||||||
|
cout << "║ " << MakeFitL(ch.GetRoadName() + ": " + to_string(ch.GetRoadValue()), kLeftColumnWidth)
|
||||||
|
<< " ║ Nature: " << MakeFitL(GetArchetypeLabel(ch.natureId), 29) << " ║" << endl;
|
||||||
|
cout << "║ Willpower: " << MakeFitL(to_string(ch.willpower), 25)
|
||||||
|
<< " ║ Demeanor: " << MakeFitL(GetArchetypeLabel(ch.demeanorId), 27) << " ║" << endl;
|
||||||
|
cout << "╠══════════════════════════════════════╩═══════════════════════════════════════╣" << endl;
|
||||||
|
cout << "║ ║" << endl;
|
||||||
|
cout << "║ ║" << endl;
|
||||||
|
cout << "║ ║" << endl;
|
||||||
|
cout << "║ <<PRESS ANY KEY TO CONTINUE>> ║" << endl;
|
||||||
|
cout << "╚══════════════════════════════════════════════════════════════════════════════╝" << endl;
|
||||||
|
WaitForKeypress();
|
||||||
|
cout << "╔══════════════════════════════════════════════════════════════════════════════╗" << endl;
|
||||||
|
cout << "║ " << MakeFitC("Abilities", 76) << " ║" << endl;
|
||||||
|
cout << "║ " << MakeFitC("Talents", 24) << MakeFitC("Skills", 24) << MakeFitC("Knowledges", 24) << " ║" << endl;
|
||||||
|
for (index = 1; index <= 10; index++) {
|
||||||
|
cout << "║ " << FormatAbilityWithValue(GetTalentLabel(index), ch.GetTalentValue(index))
|
||||||
|
<< FormatAbilityWithValue(GetSkillLabel(index), ch.GetSkillValue(index))
|
||||||
|
<< FormatAbilityWithValue(GetKnowledgeLabel(index), ch.GetKnowledgeValue(index)) << " ║" << endl;
|
||||||
|
}
|
||||||
|
cout << "╠══════════════════════════════════════╦═══════════════════════════════════════╣" << endl;
|
||||||
|
cout << "║ " << MakeFitL("Backgrounds:", kLeftColumnWidth) << " ║ " << MakeFitL("Virtues:", kRightColumnWidth) << " ║"
|
||||||
|
<< endl;
|
||||||
|
cout << "║ " << MakeFitL(background_strings[0], kLeftColumnWidth) << " ║ "
|
||||||
|
<< MakeFitB("Conscience:", to_string(ch.conscience), kRightColumnWidth) << " ║" << endl;
|
||||||
|
cout << "║ " << MakeFitL(background_strings[1], kLeftColumnWidth) << " ║ "
|
||||||
|
<< MakeFitB("Self-Control:", to_string(ch.selfControl), kRightColumnWidth) << " ║" << endl;
|
||||||
|
cout << "║ " << MakeFitL(background_strings[2], kLeftColumnWidth) << " ║ "
|
||||||
|
<< MakeFitB("Courage:", to_string(ch.courage), kRightColumnWidth) << " ║" << endl;
|
||||||
|
cout << "║ " << MakeFitL(background_strings[3], kLeftColumnWidth) << " ║ " << MakeFitL("", kRightColumnWidth) << " ║"
|
||||||
|
<< endl;
|
||||||
|
cout << "║ " << MakeFitL(background_strings[4], kLeftColumnWidth) << " ║ " << MakeFitL("", kRightColumnWidth) << " ║"
|
||||||
|
<< endl;
|
||||||
|
cout << "╠══════════════════════════════════════╩═══════════════════════════════════════╣" << endl;
|
||||||
|
cout << "║ <<PRESS ANY KEY TO CONTINUE>> ║" << endl;
|
||||||
|
cout << "╚══════════════════════════════════════════════════════════════════════════════╝" << endl;
|
||||||
|
WaitForKeypress();
|
||||||
|
/*
|
||||||
|
╔══════════════════════════════════════╦═══════════════════════════════════════╗
|
||||||
|
║ Name: Ted ║ Gender: Female Generation: 13 ║
|
||||||
|
║ Clan: Ventrue ║ Age: 35 ║
|
||||||
|
╠══════════════════════════════════════╣ Player: Jeff ║
|
||||||
|
║ Attributes ║ Chronicle: Somesuch ║
|
||||||
|
║ Physical Social Mental ║ Haven: Mom's basement ║
|
||||||
|
║ Str. 1 App. 1 Int. 1 ║ Concept: Asshole ║
|
||||||
|
║ Dex. 1 Cha. 1 Per. 1 ╠═══════════════════════════════════════╣
|
||||||
|
║ Sta. 1 Man. 1 Wit. 1 ║ Derangements: ║
|
||||||
|
╠══════════════════════════════════════╣ _____________________________________ ║
|
||||||
|
║ Disciplines: ║ _____________________________________ ║
|
||||||
|
║ ____________________________________ ║ _____________________________________ ║
|
||||||
|
║ ____________________________________ ║ _____________________________________ ║
|
||||||
|
║ ____________________________________ ║ _____________________________________ ║
|
||||||
|
╠══════════════════════════════════════╬═══════════════════════════════════════╣
|
||||||
|
║ : 2 ║ Nature: Tyrant ║
|
||||||
|
║ Willpower: 1 ║ Demeanor: Traditionalist ║
|
||||||
|
╠══════════════════════════════════════╩═══════════════════════════════════════╣
|
||||||
|
║ ║
|
||||||
|
║ ║
|
||||||
|
║ ║
|
||||||
|
║ <<PRESS ANY KEY TO CONTINUE>> ║
|
||||||
|
╚══════════════════════════════════════════════════════════════════════════════╝
|
||||||
|
╔══════════════════════════════════════════════════════════════════════════════╗
|
||||||
|
║ Abilities ║
|
||||||
|
║ Talents Skills Knowledges ║
|
||||||
|
║ Acting: 0 Animal Ken: 0 Bureaucracy: 0 ║
|
||||||
|
║ Alertness: 0 Drive: 0 Computer: 0 ║
|
||||||
|
║ Athletics: 0 Etiquette: 0 Finance: 0 ║
|
||||||
|
║ Brawl: 0 Firearms: 0 Investigation:0 ║
|
||||||
|
║ Dodge: 0 Melee: 0 Law: 0 ║
|
||||||
|
║ Empathy: 0 Music: 0 Linguistics: 0 ║
|
||||||
|
║ Intimidation: 0 Repair: 0 Medicine: 0 ║
|
||||||
|
║ Leadership: 0 Security: 0 Occult: 0 ║
|
||||||
|
║ Streetwise: 0 Stealth: 0 Politics: 0 ║
|
||||||
|
║ Subterfuge: 0 Survival: 0 Science: 0 ║
|
||||||
|
╠══════════════════════════════════════╦═══════════════════════════════════════╣
|
||||||
|
║ Backgrounds: ║ Virtues: ║
|
||||||
|
║ ║ Conscience: 1 ║
|
||||||
|
║ ║ Self-Control: 1 ║
|
||||||
|
║ ║ Courage: 1 ║
|
||||||
|
║ ║ ║
|
||||||
|
║ ║ ║
|
||||||
|
╠══════════════════════════════════════╩═══════════════════════════════════════╣
|
||||||
|
║ <<PRESS ANY KEY TO CONTINUE>> ║
|
||||||
|
╚══════════════════════════════════════════════════════════════════════════════╝
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
void CharacterGenerator() {
|
||||||
|
CharacterType ch;
|
||||||
|
CGGetHeader(ch);
|
||||||
|
CGGetDisciplines(ch);
|
||||||
|
CGGetAttributes(ch);
|
||||||
|
CGGetBackgrounds(ch);
|
||||||
|
CGGetRoad(ch);
|
||||||
|
CGSpendVirtuePoints(ch);
|
||||||
|
CGGetDerangement(ch);
|
||||||
|
ch.generation = kInitialGeneration - ch.GetBackgroundValue(kBackgroundGenerationId);
|
||||||
|
ch.willpower = ch.courage;
|
||||||
|
ch.SetRoadValue(ch.conscience + ch.selfControl);
|
||||||
|
ch.bloodPool = GetRandomInt(1, 10);
|
||||||
|
CGSpendFreebiePoints(ch);
|
||||||
|
SaveCharacterSheet(ch);
|
||||||
|
ShowCharacterSheet(ch);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // End namespace SBF
|
||||||
24
sbf-cpp/CharacterGenerator.h
Normal file
24
sbf-cpp/CharacterGenerator.h
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
#ifndef CHARACTERGENERATOR_H__
|
||||||
|
#define CHARACTERGENERATOR_H__
|
||||||
|
#include "Character.h"
|
||||||
|
|
||||||
|
namespace SBF {
|
||||||
|
void CGGetAttributes(CharacterType& ch);
|
||||||
|
void CGGetBackgrounds(CharacterType& ch);
|
||||||
|
void CGGetDerangement(CharacterType& ch);
|
||||||
|
void CGGetDisciplines(CharacterType& ch);
|
||||||
|
void CGGetHeader(CharacterType& ch);
|
||||||
|
void CGGetRoad(CharacterType& ch);
|
||||||
|
void CGSpendAbilityPoint(CharacterType& ch);
|
||||||
|
void CGSpendAttributePoint(CharacterType& ch);
|
||||||
|
void CGSpendBackgroundPoint(CharacterType& ch);
|
||||||
|
void CGSpendDisciplinePoint(CharacterType& ch);
|
||||||
|
void CGSpendFreebiePoints(CharacterType& ch);
|
||||||
|
void CGSpendHumanityPoint(CharacterType& ch);
|
||||||
|
void CGSpendVirtuePoint(CharacterType& ch);
|
||||||
|
void CGSpendVirtuePoints(CharacterType& ch);
|
||||||
|
void CharacterGenerator();
|
||||||
|
void ShowCharacterSheet(CharacterType& ch);
|
||||||
|
void SaveCharacterSheet(CharacterType& ch);
|
||||||
|
} // namespace SBF
|
||||||
|
#endif // End !defined(CHARACTERGENERATOR_H__)
|
||||||
25
sbf-cpp/CharacterGenerator_test.cpp
Normal file
25
sbf-cpp/CharacterGenerator_test.cpp
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
#include "CharacterGenerator.h"
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <sstream>
|
||||||
|
#include <string>
|
||||||
|
#include <tuple>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "test.h"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
using namespace SBF;
|
||||||
|
using namespace Test;
|
||||||
|
using std::cout;
|
||||||
|
} // End namespace
|
||||||
|
|
||||||
|
int main(int argc, char* argv[]) {
|
||||||
|
TestResults results;
|
||||||
|
|
||||||
|
results.skip("// TODO: SBF::CharacterGenerator::*");
|
||||||
|
|
||||||
|
PrintResults(cout, results);
|
||||||
|
|
||||||
|
return results.failed() + results.errors();
|
||||||
|
}
|
||||||
@@ -12,10 +12,12 @@
|
|||||||
|
|
||||||
namespace SBF {
|
namespace SBF {
|
||||||
namespace {
|
namespace {
|
||||||
|
using std::cin;
|
||||||
using std::cout;
|
using std::cout;
|
||||||
using std::endl;
|
using std::endl;
|
||||||
using std::ostream;
|
using std::ostream;
|
||||||
using std::pair;
|
using std::pair;
|
||||||
|
using std::string;
|
||||||
using std::to_string;
|
using std::to_string;
|
||||||
using std::vector;
|
using std::vector;
|
||||||
} // End namespace
|
} // End namespace
|
||||||
@@ -289,4 +291,126 @@ bool MenuItem::operator!=(const MenuItem& other) {
|
|||||||
return !(*this == other);
|
return !(*this == other);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int GetChoice(int min, int max) {
|
||||||
|
int choice;
|
||||||
|
do {
|
||||||
|
choice = GetChoice();
|
||||||
|
} while (choice < min || choice > max);
|
||||||
|
return choice;
|
||||||
|
}
|
||||||
|
|
||||||
|
int GetChoice() {
|
||||||
|
int choice;
|
||||||
|
string line;
|
||||||
|
bool has_error;
|
||||||
|
do {
|
||||||
|
has_error = false;
|
||||||
|
getline(cin, line);
|
||||||
|
try {
|
||||||
|
if (line.empty()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
choice = stoi(line);
|
||||||
|
} catch (...) {
|
||||||
|
has_error = true;
|
||||||
|
}
|
||||||
|
} while (has_error);
|
||||||
|
return choice;
|
||||||
|
}
|
||||||
|
|
||||||
|
int GetMenuChoice(vector<MenuItem> menu_items, MenuStyle style) {
|
||||||
|
int choice;
|
||||||
|
while (true) {
|
||||||
|
choice = GetChoice();
|
||||||
|
if (style.show_random && choice == style.random_item_id) {
|
||||||
|
return choice;
|
||||||
|
}
|
||||||
|
if (style.show_cancel && choice == style.cancel_item_id) {
|
||||||
|
return choice;
|
||||||
|
}
|
||||||
|
for (MenuItem item : menu_items) {
|
||||||
|
if (item.id == choice) {
|
||||||
|
return choice;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
string GetString(string prompt) {
|
||||||
|
cout << prompt << endl;
|
||||||
|
string response;
|
||||||
|
getline(cin, response);
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ChooseStringId(vector<string> labels, MenuStyle style, const string& prompt) {
|
||||||
|
MaybeClearScreen();
|
||||||
|
vector<MenuItem> menu_items = BuildMenu(labels);
|
||||||
|
style.Adjust(menu_items);
|
||||||
|
cout << prompt << endl;
|
||||||
|
PrintMenu(cout, menu_items, style);
|
||||||
|
int choice = GetMenuChoice(menu_items, style);
|
||||||
|
if (choice == style.random_item_id) {
|
||||||
|
choice = GetRandomMenuItemId(menu_items);
|
||||||
|
}
|
||||||
|
return choice;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ChooseYesOrNo(string prompt) {
|
||||||
|
MenuStyle style;
|
||||||
|
style.show_random = false;
|
||||||
|
vector<MenuItem> menu_items = BuildMenu({"Yes", "No"});
|
||||||
|
style.Adjust(menu_items, true);
|
||||||
|
cout << prompt << endl;
|
||||||
|
PrintMenu(cout, menu_items, style);
|
||||||
|
int choice = GetMenuChoice(menu_items, style);
|
||||||
|
if (choice == style.random_item_id) {
|
||||||
|
choice = GetRandomMenuItemId(menu_items);
|
||||||
|
}
|
||||||
|
return choice == 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ChooseStringIdWithValues(vector<string> labels, vector<int> values, MenuStyle style, const string& prompt) {
|
||||||
|
MaybeClearScreen();
|
||||||
|
vector<MenuItem> menu_items = BuildMenuWithValues(labels, values);
|
||||||
|
style.Adjust(menu_items, false);
|
||||||
|
cout << prompt << endl;
|
||||||
|
PrintMenu(cout, menu_items, style);
|
||||||
|
int choice = GetMenuChoice(menu_items, style);
|
||||||
|
if (choice == style.random_item_id) {
|
||||||
|
choice = GetRandomMenuItemId(menu_items);
|
||||||
|
}
|
||||||
|
return choice;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ChooseStringIdWithColors(vector<string> labels, vector<uint8_t> colors, MenuStyle style, const string& prompt) {
|
||||||
|
MaybeClearScreen();
|
||||||
|
// Check array bounds
|
||||||
|
vector<MenuItem> menu_items = BuildMenuWithColors(labels, colors);
|
||||||
|
style.Adjust(menu_items);
|
||||||
|
cout << prompt << endl;
|
||||||
|
PrintMenu(cout, menu_items, style);
|
||||||
|
int choice = GetMenuChoice(menu_items, style);
|
||||||
|
if (choice == style.random_item_id) {
|
||||||
|
choice = GetRandomMenuItemId(menu_items);
|
||||||
|
}
|
||||||
|
return choice;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ChooseMenuItemId(vector<MenuItem> menu_items, MenuStyle style, const string& prompt, bool ignore_value) {
|
||||||
|
MaybeClearScreen();
|
||||||
|
style.Adjust(menu_items, ignore_value);
|
||||||
|
cout << prompt << endl;
|
||||||
|
PrintMenu(cout, menu_items, style);
|
||||||
|
int choice = GetMenuChoice(menu_items, style);
|
||||||
|
if (choice == style.random_item_id) {
|
||||||
|
choice = GetRandomMenuItemId(menu_items);
|
||||||
|
}
|
||||||
|
return choice;
|
||||||
|
}
|
||||||
|
|
||||||
|
void WaitForKeypress() {
|
||||||
|
// TODO: Make this press any key to continue.
|
||||||
|
}
|
||||||
|
|
||||||
} // End namespace SBF
|
} // End namespace SBF
|
||||||
|
|||||||
@@ -27,8 +27,6 @@ class MenuItem;
|
|||||||
* @{
|
* @{
|
||||||
*/
|
*/
|
||||||
namespace SBF {
|
namespace SBF {
|
||||||
using std::string;
|
|
||||||
|
|
||||||
class MenuStyleBuilder {
|
class MenuStyleBuilder {
|
||||||
public:
|
public:
|
||||||
MenuStyleBuilder();
|
MenuStyleBuilder();
|
||||||
@@ -59,15 +57,15 @@ class MenuStyle {
|
|||||||
int label_width;
|
int label_width;
|
||||||
int value_width;
|
int value_width;
|
||||||
int screen_width;
|
int screen_width;
|
||||||
string random_item_name;
|
std::string random_item_name;
|
||||||
int random_item_id;
|
int random_item_id;
|
||||||
uint8_t random_item_color;
|
uint8_t random_item_color;
|
||||||
string cancel_item_name;
|
std::string cancel_item_name;
|
||||||
int cancel_item_id;
|
int cancel_item_id;
|
||||||
uint8_t cancel_item_color;
|
uint8_t cancel_item_color;
|
||||||
string id_label_separator;
|
std::string id_label_separator;
|
||||||
string label_value_separator;
|
std::string label_value_separator;
|
||||||
string menu_item_spacer;
|
std::string menu_item_spacer;
|
||||||
bool show_random;
|
bool show_random;
|
||||||
bool show_cancel;
|
bool show_cancel;
|
||||||
bool use_colors;
|
bool use_colors;
|
||||||
@@ -82,7 +80,7 @@ class MenuItem {
|
|||||||
friend std::ostream& operator<<(std::ostream& os, const MenuItem& item);
|
friend std::ostream& operator<<(std::ostream& os, const MenuItem& item);
|
||||||
bool operator==(const MenuItem& other);
|
bool operator==(const MenuItem& other);
|
||||||
bool operator!=(const MenuItem& other);
|
bool operator!=(const MenuItem& other);
|
||||||
string label;
|
std::string label;
|
||||||
int id;
|
int id;
|
||||||
int value;
|
int value;
|
||||||
uint8_t color;
|
uint8_t color;
|
||||||
@@ -92,19 +90,41 @@ class MenuItem {
|
|||||||
|
|
||||||
// TODO: Make a menu class to hold GetRandomMenuItemId, the various BuildMenu* methods, and possibly PrintMenu.
|
// TODO: Make a menu class to hold GetRandomMenuItemId, the various BuildMenu* methods, and possibly PrintMenu.
|
||||||
int GetRandomMenuItemId(std::vector<MenuItem> items);
|
int GetRandomMenuItemId(std::vector<MenuItem> items);
|
||||||
std::vector<MenuItem> BuildMenu(std::vector<string> labels);
|
std::vector<MenuItem> BuildMenu(std::vector<std::string> labels);
|
||||||
std::vector<MenuItem> BuildMenuWithValues(std::vector<std::pair<std::string, int>> items);
|
std::vector<MenuItem> BuildMenuWithValues(std::vector<std::pair<std::string, int>> items);
|
||||||
std::vector<MenuItem> BuildMenuWithValues(std::vector<std::string> labels, std::vector<int> values);
|
std::vector<MenuItem> BuildMenuWithValues(std::vector<std::string> labels, std::vector<int> values);
|
||||||
std::vector<MenuItem> BuildMenuWithColors(std::vector<std::pair<std::string, uint8_t>> items);
|
std::vector<MenuItem> BuildMenuWithColors(std::vector<std::pair<std::string, uint8_t>> items);
|
||||||
std::vector<MenuItem> BuildMenuWithColors(std::vector<std::string> labels, std::vector<uint8_t> colors);
|
std::vector<MenuItem> BuildMenuWithColors(std::vector<std::string> labels, std::vector<uint8_t> colors);
|
||||||
std::ostream& PrintMenu(std::ostream& os, std::vector<MenuItem> items, MenuStyle style);
|
std::ostream& PrintMenu(std::ostream& os, std::vector<MenuItem> items, MenuStyle style);
|
||||||
string GetTitle(MenuItem item, MenuStyle style);
|
std::string GetTitle(MenuItem item, MenuStyle style);
|
||||||
string GetTitleWithoutValue(MenuItem item, MenuStyle style);
|
std::string GetTitleWithoutValue(MenuItem item, MenuStyle style);
|
||||||
std::ostream& PrintWithMaybeColor(std::ostream& os,
|
std::ostream& PrintWithMaybeColor(std::ostream& os,
|
||||||
const std::string& text,
|
const std::string& text,
|
||||||
uint8_t text_color = kColorDefaultForeground,
|
uint8_t text_color = kColorDefaultForeground,
|
||||||
bool use_colors = false);
|
bool use_colors = false);
|
||||||
|
int ChooseStringId(std::vector<std::string> labels, MenuStyle style, const std::string& prompt);
|
||||||
|
bool ChooseYesOrNo(std::string prompt);
|
||||||
|
int GetChoice(int min, int max);
|
||||||
|
int GetChoice();
|
||||||
|
int GetMenuChoice(std::vector<MenuItem> menu_items, MenuStyle style);
|
||||||
|
std::string GetString(std::string prompt);
|
||||||
|
int ChooseStringIdWithValues(std::vector<std::string> labels,
|
||||||
|
std::vector<int> values,
|
||||||
|
MenuStyle style,
|
||||||
|
const std::string& prompt);
|
||||||
|
int ChooseMenuItemId(std::vector<MenuItem> menu_items, MenuStyle style, const std::string& prompt, bool ignore_value);
|
||||||
|
int ChooseStringIdWithColors(std::vector<std::string> labels,
|
||||||
|
std::vector<uint8_t> colors,
|
||||||
|
MenuStyle style,
|
||||||
|
const std::string& prompt);
|
||||||
|
void WaitForKeypress();
|
||||||
|
|
||||||
|
/// Clears the screen if not a debug build.
|
||||||
|
inline void MaybeClearScreen() {
|
||||||
|
#if !defined(DEBUG)
|
||||||
|
cout << "\033[1;1H\033[2J";
|
||||||
|
#endif
|
||||||
|
}
|
||||||
} // End namespace SBF
|
} // End namespace SBF
|
||||||
|
|
||||||
/** @}*/
|
/** @}*/
|
||||||
|
|||||||
@@ -1,18 +1,48 @@
|
|||||||
## Why do I have to cast std::nullopt to a specific optional type when calling a templated function i.e.
|
|
||||||
|
|
||||||
```c++
|
|
||||||
template<typename T>
|
|
||||||
void DoSomething(std::optional<T> = std::nullopt, bool report_errors = false);
|
|
||||||
```
|
|
||||||
|
|
||||||
```text
|
|
||||||
DoSomething() works fine.
|
|
||||||
DoSomething(std::nullopt, true) no matching function for call to 'DoSomething' Do_Something<std::optional<string>, bool>(
|
|
||||||
```
|
|
||||||
|
|
||||||
## Which is uglier
|
## Which is uglier
|
||||||
```c++
|
```c++
|
||||||
(*after_each)()
|
(*after_each)()
|
||||||
after_each->operator()()
|
after_each->operator()()
|
||||||
after_each.value()();
|
after_each.value()();
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## What do all of these command line args do and which do I really want?
|
||||||
|
```bash
|
||||||
|
clang \
|
||||||
|
-xc++ \
|
||||||
|
-D_FORTIFY_SOURCE=1 \
|
||||||
|
-fstack-protector \
|
||||||
|
-fcolor-diagnostics \
|
||||||
|
-Wall \
|
||||||
|
-Wthread-safety \
|
||||||
|
-Wself-assign \
|
||||||
|
-fno-omit-frame-pointer \
|
||||||
|
-O0 \
|
||||||
|
-DDEBUG \
|
||||||
|
-std=c++11 \
|
||||||
|
-iquote \
|
||||||
|
. \
|
||||||
|
-iquote \
|
||||||
|
bazel-out/darwin_arm64-fastbuild/bin \
|
||||||
|
-MD \
|
||||||
|
-MF \
|
||||||
|
bazel-out/darwin_arm64-fastbuild/bin/sbf-cpp/_objs/menus/Menus.d \
|
||||||
|
-DBAZEL_CURRENT_REPOSITORY="" \
|
||||||
|
-frandom-seed=bazel-out/darwin_arm64-fastbuild/bin/sbf-cpp/_objs/menus/Menus.o \
|
||||||
|
-isysroot \
|
||||||
|
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk \
|
||||||
|
-F/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks \
|
||||||
|
-F/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/Library/Frameworks \
|
||||||
|
-no-canonical-prefixes \
|
||||||
|
-pthread \
|
||||||
|
-no-canonical-prefixes \
|
||||||
|
-Wno-builtin-macro-redefined \
|
||||||
|
-D__DATE__="redacted" \
|
||||||
|
-D__TIMESTAMP__="redacted" \
|
||||||
|
-D__TIME__="redacted" \
|
||||||
|
-target \
|
||||||
|
arm64-apple-macosx13.3 \
|
||||||
|
-c \
|
||||||
|
sbf-cpp/Menus.cpp \
|
||||||
|
-o \
|
||||||
|
bazel-out/darwin_arm64-fastbuild/bin/sbf-cpp/_objs/menus/Menus.o
|
||||||
|
```
|
||||||
|
|||||||
477
sbf-cpp/sbf.cpp
477
sbf-cpp/sbf.cpp
@@ -1,4 +1,5 @@
|
|||||||
#include "sbf-cpp/Virtues.h"
|
#include "sbf.h"
|
||||||
|
|
||||||
#define _XOPEN_SOURCE_EXTENDED
|
#define _XOPEN_SOURCE_EXTENDED
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
@@ -10,53 +11,33 @@
|
|||||||
#include "Attributes.h"
|
#include "Attributes.h"
|
||||||
#include "Backgrounds.h"
|
#include "Backgrounds.h"
|
||||||
#include "Character.h"
|
#include "Character.h"
|
||||||
|
#include "CharacterGenerator.h"
|
||||||
#include "Clans.h"
|
#include "Clans.h"
|
||||||
#include "Disciplines.h"
|
#include "Disciplines.h"
|
||||||
|
#include "Freebies.h"
|
||||||
#include "Genders.h"
|
#include "Genders.h"
|
||||||
#include "Menus.h"
|
#include "Menus.h"
|
||||||
#include "Random.h"
|
#include "Random.h"
|
||||||
#include "Utils.h"
|
#include "Utils.h"
|
||||||
#include "sbf.h"
|
#include "Virtues.h"
|
||||||
|
|
||||||
#define KEY_ESCAPE 0033
|
#define KEY_ESCAPE 0033
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
using namespace SBF;
|
using namespace SBF;
|
||||||
using std::cin;
|
|
||||||
using std::cout;
|
using std::cout;
|
||||||
using std::endl;
|
using std::endl;
|
||||||
using std::to_string;
|
|
||||||
using std::vector;
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
void CGGetAttributes(CharacterType& ch);
|
|
||||||
void CGGetBackgrounds(CharacterType& ch);
|
|
||||||
void CGGetDerangement(CharacterType& ch);
|
|
||||||
void CGGetDisciplines(CharacterType& ch);
|
|
||||||
void CGGetHeader(CharacterType& ch);
|
|
||||||
void CGGetRoad(CharacterType& ch);
|
|
||||||
void CGSpendFreebiePoints(CharacterType& ch);
|
|
||||||
void CGSpendVirtuePoints(CharacterType& ch);
|
|
||||||
void CharacterGenerator();
|
|
||||||
void CharacterGeneratorForDummies();
|
void CharacterGeneratorForDummies();
|
||||||
int ChooseStringId(vector<string> labels, MenuStyle style, const string& prompt);
|
|
||||||
void CombatComputer();
|
void CombatComputer();
|
||||||
void DiceRoller();
|
void DiceRoller();
|
||||||
int GetChoice(int min, int max);
|
|
||||||
int GetChoice();
|
|
||||||
int GetMenuChoice(vector<MenuItem> menu_items, MenuStyle style);
|
|
||||||
string GetString(string prompt);
|
|
||||||
void MainMenu();
|
void MainMenu();
|
||||||
void MaybeClearScreen();
|
|
||||||
void RandomCharacterGenerator();
|
void RandomCharacterGenerator();
|
||||||
void SaveCharacterSheet(CharacterType& ch);
|
void SaveCharacterSheet(CharacterType& ch);
|
||||||
void ShowCharacterSheet(CharacterType& ch);
|
void ShowCharacterSheet(CharacterType& ch);
|
||||||
void ShowSplashScreen();
|
void ShowSplashScreen();
|
||||||
void VehicleGenerator();
|
void VehicleGenerator();
|
||||||
void WaitForKeypress();
|
|
||||||
int ChooseStringIdWithValues(vector<string> labels, vector<int> values, MenuStyle style, const string& prompt);
|
|
||||||
int ChooseMenuItemId(vector<MenuItem> menu_items, MenuStyle style, const string& prompt, bool ignore_value);
|
|
||||||
int ChooseStringIdWithColors(vector<string> labels, vector<uint8_t> colors, MenuStyle style, const string& prompt);
|
|
||||||
|
|
||||||
int main(int argc, char* argv[]) {
|
int main(int argc, char* argv[]) {
|
||||||
setlocale(LC_ALL, "");
|
setlocale(LC_ALL, "");
|
||||||
@@ -65,33 +46,6 @@ int main(int argc, char* argv[]) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int GetChoice(int min, int max) {
|
|
||||||
int choice;
|
|
||||||
do {
|
|
||||||
choice = GetChoice();
|
|
||||||
} while (choice < min || choice > max);
|
|
||||||
return choice;
|
|
||||||
}
|
|
||||||
|
|
||||||
int GetChoice() {
|
|
||||||
int choice;
|
|
||||||
string line;
|
|
||||||
bool has_error;
|
|
||||||
do {
|
|
||||||
has_error = false;
|
|
||||||
getline(cin, line);
|
|
||||||
try {
|
|
||||||
if (line.empty()) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
choice = stoi(line);
|
|
||||||
} catch (...) {
|
|
||||||
has_error = true;
|
|
||||||
}
|
|
||||||
} while (has_error);
|
|
||||||
return choice;
|
|
||||||
}
|
|
||||||
|
|
||||||
void MainMenu() {
|
void MainMenu() {
|
||||||
int choice = 0;
|
int choice = 0;
|
||||||
do {
|
do {
|
||||||
@@ -143,13 +97,6 @@ void MainMenu() {
|
|||||||
} while (choice != 0);
|
} while (choice != 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Clears the screen if not a debug build.
|
|
||||||
inline void MaybeClearScreen() {
|
|
||||||
#if !defined(DEBUG)
|
|
||||||
cout << "\033[1;1H\033[2J";
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void ShowSplashScreen() {
|
void ShowSplashScreen() {
|
||||||
cout << "Welcome to Tom's Storyteller's Best Friend. This is a program that is meant to" << endl
|
cout << "Welcome to Tom's Storyteller's Best Friend. This is a program that is meant to" << endl
|
||||||
<< "aid storytellers in running Vampire: the Masquerade Chronicles and Vampire: the" << endl
|
<< "aid storytellers in running Vampire: the Masquerade Chronicles and Vampire: the" << endl
|
||||||
@@ -160,28 +107,6 @@ void ShowSplashScreen() {
|
|||||||
WaitForKeypress();
|
WaitForKeypress();
|
||||||
}
|
}
|
||||||
|
|
||||||
void WaitForKeypress() {
|
|
||||||
// TODO: Make this press any key to continue.
|
|
||||||
}
|
|
||||||
|
|
||||||
void CharacterGenerator() {
|
|
||||||
CharacterType ch;
|
|
||||||
CGGetHeader(ch);
|
|
||||||
CGGetDisciplines(ch);
|
|
||||||
CGGetAttributes(ch);
|
|
||||||
CGGetBackgrounds(ch);
|
|
||||||
CGGetRoad(ch);
|
|
||||||
CGSpendVirtuePoints(ch);
|
|
||||||
CGGetDerangement(ch);
|
|
||||||
ch.generation = kInitialGeneration - ch.GetBackgroundValue(kBackgroundGenerationId);
|
|
||||||
ch.willpower = ch.courage;
|
|
||||||
ch.roadValue = ch.conscience + ch.selfControl;
|
|
||||||
ch.bloodPool = GetRandomInt(1, 10);
|
|
||||||
CGSpendFreebiePoints(ch);
|
|
||||||
SaveCharacterSheet(ch);
|
|
||||||
ShowCharacterSheet(ch);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CharacterGeneratorForDummies() {
|
void CharacterGeneratorForDummies() {
|
||||||
// TODO: Fill in this function.
|
// TODO: Fill in this function.
|
||||||
cout << "// TODO: CharacterGeneratorForDummies()" << endl;
|
cout << "// TODO: CharacterGeneratorForDummies()" << endl;
|
||||||
@@ -206,395 +131,3 @@ void VehicleGenerator() {
|
|||||||
// TODO: Fill in this function.
|
// TODO: Fill in this function.
|
||||||
cout << "// TODO: VehicleGenerator()" << endl;
|
cout << "// TODO: VehicleGenerator()" << endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CGGetHeader(CharacterType& ch) {
|
|
||||||
MaybeClearScreen();
|
|
||||||
MenuStyle ms;
|
|
||||||
ch.name = GetString("What is the character's name?");
|
|
||||||
ch.player = GetString("Who is the player?");
|
|
||||||
ch.chronicle = GetString("What chronicle is the character going to be used for?");
|
|
||||||
ch.haven = GetString("What is the character's Haven?");
|
|
||||||
ch.concept = GetString("What is the character's concept?");
|
|
||||||
ch.age = GetString("How old is the character?");
|
|
||||||
vector<string> genders;
|
|
||||||
FillGenderLabels(genders);
|
|
||||||
ch.genderId = ChooseStringId(genders, ms, "What is the character's gender?");
|
|
||||||
vector<string> clans;
|
|
||||||
FillClanLabels(clans);
|
|
||||||
ch.clanId = ChooseStringId(clans, ms, "What clan is the character from?");
|
|
||||||
vector<string> archetypes;
|
|
||||||
FillArchetypeLabels(archetypes);
|
|
||||||
ch.natureId = ChooseStringId(archetypes, ms, "What is the character's nature?");
|
|
||||||
ch.demeanorId = ChooseStringId(archetypes, ms, "What is the character's demeanor?");
|
|
||||||
}
|
|
||||||
|
|
||||||
void CGGetDisciplines(CharacterType& ch) {
|
|
||||||
MenuStyle ms;
|
|
||||||
int discipline_points = GetDisciplinePoints();
|
|
||||||
vector<int> discipline_values;
|
|
||||||
vector<string> discipline_labels;
|
|
||||||
FillDisciplineLabels(discipline_labels);
|
|
||||||
while (discipline_points > 0) {
|
|
||||||
MaybeClearScreen();
|
|
||||||
discipline_values = ch.GetDisciplineValues();
|
|
||||||
int discipline_id = ChooseStringIdWithValues(
|
|
||||||
discipline_labels,
|
|
||||||
discipline_values,
|
|
||||||
ms,
|
|
||||||
"Which discipline do you want to spend 1 of your " + to_string(discipline_points) + " points on?");
|
|
||||||
ch.SetDisciplineValue(discipline_id, ch.GetDisciplineValue(discipline_id) + 1);
|
|
||||||
discipline_points--;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void CGGetAttributes(CharacterType& ch) {
|
|
||||||
MenuStyle ms_without_values;
|
|
||||||
MenuStyle ms_with_values;
|
|
||||||
// indexed by group_id - 1 holds the rank_id
|
|
||||||
vector<int> attribute_ranks(kAttributeGroupsCount);
|
|
||||||
|
|
||||||
// Attribute groups menu (physical/social/mental)
|
|
||||||
vector<MenuItem> attribute_groups_menu_items;
|
|
||||||
for (size_t i = 1; i <= kAttributeGroupsCount; i++) {
|
|
||||||
attribute_groups_menu_items.push_back(MenuItem(GetAttributeGroupLabel(i), i));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Choose attribute group priorities.
|
|
||||||
int group_sum = 0;
|
|
||||||
int rank_sum = 1;
|
|
||||||
for (size_t i = 1; i < kAttributeGroupsCount; i++) {
|
|
||||||
int next_group_id = ChooseMenuItemId(attribute_groups_menu_items,
|
|
||||||
ms_without_values,
|
|
||||||
"Choose your " + ToLower(GetRank(i).label) + " attribute?",
|
|
||||||
true);
|
|
||||||
int next_group_index = next_group_id - 1;
|
|
||||||
attribute_groups_menu_items.at(next_group_index).is_visible = false;
|
|
||||||
attribute_ranks[next_group_index] = i;
|
|
||||||
rank_sum += i + 1;
|
|
||||||
group_sum += next_group_id;
|
|
||||||
}
|
|
||||||
|
|
||||||
// General formula for last choice given 1 to count based indexing is this. (Sum from 1 to count) - (Sum of all
|
|
||||||
// previous choice IDs). Sum(1..AllAttributesCount)-Sum(Choice[1..Choice[AllAttributesCount-1]).
|
|
||||||
int last_group = rank_sum - group_sum;
|
|
||||||
attribute_ranks[last_group - 1] = kAttributeGroupsCount;
|
|
||||||
|
|
||||||
// Spend attribute points
|
|
||||||
for (int group_id = 1; group_id <= kAttributeGroupsCount; group_id++) {
|
|
||||||
int group_index = group_id - 1;
|
|
||||||
vector<string> attribute_labels = GetAttributeLabelsInGroup(group_id);
|
|
||||||
int rank_id = attribute_ranks.at(group_index);
|
|
||||||
int attribute_points = GetAttributePointsForRank(rank_id);
|
|
||||||
while (attribute_points > 0) {
|
|
||||||
vector<int> values = ch.GetAttributeValuesInGroup(group_id);
|
|
||||||
string prompt = "Which " + ToLower(GetAttributeGroupLabel(group_id))
|
|
||||||
+ " attribute do you want to spend 1 of your " + to_string(attribute_points) + " points on?";
|
|
||||||
int attribute_id = ChooseStringIdWithValues(attribute_labels, values, ms_with_values, prompt);
|
|
||||||
ch.SetAttributeValue(group_id, attribute_id, ch.GetAttributeValue(group_id, attribute_id) + 1);
|
|
||||||
attribute_points--;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void CGGetBackgrounds(CharacterType& ch) {
|
|
||||||
// Spend background points
|
|
||||||
MenuStyle ms;
|
|
||||||
int background_points = GetBackgroundPoints();
|
|
||||||
vector<string> background_labels = GetBackgroundLabels();
|
|
||||||
while (background_points > 0) {
|
|
||||||
MaybeClearScreen();
|
|
||||||
vector<int> background_values = ch.GetBackgroundValues();
|
|
||||||
int background_id = ChooseStringIdWithValues(
|
|
||||||
background_labels,
|
|
||||||
background_values,
|
|
||||||
ms,
|
|
||||||
"Which background do you want to spend 1 of your " + to_string(background_points) + " points on?");
|
|
||||||
ch.SetBackgroundValue(background_id, ch.GetBackgroundValue(background_id) + 1);
|
|
||||||
background_points--;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void CGGetRoad(CharacterType& ch) {
|
|
||||||
// TODO: Update this to actually pick a road for VtDA.
|
|
||||||
ch.roadName = "Humanity";
|
|
||||||
}
|
|
||||||
|
|
||||||
void CGSpendVirtuePoints(CharacterType& ch) {
|
|
||||||
// Spend virtue points
|
|
||||||
MenuStyle ms;
|
|
||||||
int virtue_points = GetVirtuePoints();
|
|
||||||
vector<string> labels = GetVirtueLabels();
|
|
||||||
while (virtue_points > 0) {
|
|
||||||
vector<int> values = ch.GetVirtueValues();
|
|
||||||
int virtue_id = ChooseStringIdWithValues(
|
|
||||||
labels, values, ms, "Which virtue do you want to spend 1 of your " + to_string(virtue_points) + " points on?");
|
|
||||||
ch.SetVirtueValue(virtue_id, ch.GetVirtueValue(virtue_id) + 1);
|
|
||||||
virtue_points--;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void CGGetDerangement(CharacterType& ch) {
|
|
||||||
if (ch.clanId == kClanMalkavian) {
|
|
||||||
// If the clan is malkavian then pick a derangement.
|
|
||||||
MenuStyle ms;
|
|
||||||
ms.use_colors = true;
|
|
||||||
ch.derangementId =
|
|
||||||
ChooseStringIdWithColors(GetDerangementLabels(), GetDerangementColors(), ms, "Which derangement do you want?");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void CGSpendFreebiePoints(CharacterType& ch) {
|
|
||||||
// TODO: Fill this in.
|
|
||||||
cout << "// TODO: CGSpendFreebiePoints(CharacterType&)" << endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
string FormatAttributeValue(const string& label, int value) {
|
|
||||||
return MakeFitC(label + MakeFitL(to_string(value), 2), 12);
|
|
||||||
}
|
|
||||||
|
|
||||||
string FormatAbilityWithValue(const string& label, int value) {
|
|
||||||
return MakeFitC(MakeFitL(label + ":", 14) + to_string(value), 24);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SaveCharacterSheet(CharacterType& ch) {
|
|
||||||
// TODO: Fill this in.
|
|
||||||
cout << "// TODO: SaveCharacterSheet(CharacterType&)" << endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ShowCharacterSheet(CharacterType& ch) {
|
|
||||||
const int kLeftColumnWidth = 36;
|
|
||||||
const int kRightColumnWidth = 37;
|
|
||||||
vector<string> discipline_strings;
|
|
||||||
size_t index;
|
|
||||||
for (index = 1; index <= kDisciplinesCount; index++) {
|
|
||||||
int value = ch.GetDisciplineValue(index);
|
|
||||||
if (value > 0) {
|
|
||||||
string suffix = "";
|
|
||||||
if (value > 1) {
|
|
||||||
suffix = " x" + to_string(value);
|
|
||||||
}
|
|
||||||
discipline_strings.push_back(GetDisciplineLabel(index) + suffix);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
while (discipline_strings.size() <= 3) {
|
|
||||||
discipline_strings.push_back(RepeatChar(kLeftColumnWidth, '_'));
|
|
||||||
}
|
|
||||||
vector<string> background_strings(5);
|
|
||||||
for (index = 1; index <= kBackgroundsCount; index++) {
|
|
||||||
int value = ch.GetBackgroundValue(index);
|
|
||||||
if (value > 0) {
|
|
||||||
string suffix = "";
|
|
||||||
if (value > 1) {
|
|
||||||
suffix = " x" + to_string(value);
|
|
||||||
}
|
|
||||||
background_strings.push_back(GetBackgroundLabel(index) + suffix);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
while (background_strings.size() <= 5) {
|
|
||||||
background_strings.push_back(RepeatChar(kLeftColumnWidth, '_'));
|
|
||||||
}
|
|
||||||
string all_derangements_line = ch.GetAllDerangementsLine();
|
|
||||||
vector<string> derangement_strings = WordWrap(all_derangements_line, kLeftColumnWidth);
|
|
||||||
while (derangement_strings.size() <= 5) {
|
|
||||||
derangement_strings.push_back(RepeatChar(kLeftColumnWidth, '_'));
|
|
||||||
}
|
|
||||||
MaybeClearScreen();
|
|
||||||
cout << "╔══════════════════════════════════════╦═══════════════════════════════════════╗" << endl;
|
|
||||||
cout << "║ Name: " << MakeFitL(ch.name, 30) << " ║ Gender: " << MakeFitL(GetGenderLabel(ch.genderId), 14)
|
|
||||||
<< " Generation: " << MakeFitR(to_string(ch.generation), 2) << " ║" << endl;
|
|
||||||
cout << "║ Clan: " << MakeFitL(GetClanLabel(ch.clanId), 30) << " ║ Age: " << MakeFitL(ch.age, 32) << " ║" << endl;
|
|
||||||
cout << "╠══════════════════════════════════════╣ Player: " << MakeFitL(ch.player, 29) << " ║" << endl;
|
|
||||||
cout << "║ Attributes ║ Chronicle: " << MakeFitL(ch.chronicle, 26) << " ║" << endl;
|
|
||||||
cout << "║ " << MakeFitC("Physical", 12) << MakeFitC("Social", 12) << MakeFitC("Mental", 12)
|
|
||||||
<< " ║ Haven: " << MakeFitL(ch.haven, 30) + " ║" << endl;
|
|
||||||
cout << "║ " << FormatAttributeValue("Str. ", ch.GetPhysicalAttributeValue(kPhysicalAttributeStrengthId))
|
|
||||||
<< FormatAttributeValue("App. ", ch.GetSocialAttributeValue(kSocialAttributeAppearanceId))
|
|
||||||
<< FormatAttributeValue("Int. ", ch.GetMentalAttributeValue(kMentalAttributeIntelligenceId))
|
|
||||||
<< " ║ Concept: " << MakeFitL(ch.concept, 28) << " ║" << endl;
|
|
||||||
cout << "║ " << FormatAttributeValue("Dex. ", ch.GetPhysicalAttributeValue(kPhysicalAttributeDexterityId))
|
|
||||||
<< FormatAttributeValue("Cha. ", ch.GetSocialAttributeValue(kSocialAttributeCharismaId))
|
|
||||||
<< FormatAttributeValue("Per. ", ch.GetMentalAttributeValue(kMentalAttributePerceptionId))
|
|
||||||
<< " ╠═══════════════════════════════════════╣" << endl;
|
|
||||||
cout << "║ " << FormatAttributeValue("Sta. ", ch.GetPhysicalAttributeValue(kPhysicalAttributeStaminaId))
|
|
||||||
<< FormatAttributeValue("Man. ", ch.GetSocialAttributeValue(kSocialAttributeManipulationId))
|
|
||||||
<< FormatAttributeValue("Wit. ", ch.GetMentalAttributeValue(kMentalAttributeWitsId))
|
|
||||||
<< " ║ Derangements: ║" << endl;
|
|
||||||
cout << "╠══════════════════════════════════════╣ " << MakeFitL(derangement_strings[0], kRightColumnWidth, '_')
|
|
||||||
<< " ║" << endl;
|
|
||||||
cout << "║ Disciplines: ║ " << MakeFitL(derangement_strings[1], kRightColumnWidth, '_')
|
|
||||||
<< " ║" << endl;
|
|
||||||
cout << "║ " << MakeFitL(discipline_strings[0], kLeftColumnWidth) << " ║ "
|
|
||||||
<< MakeFitL(derangement_strings[2], kRightColumnWidth, '_') << " ║" << endl;
|
|
||||||
cout << "║ " << MakeFitL(discipline_strings[1], kLeftColumnWidth) << " ║ "
|
|
||||||
<< MakeFitL(derangement_strings[3], kRightColumnWidth, '_') << " ║" << endl;
|
|
||||||
cout << "║ " << MakeFitL(discipline_strings[2], kLeftColumnWidth) << " ║ "
|
|
||||||
<< MakeFitL(derangement_strings[4], kRightColumnWidth, '_') << " ║" << endl;
|
|
||||||
cout << "╠══════════════════════════════════════╬═══════════════════════════════════════╣" << endl;
|
|
||||||
cout << "║ " << MakeFitL(ch.roadName + ": " + to_string(ch.roadValue), kLeftColumnWidth)
|
|
||||||
<< " ║ Nature: " << MakeFitL(GetArchetypeLabel(ch.natureId), 29) << " ║" << endl;
|
|
||||||
cout << "║ Willpower: " << MakeFitL(to_string(ch.willpower), 25)
|
|
||||||
<< " ║ Demeanor: " << MakeFitL(GetArchetypeLabel(ch.demeanorId), 27) << " ║" << endl;
|
|
||||||
cout << "╠══════════════════════════════════════╩═══════════════════════════════════════╣" << endl;
|
|
||||||
cout << "║ ║" << endl;
|
|
||||||
cout << "║ ║" << endl;
|
|
||||||
cout << "║ ║" << endl;
|
|
||||||
cout << "║ <<PRESS ANY KEY TO CONTINUE>> ║" << endl;
|
|
||||||
cout << "╚══════════════════════════════════════════════════════════════════════════════╝" << endl;
|
|
||||||
WaitForKeypress();
|
|
||||||
cout << "╔══════════════════════════════════════════════════════════════════════════════╗" << endl;
|
|
||||||
cout << "║ " << MakeFitC("Abilities", 76) << " ║" << endl;
|
|
||||||
cout << "║ " << MakeFitC("Talents", 24) << MakeFitC("Skills", 24) << MakeFitC("Knowledges", 24) << " ║" << endl;
|
|
||||||
for (index = 1; index <= 10; index++) {
|
|
||||||
cout << "║ " << FormatAbilityWithValue(GetTalentLabel(index), ch.GetTalentValue(index))
|
|
||||||
<< FormatAbilityWithValue(GetSkillLabel(index), ch.GetSkillValue(index))
|
|
||||||
<< FormatAbilityWithValue(GetKnowledgeLabel(index), ch.GetKnowledgeValue(index)) << " ║" << endl;
|
|
||||||
}
|
|
||||||
cout << "╠══════════════════════════════════════╦═══════════════════════════════════════╣" << endl;
|
|
||||||
cout << "║ " << MakeFitL("Backgrounds:", kLeftColumnWidth) << " ║ " << MakeFitL("Virtues:", kRightColumnWidth) << " ║"
|
|
||||||
<< endl;
|
|
||||||
cout << "║ " << MakeFitL(background_strings[0], kLeftColumnWidth) << " ║ "
|
|
||||||
<< MakeFitB("Conscience:", to_string(ch.conscience), kRightColumnWidth) << " ║" << endl;
|
|
||||||
cout << "║ " << MakeFitL(background_strings[1], kLeftColumnWidth) << " ║ "
|
|
||||||
<< MakeFitB("Self-Control:", to_string(ch.selfControl), kRightColumnWidth) << " ║" << endl;
|
|
||||||
cout << "║ " << MakeFitL(background_strings[2], kLeftColumnWidth) << " ║ "
|
|
||||||
<< MakeFitB("Courage:", to_string(ch.courage), kRightColumnWidth) << " ║" << endl;
|
|
||||||
cout << "║ " << MakeFitL(background_strings[3], kLeftColumnWidth) << " ║ " << MakeFitL("", kRightColumnWidth) << " ║"
|
|
||||||
<< endl;
|
|
||||||
cout << "║ " << MakeFitL(background_strings[4], kLeftColumnWidth) << " ║ " << MakeFitL("", kRightColumnWidth) << " ║"
|
|
||||||
<< endl;
|
|
||||||
cout << "╠══════════════════════════════════════╩═══════════════════════════════════════╣" << endl;
|
|
||||||
cout << "║ <<PRESS ANY KEY TO CONTINUE>> ║" << endl;
|
|
||||||
cout << "╚══════════════════════════════════════════════════════════════════════════════╝" << endl;
|
|
||||||
WaitForKeypress();
|
|
||||||
/*
|
|
||||||
╔══════════════════════════════════════╦═══════════════════════════════════════╗
|
|
||||||
║ Name: Ted ║ Gender: Female Generation: 13 ║
|
|
||||||
║ Clan: Ventrue ║ Age: 35 ║
|
|
||||||
╠══════════════════════════════════════╣ Player: Jeff ║
|
|
||||||
║ Attributes ║ Chronicle: Somesuch ║
|
|
||||||
║ Physical Social Mental ║ Haven: Mom's basement ║
|
|
||||||
║ Str. 1 App. 1 Int. 1 ║ Concept: Asshole ║
|
|
||||||
║ Dex. 1 Cha. 1 Per. 1 ╠═══════════════════════════════════════╣
|
|
||||||
║ Sta. 1 Man. 1 Wit. 1 ║ Derangements: ║
|
|
||||||
╠══════════════════════════════════════╣ _____________________________________ ║
|
|
||||||
║ Disciplines: ║ _____________________________________ ║
|
|
||||||
║ ____________________________________ ║ _____________________________________ ║
|
|
||||||
║ ____________________________________ ║ _____________________________________ ║
|
|
||||||
║ ____________________________________ ║ _____________________________________ ║
|
|
||||||
╠══════════════════════════════════════╬═══════════════════════════════════════╣
|
|
||||||
║ : 2 ║ Nature: Tyrant ║
|
|
||||||
║ Willpower: 1 ║ Demeanor: Traditionalist ║
|
|
||||||
╠══════════════════════════════════════╩═══════════════════════════════════════╣
|
|
||||||
║ ║
|
|
||||||
║ ║
|
|
||||||
║ ║
|
|
||||||
║ <<PRESS ANY KEY TO CONTINUE>> ║
|
|
||||||
╚══════════════════════════════════════════════════════════════════════════════╝
|
|
||||||
╔══════════════════════════════════════════════════════════════════════════════╗
|
|
||||||
║ Abilities ║
|
|
||||||
║ Talents Skills Knowledges ║
|
|
||||||
║ Acting: 0 Animal Ken: 0 Bureaucracy: 0 ║
|
|
||||||
║ Alertness: 0 Drive: 0 Computer: 0 ║
|
|
||||||
║ Athletics: 0 Etiquette: 0 Finance: 0 ║
|
|
||||||
║ Brawl: 0 Firearms: 0 Investigation:0 ║
|
|
||||||
║ Dodge: 0 Melee: 0 Law: 0 ║
|
|
||||||
║ Empathy: 0 Music: 0 Linguistics: 0 ║
|
|
||||||
║ Intimidation: 0 Repair: 0 Medicine: 0 ║
|
|
||||||
║ Leadership: 0 Security: 0 Occult: 0 ║
|
|
||||||
║ Streetwise: 0 Stealth: 0 Politics: 0 ║
|
|
||||||
║ Subterfuge: 0 Survival: 0 Science: 0 ║
|
|
||||||
╠══════════════════════════════════════╦═══════════════════════════════════════╣
|
|
||||||
║ Backgrounds: ║ Virtues: ║
|
|
||||||
║ ║ Conscience: 1 ║
|
|
||||||
║ ║ Self-Control: 1 ║
|
|
||||||
║ ║ Courage: 1 ║
|
|
||||||
║ ║ ║
|
|
||||||
║ ║ ║
|
|
||||||
╠══════════════════════════════════════╩═══════════════════════════════════════╣
|
|
||||||
║ <<PRESS ANY KEY TO CONTINUE>> ║
|
|
||||||
╚══════════════════════════════════════════════════════════════════════════════╝
|
|
||||||
// TODO: ShowCharacterSheet(CharacterType&)
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
|
||||||
int GetRandomInt(int min, int max) {
|
|
||||||
// TODO: Fill this in.
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
string GetString(string prompt) {
|
|
||||||
cout << prompt << endl;
|
|
||||||
string response;
|
|
||||||
getline(cin, response);
|
|
||||||
return response;
|
|
||||||
}
|
|
||||||
|
|
||||||
int ChooseStringId(vector<string> labels, MenuStyle style, const string& prompt) {
|
|
||||||
MaybeClearScreen();
|
|
||||||
vector<MenuItem> menu_items = BuildMenu(labels);
|
|
||||||
style.Adjust(menu_items);
|
|
||||||
cout << prompt << endl;
|
|
||||||
PrintMenu(cout, menu_items, style);
|
|
||||||
int choice = GetMenuChoice(menu_items, style);
|
|
||||||
if (choice == style.random_item_id) {
|
|
||||||
choice = GetRandomMenuItemId(menu_items);
|
|
||||||
}
|
|
||||||
return choice;
|
|
||||||
}
|
|
||||||
|
|
||||||
int GetMenuChoice(vector<MenuItem> menu_items, MenuStyle style) {
|
|
||||||
int choice;
|
|
||||||
while (true) {
|
|
||||||
choice = GetChoice();
|
|
||||||
if (style.show_random && choice == style.random_item_id) {
|
|
||||||
return choice;
|
|
||||||
}
|
|
||||||
if (style.show_cancel && choice == style.cancel_item_id) {
|
|
||||||
return choice;
|
|
||||||
}
|
|
||||||
for (MenuItem item : menu_items) {
|
|
||||||
if (item.id == choice) {
|
|
||||||
return choice;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int ChooseStringIdWithValues(vector<string> labels, vector<int> values, MenuStyle style, const string& prompt) {
|
|
||||||
MaybeClearScreen();
|
|
||||||
vector<MenuItem> menu_items = BuildMenuWithValues(labels, values);
|
|
||||||
style.Adjust(menu_items, false);
|
|
||||||
cout << prompt << endl;
|
|
||||||
PrintMenu(cout, menu_items, style);
|
|
||||||
int choice = GetMenuChoice(menu_items, style);
|
|
||||||
if (choice == style.random_item_id) {
|
|
||||||
choice = GetRandomMenuItemId(menu_items);
|
|
||||||
}
|
|
||||||
return choice;
|
|
||||||
}
|
|
||||||
|
|
||||||
int ChooseStringIdWithColors(vector<string> labels, vector<uint8_t> colors, MenuStyle style, const string& prompt) {
|
|
||||||
MaybeClearScreen();
|
|
||||||
// Check array bounds
|
|
||||||
vector<MenuItem> menu_items = BuildMenuWithColors(labels, colors);
|
|
||||||
style.Adjust(menu_items);
|
|
||||||
cout << prompt << endl;
|
|
||||||
PrintMenu(cout, menu_items, style);
|
|
||||||
int choice = GetMenuChoice(menu_items, style);
|
|
||||||
if (choice == style.random_item_id) {
|
|
||||||
choice = GetRandomMenuItemId(menu_items);
|
|
||||||
}
|
|
||||||
return choice;
|
|
||||||
}
|
|
||||||
|
|
||||||
int ChooseMenuItemId(vector<MenuItem> menu_items, MenuStyle style, const string& prompt, bool ignore_value) {
|
|
||||||
MaybeClearScreen();
|
|
||||||
style.Adjust(menu_items, ignore_value);
|
|
||||||
cout << prompt << endl;
|
|
||||||
PrintMenu(cout, menu_items, style);
|
|
||||||
int choice = GetMenuChoice(menu_items, style);
|
|
||||||
if (choice == style.random_item_id) {
|
|
||||||
choice = GetRandomMenuItemId(menu_items);
|
|
||||||
}
|
|
||||||
return choice;
|
|
||||||
}
|
|
||||||
|
|||||||
2398
sbf-cpp/sbf.h
2398
sbf-cpp/sbf.h
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user