@@ -3082,3 +3082,77 @@ TEST_CASE( "npc_zone_sorting_ignores_personal_unsorted_source",
30823082 CHECK ( count_items_or_charges ( avatar_pos, itype_test_apple, std::nullopt ) == 1 );
30833083 CHECK ( count_items_or_charges ( dest_pos, itype_test_apple, std::nullopt ) == 0 );
30843084}
3085+
3086+ // Vehicle zone refresh during sorting must not shift personal zone positions.
3087+ TEST_CASE ( " vehicle_zone_refresh_preserves_personal_zone_positions" ,
3088+ " [zones][items][activities][sorting][vehicle]" )
3089+ {
3090+ avatar &dummy = get_avatar ();
3091+ map &here = get_map ();
3092+
3093+ clear_avatar ();
3094+ clear_map_without_vision ();
3095+ zone_manager &zm = zone_manager::get_manager ();
3096+ zm.clear ();
3097+
3098+ const tripoint_bub_ms start_pos ( 60 , 60 , 0 );
3099+ dummy.setpos ( here, start_pos );
3100+ dummy.clear_destination ();
3101+ here.ter_set ( start_pos, ter_t_floor );
3102+
3103+ const tripoint_abs_ms start_abs = here.get_abs ( start_pos );
3104+
3105+ // Personal UNSORTED zone: 5x5 area centered on player
3106+ zm.add ( " Unsorted" , zone_type_LOOT_UNSORTED, faction_your_followers,
3107+ false , true ,
3108+ tripoint_rel_ms ( -2 , -2 , 0 ), tripoint_rel_ms ( 2 , 2 , 0 ) );
3109+
3110+ // Personal LOOT_FOOD zone at offset (+2, 0)
3111+ zm.add ( " Food" , zone_type_LOOT_FOOD, faction_your_followers,
3112+ false , true ,
3113+ tripoint_rel_ms ( 2 , 0 , 0 ), tripoint_rel_ms ( 2 , 0 , 0 ) );
3114+
3115+ // Vehicle with a loot zone so it enters zone_vehicles and
3116+ // refresh_zones() fires during update_vehicle_zone_cache()
3117+ const tripoint_bub_ms veh_pos = start_pos + tripoint ( 0 , -3 , 0 );
3118+ here.ter_set ( veh_pos, ter_t_floor );
3119+ vehicle *cart = here.add_vehicle ( vehicle_prototype_test_shopping_cart,
3120+ veh_pos, 0_degrees, 0 , 0 );
3121+ REQUIRE ( cart != nullptr );
3122+ cart->set_owner ( dummy );
3123+ const tripoint_abs_ms veh_abs = here.get_abs ( veh_pos );
3124+ create_tile_zone ( " VehUnsorted" , zone_type_LOOT_UNSORTED, veh_abs, true );
3125+ REQUIRE ( zm.has_vehicle ( zone_type_LOOT_UNSORTED, veh_abs, faction_your_followers ) );
3126+
3127+ zm.cache_data ();
3128+ zm.cache_vzones ();
3129+
3130+ here.invalidate_map_cache ( 0 );
3131+ here.build_map_cache ( 0 , true );
3132+
3133+ const tripoint_abs_ms food_abs = start_abs + tripoint ( 2 , 0 , 0 );
3134+ REQUIRE ( zm.has ( zone_type_LOOT_FOOD, food_abs, faction_your_followers ) );
3135+
3136+ // Player walks south, cache_avatar_location updates cached_shift,
3137+ // then vehicle zone refresh triggers (same sequence as during sorting).
3138+ const tripoint_bub_ms moved_pos = start_pos + tripoint ( 0 , 5 , 0 );
3139+ here.ter_set ( moved_pos, ter_t_floor );
3140+ dummy.setpos ( here, moved_pos );
3141+ zm.cache_avatar_location ();
3142+
3143+ cart->zones_dirty = true ;
3144+ here.invalidate_map_cache ( 0 );
3145+ here.build_map_cache ( 0 , true );
3146+ if ( here.check_vehicle_zones ( 0 ) ) {
3147+ zm.cache_vzones ();
3148+ }
3149+
3150+ // Personal LOOT_FOOD must still be at the original position
3151+ CHECK ( zm.has ( zone_type_LOOT_FOOD, food_abs, faction_your_followers ) );
3152+
3153+ // Shifted position must not have it
3154+ const tripoint_abs_ms shifted_food = here.get_abs ( moved_pos ) + tripoint ( 2 , 0 , 0 );
3155+ if ( shifted_food != food_abs ) {
3156+ CHECK_FALSE ( zm.has ( zone_type_LOOT_FOOD, shifted_food, faction_your_followers ) );
3157+ }
3158+ }
0 commit comments