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

[DEBUG] Modify hordes through overmap editor #76269

Merged
merged 1 commit into from
Sep 18, 2024
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
7 changes: 7 additions & 0 deletions data/raw/keybindings.json
Original file line number Diff line number Diff line change
Expand Up @@ -1234,6 +1234,13 @@
"name": "Teleport to cursor",
"bindings": [ { "input_method": "keyboard_any", "key": "d" } ]
},
{
"type": "keybinding",
"id": "MODIFY_HORDE",
"category": "OVERMAP",
"name": "Modify existing horde or place new one",
"bindings": [ { "input_method": "keyboard_any", "key": "p" } ]
},
{
"type": "keybinding",
"name": "View missions",
Expand Down
3 changes: 3 additions & 0 deletions src/debug_menu.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

class Character;
class Creature;
struct mongroup;
struct tripoint;

template <typename E> struct enum_traits;
Expand Down Expand Up @@ -121,6 +122,8 @@ void wishitem( Character *you = nullptr );
void wishitem( Character *you, const tripoint & );
void wishitem( Character *you, const tripoint_bub_ms & );
void wishmonster( const std::optional<tripoint> &p );
void wishmonstergroup( tripoint_abs_omt &loc );
void wishmonstergroup_mon_selection( mongroup &group );
void wishmutate( Character *you );
void wishbionics( Character *you );
/*
Expand Down
5 changes: 5 additions & 0 deletions src/mongroup.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -449,6 +449,11 @@ void MonsterGroupManager::FinalizeMonsterGroups()
}
}

std::map<mongroup_id, MonsterGroup> &MonsterGroupManager::Get_all_Groups()
{
return MonsterGroupManager::monsterGroupMap;
}

void MonsterGroupManager::LoadMonsterGroup( const JsonObject &jo )
{
float mon_upgrade_factor = get_option<float>( "MONSTER_UPGRADE_FACTOR" );
Expand Down
3 changes: 3 additions & 0 deletions src/mongroup.h
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,9 @@ class MonsterGroupManager

static bool is_animal( const mongroup_id &group );

// Public getter for private monsterGroupMap, do not use if you don't know what you're doing.
static std::map<mongroup_id, MonsterGroup> &Get_all_Groups();

private:
static std::map<mongroup_id, MonsterGroup> monsterGroupMap;
using t_string_set = std::set<std::string>;
Expand Down
17 changes: 17 additions & 0 deletions src/overmap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7553,6 +7553,23 @@ void overmap::debug_force_add_group( const mongroup &group )
add_mon_group( group, 1 );
}

std::vector<std::reference_wrapper<mongroup>> overmap::debug_unsafe_get_groups_at(
tripoint_abs_omt &loc )
{
point_abs_om overmap;
tripoint_om_omt omt_within_overmap;
std::tie( overmap, omt_within_overmap ) = project_remain<coords::om>( loc );
tripoint_om_sm om_sm_pos = project_to<coords::sm>( omt_within_overmap );

std::vector<std::reference_wrapper <mongroup>> groups_at;
for( std::pair<const tripoint_om_sm, mongroup> &pair : zg ) {
if( pair.first == om_sm_pos ) {
groups_at.emplace_back( pair.second );
}
}
return groups_at;
}

void overmap::add_mon_group( const mongroup &group )
{
zg.emplace( group.rel_pos(), group );
Expand Down
1 change: 1 addition & 0 deletions src/overmap.h
Original file line number Diff line number Diff line change
Expand Up @@ -540,6 +540,7 @@ class overmap

// DEBUG ONLY!
void debug_force_add_group( const mongroup &group );
std::vector<std::reference_wrapper<mongroup>> debug_unsafe_get_groups_at( tripoint_abs_omt &loc );
private:
/**
* Iterate over the overmap and place the quota of specials.
Expand Down
93 changes: 93 additions & 0 deletions src/overmap_ui.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
#include "cuboid_rectangle.h"
#include "cursesdef.h"
#include "display.h"
#include "debug_menu.h"
#include "game.h"
#include "game_constants.h"
#include "game_ui.h"
Expand Down Expand Up @@ -1151,6 +1152,7 @@ static void draw_om_sidebar( ui_adaptor &ui,
print_hint( "PLACE_SPECIAL", c_light_blue );
print_hint( "SET_SPECIAL_ARGS", c_light_blue );
print_hint( "LONG_TELEPORT", c_light_blue );
print_hint( "MODIFY_HORDE", c_light_blue );
++y;
}

Expand Down Expand Up @@ -1627,6 +1629,92 @@ static void set_special_args( tripoint_abs_omt &curs )
*maybe_args = args;
}

static void modify_horde_func( tripoint_abs_omt &curs )
{
overmap &map_at_cursor = overmap_buffer.get( project_to<coords::om>( curs ).xy() );
std::vector<std::reference_wrapper<mongroup>> hordes =
map_at_cursor.debug_unsafe_get_groups_at( curs );
if( hordes.empty() ) {
if( !query_yn( _( "No hordes there. Would you like to make a new horde?" ) ) ) {
return;
} else {
debug_menu::wishmonstergroup( curs );
return;
}
}
uilist horde_list;
int entry_num = 0;
for( auto &horde_wrapper : hordes ) {
mongroup &mg = horde_wrapper.get();
// We do some special handling here to provide the information in as simple a way as possible
// emulates horde behavior
int displayed_monster_num = mg.monsters.empty() ? mg.population : mg.monsters.size();
std::string horde_description = string_format( _( "group(type: %s) with %d monsters" ),
mg.type.c_str(), displayed_monster_num );
horde_list.addentry( entry_num, true, -1, horde_description );
}
horde_list.query();
int &selected_group = horde_list.ret;
if( selected_group < 0 || static_cast<size_t>( selected_group ) > hordes.size() ) {
return;
}
mongroup &chosen_group = hordes[selected_group];

uilist smenu;
smenu.addentry( 0, true, 'I', _( "Set horde's interest (in %%)" ) );
smenu.addentry( 1, true, 'D', _( "Set horde's destination" ) );
smenu.addentry( 2, true, 'P', _( "Modify horde's population" ) );
smenu.addentry( 3, true, 'M', _( "Add a new monster to the horde" ) );
smenu.addentry( 4, true, 'B', _( "Set horde behavior" ) );
smenu.addentry( 5, true, 'T', _( "Set horde's boolean values" ) );
smenu.addentry( 6, true, 'A', _( "Add another horde to this location" ) );
smenu.query();
int new_value = 0;
tripoint_abs_omt horde_destination = tripoint_abs_omt_zero;
switch( smenu.ret ) {
case 0:
query_int( new_value, _( "Set interest to what value? Currently %d" ), chosen_group.interest );
chosen_group.set_interest( new_value );
break;
case 1:
popup( _( "Select a target destination for the horde." ) );
horde_destination = ui::omap::choose_point( true );
if( horde_destination == overmap::invalid_tripoint || horde_destination == tripoint_abs_omt_zero ) {
break;
}
chosen_group.target = project_to<coords::sm>( horde_destination ).xy();
break;
case 2:
query_int( new_value, _( "Set population to what value? Currently %d" ), chosen_group.population );
chosen_group.population = new_value;
break;
case 3:
debug_menu::wishmonstergroup_mon_selection( chosen_group );
break;
case 4:
// Screw it we hardcode a popup, if you really want to use this you're welcome to improve it
popup( _( "Set behavior to which enum value? Currently %d. \nAccepted values:\n0 = none,\n1 = city,\n2=roam,\n3=nemesis" ),
static_cast<int>( chosen_group.behaviour ) );
query_int( new_value, "" );
chosen_group.behaviour = static_cast<mongroup::horde_behaviour>( new_value );
break;
case 5:
// One day we'll be able to simply convert booleans to strings...
chosen_group.horde = query_yn(
_( "Set group's \"horde\" value to true? (Select no to set to false) \nCurrently %s" ),
chosen_group.horde ? _( "true" ) : _( "false" ) );
chosen_group.dying = query_yn(
_( "Set group's \"dying\" value to true? (Select no to set to false) \nCurrently %s" ),
chosen_group.dying ? _( "true" ) : _( "false" ) );
break;
case 6:
debug_menu::wishmonstergroup( curs );
break;
default:
break;
}
}

static std::vector<tripoint_abs_omt> get_overmap_path_to( const tripoint_abs_omt &dest,
bool driving )
{
Expand Down Expand Up @@ -1807,6 +1895,7 @@ static tripoint_abs_omt display()
ictxt.register_action( "PLACE_SPECIAL" );
ictxt.register_action( "SET_SPECIAL_ARGS" );
ictxt.register_action( "LONG_TELEPORT" );
ictxt.register_action( "MODIFY_HORDE" );
}
ictxt.register_action( "QUIT" );
std::string action;
Expand Down Expand Up @@ -2012,6 +2101,9 @@ static tripoint_abs_omt display()
g->place_player_overmap( curs );
add_msg( _( "You teleport to submap %s." ), curs.to_string() );
action = "QUIT";
} else if( action == "MODIFY_HORDE" ) {
modify_horde_func( curs );
action = "QUIT";
} else if( action == "MISSIONS" ) {
g->list_missions();
}
Expand Down Expand Up @@ -2279,6 +2371,7 @@ void ui::omap::display()
{
g->overmap_data = overmap_ui::overmap_draw_data_t(); //reset data
g->overmap_data.origin_pos = get_player_character().global_omt_location();
g->overmap_data.debug_editor = debug_mode; // always display debug editor if game is in debug mode
overmap_ui::display();
}

Expand Down
100 changes: 88 additions & 12 deletions src/wish.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,10 @@
#include "item_factory.h"
#include "itype.h"
#include "localized_comparator.h"
#include "overmap.h"
#include "overmapbuffer.h"
#include "map.h"
#include "mongroup.h"
#include "monster.h"
#include "monstergenerator.h"
#include "mtype.h"
Expand All @@ -49,6 +52,8 @@

static const efftype_id effect_pet( "pet" );

static const mongroup_id GROUP_ZOMBIE( "GROUP_ZOMBIE" );

class ui_adaptor;

class wish_mutate_callback: public uilist_callback
Expand Down Expand Up @@ -713,26 +718,97 @@ class wish_monster_callback: public uilist_callback
~wish_monster_callback() override = default;
};

static void setup_wishmonster( uilist &pick_a_monster, std::vector<const mtype *> &mtypes )
{
pick_a_monster.desired_bounds = { -1.0, -1.0, 1.0, 1.0 };
pick_a_monster.selected = uistate.wishmonster_selected;
int i = 0;
for( const mtype &montype : MonsterGenerator::generator().get_all_mtypes() ) {
pick_a_monster.addentry( i, true, 0, montype.nname() );
pick_a_monster.entries[i].extratxt.txt = montype.sym;
pick_a_monster.entries[i].extratxt.color = montype.color;
pick_a_monster.entries[i].extratxt.left = 1;
++i;
mtypes.push_back( &montype );
}
}

void debug_menu::wishmonstergroup( tripoint_abs_omt &loc )
{
bool population_group = query_yn(
_( "Is this a population-based horde, based on an existing monster group? Select \"No\" to manually assign monsters." ) );
mongroup new_group;
// Just in case, we manually set some values
new_group.abs_pos = project_to<coords::sm>( loc );
new_group.target = new_group.abs_pos.xy();
new_group.population = 1; // overwritten if population_group
new_group.horde = true;
new_group.type = GROUP_ZOMBIE; // overwritten if population_group
if( population_group ) {
uilist type_selection;
std::vector<mongroup_id> possible_groups;
int i = 0;
for( auto &group_pair : MonsterGroupManager::Get_all_Groups() ) {
possible_groups.emplace_back( group_pair.first );
type_selection.addentry( i, true, -1, group_pair.first.c_str() );
i++;
}
type_selection.query();
int &selected = type_selection.ret;
if( selected < 0 || static_cast<size_t>( selected ) >= possible_groups.size() ) {
return;
}
const mongroup_id selected_group( possible_groups[selected] );
new_group.type = selected_group;
int new_value = 0;
query_int( new_value, _( "Set population to what value? Currently %d" ), new_group.population );
overmap &there = overmap_buffer.get( project_to<coords::om>( loc ).xy() );
there.debug_force_add_group( new_group );
return; // Don't go to adding individual monsters, they'll override the values we just set
}


wishmonstergroup_mon_selection( new_group );
overmap &there = overmap_buffer.get( project_to<coords::om>( loc ).xy() );
there.debug_force_add_group( new_group );
}

void debug_menu::wishmonstergroup_mon_selection( mongroup &group )
{

std::vector<const mtype *> mtypes;
uilist new_mon_selection;
setup_wishmonster( new_mon_selection, mtypes );
wish_monster_callback cb( mtypes );
new_mon_selection.callback = &cb;
new_mon_selection.query();
int &selected = new_mon_selection.ret;
if( selected < 0 || static_cast<size_t>( selected ) >= mtypes.size() ) {
return;
}
const mtype_id &mon_type = mtypes[ selected ]->id;
for( int i = 0; i < cb.group; i++ ) {
monster new_mon( mon_type );
if( cb.friendly ) {
new_mon.friendly = -1;
new_mon.add_effect( effect_pet, 1_turns, true );
}
if( cb.hallucination ) {
new_mon.hallucination = true;
}
group.monsters.emplace_back( new_mon );
}
}

void debug_menu::wishmonster( const std::optional<tripoint> &p )
{
std::vector<const mtype *> mtypes;

uilist wmenu;
wmenu.desired_bounds = { -1.0, -1.0, 1.0, 1.0 };
wmenu.selected = uistate.wishmonster_selected;
setup_wishmonster( wmenu, mtypes );
wish_monster_callback cb( mtypes );
wmenu.callback = &cb;

int i = 0;
for( const mtype &montype : MonsterGenerator::generator().get_all_mtypes() ) {
wmenu.addentry( i, true, 0, montype.nname() );
wmenu.entries[i].extratxt.txt = montype.sym;
wmenu.entries[i].extratxt.color = montype.color;
wmenu.entries[i].extratxt.left = 1;
++i;
mtypes.push_back( &montype );
}

do {
wmenu.query();
if( wmenu.ret >= 0 ) {
Expand Down
Loading