Skip to content

Commit

Permalink
Merge pull request #24173 from glamcube17/art-req-dreams
Browse files Browse the repository at this point in the history
Artifact dreams
  • Loading branch information
ZhilkinSerg authored Jul 16, 2018
2 parents 9157781 + fde5c1b commit 3ce526f
Show file tree
Hide file tree
Showing 8 changed files with 217 additions and 50 deletions.
83 changes: 78 additions & 5 deletions src/artifact.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,16 @@ struct artifact_property_datum {
std::array<art_effect_active, 4> active_bad;
};

struct artifact_dream_datum { //Used only when generating - stored as individual members of each artifact
std::vector<std::string> msg_unmet;
std::vector<std::string> msg_met;
// Once per hour while sleeping, makes a list of each artifact that passes a (freq) in 100 check
// One item is picked from artifacts that passed that chance, and the appropriate msg is shown
// If multiple met/unmet messages are specified for the item, one is picked at random
int freq_unmet; // 100 if no reqs, since should never be unmet in that case
int freq_met; // 0 if no reqs
};

enum artifact_weapon_type {
ARTWEAP_BULK, // A bulky item that works okay for bashing
ARTWEAP_CLUB, // An item designed to bash
Expand Down Expand Up @@ -544,6 +554,31 @@ static const std::array<std::string, 20> artifact_noun = { {
translate_marker( "%s Graves" ), translate_marker( "%s Horrors" ), translate_marker( "%s Suffering" ), translate_marker( "%s Death" ), translate_marker( "%s Horror" )
} };
std::string artifact_name( const std::string &type );
//Dreams for each charge req
static const std::array<artifact_dream_datum, NUM_ACRS> artifact_dream_data = { {
{ {translate_marker("The %s is somehow vaguely dissatisfied even though it doesn't want anything. Seeing this is a bug!")},
{translate_marker("The %s is satisfied, as it should be because it has no standards. Seeing this is a bug!")},
100, 0
},{ {translate_marker("Your %s feels needy, like it wants to be held.")},
{translate_marker("You snuggle your %s closer.")},
50, 35
},{ {translate_marker("Your %s feels needy, like it wants to be touched.")},
{translate_marker("You press your %s against your skin.")},
50, 35
},{ {translate_marker("The %s is confused to find you dreaming while awake. Seeing this is a bug!")},
{translate_marker("Your %s sleeps soundly.")},
100, 33
},{ {translate_marker("Your %s longs for the glow.")},
{translate_marker("Your %s basks in the glow.")},
25, 75
},{ {translate_marker("You dream of angels' tears falling on your %s.")},
{translate_marker("You dream of playing in the rain with your %s.")},
50, 60
},{ {translate_marker("You dream that your %s is being buried alive.")},
{translate_marker("You dream of your %s dancing in a blue void.")},
50, 50
}
} };

// Constructors for artifact itypes.
it_artifact_tool::it_artifact_tool() : itype()
Expand All @@ -555,6 +590,10 @@ it_artifact_tool::it_artifact_tool() : itype()
tool->charges_per_use = 1;
artifact->charge_type = ARTC_NULL;
artifact->charge_req = ACR_NULL;
artifact->dream_msg_unmet = artifact_dream_data[(int)ACR_NULL].msg_unmet;
artifact->dream_msg_met = artifact_dream_data[(int)ACR_NULL].msg_met;
artifact->dream_freq_unmet = artifact_dream_data[(int)ACR_NULL].freq_unmet;
artifact->dream_freq_met = artifact_dream_data[(int)ACR_NULL].freq_met;
use_methods.emplace( "ARTIFACT", use_function( "ARTIFACT", &iuse::artifact ) );
}

