From 6f4a9dcced5d266fb965303f77c8e5ba0ac648f6 Mon Sep 17 00:00:00 2001 From: Chaosvolt Date: Wed, 24 Jan 2024 10:50:15 -0600 Subject: [PATCH 01/10] commit for remote Co-Authored-By: Curtis Merrill <30208401+KorGgenT@users.noreply.github.com> --- src/debug_menu.cpp | 2 +- src/init.cpp | 1 + src/item.cpp | 5 + src/item.h | 1 + src/magic_enchantment.cpp | 42 +++-- src/magic_enchantment.h | 26 ++++ src/map.cpp | 13 +- src/map.h | 6 +- src/map_extras.cpp | 3 +- src/mapgen.cpp | 9 +- src/relic.cpp | 317 ++++++++++++++++++++++++++++++++++++++ src/relic.h | 114 ++++++++++++++ 12 files changed, 508 insertions(+), 31 deletions(-) diff --git a/src/debug_menu.cpp b/src/debug_menu.cpp index 0e8fac04fa73..742e44bc7c6d 100644 --- a/src/debug_menu.cpp +++ b/src/debug_menu.cpp @@ -1585,7 +1585,7 @@ void debug() artifact_natural_property prop = static_cast( rng( ARTPROP_NULL + 1, ARTPROP_MAX - 1 ) ); m.create_anomaly( *center, prop ); - m.spawn_natural_artifact( *center, prop ); + m.spawn_artifact( *center, relic_procgen_id( "alien_reality" ) ); } break; diff --git a/src/init.cpp b/src/init.cpp index 5e7e38371f69..ebfb6df18b20 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -254,6 +254,7 @@ void DynamicDataLoader::initialize() add( "json_flag", &json_flag::load_all ); add( "mutation_flag", &json_trait_flag::load_all ); add( "fault", &fault::load_fault ); + add( "relic_procgen_data", &relic_procgen_data::load_relic_procgen_data ); add( "field_type", &field_types::load ); add( "weather_type", &weather_types::load ); add( "ammo_effect", &ammo_effects::load ); diff --git a/src/item.cpp b/src/item.cpp index b4667994e17d..e08972678c85 100644 --- a/src/item.cpp +++ b/src/item.cpp @@ -9124,6 +9124,11 @@ std::vector item::mutations_from_wearing( const Character &guy ) const return muts; } +void item::overwrite_relic( const relic &nrelic ) +{ + this->relic_data = cata::make_value( nrelic ); +} + void item::process_relic( Character &carrier ) { if( !is_relic() ) { diff --git a/src/item.h b/src/item.h index 47f220cfeaec..0f4a136e0fb5 100644 --- a/src/item.h +++ b/src/item.h @@ -1208,6 +1208,7 @@ class item : public location_visitable, public game_object * @param pos The location of the artifact (should be the player location if carried). */ void process_artifact( player *carrier, const tripoint &pos ); + void overwrite_relic( const relic &nrelic ); void process_relic( Character &carrier ); bool destroyed_at_zero_charges() const; diff --git a/src/magic_enchantment.cpp b/src/magic_enchantment.cpp index 5db1246b5315..3fa12f764d0a 100644 --- a/src/magic_enchantment.cpp +++ b/src/magic_enchantment.cpp @@ -20,23 +20,6 @@ #include "string_id.h" #include "units.h" -template struct enum_traits; - -template<> -struct enum_traits { - static constexpr enchantment::has last = enchantment::has::NUM_HAS; -}; - -template<> -struct enum_traits { - static constexpr enchantment::condition last = enchantment::condition::NUM_CONDITION; -}; - -template<> -struct enum_traits { - static constexpr enchant_vals::mod last = enchant_vals::mod::NUM_MOD; -}; - namespace io { // *INDENT-OFF* @@ -401,6 +384,31 @@ void enchantment::force_add( const enchantment &rhs ) } } +void enchantment::set_has( enchantment::has value ) +{ + active_conditions.first = value; +} + +void enchantment::add_value_add( enchant_vals::mod value, int add_value ) +{ + values_add[value] = add_value; +} + +void enchantment::add_value_mult( enchant_vals::mod value, float mult_value ) +{ + values_multiply[value] = mult_value; +} + +void enchantment::add_hit_me( const fake_spell &sp ) +{ + hit_me_effect.push_back( sp ); +} + +void enchantment::add_hit_you( const fake_spell &sp ) +{ + hit_you_effect.push_back( sp ); +} + int enchantment::get_value_add( const enchant_vals::mod value ) const { const auto found = values_add.find( value ); diff --git a/src/magic_enchantment.h b/src/magic_enchantment.h index b2b465f53995..d65666a00f5d 100644 --- a/src/magic_enchantment.h +++ b/src/magic_enchantment.h @@ -9,6 +9,7 @@ #include #include "calendar.h" +#include "enum_traits.h" #include "magic.h" #include "type_id.h" @@ -99,6 +100,14 @@ class enchantment // adds two enchantments together and ignores their conditions void force_add( const enchantment &rhs ); + void set_has( has value ); + + void add_value_add( enchant_vals::mod value, int add_value ); + void add_value_mult( enchant_vals::mod value, float mult_value ); + + void add_hit_me( const fake_spell &sp ); + void add_hit_you( const fake_spell &sp ); + int get_value_add( enchant_vals::mod value ) const; double get_value_multiply( enchant_vals::mod value ) const; @@ -172,4 +181,21 @@ class enchantment const fake_spell &sp ) const; }; +template struct enum_traits; + +template<> +struct enum_traits { + static constexpr enchantment::has last = enchantment::has::NUM_HAS; +}; + +template<> +struct enum_traits { + static constexpr enchantment::condition last = enchantment::condition::NUM_CONDITION; +}; + +template<> +struct enum_traits { + static constexpr enchant_vals::mod last = enchant_vals::mod::NUM_MOD; +}; + #endif // CATA_SRC_MAGIC_ENCHANTMENT_H diff --git a/src/map.cpp b/src/map.cpp index 82f1a0aadd1a..c3076d90f648 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -4289,15 +4289,14 @@ std::vector> map::spawn_items( const tripoint &p, return ret; } -void map::spawn_artifact( const tripoint &p ) +void map::spawn_artifact( const tripoint &p, const relic_procgen_id &id ) { - add_item_or_charges( p, item::spawn( new_artifact(), calendar::start_of_cataclysm ) ); -} + relic_procgen_data::generation_rules rules; + rules.max_attributes = 5; + rules.power_level = 1000; + rules.max_negative_power = -2000; -void map::spawn_natural_artifact( const tripoint &p, artifact_natural_property prop ) -{ - add_item_or_charges( p, item::spawn( new_natural_artifact( prop ), - calendar::start_of_cataclysm ) ); + add_item_or_charges( p, id->create_item( rules ) ); } void map::spawn_item( const tripoint &p, const itype_id &type_id, diff --git a/src/map.h b/src/map.h index 1dc556763b08..0c432457f180 100644 --- a/src/map.h +++ b/src/map.h @@ -56,6 +56,7 @@ class mapgendata; class monster; class optional_vpart_position; class player; +class relic_procgen_data; class submap; template class tripoint_range; @@ -99,6 +100,8 @@ namespace cata template class poly_serialized; } // namespace cata +using relic_procgen_id = string_id; + class map_stack : public item_stack { private: @@ -1200,8 +1203,7 @@ class map detached_ptr i_rem( point p, item *it ) { return i_rem( tripoint( p, abs_sub.z ), it ); } - void spawn_artifact( const tripoint &p ); - void spawn_natural_artifact( const tripoint &p, artifact_natural_property prop ); + void spawn_artifact( const tripoint &p, const relic_procgen_id &id ); void spawn_item( const tripoint &p, const itype_id &type_id, unsigned quantity = 1, int charges = 0, const time_point &birthday = calendar::start_of_cataclysm, int damlevel = 0 ); diff --git a/src/map_extras.cpp b/src/map_extras.cpp index 34c642f58c35..d41fcf95709c 100644 --- a/src/map_extras.cpp +++ b/src/map_extras.cpp @@ -1750,7 +1750,8 @@ static bool mx_portal_in( map &m, const tripoint &abs_sub ) artifact_natural_property prop = static_cast( rng( ARTPROP_NULL + 1, ARTPROP_MAX - 1 ) ); m.create_anomaly( portal_location, prop ); - m.spawn_natural_artifact( p + tripoint{ rng( -1, 1 ), rng( -1, 1 ), abs_sub.z }, prop ); + m.spawn_artifact( p + tripoint( rng( -1, 1 ), rng( -1, 1 ), abs_sub.z ), + relic_procgen_id( "alien_reality" ) ); break; } } diff --git a/src/mapgen.cpp b/src/mapgen.cpp index 5223d32a6f4a..50b6e2f6d590 100644 --- a/src/mapgen.cpp +++ b/src/mapgen.cpp @@ -5623,8 +5623,10 @@ void map::draw_temple( const mapgendata &dat ) square( this, t_rock_floor, point( SEEX - 1, 1 ), point( SEEX + 2, 4 ) ); square( this, t_rock_floor, point( SEEX, 5 ), point( SEEX + 1, SOUTH_EDGE ) ); line( this, t_stairs_up, point( SEEX, SOUTH_EDGE ), point( SEEX + 1, SOUTH_EDGE ) ); - spawn_artifact( tripoint( rng( SEEX, SEEX + 1 ), rng( 2, 3 ), abs_sub.z ) ); - spawn_artifact( tripoint( rng( SEEX, SEEX + 1 ), rng( 2, 3 ), abs_sub.z ) ); + spawn_artifact( tripoint( rng( SEEX, SEEX + 1 ), rng( 2, 3 ), abs_sub.z ), + relic_procgen_id( "cult" ) ); + spawn_artifact( tripoint( rng( SEEX, SEEX + 1 ), rng( 2, 3 ), abs_sub.z ), + relic_procgen_id( "cult" ) ); return; } @@ -5897,7 +5899,8 @@ void map::draw_mine( mapgendata &dat ) p3, false, calendar::start_of_cataclysm ); } place_spawns( GROUP_DOG_THING, 1, point( SEEX, SEEX ), point( SEEX + 1, SEEX + 1 ), 1, true, true ); - spawn_artifact( tripoint( rng( SEEX, SEEX + 1 ), rng( SEEY, SEEY + 1 ), abs_sub.z ) ); + spawn_artifact( tripoint( rng( SEEX, SEEX + 1 ), rng( SEEY, SEEY + 1 ), abs_sub.z ), + relic_procgen_id( "netherum_tunnels" ) ); } } diff --git a/src/relic.cpp b/src/relic.cpp index c2fefdf5b62b..36f441838de1 100644 --- a/src/relic.cpp +++ b/src/relic.cpp @@ -6,8 +6,10 @@ #include "cata_unreachable.h" #include "creature.h" #include "character.h" +#include "enum_traits.h" #include "field.h" #include "game.h" +#include "generic_factory.h" #include "json.h" #include "magic.h" #include "magic_enchantment.h" @@ -63,6 +65,23 @@ std::string enum_to_string( relic_recharge_req data ) debugmsg( "Invalid relic_recharge_req" ); abort(); } + + template<> + std::string enum_to_string( relic_procgen_data::type data ) + { + switch( data ) { + // *INDENT-OFF* + case relic_procgen_data::type::active_enchantment: return "active_enchantment"; + case relic_procgen_data::type::hit_me: return "hit_me"; + case relic_procgen_data::type::hit_you: return "hit_you"; + case relic_procgen_data::type::passive_enchantment_add: return "passive_enchantment_add"; + case relic_procgen_data::type::passive_enchantment_mult: return "passive_enchantment_mult"; + case relic_procgen_data::type::last: break; + // *INDENT-ON* + } + debugmsg( "Invalid enchantment::has" ); + abort(); + } } // namespace io bool relic_recharge::operator==( const relic_recharge &rhs ) const @@ -140,6 +159,23 @@ void relic_recharge::check() const } } +template<> +const relic_procgen_data &string_id::obj() const +{ + return relic_procgen_data_factory.obj( *this ); +} + +template<> +bool string_id::is_valid() const +{ + return relic_procgen_data_factory.is_valid( *this ); +} + +void relic_procgen_data::load_relic_procgen_data( const JsonObject &jo, const std::string &src ) +{ + relic_procgen_data_factory.load( jo, src ); +} + void relic::add_active_effect( const fake_spell &sp ) { active_effects.emplace_back( sp ); @@ -160,6 +196,85 @@ void relic::add_recharge_scheme( const relic_recharge &r ) recharge_scheme.emplace_back( r ); } + +template +void relic_procgen_data::enchantment_value_passive::load( const JsonObject &jo ) +{ + mandatory( jo, was_loaded, "type", type ); + optional( jo, was_loaded, "power_per_increment", power_per_increment, 1 ); + optional( jo, was_loaded, "increment", increment, 1 ); + optional( jo, was_loaded, "min_value", min_value, 0 ); + optional( jo, was_loaded, "max_value", max_value, 0 ); +} + +template +void relic_procgen_data::enchantment_value_passive::deserialize( JsonIn &jsin ) +{ + JsonObject jobj = jsin.get_object(); + load( jobj ); +} + +void relic_procgen_data::enchantment_active::load( const JsonObject &jo ) +{ + mandatory( jo, was_loaded, "spell_id", activated_spell ); + optional( jo, was_loaded, "base_power", base_power, 0 ); + optional( jo, was_loaded, "power_per_increment", power_per_increment, 1 ); + optional( jo, was_loaded, "increment", increment, 1 ); + optional( jo, was_loaded, "min_level", min_level, 0 ); + optional( jo, was_loaded, "max_level", max_level, 0 ); +} + +void relic_procgen_data::enchantment_active::deserialize( JsonIn &jsin ) +{ + JsonObject jobj = jsin.get_object(); + load( jobj ); +} + +void relic_procgen_data::load( const JsonObject &jo, const std::string & ) +{ + for( const JsonObject &jo_inner : jo.get_array( "passive_add_procgen_values" ) ) { + int weight = 0; + mandatory( jo_inner, was_loaded, "weight", weight ); + relic_procgen_data::enchantment_value_passive val; + val.load( jo_inner ); + + passive_add_procgen_values.add( val, weight ); + } + + for( const JsonObject &jo_inner : jo.get_array( "passive_mult_procgen_values" ) ) { + int weight = 0; + mandatory( jo_inner, was_loaded, "weight", weight ); + relic_procgen_data::enchantment_value_passive val; + val.load( jo_inner ); + + passive_mult_procgen_values.add( val, weight ); + } + + for( const JsonObject &jo_inner : jo.get_array( "type_weights" ) ) { + int weight = 0; + mandatory( jo_inner, was_loaded, "weight", weight ); + relic_procgen_data::type val; + mandatory( jo_inner, was_loaded, "value", val ); + + type_weights.add( val, weight ); + } + + for( const JsonObject &jo_inner : jo.get_array( "items" ) ) { + int weight = 0; + mandatory( jo_inner, was_loaded, "weight", weight ); + itype_id it; + mandatory( jo_inner, was_loaded, "item", it ); + + item_weights.add( it, weight ); + } +} + +void relic_procgen_data::deserialize( JsonIn &jsin ) +{ + JsonObject jobj = jsin.get_object(); + load( jobj ); +} + void relic::load( const JsonObject &jo ) { if( jo.has_array( "active_effects" ) ) { @@ -458,4 +573,206 @@ void process_recharge( item &itm, Character &carrier ) process_recharge_entry( itm, rech, carrier ); } } + +generic_factory relic_procgen_data_factory( "relic_procgen_data" ); } // namespace relic_funcs + +int relic::power_level( const relic_procgen_id &ruleset ) const +{ + int total_power_level = 0; + for( const enchantment &ench : passive_effects ) { + total_power_level += ruleset->power_level( ench ); + } + for( const fake_spell &sp : active_effects ) { + total_power_level += ruleset->power_level( sp ); + } + return total_power_level; +} + +int relic_procgen_data::power_level( const enchantment &ench ) const +{ + int power = 0; + + for( const weighted_object> + &add_val_passive : passive_add_procgen_values ) { + int val = ench.get_value_add( add_val_passive.obj.type ); + if( val != 0 ) { + power += static_cast( add_val_passive.obj.power_per_increment ) / + static_cast( add_val_passive.obj.increment ) * val; + } + } + + for( const weighted_object> + &mult_val_passive : passive_mult_procgen_values ) { + float val = ench.get_value_multiply( mult_val_passive.obj.type ); + if( val != 0.0f ) { + power += mult_val_passive.obj.power_per_increment / mult_val_passive.obj.increment * val; + } + } + + return power; +} + +int relic_procgen_data::power_level( const fake_spell &sp ) const +{ + for( const weighted_object &vals : + active_procgen_values ) { + if( vals.obj.activated_spell == sp.id ) { + return vals.obj.calc_power( sp.level ); + } + } + return 0; +} + +item relic_procgen_data::create_item( const relic_procgen_data::generation_rules &rules ) const +{ + const itype_id *it_id = item_weights.pick(); + if( it_id == nullptr ) { + debugmsg( "ERROR: %s procgen data does not have items", id.c_str() ); + return null_item_reference(); + } + + item it( *it_id, calendar::turn ); + + it.overwrite_relic( generate( rules, *it_id ) ); + + return it; +} + +relic relic_procgen_data::generate( const relic_procgen_data::generation_rules &rules, + const itype_id &it_id ) const +{ + relic ret; + int num_attributes = 0; + int negative_attribute_power = 0; + const bool is_armor = item( it_id ).is_armor(); + + while( rules.max_attributes > num_attributes && rules.power_level > ret.power_level( id ) ) { + switch( *type_weights.pick() ) { + case relic_procgen_data::type::active_enchantment: { + const relic_procgen_data::enchantment_active *active = active_procgen_values.pick(); + if( active != nullptr ) { + fake_spell active_sp; + active_sp.id = active->activated_spell; + active_sp.level = rng( active->min_level, active->max_level ); + num_attributes++; + int power = power_level( active_sp ); + if( power < 0 ) { + if( rules.max_negative_power > negative_attribute_power ) { + break; + } + negative_attribute_power += power; + } + ret.add_active_effect( active_sp ); + } + break; + } + case relic_procgen_data::type::passive_enchantment_add: { + const relic_procgen_data::enchantment_value_passive *add = passive_add_procgen_values.pick(); + if( add != nullptr ) { + enchantment ench; + int value = rng( add->min_value, add->max_value ); + if( value == 0 ) { + break; + } + ench.add_value_add( add->type, value ); + num_attributes++; + int negative_ench_attribute = power_level( ench ); + if( negative_ench_attribute < 0 ) { + if( rules.max_negative_power > negative_attribute_power ) { + break; + } + negative_attribute_power += negative_ench_attribute; + } + if( is_armor ) { + ench.set_has( enchantment::has::WORN ); + } else { + ench.set_has( enchantment::has::WIELD ); + } + ret.add_passive_effect( ench ); + } + break; + } + case relic_procgen_data::type::passive_enchantment_mult: { + const relic_procgen_data::enchantment_value_passive *mult = + passive_mult_procgen_values.pick(); + if( mult != nullptr ) { + enchantment ench; + float value = rng( mult->min_value, mult->max_value ); + ench.add_value_mult( mult->type, value ); + num_attributes++; + int negative_ench_attribute = power_level( ench ); + if( negative_ench_attribute < 0 ) { + if( rules.max_negative_power > negative_attribute_power ) { + break; + } + negative_attribute_power += negative_ench_attribute; + } + if( is_armor ) { + ench.set_has( enchantment::has::WORN ); + } else { + ench.set_has( enchantment::has::WIELD ); + } + ret.add_passive_effect( ench ); + } + break; + } + case relic_procgen_data::type::hit_me: { + const relic_procgen_data::enchantment_active *active = passive_hit_me.pick(); + if( active != nullptr ) { + fake_spell active_sp; + active_sp.id = active->activated_spell; + active_sp.level = rng( active->min_level, active->max_level ); + num_attributes++; + enchantment ench; + ench.add_hit_me( active_sp ); + int power = power_level( ench ); + if( power < 0 ) { + if( rules.max_negative_power > negative_attribute_power ) { + break; + } + negative_attribute_power += power; + } + if( is_armor ) { + ench.set_has( enchantment::has::WORN ); + } else { + ench.set_has( enchantment::has::WIELD ); + } + ret.add_passive_effect( ench ); + } + break; + } + case relic_procgen_data::type::hit_you: { + const relic_procgen_data::enchantment_active *active = passive_hit_you.pick(); + if( active != nullptr ) { + fake_spell active_sp; + active_sp.id = active->activated_spell; + active_sp.level = rng( active->min_level, active->max_level ); + num_attributes++; + enchantment ench; + ench.add_hit_you( active_sp ); + int power = power_level( ench ); + if( power < 0 ) { + if( rules.max_negative_power > negative_attribute_power ) { + break; + } + negative_attribute_power += power; + } + if( is_armor ) { + ench.set_has( enchantment::has::WORN ); + } else { + ench.set_has( enchantment::has::WIELD ); + } + ret.add_passive_effect( ench ); + } + break; + } + case relic_procgen_data::type::last: { + debugmsg( "ERROR: invalid relic procgen type" ); + break; + } + } + } + + return ret; +} \ No newline at end of file diff --git a/src/relic.h b/src/relic.h index 55a1658f109f..94160a152381 100644 --- a/src/relic.h +++ b/src/relic.h @@ -9,11 +9,14 @@ #include "magic.h" #include "magic_enchantment.h" #include "translations.h" +#include "weighted_list.h" class Creature; class JsonIn; class JsonObject; class JsonOut; +class relic; +class relic_procgen_data; struct tripoint; enum class relic_recharge_type { @@ -99,6 +102,107 @@ class relic_recharge void check() const; }; +using relic_procgen_id = string_id; + +class relic_procgen_data +{ + public: + + /* + * various procgen values for passive enchantment values + * this is a template for the ability to write a little bit + * less code and easier maintainability for additional values + */ + template + struct enchantment_value_passive { + enchant_vals::mod type; + // THIS CANNOT BE 0 + int power_per_increment = 1; + // whatever increment is used for the point values + // THIS CANNOT BE 0 + T increment = 1; + T min_value = 0; + T max_value = 0; + + int calc_power( T level ) const { + return std::round( level * static_cast( power_per_increment ) / + static_cast( increment ) ); + } + + bool was_loaded = false; + + void load( const JsonObject &jo ); + void deserialize( JsonIn &jsin ); + }; + + struct enchantment_active { + spell_id activated_spell; + // power cost of spell at level 0 + int base_power = 0; + // power cost increment per spell level increment + int power_per_increment = 1; + // number of spell levels that give the power per increment at + int increment = 1; + // min level of the spell allowed + int min_level = 0; + // max level of the spell allowed + int max_level = 0; + + int calc_power( int level ) const { + return base_power + std::round( level * + static_cast( power_per_increment ) / static_cast( increment ) ); + } + + bool was_loaded = false; + + void load( const JsonObject &jo ); + void deserialize( JsonIn &jsin ); + }; + + struct generation_rules { + // the desired power level for the generated artifact + int power_level = 0; + // the most negative (total) attributes a generated artifact can have + int max_negative_power = 0; + // the maximum number of attributes a generated artifact can have + int max_attributes = INT_MAX; + }; + + enum type { + passive_enchantment_add, + passive_enchantment_mult, + hit_you, + hit_me, + active_enchantment, + last + }; + private: + + weighted_int_list> passive_add_procgen_values; + weighted_int_list> passive_mult_procgen_values; + weighted_int_list passive_hit_you; + weighted_int_list passive_hit_me; + weighted_int_list active_procgen_values; + weighted_int_list type_weights; + weighted_int_list item_weights; + + public: + relic_procgen_id id; + + int power_level( const enchantment &ench ) const; + // power level of the active spell + int power_level( const fake_spell &sp ) const; + + item create_item( const relic_procgen_data::generation_rules &rules ) const; + relic generate( const generation_rules &rules, const itype_id &it_id ) const; + + bool was_loaded; + + static void load_relic_procgen_data( const JsonObject &jo, const std::string &src ); + void load( const JsonObject &jo, const std::string & = "" ); + void deserialize( JsonIn &jsin ); +}; + class relic { private: @@ -135,6 +239,9 @@ class relic return recharge_scheme; } + // what is the power level of this artifact, given a specific ruleset + int power_level( const relic_procgen_id &ruleset ) const; + void check() const; }; @@ -148,4 +255,11 @@ void process_recharge( item &itm, Character &carrier ); } // namespace relic_funcs +template struct enum_traits; + +template<> +struct enum_traits { + static constexpr relic_procgen_data::type last = relic_procgen_data::type::last; +}; + #endif // CATA_SRC_RELIC_H From bbf1f2cde7f3f987127180b4d02a2baf1c1dac83 Mon Sep 17 00:00:00 2001 From: Chaosvolt Date: Sat, 27 Jan 2024 13:40:47 -0600 Subject: [PATCH 02/10] Commit for remote Co-Authored-By: Curtis Merrill <30208401+KorGgenT@users.noreply.github.com> --- data/json/artifact/relic_procgen_data.json | 125 ++++++++++++++++++ .../en/mod/json/reference/items/relics.md | 56 ++++++++ 2 files changed, 181 insertions(+) create mode 100644 data/json/artifact/relic_procgen_data.json diff --git a/data/json/artifact/relic_procgen_data.json b/data/json/artifact/relic_procgen_data.json new file mode 100644 index 000000000000..b9c8593b5ea8 --- /dev/null +++ b/data/json/artifact/relic_procgen_data.json @@ -0,0 +1,125 @@ +[ + { + "type": "relic_procgen_data", + "id": "cult", + "passive_add_procgen_values": [ + { "weight": 100, "min_value": -1, "max_value": 1, "type": "STRENGTH", "increment": 1, "power_per_increment": 250 }, + { + "weight": 100, + "min_value": -1, + "max_value": 1, + "type": "DEXTERITY", + "increment": 1, + "power_per_increment": 250 + }, + { + "weight": 100, + "min_value": -1, + "max_value": 1, + "type": "PERCEPTION", + "increment": 1, + "power_per_increment": 250 + }, + { + "weight": 100, + "min_value": -1, + "max_value": 1, + "type": "INTELLIGENCE", + "increment": 1, + "power_per_increment": 250 + }, + { + "weight": 100, + "min_value": -100, + "max_value": 25, + "type": "SPEED", + "increment": 5, + "power_per_increment": 200 + } + ], + "type_weights": [ { "weight": 100, "value": "passive_enchantment_add" } ], + "items": [ { "weight": 100, "item": "spoon" } ] + }, + { + "type": "relic_procgen_data", + "id": "netherum_tunnels", + "passive_add_procgen_values": [ + { "weight": 100, "min_value": -1, "max_value": 1, "type": "STRENGTH", "increment": 1, "power_per_increment": 250 }, + { + "weight": 100, + "min_value": -1, + "max_value": 1, + "type": "DEXTERITY", + "increment": 1, + "power_per_increment": 250 + }, + { + "weight": 100, + "min_value": -1, + "max_value": 1, + "type": "PERCEPTION", + "increment": 1, + "power_per_increment": 250 + }, + { + "weight": 100, + "min_value": -1, + "max_value": 1, + "type": "INTELLIGENCE", + "increment": 1, + "power_per_increment": 250 + }, + { + "weight": 100, + "min_value": -100, + "max_value": 25, + "type": "SPEED", + "increment": 5, + "power_per_increment": 200 + } + ], + "type_weights": [ { "weight": 100, "value": "passive_enchantment_add" } ], + "items": [ { "weight": 100, "item": "spoon" } ] + }, + { + "type": "relic_procgen_data", + "id": "alien_reality", + "passive_add_procgen_values": [ + { "weight": 100, "min_value": -1, "max_value": 1, "type": "STRENGTH", "increment": 1, "power_per_increment": 250 }, + { + "weight": 100, + "min_value": -1, + "max_value": 1, + "type": "DEXTERITY", + "increment": 1, + "power_per_increment": 250 + }, + { + "weight": 100, + "min_value": -1, + "max_value": 1, + "type": "PERCEPTION", + "increment": 1, + "power_per_increment": 250 + }, + { + "weight": 100, + "min_value": -1, + "max_value": 1, + "type": "INTELLIGENCE", + "increment": 1, + "power_per_increment": 250 + }, + { + "weight": 100, + "min_value": -100, + "max_value": 25, + "type": "SPEED", + "increment": 5, + "power_per_increment": 200 + } + ], + "type_weights": [ { "weight": 100, "value": "passive_enchantment_add" } ], + "items": [ { "weight": 100, "item": "spoon" } ] + } +] diff --git a/doc/src/content/docs/en/mod/json/reference/items/relics.md b/doc/src/content/docs/en/mod/json/reference/items/relics.md index 742d1ca98cc6..e9a9a1d76339 100644 --- a/doc/src/content/docs/en/mod/json/reference/items/relics.md +++ b/doc/src/content/docs/en/mod/json/reference/items/relics.md @@ -64,3 +64,59 @@ optional): | `rad` | Character or map tile must be irradiated | | `wet` | Character must be wet, or it's raining | | `sky` | Character must be above z=0 | + +# Generation + +The procedural generation of artifacts is defined in Json. The object looks like the following: +```json +{ + "type": "relic_procgen_data", + "id": "cult", + "passive_add_procgen_values": [ + { + "weight": 100, + "min_value": -1, + "max_value": 1, + "type": "STRENGTH", + "increment": 1, + "power_per_increment": 250 + } + ], + "passive_mult_procgen_values": [ + { + "weight": 100, + "min_value": -1.5, + "max_value": 1.5, + "type": "STRENGTH", + "increment": 0.1, + "power_per_increment": 250 + } + ], + "type_weights": [ { "weight": 100, "value": "passive_enchantment_add" } ], + "items": [ { "weight": 100, "item": "spoon" } ] + } +``` + +## passive_add_procgen_values and passive_mult_procgen_values + +As the names suggest, these are *passive* benefits/penalties to having the artifact (ie. always present without activating the artifact's abilities). **Add** values add or subtract from existing scores, and **mult** values multiply them. These are entered as a list of possible 'abilities' the artifact could get. It does not by default get all these abilities, rather when it spawns it selects from the list provided. + +- **weight:** the weight of this value in the list, to be chosen randomly +- **min_value:** the minimum possible value for this value type. for add must be an integer, for mult it can be a float +- **max_value:** the maximum possible value for this value type. for add must be an integer, for mult it can be a float +- **type:** the type of enchantment value. see MAGIC.md for detailed documentation on enchantment values +- **increment:** the increment that is used for the power multiplier +- **power_per_increment:** the power value per increment + +## type_weights +This determines the relative weight of the 'add' and 'mult' types. When generated, an artifact first decides if it is going to apply an 'add' or a 'mult' ability based on the type_weights of each. Then it uses the weights of the entries under the selected type to pick an ability. This continues cycling until the artifact reaches the defined power level. Possible values right now that are functional are: +- passive_enchantment_add +- passive_enchantment_mult + +This must be included in a dataset or it could cause a crash. + +## items +This provides a list of possible items that this artifact can spawn as, if it appears randomly in a hard-coded map extra. + +## Power Level +An artifact's power level is a summation of its attributes. For example, each point of strength addition in the above object, the artifact is a +250 power, so an artifact with +2 strength would have a power level of 500. similarly, if an artifact had a strength multiplier of 0.8, it would have a power level of -500. \ No newline at end of file From f2a72375d8a16083056a18ad53c5686c9a3caa3f Mon Sep 17 00:00:00 2001 From: Chaosvolt Date: Sat, 27 Jan 2024 20:12:19 -0600 Subject: [PATCH 03/10] smol fixes --- src/map.cpp | 2 +- src/mondeath.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/map.cpp b/src/map.cpp index c3076d90f648..41b97ec39410 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -4296,7 +4296,7 @@ void map::spawn_artifact( const tripoint &p, const relic_procgen_id &id ) rules.power_level = 1000; rules.max_negative_power = -2000; - add_item_or_charges( p, id->create_item( rules ) ); + add_item_or_charges( p, item::spawn( id->create_item( rules ) ) ); } void map::spawn_item( const tripoint &p, const itype_id &type_id, diff --git a/src/mondeath.cpp b/src/mondeath.cpp index 220a4ef06338..850e7a5f2f54 100644 --- a/src/mondeath.cpp +++ b/src/mondeath.cpp @@ -575,7 +575,7 @@ void mdeath::amigara( monster &z ) add_msg( _( "Your obsession with the fault fades away…" ) ); } - g->m.spawn_artifact( z.pos() ); + g->m.spawn_artifact( z.pos(), relic_procgen_id( "netherum_tunnels" ) ); } void mdeath::thing( monster &z ) From 08494b429b7f36967bd2135222100244a6db68b7 Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Sun, 28 Jan 2024 02:30:26 +0000 Subject: [PATCH 04/10] style(autofix.ci): automated formatting --- .../en/mod/json/reference/items/relics.md | 84 ++++++++++++------- src/relic.cpp | 14 ++-- 2 files changed, 59 insertions(+), 39 deletions(-) diff --git a/doc/src/content/docs/en/mod/json/reference/items/relics.md b/doc/src/content/docs/en/mod/json/reference/items/relics.md index e9a9a1d76339..268aa6e587e8 100644 --- a/doc/src/content/docs/en/mod/json/reference/items/relics.md +++ b/doc/src/content/docs/en/mod/json/reference/items/relics.md @@ -68,55 +68,75 @@ optional): # Generation The procedural generation of artifacts is defined in Json. The object looks like the following: + ```json { - "type": "relic_procgen_data", - "id": "cult", - "passive_add_procgen_values": [ - { - "weight": 100, - "min_value": -1, - "max_value": 1, - "type": "STRENGTH", - "increment": 1, - "power_per_increment": 250 - } - ], - "passive_mult_procgen_values": [ - { - "weight": 100, - "min_value": -1.5, - "max_value": 1.5, - "type": "STRENGTH", - "increment": 0.1, - "power_per_increment": 250 - } - ], - "type_weights": [ { "weight": 100, "value": "passive_enchantment_add" } ], - "items": [ { "weight": 100, "item": "spoon" } ] - } + "type": "relic_procgen_data", + "id": "cult", + "passive_add_procgen_values": [ + { + "weight": 100, + "min_value": -1, + "max_value": 1, + "type": "STRENGTH", + "increment": 1, + "power_per_increment": 250 + } + ], + "passive_mult_procgen_values": [ + { + "weight": 100, + "min_value": -1.5, + "max_value": 1.5, + "type": "STRENGTH", + "increment": 0.1, + "power_per_increment": 250 + } + ], + "type_weights": [{ "weight": 100, "value": "passive_enchantment_add" }], + "items": [{ "weight": 100, "item": "spoon" }] +} ``` ## passive_add_procgen_values and passive_mult_procgen_values -As the names suggest, these are *passive* benefits/penalties to having the artifact (ie. always present without activating the artifact's abilities). **Add** values add or subtract from existing scores, and **mult** values multiply them. These are entered as a list of possible 'abilities' the artifact could get. It does not by default get all these abilities, rather when it spawns it selects from the list provided. +As the names suggest, these are _passive_ benefits/penalties to having the artifact (ie. always +present without activating the artifact's abilities). **Add** values add or subtract from existing +scores, and **mult** values multiply them. These are entered as a list of possible 'abilities' the +artifact could get. It does not by default get all these abilities, rather when it spawns it selects +from the list provided. - **weight:** the weight of this value in the list, to be chosen randomly -- **min_value:** the minimum possible value for this value type. for add must be an integer, for mult it can be a float -- **max_value:** the maximum possible value for this value type. for add must be an integer, for mult it can be a float -- **type:** the type of enchantment value. see MAGIC.md for detailed documentation on enchantment values +- **min_value:** the minimum possible value for this value type. for add must be an integer, for + mult it can be a float +- **max_value:** the maximum possible value for this value type. for add must be an integer, for + mult it can be a float +- **type:** the type of enchantment value. see MAGIC.md for detailed documentation on enchantment + values - **increment:** the increment that is used for the power multiplier - **power_per_increment:** the power value per increment ## type_weights -This determines the relative weight of the 'add' and 'mult' types. When generated, an artifact first decides if it is going to apply an 'add' or a 'mult' ability based on the type_weights of each. Then it uses the weights of the entries under the selected type to pick an ability. This continues cycling until the artifact reaches the defined power level. Possible values right now that are functional are: + +This determines the relative weight of the 'add' and 'mult' types. When generated, an artifact first +decides if it is going to apply an 'add' or a 'mult' ability based on the type_weights of each. Then +it uses the weights of the entries under the selected type to pick an ability. This continues +cycling until the artifact reaches the defined power level. Possible values right now that are +functional are: + - passive_enchantment_add - passive_enchantment_mult This must be included in a dataset or it could cause a crash. ## items -This provides a list of possible items that this artifact can spawn as, if it appears randomly in a hard-coded map extra. + +This provides a list of possible items that this artifact can spawn as, if it appears randomly in a +hard-coded map extra. ## Power Level -An artifact's power level is a summation of its attributes. For example, each point of strength addition in the above object, the artifact is a +250 power, so an artifact with +2 strength would have a power level of 500. similarly, if an artifact had a strength multiplier of 0.8, it would have a power level of -500. \ No newline at end of file + +An artifact's power level is a summation of its attributes. For example, each point of strength +addition in the above object, the artifact is a +250 power, so an artifact with +2 strength would +have a power level of 500. similarly, if an artifact had a strength multiplier of 0.8, it would have +a power level of -500. diff --git a/src/relic.cpp b/src/relic.cpp index 36f441838de1..abea2614882d 100644 --- a/src/relic.cpp +++ b/src/relic.cpp @@ -66,10 +66,10 @@ std::string enum_to_string( relic_recharge_req data ) abort(); } - template<> - std::string enum_to_string( relic_procgen_data::type data ) - { - switch( data ) { +template<> +std::string enum_to_string( relic_procgen_data::type data ) +{ + switch( data ) { // *INDENT-OFF* case relic_procgen_data::type::active_enchantment: return "active_enchantment"; case relic_procgen_data::type::hit_me: return "hit_me"; @@ -78,10 +78,10 @@ std::string enum_to_string( relic_recharge_req data ) case relic_procgen_data::type::passive_enchantment_mult: return "passive_enchantment_mult"; case relic_procgen_data::type::last: break; // *INDENT-ON* - } - debugmsg( "Invalid enchantment::has" ); - abort(); } + debugmsg( "Invalid enchantment::has" ); + abort(); +} } // namespace io bool relic_recharge::operator==( const relic_recharge &rhs ) const From 5538dc09ac8c3733174aa2f9715c19b9b8ec91a2 Mon Sep 17 00:00:00 2001 From: Chaosvolt Date: Sat, 3 Feb 2024 12:41:27 -0600 Subject: [PATCH 05/10] Update relic.cpp --- src/relic.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/relic.cpp b/src/relic.cpp index abea2614882d..1389d9d198a2 100644 --- a/src/relic.cpp +++ b/src/relic.cpp @@ -159,6 +159,11 @@ void relic_recharge::check() const } } +namespace +{ +generic_factory relic_procgen_data_factory( "relic_procgen_data" ); +} // namespace + template<> const relic_procgen_data &string_id::obj() const { @@ -574,7 +579,6 @@ void process_recharge( item &itm, Character &carrier ) } } -generic_factory relic_procgen_data_factory( "relic_procgen_data" ); } // namespace relic_funcs int relic::power_level( const relic_procgen_id &ruleset ) const From e2660b51b9be5c2757fa622446fd0471321aaa61 Mon Sep 17 00:00:00 2001 From: Chaosvolt Date: Tue, 27 Feb 2024 00:10:12 -0600 Subject: [PATCH 06/10] Update relic.cpp --- src/relic.cpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/relic.cpp b/src/relic.cpp index 1389d9d198a2..c6cf8aab0dfc 100644 --- a/src/relic.cpp +++ b/src/relic.cpp @@ -631,16 +631,17 @@ int relic_procgen_data::power_level( const fake_spell &sp ) const item relic_procgen_data::create_item( const relic_procgen_data::generation_rules &rules ) const { const itype_id *it_id = item_weights.pick(); - if( it_id == nullptr ) { + + if( it_id->empty() ) { debugmsg( "ERROR: %s procgen data does not have items", id.c_str() ); - return null_item_reference(); + return detached_ptr(); } - item it( *it_id, calendar::turn ); + detached_ptr it = item::spawn( *it_id, calendar::turn ); - it.overwrite_relic( generate( rules, *it_id ) ); + it->overwrite_relic( generate( rules, *it_id ) ); - return it; + return detached_ptr( it ); } relic relic_procgen_data::generate( const relic_procgen_data::generation_rules &rules, @@ -779,4 +780,4 @@ relic relic_procgen_data::generate( const relic_procgen_data::generation_rules & } return ret; -} \ No newline at end of file +} From 00709a5a437e24caef2e2c253a6634e59d17bcda Mon Sep 17 00:00:00 2001 From: Chaosvolt Date: Tue, 27 Feb 2024 10:45:24 -0600 Subject: [PATCH 07/10] Continue work on the thing a bit --- src/relic.cpp | 2 +- src/relic.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/relic.cpp b/src/relic.cpp index c6cf8aab0dfc..e7b3554e9f19 100644 --- a/src/relic.cpp +++ b/src/relic.cpp @@ -628,7 +628,7 @@ int relic_procgen_data::power_level( const fake_spell &sp ) const return 0; } -item relic_procgen_data::create_item( const relic_procgen_data::generation_rules &rules ) const +detached_ptr relic_procgen_data::create_item( const relic_procgen_data::generation_rules& rules ) const { const itype_id *it_id = item_weights.pick(); diff --git a/src/relic.h b/src/relic.h index 94160a152381..a457c58b6fe1 100644 --- a/src/relic.h +++ b/src/relic.h @@ -193,7 +193,7 @@ class relic_procgen_data // power level of the active spell int power_level( const fake_spell &sp ) const; - item create_item( const relic_procgen_data::generation_rules &rules ) const; + detached_ptr create_item( const relic_procgen_data::generation_rules &rules ) const; relic generate( const generation_rules &rules, const itype_id &it_id ) const; bool was_loaded; From be44b87c3b3f3aefd9e2a21d58bfb94d3bd6ac57 Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Tue, 27 Feb 2024 16:46:23 +0000 Subject: [PATCH 08/10] style(autofix.ci): automated formatting --- src/relic.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/relic.cpp b/src/relic.cpp index e7b3554e9f19..d7de10202688 100644 --- a/src/relic.cpp +++ b/src/relic.cpp @@ -628,7 +628,8 @@ int relic_procgen_data::power_level( const fake_spell &sp ) const return 0; } -detached_ptr relic_procgen_data::create_item( const relic_procgen_data::generation_rules& rules ) const +detached_ptr relic_procgen_data::create_item( const relic_procgen_data::generation_rules + &rules ) const { const itype_id *it_id = item_weights.pick(); From 6eee4cd62b16981aebb39955268b7d0931093208 Mon Sep 17 00:00:00 2001 From: Chaosvolt Date: Sun, 31 Mar 2024 14:36:49 -0500 Subject: [PATCH 09/10] Update map.cpp --- src/map.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/map.cpp b/src/map.cpp index acd85b4379d5..a3c07063e03b 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -4356,7 +4356,7 @@ void map::spawn_artifact( const tripoint &p, const relic_procgen_id &id ) rules.power_level = 1000; rules.max_negative_power = -2000; - add_item_or_charges( p, item::spawn( id->create_item( rules ) ) ); + add_item_or_charges( p, id->create_item( rules ) ); } void map::spawn_item( const tripoint &p, const itype_id &type_id, From 22638fe8da90e00aca2e45d7ce92a3c4ae328918 Mon Sep 17 00:00:00 2001 From: Chaosvolt Date: Tue, 2 Apr 2024 11:41:26 -0500 Subject: [PATCH 10/10] Suggestions per feedback --- src/relic.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/relic.cpp b/src/relic.cpp index d7de10202688..36ed6e94fc29 100644 --- a/src/relic.cpp +++ b/src/relic.cpp @@ -633,7 +633,7 @@ detached_ptr relic_procgen_data::create_item( const relic_procgen_data::ge { const itype_id *it_id = item_weights.pick(); - if( it_id->empty() ) { + if( it_id->is_empty() ) { debugmsg( "ERROR: %s procgen data does not have items", id.c_str() ); return detached_ptr(); } @@ -642,7 +642,7 @@ detached_ptr relic_procgen_data::create_item( const relic_procgen_data::ge it->overwrite_relic( generate( rules, *it_id ) ); - return detached_ptr( it ); + return it; } relic relic_procgen_data::generate( const relic_procgen_data::generation_rules &rules,