Skip to content

Commit

Permalink
Modify hordes through overmap editor
Browse files Browse the repository at this point in the history
  • Loading branch information
RenechCDDA committed Sep 10, 2024
1 parent e398be1 commit 8d4c3af
Show file tree
Hide file tree
Showing 8 changed files with 217 additions and 12 deletions.
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;
class mongroup;

Check failure on line 15 in src/debug_menu.h

View workflow job for this annotation

GitHub Actions / build (src)

class 'mongroup' was previously declared as a struct; this is valid, but may result in linker errors under the Microsoft C++ ABI [clang-diagnostic-mismatched-tags,-warnings-as-errors]

Check failure on line 15 in src/debug_menu.h

View workflow job for this annotation

GitHub Actions / build (src)

class 'mongroup' was previously declared as a struct; this is valid, but may result in linker errors under the Microsoft C++ ABI [clang-diagnostic-mismatched-tags,-warnings-as-errors]
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 @@ -1150,6 +1151,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 @@ -1626,6 +1628,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 @@ -1806,6 +1894,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 @@ -2011,6 +2100,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 @@ -2278,6 +2370,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

0 comments on commit 8d4c3af

Please sign in to comment.