Expand Down Expand Up @@ -719,10 +758,19 @@ std::string new_artifact()
if (one_in(8) && num_bad + num_good >= 4) {
def.artifact->charge_type = ARTC_NULL; // 1 in 8 chance that it can't recharge!
}
//Maybe pick an extra recharge requirement
if (one_in( std::max(1, 8-num_good) ) && def.artifact->charge_type!=ARTC_NULL ) {
// Maybe pick an extra recharge requirement
if (one_in( std::max(1, 6-num_good) ) && def.artifact->charge_type!=ARTC_NULL ) {
def.artifact->charge_req = art_charge_req( rng(ACR_NULL + 1, NUM_ACRS - 1) );
}
// Assign dream data (stored individually so they can be overridden in json)
def.artifact->dream_msg_unmet = artifact_dream_data[(int)(def.artifact->charge_req)].msg_unmet;
def.artifact->dream_msg_met = artifact_dream_data[(int)(def.artifact->charge_req)].msg_met;
def.artifact->dream_freq_unmet = artifact_dream_data[(int)(def.artifact->charge_req)].freq_unmet;
def.artifact->dream_freq_met = artifact_dream_data[(int)(def.artifact->charge_req)].freq_met;
// Stronger artifacts have a higher chance of picking their dream
def.artifact->dream_freq_unmet *= ( 1 + 0.1*(num_bad+num_good) );
def.artifact->dream_freq_met *= ( 1 + 0.1*(num_bad+num_good) );

item_controller->add_item_type( static_cast<itype &>( def ) );
return def.get_id();
} else { // Generate an armor artifact
Expand Down Expand Up @@ -933,10 +981,15 @@ std::string new_natural_artifact(artifact_natural_property prop)
def.tool->def_charges = def.tool->max_charges = rng( 1, 4 );
def.artifact->charge_type = art_charge( rng(ARTC_NULL + 1, NUM_ARTCS - 1) );
//Maybe pick an extra recharge requirement
if (one_in(8)) {
if (one_in(6)) {
def.artifact->charge_req = art_charge_req( rng(ACR_NULL + 1, NUM_ACRS - 1) );
}
}
// Assign dream data (stored individually so they can be overridden in json)
def.artifact->dream_msg_unmet = artifact_dream_data[(int)(def.artifact->charge_req)].msg_unmet;
def.artifact->dream_msg_met = artifact_dream_data[(int)(def.artifact->charge_req)].msg_met;
def.artifact->dream_freq_unmet = artifact_dream_data[(int)(def.artifact->charge_req)].freq_unmet;
def.artifact->dream_freq_met = artifact_dream_data[(int)(def.artifact->charge_req)].freq_met;
item_controller->add_item_type( static_cast<itype &>( def ) );
return def.get_id();
}
Expand Down Expand Up @@ -1014,7 +1067,6 @@ std::string artifact_name( const std::string &type )
return ret;
}


/* Json Loading and saving */

void load_artifacts(const std::string &artfilename)
Expand Down Expand Up @@ -1103,6 +1155,23 @@ void it_artifact_tool::deserialize(JsonObject &jo)
while (ja.has_more()) {
artifact->effects_carried.push_back((art_effect_passive)ja.next_int());
}

//Generate any missing dream data (due to e.g. old save)
if( !jo.has_array("dream_unmet") ) { artifact->dream_msg_unmet = artifact_dream_data[(int)(artifact->charge_req)].msg_unmet; }
else {
ja = jo.get_array("dream_unmet");
while (ja.has_more()) { artifact->dream_msg_unmet.push_back( ja.next_string() ); }
}
if( !jo.has_array("dream_met") ) { artifact->dream_msg_met = artifact_dream_data[(int)(artifact->charge_req)].msg_met; }
else {
ja = jo.get_array("dream_met");
while (ja.has_more()) { artifact->dream_msg_met.push_back( ja.next_string() ); }
}
if( jo.has_int( "dream_freq_unmet") ) { artifact->dream_freq_unmet = jo.get_int( "dream_freq_unmet" ); }
else{ artifact->dream_freq_unmet = artifact_dream_data[(int)(artifact->charge_req)].freq_unmet; }
if( jo.has_int( "dream_freq_met") ) { artifact->dream_freq_met = jo.get_int( "dream_freq_met" ); }
else{ artifact->dream_freq_met = artifact_dream_data[(int)(artifact->charge_req)].freq_met; }

}

