diff --git a/data/json/professions.json b/data/json/professions.json index 86f7358649af8..7c3d7230f1b9b 100644 --- a/data/json/professions.json +++ b/data/json/professions.json @@ -1481,6 +1481,23 @@ "female": [ "bra", "panties" ] } }, + { + "type": "profession", + "ident": "trucker", + "name": "Trucker", + "description": "You ruled the road in your big rig and managed to drive it somewhere you hoped was safe when the riots hit. Now it's just you and your trusty truck cab.", + "points": 5, + "skills": [ { "level": 1, "name": "mechanics" }, { "level": 4, "name": "driving" } ], + "vehicle": "semi_truck", + "items": { + "both": { + "items": [ "tank_top", "socks", "boots_steel", "pants", "multitool", "wristwatch", "gloves_work", "hat_ball" ], + "entries": [ { "group": "charged_cell_phone" } ] + }, + "male": [ "boxer_shorts" ], + "female": [ "bra", "panties" ] + } + }, { "type": "profession", "ident": "lumberjack", diff --git a/doc/JSON_INFO.md b/doc/JSON_INFO.md index 558aefa103f23..8daf02a4eba57 100644 --- a/doc/JSON_INFO.md +++ b/doc/JSON_INFO.md @@ -803,12 +803,20 @@ Example for mods: This mod removes one of the rocks (the other rock is still created), the t-shirt, adds a 2x4 item and gives female characters a t-shirt with the special snippet id. -#### `pet` +#### `pets` -(optional, string mtype_id) +(optional, array of string mtype_ids ) -A string that is the same as a monster id -player will start with this as a tamed pet. +A list of strings, each is the same as a monster id +player will start with these as tamed pets. + +#### `vehicle` + +(optional, string vproto_id ) + +A string, which is the same as a vehicle ( vproto_id ) +player will start with this as a nearby vehicle. +( it will find the nearest road and place it there, then mark it as "remembered" on the overmap ) #### `flags` diff --git a/src/game.cpp b/src/game.cpp index 76136a41b2ec6..e96aa66d02852 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -838,6 +838,11 @@ bool game::start_game() add_msg( m_debug, "cannot place starting pet, no space!" ); } } + if( u.starting_vehicle && + !place_vehicle_nearby( u.starting_vehicle, u.global_omt_location().xy(), 1, 30, + std::vector {} ) ) { + debugmsg( "could not place starting vehicle" ); + } // Assign all of this scenario's missions to the player. for( const mission_type_id &m : scen->missions() ) { const auto mission = mission::reserve_new( m, character_id() ); @@ -848,6 +853,53 @@ bool game::start_game() return true; } +vehicle *game::place_vehicle_nearby( const vproto_id &id, const point &origin, int min_distance, + int max_distance, const std::vector &omt_search_types ) +{ + std::vector search_types = omt_search_types; + if( search_types.empty() ) { + vehicle veh( id ); + std::vector omt_search_types; + if( veh.max_ground_velocity() > 0 ) { + search_types.push_back( "road" ); + search_types.push_back( "field" ); + } else if( veh.can_float() ) { + search_types.push_back( "river" ); + search_types.push_back( "lake" ); + } + } + for( const std::string &search_type : search_types ) { + omt_find_params find_params; + find_params.must_see = false; + find_params.cant_see = false; + find_params.types.emplace_back( search_type, ot_match_type::type ); + // find nearest road + find_params.min_distance = min_distance; + find_params.search_range = max_distance; + // if player spawns underground, park their car on the surface. + const tripoint omt_origin( origin.x, origin.y, 0 ); + for( const tripoint &goal : overmap_buffer.find_all( omt_origin, find_params ) ) { + // try place vehicle there. + tinymap target_map; + target_map.load( omt_to_sm_copy( goal ), false ); + const tripoint origin( SEEX, SEEY, goal.z ); + static const std::vector angles = {0, 90, 180, 270}; + vehicle *veh = target_map.add_vehicle( id, origin, random_entry( angles ), rng( 50, 80 ), + 0, + false ); + if( veh ) { + tripoint abs_local = g->m.getlocal( target_map.getabs( origin ) ); + veh->sm_pos = ms_to_sm_remain( abs_local ); + veh->pos = abs_local.xy(); + overmap_buffer.add_vehicle( veh ); + target_map.save(); + return veh; + } + } + } + return nullptr; +} + //Make any nearby overmap npcs active, and put them in the right location. void game::load_npcs() { diff --git a/src/game.h b/src/game.h index b267103adb732..fbbca30dcfdbd 100644 --- a/src/game.h +++ b/src/game.h @@ -733,7 +733,9 @@ class game // Data Initialization void init_autosave(); // Initializes autosave parameters void create_starting_npcs(); // Creates NPCs that start near you - + // create vehicle nearby, for example; for a profession vehicle. + vehicle *place_vehicle_nearby( const vproto_id &id, const point &origin, int min_distance, + int max_distance, const std::vector &omt_search_types = {} ); // V Menu Functions and helpers: void list_items_monsters(); // Called when you invoke the `V`-menu diff --git a/src/newcharacter.cpp b/src/newcharacter.cpp index 8e38efc495c45..1364aa4da65d4 100644 --- a/src/newcharacter.cpp +++ b/src/newcharacter.cpp @@ -52,6 +52,7 @@ #include "pimpl.h" #include "type_id.h" #include "cata_string_consts.h" +#include "veh_type.h" // Colors used in this file: (Most else defaults to c_light_gray) #define COL_STAT_ACT c_white // Selected stat @@ -518,6 +519,7 @@ bool avatar::create( character_type type, const std::string &tempname ) for( mtype_id elem : prof->pets() ) { starting_pets.push_back( elem ); } + starting_vehicle = prof->vehicle(); std::list prof_items = prof->items( male, get_mutations() ); for( item &it : prof_items ) { @@ -1476,7 +1478,6 @@ tab_direction set_profession( const catacurses::window &w, avatar &u, points_lef } } // Profession pet - cata::optional montype; if( !sorted_profs[cur_id]->pets().empty() ) { buffer += colorize( _( "Pets:" ), c_light_blue ) + "\n"; for( auto elem : sorted_profs[cur_id]->pets() ) { @@ -1484,6 +1485,12 @@ tab_direction set_profession( const catacurses::window &w, avatar &u, points_lef buffer += mon.get_name() + "\n"; } } + // Profession vehicle + if( sorted_profs[cur_id]->vehicle() ) { + buffer += colorize( _( "Vehicle:" ), c_light_blue ) + "\n"; + vproto_id veh_id = sorted_profs[cur_id]->vehicle(); + buffer += veh_id->name; + } // Profession spells if( !sorted_profs[cur_id]->spells().empty() ) { buffer += colorize( _( "Spells:" ), c_light_blue ) + "\n"; diff --git a/src/overmapbuffer.cpp b/src/overmapbuffer.cpp index 2de6c86b85028..516c69e5fc9db 100644 --- a/src/overmapbuffer.cpp +++ b/src/overmapbuffer.cpp @@ -600,7 +600,8 @@ void overmapbuffer::remove_vehicle( const vehicle *veh ) void overmapbuffer::add_vehicle( vehicle *veh ) { - const point omt = ms_to_omt_copy( g->m.getabs( veh->global_pos3().xy() ) ); + const point abs_pos = g->m.getabs( veh->global_pos3().xy() ); + const point omt = ms_to_omt_copy( abs_pos ); const overmap_with_local_coords om_loc = get_om_global( omt ); int id = om_loc.om->vehicles.size() + 1; // this *should* be unique but just in case diff --git a/src/player.h b/src/player.h index d3c2e13f3b96f..262c258fd5f4d 100644 --- a/src/player.h +++ b/src/player.h @@ -943,7 +943,7 @@ class player : public Character bool reach_attacking = false; bool manual_examine = false; - + vproto_id starting_vehicle; std::vector starting_pets; void make_craft_with_command( const recipe_id &id_to_make, int batch_size, bool is_long = false, diff --git a/src/profession.cpp b/src/profession.cpp index 20765f0229e7b..87f4c62ddd64f 100644 --- a/src/profession.cpp +++ b/src/profession.cpp @@ -171,6 +171,9 @@ void profession::load( const JsonObject &jo, const std::string & ) _description_male = to_translation( "prof_desc_male", desc ); _description_female = to_translation( "prof_desc_female", desc ); } + if( jo.has_string( "vehicle" ) ) { + _starting_vehicle = vproto_id( jo.get_string( "vehicle" ) ); + } if( jo.has_array( "pets" ) ) { for( JsonObject subobj : jo.get_array( "pets" ) ) { int count = subobj.get_int( "amount" ); @@ -286,7 +289,10 @@ void profession::check_definition() const if( !item_group::group_is_defined( _starting_items_female ) ) { debugmsg( "_starting_items_female group is undefined" ); } - + if( _starting_vehicle && !_starting_vehicle.is_valid() ) { + debugmsg( "vehicle prototype %s for profession %s does not exist", _starting_vehicle.c_str(), + id.c_str() ); + } for( const auto &a : _starting_CBMs ) { if( !a.is_valid() ) { debugmsg( "bionic %s for profession %s does not exist", a.c_str(), id.c_str() ); @@ -434,6 +440,11 @@ std::list profession::items( bool male, const std::vector &trait return result; } +vproto_id profession::vehicle() const +{ + return _starting_vehicle; +} + std::vector profession::pets() const { return _starting_pets; diff --git a/src/profession.h b/src/profession.h index 8c3c5fb96340e..3af14b3c274e8 100644 --- a/src/profession.h +++ b/src/profession.h @@ -13,6 +13,7 @@ #include "pldata.h" #include "translations.h" #include "type_id.h" +#include "veh_type.h" template class generic_factory; @@ -69,6 +70,7 @@ class profession std::vector _starting_CBMs; std::vector _starting_traits; std::vector _starting_pets; + vproto_id _starting_vehicle = vproto_id::NULL_ID(); // the int is what level the spell starts at std::map _starting_spells; std::set flags; // flags for some special properties of the profession @@ -103,6 +105,7 @@ class profession signed int point_cost() const; std::list items( bool male, const std::vector &traits ) const; std::vector addictions() const; + vproto_id vehicle() const; std::vector pets() const; std::vector CBMs() const; StartingSkillList skills() const; diff --git a/src/string_id_null_ids.cpp b/src/string_id_null_ids.cpp index 3c57688a3b1dd..15648c9294284 100644 --- a/src/string_id_null_ids.cpp +++ b/src/string_id_null_ids.cpp @@ -26,6 +26,7 @@ MAKE_NULL_ID( faction, "NULL" ) MAKE_NULL_ID( ammunition_type, "NULL" ) MAKE_NULL_ID( vpart_info, "null" ) MAKE_NULL_ID( emit, "null" ) +MAKE_NULL_ID( vehicle_prototype, "null" ) MAKE_NULL_ID( anatomy, "null_anatomy" ) MAKE_NULL_ID( martialart, "style_none" ) MAKE_NULL_ID( recipe, "null" )