Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create template from already existing character #35467

Merged
merged 3 commits into from
Nov 16, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
95 changes: 95 additions & 0 deletions src/avatar.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,14 @@ mission *avatar::get_active_mission() const
return active_mission;
}

void avatar::reset_all_misions()
{
active_mission = nullptr;
active_missions.clear();
completed_missions.clear();
failed_missions.clear();
}

tripoint avatar::get_active_mission_target() const
{
if( active_mission == nullptr ) {
Expand Down Expand Up @@ -1583,3 +1591,90 @@ bool avatar::invoke_item( item *used, const std::string &method )
{
return Character::invoke_item( used, method );
}

points_left::points_left()
{
limit = MULTI_POOL;
init_from_options();
}

void points_left::init_from_options()
{
stat_points = get_option<int>( "INITIAL_STAT_POINTS" );
trait_points = get_option<int>( "INITIAL_TRAIT_POINTS" );
skill_points = get_option<int>( "INITIAL_SKILL_POINTS" );
}

// Highest amount of points to spend on stats without points going invalid
int points_left::stat_points_left() const
{
switch( limit ) {
case FREEFORM:
case ONE_POOL:
return stat_points + trait_points + skill_points;
case MULTI_POOL:
return std::min( trait_points_left(),
stat_points + std::min( 0, trait_points + skill_points ) );
case TRANSFER:
return 0;
}

return 0;
}

int points_left::trait_points_left() const
{
switch( limit ) {
case FREEFORM:
case ONE_POOL:
return stat_points + trait_points + skill_points;
case MULTI_POOL:
return stat_points + trait_points + std::min( 0, skill_points );
case TRANSFER:
return 0;
}

return 0;
}

int points_left::skill_points_left() const
{
return stat_points + trait_points + skill_points;
}

bool points_left::is_freeform()
{
return limit == FREEFORM;
}

bool points_left::is_valid()
{
return is_freeform() ||
( stat_points_left() >= 0 && trait_points_left() >= 0 &&
skill_points_left() >= 0 );
}

bool points_left::has_spare()
{
return !is_freeform() && is_valid() && skill_points_left() > 0;
}

std::string points_left::to_string()
{
if( limit == MULTI_POOL ) {
return string_format(
_( "Points left: <color_%s>%d</color>%c<color_%s>%d</color>%c<color_%s>%d</color>=<color_%s>%d</color>" ),
stat_points_left() >= 0 ? "light_gray" : "red", stat_points,
trait_points >= 0 ? '+' : '-',
trait_points_left() >= 0 ? "light_gray" : "red", abs( trait_points ),
skill_points >= 0 ? '+' : '-',
skill_points_left() >= 0 ? "light_gray" : "red", abs( skill_points ),
is_valid() ? "light_gray" : "red", stat_points + trait_points + skill_points );
} else if( limit == ONE_POOL ) {
return string_format( _( "Points left: %4d" ), skill_points_left() );
} else if( limit == TRANSFER ) {
return _( "Character Transfer: No changes can be made." );
} else {
return _( "Freeform" );
}
}
27 changes: 27 additions & 0 deletions src/avatar.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ class avatar : public player
bool create( character_type type, const std::string &tempname = "" );
void randomize( bool random_scenario, points_left &points, bool play_now = false );
bool load_template( const std::string &template_name, points_left &points );
void save_template( const std::string &name, const points_left &points );

bool is_avatar() const override {
return true;
Expand Down Expand Up @@ -80,6 +81,8 @@ class avatar : public player
void update_mental_focus();
/** Resets stats, and applies effects in an idempotent manner */
void reset_stats() override;
/** Resets all missions before saving character to template */
void reset_all_misions();

std::vector<mission *> get_active_missions() const;
std::vector<mission *> get_completed_missions() const;
Expand Down Expand Up @@ -219,4 +222,28 @@ class avatar : public player
int per_upgrade = 0;
};

struct points_left {
OzoneH3 marked this conversation as resolved.
Show resolved Hide resolved
int stat_points;
int trait_points;
int skill_points;

enum point_limit : int {
FREEFORM = 0,
ONE_POOL,
MULTI_POOL,
TRANSFER,
} limit;

points_left();
void init_from_options();
// Highest amount of points to spend on stats without points going invalid
int stat_points_left() const;
int trait_points_left() const;
int skill_points_left() const;
bool is_freeform();
bool is_valid();
bool has_spare();
std::string to_string();
};

#endif
6 changes: 3 additions & 3 deletions src/character.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -280,11 +280,11 @@ Character::~Character() = default;
Character::Character( Character && ) = default;
Character &Character::operator=( Character && ) = default;

void Character::setID( character_id i )
void Character::setID( character_id i, bool force )
{
if( id.is_valid() ) {
if( id.is_valid() && !force ) {
debugmsg( "tried to set id of a npc/player, but has already a id: %d", id.get_value() );
} else if( !i.is_valid() ) {
} else if( !i.is_valid() && !force ) {
debugmsg( "tried to set invalid id of a npc/player: %d", i.get_value() );
} else {
id = i;
Expand Down
3 changes: 2 additions & 1 deletion src/character.h
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,8 @@ class Character : public Creature, public visitable<Character>

character_id getID() const;
// sets the ID, will *only* succeed when the current id is not valid
void setID( character_id i );
// allows forcing a -1 id which is required for templates to not throw errors
void setID( character_id i, bool force = false );

field_type_id bloodType() const override;
field_type_id gibType() const override;
Expand Down
94 changes: 71 additions & 23 deletions src/main_menu.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,7 @@ void main_menu::init_strings()
vWorldSubItems.push_back( pgettext( "Main Menu|World", "<R|r>eset World" ) );
vWorldSubItems.push_back( pgettext( "Main Menu|World", "<S|s>how World Mods" ) );
vWorldSubItems.push_back( pgettext( "Main Menu|World", "<C|c>opy World Settings" ) );
vWorldSubItems.push_back( pgettext( "Main Menu|World", "Character to <T|t>emplate" ) );

vWorldHotkeys.clear();
for( const std::string &item : vWorldSubItems ) {
Expand Down Expand Up @@ -877,32 +878,43 @@ bool main_menu::new_character_tab()
return start;
}

bool main_menu::load_character_tab()
bool main_menu::load_character_tab( bool transfer )
{
bool start = false;
const auto all_worldnames = world_generator->all_worldnames();

const size_t last_world_pos = std::find( all_worldnames.begin(), all_worldnames.end(),
world_generator->last_world_name ) - all_worldnames.begin();
if( last_world_pos < all_worldnames.size() ) {
sel2 = last_world_pos;
if( transfer ) {
layer = 3;
sel1 = 2;
sel2 -= 1;
sel3 = 0;
savegames = world_generator->get_world( all_worldnames[sel2] )->world_saves;
}
const size_t last_character_pos = std::find_if( savegames.begin(), savegames.end(),
[]( const save_t &it ) {
return it.player_name() == world_generator->last_character_name;
} ) - savegames.begin();
if( last_character_pos < savegames.size() ) {
sel3 = last_character_pos;
} else {
sel3 = 0;
const size_t last_world_pos = std::find( all_worldnames.begin(), all_worldnames.end(),
world_generator->last_world_name ) - all_worldnames.begin();
if( last_world_pos < all_worldnames.size() ) {
sel2 = last_world_pos;
savegames = world_generator->get_world( all_worldnames[sel2] )->world_saves;
}

const size_t last_character_pos = std::find_if( savegames.begin(), savegames.end(),
[]( const save_t &it ) {
return it.player_name() == world_generator->last_character_name;
} ) - savegames.begin();
if( last_character_pos < savegames.size() ) {
sel3 = last_character_pos;
} else {
sel3 = 0;
}
}

const int offset_x = transfer ? 25 : 15;
const int offset_y = transfer ? -1 : 0;
while( !start && sel1 == 2 && ( layer == 2 || layer == 3 ) ) {
print_menu( w_open, 2, menu_offset );
print_menu( w_open, transfer ? 3 : 2, menu_offset );
if( layer == 2 && sel1 == 2 ) {
if( all_worldnames.empty() ) {
mvwprintz( w_open, menu_offset + point( 15 + extra_w / 2, -2 ),
mvwprintz( w_open, menu_offset + point( offset_x + extra_w / 2, -2 ),
c_red, "%s", _( "No Worlds found!" ) );
on_error();
} else {
Expand All @@ -918,7 +930,7 @@ bool main_menu::load_character_tab()
color1 = c_white;
color2 = h_white;
}
mvwprintz( w_open, point( 15 + menu_offset.x + extra_w / 2, line ),
mvwprintz( w_open, point( offset_x + menu_offset.x + extra_w / 2, line + offset_y ),
( sel2 == i ? color2 : color1 ), "%s (%d)",
world_name, savegames_count );
}
Expand Down Expand Up @@ -960,19 +972,19 @@ bool main_menu::load_character_tab()
savegames.erase( new_end, savegames.end() );
}

mvwprintz( w_open, menu_offset + point( 15 + extra_w / 2, -2 - sel2 ), h_white,
mvwprintz( w_open, menu_offset + point( offset_x + extra_w / 2, -2 - sel2 + offset_y ), h_white,
"%s", wn );

if( savegames.empty() ) {
mvwprintz( w_open, menu_offset + point( 40 + extra_w / 2, -2 - sel2 ),
mvwprintz( w_open, menu_offset + point( 40 + extra_w / 2, -2 - sel2 + offset_y ),
c_red, "%s", _( "No save games found!" ) );
on_error();
} else {
int line = menu_offset.y - 2;

for( const auto &savename : savegames ) {
const bool selected = sel3 + line == menu_offset.y - 2;
mvwprintz( w_open, point( 40 + menu_offset.x + extra_w / 2, line-- ),
mvwprintz( w_open, point( 40 + menu_offset.x + extra_w / 2, line-- + offset_y ),
selected ? h_white : c_white,
"%s", savename.player_name() );
}
Expand All @@ -982,7 +994,7 @@ bool main_menu::load_character_tab()
std::string action = handle_input_timeout( ctxt );
if( errflag && action != "TIMEOUT" ) {
clear_error();
layer = 2;
layer = transfer ? 1 : 2;
} else if( action == "DOWN" ) {
if( sel3 > 0 ) {
sel3--;
Expand All @@ -996,19 +1008,21 @@ bool main_menu::load_character_tab()
sel3 = 0;
}
} else if( action == "LEFT" || action == "QUIT" ) {
layer = 2;
layer = transfer ? 1 : 2;
sel3 = 0;
print_menu( w_open, sel1, menu_offset );
}
if( action == "RIGHT" || action == "CONFIRM" ) {
if( sel3 >= 0 && sel3 < static_cast<int>( savegames.size() ) ) {
werase( w_background );
wrefresh( w_background );

WORLDPTR world = world_generator->get_world( all_worldnames[sel2] );
world_generator->last_world_name = world->world_name;
world_generator->last_character_name = savegames[sel3].player_name();
world_generator->save_last_world_info();
world_generator->set_active_world( world );

try {
g->setup();
} catch( const std::exception &err ) {
Expand All @@ -1023,14 +1037,45 @@ bool main_menu::load_character_tab()
}
}
} // end while

if( transfer ) {
layer = 3;
sel1 = 3;
sel2++;
sel3 = vWorldSubItems.size() - 1;
}

return start;
}

void main_menu::world_tab()
{
while( sel1 == 3 && ( layer == 2 || layer == 3 ) ) {
while( sel1 == 3 && ( layer == 2 || layer == 3 || layer == 4 ) ) {
print_menu( w_open, 3, menu_offset );
if( layer == 3 ) { // World Menu
if( layer == 4 ) { //Character to Template
if( load_character_tab( true ) ) {
points_left points;
points.stat_points = 0;
points.trait_points = 0;
points.skill_points = 0;
points.limit = points_left::TRANSFER;

g->u.setID( character_id(), true );
g->u.reset_all_misions();
g->u.save_template( g->u.name, points );

g->u = avatar();
MAPBUFFER.reset();
overmap_buffer.clear();

load_char_templates();

werase( w_background );
wrefresh( w_background );

layer = 3;
}
} else if( layer == 3 ) { // World Menu
// Show options for Destroy, Reset worlds.
// Reset and Destroy ask for world to modify.
// Reset empties world of everything but options, then makes new world within it.
Expand Down Expand Up @@ -1109,6 +1154,9 @@ void main_menu::world_tab()
} else if( sel3 == 3 ) { // Copy World settings
layer = 2;
world_generator->make_new_world( true, all_worldnames[sel2 - 1] );
} else if( sel3 == 4 ) { // Character to Template
layer = 4;
sel4 = 0;
}

if( query_yes ) {
Expand Down
3 changes: 2 additions & 1 deletion src/main_menu.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ class main_menu
// Tab functions. They return whether a game was started or not. The ones that can never
// start a game have a void return type.
bool new_character_tab();
bool load_character_tab();
bool load_character_tab( bool transfer = false );
void world_tab();

/*
Expand All @@ -67,6 +67,7 @@ class main_menu
int sel1 = 1;
int sel2 = 1;
int sel3 = 1;
int sel4 = 1;
int layer = 1;
point LAST_TERM;
catacurses::window w_open;
Expand Down
Loading