void it_artifact_armor::deserialize(JsonObject &jo)
Expand Down Expand Up @@ -1230,10 +1299,14 @@ void it_artifact_tool::serialize(JsonOut &json) const

// artifact data
json.member("charge_type", artifact->charge_type);
json.member("charge_req", artifact->charge_req);
json.member("charge_req", artifact->charge_req);
serialize_enum_vector_as_int( json, "effects_wielded", artifact->effects_wielded );
serialize_enum_vector_as_int( json, "effects_activated", artifact->effects_activated );
serialize_enum_vector_as_int( json, "effects_carried", artifact->effects_carried );
json.member("dream_unmet", artifact->dream_msg_unmet);
json.member("dream_met", artifact->dream_msg_met);
json.member("dream_freq_unmet", artifact->dream_freq_unmet);
json.member("dream_freq_met", artifact->dream_freq_met);

json.end_object();
}
Expand Down
2 changes: 2 additions & 0 deletions src/artifact.h
Original file line number Diff line number Diff line change
Expand Up @@ -114,4 +114,6 @@ void load_artifacts( const std::string &filename );
// save artifact definitions to json, path must be the same as for loading.
bool save_artifacts( const std::string &path );

bool check_art_charge_req( item &it );

#endif
143 changes: 98 additions & 45 deletions src/game.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1596,6 +1596,10 @@ bool game::do_turn()
if( player_is_sleeping ) {
if( calendar::once_every( 30_minutes ) || !player_was_sleeping ) {
draw();
//Putting this in here to save on checking
if( calendar::once_every( 1_hours ) ) {
add_artifact_dreams( );
}
}

if( calendar::once_every( 1_minutes ) ) {
Expand Down Expand Up @@ -13368,51 +13372,7 @@ void game::process_artifact( item &it, player &p )
// Recharge it if necessary
if( it.ammo_remaining() < it.ammo_capacity() && calendar::once_every( 1_minutes ) ) {
//Before incrementing charge, check that any extra requirements are met
const bool heldweapon = ( wielded && !it.is_armor() ); //don't charge wielded clothes
bool reqsmet = true;
switch( it.type->artifact->charge_req ) {
case(ACR_NULL):
case(NUM_ACRS):
break;
case(ACR_EQUIP):
//Generated artifacts won't both be wearable and have charges, but nice for mods
reqsmet = ( worn || heldweapon );
break;
case(ACR_SKIN):
//As ACR_EQUIP, but also requires nothing worn on bodypart wielding or wearing item
if( !worn && !heldweapon ){ reqsmet = false; break; }
for( const body_part bp : all_body_parts ) {
if( it.covers(bp) || ( heldweapon && ( bp == bp_hand_r || bp == bp_hand_l ) ) ) {
reqsmet = true;
for ( auto &i : p.worn ) {
if ( i.covers(bp) && ( &it != &i ) && i.get_coverage() > 50 ) {
reqsmet = false; break; //This one's no good, check the next body part
}
}
if(reqsmet){ break; } //Only need skin contact on one bodypart
}
}
break;
case(ACR_SLEEP):
reqsmet = p.has_effect( effect_sleep );
break;
case(ACR_RAD):
reqsmet = ( ( g->m.get_radiation( p.pos() ) > 0 ) || ( p.radiation > 0 ) );
break;
case(ACR_WET):
reqsmet = std::any_of( p.body_wetness.begin(), p.body_wetness.end(),
[]( const int w ) { return w != 0; } );
if(!reqsmet && sum_conditions( calendar::turn-1, calendar::turn, p.pos() ).rain_amount > 0
&& !( p.in_vehicle && g->m.veh_at(p.pos())->is_inside() ) ){
reqsmet = true;
}
break;
case(ACR_SKY):
reqsmet = ( p.posz() > 0 );
break;
}
//Proceed with actually recharging if all extra requirements met
if(reqsmet){
if( check_art_charge_req( it ) ) {
switch( it.type->artifact->charge_type ) {
case ARTC_NULL:
case NUM_ARTCS:
Expand Down Expand Up @@ -13584,6 +13544,57 @@ void game::process_artifact( item &it, player &p )
p.dex_cur = p.get_dex();
p.per_cur = p.get_per();
}
//Check if an artifact's extra charge requirements are currently met
bool check_art_charge_req( item& it )
{
player& p = g->u;
bool reqsmet = true;
const bool worn = p.is_worn( it );
const bool wielded = ( &it == &p.weapon );
const bool heldweapon = ( wielded && !it.is_armor() ); //don't charge wielded clothes
switch( it.type->artifact->charge_req ) {
case(ACR_NULL):
case(NUM_ACRS):
break;
case(ACR_EQUIP):
//Generated artifacts won't both be wearable and have charges, but nice for mods
reqsmet = ( worn || heldweapon );
break;
case(ACR_SKIN):
//As ACR_EQUIP, but also requires nothing worn on bodypart wielding or wearing item
if( !worn && !heldweapon ){ reqsmet = false; break; }
for( const body_part bp : all_body_parts ) {
if( it.covers(bp) || ( heldweapon && ( bp == bp_hand_r || bp == bp_hand_l ) ) ) {
reqsmet = true;
for ( auto &i : p.worn ) {
if ( i.covers(bp) && ( &it != &i ) && i.get_coverage() > 50 ) {
reqsmet = false; break; //This one's no good, check the next body part
}
}
if(reqsmet){ break; } //Only need skin contact on one bodypart
}
}
break;
case(ACR_SLEEP):
reqsmet = p.has_effect( effect_sleep );
break;
case(ACR_RAD):
reqsmet = ( ( g->m.get_radiation( p.pos() ) > 0 ) || ( p.radiation > 0 ) );
break;
case(ACR_WET):
reqsmet = std::any_of( p.body_wetness.begin(), p.body_wetness.end(),
[]( const int w ) { return w != 0; } );
if(!reqsmet && sum_conditions( calendar::turn-1, calendar::turn, p.pos() ).rain_amount > 0
&& !( p.in_vehicle && g->m.veh_at(p.pos())->is_inside() ) ){
reqsmet = true;
}
break;
case(ACR_SKY):
reqsmet = ( p.posz() > 0 );
break;
}
return reqsmet;
}

void game::start_calendar()
{
Expand Down Expand Up @@ -13794,6 +13805,48 @@ void game::add_artifact_messages( const std::vector<art_effect_passive> &effects
}
}

void game::add_artifact_dreams( ) {
//If player is sleeping, get a dream from a carried artifact
//Don't need to check that player is sleeping here, that's done before calling
std::list<item *> art_items = g->u.get_artifact_items();
std::list<item *> arts_with_dream;
std::vector<item *> valid_arts;
std::vector<std::vector<std::string>> valid_dreams; // Tracking separately so we only need to check its req once
//Pull the list of dreams
add_msg(m_debug, string_format("Checking %s carried artifacts", art_items.size() ) );
for( auto &it : art_items ) {
//Pick only the ones with an applicable dream
auto art = it->type->artifact;
if(art->charge_req != ACR_NULL && ( it->ammo_remaining() < it->ammo_capacity() || it->ammo_capacity() == 0 ) ) { //or max 0 in case of wacky mod shenanigans
add_msg(m_debug, string_format("Checking artifact %s", it->tname().c_str() ) );
if( check_art_charge_req( *it ) ) {
add_msg(m_debug, string_format(" Has freq %s,%s", art->dream_freq_met, art->dream_freq_unmet ) );
if( art->dream_freq_met > 0 && x_in_y( art->dream_freq_met, 100 ) ) {
add_msg(m_debug, string_format("Adding met dream from %s", it->tname().c_str() ) );
valid_arts.push_back( it );
valid_dreams.push_back( art->dream_msg_met );
}
} else {
add_msg(m_debug, string_format(" Has freq %s,%s", art->dream_freq_met, art->dream_freq_unmet ) );
if( art->dream_freq_unmet > 0 && x_in_y( art->dream_freq_unmet, 100 ) ) {
add_msg(m_debug, string_format("Adding unmet dream from %s", it->tname().c_str() ) );
valid_arts.push_back( it );
valid_dreams.push_back( art->dream_msg_unmet );
}
}
}
}
if( !valid_dreams.empty() ) {
add_msg(m_debug, string_format("Found %s valid artifact dreams", valid_dreams.size() ) );
const int selected = rng( 0, valid_arts.size()-1 );
auto it = valid_arts[selected];
auto msg = random_entry( valid_dreams[selected] );
const std::string& dream = string_format( _( msg.c_str() ) , it->tname().c_str() );
add_msg( dream );
}
else{add_msg(m_debug,"Didn't have any dreams, sorry");}
}

int game::get_levx() const
{
return m.get_abs_sub().x;
Expand Down
1 change: 1 addition & 0 deletions src/game.h
Original file line number Diff line number Diff line change
Expand Up @@ -529,6 +529,7 @@ class game

void process_artifact( item &it, player &p );
void add_artifact_messages( const std::vector<art_effect_passive> &effects );
void add_artifact_dreams( );

void peek();
void peek( const tripoint &p );
Expand Down
7 changes: 7 additions & 0 deletions src/item_factory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1174,6 +1174,13 @@ void Item_factory::load( islot_artifact &slot, JsonObject &jo, const std::string
{
slot.charge_type = jo.get_enum_value( "charge_type", ARTC_NULL );
slot.charge_req = jo.get_enum_value( "charge_req", ACR_NULL );
// No dreams unless specified for artifacts embedded in items.
// If specifying dreams, message should be set too,
// since the array with the defaults isn't accessible from here.
slot.dream_freq_unmet = jo.get_int( "dream_freq_unmet", 0 );
slot.dream_freq_met = jo.get_int( "dream_freq_met", 0 );
slot.dream_msg_unmet = jo.get_string_array( "dream_unmet" ); //@todo Make sure it doesn't cause problems if this is empty
slot.dream_msg_met = jo.get_string_array( "dream_met" );
load_optional_enum_array( slot.effects_wielded, jo, "effects_wielded" );
load_optional_enum_array( slot.effects_activated, jo, "effects_activated" );
load_optional_enum_array( slot.effects_carried, jo, "effects_carried" );
Expand Down
6 changes: 6 additions & 0 deletions src/itype.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ enum art_effect_active : int;
enum art_charge : int;
enum art_charge_req : int;
enum art_effect_passive : int;
struct artifact_dream_datum;
class material_type;
using material_id = string_id<material_type>;
typedef std::string itype_id;
Expand Down Expand Up @@ -650,7 +651,12 @@ struct islot_artifact {
std::vector<art_effect_active> effects_activated;
std::vector<art_effect_passive> effects_carried;
std::vector<art_effect_passive> effects_worn;
std::vector<std::string> dream_msg_unmet;
std::vector<std::string> dream_msg_met;
int dream_freq_unmet;
int dream_freq_met;
};
bool check_art_charge_req( item& it );

struct itype {
friend class Item_factory;
Expand Down
23 changes: 23 additions & 0 deletions src/player.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2632,6 +2632,29 @@ std::list<item *> player::get_radio_items()
return rc_items;
}

std::list<item *> player::get_artifact_items()
{
std::list<item *> art_items;
const invslice &stacks = inv.slice();
for( auto &stack : stacks ) {
item &stack_iter = stack->front();
if( stack_iter.is_artifact() ) {
art_items.push_back( &stack_iter );
}
}
for( auto &elem : worn ) {
if( elem.is_artifact() ) {
art_items.push_back( &elem );
}
}
if( is_armed() ) {
if( weapon.is_artifact() ) {
art_items.push_back( &weapon );
}
}
return art_items;
}

bool player::has_active_optcloak() const
{
for( auto &w : worn ) {
Expand Down
Loading

0 comments on commit 3ce526f

Please sign in to comment.