Skip to content

Commit

Permalink
Create template from already existing character (#35467)
Browse files Browse the repository at this point in the history
* initial progress

* Create template from character.

* tidy up code
  • Loading branch information
OzoneH3 authored and ZhilkinSerg committed Nov 16, 2019
1 parent 6a9c494 commit 3ca8f49
Show file tree
Hide file tree
Showing 7 changed files with 229 additions and 128 deletions.
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 {
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

0 comments on commit 3ca8f49

Please sign in to comment.