From 2bcdf8aaf5528dc2222f3db5573af47d110b9635 Mon Sep 17 00:00:00 2001 From: Graywolfe813 Date: Fri, 9 Mar 2018 07:45:57 -0700 Subject: [PATCH 01/35] Add support for resizing *nix terminal. --- src/cursesdef.h | 2 +- src/ncurses_def.cpp | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/cursesdef.h b/src/cursesdef.h index c19c8aaf822f..172da09da764 100644 --- a/src/cursesdef.h +++ b/src/cursesdef.h @@ -98,7 +98,6 @@ void mvwvline( const window &win, int y, int x, chtype ch, int n ); void wrefresh( const window &win ); void refresh(); void wredrawln( const window &win, int beg_line, int num_lines ); - void mvwprintw( const window &win, int y, int x, const std::string &text ); template inline void mvwprintw( const window &win, const int y, const int x, const char *const fmt, @@ -114,6 +113,7 @@ inline void wprintw( const window &win, const char *const fmt, Args &&... args ) return wprintw( win, string_format( fmt, std::forward( args )... ) ); } +void resizeterm(); void werase( const window &win ); void init_pair( short pair, base_color f, base_color b ); void wmove( const window &win, int y, int x ); diff --git a/src/ncurses_def.cpp b/src/ncurses_def.cpp index c6fed3eaedf8..a29b7f85c213 100644 --- a/src/ncurses_def.cpp +++ b/src/ncurses_def.cpp @@ -18,6 +18,7 @@ #include "catacharset.h" #include "color.h" +#include "game.h" #include extern int VIEW_OFFSET_X; // X position of terrain window @@ -197,6 +198,15 @@ void catacurses::init_pair( const short pair, const base_color f, const base_col catacurses::window catacurses::stdscr; +void catacurses::resizeterm() +{ + const int new_x = ::getmaxx( stdscr.get<::WINDOW>() ); + const int new_y = ::getmaxy( stdscr.get<::WINDOW>() ); + if( ::is_term_resized( new_x, new_y ) ) { + g->init_ui(); + } +} + // init_interface is defined in another cpp file, depending on build type: // wincurse.cpp for Windows builds without SDL and sdltiles.cpp for SDL builds. void catacurses::init_interface() @@ -245,6 +255,8 @@ input_event input_manager::get_input_event() rval.type = CATA_INPUT_ERROR; } // ncurses mouse handling + } else if( key == KEY_RESIZE ) { + catacurses::resizeterm(); } else if( key == KEY_MOUSE ) { MEVENT event; if( getmouse( &event ) == OK ) { From 26fa9547185c749f16f6d1d4329725cee1ba09b6 Mon Sep 17 00:00:00 2001 From: Graywolfe813 Date: Fri, 9 Mar 2018 10:31:30 -0700 Subject: [PATCH 02/35] Add support for resizing window in Tiles. --- src/cursesport.cpp | 6 ++++++ src/sdltiles.cpp | 17 ++++++++++++++++- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/src/cursesport.cpp b/src/cursesport.cpp index 4112e3e879c3..e9c247f4d266 100644 --- a/src/cursesport.cpp +++ b/src/cursesport.cpp @@ -407,6 +407,12 @@ void catacurses::mvwprintw(const window &win, int y, int x, const std::string &p return printstring(win.get(), printbuf); } +//Resizes the underlying terminal after a Window's console resize(maybe?) Not used in TILES +void catacurses::resizeterm() +{ + //TODO: Windows console implementation +} + //erases a window of all text and attributes void catacurses::werase(const window &win_) { diff --git a/src/sdltiles.cpp b/src/sdltiles.cpp index f989cd441200..aef0fe8346c3 100644 --- a/src/sdltiles.cpp +++ b/src/sdltiles.cpp @@ -323,9 +323,9 @@ bool WinCreate() int window_flags = 0; WindowWidth = TERMINAL_WIDTH * fontwidth; WindowHeight = TERMINAL_HEIGHT * fontheight; + window_flags |= SDL_WINDOW_RESIZABLE; if( get_option( "SCALING_MODE" ) != "none" ) { - window_flags |= SDL_WINDOW_RESIZABLE; SDL_SetHint( SDL_HINT_RENDER_SCALE_QUALITY, get_option( "SCALING_MODE" ).c_str() ); } @@ -1266,6 +1266,21 @@ void CheckMessages() case SDL_WINDOWEVENT_RESTORED: needupdate = true; break; + case SDL_WINDOWEVENT_RESIZED: + WindowWidth = ev.window.data1; + WindowHeight = ev.window.data2; + TERMINAL_WIDTH = WindowWidth / fontwidth; + TERMINAL_HEIGHT = WindowHeight / fontheight; + SetupRenderTarget(); + g->init_ui(); + catacurses::refresh(); + if( g->is_core_data_loaded() ) { + tilecontext->reinit_minimap(); + g->refresh_all(); + } + needupdate = true; + refresh_display(); + break; default: break; } From 1de99f97f47eb4c99cc056731ec6cc0a71d9fce4 Mon Sep 17 00:00:00 2001 From: Graywolfe813 Date: Fri, 9 Mar 2018 15:05:38 -0700 Subject: [PATCH 03/35] Add support for resizing in Windows console. --- src/cursesport.cpp | 3 +- src/output.cpp | 3 ++ src/wincurse.cpp | 87 ++++++++++++++++++++++++++++++++++++---------- 3 files changed, 73 insertions(+), 20 deletions(-) diff --git a/src/cursesport.cpp b/src/cursesport.cpp index e9c247f4d266..8ee4e13b5638 100644 --- a/src/cursesport.cpp +++ b/src/cursesport.cpp @@ -5,6 +5,7 @@ #include "color.h" #include "catacharset.h" #include "animation.h" +#include "game.h" #include // strlen #include @@ -410,7 +411,7 @@ void catacurses::mvwprintw(const window &win, int y, int x, const std::string &p //Resizes the underlying terminal after a Window's console resize(maybe?) Not used in TILES void catacurses::resizeterm() { - //TODO: Windows console implementation + g->init_ui(); } //erases a window of all text and attributes diff --git a/src/output.cpp b/src/output.cpp index b994eb10ac9e..c6d1f500bca7 100644 --- a/src/output.cpp +++ b/src/output.cpp @@ -2088,6 +2088,8 @@ std::string format_volume( const units::volume &volume, int width, bool *out_tru // In non-SDL mode, width/height is just what's specified in the menu #if !defined(TILES) +// We need to override these for Windows console resizing +#if !(defined _WIN32 || defined __WIN32__) int get_terminal_width() { int width = get_option( "TERMINAL_X" ); @@ -2098,6 +2100,7 @@ int get_terminal_height() { return get_option( "TERMINAL_Y" ); } +#endif bool is_draw_tiles_mode() { diff --git a/src/wincurse.cpp b/src/wincurse.cpp index 77b3100e9cd4..309e8e950bcd 100644 --- a/src/wincurse.cpp +++ b/src/wincurse.cpp @@ -49,6 +49,9 @@ std::array::COLOR_NAMES_COUNT> windowsPalette; unsigned char *dcbits; //the bits of the screen image, for direct access bool CursorVisible = true; // Showcursor is a somewhat weird function +static int TERMINAL_WIDTH; +static int TERMINAL_HEIGHT; + //*********************************** //Non-curses, Window functions * //*********************************** @@ -87,7 +90,7 @@ bool WinCreate() return false; // Adjust window size - uint32_t WndStyle = WS_CAPTION | WS_MINIMIZEBOX | WS_SYSMENU | WS_VISIBLE; // Basic window, show on creation + uint32_t WndStyle = WS_CAPTION | WS_MINIMIZEBOX | WS_SIZEBOX | WS_MAXIMIZEBOX | WS_SYSMENU | WS_VISIBLE; // Basic window, show on creation RECT WndRect; WndRect.left = WndRect.top = 0; WndRect.right = WindowWidth; @@ -108,7 +111,7 @@ bool WinCreate() 0, 0, WindowINST, NULL); if (WindowHandle == 0) return false; - + return true; }; @@ -126,6 +129,36 @@ void WinDestroy() } }; +// creates a backbuffer to prevent flickering +void create_backbuffer() +{ + if( WindowDC != NULL ) { + if( ReleaseDC(WindowHandle, WindowDC) == 0 ) { + WindowDC = 0; + } + } + if( backbuffer != NULL ) { + if( ReleaseDC(WindowHandle, backbuffer) == 0 ) { + backbuffer = 0; + } + } + WindowDC = GetDC(WindowHandle); + backbuffer = CreateCompatibleDC(WindowDC); + + BITMAPINFO bmi = BITMAPINFO(); + bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + bmi.bmiHeader.biWidth = WindowWidth; + bmi.bmiHeader.biHeight = -WindowHeight; + bmi.bmiHeader.biPlanes = 1; + bmi.bmiHeader.biBitCount = 8; + bmi.bmiHeader.biCompression = BI_RGB; // Raw RGB + bmi.bmiHeader.biSizeImage = WindowWidth * WindowHeight * 1; + bmi.bmiHeader.biClrUsed = color_loader::COLOR_NAMES_COUNT; // Colors in the palette + bmi.bmiHeader.biClrImportant = color_loader::COLOR_NAMES_COUNT; // Colors in the palette + backbit = CreateDIBSection(0, &bmi, DIB_RGB_COLORS, (void**)&dcbits, NULL, 0); + DeleteObject(SelectObject(backbuffer, backbit));//load the buffer into DC +} + // Copied from sdlcurses.cpp #define ALT_BUFFER_SIZE 8 static char alt_buffer[ALT_BUFFER_SIZE] = {}; @@ -251,6 +284,26 @@ LRESULT CALLBACK ProcessMessages(HWND__ *hWnd,unsigned int Msg, lastchar = code; } return 0; + + case WM_SIZE: + case WM_SIZING: + RECT WndRect; + if( GetClientRect( WindowHandle, &WndRect ) ) { + TERMINAL_WIDTH = WndRect.right / fontwidth; + TERMINAL_HEIGHT = WndRect.bottom / fontheight; + WindowWidth = TERMINAL_WIDTH * fontwidth; + WindowHeight = TERMINAL_HEIGHT * fontheight; + catacurses::resizeterm(); + create_backbuffer(); + SetBkMode(backbuffer, TRANSPARENT);//Transparent font backgrounds + SelectObject(backbuffer, font);//Load our font into the DC + color_loader().load( windowsPalette ); + if( SetDIBColorTable(backbuffer, 0, windowsPalette.size(), windowsPalette.data() ) == 0 ) { + throw std::runtime_error( "SetDIBColorTable failed" ); + } + catacurses::refresh(); + } + return 0; case WM_SYSCHAR: add_alt_code((char)wParam); @@ -458,6 +511,14 @@ int projected_window_height(int) return get_option( "TERMINAL_Y" ) * fontheight; } +int get_terminal_width() { + return TERMINAL_WIDTH; +} + +int get_terminal_height() { + return TERMINAL_HEIGHT; +} + //*********************************** //Pseudo-Curses Functions * //*********************************** @@ -474,28 +535,16 @@ void catacurses::init_interface() ::fontheight = fl.fontheight; halfwidth=fontwidth / 2; halfheight=fontheight / 2; - WindowWidth= get_option( "TERMINAL_X" ) * fontwidth; - WindowHeight = get_option( "TERMINAL_Y" ) * fontheight; + TERMINAL_WIDTH = get_option( "TERMINAL_X" ); + TERMINAL_HEIGHT = get_option( "TERMINAL_Y" ); + WindowWidth = TERMINAL_WIDTH * fontwidth; + WindowHeight = TERMINAL_HEIGHT * fontheight; WinCreate(); //Create the actual window, register it, etc timeBeginPeriod(1); // Set Sleep resolution to 1ms CheckMessages(); //Let the message queue handle setting up the window - WindowDC = GetDC(WindowHandle); - backbuffer = CreateCompatibleDC(WindowDC); - - BITMAPINFO bmi = BITMAPINFO(); - bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); - bmi.bmiHeader.biWidth = WindowWidth; - bmi.bmiHeader.biHeight = -WindowHeight; - bmi.bmiHeader.biPlanes = 1; - bmi.bmiHeader.biBitCount = 8; - bmi.bmiHeader.biCompression = BI_RGB; // Raw RGB - bmi.bmiHeader.biSizeImage = WindowWidth * WindowHeight * 1; - bmi.bmiHeader.biClrUsed = color_loader::COLOR_NAMES_COUNT; // Colors in the palette - bmi.bmiHeader.biClrImportant = color_loader::COLOR_NAMES_COUNT; // Colors in the palette - backbit = CreateDIBSection(0, &bmi, DIB_RGB_COLORS, (void**)&dcbits, NULL, 0); - DeleteObject(SelectObject(backbuffer, backbit));//load the buffer into DC + create_backbuffer(); // Load private fonts if (SetCurrentDirectoryW(L"data\\font")){ From a7d18d4e97c04e73fe40b2be00cef9a8cb64f0b2 Mon Sep 17 00:00:00 2001 From: Graywolfe813 Date: Sat, 10 Mar 2018 15:45:13 -0700 Subject: [PATCH 04/35] Remove tiles redraw to avoid crashing. --- src/sdltiles.cpp | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/src/sdltiles.cpp b/src/sdltiles.cpp index aef0fe8346c3..f612cd90aa6d 100644 --- a/src/sdltiles.cpp +++ b/src/sdltiles.cpp @@ -1246,6 +1246,22 @@ long sdl_keysym_to_curses( SDL_Keysym keysym ) } } +bool handle_resize(int w, int h) +{ + if( ( w != WindowWidth ) || ( h != WindowHeight ) ) { + WindowWidth = w; + WindowHeight = h; + TERMINAL_WIDTH = WindowWidth / fontwidth; + TERMINAL_HEIGHT = WindowHeight / fontheight; + SetupRenderTarget(); + g->init_ui(); + tilecontext->reinit_minimap(); + + return true; + } + return false; +} + //Check for any window messages (keypress, paint, mousemove, etc) void CheckMessages() { @@ -1267,19 +1283,7 @@ void CheckMessages() needupdate = true; break; case SDL_WINDOWEVENT_RESIZED: - WindowWidth = ev.window.data1; - WindowHeight = ev.window.data2; - TERMINAL_WIDTH = WindowWidth / fontwidth; - TERMINAL_HEIGHT = WindowHeight / fontheight; - SetupRenderTarget(); - g->init_ui(); - catacurses::refresh(); - if( g->is_core_data_loaded() ) { - tilecontext->reinit_minimap(); - g->refresh_all(); - } - needupdate = true; - refresh_display(); + needupdate = handle_resize( ev.window.data1, ev.window.data2 ); break; default: break; From f3a8ea44dde685a66a8a18be10a7d9c870df134f Mon Sep 17 00:00:00 2001 From: Graywolfe813 Date: Sun, 11 Mar 2018 04:34:32 -0600 Subject: [PATCH 05/35] Add toggling to/from fullscreen, set min size. --- src/game.cpp | 4 ++++ src/sdltiles.cpp | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/src/game.cpp b/src/game.cpp index 7a438c0211a6..ef0fa36741de 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -205,6 +205,7 @@ void intro(); game *g; #ifdef TILES extern std::unique_ptr tilecontext; +extern void toggle_fullscreen_window(); #endif // TILES uistatedata uistate; @@ -693,6 +694,9 @@ void game::toggle_fullscreen() fullscreen = !fullscreen; init_ui(); refresh_all(); +#else + toggle_fullscreen_window(); + refresh_all(); #endif } diff --git a/src/sdltiles.cpp b/src/sdltiles.cpp index f612cd90aa6d..d7d2f8fc09f6 100644 --- a/src/sdltiles.cpp +++ b/src/sdltiles.cpp @@ -230,6 +230,7 @@ int fontwidth; //the width of the font, background is always this size int fontheight; //the height of the font, background is always this size static int TERMINAL_WIDTH; static int TERMINAL_HEIGHT; +bool fullscreen; static SDL_Joystick *joystick; // Only one joystick for now. @@ -333,6 +334,7 @@ bool WinCreate() window_flags |= SDL_WINDOW_FULLSCREEN; } else if (get_option( "FULLSCREEN" ) == "windowedbl") { window_flags |= SDL_WINDOW_FULLSCREEN_DESKTOP; + fullscreen = true; SDL_SetHint(SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS, "0"); } @@ -360,6 +362,8 @@ bool WinCreate() TERMINAL_HEIGHT = WindowHeight / fontheight; } + SDL_SetWindowMinimumSize( ::window.get(), fontwidth * 80, fontheight * 24 ); + // Initialize framebuffer caches terminal_framebuffer.resize(TERMINAL_HEIGHT); for (int i = 0; i < TERMINAL_HEIGHT; i++) { @@ -659,6 +663,8 @@ static void try_sdl_update() } } + + //for resetting the render target after updating texture caches in cata_tiles.cpp void set_displaybuffer_rendertarget() { @@ -1262,6 +1268,32 @@ bool handle_resize(int w, int h) return false; } +void toggle_fullscreen_window() +{ + static int restore_win_w = get_option( "TERMINAL_X" ) * fontwidth; + static int restore_win_h = get_option( "TERMINAL_Y" ) * fontheight; + + if ( fullscreen ) { + if( SDL_SetWindowFullscreen( ::window.get(), 0 ) != 0 ) { + dbg(D_ERROR) << "SDL_SetWinodwFullscreen failed: " << SDL_GetError(); + return; + } + SDL_RestoreWindow( ::window.get() ); + SDL_SetWindowSize( window.get(), restore_win_w, restore_win_h ); + } else { + restore_win_w = WindowWidth; + restore_win_h = WindowHeight; + if( SDL_SetWindowFullscreen( ::window.get(), SDL_WINDOW_FULLSCREEN_DESKTOP ) != 0 ) { + dbg(D_ERROR) << "SDL_SetWinodwFullscreen failed: " << SDL_GetError(); + return; + } + } + int nw, nh; + SDL_GetWindowSize( ::window.get(), &nw, &nh ); + handle_resize( nw, nh ); + fullscreen = !fullscreen; +} + //Check for any window messages (keypress, paint, mousemove, etc) void CheckMessages() { From 8c415d1dbc01b1a4daae03c37e814ceeb9bb5dfb Mon Sep 17 00:00:00 2001 From: Graywolfe813 Date: Sun, 11 Mar 2018 04:45:12 -0600 Subject: [PATCH 06/35] Insure minimum size gets set. --- src/sdltiles.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sdltiles.cpp b/src/sdltiles.cpp index d7d2f8fc09f6..38627006acaa 100644 --- a/src/sdltiles.cpp +++ b/src/sdltiles.cpp @@ -1280,6 +1280,7 @@ void toggle_fullscreen_window() } SDL_RestoreWindow( ::window.get() ); SDL_SetWindowSize( window.get(), restore_win_w, restore_win_h ); + SDL_SetWindowMinimumSize( ::window.get(), fontwidth * 80, fontheight * 24 ); } else { restore_win_w = WindowWidth; restore_win_h = WindowHeight; From 243659d7051275b8ef2a12e4229b0d64b4221d1b Mon Sep 17 00:00:00 2001 From: Graywolfe813 Date: Mon, 12 Mar 2018 12:21:18 -0600 Subject: [PATCH 07/35] Move wincurse resize outside of message handler, other CR tweaks. --- src/sdltiles.cpp | 10 ++++----- src/wincurse.cpp | 58 ++++++++++++++++++++++++++---------------------- 2 files changed, 37 insertions(+), 31 deletions(-) diff --git a/src/sdltiles.cpp b/src/sdltiles.cpp index 38627006acaa..04922159b8e3 100644 --- a/src/sdltiles.cpp +++ b/src/sdltiles.cpp @@ -1274,23 +1274,23 @@ void toggle_fullscreen_window() static int restore_win_h = get_option( "TERMINAL_Y" ) * fontheight; if ( fullscreen ) { - if( SDL_SetWindowFullscreen( ::window.get(), 0 ) != 0 ) { + if( SDL_SetWindowFullscreen( window.get(), 0 ) != 0 ) { dbg(D_ERROR) << "SDL_SetWinodwFullscreen failed: " << SDL_GetError(); return; } - SDL_RestoreWindow( ::window.get() ); + SDL_RestoreWindow( window.get() ); SDL_SetWindowSize( window.get(), restore_win_w, restore_win_h ); - SDL_SetWindowMinimumSize( ::window.get(), fontwidth * 80, fontheight * 24 ); + SDL_SetWindowMinimumSize( window.get(), fontwidth * 80, fontheight * 24 ); } else { restore_win_w = WindowWidth; restore_win_h = WindowHeight; - if( SDL_SetWindowFullscreen( ::window.get(), SDL_WINDOW_FULLSCREEN_DESKTOP ) != 0 ) { + if( SDL_SetWindowFullscreen( window.get(), SDL_WINDOW_FULLSCREEN_DESKTOP ) != 0 ) { dbg(D_ERROR) << "SDL_SetWinodwFullscreen failed: " << SDL_GetError(); return; } } int nw, nh; - SDL_GetWindowSize( ::window.get(), &nw, &nh ); + SDL_GetWindowSize( window.get(), &nw, &nh ); handle_resize( nw, nh ); fullscreen = !fullscreen; } diff --git a/src/wincurse.cpp b/src/wincurse.cpp index 309e8e950bcd..5b9391dab1ef 100644 --- a/src/wincurse.cpp +++ b/src/wincurse.cpp @@ -48,6 +48,7 @@ HFONT font; //Handle to the font created by CreateFont std::array::COLOR_NAMES_COUNT> windowsPalette; unsigned char *dcbits; //the bits of the screen image, for direct access bool CursorVisible = true; // Showcursor is a somewhat weird function +bool needs_resize = false; // The window needs to be resized static int TERMINAL_WIDTH; static int TERMINAL_HEIGHT; @@ -133,17 +134,13 @@ void WinDestroy() void create_backbuffer() { if( WindowDC != NULL ) { - if( ReleaseDC(WindowHandle, WindowDC) == 0 ) { - WindowDC = 0; - } + ReleaseDC( WindowHandle, WindowDC ); } if( backbuffer != NULL ) { - if( ReleaseDC(WindowHandle, backbuffer) == 0 ) { - backbuffer = 0; - } + ReleaseDC(WindowHandle, backbuffer ); } - WindowDC = GetDC(WindowHandle); - backbuffer = CreateCompatibleDC(WindowDC); + WindowDC = GetDC( WindowHandle ); + backbuffer = CreateCompatibleDC( WindowDC ); BITMAPINFO bmi = BITMAPINFO(); bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); @@ -155,8 +152,29 @@ void create_backbuffer() bmi.bmiHeader.biSizeImage = WindowWidth * WindowHeight * 1; bmi.bmiHeader.biClrUsed = color_loader::COLOR_NAMES_COUNT; // Colors in the palette bmi.bmiHeader.biClrImportant = color_loader::COLOR_NAMES_COUNT; // Colors in the palette - backbit = CreateDIBSection(0, &bmi, DIB_RGB_COLORS, (void**)&dcbits, NULL, 0); - DeleteObject(SelectObject(backbuffer, backbit));//load the buffer into DC + backbit = CreateDIBSection( 0, &bmi, DIB_RGB_COLORS, ( void** ) &dcbits, NULL, 0); + DeleteObject(SelectObject( backbuffer, backbit ) );//load the buffer into DC +} + +void resize_window() +{ + needs_resize = false; + RECT WndRect; + if( GetClientRect( WindowHandle, &WndRect ) ) { + TERMINAL_WIDTH = WndRect.right / fontwidth; + TERMINAL_HEIGHT = WndRect.bottom / fontheight; + WindowWidth = TERMINAL_WIDTH * fontwidth; + WindowHeight = TERMINAL_HEIGHT * fontheight; + catacurses::resizeterm(); + create_backbuffer(); + SetBkMode(backbuffer, TRANSPARENT);//Transparent font backgrounds + SelectObject(backbuffer, font);//Load our font into the DC + color_loader().load( windowsPalette ); + if( SetDIBColorTable(backbuffer, 0, windowsPalette.size(), windowsPalette.data() ) == 0 ) { + throw std::runtime_error( "SetDIBColorTable failed" ); + } + catacurses::refresh(); + } } // Copied from sdlcurses.cpp @@ -287,22 +305,7 @@ LRESULT CALLBACK ProcessMessages(HWND__ *hWnd,unsigned int Msg, case WM_SIZE: case WM_SIZING: - RECT WndRect; - if( GetClientRect( WindowHandle, &WndRect ) ) { - TERMINAL_WIDTH = WndRect.right / fontwidth; - TERMINAL_HEIGHT = WndRect.bottom / fontheight; - WindowWidth = TERMINAL_WIDTH * fontwidth; - WindowHeight = TERMINAL_HEIGHT * fontheight; - catacurses::resizeterm(); - create_backbuffer(); - SetBkMode(backbuffer, TRANSPARENT);//Transparent font backgrounds - SelectObject(backbuffer, font);//Load our font into the DC - color_loader().load( windowsPalette ); - if( SetDIBColorTable(backbuffer, 0, windowsPalette.size(), windowsPalette.data() ) == 0 ) { - throw std::runtime_error( "SetDIBColorTable failed" ); - } - catacurses::refresh(); - } + needs_resize = true; return 0; case WM_SYSCHAR: @@ -497,6 +500,9 @@ void CheckMessages() TranslateMessage(&msg); DispatchMessage(&msg); } + if( needs_resize ) { + resize_window(); + } } // Calculates the new width of the window, given the number of columns. From cb4bf359c9007ad00224bc1f7bc46f719dc08152 Mon Sep 17 00:00:00 2001 From: Graywolfe813 Date: Mon, 12 Mar 2018 17:06:02 -0600 Subject: [PATCH 08/35] Prevent attempting to resize before initialization. --- src/wincurse.cpp | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/src/wincurse.cpp b/src/wincurse.cpp index 5b9391dab1ef..7e41a0187b19 100644 --- a/src/wincurse.cpp +++ b/src/wincurse.cpp @@ -49,6 +49,7 @@ std::array::COLOR_NAMES_COUNT> windowsPalette; unsigned char *dcbits; //the bits of the screen image, for direct access bool CursorVisible = true; // Showcursor is a somewhat weird function bool needs_resize = false; // The window needs to be resized +bool initialized = false; static int TERMINAL_WIDTH; static int TERMINAL_HEIGHT; @@ -156,8 +157,11 @@ void create_backbuffer() DeleteObject(SelectObject( backbuffer, backbit ) );//load the buffer into DC } -void resize_window() +void handle_resize() { + if( !initialized ) { + return; + } needs_resize = false; RECT WndRect; if( GetClientRect( WindowHandle, &WndRect ) ) { @@ -306,6 +310,22 @@ LRESULT CALLBACK ProcessMessages(HWND__ *hWnd,unsigned int Msg, case WM_SIZE: case WM_SIZING: needs_resize = true; + RECT WndRect; + if( GetClientRect( WindowHandle, &WndRect ) ) { + TERMINAL_WIDTH = WndRect.right / fontwidth; + TERMINAL_HEIGHT = WndRect.bottom / fontheight; + WindowWidth = TERMINAL_WIDTH * fontwidth; + WindowHeight = TERMINAL_HEIGHT * fontheight; + catacurses::resizeterm(); + create_backbuffer(); + SetBkMode(backbuffer, TRANSPARENT);//Transparent font backgrounds + SelectObject(backbuffer, font);//Load our font into the DC + color_loader().load( windowsPalette ); + if( SetDIBColorTable(backbuffer, 0, windowsPalette.size(), windowsPalette.data() ) == 0 ) { + throw std::runtime_error( "SetDIBColorTable failed" ); + } + catacurses::refresh(); + } return 0; case WM_SYSCHAR: @@ -378,6 +398,7 @@ inline void FillRectDIB(int x, int y, int width, int height, unsigned char color void cata_cursesport::curses_drawwindow( const catacurses::window &w ) { + WINDOW *const win = w.get(); int i,j,drawx,drawy; wchar_t tmp; @@ -501,7 +522,7 @@ void CheckMessages() DispatchMessage(&msg); } if( needs_resize ) { - resize_window(); + handle_resize(); } } @@ -584,6 +605,8 @@ void catacurses::init_interface() stdscr = newwin( get_option( "TERMINAL_Y" ), get_option( "TERMINAL_X" ),0,0 ); //newwin calls `new WINDOW`, and that will throw, but not return nullptr. + + initialized = true; } // A very accurate and responsive timer (NEVER use GetTickCount) From 45c2890d6836428b54ec6dc973c5f9b9dd94b5f2 Mon Sep 17 00:00:00 2001 From: Graywolfe813 Date: Mon, 12 Mar 2018 21:36:22 -0600 Subject: [PATCH 09/35] Remove term size check in toggle_fullscreen to prevent sidebar lockout. --- src/game.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/game.cpp b/src/game.cpp index ef0fa36741de..5fdb09dbcbb8 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -688,9 +688,6 @@ void game::toggle_sidebar_style() void game::toggle_fullscreen() { #ifndef TILES - if (TERMX > 121 || TERMY > 121) { - return; - } fullscreen = !fullscreen; init_ui(); refresh_all(); From 362e265eeccc1079ea38295482e9ca7b67b3d255 Mon Sep 17 00:00:00 2001 From: Graywolfe813 Date: Wed, 14 Mar 2018 02:15:34 -0600 Subject: [PATCH 10/35] Handle window resizing using a signal and slot approach. --- src/cursesdef.h | 4 ++- src/cursesport.cpp | 7 +++-- src/game.cpp | 6 ++++ src/game.h | 2 +- src/main.cpp | 2 +- src/ncurses_def.cpp | 13 ++++---- src/sdltiles.cpp | 5 ++-- src/signals.h | 72 +++++++++++++++++++++++++++++++++++++++++++++ src/wincurse.cpp | 18 +----------- 9 files changed, 96 insertions(+), 33 deletions(-) create mode 100644 src/signals.h diff --git a/src/cursesdef.h b/src/cursesdef.h index 172da09da764..6f7dfa815f95 100644 --- a/src/cursesdef.h +++ b/src/cursesdef.h @@ -3,6 +3,7 @@ #define CURSESDEF_H #include "string_formatter.h" +#include "signals.h" #include #include @@ -89,6 +90,7 @@ using chtype = int; using attr_t = unsigned short; extern window stdscr; +extern Signal window_resized; window newwin( int nlines, int ncols, int begin_y, int begin_x ); void wborder( const window &win, chtype ls, chtype rs, chtype ts, chtype bs, chtype tl, chtype tr, @@ -113,7 +115,7 @@ inline void wprintw( const window &win, const char *const fmt, Args &&... args ) return wprintw( win, string_format( fmt, std::forward( args )... ) ); } -void resizeterm(); +void resizeterm( int w, int h ); void werase( const window &win ); void init_pair( short pair, base_color f, base_color b ); void wmove( const window &win, int y, int x ); diff --git a/src/cursesport.cpp b/src/cursesport.cpp index 8ee4e13b5638..6bf4bc244db8 100644 --- a/src/cursesport.cpp +++ b/src/cursesport.cpp @@ -34,6 +34,7 @@ catacurses::window catacurses::stdscr; std::array cata_cursesport::colorpairs; //storage for pair'ed colored +Signal catacurses::window_resized; static bool wmove_internal( const catacurses::window &win_, const int y, const int x ) { @@ -408,10 +409,10 @@ void catacurses::mvwprintw(const window &win, int y, int x, const std::string &p return printstring(win.get(), printbuf); } -//Resizes the underlying terminal after a Window's console resize(maybe?) Not used in TILES -void catacurses::resizeterm() +//Resizes the underlying terminal after a Window's console resize. Not used in TILES +void catacurses::resizeterm( int w, int h ) { - g->init_ui(); + catacurses::window_resized.emit( w, h ); } //erases a window of all text and attributes diff --git a/src/game.cpp b/src/game.cpp index 5fdb09dbcbb8..80d010cb2f74 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -730,6 +730,12 @@ void game::reenter_fullscreen() } } +// Callback for main window resize signal +void game::on_window_resized( int, int ) +{ + init_ui(); +} + /* * Initialize more stuff after mapbuffer is loaded. */ diff --git a/src/game.h b/src/game.h index f54cc64d84c2..abb0b3e9ec2a 100644 --- a/src/game.h +++ b/src/game.h @@ -550,6 +550,7 @@ class game void toggle_pixel_minimap(void); void temp_exit_fullscreen(void); void reenter_fullscreen(void); + void on_window_resized( int w = 0, int h = 0 ); // handles main window resize void zoom_in(); void zoom_out(); void reset_zoom(); @@ -981,7 +982,6 @@ class game void draw_sidebar(); void draw_sidebar_messages(); void draw_pixel_minimap(); // Draws the pixel minimap based on the player's current location - // int autosave_timeout(); // If autosave enabled, how long we should wait for user inaction before saving. void autosave(); // automatic quicksaves - Performs some checks before calling quicksave() void quicksave(); // Saves the game without quitting diff --git a/src/main.cpp b/src/main.cpp index d142e9cf0ccd..3ea9626d496d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -472,8 +472,8 @@ int main(int argc, char *argv[]) } // Now we do the actual game. - g->init_ui(); + catacurses::window_resized.connect_member(g, &game::on_window_resized); catacurses::curs_set( 0 ); // Invisible cursor here, because MAPBUFFER.load() is crash-prone diff --git a/src/ncurses_def.cpp b/src/ncurses_def.cpp index a29b7f85c213..658dc5716483 100644 --- a/src/ncurses_def.cpp +++ b/src/ncurses_def.cpp @@ -18,7 +18,6 @@ #include "catacharset.h" #include "color.h" -#include "game.h" #include extern int VIEW_OFFSET_X; // X position of terrain window @@ -197,13 +196,12 @@ void catacurses::init_pair( const short pair, const base_color f, const base_col } catacurses::window catacurses::stdscr; +Signal catacurses::window_resized; -void catacurses::resizeterm() +void catacurses::resizeterm( int w, int h ) { - const int new_x = ::getmaxx( stdscr.get<::WINDOW>() ); - const int new_y = ::getmaxy( stdscr.get<::WINDOW>() ); - if( ::is_term_resized( new_x, new_y ) ) { - g->init_ui(); + if( ::is_term_resized( w, h ) ) { + window_resized.emit( w, h ); } } @@ -256,7 +254,8 @@ input_event input_manager::get_input_event() } // ncurses mouse handling } else if( key == KEY_RESIZE ) { - catacurses::resizeterm(); + catacurses::resizeterm( ::getmaxx( catacurses::stdscr.get<::WINDOW>() ), + ::getmaxy( catacurses::stdscr.get<::WINDOW>() ) ); } else if( key == KEY_MOUSE ) { MEVENT event; if( getmouse( &event ) == OK ) { diff --git a/src/sdltiles.cpp b/src/sdltiles.cpp index 04922159b8e3..9828ff9b733f 100644 --- a/src/sdltiles.cpp +++ b/src/sdltiles.cpp @@ -1252,7 +1252,7 @@ long sdl_keysym_to_curses( SDL_Keysym keysym ) } } -bool handle_resize(int w, int h) +bool handle_resize( int w, int h ) { if( ( w != WindowWidth ) || ( h != WindowHeight ) ) { WindowWidth = w; @@ -1260,9 +1260,8 @@ bool handle_resize(int w, int h) TERMINAL_WIDTH = WindowWidth / fontwidth; TERMINAL_HEIGHT = WindowHeight / fontheight; SetupRenderTarget(); - g->init_ui(); tilecontext->reinit_minimap(); - + catacurses::window_resized.emit( TERMINAL_WIDTH, TERMINAL_HEIGHT ); return true; } return false; diff --git a/src/signals.h b/src/signals.h new file mode 100644 index 000000000000..07614746de85 --- /dev/null +++ b/src/signals.h @@ -0,0 +1,72 @@ + +// Template courtesy of Simon Schneegans, see: http://simmesimme.github.io/tutorials/2015/09/20/signal-slot + +#ifndef SIGNALS_HPP +#define SIGNALS_HPP + +#include +#include + + +template +class Signal +{ + + public: + + Signal() : current_id_( 0 ) {} + + // copy creates new Signal + Signal( Signal const & ) : current_id_( 0 ) {} + + // connects a member function to this Signal + template + int connect_member( T *inst, void ( T::*func )( Args... ) ) { + return connect( [ = ]( Args... args ) { + ( inst->*func )( args... ); + } ); + } + + // connects a const member function to this Signal + template + int connect_member( T *inst, void ( T::*func )( Args... ) const ) { + return connect( [ = ]( Args... args ) { + ( inst->*func )( args... ); + } ); + } + + // connects a std::function to the Signal. The returned + // value can be used to disconnect the function again + int connect( std::function const &slot ) const { + slots_.insert( std::make_pair( ++current_id_, slot ) ); + return current_id_; + } + + // disconnects a previously connected function + void disconnect( int id ) const { + slots_.erase( id ); + } + + // disconnects all previously connected functions + void disconnect_all() const { + slots_.clear(); + } + + // calls all connected functions + void emit( Args... p ) { + for( auto it : slots_ ) { + it.second( p... ); + } + } + + // assignment creates new Signal + Signal &operator=( Signal const & ) { + disconnect_all(); + } + + private: + mutable std::map> slots_; + mutable int current_id_; +}; + +#endif /* SIGNALS_H */ diff --git a/src/wincurse.cpp b/src/wincurse.cpp index 7e41a0187b19..0a7d3fd9be72 100644 --- a/src/wincurse.cpp +++ b/src/wincurse.cpp @@ -169,7 +169,7 @@ void handle_resize() TERMINAL_HEIGHT = WndRect.bottom / fontheight; WindowWidth = TERMINAL_WIDTH * fontwidth; WindowHeight = TERMINAL_HEIGHT * fontheight; - catacurses::resizeterm(); + catacurses::resizeterm( TERMINAL_WIDTH, TERMINAL_HEIGHT ); create_backbuffer(); SetBkMode(backbuffer, TRANSPARENT);//Transparent font backgrounds SelectObject(backbuffer, font);//Load our font into the DC @@ -310,22 +310,6 @@ LRESULT CALLBACK ProcessMessages(HWND__ *hWnd,unsigned int Msg, case WM_SIZE: case WM_SIZING: needs_resize = true; - RECT WndRect; - if( GetClientRect( WindowHandle, &WndRect ) ) { - TERMINAL_WIDTH = WndRect.right / fontwidth; - TERMINAL_HEIGHT = WndRect.bottom / fontheight; - WindowWidth = TERMINAL_WIDTH * fontwidth; - WindowHeight = TERMINAL_HEIGHT * fontheight; - catacurses::resizeterm(); - create_backbuffer(); - SetBkMode(backbuffer, TRANSPARENT);//Transparent font backgrounds - SelectObject(backbuffer, font);//Load our font into the DC - color_loader().load( windowsPalette ); - if( SetDIBColorTable(backbuffer, 0, windowsPalette.size(), windowsPalette.data() ) == 0 ) { - throw std::runtime_error( "SetDIBColorTable failed" ); - } - catacurses::refresh(); - } return 0; case WM_SYSCHAR: From 8e04b4d9c67ded09ec1aa34147c8c23af9b6eac3 Mon Sep 17 00:00:00 2001 From: Graywolfe813 Date: Wed, 14 Mar 2018 10:28:27 -0600 Subject: [PATCH 11/35] Revert "Handle window resizing using a signal and slot approach." This reverts commit 362e265eeccc1079ea38295482e9ca7b67b3d255. --- src/cursesdef.h | 4 +-- src/cursesport.cpp | 7 ++--- src/game.cpp | 6 ---- src/game.h | 2 +- src/main.cpp | 2 +- src/ncurses_def.cpp | 13 ++++---- src/sdltiles.cpp | 5 ++-- src/signals.h | 72 --------------------------------------------- src/wincurse.cpp | 18 +++++++++++- 9 files changed, 33 insertions(+), 96 deletions(-) delete mode 100644 src/signals.h diff --git a/src/cursesdef.h b/src/cursesdef.h index 6f7dfa815f95..172da09da764 100644 --- a/src/cursesdef.h +++ b/src/cursesdef.h @@ -3,7 +3,6 @@ #define CURSESDEF_H #include "string_formatter.h" -#include "signals.h" #include #include @@ -90,7 +89,6 @@ using chtype = int; using attr_t = unsigned short; extern window stdscr; -extern Signal window_resized; window newwin( int nlines, int ncols, int begin_y, int begin_x ); void wborder( const window &win, chtype ls, chtype rs, chtype ts, chtype bs, chtype tl, chtype tr, @@ -115,7 +113,7 @@ inline void wprintw( const window &win, const char *const fmt, Args &&... args ) return wprintw( win, string_format( fmt, std::forward( args )... ) ); } -void resizeterm( int w, int h ); +void resizeterm(); void werase( const window &win ); void init_pair( short pair, base_color f, base_color b ); void wmove( const window &win, int y, int x ); diff --git a/src/cursesport.cpp b/src/cursesport.cpp index 6bf4bc244db8..8ee4e13b5638 100644 --- a/src/cursesport.cpp +++ b/src/cursesport.cpp @@ -34,7 +34,6 @@ catacurses::window catacurses::stdscr; std::array cata_cursesport::colorpairs; //storage for pair'ed colored -Signal catacurses::window_resized; static bool wmove_internal( const catacurses::window &win_, const int y, const int x ) { @@ -409,10 +408,10 @@ void catacurses::mvwprintw(const window &win, int y, int x, const std::string &p return printstring(win.get(), printbuf); } -//Resizes the underlying terminal after a Window's console resize. Not used in TILES -void catacurses::resizeterm( int w, int h ) +//Resizes the underlying terminal after a Window's console resize(maybe?) Not used in TILES +void catacurses::resizeterm() { - catacurses::window_resized.emit( w, h ); + g->init_ui(); } //erases a window of all text and attributes diff --git a/src/game.cpp b/src/game.cpp index 80d010cb2f74..5fdb09dbcbb8 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -730,12 +730,6 @@ void game::reenter_fullscreen() } } -// Callback for main window resize signal -void game::on_window_resized( int, int ) -{ - init_ui(); -} - /* * Initialize more stuff after mapbuffer is loaded. */ diff --git a/src/game.h b/src/game.h index abb0b3e9ec2a..f54cc64d84c2 100644 --- a/src/game.h +++ b/src/game.h @@ -550,7 +550,6 @@ class game void toggle_pixel_minimap(void); void temp_exit_fullscreen(void); void reenter_fullscreen(void); - void on_window_resized( int w = 0, int h = 0 ); // handles main window resize void zoom_in(); void zoom_out(); void reset_zoom(); @@ -982,6 +981,7 @@ class game void draw_sidebar(); void draw_sidebar_messages(); void draw_pixel_minimap(); // Draws the pixel minimap based on the player's current location + // int autosave_timeout(); // If autosave enabled, how long we should wait for user inaction before saving. void autosave(); // automatic quicksaves - Performs some checks before calling quicksave() void quicksave(); // Saves the game without quitting diff --git a/src/main.cpp b/src/main.cpp index 3ea9626d496d..d142e9cf0ccd 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -472,8 +472,8 @@ int main(int argc, char *argv[]) } // Now we do the actual game. + g->init_ui(); - catacurses::window_resized.connect_member(g, &game::on_window_resized); catacurses::curs_set( 0 ); // Invisible cursor here, because MAPBUFFER.load() is crash-prone diff --git a/src/ncurses_def.cpp b/src/ncurses_def.cpp index 658dc5716483..a29b7f85c213 100644 --- a/src/ncurses_def.cpp +++ b/src/ncurses_def.cpp @@ -18,6 +18,7 @@ #include "catacharset.h" #include "color.h" +#include "game.h" #include extern int VIEW_OFFSET_X; // X position of terrain window @@ -196,12 +197,13 @@ void catacurses::init_pair( const short pair, const base_color f, const base_col } catacurses::window catacurses::stdscr; -Signal catacurses::window_resized; -void catacurses::resizeterm( int w, int h ) +void catacurses::resizeterm() { - if( ::is_term_resized( w, h ) ) { - window_resized.emit( w, h ); + const int new_x = ::getmaxx( stdscr.get<::WINDOW>() ); + const int new_y = ::getmaxy( stdscr.get<::WINDOW>() ); + if( ::is_term_resized( new_x, new_y ) ) { + g->init_ui(); } } @@ -254,8 +256,7 @@ input_event input_manager::get_input_event() } // ncurses mouse handling } else if( key == KEY_RESIZE ) { - catacurses::resizeterm( ::getmaxx( catacurses::stdscr.get<::WINDOW>() ), - ::getmaxy( catacurses::stdscr.get<::WINDOW>() ) ); + catacurses::resizeterm(); } else if( key == KEY_MOUSE ) { MEVENT event; if( getmouse( &event ) == OK ) { diff --git a/src/sdltiles.cpp b/src/sdltiles.cpp index 9828ff9b733f..04922159b8e3 100644 --- a/src/sdltiles.cpp +++ b/src/sdltiles.cpp @@ -1252,7 +1252,7 @@ long sdl_keysym_to_curses( SDL_Keysym keysym ) } } -bool handle_resize( int w, int h ) +bool handle_resize(int w, int h) { if( ( w != WindowWidth ) || ( h != WindowHeight ) ) { WindowWidth = w; @@ -1260,8 +1260,9 @@ bool handle_resize( int w, int h ) TERMINAL_WIDTH = WindowWidth / fontwidth; TERMINAL_HEIGHT = WindowHeight / fontheight; SetupRenderTarget(); + g->init_ui(); tilecontext->reinit_minimap(); - catacurses::window_resized.emit( TERMINAL_WIDTH, TERMINAL_HEIGHT ); + return true; } return false; diff --git a/src/signals.h b/src/signals.h deleted file mode 100644 index 07614746de85..000000000000 --- a/src/signals.h +++ /dev/null @@ -1,72 +0,0 @@ - -// Template courtesy of Simon Schneegans, see: http://simmesimme.github.io/tutorials/2015/09/20/signal-slot - -#ifndef SIGNALS_HPP -#define SIGNALS_HPP - -#include -#include - - -template -class Signal -{ - - public: - - Signal() : current_id_( 0 ) {} - - // copy creates new Signal - Signal( Signal const & ) : current_id_( 0 ) {} - - // connects a member function to this Signal - template - int connect_member( T *inst, void ( T::*func )( Args... ) ) { - return connect( [ = ]( Args... args ) { - ( inst->*func )( args... ); - } ); - } - - // connects a const member function to this Signal - template - int connect_member( T *inst, void ( T::*func )( Args... ) const ) { - return connect( [ = ]( Args... args ) { - ( inst->*func )( args... ); - } ); - } - - // connects a std::function to the Signal. The returned - // value can be used to disconnect the function again - int connect( std::function const &slot ) const { - slots_.insert( std::make_pair( ++current_id_, slot ) ); - return current_id_; - } - - // disconnects a previously connected function - void disconnect( int id ) const { - slots_.erase( id ); - } - - // disconnects all previously connected functions - void disconnect_all() const { - slots_.clear(); - } - - // calls all connected functions - void emit( Args... p ) { - for( auto it : slots_ ) { - it.second( p... ); - } - } - - // assignment creates new Signal - Signal &operator=( Signal const & ) { - disconnect_all(); - } - - private: - mutable std::map> slots_; - mutable int current_id_; -}; - -#endif /* SIGNALS_H */ diff --git a/src/wincurse.cpp b/src/wincurse.cpp index 0a7d3fd9be72..7e41a0187b19 100644 --- a/src/wincurse.cpp +++ b/src/wincurse.cpp @@ -169,7 +169,7 @@ void handle_resize() TERMINAL_HEIGHT = WndRect.bottom / fontheight; WindowWidth = TERMINAL_WIDTH * fontwidth; WindowHeight = TERMINAL_HEIGHT * fontheight; - catacurses::resizeterm( TERMINAL_WIDTH, TERMINAL_HEIGHT ); + catacurses::resizeterm(); create_backbuffer(); SetBkMode(backbuffer, TRANSPARENT);//Transparent font backgrounds SelectObject(backbuffer, font);//Load our font into the DC @@ -310,6 +310,22 @@ LRESULT CALLBACK ProcessMessages(HWND__ *hWnd,unsigned int Msg, case WM_SIZE: case WM_SIZING: needs_resize = true; + RECT WndRect; + if( GetClientRect( WindowHandle, &WndRect ) ) { + TERMINAL_WIDTH = WndRect.right / fontwidth; + TERMINAL_HEIGHT = WndRect.bottom / fontheight; + WindowWidth = TERMINAL_WIDTH * fontwidth; + WindowHeight = TERMINAL_HEIGHT * fontheight; + catacurses::resizeterm(); + create_backbuffer(); + SetBkMode(backbuffer, TRANSPARENT);//Transparent font backgrounds + SelectObject(backbuffer, font);//Load our font into the DC + color_loader().load( windowsPalette ); + if( SetDIBColorTable(backbuffer, 0, windowsPalette.size(), windowsPalette.data() ) == 0 ) { + throw std::runtime_error( "SetDIBColorTable failed" ); + } + catacurses::refresh(); + } return 0; case WM_SYSCHAR: From 70cb26990f24403c3d32f7617e63ce0f60d739d3 Mon Sep 17 00:00:00 2001 From: Graywolfe813 Date: Wed, 14 Mar 2018 10:31:17 -0600 Subject: [PATCH 12/35] Remove duplicate resizing code. --- src/wincurse.cpp | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/src/wincurse.cpp b/src/wincurse.cpp index 7e41a0187b19..30b74bce93aa 100644 --- a/src/wincurse.cpp +++ b/src/wincurse.cpp @@ -310,22 +310,6 @@ LRESULT CALLBACK ProcessMessages(HWND__ *hWnd,unsigned int Msg, case WM_SIZE: case WM_SIZING: needs_resize = true; - RECT WndRect; - if( GetClientRect( WindowHandle, &WndRect ) ) { - TERMINAL_WIDTH = WndRect.right / fontwidth; - TERMINAL_HEIGHT = WndRect.bottom / fontheight; - WindowWidth = TERMINAL_WIDTH * fontwidth; - WindowHeight = TERMINAL_HEIGHT * fontheight; - catacurses::resizeterm(); - create_backbuffer(); - SetBkMode(backbuffer, TRANSPARENT);//Transparent font backgrounds - SelectObject(backbuffer, font);//Load our font into the DC - color_loader().load( windowsPalette ); - if( SetDIBColorTable(backbuffer, 0, windowsPalette.size(), windowsPalette.data() ) == 0 ) { - throw std::runtime_error( "SetDIBColorTable failed" ); - } - catacurses::refresh(); - } return 0; case WM_SYSCHAR: From 968ea2495af890e7d4120b6d893f67dc87a1629f Mon Sep 17 00:00:00 2001 From: BorkBorkGoesTheCode Date: Wed, 16 May 2018 20:38:02 -0500 Subject: [PATCH 13/35] Reduced Necropolis zombie spawns Cut zombie density by half in most cases --- data/json/mapgen/necropolis/necropolisB1.json | 122 +++---- data/json/mapgen/necropolis/necropolisB2.json | 308 +++++++++--------- data/json/mapgen/necropolis/necropolisB3.json | 292 ++++++++--------- 3 files changed, 361 insertions(+), 361 deletions(-) diff --git a/data/json/mapgen/necropolis/necropolisB1.json b/data/json/mapgen/necropolis/necropolisB1.json index 94af551f4c68..26e63e4a5541 100644 --- a/data/json/mapgen/necropolis/necropolisB1.json +++ b/data/json/mapgen/necropolis/necropolisB1.json @@ -613,7 +613,7 @@ "{": "f_rubble" }, "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_SEWERS", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.1 } + { "monster": "GROUP_NECROPOLIS_SEWERS", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.05 } ] } }, @@ -729,7 +729,7 @@ "{": "f_rubble" }, "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_SEWERS", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.1 } + { "monster": "GROUP_NECROPOLIS_SEWERS", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.05 } ] } }, @@ -845,7 +845,7 @@ "{": "f_rubble" }, "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_SEWERS", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.1 } + { "monster": "GROUP_NECROPOLIS_SEWERS", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.05 } ] } }, @@ -965,7 +965,7 @@ { "item": "sewage_plant", "x": [ 12, 13 ], "y": [ 13, 13 ], "chance": 60 } ], "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_SEWERS", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.1 } + { "monster": "GROUP_NECROPOLIS_SEWERS", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.05 } ] } }, @@ -1307,7 +1307,7 @@ "{": "f_rubble" }, "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_SEWERS", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.1 } + { "monster": "GROUP_NECROPOLIS_SEWERS", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.05 } ] } }, @@ -1423,7 +1423,7 @@ "{": "f_rubble" }, "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_SEWERS", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.1 } + { "monster": "GROUP_NECROPOLIS_SEWERS", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.05 } ] } }, @@ -1539,7 +1539,7 @@ "{": "f_rubble" }, "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_SEWERS", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.1 } + { "monster": "GROUP_NECROPOLIS_SEWERS", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.05 } ] } }, @@ -1655,7 +1655,7 @@ "{": "f_rubble" }, "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_SEWERS", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.1 } + { "monster": "GROUP_NECROPOLIS_SEWERS", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.05 } ] } }, @@ -1771,7 +1771,7 @@ "{": "f_rubble" }, "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_SEWERS", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.1 } + { "monster": "GROUP_NECROPOLIS_SEWERS", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.05 } ] } }, @@ -1887,7 +1887,7 @@ "{": "f_rubble" }, "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_SEWERS", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.1 } + { "monster": "GROUP_NECROPOLIS_SEWERS", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.05 } ] } }, @@ -2006,7 +2006,7 @@ { "item": "cop_evidence", "x": [ 2, 3 ], "y": [ 16, 16 ], "chance": 60 } ], "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_SEWERS", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.1 } + { "monster": "GROUP_NECROPOLIS_SEWERS", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.05 } ] } }, @@ -2239,7 +2239,7 @@ { "item": "vault_survival", "x": [ 22, 23 ], "y": [ 20, 20 ], "chance": 85 } ], "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_SEWERS", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.1 } + { "monster": "GROUP_NECROPOLIS_SEWERS", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.05 } ] } }, @@ -2360,7 +2360,7 @@ { "group": "survivor_weapons", "x": [ 5, 6 ], "y": [ 22, 23 ], "chance": 50, "ammo": 100, "magazine": 100 } ], "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_SEWERS", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.1 } + { "monster": "GROUP_NECROPOLIS_SEWERS", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.05 } ], "place_vehicles": [ { "vehicle": "humvee", "x": 18, "y": 22, "chance": 100, "rotation": 270 } @@ -2479,7 +2479,7 @@ "{": "f_rubble" }, "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_SEWERS", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.1 } + { "monster": "GROUP_NECROPOLIS_SEWERS", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.05 } ] } }, @@ -2595,7 +2595,7 @@ "{": "f_rubble" }, "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_SEWERS", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.1 } + { "monster": "GROUP_NECROPOLIS_SEWERS", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.05 } ] } }, @@ -2711,7 +2711,7 @@ "{": "f_rubble" }, "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_SEWERS", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.1 } + { "monster": "GROUP_NECROPOLIS_SEWERS", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.05 } ] } }, @@ -2827,7 +2827,7 @@ "{": "f_rubble" }, "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_SEWERS", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.1 } + { "monster": "GROUP_NECROPOLIS_SEWERS", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.05 } ] } }, @@ -2943,7 +2943,7 @@ "{": "f_rubble" }, "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_SEWERS", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.1 } + { "monster": "GROUP_NECROPOLIS_SEWERS", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.05 } ] } }, @@ -3059,7 +3059,7 @@ "{": "f_rubble" }, "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_SEWERS", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.1 } + { "monster": "GROUP_NECROPOLIS_SEWERS", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.05 } ] } }, @@ -3294,7 +3294,7 @@ { "item": "cannedfood", "x": [ 15, 16 ], "y": [ 21, 22 ], "chance": 90 } ], "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_SEWERS", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.1 } + { "monster": "GROUP_NECROPOLIS_SEWERS", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.05 } ] } }, @@ -3414,7 +3414,7 @@ { "item": "cannedfood", "x": [ 7, 8 ], "y": [ 15, 16 ], "chance": 90 } ], "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.2 } + { "monster": "GROUP_NECROPOLIS_VAULT", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.1 } ] } }, @@ -3534,7 +3534,7 @@ { "item": "consumer_electronics", "x": [ 3, 3 ], "y": [ 4, 5 ], "chance": 70 } ], "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_SEWERS", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.1 } + { "monster": "GROUP_NECROPOLIS_SEWERS", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.05 } ] } }, @@ -3650,7 +3650,7 @@ "{": "f_rubble" }, "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_SEWERS", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.1 } + { "monster": "GROUP_NECROPOLIS_SEWERS", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.05 } ] } }, @@ -3766,7 +3766,7 @@ "{": "f_rubble" }, "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_SEWERS", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.1 } + { "monster": "GROUP_NECROPOLIS_SEWERS", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.05 } ] } }, @@ -3885,7 +3885,7 @@ { "item": "homebooks", "x": [ 18, 19 ], "y": [ 15, 15 ], "chance": 90 } ], "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_SEWERS", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.1 } + { "monster": "GROUP_NECROPOLIS_SEWERS", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.05 } ] } }, @@ -4007,7 +4007,7 @@ { "item": "pawn", "x": [ 23, 23 ], "y": [ 4, 8 ], "chance": 70 } ], "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_SEWERS", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.1 } + { "monster": "GROUP_NECROPOLIS_SEWERS", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.05 } ] } }, @@ -4123,7 +4123,7 @@ "{": "f_rubble" }, "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_SEWERS", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.1 } + { "monster": "GROUP_NECROPOLIS_SEWERS", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.05 } ] } }, @@ -4356,7 +4356,7 @@ { "item": "cannedfood", "x": [ 7, 8 ], "y": [ 18, 19 ], "chance": 90 } ], "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.1 } + { "monster": "GROUP_NECROPOLIS_VAULT", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.05 } ], "place_vehicles": [ { "vehicle": "humvee", "x": 13, "y": 16, "chance": 100, "rotation": 270 } @@ -4475,7 +4475,7 @@ "{": "f_rubble" }, "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_SEWERS", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.1 } + { "monster": "GROUP_NECROPOLIS_SEWERS", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.05 } ] } }, @@ -4591,7 +4591,7 @@ "{": "f_rubble" }, "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_SEWERS", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.1 } + { "monster": "GROUP_NECROPOLIS_SEWERS", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.05 } ] } }, @@ -4707,7 +4707,7 @@ "{": "f_rubble" }, "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_SEWERS", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.1 } + { "monster": "GROUP_NECROPOLIS_SEWERS", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.05 } ] } }, @@ -4823,7 +4823,7 @@ "{": "f_rubble" }, "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_SEWERS", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.1 } + { "monster": "GROUP_NECROPOLIS_SEWERS", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.05 } ] } }, @@ -4939,7 +4939,7 @@ "{": "f_rubble" }, "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_SEWERS", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.1 } + { "monster": "GROUP_NECROPOLIS_SEWERS", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.05 } ] } }, @@ -5055,7 +5055,7 @@ "{": "f_rubble" }, "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_SEWERS", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.1 } + { "monster": "GROUP_NECROPOLIS_SEWERS", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.05 } ] } }, @@ -5171,7 +5171,7 @@ "{": "f_rubble" }, "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_SEWERS", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.1 } + { "monster": "GROUP_NECROPOLIS_SEWERS", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.05 } ] } }, @@ -5400,7 +5400,7 @@ "{": "f_rubble" }, "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_SEWERS", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.1 } + { "monster": "GROUP_NECROPOLIS_SEWERS", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.05 } ] } }, @@ -5516,7 +5516,7 @@ "{": "f_rubble" }, "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_SEWERS", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.1 } + { "monster": "GROUP_NECROPOLIS_SEWERS", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.05 } ] } }, @@ -5632,7 +5632,7 @@ "{": "f_rubble" }, "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_SEWERS", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.1 } + { "monster": "GROUP_NECROPOLIS_SEWERS", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.05 } ] } }, @@ -5748,7 +5748,7 @@ "{": "f_rubble" }, "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_SWIMMING", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.1 } + { "monster": "GROUP_NECROPOLIS_SWIMMING", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.05 } ] } }, @@ -5864,7 +5864,7 @@ "{": "f_rubble" }, "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_SWIMMING", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.15 } + { "monster": "GROUP_NECROPOLIS_SWIMMING", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.075 } ] } }, @@ -5980,7 +5980,7 @@ "{": "f_rubble" }, "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_SWIMMING", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.15 } + { "monster": "GROUP_NECROPOLIS_SWIMMING", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.075 } ] } }, @@ -6096,7 +6096,7 @@ "{": "f_rubble" }, "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_SEWERS", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.1 } + { "monster": "GROUP_NECROPOLIS_SEWERS", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.05 } ] } }, @@ -6212,7 +6212,7 @@ "{": "f_rubble" }, "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_SEWERS", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.1 } + { "monster": "GROUP_NECROPOLIS_SEWERS", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.05 } ] } }, @@ -6331,7 +6331,7 @@ { "item": "alcohol", "x": [ 2, 2 ], "y": [ 14, 14 ], "chance": 90 } ], "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_SEWERS", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.1 } + { "monster": "GROUP_NECROPOLIS_SEWERS", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.05 } ] } }, @@ -6447,7 +6447,7 @@ "{": "f_rubble" }, "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_SEWERS", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.1 } + { "monster": "GROUP_NECROPOLIS_SEWERS", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.05 } ] } }, @@ -6567,7 +6567,7 @@ { "item": "sewage_plant", "x": [ 14, 16 ], "y": [ 12, 12 ], "chance": 70 } ], "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_SEWERS", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.1 } + { "monster": "GROUP_NECROPOLIS_SEWERS", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.05 } ] } }, @@ -6683,7 +6683,7 @@ "{": "f_rubble" }, "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_SEWERS", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.1 } + { "monster": "GROUP_NECROPOLIS_SEWERS", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.05 } ] } }, @@ -6799,7 +6799,7 @@ "{": "f_rubble" }, "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_SEWERS", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.1 } + { "monster": "GROUP_NECROPOLIS_SEWERS", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.05 } ] } }, @@ -6915,7 +6915,7 @@ "{": "f_rubble" }, "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_SEWERS", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.1 } + { "monster": "GROUP_NECROPOLIS_SEWERS", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.05 } ] } }, @@ -7031,7 +7031,7 @@ "{": "f_rubble" }, "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_SWIMMING", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.1 } + { "monster": "GROUP_NECROPOLIS_SWIMMING", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.05 } ] } }, @@ -7147,7 +7147,7 @@ "{": "f_rubble" }, "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_SWIMMING", "x": [ 13, 18 ], "y": [ 2, 10 ], "density": 0.5 } + { "monster": "GROUP_NECROPOLIS_SWIMMING", "x": [ 13, 18 ], "y": [ 2, 10 ], "density": 0.25 } ] } }, @@ -7263,7 +7263,7 @@ "{": "f_rubble" }, "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_SEWERS", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.1 } + { "monster": "GROUP_NECROPOLIS_SEWERS", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.05 } ] } }, @@ -7605,7 +7605,7 @@ "{": "f_rubble" }, "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_SEWERS", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.1 } + { "monster": "GROUP_NECROPOLIS_SEWERS", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.05 } ] } }, @@ -7721,7 +7721,7 @@ "{": "f_rubble" }, "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_SEWERS", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.1 } + { "monster": "GROUP_NECROPOLIS_SEWERS", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.05 } ] } }, @@ -7837,7 +7837,7 @@ "{": "f_rubble" }, "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_SEWERS", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.1 } + { "monster": "GROUP_NECROPOLIS_SEWERS", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.05 } ] } }, @@ -7953,7 +7953,7 @@ "{": "f_rubble" }, "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_SEWERS", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.1 } + { "monster": "GROUP_NECROPOLIS_SEWERS", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.05 } ] } }, @@ -8069,7 +8069,7 @@ "{": "f_rubble" }, "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_SEWERS", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.1 } + { "monster": "GROUP_NECROPOLIS_SEWERS", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.05 } ] } }, @@ -8185,7 +8185,7 @@ "{": "f_rubble" }, "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_SEWERS", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.1 } + { "monster": "GROUP_NECROPOLIS_SEWERS", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.05 } ] } }, @@ -8301,7 +8301,7 @@ "{": "f_rubble" }, "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_SEWERS", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.1 } + { "monster": "GROUP_NECROPOLIS_SEWERS", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.05 } ] } }, @@ -8982,7 +8982,7 @@ "{": "f_rubble" }, "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_SEWERS", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.1 } + { "monster": "GROUP_NECROPOLIS_SEWERS", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.05 } ] } }, @@ -9098,7 +9098,7 @@ "{": "f_rubble" }, "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_SWIMMING", "x": [ 18, 23 ], "y": [ 0, 5 ], "density": 0.3 } + { "monster": "GROUP_NECROPOLIS_SWIMMING", "x": [ 18, 23 ], "y": [ 0, 5 ], "density": 0.15 } ] } }, diff --git a/data/json/mapgen/necropolis/necropolisB2.json b/data/json/mapgen/necropolis/necropolisB2.json index e0a60797a66d..96d33b510903 100644 --- a/data/json/mapgen/necropolis/necropolisB2.json +++ b/data/json/mapgen/necropolis/necropolisB2.json @@ -187,8 +187,8 @@ { "group": "army_personal_locker", "x": [ 13, 13 ], "y": [ 18, 18 ], "chance": 70 } ], "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.2 }, - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.7 } + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.1 }, + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.35 } ] } }, @@ -360,8 +360,8 @@ { "group": "army_personal_locker", "x": [ 3, 3 ], "y": [ 18, 18 ], "chance": 70 } ], "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.2 }, - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.7 } + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.1 }, + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.35 } ] } }, @@ -532,8 +532,8 @@ { "group": "army_bed", "x": [ 17, 18 ], "y": [ 23, 23 ], "chance": 70 } ], "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.2 }, - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.7 } + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.1 }, + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.35 } ] } }, @@ -702,8 +702,8 @@ { "group": "army_personal_locker", "x": [ 3, 3 ], "y": [ 1, 1 ], "chance": 70 } ], "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.2 }, - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.7 } + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.1 }, + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.35 } ] } }, @@ -862,8 +862,8 @@ { "group": "office", "x": [ 1, 3 ], "y": [ 23, 23 ], "chance": 60 } ], "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.2 }, - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.4 } + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.1 }, + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.2 } ] } }, @@ -1021,8 +1021,8 @@ { "group": "office", "x": [ 18, 18 ], "y": [ 19, 20 ], "chance": 80 } ], "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.2 }, - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.4 } + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.1 }, + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.2 } ] } }, @@ -1189,8 +1189,8 @@ { "group": "chem_lab", "x": [ 7, 13 ], "y": [ 23, 23 ], "chance": 80 } ], "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.2 }, - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.4 } + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.1 }, + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.2 } ] } }, @@ -1346,8 +1346,8 @@ { "group": "hospital_bed", "x": [ 12, 12 ], "y": [ 18, 18 ], "chance": 70 } ], "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.2 }, - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.4 } + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.1 }, + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.2 } ] } }, @@ -1507,8 +1507,8 @@ { "group": "bed", "x": [ 6, 7 ], "y": [ 10, 10 ], "chance": 50 } ], "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.2 }, - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.4 } + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.1 }, + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.2 } ] } }, @@ -1671,8 +1671,8 @@ { "group": "gym", "x": [ 10, 10 ], "y": [ 15, 15 ], "chance": 50 } ], "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.2 }, - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.4 } + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.1 }, + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.2 } ] } }, @@ -1830,8 +1830,8 @@ { "group": "gym", "x": [ 4, 4 ], "y": [ 15, 15 ], "chance": 50 } ], "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.1 }, - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.9 } + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.05 }, + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.45 } ], "place_vehicles": [ { "vehicle": "golf_cart", "x": 16, "y": 8, "chance": 75, "rotation": 90 } @@ -1988,8 +1988,8 @@ { "group": "army_personal_locker", "x": [ 15, 15 ], "y": [ 0, 0 ], "chance": 70 } ], "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.1 }, - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.9 } + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.05 }, + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.45 } ] } }, @@ -2149,8 +2149,8 @@ { "group": "army_personal_locker", "x": [ 5, 5 ], "y": [ 0, 0 ], "chance": 70 } ], "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.1 }, - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.5 } + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.05 }, + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.45 } ] } }, @@ -2310,8 +2310,8 @@ { "group": "hospital_bed", "x": [ 12, 12 ], "y": [ 1, 1 ], "chance": 70 } ], "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.1 }, - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.5 } + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.05 }, + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.45 } ], "place_vehicles": [ { "vehicle": "golf_cart", "x": 4, "y": 20, "chance": 75, "rotation": 90 } @@ -2465,8 +2465,8 @@ { "group": "hospital_bed", "x": [ 5, 5 ], "y": [ 18, 18 ], "chance": 70 } ], "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.1 }, - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.5 } + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.05 }, + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.45 } ], "place_vehicles": [ { "vehicle": "golf_cart", "x": 19, "y": 21, "chance": 75, "rotation": 90 } @@ -2626,8 +2626,8 @@ { "group": "hospital_bed", "x": [ 3, 13 ], "y": [ 18, 18 ], "chance": 70 } ], "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.1 }, - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.5 } + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.05 }, + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.45 } ], "place_vehicles": [ { "vehicle": "golf_cart", "x": 3, "y": 22, "chance": 75, "rotation": 90 } @@ -2789,8 +2789,8 @@ { "group": "hospital_bed", "x": [ 12, 12 ], "y": [ 8, 8 ], "chance": 70 } ], "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 12 ], "y": [ 1, 22 ], "density": 0.1 }, - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 12 ], "y": [ 1, 22 ], "density": 0.5 } + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 12 ], "y": [ 1, 22 ], "density": 0.05 }, + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 12 ], "y": [ 1, 22 ], "density": 0.45 } ] } }, @@ -2941,8 +2941,8 @@ { "group": "prison_weapons", "x": [ 9, 10 ], "y": [ 3, 3 ], "chance": 70 } ], "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.1 }, - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.5 } + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.05 }, + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.45 } ] } }, @@ -3090,7 +3090,7 @@ { "group": "cop_armory", "x": [ 19, 19 ], "y": [ 22, 23 ], "chance": 90 } ], "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.5 } + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.25 } ] } }, @@ -3240,8 +3240,8 @@ { "group": "fireman_gear", "x": [ 13, 15 ], "y": [ 16, 16 ], "chance": 85 } ], "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 12 ], "density": 0.1 }, - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 12 ], "density": 0.5 } + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 12 ], "density": 0.05 }, + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 12 ], "density": 0.25 } ] } }, @@ -3391,7 +3391,7 @@ { "group": "fireman_gear", "x": [ 6, 8 ], "y": [ 20, 20 ], "chance": 85 } ], "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 10 ], "density": 0.5 } + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 10 ], "density": 0.25 } ] } }, @@ -3541,8 +3541,8 @@ { "group": "mechanics", "x": [ 2, 3 ], "y": [ 20, 20 ], "chance": 85 } ], "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 6, 22 ], "y": [ 1, 20 ], "density": 0.1 }, - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 6, 22 ], "y": [ 1, 20 ], "density": 0.5 } + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 6, 22 ], "y": [ 1, 20 ], "density": 0.05 }, + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 6, 22 ], "y": [ 1, 20 ], "density": 0.25 } ], "place_vehicles": [ { "vehicle": "golf_cart", "x": 13, "y": 22, "chance": 50, "rotation": 270 } @@ -3701,8 +3701,8 @@ { "class": "old_guard_soldier", "x": 14, "y": 20 } ], "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 10 ], "density": 0.5 }, - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 10 ], "density": 0.5 } + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 10 ], "density": 0.25 }, + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 10 ], "density": 0.25 } ], "place_vehicles": [ { "vehicle": "golf_cart", "x": 11, "y": 7, "chance": 25, "rotation": 270 }, @@ -3852,8 +3852,8 @@ "z": "f_crate_c" }, "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.3 }, - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.3 } + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.15 }, + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.15 } ], "place_vehicles": [ { "vehicle": "golf_cart", "x": 23, "y": 7, "chance": 25, "rotation": 270 }, @@ -4012,8 +4012,8 @@ { "group": "prison_armor", "x": [ 12, 13 ], "y": [ 10, 10 ], "chance": 85 } ], "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.2 }, - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.5 } + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.1 }, + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.25 } ], "place_vehicles": [ { "vehicle": "golf_cart", "x": 19, "y": 17, "chance": 25, "rotation": 90 }, @@ -4168,8 +4168,8 @@ { "group": "prison_armor", "x": [ 11, 17 ], "y": [ 7, 7 ], "chance": 85 } ], "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.1 }, - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.5 } + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.05 }, + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.25 } ], "place_vehicles": [ { "vehicle": "golf_cart", "x": 5, "y": 17, "chance": 25, "rotation": 270 }, @@ -4335,8 +4335,8 @@ { "group": "cop_gear", "x": [ 19, 21 ], "y": [ 0, 0 ], "chance": 50 } ], "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.1 }, - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.5 } + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.05 }, + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.25 } ] } }, @@ -4481,8 +4481,8 @@ "z": "f_crate_c" }, "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.1 }, - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.5 } + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.05 }, + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.25 } ] } }, @@ -4630,8 +4630,8 @@ { "group": "cleaning", "x": [ 11, 11 ], "y": [ 4, 4 ], "chance": 50 } ], "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 4, 22 ], "density": 0.1 }, - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 4, 22 ], "density": 0.5 } + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 4, 22 ], "density": 0.05 }, + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 4, 22 ], "density": 0.25 } ], "place_vehicles": [ { "vehicle": "golf_cart", "x": 10, "y": 9, "chance": 25, "rotation": 180 } @@ -4784,8 +4784,8 @@ { "group": "electronics", "x": [ 6, 6 ], "y": [ 2, 5 ], "chance": 90 } ], "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.1 }, - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.5 } + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.05 }, + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.25 } ] } }, @@ -4930,8 +4930,8 @@ "z": "f_crate_c" }, "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 4, 22 ], "y": [ 1, 22 ], "density": 0.1 }, - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 4, 22 ], "y": [ 1, 22 ], "density": 0.5 } + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 4, 22 ], "y": [ 1, 22 ], "density": 0.05 }, + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 4, 22 ], "y": [ 1, 22 ], "density": 0.25 } ], "place_vehicles": [ { "vehicle": "golf_cart", "x": 16, "y": 18, "chance": 25, "rotation": 270 }, @@ -5080,8 +5080,8 @@ "z": "f_crate_c" }, "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 6, 22 ], "density": 0.1 }, - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 6, 22 ], "density": 0.5 } + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 6, 22 ], "density": 0.05 }, + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 6, 22 ], "density": 0.25 } ], "place_vehicles": [ { "vehicle": "golf_cart", "x": 23, "y": 8, "chance": 15, "rotation": 270 }, @@ -5238,8 +5238,8 @@ { "group": "office", "x": [ 13, 15 ], "y": [ 2, 6 ], "chance": 70 } ], "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 12, 14 ], "density": 0.7 }, - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 12, 14 ], "density": 0.7 } + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 12, 14 ], "density": 0.35 }, + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 12, 14 ], "density": 0.35 } ] } }, @@ -5391,8 +5391,8 @@ { "group": "office", "x": [ 9, 11 ], "y": [ 20, 20 ], "chance": 70 } ], "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 12 ], "density": 0.1 }, - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 12 ], "density": 0.5 } + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 12 ], "density": 0.05 }, + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 12 ], "density": 0.25 } ] } }, @@ -5547,8 +5547,8 @@ { "class": "old_guard_soldier", "x": 8, "y": 21 } ], "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 12 ], "density": 0.1 }, - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 12 ], "density": 0.5 } + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 12 ], "density": 0.05 }, + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 12 ], "density": 0.25 } ] } }, @@ -5696,8 +5696,8 @@ { "group": "bed", "x": [ 20, 21 ], "y": [ 8, 8 ], "chance": 50 } ], "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 14 ], "density": 0.1 }, - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 14 ], "density": 0.1 } + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 14 ], "density": 0.05 }, + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 14 ], "density": 0.05 } ] } }, @@ -5842,8 +5842,8 @@ "z": "f_crate_c" }, "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.1 }, - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.2 } + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.05 }, + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.1 } ] } }, @@ -5992,8 +5992,8 @@ { "group": "cavern", "x": [ 20, 22 ], "y": [ 23, 23 ], "chance": 80 } ], "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.2 }, - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.2 } + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.1 }, + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.1 } ], "place_vehicles": [ { "vehicle": "humvee", "x": 9, "y": 1, "chance": 60, "rotation": 90 }, @@ -6145,8 +6145,8 @@ { "group": "novels", "x": [ 15, 15 ], "y": [ 19, 20 ], "chance": 80 } ], "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.1 }, - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.1 } + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.05 }, + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.05 } ], "place_vehicles": [ { "vehicle": "humvee", "x": 5, "y": 1, "chance": 100, "rotation": 90 }, @@ -6301,8 +6301,8 @@ { "group": "necropolis_visitors", "x": [ 12, 13 ], "y": [ 7, 7 ], "chance": 80 } ], "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.1 }, - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.5 } + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.05 }, + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.25 } ] } }, @@ -6452,8 +6452,8 @@ { "group": "prison_armor", "x": [ 21, 21 ], "y": [ 0, 2 ], "chance": 80 } ], "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 12 ], "y": [ 1, 22 ], "density": 0.1 }, - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 12 ], "y": [ 1, 22 ], "density": 0.5 } + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 12 ], "y": [ 1, 22 ], "density": 0.05 }, + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 12 ], "y": [ 1, 22 ], "density": 0.25 } ] } }, @@ -6606,8 +6606,8 @@ { "group": "dining", "x": [ 17, 18 ], "y": [ 22, 23 ], "chance": 70 } ], "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 18, 22 ], "density": 0.1 }, - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 18, 22 ], "density": 0.5 } + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 18, 22 ], "density": 0.05 }, + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 18, 22 ], "density": 0.25 } ] } }, @@ -6761,8 +6761,8 @@ { "group": "dining", "x": [ 0, 1 ], "y": [ 22, 23 ], "chance": 70 } ], "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 18, 22 ], "density": 0.1 }, - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 18, 22 ], "density": 0.1 } + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 18, 22 ], "density": 0.05 }, + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 18, 22 ], "density": 0.05 } ] } }, @@ -6922,8 +6922,8 @@ { "class": "old_guard_soldier", "x": 18, "y": 5 } ], "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 4 ], "y": [ 20, 22 ], "density": 0.1 }, - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 6 ], "y": [ 20, 22 ], "density": 0.1 } + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 4 ], "y": [ 20, 22 ], "density": 0.05 }, + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 6 ], "y": [ 20, 22 ], "density": 0.05 } ] } }, @@ -7074,8 +7074,8 @@ { "group": "novels", "x": [ 1, 2 ], "y": [ 9, 9 ], "chance": 70 } ], "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 12, 22 ], "y": [ 1, 22 ], "density": 0.1 }, - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 12, 22 ], "y": [ 1, 22 ], "density": 0.2 } + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 12, 22 ], "y": [ 1, 22 ], "density": 0.05 }, + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 12, 22 ], "y": [ 1, 22 ], "density": 0.05 } ] } }, @@ -7369,8 +7369,8 @@ { "group": "mechanics", "x": [ 14, 14 ], "y": [ 10, 13 ], "chance": 85 } ], "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.1 }, - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.1 } + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.05 }, + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.05 } ] } }, @@ -7522,8 +7522,8 @@ { "group": "novels", "x": [ 5, 5 ], "y": [ 3, 4 ], "chance": 70 } ], "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.1 }, - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.1 } + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.05 }, + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.05 } ], "place_vehicles": [ { "vehicle": "golf_cart", "x": 20, "y": 19, "chance": 25, "rotation": 90 }, @@ -7674,8 +7674,8 @@ "z": "f_crate_c" }, "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.1 }, - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.1 } + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.05 }, + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.05 } ], "place_vehicles": [ { "vehicle": "golf_cart", "x": 3, "y": 19, "chance": 25, "rotation": 90 }, @@ -7825,8 +7825,8 @@ "z": "f_crate_c" }, "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.1 }, - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.1 } + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.05 }, + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.05 } ], "place_vehicles": [ { "vehicle": "golf_cart", "x": 11, "y": 2, "chance": 20, "rotation": 270 }, @@ -7995,8 +7995,8 @@ { "group": "dining", "x": [ 10, 11 ], "y": [ 0, 1 ], "chance": 70 } ], "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.3 }, - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.5 } + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.15 }, + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.25 } ] } }, @@ -8154,8 +8154,8 @@ { "group": "dining", "x": [ 0, 1 ], "y": [ 0, 1 ], "chance": 70 } ], "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.3 }, - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.5 } + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.15 }, + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.25 } ] } }, @@ -8313,8 +8313,8 @@ { "group": "cleaning", "x": [ 3, 3 ], "y": [ 9, 9 ], "chance": 70 } ], "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.3 }, - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.5 } + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.15 }, + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.25 } ] } }, @@ -8459,8 +8459,8 @@ "z": "f_crate_c" }, "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.1 }, - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.1 } + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.05 }, + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.05 } ], "place_vehicles": [ { "vehicle": "golf_cart", "x": 16, "y": 1, "chance": 15, "rotation": 270 }, @@ -8758,8 +8758,8 @@ "z": "f_crate_c" }, "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.1 }, - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.1 } + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.05 }, + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.05 } ] } }, @@ -8904,8 +8904,8 @@ "z": "f_crate_c" }, "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.1 }, - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.3 } + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.05 }, + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.15 } ], "place_vehicles": [ { "vehicle": "golf_cart", "x": 19, "y": 16, "chance": 20, "rotation": 270 }, @@ -9063,8 +9063,8 @@ { "group": "novels", "x": [ 20, 20 ], "y": [ 22, 23 ], "chance": 80 } ], "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.1 }, - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.2 } + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.05 }, + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.1 } ] } }, @@ -9218,8 +9218,8 @@ { "group": "cleaning", "x": [ 17, 17 ], "y": [ 20, 22 ], "chance": 80 } ], "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.1 }, - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.2 } + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.05 }, + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.1 } ] } }, @@ -9368,8 +9368,8 @@ { "group": "dining", "x": [ 10, 11 ], "y": [ 0, 1 ], "chance": 50 } ], "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.1 }, - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.5 } + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.05 }, + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.25 } ] } }, @@ -9518,8 +9518,8 @@ { "group": "dining", "x": [ 18, 19 ], "y": [ 0, 1 ], "chance": 50 } ], "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.1 }, - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.5 } + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.05 }, + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.25 } ], "place_vehicles": [ { "vehicle": "golf_cart", "x": 19, "y": 16, "chance": 20, "rotation": 180 }, @@ -9684,8 +9684,8 @@ { "group": "dining", "x": [ 8, 9 ], "y": [ 0, 1 ], "chance": 50 } ], "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.1 }, - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.5 } + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.05 }, + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.25 } ] } }, @@ -9830,8 +9830,8 @@ "z": "f_crate_c" }, "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 16 ], "density": 0.1 }, - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 16 ], "density": 0.5 } + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 16 ], "density": 0.05 }, + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 16 ], "density": 0.25 } ] } }, @@ -9976,8 +9976,8 @@ "z": "f_crate_c" }, "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.1 }, - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.5 } + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.05 }, + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.25 } ] } }, @@ -10150,8 +10150,8 @@ { "group": "office", "x": [ 7, 9 ], "y": [ 1, 1 ], "chance": 75 } ], "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.4 }, - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.5 } + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.2 }, + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.25 } ] } }, @@ -10313,8 +10313,8 @@ { "group": "office", "x": [ 19, 21 ], "y": [ 3, 3 ], "chance": 75 } ], "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.4 }, - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.5 } + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.2 }, + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.25 } ] } }, @@ -10477,8 +10477,8 @@ { "group": "office", "x": [ 3, 5 ], "y": [ 3, 3 ], "chance": 75 } ], "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.3 }, - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.5 } + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.15 }, + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.25 } ] } }, @@ -10646,8 +10646,8 @@ { "group": "office", "x": [ 20, 20 ], "y": [ 5, 7 ], "chance": 75 } ], "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.1 }, - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.1 } + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.05 }, + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.05 } ] } }, @@ -10798,8 +10798,8 @@ { "group": "office", "x": [ 0, 3 ], "y": [ 3, 3 ], "chance": 75 } ], "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 10 ], "density": 0.1 }, - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 10 ], "density": 0.1 } + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 10 ], "density": 0.05 }, + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 10 ], "density": 0.05 } ] } }, @@ -10944,8 +10944,8 @@ "z": "f_crate_c" }, "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.1 }, - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.5 } + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.05 }, + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.25 } ], "place_vehicles": [ { "vehicle": "golf_cart", "x": 19, "y": 0, "chance": 20, "rotation": 180 }, @@ -11105,8 +11105,8 @@ { "group": "dining", "x": [ 19, 20 ], "y": [ 5, 6 ], "chance": 50 } ], "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.1 }, - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.5 } + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.05 }, + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.25 } ] } }, @@ -11270,8 +11270,8 @@ { "group": "dining", "x": [ 1, 2 ], "y": [ 5, 6 ], "chance": 50 } ], "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.1 }, - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.5 } + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.05 }, + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.25 } ] } }, @@ -11585,8 +11585,8 @@ { "group": "school", "x": [ 12, 12 ], "y": [ 19, 21 ], "chance": 75 } ], "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.4 }, - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.5 } + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.2 }, + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.25 } ] } }, @@ -11747,8 +11747,8 @@ { "group": "school", "x": [ 0, 10 ], "y": [ 0, 0 ], "chance": 50 } ], "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.9 }, - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.9 } + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.45 }, + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.45 } ] } }, @@ -11905,8 +11905,8 @@ { "group": "school", "x": [ 10, 10 ], "y": [ 5, 8 ], "chance": 75 } ], "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.3 }, - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.5 } + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.15 }, + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.25 } ] } }, @@ -12059,8 +12059,8 @@ { "group": "office", "x": [ 7, 9 ], "y": [ 20, 20 ], "chance": 75 } ], "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.1 }, - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.5 } + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.05 }, + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.25 } ] } }, @@ -12205,8 +12205,8 @@ "z": "f_crate_c" }, "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.1 }, - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.5 } + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.05 }, + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.25 } ] } }, @@ -12355,8 +12355,8 @@ { "group": "office", "x": [ 17, 19 ], "y": [ 20, 20 ], "chance": 75 } ], "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.1 }, - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.5 } + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.05 }, + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.25 } ] } }, @@ -12508,8 +12508,8 @@ { "group": "cloning_vat", "x": [ 22, 22 ], "y": [ 11, 11 ], "chance": 75 } ], "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.1 }, - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.5 } + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.05 }, + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.25 } ] } }, @@ -12662,8 +12662,8 @@ { "group": "cloning_vat", "x": [ 1, 1 ], "y": [ 11, 11 ], "chance": 75 } ], "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.1 }, - { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.5 } + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.05 }, + { "monster": "GROUP_NECROPOLIS_VAULT2", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.25 } ] } } diff --git a/data/json/mapgen/necropolis/necropolisB3.json b/data/json/mapgen/necropolis/necropolisB3.json index 79aad4058e4a..0318466aead0 100644 --- a/data/json/mapgen/necropolis/necropolisB3.json +++ b/data/json/mapgen/necropolis/necropolisB3.json @@ -661,8 +661,8 @@ { "item": "traveler", "x": [ 14, 14 ], "y": [ 7, 7 ], "chance": 40 } ], "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 5, 22 ], "y": [ 1, 22 ], "density": 0.3 }, - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 5, 22 ], "y": [ 1, 22 ], "density": 0.6 } + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 5, 22 ], "y": [ 1, 22 ], "density": 0.15 }, + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 5, 22 ], "y": [ 1, 22 ], "density": 0.3 } ] } }, @@ -835,8 +835,8 @@ { "item": "traveler", "x": [ 1, 1 ], "y": [ 7, 7 ], "chance": 40 } ], "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.3 }, - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.6 } + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.15 }, + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.3 } ] } }, @@ -993,8 +993,8 @@ { "item": "fridgesnacks", "x": [ 16, 16 ], "y": [ 2, 2 ], "chance": 70 } ], "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.3 }, - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.3 } + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.15 }, + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.15 } ] } }, @@ -1158,8 +1158,8 @@ { "item": "lab_torso", "x": [ 4, 4 ], "y": [ 2, 5 ], "chance": 70 } ], "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.3 }, - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.3 } + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.15 }, + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.15 } ] } }, @@ -1317,8 +1317,8 @@ { "item": "bed", "x": [ 21, 22 ], "y": [ 8, 9 ], "chance": 70 } ], "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.2 }, - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.2 } + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.1 }, + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.1 } ] } }, @@ -1488,8 +1488,8 @@ { "item": "bed", "x": [ 21, 22 ], "y": [ 8, 9 ], "chance": 70 } ], "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.3 }, - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.2 } + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.15 }, + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.1 } ] } }, @@ -1640,8 +1640,8 @@ { "item": "electronics", "x": [ 20, 22 ], "y": [ 2, 2 ], "chance": 50 } ], "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 9, 22 ], "density": 0.2 }, - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 9, 22 ], "density": 0.2 } + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 9, 22 ], "density": 0.1 }, + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 9, 22 ], "density": 0.1 } ] } }, @@ -2115,8 +2115,8 @@ { "item": "traveler", "x": [ 10, 10 ], "y": [ 1, 1 ], "chance": 70 } ], "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.6 }, - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.6 } + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.3 }, + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.3 } ] } }, @@ -2273,8 +2273,8 @@ { "item": "traveler", "x": [ 1, 1 ], "y": [ 1, 1 ], "chance": 70 } ], "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.4 }, - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.4 } + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.2 }, + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.2 } ] } }, @@ -2424,8 +2424,8 @@ { "item": "magazines", "x": [ 14, 15 ], "y": [ 7, 8 ], "chance": 60 } ], "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.4 }, - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.4 } + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.2 }, + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.2 } ] } }, @@ -2581,8 +2581,8 @@ { "item": "magazines", "x": [ 3, 14 ], "y": [ 7, 8 ], "chance": 60 } ], "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.3 }, - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.3 } + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.15 }, + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.15 } ] } }, @@ -2739,8 +2739,8 @@ { "item": "traveler", "x": [ 2, 2 ], "y": [ 10, 10 ], "chance": 70 } ], "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.3 }, - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.3 } + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.15 }, + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.15 } ] } }, @@ -2895,8 +2895,8 @@ { "item": "dresser", "x": [ 6, 6 ], "y": [ 21, 21 ], "chance": 70 } ], "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.2 }, - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.2 } + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.1 }, + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.1 } ] } }, @@ -3049,8 +3049,8 @@ "r": { "item": "traveler", "chance": 60 } }, "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.5 }, - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.5 } + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.25 }, + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.25 } ] } }, @@ -3243,8 +3243,8 @@ { "item": "traveler", "x": [ 5, 5 ], "y": [ 8, 10 ], "chance": 60 } ], "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.5 }, - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.5 } + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.25 }, + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.25 } ] } }, @@ -3398,8 +3398,8 @@ "r": { "item": "traveler", "chance": 60 } }, "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.5 }, - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.5 } + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.25 }, + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.25 } ] } }, @@ -3553,8 +3553,8 @@ "r": { "item": "traveler", "chance": 60 } }, "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.5 }, - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.5 } + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.25 }, + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.25 } ] } }, @@ -3708,8 +3708,8 @@ "r": { "item": "traveler", "chance": 60 } }, "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.5 }, - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.5 } + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.25 }, + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.25 } ] } }, @@ -3861,8 +3861,8 @@ "L": { "item": "traveler", "chance": 60 } }, "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.4 }, - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.4 } + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.2 }, + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.2 } ] } }, @@ -4161,8 +4161,8 @@ "~": "f_shower" }, "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.3 }, - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.3 } + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.15 }, + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.15 } ] } }, @@ -4313,8 +4313,8 @@ "L": { "item": "traveler", "chance": 60 } }, "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.7 }, - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.7 } + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.35 }, + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.35 } ] } }, @@ -4467,8 +4467,8 @@ "r": { "item": "traveler", "chance": 60 } }, "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.4 }, - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.4 } + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.2 }, + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.2 } ] } }, @@ -4621,8 +4621,8 @@ "r": { "item": "traveler", "chance": 60 } }, "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.5 }, - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.5 } + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.25 }, + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.25 } ] } }, @@ -4776,8 +4776,8 @@ "r": { "item": "traveler", "chance": 60 } }, "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.6 }, - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.6 } + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.3 }, + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.3 } ] } }, @@ -4931,8 +4931,8 @@ "r": { "item": "traveler", "chance": 60 } }, "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.6 }, - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.6 } + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.3 }, + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.3 } ] } }, @@ -5086,8 +5086,8 @@ "r": { "item": "traveler", "chance": 60 } }, "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.6 }, - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.6 } + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.3 }, + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.3 } ] } }, @@ -5241,8 +5241,8 @@ "r": { "item": "traveler", "chance": 60 } }, "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.6 }, - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.6 } + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.3 }, + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.3 } ] } }, @@ -5396,8 +5396,8 @@ "r": { "item": "traveler", "chance": 60 } }, "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.6 }, - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.6 } + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.3 }, + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.3 } ] } }, @@ -5551,8 +5551,8 @@ "r": { "item": "traveler", "chance": 60 } }, "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.6 }, - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.6 } + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.3 }, + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.3 } ] } }, @@ -5705,8 +5705,8 @@ "r": { "item": "traveler", "chance": 60 } }, "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 15 ], "density": 0.9 }, - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 15 ], "density": 0.9 } + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 15 ], "density": 0.45 }, + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 15 ], "density": 0.45 } ] } }, @@ -5858,8 +5858,8 @@ "r": { "item": "mechanics", "chance": 65 } }, "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.2 }, - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.2 } + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.1 }, + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.1 } ] } }, @@ -6316,8 +6316,8 @@ "r": { "item": "alcohol", "chance": 5 } }, "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.4 }, - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.4 } + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.2 }, + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.2 } ] } }, @@ -6474,8 +6474,8 @@ { "item": "allclothes", "x": [ 3, 4 ], "y": [ 13, 16 ], "chance": 75 } ], "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.4 }, - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.4 } + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.2 }, + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.2 } ] } }, @@ -6629,8 +6629,8 @@ "r": { "item": "traveler", "chance": 60 } }, "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.6 }, - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.6 } + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.3 }, + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.3 } ] } }, @@ -6784,8 +6784,8 @@ "r": { "item": "traveler", "chance": 60 } }, "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.6 }, - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.6 } + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.3 }, + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.3 } ] } }, @@ -6939,8 +6939,8 @@ "r": { "item": "traveler", "chance": 60 } }, "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.6 }, - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.6 } + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.3 }, + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.3 } ] } }, @@ -7094,8 +7094,8 @@ "r": { "item": "traveler", "chance": 60 } }, "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.3 }, - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.3 } + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.15 }, + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.15 } ] } }, @@ -7248,8 +7248,8 @@ "r": { "item": "traveler", "chance": 60 } }, "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.4 }, - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.4 } + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.2 }, + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.2 } ] } }, @@ -7402,8 +7402,8 @@ "r": [ { "item": "jumpsuit", "chance": 1 }, { "item": "jumpsuit", "chance": 2 }, { "item": "jumpsuit", "chance": 3 }, { "item": "jumpsuit", "chance": 4 }, { "item": "pants", "chance": 3 }, { "item": "coat_lab", "chance": 3 } ] }, "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 8, 22 ], "density": 0.4 }, - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 8, 22 ], "density": 0.4 } + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 8, 22 ], "density": 0.2 }, + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 8, 22 ], "density": 0.2 } ] } }, @@ -7557,8 +7557,8 @@ "r": { "item": "tools_tailor", "chance": 60 } }, "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.5 }, - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.5 } + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.25 }, + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.25 } ] } }, @@ -7712,8 +7712,8 @@ "r": { "item": "traveler", "chance": 60 } }, "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.6 }, - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.6 } + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.3 }, + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.3 } ] } }, @@ -7867,8 +7867,8 @@ "r": { "item": "traveler", "chance": 60 } }, "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.5 }, - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.5 } + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.25 }, + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.25 } ] } }, @@ -8020,8 +8020,8 @@ { "item": "cannedfood", "x": [ 23, 23 ], "y": [ 23, 23 ], "chance": 75 } ], "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.4 }, - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.4 } + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.2 }, + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.2 } ] } }, @@ -8173,8 +8173,8 @@ "r": { "item": "cannedfood", "chance": 60 } }, "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.3 }, - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.3 } + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.15 }, + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.15 } ] } }, @@ -8327,8 +8327,8 @@ "r": { "item": "traveler", "chance": 60 } }, "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.4 }, - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.4 } + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.2 }, + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.2 } ] } }, @@ -8481,8 +8481,8 @@ "r": { "item": "traveler", "chance": 60 } }, "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.6 }, - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.6 } + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.3 }, + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.3 } ] } }, @@ -8635,8 +8635,8 @@ "r": { "item": "traveler", "chance": 60 } }, "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.5 }, - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.5 } + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.25 }, + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.25 } ] } }, @@ -8789,8 +8789,8 @@ "r": { "item": "traveler", "chance": 60 } }, "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.5 }, - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.5 } + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.25 }, + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.25 } ] } }, @@ -8944,8 +8944,8 @@ "r": { "item": "traveler", "chance": 60 } }, "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.6 }, - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.6 } + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.3 }, + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.3 } ] } }, @@ -9099,8 +9099,8 @@ "r": { "item": "traveler", "chance": 60 } }, "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.7 }, - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.7 } + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.35 }, + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.35 } ] } }, @@ -9254,8 +9254,8 @@ "r": { "item": "traveler", "chance": 60 } }, "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.6 }, - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.6 } + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.3 }, + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.3 } ] } }, @@ -9410,8 +9410,8 @@ "n": [ { "item": "water_clean", "chance": 20 } ] }, "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.4 }, - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.4 } + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.2 }, + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.2 } ] } }, @@ -9567,8 +9567,8 @@ "n": [ { "item": "water_clean", "chance": 20 } ] }, "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.3 }, - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.3 } + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.15 }, + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.15 } ] } }, @@ -9723,8 +9723,8 @@ "r": { "item": "produce", "chance": 60 } }, "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.4 }, - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.4 } + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.2 }, + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.2 } ] } }, @@ -9877,8 +9877,8 @@ "r": { "item": "traveler", "chance": 60 } }, "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.6 }, - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.6 } + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.3 }, + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.3 } ] } }, @@ -10031,8 +10031,8 @@ "r": { "item": "traveler", "chance": 60 } }, "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.6 }, - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.6 } + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.3 }, + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.3 } ] } }, @@ -10185,8 +10185,8 @@ "r": { "item": "traveler", "chance": 60 } }, "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.5 }, - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.5 } + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.25 }, + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.25 } ] } }, @@ -10340,8 +10340,8 @@ "r": { "item": "traveler", "chance": 60 } }, "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.6 }, - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.6 } + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.3 }, + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.3 } ] } }, @@ -10495,8 +10495,8 @@ "r": { "item": "traveler", "chance": 60 } }, "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.6 }, - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.6 } + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.3 }, + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.3 } ] } }, @@ -10650,8 +10650,8 @@ "r": { "item": "mechanics", "chance": 60 } }, "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.6 }, - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.6 } + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.3 }, + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.3 } ] } }, @@ -10801,8 +10801,8 @@ "k": { "item": "office", "chance": 60 } }, "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.4 }, - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.4 } + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.2 }, + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.2 } ] } }, @@ -10953,8 +10953,8 @@ "o": { "item": "textbooks", "chance": 60 } }, "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.3 }, - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.3 } + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.15 }, + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.15 } ] } }, @@ -11105,8 +11105,8 @@ "c": { "item": "beauty", "chance": 20 } }, "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.3 }, - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.3 } + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.15 }, + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.15 } ] } }, @@ -11258,8 +11258,8 @@ "c": { "item": "beauty", "chance": 20 } }, "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.3 }, - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.3 } + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.15 }, + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.15 } ] } }, @@ -11412,8 +11412,8 @@ "r": { "item": "traveler", "chance": 60 } }, "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.2 }, - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.2 } + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.1 }, + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.1 } ] } }, @@ -11563,8 +11563,8 @@ "r": { "item": "textbooks", "chance": 60 } }, "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.3 }, - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.3 } + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.15 }, + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.15 } ] } }, @@ -11741,8 +11741,8 @@ { "item": "textbooks", "x": [ 0, 1 ], "y": [ 21, 21 ], "chance": 75 } ], "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.3 }, - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.3 } + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.15 }, + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.15 } ] } }, @@ -11892,8 +11892,8 @@ "r": { "item": "textbooks", "chance": 60 } }, "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.3 }, - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.3 } + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.15 }, + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.15 } ] } }, @@ -12044,8 +12044,8 @@ "r": { "item": "mechanics", "chance": 60 } }, "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.3 }, - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.3 } + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.15 }, + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.15 } ] } }, @@ -12195,8 +12195,8 @@ "/": { "item": "tools_common", "chance": 2 } }, "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.3 }, - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.3 } + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.15 }, + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.15 } ] } }, @@ -12348,8 +12348,8 @@ "r": { "item": "mechanics", "chance": 60 } }, "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.3 }, - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.3 } + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.15 }, + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.15 } ] } }, @@ -12499,8 +12499,8 @@ "b": { "item": "sports", "chance": 10 } }, "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.3 }, - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.3 } + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.15 }, + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.15 } ] } }, @@ -12651,8 +12651,8 @@ "l": { "item": "vending_drink", "chance": 70 } }, "place_monsters": [ - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.2 }, - { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.2 } + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.1 }, + { "monster": "GROUP_NECROPOLIS_VAULT3", "x": [ 1, 22 ], "y": [ 1, 22 ], "density": 0.1 } ] } } From 826ffd0adfe864677e6ea56aa39904487f15042f Mon Sep 17 00:00:00 2001 From: David DeGanne Date: Wed, 16 May 2018 21:40:35 -0600 Subject: [PATCH 14/35] Modified recipes "soap" and "lye powder from lye", added recipe "lye from lye powder" --- .../json/recipes/recipe_medsandchemicals.json | 23 +++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/data/json/recipes/recipe_medsandchemicals.json b/data/json/recipes/recipe_medsandchemicals.json index 48e3b87056a1..13e63204ee0a 100644 --- a/data/json/recipes/recipe_medsandchemicals.json +++ b/data/json/recipes/recipe_medsandchemicals.json @@ -1788,7 +1788,7 @@ "batch_time_factors": [ 80, 4 ], "qualities": [ { "id": "BOIL", "level": 1 } ], "tools": [ [ [ "water_boiling_heat", 120, "LIST" ] ] ], - "components": [ [ [ "lye", 40 ] ] ] + "components": [ [ [ "lye", 6 ] ] ] },{ "type" : "recipe", "result": "heatpack", @@ -1932,7 +1932,7 @@ [ "cooking_oil", 2 ] ], [ - [ "lye_powder", 10 ], [ "lye", 2 ] + [ "lye", 2 ] ], [ [ "water", 1 ], @@ -2371,5 +2371,24 @@ [ [ "water", 1 ], [ "water_clean", 1 ] ], [ [ "ash", 500 ] ] ] +},{ + "result": "lye", + "id_suffix": "from_lye_powder", + "type": "recipe", + "category": "CC_CHEM", + "subcategory": "CSC_CHEM_OTHER", + "skill_used": "cooking", + "difficulty": 2, + "time": 5000, + "batch_time_factors": [ 20, 2 ], + "autolearn": true, + "qualities":[ + {"id":"CHEM","level":1}, + {"id":"CONTAIN","level":1} + ], "tools": [ [ [ "surface_heat", 5, "LIST" ] ] ], + "components": [ + [ [ "water", 1 ], [ "water_clean", 1 ] ], + [ [ "lye_powder", 35 ] ] + ] } ] From 377748f97b44926e16ff496e667ef5a899486614 Mon Sep 17 00:00:00 2001 From: Night-Pryanik Date: Thu, 17 May 2018 10:18:14 +0400 Subject: [PATCH 15/35] Added monster_species field to the json --- data/json/npcs/missiondef.json | 1 + src/mission.cpp | 1 + src/mission.h | 1 + src/missiondef.cpp | 4 ++++ 4 files changed, 7 insertions(+) diff --git a/data/json/npcs/missiondef.json b/data/json/npcs/missiondef.json index 893e2a1d6019..f4e03d23b92a 100644 --- a/data/json/npcs/missiondef.json +++ b/data/json/npcs/missiondef.json @@ -505,6 +505,7 @@ "type": "mission_definition", "name": "Kill 100 Zombies", "goal": "MGOAL_KILL_MONSTER_SPEC", + "monster_species": "ZOMBIE", "difficulty": 5, "value": 250000, "start": "kill_100_z", diff --git a/src/mission.cpp b/src/mission.cpp index be29ccc1ecc3..77b172b59fc8 100644 --- a/src/mission.cpp +++ b/src/mission.cpp @@ -29,6 +29,7 @@ mission mission_type::create( const int npc_id ) const ret.item_count = item_count; ret.value = value; ret.follow_up = follow_up; + ret.monster_species = monster_species; if( deadline_low != 0 || deadline_high != 0 ) { ret.deadline = int( calendar::turn ) + rng( deadline_low, deadline_high ); diff --git a/src/mission.h b/src/mission.h index db6236d658a0..563e6a2ff7ed 100644 --- a/src/mission.h +++ b/src/mission.h @@ -182,6 +182,7 @@ struct mission_type { npc_class_id recruit_class = npc_class_id( "NC_NONE" ); // The type of NPC you are to recruit int target_npc_id = -1; std::string monster_type = "mon_null"; + species_id monster_species; int monster_kill_goal = -1; string_id target_id; mission_type_id follow_up = mission_type_id( "MISSION_NULL" ); diff --git a/src/missiondef.cpp b/src/missiondef.cpp index f7d3f34f3c58..51658b89d469 100644 --- a/src/missiondef.cpp +++ b/src/missiondef.cpp @@ -305,6 +305,10 @@ void mission_type::load( JsonObject &jo, const std::string &src ) follow_up = mission_type_id( jo.get_string( "followup" ) ); } + if( jo.has_member( "monster_species" ) ) { + monster_species = species_id( jo.get_string( "monster_species" ) ); + } + assign( jo, "destination", target_id, strict ); } From 0243bc026e4c375ec10ad78c4202cae02cdd623e Mon Sep 17 00:00:00 2001 From: Night-Pryanik Date: Thu, 17 May 2018 10:28:40 +0400 Subject: [PATCH 16/35] Kill count for the mission now reads data from the json --- src/mission_start.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/mission_start.cpp b/src/mission_start.cpp index 5b9d6fcb1515..fcc47e2cfd4b 100644 --- a/src/mission_start.cpp +++ b/src/mission_start.cpp @@ -37,8 +37,6 @@ const mtype_id mon_zombie_necro( "mon_zombie_necro" ); const efftype_id effect_infection( "infection" ); -const species_id ZOMBIE( "ZOMBIE" ); - /* These functions are responsible for making changes to the game at the moment * the mission is accepted by the player. They are also responsible for * updating *miss with the target and any other important information. @@ -361,9 +359,8 @@ void mission_start::kill_100_z( mission *miss ) { npc *p = g->find_npc( miss->npc_id ); p->set_attitude( NPCATT_FOLLOW );//npc joins you - miss->monster_species = ZOMBIE; int killed = 0; - killed += g->kill_count( ZOMBIE ); + killed += g->kill_count( miss->monster_species ); miss->monster_kill_goal = 100 + killed; //your kill score must increase by 100 } From b6da798e058990c3242348a34dca2663a1cfa321 Mon Sep 17 00:00:00 2001 From: David DeGanne Date: Thu, 17 May 2018 01:11:47 -0600 Subject: [PATCH 17/35] removed "lye from lye powder" recipe, added back soap directly from lye powder --- .../json/recipes/recipe_medsandchemicals.json | 26 +++---------------- 1 file changed, 4 insertions(+), 22 deletions(-) diff --git a/data/json/recipes/recipe_medsandchemicals.json b/data/json/recipes/recipe_medsandchemicals.json index 13e63204ee0a..5079d9bbe348 100644 --- a/data/json/recipes/recipe_medsandchemicals.json +++ b/data/json/recipes/recipe_medsandchemicals.json @@ -1932,11 +1932,12 @@ [ "cooking_oil", 2 ] ], [ - [ "lye", 2 ] + [ "lye", 2 ], + [ "lye_powder", 75 ] ], [ - [ "water", 1 ], - [ "water_clean", 1 ] + [ "water", 2 ], + [ "water_clean", 2 ] ] ] },{ @@ -2371,24 +2372,5 @@ [ [ "water", 1 ], [ "water_clean", 1 ] ], [ [ "ash", 500 ] ] ] -},{ - "result": "lye", - "id_suffix": "from_lye_powder", - "type": "recipe", - "category": "CC_CHEM", - "subcategory": "CSC_CHEM_OTHER", - "skill_used": "cooking", - "difficulty": 2, - "time": 5000, - "batch_time_factors": [ 20, 2 ], - "autolearn": true, - "qualities":[ - {"id":"CHEM","level":1}, - {"id":"CONTAIN","level":1} - ], "tools": [ [ [ "surface_heat", 5, "LIST" ] ] ], - "components": [ - [ [ "water", 1 ], [ "water_clean", 1 ] ], - [ [ "lye_powder", 35 ] ] - ] } ] From 4bcbac0307d8b7ea869d6e6fbf08afd0621ecb73 Mon Sep 17 00:00:00 2001 From: "Octav \"Narc\" Sandulescu" Date: Thu, 17 May 2018 19:16:10 +0300 Subject: [PATCH 18/35] Fix src/crafting.cpp line endings (#23746) Somehow the commits in #23721 managed to get it converted to CRLF (maybe due to the Github editor?) and the `.gitattributes` got ignored. This is literally just a `git pull` followed by `git commit -a`. --- src/crafting.cpp | 2930 +++++++++++++++++++++++----------------------- 1 file changed, 1465 insertions(+), 1465 deletions(-) diff --git a/src/crafting.cpp b/src/crafting.cpp index d4cca2cfc2d7..caddba368ca4 100644 --- a/src/crafting.cpp +++ b/src/crafting.cpp @@ -1,1465 +1,1465 @@ -#include "crafting.h" - -#include "catacharset.h" -#include "craft_command.h" -#include "debug.h" -#include "game.h" -#include "game_inventory.h" -#include "input.h" -#include "bionics.h" -#include "inventory.h" -#include "itype.h" -#include "ammo.h" -#include "map.h" -#include "messages.h" -#include "item.h" -#include "npc.h" -#include "calendar.h" -#include "options.h" -#include "output.h" -#include "recipe_dictionary.h" -#include "requirements.h" -#include "rng.h" -#include "translations.h" -#include "ui.h" -#include "vpart_position.h" -#include "vehicle.h" -#include "crafting_gui.h" - -#include //std::min -#include -#include //sqrt -#include -#include -#include -#include - -const efftype_id effect_contacts( "contacts" ); - -void remove_from_component_lookup( recipe *r ); -void drop_or_handle( const item &newit, player &p ); - -static const trait_id trait_DEBUG_HS( "DEBUG_HS" ); -static const trait_id trait_PAWS_LARGE( "PAWS_LARGE" ); -static const trait_id trait_PAWS( "PAWS" ); - -static bool crafting_allowed( const player &p, const recipe &rec ) -{ - if( p.morale_crafting_speed_multiplier( rec ) <= 0.0f ) { - add_msg( m_info, _( "Your morale is too low to craft..." ) ); - return false; - } - - if( p.lighting_craft_speed_multiplier( rec ) <= 0.0f ) { - add_msg( m_info, _( "You can't see to craft!" ) ); - return false; - } - - return true; -} - -float player::lighting_craft_speed_multiplier( const recipe &rec ) const -{ - // negative is bright, 0 is just bright enough, positive is dark, +7.0f is pitch black - float darkness = fine_detail_vision_mod() - 4.0f; - if( darkness <= 0.0f ) { - return 1.0f; // it's bright, go for it - } - bool rec_blind = rec.has_flag( "BLIND_HARD" ) || rec.has_flag( "BLIND_EASY" ); - if( darkness > 0 && !rec_blind ) { - return 0.0f; // it's dark and this recipe can't be crafted in the dark - } - if( rec.has_flag( "BLIND_EASY" ) ) { - // 100% speed in well lit area at skill+0 - // 25% speed in pitch black at skill+0 - // skill+2 removes speed penalty - return 1.0f - ( darkness / ( 7.0f / 0.75f ) ) * std::max( 0, - 2 - exceeds_recipe_requirements( rec ) ) / 2.0f; - } - if( rec.has_flag( "BLIND_HARD" ) && exceeds_recipe_requirements( rec ) >= 2 ) { - // 100% speed in well lit area at skill+2 - // 25% speed in pitch black at skill+2 - // skill+8 removes speed penalty - return 1.0f - ( darkness / ( 7.0f / 0.75f ) ) * std::max( 0, - 8 - exceeds_recipe_requirements( rec ) ) / 6.0f; - } - return 0.0f; // it's dark and you could craft this if you had more skill -} - -float player::morale_crafting_speed_multiplier( const recipe &rec ) const -{ - int morale = get_morale_level(); - if( morale >= 0 ) { - // No bonus for being happy yet - return 1.0f; - } - - // Harder jobs are more frustrating, even when skilled - // For each skill where skill=difficulty, multiply effective morale by 200% - float morale_mult = std::max( 1.0f, 2.0f * rec.difficulty / std::max( 1, - get_skill_level( rec.skill_used ) ) ); - for( const auto &pr : rec.required_skills ) { - morale_mult *= std::max( 1.0f, 2.0f * pr.second / std::max( 1, get_skill_level( pr.first ) ) ); - } - - // Halve speed at -50 effective morale, quarter at -150 - float morale_effect = 1.0f + ( morale_mult * morale ) / -50.0f; - - return 1.0f / morale_effect; -} - -float player::crafting_speed_multiplier( const recipe &rec, bool in_progress ) const -{ - float result = morale_crafting_speed_multiplier( rec ) * lighting_craft_speed_multiplier( rec ); - // Can't start if we'd need 300% time, but we can still finish the job - if( !in_progress && result < 0.33f ) { - return 0.0f; - } - // If we're working below 20% speed, just give up - if( result < 0.2f ) { - return 0.0f; - } - - return result; -} - -bool player::has_morale_to_craft() const -{ - return get_morale_level() >= -50; -} - -void player::craft() -{ - int batch_size = 0; - const recipe *rec = select_crafting_recipe( batch_size ); - if( rec ) { - if( crafting_allowed( *this, *rec ) ) { - make_craft( rec->ident(), batch_size ); - } - } -} - -void player::recraft() -{ - if( lastrecipe.str().empty() ) { - popup( _( "Craft something first" ) ); - } else if( making_would_work( lastrecipe, last_batch ) ) { - last_craft->execute(); - } -} - -void player::long_craft() -{ - int batch_size = 0; - const recipe *rec = select_crafting_recipe( batch_size ); - if( rec ) { - if( crafting_allowed( *this, *rec ) ) { - make_all_craft( rec->ident(), batch_size ); - } - } -} - -bool player::making_would_work( const recipe_id &id_to_make, int batch_size ) -{ - const auto &making = *id_to_make; - if( !( making && crafting_allowed( *this, making ) ) ) { - return false; - } - - if( !can_make( &making, batch_size ) ) { - std::ostringstream buffer; - buffer << _( "You can no longer make that craft!" ) << "\n"; - buffer << making.requirements().list_missing(); - popup( buffer.str(), PF_NONE ); - return false; - } - - return check_eligible_containers_for_crafting( making, batch_size ); -} - -size_t available_assistant_count( const player &u, const recipe &rec ) -{ - // NPCs around you should assist in batch production if they have the skills - // @todo Cache them in activity, include them in modifier calculations - const auto helpers = u.get_crafting_helpers(); - return std::count_if( helpers.begin(), helpers.end(), - [&]( const npc * np ) { - return np->get_skill_level( rec.skill_used ) >= rec.difficulty; - } ); -} - -int player::base_time_to_craft( const recipe &rec, int batch_size ) const -{ - const size_t assistants = available_assistant_count( *this, rec ); - return rec.batch_time( batch_size, 1.0f, assistants ); -} - -int player::expected_time_to_craft( const recipe &rec, int batch_size ) const -{ - const size_t assistants = available_assistant_count( *this, rec ); - float modifier = crafting_speed_multiplier( rec ); - return rec.batch_time( batch_size, modifier, assistants ); -} - -bool player::check_eligible_containers_for_crafting( const recipe &rec, int batch_size ) const -{ - std::vector conts = get_eligible_containers_for_crafting(); - const std::vector res = rec.create_results( batch_size ); - const std::vector bps = rec.create_byproducts( batch_size ); - std::vector all; - all.reserve( res.size() + bps.size() ); - all.insert( all.end(), res.begin(), res.end() ); - all.insert( all.end(), bps.begin(), bps.end() ); - - for( const item &prod : all ) { - if( !prod.made_of( LIQUID ) ) { - continue; - } - - // we go trough half-filled containers first, then go through empty containers if we need - std::sort( conts.begin(), conts.end(), item_ptr_compare_by_charges ); - - long charges_to_store = prod.charges; - for( const item *elem : conts ) { - if( charges_to_store <= 0 ) { - break; - } - - if( !elem->is_container_empty() ) { - if( elem->contents.front().typeId() == prod.typeId() ) { - charges_to_store -= elem->get_remaining_capacity_for_liquid( elem->contents.front(), true ); - } - } else { - charges_to_store -= elem->get_remaining_capacity_for_liquid( prod, true ); - } - } - - // also check if we're currently in a vehicle that has the necessary storage - if( charges_to_store > 0 ) { - if( optional_vpart_position vp = g->m.veh_at( pos() ) ) { - const itype_id &ftype = prod.typeId(); - int fuel_cap = vp->vehicle().fuel_capacity( ftype ); - int fuel_amnt = vp->vehicle().fuel_left( ftype ); - - if( fuel_cap >= 0 ) { - int fuel_space_left = fuel_cap - fuel_amnt; - charges_to_store -= fuel_space_left; - } - } - } - - if( charges_to_store > 0 ) { - popup( _( "You don't have anything to store %s in!" ), prod.tname().c_str() ); - return false; - } - } - - return true; -} - -bool is_container_eligible_for_crafting( const item &cont, bool allow_bucket ) -{ - if( cont.is_watertight_container() || ( allow_bucket && cont.is_bucket() ) ) { - return !cont.is_container_full( allow_bucket ); - } - - return false; -} - -std::vector player::get_eligible_containers_for_crafting() const -{ - std::vector conts; - - if( is_container_eligible_for_crafting( weapon, true ) ) { - conts.push_back( &weapon ); - } - for( const auto &it : worn ) { - if( is_container_eligible_for_crafting( it, false ) ) { - conts.push_back( &it ); - } - } - for( size_t i = 0; i < inv.size(); i++ ) { - for( const auto &it : inv.const_stack( i ) ) { - if( is_container_eligible_for_crafting( it, false ) ) { - conts.push_back( &it ); - } - } - } - - // get all potential containers within PICKUP_RANGE tiles including vehicles - for( const auto &loc : closest_tripoints_first( PICKUP_RANGE, pos() ) ) { - if( g->m.accessible_items( pos(), loc, PICKUP_RANGE ) ) { - for( const auto &it : g->m.i_at( loc ) ) { - if( is_container_eligible_for_crafting( it, true ) ) { - conts.emplace_back( &it ); - } - } - } - - if( optional_vpart_position vp = g->m.veh_at( loc ) ) { - const int part = vp->vehicle().part_with_feature( vp->part_index(), "CARGO" ); - if( part != -1 ) { - for( const auto &it : vp->vehicle().get_items( part ) ) { - if( is_container_eligible_for_crafting( it, false ) ) { - conts.emplace_back( &it ); - } - } - } - } - } - - return conts; -} - -bool player::can_make( const recipe *r, int batch_size ) -{ - const inventory &crafting_inv = crafting_inventory(); - - if( has_recipe( r, crafting_inv, get_crafting_helpers() ) < 0 ) { - return false; - } - - return r->requirements().can_make_with_inventory( crafting_inv, batch_size ); -} - -const inventory &player::crafting_inventory() -{ - if( cached_moves == moves - && cached_time == calendar::turn - && cached_position == pos() ) { - return cached_crafting_inventory; - } - cached_crafting_inventory.form_from_map( pos(), PICKUP_RANGE, false ); - cached_crafting_inventory += inv; - cached_crafting_inventory += weapon; - cached_crafting_inventory += worn; - for( const auto &bio : *my_bionics ) { - const auto &bio_data = bio.info(); - if( ( !bio_data.activated || bio.powered ) && - !bio_data.fake_item.empty() ) { - cached_crafting_inventory += item( bio.info().fake_item, - calendar::turn, power_level ); - } - } - - cached_moves = moves; - cached_time = calendar::turn; - cached_position = pos(); - return cached_crafting_inventory; -} - -void player::invalidate_crafting_inventory() -{ - cached_time = calendar::before_time_starts; -} - -void player::make_craft( const recipe_id &id_to_make, int batch_size ) -{ - make_craft_with_command( id_to_make, batch_size ); -} - -void player::make_all_craft( const recipe_id &id_to_make, int batch_size ) -{ - make_craft_with_command( id_to_make, batch_size, true ); -} - -void player::make_craft_with_command( const recipe_id &id_to_make, int batch_size, bool is_long ) -{ - const auto &recipe_to_make = *id_to_make; - - if( !recipe_to_make ) { - return; - } - - *last_craft = craft_command( &recipe_to_make, batch_size, is_long, this ); - last_craft->execute(); -} - -// @param offset is the index of the created item in the range [0, batch_size-1], -// it makes sure that the used items are distributed equally among the new items. -void set_components( std::vector &components, const std::list &used, - const int batch_size, const size_t offset ) -{ - if( batch_size <= 1 ) { - components.insert( components.begin(), used.begin(), used.end() ); - return; - } - // This count does *not* include items counted by charges! - size_t non_charges_counter = 0; - for( auto &tmp : used ) { - if( tmp.count_by_charges() ) { - components.push_back( tmp ); - // This assumes all (count-by-charges) items of the same type have been merged into one, - // which has a charges value that can be evenly divided by batch_size. - components.back().charges = tmp.charges / batch_size; - } else { - if( ( non_charges_counter + offset ) % batch_size == 0 ) { - components.push_back( tmp ); - } - non_charges_counter++; - } - } -} - -std::list player::consume_components_for_craft( const recipe *making, int batch_size ) -{ - std::list used; - if( has_trait( trait_id( "DEBUG_HS" ) ) ) { - return used; - } - if( last_craft->has_cached_selections() ) { - used = last_craft->consume_components(); - } else { - // This should fail and return, but currently crafting_command isn't saved - // Meaning there are still cases where has_cached_selections will be false - // @todo: Allow saving last_craft and debugmsg+fail craft if selection isn't cached - const auto &req = making->requirements(); - for( const auto &it : req.get_components() ) { - std::list tmp = consume_items( it, batch_size ); - used.splice( used.end(), tmp ); - } - for( const auto &it : req.get_tools() ) { - consume_tools( it, batch_size ); - } - } - return used; -} - - -void player::complete_craft() -{ - //@todo: change making to be a reference, it can never be null anyway - const recipe *making = &recipe_id( activity.name ).obj(); // Which recipe is it? - int batch_size = activity.values.front(); - if( making == nullptr ) { - debugmsg( "no recipe with id %s found", activity.name.c_str() ); - activity.set_to_null(); - return; - } - - int secondary_dice = 0; - int secondary_difficulty = 0; - for( const auto &pr : making->required_skills ) { - secondary_dice += get_skill_level( pr.first ); - secondary_difficulty += pr.second; - } - - // # of dice is 75% primary skill, 25% secondary (unless secondary is null) - int skill_dice; - if( secondary_difficulty > 0 ) { - skill_dice = get_skill_level( making->skill_used ) * 3 + secondary_dice; - } else { - skill_dice = get_skill_level( making->skill_used ) * 4; - } - - auto helpers = g->u.get_crafting_helpers(); - for( const npc *np : helpers ) { - if( np->get_skill_level( making->skill_used ) >= - get_skill_level( making->skill_used ) ) { - // NPC assistance is worth half a skill level - skill_dice += 2; - add_msg( m_info, _( "%s helps with crafting..." ), np->name.c_str() ); - break; - } - } - - // farsightedness can impose a penalty on electronics and tailoring success - // it's equivalent to a 2-rank electronics penalty, 1-rank tailoring - if( has_trait( trait_id( "HYPEROPIC" ) ) && !is_wearing( "glasses_reading" ) && - !is_wearing( "glasses_bifocal" ) && !has_effect( effect_contacts ) ) { - int main_rank_penalty = 0; - if( making->skill_used == skill_id( "electronics" ) ) { - main_rank_penalty = 2; - } else if( making->skill_used == skill_id( "tailor" ) ) { - main_rank_penalty = 1; - } - skill_dice -= main_rank_penalty * 4; - } - - // It's tough to craft with paws. Fortunately it's just a matter of grip and fine-motor, - // not inability to see what you're doing - if( has_trait( trait_PAWS ) || has_trait( trait_PAWS_LARGE ) ) { - int paws_rank_penalty = 0; - if( has_trait( trait_PAWS_LARGE ) ) { - paws_rank_penalty += 1; - } - if( making->skill_used == skill_id( "electronics" ) - || making->skill_used == skill_id( "tailor" ) - || making->skill_used == skill_id( "mechanics" ) ) { - paws_rank_penalty += 1; - } - skill_dice -= paws_rank_penalty * 4; - } - - // Sides on dice is 16 plus your current intelligence - ///\EFFECT_INT increases crafting success chance - int skill_sides = 16 + int_cur; - - int diff_dice; - if( secondary_difficulty > 0 ) { - diff_dice = making->difficulty * 3 + secondary_difficulty; - } else { - // Since skill level is * 4 also - diff_dice = making->difficulty * 4; - } - - int diff_sides = 24; // 16 + 8 (default intelligence) - - int skill_roll = dice( skill_dice, skill_sides ); - int diff_roll = dice( diff_dice, diff_sides ); - - if( making->skill_used ) { - // normalize experience gain to crafting time, giving a bonus for longer crafting - const double batch_mult = batch_size + base_time_to_craft( *making, batch_size ) / 30000.0; - practice( making->skill_used, ( int )( ( making->difficulty * 15 + 10 ) * batch_mult ), - ( int )making->difficulty * 1.25 ); - - //NPCs assisting or watching should gain experience... - for( auto &elem : helpers ) { - //If the NPC can understand what you are doing, they gain more exp - if( elem->get_skill_level( making->skill_used ) >= making->difficulty ) { - elem->practice( making->skill_used, - ( int )( ( making->difficulty * 15 + 10 ) * batch_mult * - .50 ), ( int )making->difficulty * 1.25 ); - if( batch_size > 1 ) { - add_msg( m_info, _( "%s assists with crafting..." ), elem->name.c_str() ); - } - if( batch_size == 1 ) { - add_msg( m_info, _( "%s could assist you with a batch..." ), elem->name.c_str() ); - } - //NPCs around you understand the skill used better - } else { - elem->practice( making->skill_used, - ( int )( ( making->difficulty * 15 + 10 ) * batch_mult * .15 ), - ( int )making->difficulty * 1.25 ); - add_msg( m_info, _( "%s watches you craft..." ), elem->name.c_str() ); - } - } - - } - - // Messed up badly; waste some components. - if( making->difficulty != 0 && diff_roll > skill_roll * ( 1 + 0.1 * rng( 1, 5 ) ) ) { - add_msg( m_bad, _( "You fail to make the %s, and waste some materials." ), making->result_name() ); - consume_components_for_craft( making, batch_size ); - activity.set_to_null(); - return; - // Messed up slightly; no components wasted. - } else if( diff_roll > skill_roll ) { - add_msg( m_neutral, _( "You fail to make the %s, but don't waste any materials." ), - making->result_name() ); - //this method would only have been called from a place that nulls activity.type, - //so it appears that it's safe to NOT null that variable here. - //rationale: this allows certain contexts (e.g. ACT_LONGCRAFT) to distinguish major and minor failures - return; - } - - // If we're here, the craft was a success! - // Use up the components and tools - std::list used = consume_components_for_craft( making, batch_size ); - if( last_craft->has_cached_selections() && used.empty() ) { - // This signals failure, even though there seem to be several paths where it shouldn't... - return; - } - if( !used.empty() ) { - reset_encumbrance(); // in case we were wearing something just consumed up. - } - - // Set up the new item, and assign an inventory letter if available - std::vector newits = making->create_results( batch_size ); - bool first = true; - float used_age_tally = 0; - int used_age_count = 0; - size_t newit_counter = 0; - for( item &newit : newits ) { - // messages, learning of recipe, food spoilage calculation only once - if( first ) { - first = false; - if( knows_recipe( making ) ) { - add_msg( _( "You craft %s from memory." ), newit.type_name( 1 ).c_str() ); - } else { - add_msg( _( "You craft %s using a book as a reference." ), newit.type_name( 1 ).c_str() ); - // If we made it, but we don't know it, - // we're making it from a book and have a chance to learn it. - // Base expected time to learn is 1000*(difficulty^4)/skill/int moves. - // This means time to learn is greatly decreased with higher skill level, - // but also keeps going up as difficulty goes up. - // Worst case is lvl 10, which will typically take - // 10^4/10 (1,000) minutes, or about 16 hours of crafting it to learn. - int difficulty = has_recipe( making, crafting_inventory(), helpers ); - ///\EFFECT_INT increases chance to learn recipe when crafting from a book - if( x_in_y( making->time, ( 1000 * 8 * - ( difficulty * difficulty * difficulty * difficulty ) ) / - ( std::max( get_skill_level( making->skill_used ), 1 ) * std::max( get_int(), 1 ) ) ) ) { - learn_recipe( ( recipe * )making ); - add_msg( m_good, _( "You memorized the recipe for %s!" ), - newit.type_name( 1 ).c_str() ); - } - } - - for( auto &elem : used ) { - if( elem.goes_bad() ) { - used_age_tally += elem.get_relative_rot(); - ++used_age_count; - } - } - } - - // Don't store components for things made by charges, - // don't store components for things that can't be uncrafted. - if( recipe_dictionary::get_uncraft( making->result() ) && !newit.count_by_charges() ) { - // Setting this for items counted by charges gives only problems: - // those items are automatically merged everywhere (map/vehicle/inventory), - // which would either loose this information or merge it somehow. - set_components( newit.components, used, batch_size, newit_counter ); - newit_counter++; - } - finalize_crafted_item( newit, used_age_tally, used_age_count ); - set_item_inventory( newit ); - } - - if( making->has_byproducts() ) { - std::vector bps = making->create_byproducts( batch_size ); - for( auto &bp : bps ) { - finalize_crafted_item( bp, used_age_tally, used_age_count ); - set_item_inventory( bp ); - } - } - - inv.restack( *this ); -} - -void set_item_spoilage( item &newit, float used_age_tally, int used_age_count ) -{ - newit.set_relative_rot( used_age_tally / used_age_count ); -} - -void set_item_food( item &newit ) -{ - //@todo: encapsulate this into some function - int bday_tmp = to_turn( newit.birthday() ) % 3600; // fuzzy birthday for stacking reasons - newit.set_birthday( newit.birthday() + 3600_turns - time_duration::from_turns( bday_tmp ) ); - if( newit.has_flag( "EATEN_HOT" ) ) { // hot foods generated - newit.item_tags.insert( "HOT" ); - newit.item_counter = 600; - newit.active = true; - } -} - -void finalize_crafted_item( item &newit, float used_age_tally, int used_age_count ) -{ - if( newit.is_food() ) { - set_item_food( newit ); - } - if( used_age_count > 0 && newit.goes_bad() ) { - set_item_spoilage( newit, used_age_tally, used_age_count ); - } -} - -void set_item_inventory( item &newit ) -{ - if( newit.made_of( LIQUID ) ) { - g->handle_all_liquid( newit, PICKUP_RANGE ); - } else { - g->u.inv.assign_empty_invlet( newit, g->u ); - // We might not have space for the item - if( !g->u.can_pickVolume( newit ) ) { //Accounts for result_mult - add_msg( _( "There's no room in your inventory for the %s, so you drop it." ), - newit.tname().c_str() ); - g->m.add_item_or_charges( g->u.pos(), newit ); - } else if( !g->u.can_pickWeight( newit, !get_option( "DANGEROUS_PICKUPS" ) ) ) { - add_msg( _( "The %s is too heavy to carry, so you drop it." ), - newit.tname().c_str() ); - g->m.add_item_or_charges( g->u.pos(), newit ); - } else { - newit = g->u.i_add( newit ); - add_msg( m_info, "%c - %s", newit.invlet == 0 ? ' ' : newit.invlet, newit.tname().c_str() ); - } - } -} - -/* selection of component if a recipe requirement has multiple options (e.g. 'duct tap' or 'welder') */ -comp_selection player::select_item_component( const std::vector &components, - int batch, inventory &map_inv, bool can_cancel ) -{ - std::vector player_has; - std::vector map_has; - std::vector mixed; - - comp_selection selected; - - for( const auto &component : components ) { - itype_id type = component.type; - int count = ( component.count > 0 ) ? component.count * batch : abs( component.count ); - bool pl = false, mp = false; - - if( item::count_by_charges( type ) && count > 0 ) { - if( has_charges( type, count ) ) { - player_has.push_back( component ); - pl = true; - } - if( map_inv.has_charges( type, count ) ) { - map_has.push_back( component ); - mp = true; - } - if( !pl && !mp && charges_of( type ) + map_inv.charges_of( type ) >= count ) { - mixed.push_back( component ); - } - } else { // Counting by units, not charges - - if( has_amount( type, count ) ) { - player_has.push_back( component ); - pl = true; - } - if( map_inv.has_components( type, count ) ) { - map_has.push_back( component ); - mp = true; - } - if( !pl && !mp && amount_of( type ) + map_inv.amount_of( type ) >= count ) { - mixed.push_back( component ); - } - - } - } - - /* select 1 component to use */ - if( player_has.size() + map_has.size() + mixed.size() == 1 ) { // Only 1 choice - if( player_has.size() == 1 ) { - selected.use_from = use_from_player; - selected.comp = player_has[0]; - } else if( map_has.size() == 1 ) { - selected.use_from = use_from_map; - selected.comp = map_has[0]; - } else { - selected.use_from = use_from_both; - selected.comp = mixed[0]; - } - } else { // Let the player pick which component they want to use - uimenu cmenu; - // Populate options with the names of the items - for( auto &map_ha : map_has ) { // Index 0-(map_has.size()-1) - std::string tmpStr = string_format( _( "%s (%d/%d nearby)" ), - item::nname( map_ha.type ), - ( map_ha.count * batch ), - item::count_by_charges( map_ha.type ) ? map_inv.charges_of( map_ha.type ) : map_inv.amount_of( - map_ha.type ) ); - cmenu.addentry( tmpStr ); - } - for( auto &player_ha : player_has ) { // Index map_has.size()-(map_has.size()+player_has.size()-1) - std::string tmpStr = string_format( _( "%s (%d/%d on person)" ), - item::nname( player_ha.type ), - ( player_ha.count * batch ), - item::count_by_charges( player_ha.type ) ? charges_of( player_ha.type ) : amount_of( - player_ha.type ) ); - cmenu.addentry( tmpStr ); - } - for( auto &elem : - mixed ) { // Index player_has.size()-(map_has.size()+player_has.size()+mixed.size()-1) - std::string tmpStr = string_format( _( "%s (%d/%d nearby & on person)" ), - item::nname( elem.type ), - ( elem.count * batch ), - item::count_by_charges( elem.type ) ? map_inv.charges_of( elem.type ) + charges_of( elem.type ) : - map_inv.amount_of( elem.type ) + amount_of( elem.type ) ); - cmenu.addentry( tmpStr ); - } - - // Unlike with tools, it's a bad thing if there aren't any components available - if( cmenu.entries.empty() ) { - if( has_trait( trait_id( "DEBUG_HS" ) ) ) { - selected.use_from = use_from_player; - return selected; - } - - debugmsg( "Attempted a recipe with no available components!" ); - selected.use_from = cancel; - return selected; - } - - if( can_cancel ) { - cmenu.addentry( -1, true, 'q', _( "Cancel" ) ); - } - - // Get the selection via a menu popup - cmenu.title = _( "Use which component?" ); - cmenu.query(); - - // The choices only go up to index map_has.size()+player_has.size()+mixed.size()-1. Thus the next index is cancel. - if( cmenu.ret == static_cast( map_has.size() + player_has.size() + mixed.size() ) ) { - selected.use_from = cancel; - return selected; - } - - size_t uselection = static_cast( cmenu.ret ); - if( uselection < map_has.size() ) { - selected.use_from = usage::use_from_map; - selected.comp = map_has[uselection]; - } else if( uselection < map_has.size() + player_has.size() ) { - uselection -= map_has.size(); - selected.use_from = usage::use_from_player; - selected.comp = player_has[uselection]; - } else { - uselection -= map_has.size() + player_has.size(); - selected.use_from = usage::use_from_both; - selected.comp = mixed[uselection]; - } - } - - return selected; -} - -// Prompts player to empty all newly-unsealed containers in inventory -// Called after something that might have opened containers (making them buckets) but not emptied them -void empty_buckets( player &p ) -{ - // First grab (remove) all items that are non-empty buckets and not wielded - auto buckets = p.remove_items_with( [&p]( const item & it ) { - return it.is_bucket_nonempty() && &it != &p.weapon; - }, INT_MAX ); - for( auto &it : buckets ) { - for( const item &in : it.contents ) { - drop_or_handle( in, p ); - } - - it.contents.clear(); - drop_or_handle( it, p ); - } -} - -std::list player::consume_items( const comp_selection &is, int batch ) -{ - std::list ret; - - if( has_trait( trait_DEBUG_HS ) ) { - return ret; - } - - item_comp selected_comp = is.comp; - - const tripoint &loc = pos(); - const bool by_charges = ( item::count_by_charges( selected_comp.type ) && selected_comp.count > 0 ); - // Count given to use_amount/use_charges, changed by those functions! - long real_count = ( selected_comp.count > 0 ) ? selected_comp.count * batch : abs( - selected_comp.count ); - // First try to get everything from the map, than (remaining amount) from player - if( is.use_from & use_from_map ) { - if( by_charges ) { - std::list tmp = g->m.use_charges( loc, PICKUP_RANGE, selected_comp.type, real_count ); - ret.splice( ret.end(), tmp ); - } else { - std::list tmp = g->m.use_amount( loc, PICKUP_RANGE, selected_comp.type, - real_count ); - remove_ammo( tmp, *this ); - ret.splice( ret.end(), tmp ); - } - } - if( is.use_from & use_from_player ) { - if( by_charges ) { - std::list tmp = use_charges( selected_comp.type, real_count ); - ret.splice( ret.end(), tmp ); - } else { - std::list tmp = use_amount( selected_comp.type, real_count ); - remove_ammo( tmp, *this ); - ret.splice( ret.end(), tmp ); - } - } - // condense those items into one - if( by_charges && ret.size() > 1 ) { - std::list::iterator b = ret.begin(); - b++; - while( ret.size() > 1 ) { - ret.front().charges += b->charges; - b = ret.erase( b ); - } - } - lastconsumed = selected_comp.type; - empty_buckets( *this ); - return ret; -} - -/* This call is in-efficient when doing it for multiple items with the same map inventory. -In that case, consider using select_item_component with 1 pre-created map inventory, and then passing the results -to consume_items */ -std::list player::consume_items( const std::vector &components, int batch ) -{ - inventory map_inv; - map_inv.form_from_map( pos(), PICKUP_RANGE ); - return consume_items( select_item_component( components, batch, map_inv ), batch ); -} - -comp_selection -player::select_tool_component( const std::vector &tools, int batch, inventory &map_inv, - const std::string &hotkeys, bool can_cancel ) -{ - - comp_selection selected; - - bool found_nocharge = false; - std::vector player_has; - std::vector map_has; - // Use charges of any tools that require charges used - for( auto it = tools.begin(); it != tools.end() && !found_nocharge; ++it ) { - itype_id type = it->type; - if( it->count > 0 ) { - long count = it->count * batch; - if( has_charges( type, count ) ) { - player_has.push_back( *it ); - } - if( map_inv.has_charges( type, count ) ) { - map_has.push_back( *it ); - } - } else if( has_amount( type, 1 ) || map_inv.has_tools( type, 1 ) ) { - selected.comp = *it; - found_nocharge = true; - } - } - if( found_nocharge ) { - selected.use_from = use_from_none; - return selected; // Default to using a tool that doesn't require charges - } - - if( player_has.size() + map_has.size() == 1 ) { - if( map_has.empty() ) { - selected.use_from = use_from_player; - selected.comp = player_has[0]; - } else { - selected.use_from = use_from_map; - selected.comp = map_has[0]; - } - } else { // Variety of options, list them and pick one - // Populate the list - uimenu tmenu( hotkeys ); - for( auto &map_ha : map_has ) { - if( item::find_type( map_ha.type )->maximum_charges() > 1 ) { - std::string tmpStr = string_format( "%s (%d/%d charges nearby)", - item::nname( map_ha.type ), - ( map_ha.count * batch ), - map_inv.charges_of( map_ha.type ) ); - tmenu.addentry( tmpStr ); - } else { - std::string tmpStr = item::nname( map_ha.type ) + _( " (nearby)" ); - tmenu.addentry( tmpStr ); - } - } - for( auto &player_ha : player_has ) { - if( item::find_type( player_ha.type )->maximum_charges() > 1 ) { - std::string tmpStr = string_format( "%s (%d/%d charges on person)", - item::nname( player_ha.type ), - ( player_ha.count * batch ), - charges_of( player_ha.type ) ); - tmenu.addentry( tmpStr ); - } else { - tmenu.addentry( item::nname( player_ha.type ) ); - } - } - - if( tmenu.entries.empty() ) { // This SHOULD only happen if cooking with a fire, - selected.use_from = use_from_none; - return selected; // and the fire goes out. - } - - if( can_cancel ) { - tmenu.addentry( -1, true, 'q', _( "Cancel" ) ); - } - - // Get selection via a popup menu - tmenu.title = _( "Use which tool?" ); - tmenu.query(); - - if( tmenu.ret == static_cast( map_has.size() + player_has.size() ) ) { - selected.use_from = cancel; - return selected; - } - - size_t uselection = static_cast( tmenu.ret ); - if( uselection < map_has.size() ) { - selected.use_from = use_from_map; - selected.comp = map_has[uselection]; - } else { - uselection -= map_has.size(); - selected.use_from = use_from_player; - selected.comp = player_has[uselection]; - } - } - - return selected; -} - -/* we use this if we selected the tool earlier */ -void player::consume_tools( const comp_selection &tool, int batch ) -{ - if( has_trait( trait_DEBUG_HS ) ) { - return; - } - - if( tool.use_from & use_from_player ) { - use_charges( tool.comp.type, tool.comp.count * batch ); - } - if( tool.use_from & use_from_map ) { - long quantity = tool.comp.count * batch; - g->m.use_charges( pos(), PICKUP_RANGE, tool.comp.type, quantity ); - } - - // else, use_from_none (or cancel), so we don't use up any tools; -} - -/* This call is in-efficient when doing it for multiple items with the same map inventory. -In that case, consider using select_tool_component with 1 pre-created map inventory, and then passing the results -to consume_tools */ -void player::consume_tools( const std::vector &tools, int batch, - const std::string &hotkeys ) -{ - inventory map_inv; - map_inv.form_from_map( pos(), PICKUP_RANGE ); - consume_tools( select_tool_component( tools, batch, map_inv, hotkeys ), batch ); -} - -ret_val player::can_disassemble( const item &obj, const inventory &inv ) const -{ - const auto &r = recipe_dictionary::get_uncraft( obj.typeId() ); - - if( !r ) { - return ret_val::make_failure( _( "You cannot disassemble this." ) ); - } - - // check sufficient light - if( lighting_craft_speed_multiplier( r ) == 0.0f ) { - return ret_val::make_failure( _( "You can't see to craft!" ) ); - } - // refuse to disassemble rotten items - if( obj.goes_bad() || ( obj.is_food_container() && obj.contents.front().goes_bad() ) ) { - if( obj.rotten() || ( obj.is_food_container() && obj.contents.front().rotten() ) ) { - return ret_val::make_failure( _( "It's rotten, I'm not taking that apart." ) ); - } - } - - if( obj.count_by_charges() && !r.has_flag( "UNCRAFT_SINGLE_CHARGE" ) ) { - // Create a new item to get the default charges - int qty = r.create_result().charges; - if( obj.charges < qty ) { - auto msg = ngettext( "You need at least %d charge of %s.", - "You need at least %d charges of %s.", qty ); - return ret_val::make_failure( msg, qty, obj.tname().c_str() ); - } - } - - const auto &dis = r.disassembly_requirements(); - - for( const auto &opts : dis.get_qualities() ) { - for( const auto &qual : opts ) { - if( !qual.has( inv ) ) { - // Here should be no dot at the end of the string as 'to_string()' provides it. - return ret_val::make_failure( _( "You need %s" ), qual.to_string().c_str() ); - } - } - } - - for( const auto &opts : dis.get_tools() ) { - const bool found = std::any_of( opts.begin(), opts.end(), - [&]( const tool_comp & tool ) { - return ( tool.count <= 0 && inv.has_tools( tool.type, 1 ) ) || - ( tool.count > 0 && inv.has_charges( tool.type, tool.count ) ); - } ); - - if( !found ) { - if( opts.front().count <= 0 ) { - return ret_val::make_failure( _( "You need %s." ), - item::nname( opts.front().type ).c_str() ); - } else { - return ret_val::make_failure( ngettext( "You need a %s with %d charge.", - "You need a %s with %d charges.", opts.front().count ), - item::nname( opts.front().type ).c_str(), - opts.front().count ); - } - } - } - - return ret_val::make_success(); -} - -bool player::disassemble() -{ - auto loc = game_menus::inv::disassemble( *this ); - - if( !loc ) { - add_msg( _( "Never mind." ) ); - return false; - } - - return disassemble( loc.obtain( *this ) ); -} - -bool player::disassemble( int dis_pos ) -{ - return disassemble( i_at( dis_pos ), dis_pos, false ); -} - -bool player::disassemble( item &obj, int pos, bool ground, bool interactive ) -{ - const auto ret = can_disassemble( obj, crafting_inventory() ); - - if( !ret.success() ) { - if( interactive ) { - add_msg_if_player( m_info, "%s", ret.c_str() ); - } - return false; - } - - const auto &r = recipe_dictionary::get_uncraft( obj.typeId() ); - // last chance to back out - if( interactive && get_option( "QUERY_DISASSEMBLE" ) ) { - const auto components( r.disassembly_requirements().get_components() ); - std::ostringstream list; - for( const auto &elem : components ) { - list << "- " << elem.front().to_string() << std::endl; - } - - if( !r.learn_by_disassembly.empty() && !knows_recipe( &r ) && can_decomp_learn( r ) ) { - if( !query_yn( - _( "Disassembling the %s may yield:\n%s\nReally disassemble?\nYou feel you may be able to understand this object's construction.\n" ), - obj.tname().c_str(), - list.str().c_str() ) ) { - return false; - } - } else if( !query_yn( _( "Disassembling the %s may yield:\n%s\nReally disassemble?" ), - obj.tname().c_str(), - list.str().c_str() ) ) { - return false; - } - } - - if( activity.id() != activity_id( "ACT_DISASSEMBLE" ) ) { - assign_activity( activity_id( "ACT_DISASSEMBLE" ), r.time ); - } else if( activity.moves_left <= 0 ) { - activity.moves_left = r.time; - } - - activity.values.push_back( pos ); - activity.coords.push_back( ground ? this->pos() : tripoint_min ); - activity.str_values.push_back( r.result() ); - - return true; -} - -void player::disassemble_all( bool one_pass ) -{ - // Reset all the activity values - assign_activity( activity_id( "ACT_DISASSEMBLE" ), 0 ); - auto items = g->m.i_at( pos() ); - bool found_any = false; - if( !one_pass ) { - // Kinda hacky - // That INT_MIN notes we want infinite uncraft - // If INT_MIN is reached in complete_disassemble, - // we will call this function again. - activity.values.push_back( INT_MIN ); - activity.str_values.push_back( "" ); - activity.coords.push_back( tripoint_min ); - } - - for( size_t i = 0; i < items.size(); i++ ) { - if( disassemble( items[i], i, true, false ) ) { - found_any = true; - } - } - - if( !found_any ) { - // Reset the activity - don't loop if there is nothing to do - activity = player_activity(); - } -} - -item &get_item_for_uncraft( player &p, int item_pos, - const tripoint &loc, bool from_ground ) -{ - item *org_item; - if( from_ground ) { - auto items_on_ground = g->m.i_at( loc ); - if( static_cast( item_pos ) >= items_on_ground.size() ) { - return null_item_reference(); - } - org_item = &items_on_ground[item_pos]; - } else { - org_item = &p.i_at( item_pos ); - } - - return *org_item; -} - -void player::complete_disassemble() -{ - // Clean up old settings - // Warning: Breaks old saves with disassembly in progress! - // But so would adding a new recipe... - if( activity.values.empty() || - activity.values.size() != activity.str_values.size() || - activity.values.size() != activity.coords.size() ) { - debugmsg( "bad disassembly activity values" ); - activity.set_to_null(); - return; - } - - // Disassembly activity is now saved in 3 parallel vectors: - // item position, tripoint location (tripoint_min for inventory) and recipe - - // Note: we're reading from the back (in inverse order) - // This is to avoid having to maintain indices - const int item_pos = activity.values.back(); - const tripoint loc = activity.coords.back(); - const auto recipe_name = activity.str_values.back(); - - activity.values.pop_back(); - activity.str_values.pop_back(); - activity.coords.pop_back(); - - if( item_pos == INT_MIN ) { - disassemble_all( false ); - return; - } - - const bool from_ground = loc != tripoint_min; - - complete_disassemble( item_pos, loc, from_ground, recipe_dictionary::get_uncraft( recipe_name ) ); - - if( !activity ) { - // Something above went wrong, don't continue - return; - } - - // Try to get another disassembly target from the activity - if( activity.values.empty() ) { - // No more targets - activity.set_to_null(); - return; - } - - if( activity.values.back() == INT_MIN ) { - disassemble_all( false ); - return; - } - - const auto &next_recipe = recipe_dictionary::get_uncraft( activity.str_values.back() ); - if( !next_recipe ) { - activity.set_to_null(); - return; - } - - activity.moves_left = next_recipe.time; -} - -// TODO: Make them accessible in a less ugly way -void remove_radio_mod( item &, player & ); - -void player::complete_disassemble( int item_pos, const tripoint &loc, - bool from_ground, const recipe &dis ) -{ - // Get the proper recipe - the one for disassembly, not assembly - const auto dis_requirements = dis.disassembly_requirements(); - item &org_item = get_item_for_uncraft( *this, item_pos, loc, from_ground ); - bool filthy = org_item.is_filthy(); - if( org_item.is_null() ) { - add_msg( _( "The item has vanished." ) ); - activity.set_to_null(); - return; - } - - if( org_item.typeId() != dis.result() ) { - add_msg( _( "The item might be gone, at least it is not at the expected position anymore." ) ); - activity.set_to_null(); - return; - } - // Make a copy to keep its data (damage/components) even after it - // has been removed. - item dis_item = org_item; - - float component_success_chance = std::min( std::pow( 0.8, dis_item.damage() ), 1.0 ); - - add_msg( _( "You disassemble the %s into its components." ), dis_item.tname().c_str() ); - // Remove any batteries, ammo and mods first - remove_ammo( dis_item, *this ); - remove_radio_mod( dis_item, *this ); - - if( dis_item.count_by_charges() ) { - // remove the charges that one would get from crafting it - org_item.charges -= dis.create_result().charges; - } - // remove the item, except when it's counted by charges and still has some - if( !org_item.count_by_charges() || org_item.charges <= 0 ) { - if( from_ground ) { - g->m.i_rem( loc, item_pos ); - } else { - i_rem( item_pos ); - } - } - - // Consume tool charges - for( const auto &it : dis_requirements.get_tools() ) { - consume_tools( it ); - } - - // add the components to the map - // Player skills should determine how many components are returned - - int skill_dice = 2 + get_skill_level( dis.skill_used ) * 3; - skill_dice += get_skill_level( dis.skill_used ); - - // Sides on dice is 16 plus your current intelligence - ///\EFFECT_INT increases success rate for disassembling items - int skill_sides = 16 + int_cur; - - int diff_dice = dis.difficulty; - int diff_sides = 24; // 16 + 8 (default intelligence) - - // disassembly only nets a bit of practice - if( dis.skill_used ) { - practice( dis.skill_used, ( dis.difficulty ) * 2, dis.difficulty ); - } - - // If the components aren't empty, we want items exactly identical to them - // Even if the best-fit recipe does not involve those items - std::vector components = dis_item.components; - // If the components are empty, item is the default kind and made of default components - if( components.empty() ) { - for( const auto &altercomps : dis_requirements.get_components() ) { - const item_comp &comp = altercomps.front(); - int compcount = comp.count; - item newit( comp.type, calendar::turn ); - // Counted-by-charge items that can be disassembled individually - // have their component count multiplied by the number of charges. - if( dis_item.count_by_charges() && dis.has_flag( "UNCRAFT_SINGLE_CHARGE" ) ) { - compcount *= std::min( dis_item.charges, dis.create_result().charges ); - } - // Compress liquids and counted-by-charges items into one item, - // they are added together on the map anyway and handle_liquid - // should only be called once to put it all into a container at once. - if( newit.count_by_charges() || newit.made_of( LIQUID ) ) { - newit.charges = compcount; - compcount = 1; - } else if( !newit.craft_has_charges() && newit.charges > 0 ) { - // tools that can be unloaded should be created unloaded, - // tools that can't be unloaded will keep their default charges. - newit.charges = 0; - } - - for( ; compcount > 0; compcount-- ) { - components.emplace_back( newit ); - } - } - } - - for( const item &newit : components ) { - const bool comp_success = ( dice( skill_dice, skill_sides ) > dice( diff_dice, diff_sides ) ); - if( dis.difficulty != 0 && !comp_success ) { - add_msg( m_bad, _( "You fail to recover %s." ), newit.tname().c_str() ); - continue; - } - const bool dmg_success = component_success_chance > rng_float( 0, 1 ); - if( !dmg_success ) { - // Show reason for failure (damaged item, tname contains the damage adjective) - //~ %1s - material, %2$s - disassembled item - add_msg( m_bad, _( "You fail to recover %1$s from the %2$s." ), newit.tname().c_str(), - dis_item.tname().c_str() ); - continue; - } - // Use item from components list, or (if not contained) - // use newit, the default constructed. - item act_item = newit; - - if( filthy ) { - act_item.item_tags.insert( "FILTHY" ); - } - - for( item::t_item_vector::iterator a = dis_item.components.begin(); a != dis_item.components.end(); - ++a ) { - if( a->type == newit.type ) { - act_item = *a; - dis_item.components.erase( a ); - break; - } - } - - const optional_vpart_position vp = g->m.veh_at( pos() ); - const int veh_part = vp ? vp->vehicle().part_with_feature( vp->part_index(), "CARGO" ) : -1; - - if( act_item.made_of( LIQUID ) ) { - g->handle_all_liquid( act_item, PICKUP_RANGE ); - } else if( veh_part != -1 && vp->vehicle().add_item( veh_part, act_item ) ) { - // add_item did put the items in the vehicle, nothing further to be done - } else { - // TODO: For items counted by charges, add as much as we can to the vehicle, and - // the rest on the ground (see dropping code and @vehicle::add_charges) - g->m.add_item_or_charges( pos(), act_item ); - } - } - - if( !dis.learn_by_disassembly.empty() && !knows_recipe( &dis ) ) { - if( can_decomp_learn( dis ) ) { - // @todo: make this depend on intelligence - if( one_in( 4 ) ) { - learn_recipe( &dis.ident().obj() );//@todo: change to forward an id or a reference - add_msg( m_good, _( "You learned a recipe for %s from disassembling it!" ), - dis_item.tname().c_str() ); - } else { - add_msg( m_info, _( "You might be able to learn a recipe for %s if you disassemble another." ), - dis_item.tname().c_str() ); - } - } else { - add_msg( m_info, _( "If you had better skills, you might learn a recipe next time." ) ); - } - } -} - -void remove_ammo( std::list &dis_items, player &p ) -{ - for( auto &dis_item : dis_items ) { - remove_ammo( dis_item, p ); - } -} - -void drop_or_handle( const item &newit, player &p ) -{ - if( newit.made_of( LIQUID ) && &p == &g->u ) { // TODO: what about NPCs? - g->handle_all_liquid( newit, PICKUP_RANGE ); - } else { - item tmp( newit ); - p.i_add_or_drop( tmp ); - } -} - -void remove_ammo( item &dis_item, player &p ) -{ - for( auto iter = dis_item.contents.begin(); iter != dis_item.contents.end(); ) { - if( iter->is_irremovable() ) { - iter++; - continue; - } - drop_or_handle( *iter, p ); - iter = dis_item.contents.erase( iter ); - } - - if( dis_item.has_flag( "NO_UNLOAD" ) ) { - return; - } - if( dis_item.is_gun() && dis_item.ammo_current() != "null" ) { - item ammodrop( dis_item.ammo_current(), calendar::turn ); - ammodrop.charges = dis_item.charges; - drop_or_handle( ammodrop, p ); - dis_item.charges = 0; - } - if( dis_item.is_tool() && dis_item.charges > 0 && dis_item.ammo_type() ) { - item ammodrop( dis_item.ammo_type()->default_ammotype(), calendar::turn ); - ammodrop.charges = dis_item.charges; - if( dis_item.ammo_type() == ammotype( "plutonium" ) ) { - ammodrop.charges /= PLUTONIUM_CHARGES; - } - drop_or_handle( ammodrop, p ); - dis_item.charges = 0; - } -} - -std::vector player::get_crafting_helpers() const -{ - return g->get_npcs_if( [this]( const npc & guy ) { - return rl_dist( guy.pos(), pos() ) < PICKUP_RANGE && guy.is_friend() && - !guy.in_sleep_state() && g->m.clear_path( pos(), guy.pos(), PICKUP_RANGE, 1, 100 ); - } ); -} \ No newline at end of file +#include "crafting.h" + +#include "catacharset.h" +#include "craft_command.h" +#include "debug.h" +#include "game.h" +#include "game_inventory.h" +#include "input.h" +#include "bionics.h" +#include "inventory.h" +#include "itype.h" +#include "ammo.h" +#include "map.h" +#include "messages.h" +#include "item.h" +#include "npc.h" +#include "calendar.h" +#include "options.h" +#include "output.h" +#include "recipe_dictionary.h" +#include "requirements.h" +#include "rng.h" +#include "translations.h" +#include "ui.h" +#include "vpart_position.h" +#include "vehicle.h" +#include "crafting_gui.h" + +#include //std::min +#include +#include //sqrt +#include +#include +#include +#include + +const efftype_id effect_contacts( "contacts" ); + +void remove_from_component_lookup( recipe *r ); +void drop_or_handle( const item &newit, player &p ); + +static const trait_id trait_DEBUG_HS( "DEBUG_HS" ); +static const trait_id trait_PAWS_LARGE( "PAWS_LARGE" ); +static const trait_id trait_PAWS( "PAWS" ); + +static bool crafting_allowed( const player &p, const recipe &rec ) +{ + if( p.morale_crafting_speed_multiplier( rec ) <= 0.0f ) { + add_msg( m_info, _( "Your morale is too low to craft..." ) ); + return false; + } + + if( p.lighting_craft_speed_multiplier( rec ) <= 0.0f ) { + add_msg( m_info, _( "You can't see to craft!" ) ); + return false; + } + + return true; +} + +float player::lighting_craft_speed_multiplier( const recipe &rec ) const +{ + // negative is bright, 0 is just bright enough, positive is dark, +7.0f is pitch black + float darkness = fine_detail_vision_mod() - 4.0f; + if( darkness <= 0.0f ) { + return 1.0f; // it's bright, go for it + } + bool rec_blind = rec.has_flag( "BLIND_HARD" ) || rec.has_flag( "BLIND_EASY" ); + if( darkness > 0 && !rec_blind ) { + return 0.0f; // it's dark and this recipe can't be crafted in the dark + } + if( rec.has_flag( "BLIND_EASY" ) ) { + // 100% speed in well lit area at skill+0 + // 25% speed in pitch black at skill+0 + // skill+2 removes speed penalty + return 1.0f - ( darkness / ( 7.0f / 0.75f ) ) * std::max( 0, + 2 - exceeds_recipe_requirements( rec ) ) / 2.0f; + } + if( rec.has_flag( "BLIND_HARD" ) && exceeds_recipe_requirements( rec ) >= 2 ) { + // 100% speed in well lit area at skill+2 + // 25% speed in pitch black at skill+2 + // skill+8 removes speed penalty + return 1.0f - ( darkness / ( 7.0f / 0.75f ) ) * std::max( 0, + 8 - exceeds_recipe_requirements( rec ) ) / 6.0f; + } + return 0.0f; // it's dark and you could craft this if you had more skill +} + +float player::morale_crafting_speed_multiplier( const recipe &rec ) const +{ + int morale = get_morale_level(); + if( morale >= 0 ) { + // No bonus for being happy yet + return 1.0f; + } + + // Harder jobs are more frustrating, even when skilled + // For each skill where skill=difficulty, multiply effective morale by 200% + float morale_mult = std::max( 1.0f, 2.0f * rec.difficulty / std::max( 1, + get_skill_level( rec.skill_used ) ) ); + for( const auto &pr : rec.required_skills ) { + morale_mult *= std::max( 1.0f, 2.0f * pr.second / std::max( 1, get_skill_level( pr.first ) ) ); + } + + // Halve speed at -50 effective morale, quarter at -150 + float morale_effect = 1.0f + ( morale_mult * morale ) / -50.0f; + + return 1.0f / morale_effect; +} + +float player::crafting_speed_multiplier( const recipe &rec, bool in_progress ) const +{ + float result = morale_crafting_speed_multiplier( rec ) * lighting_craft_speed_multiplier( rec ); + // Can't start if we'd need 300% time, but we can still finish the job + if( !in_progress && result < 0.33f ) { + return 0.0f; + } + // If we're working below 20% speed, just give up + if( result < 0.2f ) { + return 0.0f; + } + + return result; +} + +bool player::has_morale_to_craft() const +{ + return get_morale_level() >= -50; +} + +void player::craft() +{ + int batch_size = 0; + const recipe *rec = select_crafting_recipe( batch_size ); + if( rec ) { + if( crafting_allowed( *this, *rec ) ) { + make_craft( rec->ident(), batch_size ); + } + } +} + +void player::recraft() +{ + if( lastrecipe.str().empty() ) { + popup( _( "Craft something first" ) ); + } else if( making_would_work( lastrecipe, last_batch ) ) { + last_craft->execute(); + } +} + +void player::long_craft() +{ + int batch_size = 0; + const recipe *rec = select_crafting_recipe( batch_size ); + if( rec ) { + if( crafting_allowed( *this, *rec ) ) { + make_all_craft( rec->ident(), batch_size ); + } + } +} + +bool player::making_would_work( const recipe_id &id_to_make, int batch_size ) +{ + const auto &making = *id_to_make; + if( !( making && crafting_allowed( *this, making ) ) ) { + return false; + } + + if( !can_make( &making, batch_size ) ) { + std::ostringstream buffer; + buffer << _( "You can no longer make that craft!" ) << "\n"; + buffer << making.requirements().list_missing(); + popup( buffer.str(), PF_NONE ); + return false; + } + + return check_eligible_containers_for_crafting( making, batch_size ); +} + +size_t available_assistant_count( const player &u, const recipe &rec ) +{ + // NPCs around you should assist in batch production if they have the skills + // @todo Cache them in activity, include them in modifier calculations + const auto helpers = u.get_crafting_helpers(); + return std::count_if( helpers.begin(), helpers.end(), + [&]( const npc * np ) { + return np->get_skill_level( rec.skill_used ) >= rec.difficulty; + } ); +} + +int player::base_time_to_craft( const recipe &rec, int batch_size ) const +{ + const size_t assistants = available_assistant_count( *this, rec ); + return rec.batch_time( batch_size, 1.0f, assistants ); +} + +int player::expected_time_to_craft( const recipe &rec, int batch_size ) const +{ + const size_t assistants = available_assistant_count( *this, rec ); + float modifier = crafting_speed_multiplier( rec ); + return rec.batch_time( batch_size, modifier, assistants ); +} + +bool player::check_eligible_containers_for_crafting( const recipe &rec, int batch_size ) const +{ + std::vector conts = get_eligible_containers_for_crafting(); + const std::vector res = rec.create_results( batch_size ); + const std::vector bps = rec.create_byproducts( batch_size ); + std::vector all; + all.reserve( res.size() + bps.size() ); + all.insert( all.end(), res.begin(), res.end() ); + all.insert( all.end(), bps.begin(), bps.end() ); + + for( const item &prod : all ) { + if( !prod.made_of( LIQUID ) ) { + continue; + } + + // we go trough half-filled containers first, then go through empty containers if we need + std::sort( conts.begin(), conts.end(), item_ptr_compare_by_charges ); + + long charges_to_store = prod.charges; + for( const item *elem : conts ) { + if( charges_to_store <= 0 ) { + break; + } + + if( !elem->is_container_empty() ) { + if( elem->contents.front().typeId() == prod.typeId() ) { + charges_to_store -= elem->get_remaining_capacity_for_liquid( elem->contents.front(), true ); + } + } else { + charges_to_store -= elem->get_remaining_capacity_for_liquid( prod, true ); + } + } + + // also check if we're currently in a vehicle that has the necessary storage + if( charges_to_store > 0 ) { + if( optional_vpart_position vp = g->m.veh_at( pos() ) ) { + const itype_id &ftype = prod.typeId(); + int fuel_cap = vp->vehicle().fuel_capacity( ftype ); + int fuel_amnt = vp->vehicle().fuel_left( ftype ); + + if( fuel_cap >= 0 ) { + int fuel_space_left = fuel_cap - fuel_amnt; + charges_to_store -= fuel_space_left; + } + } + } + + if( charges_to_store > 0 ) { + popup( _( "You don't have anything to store %s in!" ), prod.tname().c_str() ); + return false; + } + } + + return true; +} + +bool is_container_eligible_for_crafting( const item &cont, bool allow_bucket ) +{ + if( cont.is_watertight_container() || ( allow_bucket && cont.is_bucket() ) ) { + return !cont.is_container_full( allow_bucket ); + } + + return false; +} + +std::vector player::get_eligible_containers_for_crafting() const +{ + std::vector conts; + + if( is_container_eligible_for_crafting( weapon, true ) ) { + conts.push_back( &weapon ); + } + for( const auto &it : worn ) { + if( is_container_eligible_for_crafting( it, false ) ) { + conts.push_back( &it ); + } + } + for( size_t i = 0; i < inv.size(); i++ ) { + for( const auto &it : inv.const_stack( i ) ) { + if( is_container_eligible_for_crafting( it, false ) ) { + conts.push_back( &it ); + } + } + } + + // get all potential containers within PICKUP_RANGE tiles including vehicles + for( const auto &loc : closest_tripoints_first( PICKUP_RANGE, pos() ) ) { + if( g->m.accessible_items( pos(), loc, PICKUP_RANGE ) ) { + for( const auto &it : g->m.i_at( loc ) ) { + if( is_container_eligible_for_crafting( it, true ) ) { + conts.emplace_back( &it ); + } + } + } + + if( optional_vpart_position vp = g->m.veh_at( loc ) ) { + const int part = vp->vehicle().part_with_feature( vp->part_index(), "CARGO" ); + if( part != -1 ) { + for( const auto &it : vp->vehicle().get_items( part ) ) { + if( is_container_eligible_for_crafting( it, false ) ) { + conts.emplace_back( &it ); + } + } + } + } + } + + return conts; +} + +bool player::can_make( const recipe *r, int batch_size ) +{ + const inventory &crafting_inv = crafting_inventory(); + + if( has_recipe( r, crafting_inv, get_crafting_helpers() ) < 0 ) { + return false; + } + + return r->requirements().can_make_with_inventory( crafting_inv, batch_size ); +} + +const inventory &player::crafting_inventory() +{ + if( cached_moves == moves + && cached_time == calendar::turn + && cached_position == pos() ) { + return cached_crafting_inventory; + } + cached_crafting_inventory.form_from_map( pos(), PICKUP_RANGE, false ); + cached_crafting_inventory += inv; + cached_crafting_inventory += weapon; + cached_crafting_inventory += worn; + for( const auto &bio : *my_bionics ) { + const auto &bio_data = bio.info(); + if( ( !bio_data.activated || bio.powered ) && + !bio_data.fake_item.empty() ) { + cached_crafting_inventory += item( bio.info().fake_item, + calendar::turn, power_level ); + } + } + + cached_moves = moves; + cached_time = calendar::turn; + cached_position = pos(); + return cached_crafting_inventory; +} + +void player::invalidate_crafting_inventory() +{ + cached_time = calendar::before_time_starts; +} + +void player::make_craft( const recipe_id &id_to_make, int batch_size ) +{ + make_craft_with_command( id_to_make, batch_size ); +} + +void player::make_all_craft( const recipe_id &id_to_make, int batch_size ) +{ + make_craft_with_command( id_to_make, batch_size, true ); +} + +void player::make_craft_with_command( const recipe_id &id_to_make, int batch_size, bool is_long ) +{ + const auto &recipe_to_make = *id_to_make; + + if( !recipe_to_make ) { + return; + } + + *last_craft = craft_command( &recipe_to_make, batch_size, is_long, this ); + last_craft->execute(); +} + +// @param offset is the index of the created item in the range [0, batch_size-1], +// it makes sure that the used items are distributed equally among the new items. +void set_components( std::vector &components, const std::list &used, + const int batch_size, const size_t offset ) +{ + if( batch_size <= 1 ) { + components.insert( components.begin(), used.begin(), used.end() ); + return; + } + // This count does *not* include items counted by charges! + size_t non_charges_counter = 0; + for( auto &tmp : used ) { + if( tmp.count_by_charges() ) { + components.push_back( tmp ); + // This assumes all (count-by-charges) items of the same type have been merged into one, + // which has a charges value that can be evenly divided by batch_size. + components.back().charges = tmp.charges / batch_size; + } else { + if( ( non_charges_counter + offset ) % batch_size == 0 ) { + components.push_back( tmp ); + } + non_charges_counter++; + } + } +} + +std::list player::consume_components_for_craft( const recipe *making, int batch_size ) +{ + std::list used; + if( has_trait( trait_id( "DEBUG_HS" ) ) ) { + return used; + } + if( last_craft->has_cached_selections() ) { + used = last_craft->consume_components(); + } else { + // This should fail and return, but currently crafting_command isn't saved + // Meaning there are still cases where has_cached_selections will be false + // @todo: Allow saving last_craft and debugmsg+fail craft if selection isn't cached + const auto &req = making->requirements(); + for( const auto &it : req.get_components() ) { + std::list tmp = consume_items( it, batch_size ); + used.splice( used.end(), tmp ); + } + for( const auto &it : req.get_tools() ) { + consume_tools( it, batch_size ); + } + } + return used; +} + + +void player::complete_craft() +{ + //@todo: change making to be a reference, it can never be null anyway + const recipe *making = &recipe_id( activity.name ).obj(); // Which recipe is it? + int batch_size = activity.values.front(); + if( making == nullptr ) { + debugmsg( "no recipe with id %s found", activity.name.c_str() ); + activity.set_to_null(); + return; + } + + int secondary_dice = 0; + int secondary_difficulty = 0; + for( const auto &pr : making->required_skills ) { + secondary_dice += get_skill_level( pr.first ); + secondary_difficulty += pr.second; + } + + // # of dice is 75% primary skill, 25% secondary (unless secondary is null) + int skill_dice; + if( secondary_difficulty > 0 ) { + skill_dice = get_skill_level( making->skill_used ) * 3 + secondary_dice; + } else { + skill_dice = get_skill_level( making->skill_used ) * 4; + } + + auto helpers = g->u.get_crafting_helpers(); + for( const npc *np : helpers ) { + if( np->get_skill_level( making->skill_used ) >= + get_skill_level( making->skill_used ) ) { + // NPC assistance is worth half a skill level + skill_dice += 2; + add_msg( m_info, _( "%s helps with crafting..." ), np->name.c_str() ); + break; + } + } + + // farsightedness can impose a penalty on electronics and tailoring success + // it's equivalent to a 2-rank electronics penalty, 1-rank tailoring + if( has_trait( trait_id( "HYPEROPIC" ) ) && !is_wearing( "glasses_reading" ) && + !is_wearing( "glasses_bifocal" ) && !has_effect( effect_contacts ) ) { + int main_rank_penalty = 0; + if( making->skill_used == skill_id( "electronics" ) ) { + main_rank_penalty = 2; + } else if( making->skill_used == skill_id( "tailor" ) ) { + main_rank_penalty = 1; + } + skill_dice -= main_rank_penalty * 4; + } + + // It's tough to craft with paws. Fortunately it's just a matter of grip and fine-motor, + // not inability to see what you're doing + if( has_trait( trait_PAWS ) || has_trait( trait_PAWS_LARGE ) ) { + int paws_rank_penalty = 0; + if( has_trait( trait_PAWS_LARGE ) ) { + paws_rank_penalty += 1; + } + if( making->skill_used == skill_id( "electronics" ) + || making->skill_used == skill_id( "tailor" ) + || making->skill_used == skill_id( "mechanics" ) ) { + paws_rank_penalty += 1; + } + skill_dice -= paws_rank_penalty * 4; + } + + // Sides on dice is 16 plus your current intelligence + ///\EFFECT_INT increases crafting success chance + int skill_sides = 16 + int_cur; + + int diff_dice; + if( secondary_difficulty > 0 ) { + diff_dice = making->difficulty * 3 + secondary_difficulty; + } else { + // Since skill level is * 4 also + diff_dice = making->difficulty * 4; + } + + int diff_sides = 24; // 16 + 8 (default intelligence) + + int skill_roll = dice( skill_dice, skill_sides ); + int diff_roll = dice( diff_dice, diff_sides ); + + if( making->skill_used ) { + // normalize experience gain to crafting time, giving a bonus for longer crafting + const double batch_mult = batch_size + base_time_to_craft( *making, batch_size ) / 30000.0; + practice( making->skill_used, ( int )( ( making->difficulty * 15 + 10 ) * batch_mult ), + ( int )making->difficulty * 1.25 ); + + //NPCs assisting or watching should gain experience... + for( auto &elem : helpers ) { + //If the NPC can understand what you are doing, they gain more exp + if( elem->get_skill_level( making->skill_used ) >= making->difficulty ) { + elem->practice( making->skill_used, + ( int )( ( making->difficulty * 15 + 10 ) * batch_mult * + .50 ), ( int )making->difficulty * 1.25 ); + if( batch_size > 1 ) { + add_msg( m_info, _( "%s assists with crafting..." ), elem->name.c_str() ); + } + if( batch_size == 1 ) { + add_msg( m_info, _( "%s could assist you with a batch..." ), elem->name.c_str() ); + } + //NPCs around you understand the skill used better + } else { + elem->practice( making->skill_used, + ( int )( ( making->difficulty * 15 + 10 ) * batch_mult * .15 ), + ( int )making->difficulty * 1.25 ); + add_msg( m_info, _( "%s watches you craft..." ), elem->name.c_str() ); + } + } + + } + + // Messed up badly; waste some components. + if( making->difficulty != 0 && diff_roll > skill_roll * ( 1 + 0.1 * rng( 1, 5 ) ) ) { + add_msg( m_bad, _( "You fail to make the %s, and waste some materials." ), making->result_name() ); + consume_components_for_craft( making, batch_size ); + activity.set_to_null(); + return; + // Messed up slightly; no components wasted. + } else if( diff_roll > skill_roll ) { + add_msg( m_neutral, _( "You fail to make the %s, but don't waste any materials." ), + making->result_name() ); + //this method would only have been called from a place that nulls activity.type, + //so it appears that it's safe to NOT null that variable here. + //rationale: this allows certain contexts (e.g. ACT_LONGCRAFT) to distinguish major and minor failures + return; + } + + // If we're here, the craft was a success! + // Use up the components and tools + std::list used = consume_components_for_craft( making, batch_size ); + if( last_craft->has_cached_selections() && used.empty() ) { + // This signals failure, even though there seem to be several paths where it shouldn't... + return; + } + if( !used.empty() ) { + reset_encumbrance(); // in case we were wearing something just consumed up. + } + + // Set up the new item, and assign an inventory letter if available + std::vector newits = making->create_results( batch_size ); + bool first = true; + float used_age_tally = 0; + int used_age_count = 0; + size_t newit_counter = 0; + for( item &newit : newits ) { + // messages, learning of recipe, food spoilage calculation only once + if( first ) { + first = false; + if( knows_recipe( making ) ) { + add_msg( _( "You craft %s from memory." ), newit.type_name( 1 ).c_str() ); + } else { + add_msg( _( "You craft %s using a book as a reference." ), newit.type_name( 1 ).c_str() ); + // If we made it, but we don't know it, + // we're making it from a book and have a chance to learn it. + // Base expected time to learn is 1000*(difficulty^4)/skill/int moves. + // This means time to learn is greatly decreased with higher skill level, + // but also keeps going up as difficulty goes up. + // Worst case is lvl 10, which will typically take + // 10^4/10 (1,000) minutes, or about 16 hours of crafting it to learn. + int difficulty = has_recipe( making, crafting_inventory(), helpers ); + ///\EFFECT_INT increases chance to learn recipe when crafting from a book + if( x_in_y( making->time, ( 1000 * 8 * + ( difficulty * difficulty * difficulty * difficulty ) ) / + ( std::max( get_skill_level( making->skill_used ), 1 ) * std::max( get_int(), 1 ) ) ) ) { + learn_recipe( ( recipe * )making ); + add_msg( m_good, _( "You memorized the recipe for %s!" ), + newit.type_name( 1 ).c_str() ); + } + } + + for( auto &elem : used ) { + if( elem.goes_bad() ) { + used_age_tally += elem.get_relative_rot(); + ++used_age_count; + } + } + } + + // Don't store components for things made by charges, + // don't store components for things that can't be uncrafted. + if( recipe_dictionary::get_uncraft( making->result() ) && !newit.count_by_charges() ) { + // Setting this for items counted by charges gives only problems: + // those items are automatically merged everywhere (map/vehicle/inventory), + // which would either loose this information or merge it somehow. + set_components( newit.components, used, batch_size, newit_counter ); + newit_counter++; + } + finalize_crafted_item( newit, used_age_tally, used_age_count ); + set_item_inventory( newit ); + } + + if( making->has_byproducts() ) { + std::vector bps = making->create_byproducts( batch_size ); + for( auto &bp : bps ) { + finalize_crafted_item( bp, used_age_tally, used_age_count ); + set_item_inventory( bp ); + } + } + + inv.restack( *this ); +} + +void set_item_spoilage( item &newit, float used_age_tally, int used_age_count ) +{ + newit.set_relative_rot( used_age_tally / used_age_count ); +} + +void set_item_food( item &newit ) +{ + //@todo: encapsulate this into some function + int bday_tmp = to_turn( newit.birthday() ) % 3600; // fuzzy birthday for stacking reasons + newit.set_birthday( newit.birthday() + 3600_turns - time_duration::from_turns( bday_tmp ) ); + if( newit.has_flag( "EATEN_HOT" ) ) { // hot foods generated + newit.item_tags.insert( "HOT" ); + newit.item_counter = 600; + newit.active = true; + } +} + +void finalize_crafted_item( item &newit, float used_age_tally, int used_age_count ) +{ + if( newit.is_food() ) { + set_item_food( newit ); + } + if( used_age_count > 0 && newit.goes_bad() ) { + set_item_spoilage( newit, used_age_tally, used_age_count ); + } +} + +void set_item_inventory( item &newit ) +{ + if( newit.made_of( LIQUID ) ) { + g->handle_all_liquid( newit, PICKUP_RANGE ); + } else { + g->u.inv.assign_empty_invlet( newit, g->u ); + // We might not have space for the item + if( !g->u.can_pickVolume( newit ) ) { //Accounts for result_mult + add_msg( _( "There's no room in your inventory for the %s, so you drop it." ), + newit.tname().c_str() ); + g->m.add_item_or_charges( g->u.pos(), newit ); + } else if( !g->u.can_pickWeight( newit, !get_option( "DANGEROUS_PICKUPS" ) ) ) { + add_msg( _( "The %s is too heavy to carry, so you drop it." ), + newit.tname().c_str() ); + g->m.add_item_or_charges( g->u.pos(), newit ); + } else { + newit = g->u.i_add( newit ); + add_msg( m_info, "%c - %s", newit.invlet == 0 ? ' ' : newit.invlet, newit.tname().c_str() ); + } + } +} + +/* selection of component if a recipe requirement has multiple options (e.g. 'duct tap' or 'welder') */ +comp_selection player::select_item_component( const std::vector &components, + int batch, inventory &map_inv, bool can_cancel ) +{ + std::vector player_has; + std::vector map_has; + std::vector mixed; + + comp_selection selected; + + for( const auto &component : components ) { + itype_id type = component.type; + int count = ( component.count > 0 ) ? component.count * batch : abs( component.count ); + bool pl = false, mp = false; + + if( item::count_by_charges( type ) && count > 0 ) { + if( has_charges( type, count ) ) { + player_has.push_back( component ); + pl = true; + } + if( map_inv.has_charges( type, count ) ) { + map_has.push_back( component ); + mp = true; + } + if( !pl && !mp && charges_of( type ) + map_inv.charges_of( type ) >= count ) { + mixed.push_back( component ); + } + } else { // Counting by units, not charges + + if( has_amount( type, count ) ) { + player_has.push_back( component ); + pl = true; + } + if( map_inv.has_components( type, count ) ) { + map_has.push_back( component ); + mp = true; + } + if( !pl && !mp && amount_of( type ) + map_inv.amount_of( type ) >= count ) { + mixed.push_back( component ); + } + + } + } + + /* select 1 component to use */ + if( player_has.size() + map_has.size() + mixed.size() == 1 ) { // Only 1 choice + if( player_has.size() == 1 ) { + selected.use_from = use_from_player; + selected.comp = player_has[0]; + } else if( map_has.size() == 1 ) { + selected.use_from = use_from_map; + selected.comp = map_has[0]; + } else { + selected.use_from = use_from_both; + selected.comp = mixed[0]; + } + } else { // Let the player pick which component they want to use + uimenu cmenu; + // Populate options with the names of the items + for( auto &map_ha : map_has ) { // Index 0-(map_has.size()-1) + std::string tmpStr = string_format( _( "%s (%d/%d nearby)" ), + item::nname( map_ha.type ), + ( map_ha.count * batch ), + item::count_by_charges( map_ha.type ) ? map_inv.charges_of( map_ha.type ) : map_inv.amount_of( + map_ha.type ) ); + cmenu.addentry( tmpStr ); + } + for( auto &player_ha : player_has ) { // Index map_has.size()-(map_has.size()+player_has.size()-1) + std::string tmpStr = string_format( _( "%s (%d/%d on person)" ), + item::nname( player_ha.type ), + ( player_ha.count * batch ), + item::count_by_charges( player_ha.type ) ? charges_of( player_ha.type ) : amount_of( + player_ha.type ) ); + cmenu.addentry( tmpStr ); + } + for( auto &elem : + mixed ) { // Index player_has.size()-(map_has.size()+player_has.size()+mixed.size()-1) + std::string tmpStr = string_format( _( "%s (%d/%d nearby & on person)" ), + item::nname( elem.type ), + ( elem.count * batch ), + item::count_by_charges( elem.type ) ? map_inv.charges_of( elem.type ) + charges_of( elem.type ) : + map_inv.amount_of( elem.type ) + amount_of( elem.type ) ); + cmenu.addentry( tmpStr ); + } + + // Unlike with tools, it's a bad thing if there aren't any components available + if( cmenu.entries.empty() ) { + if( has_trait( trait_id( "DEBUG_HS" ) ) ) { + selected.use_from = use_from_player; + return selected; + } + + debugmsg( "Attempted a recipe with no available components!" ); + selected.use_from = cancel; + return selected; + } + + if( can_cancel ) { + cmenu.addentry( -1, true, 'q', _( "Cancel" ) ); + } + + // Get the selection via a menu popup + cmenu.title = _( "Use which component?" ); + cmenu.query(); + + // The choices only go up to index map_has.size()+player_has.size()+mixed.size()-1. Thus the next index is cancel. + if( cmenu.ret == static_cast( map_has.size() + player_has.size() + mixed.size() ) ) { + selected.use_from = cancel; + return selected; + } + + size_t uselection = static_cast( cmenu.ret ); + if( uselection < map_has.size() ) { + selected.use_from = usage::use_from_map; + selected.comp = map_has[uselection]; + } else if( uselection < map_has.size() + player_has.size() ) { + uselection -= map_has.size(); + selected.use_from = usage::use_from_player; + selected.comp = player_has[uselection]; + } else { + uselection -= map_has.size() + player_has.size(); + selected.use_from = usage::use_from_both; + selected.comp = mixed[uselection]; + } + } + + return selected; +} + +// Prompts player to empty all newly-unsealed containers in inventory +// Called after something that might have opened containers (making them buckets) but not emptied them +void empty_buckets( player &p ) +{ + // First grab (remove) all items that are non-empty buckets and not wielded + auto buckets = p.remove_items_with( [&p]( const item & it ) { + return it.is_bucket_nonempty() && &it != &p.weapon; + }, INT_MAX ); + for( auto &it : buckets ) { + for( const item &in : it.contents ) { + drop_or_handle( in, p ); + } + + it.contents.clear(); + drop_or_handle( it, p ); + } +} + +std::list player::consume_items( const comp_selection &is, int batch ) +{ + std::list ret; + + if( has_trait( trait_DEBUG_HS ) ) { + return ret; + } + + item_comp selected_comp = is.comp; + + const tripoint &loc = pos(); + const bool by_charges = ( item::count_by_charges( selected_comp.type ) && selected_comp.count > 0 ); + // Count given to use_amount/use_charges, changed by those functions! + long real_count = ( selected_comp.count > 0 ) ? selected_comp.count * batch : abs( + selected_comp.count ); + // First try to get everything from the map, than (remaining amount) from player + if( is.use_from & use_from_map ) { + if( by_charges ) { + std::list tmp = g->m.use_charges( loc, PICKUP_RANGE, selected_comp.type, real_count ); + ret.splice( ret.end(), tmp ); + } else { + std::list tmp = g->m.use_amount( loc, PICKUP_RANGE, selected_comp.type, + real_count ); + remove_ammo( tmp, *this ); + ret.splice( ret.end(), tmp ); + } + } + if( is.use_from & use_from_player ) { + if( by_charges ) { + std::list tmp = use_charges( selected_comp.type, real_count ); + ret.splice( ret.end(), tmp ); + } else { + std::list tmp = use_amount( selected_comp.type, real_count ); + remove_ammo( tmp, *this ); + ret.splice( ret.end(), tmp ); + } + } + // condense those items into one + if( by_charges && ret.size() > 1 ) { + std::list::iterator b = ret.begin(); + b++; + while( ret.size() > 1 ) { + ret.front().charges += b->charges; + b = ret.erase( b ); + } + } + lastconsumed = selected_comp.type; + empty_buckets( *this ); + return ret; +} + +/* This call is in-efficient when doing it for multiple items with the same map inventory. +In that case, consider using select_item_component with 1 pre-created map inventory, and then passing the results +to consume_items */ +std::list player::consume_items( const std::vector &components, int batch ) +{ + inventory map_inv; + map_inv.form_from_map( pos(), PICKUP_RANGE ); + return consume_items( select_item_component( components, batch, map_inv ), batch ); +} + +comp_selection +player::select_tool_component( const std::vector &tools, int batch, inventory &map_inv, + const std::string &hotkeys, bool can_cancel ) +{ + + comp_selection selected; + + bool found_nocharge = false; + std::vector player_has; + std::vector map_has; + // Use charges of any tools that require charges used + for( auto it = tools.begin(); it != tools.end() && !found_nocharge; ++it ) { + itype_id type = it->type; + if( it->count > 0 ) { + long count = it->count * batch; + if( has_charges( type, count ) ) { + player_has.push_back( *it ); + } + if( map_inv.has_charges( type, count ) ) { + map_has.push_back( *it ); + } + } else if( has_amount( type, 1 ) || map_inv.has_tools( type, 1 ) ) { + selected.comp = *it; + found_nocharge = true; + } + } + if( found_nocharge ) { + selected.use_from = use_from_none; + return selected; // Default to using a tool that doesn't require charges + } + + if( player_has.size() + map_has.size() == 1 ) { + if( map_has.empty() ) { + selected.use_from = use_from_player; + selected.comp = player_has[0]; + } else { + selected.use_from = use_from_map; + selected.comp = map_has[0]; + } + } else { // Variety of options, list them and pick one + // Populate the list + uimenu tmenu( hotkeys ); + for( auto &map_ha : map_has ) { + if( item::find_type( map_ha.type )->maximum_charges() > 1 ) { + std::string tmpStr = string_format( "%s (%d/%d charges nearby)", + item::nname( map_ha.type ), + ( map_ha.count * batch ), + map_inv.charges_of( map_ha.type ) ); + tmenu.addentry( tmpStr ); + } else { + std::string tmpStr = item::nname( map_ha.type ) + _( " (nearby)" ); + tmenu.addentry( tmpStr ); + } + } + for( auto &player_ha : player_has ) { + if( item::find_type( player_ha.type )->maximum_charges() > 1 ) { + std::string tmpStr = string_format( "%s (%d/%d charges on person)", + item::nname( player_ha.type ), + ( player_ha.count * batch ), + charges_of( player_ha.type ) ); + tmenu.addentry( tmpStr ); + } else { + tmenu.addentry( item::nname( player_ha.type ) ); + } + } + + if( tmenu.entries.empty() ) { // This SHOULD only happen if cooking with a fire, + selected.use_from = use_from_none; + return selected; // and the fire goes out. + } + + if( can_cancel ) { + tmenu.addentry( -1, true, 'q', _( "Cancel" ) ); + } + + // Get selection via a popup menu + tmenu.title = _( "Use which tool?" ); + tmenu.query(); + + if( tmenu.ret == static_cast( map_has.size() + player_has.size() ) ) { + selected.use_from = cancel; + return selected; + } + + size_t uselection = static_cast( tmenu.ret ); + if( uselection < map_has.size() ) { + selected.use_from = use_from_map; + selected.comp = map_has[uselection]; + } else { + uselection -= map_has.size(); + selected.use_from = use_from_player; + selected.comp = player_has[uselection]; + } + } + + return selected; +} + +/* we use this if we selected the tool earlier */ +void player::consume_tools( const comp_selection &tool, int batch ) +{ + if( has_trait( trait_DEBUG_HS ) ) { + return; + } + + if( tool.use_from & use_from_player ) { + use_charges( tool.comp.type, tool.comp.count * batch ); + } + if( tool.use_from & use_from_map ) { + long quantity = tool.comp.count * batch; + g->m.use_charges( pos(), PICKUP_RANGE, tool.comp.type, quantity ); + } + + // else, use_from_none (or cancel), so we don't use up any tools; +} + +/* This call is in-efficient when doing it for multiple items with the same map inventory. +In that case, consider using select_tool_component with 1 pre-created map inventory, and then passing the results +to consume_tools */ +void player::consume_tools( const std::vector &tools, int batch, + const std::string &hotkeys ) +{ + inventory map_inv; + map_inv.form_from_map( pos(), PICKUP_RANGE ); + consume_tools( select_tool_component( tools, batch, map_inv, hotkeys ), batch ); +} + +ret_val player::can_disassemble( const item &obj, const inventory &inv ) const +{ + const auto &r = recipe_dictionary::get_uncraft( obj.typeId() ); + + if( !r ) { + return ret_val::make_failure( _( "You cannot disassemble this." ) ); + } + + // check sufficient light + if( lighting_craft_speed_multiplier( r ) == 0.0f ) { + return ret_val::make_failure( _( "You can't see to craft!" ) ); + } + // refuse to disassemble rotten items + if( obj.goes_bad() || ( obj.is_food_container() && obj.contents.front().goes_bad() ) ) { + if( obj.rotten() || ( obj.is_food_container() && obj.contents.front().rotten() ) ) { + return ret_val::make_failure( _( "It's rotten, I'm not taking that apart." ) ); + } + } + + if( obj.count_by_charges() && !r.has_flag( "UNCRAFT_SINGLE_CHARGE" ) ) { + // Create a new item to get the default charges + int qty = r.create_result().charges; + if( obj.charges < qty ) { + auto msg = ngettext( "You need at least %d charge of %s.", + "You need at least %d charges of %s.", qty ); + return ret_val::make_failure( msg, qty, obj.tname().c_str() ); + } + } + + const auto &dis = r.disassembly_requirements(); + + for( const auto &opts : dis.get_qualities() ) { + for( const auto &qual : opts ) { + if( !qual.has( inv ) ) { + // Here should be no dot at the end of the string as 'to_string()' provides it. + return ret_val::make_failure( _( "You need %s" ), qual.to_string().c_str() ); + } + } + } + + for( const auto &opts : dis.get_tools() ) { + const bool found = std::any_of( opts.begin(), opts.end(), + [&]( const tool_comp & tool ) { + return ( tool.count <= 0 && inv.has_tools( tool.type, 1 ) ) || + ( tool.count > 0 && inv.has_charges( tool.type, tool.count ) ); + } ); + + if( !found ) { + if( opts.front().count <= 0 ) { + return ret_val::make_failure( _( "You need %s." ), + item::nname( opts.front().type ).c_str() ); + } else { + return ret_val::make_failure( ngettext( "You need a %s with %d charge.", + "You need a %s with %d charges.", opts.front().count ), + item::nname( opts.front().type ).c_str(), + opts.front().count ); + } + } + } + + return ret_val::make_success(); +} + +bool player::disassemble() +{ + auto loc = game_menus::inv::disassemble( *this ); + + if( !loc ) { + add_msg( _( "Never mind." ) ); + return false; + } + + return disassemble( loc.obtain( *this ) ); +} + +bool player::disassemble( int dis_pos ) +{ + return disassemble( i_at( dis_pos ), dis_pos, false ); +} + +bool player::disassemble( item &obj, int pos, bool ground, bool interactive ) +{ + const auto ret = can_disassemble( obj, crafting_inventory() ); + + if( !ret.success() ) { + if( interactive ) { + add_msg_if_player( m_info, "%s", ret.c_str() ); + } + return false; + } + + const auto &r = recipe_dictionary::get_uncraft( obj.typeId() ); + // last chance to back out + if( interactive && get_option( "QUERY_DISASSEMBLE" ) ) { + const auto components( r.disassembly_requirements().get_components() ); + std::ostringstream list; + for( const auto &elem : components ) { + list << "- " << elem.front().to_string() << std::endl; + } + + if( !r.learn_by_disassembly.empty() && !knows_recipe( &r ) && can_decomp_learn( r ) ) { + if( !query_yn( + _( "Disassembling the %s may yield:\n%s\nReally disassemble?\nYou feel you may be able to understand this object's construction.\n" ), + obj.tname().c_str(), + list.str().c_str() ) ) { + return false; + } + } else if( !query_yn( _( "Disassembling the %s may yield:\n%s\nReally disassemble?" ), + obj.tname().c_str(), + list.str().c_str() ) ) { + return false; + } + } + + if( activity.id() != activity_id( "ACT_DISASSEMBLE" ) ) { + assign_activity( activity_id( "ACT_DISASSEMBLE" ), r.time ); + } else if( activity.moves_left <= 0 ) { + activity.moves_left = r.time; + } + + activity.values.push_back( pos ); + activity.coords.push_back( ground ? this->pos() : tripoint_min ); + activity.str_values.push_back( r.result() ); + + return true; +} + +void player::disassemble_all( bool one_pass ) +{ + // Reset all the activity values + assign_activity( activity_id( "ACT_DISASSEMBLE" ), 0 ); + auto items = g->m.i_at( pos() ); + bool found_any = false; + if( !one_pass ) { + // Kinda hacky + // That INT_MIN notes we want infinite uncraft + // If INT_MIN is reached in complete_disassemble, + // we will call this function again. + activity.values.push_back( INT_MIN ); + activity.str_values.push_back( "" ); + activity.coords.push_back( tripoint_min ); + } + + for( size_t i = 0; i < items.size(); i++ ) { + if( disassemble( items[i], i, true, false ) ) { + found_any = true; + } + } + + if( !found_any ) { + // Reset the activity - don't loop if there is nothing to do + activity = player_activity(); + } +} + +item &get_item_for_uncraft( player &p, int item_pos, + const tripoint &loc, bool from_ground ) +{ + item *org_item; + if( from_ground ) { + auto items_on_ground = g->m.i_at( loc ); + if( static_cast( item_pos ) >= items_on_ground.size() ) { + return null_item_reference(); + } + org_item = &items_on_ground[item_pos]; + } else { + org_item = &p.i_at( item_pos ); + } + + return *org_item; +} + +void player::complete_disassemble() +{ + // Clean up old settings + // Warning: Breaks old saves with disassembly in progress! + // But so would adding a new recipe... + if( activity.values.empty() || + activity.values.size() != activity.str_values.size() || + activity.values.size() != activity.coords.size() ) { + debugmsg( "bad disassembly activity values" ); + activity.set_to_null(); + return; + } + + // Disassembly activity is now saved in 3 parallel vectors: + // item position, tripoint location (tripoint_min for inventory) and recipe + + // Note: we're reading from the back (in inverse order) + // This is to avoid having to maintain indices + const int item_pos = activity.values.back(); + const tripoint loc = activity.coords.back(); + const auto recipe_name = activity.str_values.back(); + + activity.values.pop_back(); + activity.str_values.pop_back(); + activity.coords.pop_back(); + + if( item_pos == INT_MIN ) { + disassemble_all( false ); + return; + } + + const bool from_ground = loc != tripoint_min; + + complete_disassemble( item_pos, loc, from_ground, recipe_dictionary::get_uncraft( recipe_name ) ); + + if( !activity ) { + // Something above went wrong, don't continue + return; + } + + // Try to get another disassembly target from the activity + if( activity.values.empty() ) { + // No more targets + activity.set_to_null(); + return; + } + + if( activity.values.back() == INT_MIN ) { + disassemble_all( false ); + return; + } + + const auto &next_recipe = recipe_dictionary::get_uncraft( activity.str_values.back() ); + if( !next_recipe ) { + activity.set_to_null(); + return; + } + + activity.moves_left = next_recipe.time; +} + +// TODO: Make them accessible in a less ugly way +void remove_radio_mod( item &, player & ); + +void player::complete_disassemble( int item_pos, const tripoint &loc, + bool from_ground, const recipe &dis ) +{ + // Get the proper recipe - the one for disassembly, not assembly + const auto dis_requirements = dis.disassembly_requirements(); + item &org_item = get_item_for_uncraft( *this, item_pos, loc, from_ground ); + bool filthy = org_item.is_filthy(); + if( org_item.is_null() ) { + add_msg( _( "The item has vanished." ) ); + activity.set_to_null(); + return; + } + + if( org_item.typeId() != dis.result() ) { + add_msg( _( "The item might be gone, at least it is not at the expected position anymore." ) ); + activity.set_to_null(); + return; + } + // Make a copy to keep its data (damage/components) even after it + // has been removed. + item dis_item = org_item; + + float component_success_chance = std::min( std::pow( 0.8, dis_item.damage() ), 1.0 ); + + add_msg( _( "You disassemble the %s into its components." ), dis_item.tname().c_str() ); + // Remove any batteries, ammo and mods first + remove_ammo( dis_item, *this ); + remove_radio_mod( dis_item, *this ); + + if( dis_item.count_by_charges() ) { + // remove the charges that one would get from crafting it + org_item.charges -= dis.create_result().charges; + } + // remove the item, except when it's counted by charges and still has some + if( !org_item.count_by_charges() || org_item.charges <= 0 ) { + if( from_ground ) { + g->m.i_rem( loc, item_pos ); + } else { + i_rem( item_pos ); + } + } + + // Consume tool charges + for( const auto &it : dis_requirements.get_tools() ) { + consume_tools( it ); + } + + // add the components to the map + // Player skills should determine how many components are returned + + int skill_dice = 2 + get_skill_level( dis.skill_used ) * 3; + skill_dice += get_skill_level( dis.skill_used ); + + // Sides on dice is 16 plus your current intelligence + ///\EFFECT_INT increases success rate for disassembling items + int skill_sides = 16 + int_cur; + + int diff_dice = dis.difficulty; + int diff_sides = 24; // 16 + 8 (default intelligence) + + // disassembly only nets a bit of practice + if( dis.skill_used ) { + practice( dis.skill_used, ( dis.difficulty ) * 2, dis.difficulty ); + } + + // If the components aren't empty, we want items exactly identical to them + // Even if the best-fit recipe does not involve those items + std::vector components = dis_item.components; + // If the components are empty, item is the default kind and made of default components + if( components.empty() ) { + for( const auto &altercomps : dis_requirements.get_components() ) { + const item_comp &comp = altercomps.front(); + int compcount = comp.count; + item newit( comp.type, calendar::turn ); + // Counted-by-charge items that can be disassembled individually + // have their component count multiplied by the number of charges. + if( dis_item.count_by_charges() && dis.has_flag( "UNCRAFT_SINGLE_CHARGE" ) ) { + compcount *= std::min( dis_item.charges, dis.create_result().charges ); + } + // Compress liquids and counted-by-charges items into one item, + // they are added together on the map anyway and handle_liquid + // should only be called once to put it all into a container at once. + if( newit.count_by_charges() || newit.made_of( LIQUID ) ) { + newit.charges = compcount; + compcount = 1; + } else if( !newit.craft_has_charges() && newit.charges > 0 ) { + // tools that can be unloaded should be created unloaded, + // tools that can't be unloaded will keep their default charges. + newit.charges = 0; + } + + for( ; compcount > 0; compcount-- ) { + components.emplace_back( newit ); + } + } + } + + for( const item &newit : components ) { + const bool comp_success = ( dice( skill_dice, skill_sides ) > dice( diff_dice, diff_sides ) ); + if( dis.difficulty != 0 && !comp_success ) { + add_msg( m_bad, _( "You fail to recover %s." ), newit.tname().c_str() ); + continue; + } + const bool dmg_success = component_success_chance > rng_float( 0, 1 ); + if( !dmg_success ) { + // Show reason for failure (damaged item, tname contains the damage adjective) + //~ %1s - material, %2$s - disassembled item + add_msg( m_bad, _( "You fail to recover %1$s from the %2$s." ), newit.tname().c_str(), + dis_item.tname().c_str() ); + continue; + } + // Use item from components list, or (if not contained) + // use newit, the default constructed. + item act_item = newit; + + if( filthy ) { + act_item.item_tags.insert( "FILTHY" ); + } + + for( item::t_item_vector::iterator a = dis_item.components.begin(); a != dis_item.components.end(); + ++a ) { + if( a->type == newit.type ) { + act_item = *a; + dis_item.components.erase( a ); + break; + } + } + + const optional_vpart_position vp = g->m.veh_at( pos() ); + const int veh_part = vp ? vp->vehicle().part_with_feature( vp->part_index(), "CARGO" ) : -1; + + if( act_item.made_of( LIQUID ) ) { + g->handle_all_liquid( act_item, PICKUP_RANGE ); + } else if( veh_part != -1 && vp->vehicle().add_item( veh_part, act_item ) ) { + // add_item did put the items in the vehicle, nothing further to be done + } else { + // TODO: For items counted by charges, add as much as we can to the vehicle, and + // the rest on the ground (see dropping code and @vehicle::add_charges) + g->m.add_item_or_charges( pos(), act_item ); + } + } + + if( !dis.learn_by_disassembly.empty() && !knows_recipe( &dis ) ) { + if( can_decomp_learn( dis ) ) { + // @todo: make this depend on intelligence + if( one_in( 4 ) ) { + learn_recipe( &dis.ident().obj() );//@todo: change to forward an id or a reference + add_msg( m_good, _( "You learned a recipe for %s from disassembling it!" ), + dis_item.tname().c_str() ); + } else { + add_msg( m_info, _( "You might be able to learn a recipe for %s if you disassemble another." ), + dis_item.tname().c_str() ); + } + } else { + add_msg( m_info, _( "If you had better skills, you might learn a recipe next time." ) ); + } + } +} + +void remove_ammo( std::list &dis_items, player &p ) +{ + for( auto &dis_item : dis_items ) { + remove_ammo( dis_item, p ); + } +} + +void drop_or_handle( const item &newit, player &p ) +{ + if( newit.made_of( LIQUID ) && &p == &g->u ) { // TODO: what about NPCs? + g->handle_all_liquid( newit, PICKUP_RANGE ); + } else { + item tmp( newit ); + p.i_add_or_drop( tmp ); + } +} + +void remove_ammo( item &dis_item, player &p ) +{ + for( auto iter = dis_item.contents.begin(); iter != dis_item.contents.end(); ) { + if( iter->is_irremovable() ) { + iter++; + continue; + } + drop_or_handle( *iter, p ); + iter = dis_item.contents.erase( iter ); + } + + if( dis_item.has_flag( "NO_UNLOAD" ) ) { + return; + } + if( dis_item.is_gun() && dis_item.ammo_current() != "null" ) { + item ammodrop( dis_item.ammo_current(), calendar::turn ); + ammodrop.charges = dis_item.charges; + drop_or_handle( ammodrop, p ); + dis_item.charges = 0; + } + if( dis_item.is_tool() && dis_item.charges > 0 && dis_item.ammo_type() ) { + item ammodrop( dis_item.ammo_type()->default_ammotype(), calendar::turn ); + ammodrop.charges = dis_item.charges; + if( dis_item.ammo_type() == ammotype( "plutonium" ) ) { + ammodrop.charges /= PLUTONIUM_CHARGES; + } + drop_or_handle( ammodrop, p ); + dis_item.charges = 0; + } +} + +std::vector player::get_crafting_helpers() const +{ + return g->get_npcs_if( [this]( const npc & guy ) { + return rl_dist( guy.pos(), pos() ) < PICKUP_RANGE && guy.is_friend() && + !guy.in_sleep_state() && g->m.clear_path( pos(), guy.pos(), PICKUP_RANGE, 1, 100 ); + } ); +} From f030180c4b63be91562359f11cd0ed452e47d7b3 Mon Sep 17 00:00:00 2001 From: Kevin Granade Date: Fri, 18 May 2018 03:03:29 +0000 Subject: [PATCH 19/35] Extract reset ui call to a helper module. --- src/cursesport.cpp | 10 +++++----- src/game_ui.cpp | 8 ++++++++ src/game_ui.h | 10 ++++++++++ src/ncurses_def.cpp | 4 ++-- src/sdltiles.cpp | 32 ++++++++++++++++---------------- 5 files changed, 41 insertions(+), 23 deletions(-) create mode 100644 src/game_ui.cpp create mode 100644 src/game_ui.h diff --git a/src/cursesport.cpp b/src/cursesport.cpp index b11884571569..0147f877f25f 100644 --- a/src/cursesport.cpp +++ b/src/cursesport.cpp @@ -1,11 +1,11 @@ #if (defined TILES || defined _WIN32 || defined WINDOWS) +#include "animation.h" +#include "catacharset.h" +#include "color.h" #include "cursesport.h" #include "cursesdef.h" +#include "game_ui.h" #include "output.h" -#include "color.h" -#include "catacharset.h" -#include "animation.h" -#include "game.h" #include // strlen #include @@ -413,7 +413,7 @@ void catacurses::mvwprintw(const window &win, int y, int x, const std::string &p //Resizes the underlying terminal after a Window's console resize(maybe?) Not used in TILES void catacurses::resizeterm() { - g->init_ui(); + game_ui::init_ui(); } //erases a window of all text and attributes diff --git a/src/game_ui.cpp b/src/game_ui.cpp new file mode 100644 index 000000000000..f690d9ed6f89 --- /dev/null +++ b/src/game_ui.cpp @@ -0,0 +1,8 @@ +#include "game_ui.h" +#include "game.h" + + +void game_ui::init_ui() +{ + g->init_ui(); +} diff --git a/src/game_ui.h b/src/game_ui.h new file mode 100644 index 000000000000..0b83ce30dd35 --- /dev/null +++ b/src/game_ui.h @@ -0,0 +1,10 @@ +#pragma once +#ifndef GAME_UI_H +#define GAME_UI_H + +namespace game_ui +{ + void init_ui(); +} + +#endif // GAME_UI_H diff --git a/src/ncurses_def.cpp b/src/ncurses_def.cpp index 22acdc18fb4c..9107dad50339 100644 --- a/src/ncurses_def.cpp +++ b/src/ncurses_def.cpp @@ -22,7 +22,7 @@ #include "catacharset.h" #include "color.h" -#include "game.h" +#include "game_ui.h" #include extern int VIEW_OFFSET_X; // X position of terrain window @@ -207,7 +207,7 @@ void catacurses::resizeterm() const int new_x = ::getmaxx( stdscr.get<::WINDOW>() ); const int new_y = ::getmaxy( stdscr.get<::WINDOW>() ); if( ::is_term_resized( new_x, new_y ) ) { - g->init_ui(); + game_ui::init_ui(); } } diff --git a/src/sdltiles.cpp b/src/sdltiles.cpp index ad9023afe373..6fc1db5eab09 100644 --- a/src/sdltiles.cpp +++ b/src/sdltiles.cpp @@ -1,5 +1,10 @@ #if (defined TILES) +#include "cata_utility.h" +#include "color_loader.h" #include "cursesport.h" +#include "font_loader.h" +#include "game_ui.h" +#include "loading_ui.h" #include "options.h" #include "output.h" #include "input.h" @@ -9,28 +14,23 @@ #include "debug.h" #include "player.h" #include "translations.h" -#include -#include -#include -#include -#include -#include -#include -#include #include "cata_tiles.h" #include "get_version.h" #include "init.h" #include "path_info.h" #include "string_formatter.h" #include "filesystem.h" -#include "game.h" #include "lightmap.h" #include "rng.h" #include -#include "cata_utility.h" -#include "color_loader.h" -#include "font_loader.h" -#include "loading_ui.h" +#include +#include +#include +#include +#include +#include +#include +#include #if (defined _WIN32 || defined WINDOWS) # include "platform_win.h" @@ -1269,9 +1269,9 @@ bool handle_resize(int w, int h) TERMINAL_WIDTH = WindowWidth / fontwidth; TERMINAL_HEIGHT = WindowHeight / fontheight; SetupRenderTarget(); - g->init_ui(); + game_ui::init_ui(); tilecontext->reinit_minimap(); - + return true; } return false; @@ -1793,7 +1793,7 @@ bool gamepad_available() { void rescale_tileset(int size) { tilecontext->set_draw_scale(size); - g->init_ui(); + game_ui::init_ui(); ClearScreen(); } From 58508b9db8e5f98370391212a26d59912726b33f Mon Sep 17 00:00:00 2001 From: BorkBorkGoesTheCode Date: Fri, 18 May 2018 02:13:14 -0500 Subject: [PATCH 20/35] Replaced SVS-24 and CW-24 sight_dispersion with default_mod red_dot_sight and removed CWD-63 sight_dispersion --- data/mods/EW_Pack/ew_weapons.json | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/data/mods/EW_Pack/ew_weapons.json b/data/mods/EW_Pack/ew_weapons.json index fc2e7e360447..026f7395181f 100644 --- a/data/mods/EW_Pack/ew_weapons.json +++ b/data/mods/EW_Pack/ew_weapons.json @@ -16,9 +16,9 @@ "bashing": 12, "to_hit": -1, "dispersion": 75, - "sight_dispersion": 60, "durability": 9, "burst": 12, + "default_mods": [ "red_dot_sight" ], "valid_mod_locations": [[ "accessories", 4 ],[ "barrel", 1 ],[ "bore", 1 ],[ "grip", 1 ],[ "magazine", 1 ],[ "mechanism", 4 ],[ "muzzle", 1 ],[ "rail", 1 ],[ "sights", 1 ],[ "stock", 1 ],[ "underbarrel", 1 ]], "magazines": [ [ "654", [ "svs24_robomag", "svs24_robodrum" ] ], @@ -55,8 +55,8 @@ "bashing": 12, "to_hit": -1, "dispersion": 75, - "sight_dispersion": 60, "durability": 9, + "default_mods": [ "red_dot_sight" ], "valid_mod_locations": [[ "accessories", 4],[ "barrel", 1],[ "bore", 1],[ "grip", 1],["magazine", 1],[ "mechanism", 4 ],[ "muzzle", 1 ],[ "rail", 1 ],[ "sights", 1 ],[ "stock", 1 ],[ "underbarrel", 1 ]], "magazines": [ [ "545x39", [ "cw24_robomag", "cw24_bigrobomag" ] ], @@ -112,7 +112,6 @@ "description": "Civilian version of the SVS-24. It has a modified receiver and a new crudely crafted full-auto bolt carrier. Don't expect the original reliability.", "price": 420000, "dispersion": 120, - "sight_dispersion": 60, "durability": 6, "burst": 8 }, @@ -132,7 +131,6 @@ "bashing": 12, "to_hit": -1, "dispersion": 75, - "sight_dispersion": 60, "durability": 9, "valid_mod_locations": [[ "accessories", 4],[ "barrel", 1],[ "bore", 1],[ "grip", 1],["magazine", 1],[ "mechanism", 4 ],[ "muzzle", 1 ],[ "rail", 1 ],[ "sights", 1 ],[ "stock", 1 ],[ "underbarrel", 1 ]], "magazines": [ From ad8c163d2403f8bbcbcc5c1b2861f28bf8de4075 Mon Sep 17 00:00:00 2001 From: BorkBorkGoesTheCode Date: Fri, 18 May 2018 02:25:39 -0500 Subject: [PATCH 21/35] Reduced tankmod weapon sight_dispersion to 10 --- data/mods/Tanks/items.json | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/data/mods/Tanks/items.json b/data/mods/Tanks/items.json index 107b2c86c106..f22d14eb3075 100644 --- a/data/mods/Tanks/items.json +++ b/data/mods/Tanks/items.json @@ -421,7 +421,7 @@ "bashing": 12, "to_hit": -1, "dispersion": 0, - "sight_dispersion": 75, + "sight_dispersion": 10, "durability": 8, "burst": 5, "reload": 100, @@ -448,7 +448,7 @@ "to_hit": -6, "range": 45, "dispersion": 0, - "sight_dispersion": 60, + "sight_dispersion": 10, "durability": 9, "clip_size": 1, "reload": 100, @@ -471,7 +471,7 @@ "to_hit": -6, "range": 30, "dispersion": 20, - "sight_dispersion": 60, + "sight_dispersion": 10, "durability": 9, "clip_size": 5, "reload": 100, @@ -495,7 +495,7 @@ "to_hit": -6, "range": 15, "dispersion": 25, - "sight_dispersion": 60, + "sight_dispersion": 10, "durability": 8, "clip_size": 5, "reload": 100, @@ -520,7 +520,7 @@ "to_hit": -6, "range": 45, "dispersion": 10, - "sight_dispersion": 60, + "sight_dispersion": 10, "durability": 9, "clip_size": 1, "reload": 100, @@ -544,7 +544,7 @@ "to_hit": -6, "range": 15, "dispersion": 100, - "sight_dispersion": 60, + "sight_dispersion": 10, "durability": 9, "clip_size": 1, "reload": 100, @@ -566,7 +566,7 @@ "bashing": 12, "to_hit": -6, "dispersion": 0, - "sight_dispersion": 20, + "sight_dispersion": 10, "durability": 9, "clip_size": 2, "reload": 100, From cd9f90961cd6e3731f9666e9f381dca6c75c2f58 Mon Sep 17 00:00:00 2001 From: BorkBorkGoesTheCode Date: Fri, 18 May 2018 02:29:52 -0500 Subject: [PATCH 22/35] Removed sight_dispersion from Generic Guns lever action carbine and pipe rifle --- data/mods/Generic_Guns/gg_guns.json | 2 -- 1 file changed, 2 deletions(-) diff --git a/data/mods/Generic_Guns/gg_guns.json b/data/mods/Generic_Guns/gg_guns.json index f30fc2c8b1c3..f79ba8f10d98 100644 --- a/data/mods/Generic_Guns/gg_guns.json +++ b/data/mods/Generic_Guns/gg_guns.json @@ -17,7 +17,6 @@ "skill": "rifle", "ranged_damage": -2, "dispersion": 105, - "sight_dispersion": 60, "durability": 6, "loudness": 150, "clip_size": 1, @@ -42,7 +41,6 @@ "skill": "rifle", "ranged_damage": 4, "dispersion": 75, - "sight_dispersion": 60, "durability": 7, "loudness": 150, "clip_size": 12, From 0720fa8420f28b0dc3e276266020ed3f8d9e6873 Mon Sep 17 00:00:00 2001 From: OzoneH3 Date: Fri, 18 May 2018 09:54:46 +0200 Subject: [PATCH 23/35] Fix astyle --- src/game_ui.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/game_ui.h b/src/game_ui.h index 0b83ce30dd35..01bda306d938 100644 --- a/src/game_ui.h +++ b/src/game_ui.h @@ -4,7 +4,7 @@ namespace game_ui { - void init_ui(); +void init_ui(); } #endif // GAME_UI_H From d4af278359a7d7b66af96530c74c721f6cce6629 Mon Sep 17 00:00:00 2001 From: budg3 <15700356+budg3@users.noreply.github.com> Date: Fri, 18 May 2018 09:34:35 +0000 Subject: [PATCH 24/35] Add files via upload removed on_roof from turret_mount --- data/json/vehicle_parts.json | 1 - 1 file changed, 1 deletion(-) diff --git a/data/json/vehicle_parts.json b/data/json/vehicle_parts.json index 3e8defa27b3b..406a0447aa84 100755 --- a/data/json/vehicle_parts.json +++ b/data/json/vehicle_parts.json @@ -2184,7 +2184,6 @@ "durability": 400, "item": "turret_mount", "difficulty": 2, - "location": "on_roof", "requirements": { "install": { "skills": [ [ "mechanics", 1 ] ], "time": 120000, "using": [ [ "welding_standard", 10 ] ] }, "removal": { "skills": [ [ "mechanics", 1 ] ], "using": "vehicle_weld_removal" }, From b982f17a1f000dc3bbc8892f741c3e46b22850c9 Mon Sep 17 00:00:00 2001 From: budg3 <15700356+budg3@users.noreply.github.com> Date: Fri, 18 May 2018 09:36:13 +0000 Subject: [PATCH 25/35] added on_roof to turrets --- data/json/vehicleparts/turret.json | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/data/json/vehicleparts/turret.json b/data/json/vehicleparts/turret.json index 77b9ea65cc52..6786ea9765fe 100644 --- a/data/json/vehicleparts/turret.json +++ b/data/json/vehicleparts/turret.json @@ -17,6 +17,7 @@ "color": "dark_gray", "broken_color": "dark_gray", "difficulty": 5, + "location": "on_roof", "breaks_into": [ { "item": "flamethrower", "prob": 50 } ], "requirements": { "install": { @@ -38,6 +39,7 @@ "color": "magenta", "broken_color": "magenta", "difficulty": 6, + "location": "on_roof", "breaks_into": [ { "item": "ftk93", "prob": 50 } ], "requirements": { "install": { @@ -58,6 +60,7 @@ "color": "magenta", "broken_color": "magenta", "difficulty": 5, + "location": "on_roof", "breaks_into": [ { "item": "laser_rifle", "prob": 50 } ], "requirements": { "install": { @@ -78,6 +81,7 @@ "color": "cyan", "broken_color": "cyan", "difficulty": 3, + "location": "on_roof", "breaks_into": [ { "item": "m249", "prob": 50 } ], "requirements": { "install": { @@ -98,6 +102,7 @@ "color": "green", "broken_color": "green", "difficulty": 3, + "location": "on_roof", "breaks_into": [ { "item": "abzats", "prob": 50 } ], "requirements": { "install": { @@ -118,6 +123,7 @@ "color": "green", "broken_color": "green", "difficulty": 4, + "location": "on_roof", "breaks_into": [ { "item": "m2browning", "prob": 50 } ], "requirements": { "install": { @@ -138,6 +144,7 @@ "color": "green", "broken_color": "green", "difficulty": 6, + "location": "on_roof", "breaks_into": [ { "item": "m134", "prob": 50 } ], "requirements": { "install": { @@ -158,6 +165,7 @@ "color": "cyan", "broken_color": "cyan", "difficulty": 4, + "location": "on_roof", "breaks_into": [ { "item": "m1918", "prob": 50 } ], "requirements": { "install": { @@ -178,6 +186,7 @@ "color": "cyan", "broken_color": "cyan", "difficulty": 4, + "location": "on_roof", "breaks_into": [ { "item": "m240", "prob": 50 } ], "requirements": { "install": { @@ -198,6 +207,7 @@ "color": "cyan", "broken_color": "cyan", "difficulty": 4, + "location": "on_roof", "breaks_into": [ { "item": "m60", "prob": 50 } ], "requirements": { "install": { @@ -218,6 +228,7 @@ "color": "green", "broken_color": "green", "difficulty": 5, + "location": "on_roof", "breaks_into": [ { "item": "mark19", "prob": 50 } ], "requirements": { "install": { @@ -238,6 +249,7 @@ "color": "white", "broken_color": "white", "difficulty": 6, + "location": "on_roof", "breaks_into": [ { "item": "rm298", "prob": 50 } ], "requirements": { "install": { @@ -258,6 +270,7 @@ "color": "white", "broken_color": "white", "difficulty": 6, + "location": "on_roof", "breaks_into": [ { "item": "rm614_lmg", "prob": 50 } ], "requirements": { "install": { @@ -278,6 +291,7 @@ "color": "light_blue", "broken_color": "light_blue", "difficulty": 5, + "location": "on_roof", "breaks_into": [ { "item": "plasma_rifle", "prob": 50 } ], "requirements": { "install": { @@ -299,6 +313,7 @@ "color": "blue", "broken_color": "blue", "difficulty": 4, + "location": "on_roof", "breaks_into": [ { "item": "watercannon", "prob": 50 } ], "requirements": { "install": { From 2fa43ccd9db4ab13696b4adf93cc354af0949d5a Mon Sep 17 00:00:00 2001 From: budg3 <15700356+budg3@users.noreply.github.com> Date: Fri, 18 May 2018 09:37:15 +0000 Subject: [PATCH 26/35] added on_roof to turrets in blazemod --- data/mods/blazemod/blaze_weapons_parts.json | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/data/mods/blazemod/blaze_weapons_parts.json b/data/mods/blazemod/blaze_weapons_parts.json index cdb97fb43131..7ae30de30baf 100644 --- a/data/mods/blazemod/blaze_weapons_parts.json +++ b/data/mods/blazemod/blaze_weapons_parts.json @@ -17,6 +17,7 @@ "color": "light_gray", "broken_color": "light_gray", "difficulty": 4, + "location": "on_roof", "folded_volume": 12, "breaks_into": [ { "item": "bolter", "count": [ 0, 1 ] } ] }, @@ -29,6 +30,7 @@ "color": "brown", "broken_color": "brown", "difficulty": 4, + "location": "on_roof", "folded_volume": 22, "breaks_into": [ { "item": "cangun", "count": [ 0, 1 ] } ] }, @@ -41,6 +43,7 @@ "color": "dark_gray", "broken_color": "dark_gray", "difficulty": 5, + "location": "on_roof", "range": 15, "folded_volume": 15, "breaks_into": [ { "item": "gattler", "count": [ 0, 1 ] } ] @@ -54,6 +57,7 @@ "color": "blue", "broken_color": "blue", "difficulty": 6, + "location": "on_roof", "folded_volume": 12, "breaks_into": [ { "item": "lasgun", "count": [ 0, 1 ] } ] }, @@ -66,6 +70,7 @@ "color": "blue", "broken_color": "blue", "difficulty": 6, + "location": "on_roof", "folded_volume": 12, "breaks_into": [ { "item": "lasgun", "count": [ 0, 1 ] } ] }, @@ -78,6 +83,7 @@ "color": "magenta", "broken_color": "magenta", "difficulty": 6, + "location": "on_roof", "folded_volume": 18, "breaks_into": [ { "item": "lasgunx", "count": [ 0, 1 ] } ] }, @@ -91,6 +97,7 @@ "color": "dark_gray", "broken_color": "dark_gray", "difficulty": 5, + "location": "on_roof", "durability": 400, "power": -5, "range": 12, @@ -107,6 +114,7 @@ "color": "red", "broken_color": "red", "difficulty": 4, + "location": "on_roof", "range": 12, "folded_volume": 19, "breaks_into": [ { "item": "rm451_flamethrower", "count": [ 0, 1 ] } ], @@ -121,6 +129,7 @@ "color": "dark_gray", "broken_color": "dark_gray", "difficulty": 5, + "location": "on_roof", "folded_volume": 10, "breaks_into": [ { "item": "nailer", "count": [ 0, 1 ] } ] }, @@ -133,6 +142,7 @@ "color": "blue", "broken_color": "blue", "difficulty": 5, + "location": "on_roof", "folded_volume": 15, "breaks_into": [ { "item": "needler", "count": [ 0, 1 ] } ] }, @@ -145,6 +155,7 @@ "color": "magenta", "broken_color": "magenta", "difficulty": 5, + "location": "on_roof", "range": 14, "folded_volume": 16, "breaks_into": [ { "item": "railturret", "count": [ 0, 1 ] } ] @@ -159,6 +170,7 @@ "color": "dark_gray", "broken_color": "dark_gray", "difficulty": 5, + "location": "on_roof", "durability": 400, "power": -25, "range": 15, @@ -172,6 +184,7 @@ "type": "vehicle_part", "name": "manual rotary speargun", "item": "rslauncher", + "location": "on_roof", "color": "light_gray", "broken_color": "light_gray", "difficulty": 5, @@ -187,6 +200,7 @@ "color": "brown", "broken_color": "brown", "difficulty": 4, + "location": "on_roof", "folded_volume": 20, "breaks_into": [ { "item": "scorpion", "count": [ 0, 1 ] } ] }, @@ -199,6 +213,7 @@ "color": "blue", "broken_color": "blue", "difficulty": 6, + "location": "on_roof", "folded_volume": 14, "breaks_into": [ { "item": "shardgun", "count": [ 0, 1 ] } ] }, @@ -211,6 +226,7 @@ "color": "light_gray", "broken_color": "light_gray", "difficulty": 3, + "location": "on_roof", "folded_volume": 12, "breaks_into": [ { "item": "slauncher", "count": [ 0, 1 ] } ] }, @@ -223,6 +239,7 @@ "color": "blue", "broken_color": "blue", "difficulty": 3, + "location": "on_roof", "folded_volume": 15, "breaks_into": [ { "item": "teslagun", "count": [ 0, 1 ] } ] }, @@ -235,6 +252,7 @@ "color": "light_gray", "broken_color": "light_gray", "difficulty": 4, + "location": "on_roof", "folded_volume": 15, "breaks_into": [ { "item": "tiharturret", "count": [ 0, 1 ] } ] } From 578bf9c7ea216eef7499ad1c0f3534ee009a8b5b Mon Sep 17 00:00:00 2001 From: budg3 <15700356+budg3@users.noreply.github.com> Date: Fri, 18 May 2018 09:38:13 +0000 Subject: [PATCH 27/35] added on_roof to turrets in Tanks mod --- data/mods/Tanks/parts.json | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/data/mods/Tanks/parts.json b/data/mods/Tanks/parts.json index c9dce3c464ce..02fb4baedba9 100644 --- a/data/mods/Tanks/parts.json +++ b/data/mods/Tanks/parts.json @@ -19,6 +19,7 @@ "size": 10, "extend": { "flags": [ "MANUAL" ] }, "breaks_into": [ { "item": "tank_gun_manual", "prob": 50 } ], + "location": "on_roof", "requirements": { "install": { "skills": [ [ "mechanics", 5 ], [ "launcher", 2 ] ], "time": 10000 }, "removal": { "skills": [ [ "mechanics", 3 ] ] } @@ -34,6 +35,7 @@ "size": 50, "extend": { "flags": [ "MANUAL" ] }, "breaks_into": [ { "item": "tank_gun_auto", "prob": 50 } ], + "location": "on_roof", "requirements": { "install": { "skills": [ [ "mechanics", 5 ], [ "launcher", 2 ] ], "time": 10000 }, "removal": { "skills": [ [ "mechanics", 3 ] ] } @@ -49,6 +51,7 @@ "size": 100, "extend": { "flags": [ "NEEDS_BATTERY_MOUNT" ] }, "breaks_into": [ { "item": "tank_gun_rws", "prob": 50 } ], + "location": "on_roof", "requirements": { "install": { "skills": [ [ "mechanics", 5 ], [ "launcher", 3 ], [ "electronics", 3 ] ], "time": 10000 }, "removal": { "skills": [ [ "mechanics", 3 ] ] } @@ -64,6 +67,7 @@ "size": 15, "extend": { "flags": [ "MANUAL" ] }, "breaks_into": [ { "item": "howitzer_gun", "prob": 50 } ], + "location": "on_roof", "requirements": { "install": { "skills": [ [ "mechanics", 5 ], [ "launcher", 3 ] ], "time": 10000 }, "removal": { "skills": [ [ "mechanics", 3 ] ] } @@ -79,6 +83,7 @@ "size": 35, "extend": { "flags": [ "MANUAL" ] }, "breaks_into": [ { "item": "atgm_launcher", "prob": 50 } ], + "location": "on_roof", "requirements": { "install": { "skills": [ [ "mechanics", 5 ], [ "launcher", 3 ] ], "time": 10000 }, "removal": { "skills": [ [ "mechanics", 3 ] ] } @@ -94,6 +99,7 @@ "size": 35, "extend": { "flags": [ "NEEDS_BATTERY_MOUNT" ] }, "breaks_into": [ { "item": "atgm_turret", "prob": 50 } ], + "location": "on_roof", "requirements": { "install": { "skills": [ [ "mechanics", 5 ], [ "launcher", 3 ], [ "electronics", 3 ] ], "time": 10000 }, "removal": { "skills": [ [ "mechanics", 3 ] ] } @@ -109,6 +115,7 @@ "size": 50, "extend": { "flags": [ "MANUAL" ] }, "breaks_into": [ { "item": "30mm_autocannon", "prob": 50 } ], + "location": "on_roof", "requirements": { "install": { "skills": [ [ "mechanics", 5 ], [ "rifle", 3 ] ], "time": 10000 }, "removal": { "skills": [ [ "mechanics", 3 ] ] } From 23eb8022de95749d2bdd4f9b763fabc5cff01bcd Mon Sep 17 00:00:00 2001 From: Cyrano7 Date: Fri, 18 May 2018 06:37:30 -0700 Subject: [PATCH 28/35] Fix some typos --- data/json/furniture.json | 8 ++++---- data/json/items/gunmod/sights.json | 2 +- data/json/items/magazine/50.json | 2 +- data/json/items/tools.json | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/data/json/furniture.json b/data/json/furniture.json index 0a9cf198d915..b463c80a59f0 100644 --- a/data/json/furniture.json +++ b/data/json/furniture.json @@ -354,7 +354,7 @@ "id" : "f_oven", "name": "oven", "symbol": "#", - "description": "Used for heating and cooking food with eletricity. Doesn't look like it's working, although it stil has parts.", + "description": "Used for heating and cooking food with electricity. Doesn't look like it's working, although it still has parts.", "color": "dark_gray", "move_cost_mod": 2, "required_str": 10, @@ -633,7 +633,7 @@ "id" : "f_exercise", "name": "exercise machine", "symbol": "T", - "description": "Typically used for, well, excercising. You're not up for it.", + "description": "Typically used for, well, exercising. You're not up for it.", "color": "dark_gray", "move_cost_mod": 1, "required_str": 8, @@ -1099,7 +1099,7 @@ "type" : "furniture", "id" : "f_bigmirror_b", "name": "broken standing mirror", - "description": "You could look at yourself, if the mirror wasnt covered in cracks and fractures.", + "description": "You could look at yourself, if the mirror wasn't covered in cracks and fractures.", "symbol": "{", "color": "light_gray", "move_cost_mod": 2, @@ -2415,7 +2415,7 @@ "type" : "furniture", "id" : "f_floor_canvas", "name": "heavy punching bag", - "description": "Punch Punch! Excercise those arms!", + "description": "Punch Punch! Exercise those arms!", "symbol": "0", "color": "dark_gray", "move_cost_mod": -1, diff --git a/data/json/items/gunmod/sights.json b/data/json/items/gunmod/sights.json index 005047820707..a6912d4f1976 100644 --- a/data/json/items/gunmod/sights.json +++ b/data/json/items/gunmod/sights.json @@ -146,7 +146,7 @@ "id": "tele_sight", "type": "GUNMOD", "name": "telescopic sight", - "description": "A simple telescopic sight, essentially a small telescope with crosshairs. Increases weight but improves accuracy.", + "description": "A simple telescopic sight, essentially a small telescope with crosshairs. Increases weight but improves accuracy.", "weight": 300, "volume": 1, "price": 30000, diff --git a/data/json/items/magazine/50.json b/data/json/items/magazine/50.json index 40bead0713f8..73b9c9e7f551 100644 --- a/data/json/items/magazine/50.json +++ b/data/json/items/magazine/50.json @@ -14,7 +14,7 @@ "type": "MAGAZINE", "name": "Barrett magazine", "//": "Fully loaded 10 magazine, presumably loaded with the 114.7 gram M33 Ball cartridge, weighs 1868.801 grams. Source inetres.com", - "description": "Standard 10-round box magazine for a Barrett M107A1 anti-materiel rifle.", + "description": "Standard 10-round box magazine for a Barrett M107A1 anti-material rifle.", "weight": 727, "volume": 2, "price": 10000, diff --git a/data/json/items/tools.json b/data/json/items/tools.json index 8e3a72f42568..9fa629478f27 100644 --- a/data/json/items/tools.json +++ b/data/json/items/tools.json @@ -1375,7 +1375,7 @@ "type": "TOOL", "name": "cell phone", "name_plural": "cell phones", - "description": "This is a typical cellphone that features an LED for use as a flash. Using this cellphone will turn it on and provide light, assuming it is charged with batteries. Also it has a clock app that includes an alarm.", + "description": "This is a typical cellphone that features an LED for use as a flash. Using this cellphone will turn it on and provide light, assuming it is charged with batteries. Also it has a clock app that includes an alarm.", "weight": 226, "volume": 1, "price": 10000, From a3d8e9d15fdb917d0d8664e195cc75bfed119b76 Mon Sep 17 00:00:00 2001 From: Cyrano7 Date: Fri, 18 May 2018 06:38:47 -0700 Subject: [PATCH 29/35] Delete some extra spaces --- data/json/items/armor.json | 2 +- data/json/recipes/other/medical.json | 4 ++-- data/json/recipes/weapon/mods.json | 10 +++++----- data/json/vehicles/utility.json | 4 ++-- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/data/json/items/armor.json b/data/json/items/armor.json index c66a7ee0939f..68fe46960c53 100644 --- a/data/json/items/armor.json +++ b/data/json/items/armor.json @@ -8548,7 +8548,7 @@ "type": "holster", "holster_prompt": "Sheath blade", "holster_msg": "You sheath your %s", - "min_volume": 1, + "min_volume": 1, "max_volume": 4, "draw_cost": 3, "flags": [ "SHEATH_KNIFE", "SHEATH_SWORD" ] diff --git a/data/json/recipes/other/medical.json b/data/json/recipes/other/medical.json index 85adda206254..d99f3a8457d6 100644 --- a/data/json/recipes/other/medical.json +++ b/data/json/recipes/other/medical.json @@ -125,7 +125,7 @@ [ [ "rag", 1 ] ], [ [ "disinfectant", 1 ] ] ] - }, + }, { "type": "recipe", "result": "disincotton_ball", @@ -139,5 +139,5 @@ [ [ "cotton_ball", 1 ] ], [ [ "disinfectant", 1 ] ] ] - } + } ] diff --git a/data/json/recipes/weapon/mods.json b/data/json/recipes/weapon/mods.json index 1ddaa4e88956..644cffbddd5a 100644 --- a/data/json/recipes/weapon/mods.json +++ b/data/json/recipes/weapon/mods.json @@ -80,8 +80,8 @@ "using": [ [ "soldering_standard", 10 ], [ "surface_heat", 10 ] ], "qualities": [ { "id": "SCREW_FINE", "level": 1 } ], "tools": [ [ [ "mold_plastic", -1 ] ] ], - "components": [ - [ + "components": [ + [ [ "scrap", 1 ], [ "can_drink_unsealed", 1 ], [ "can_food_unsealed", 1 ], [ "canister_empty", 1 ], [ "plastic_chunk", 1 ] ], @@ -102,8 +102,8 @@ "using": [ [ "soldering_standard", 10 ], [ "surface_heat", 10 ] ], "qualities": [ { "id": "SCREW_FINE", "level": 1 } ], "tools": [ [ [ "mold_plastic", -1 ] ] ], - "components": [ - [ + "components": [ + [ [ "scrap", 1 ], [ "can_drink_unsealed", 1 ], [ "can_food_unsealed", 1 ], [ "canister_empty", 1 ], [ "plastic_chunk", 1 ] ], @@ -129,7 +129,7 @@ { "id": "SAW_M_FINE", "level": 1 } ], "tools": [ [ [ "mold_plastic", -1 ] ] ], - "components": [ + "components": [ [ [ "scrap", 3 ], [ "steel_chunk", 1 ] ], [ [ "plastic_chunk", 1 ] ], [ [ "spring", 1 ] ] diff --git a/data/json/vehicles/utility.json b/data/json/vehicles/utility.json index 01b7a79cdd7a..4ff88a136d1e 100644 --- a/data/json/vehicles/utility.json +++ b/data/json/vehicles/utility.json @@ -239,9 +239,9 @@ "type": "vehicle", "id": "forklift", "name": "Forklift", - "blueprint": [ + "blueprint": [ [ "o#o-" ], - [ "o|o-" ] + [ "o|o-" ] ], "parts": [ { "x": -1, "y": 0, "part": "frame_sw" }, From f3ae65711332209901917e25305c66fe33493a81 Mon Sep 17 00:00:00 2001 From: ZhilkinSerg Date: Fri, 18 May 2018 17:25:05 +0300 Subject: [PATCH 30/35] Include game.h to sdtiles.cpp --- src/sdltiles.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sdltiles.cpp b/src/sdltiles.cpp index 6fc1db5eab09..b35009ffef85 100644 --- a/src/sdltiles.cpp +++ b/src/sdltiles.cpp @@ -1,4 +1,5 @@ #if (defined TILES) +#include "game.h" #include "cata_utility.h" #include "color_loader.h" #include "cursesport.h" From 21f1b6b3d8bcbd447120af972b1915cc726ba79b Mon Sep 17 00:00:00 2001 From: BorkBorkGoesTheCode Date: Fri, 18 May 2018 20:50:08 -0500 Subject: [PATCH 31/35] 40mm automatics: Rebalanced around 1 second of automatic fire Mark 19: auto 6, down from 10 --- data/json/items/gun/40mm.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/json/items/gun/40mm.json b/data/json/items/gun/40mm.json index cbea5e300667..22bb51032ac8 100644 --- a/data/json/items/gun/40mm.json +++ b/data/json/items/gun/40mm.json @@ -75,7 +75,7 @@ "reload": 400, "modes": [ [ "DEFAULT", "semi-auto", 1, "NPC_AVOID" ], - [ "AUTO", "auto", 10, "NPC_AVOID" ] + [ "AUTO", "auto", 6, "NPC_AVOID" ] ], "magazines": [ [ "40mm", [ "belt40mm" ] ] ], "flags": [ "MOUNTED_GUN" ] From aebc23811abb990c43ab7144dfcf3ddaaefcfb26 Mon Sep 17 00:00:00 2001 From: Jesse Calvert <35825273+jesseclvrt@users.noreply.github.com> Date: Fri, 18 May 2018 21:11:31 -0700 Subject: [PATCH 32/35] Poison does not occur when armor blocks an attack Fixes "Spider poison ignored armor" #23681 --- src/monster.cpp | 35 +++++++++++++++++++---------------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/src/monster.cpp b/src/monster.cpp index 74ff91970bf1..ab0edfc5c7a3 100644 --- a/src/monster.cpp +++ b/src/monster.cpp @@ -1055,6 +1055,7 @@ void monster::melee_attack( Creature &target, float accuracy ) bp_hit = dealt_dam.bp_hit; const int total_dealt = dealt_dam.total_damage(); + bool armor_block = false; if( hitspread < 0 ) { // Miss if( u_see_me && !target.in_sleep_state() ) { @@ -1095,6 +1096,7 @@ void monster::melee_attack( Creature &target, float accuracy ) } else { // No damage dealt if( u_see_me ) { + armor_block = true; if( target.is_player() ) { //~ 1$s is attacker name, 2$s is bodypart name in accusative, 3$s is armor name add_msg(_("The %1$s hits your %2$s, but your %3$s protects you."), name().c_str(), @@ -1149,25 +1151,26 @@ void monster::melee_attack( Creature &target, float accuracy ) } const int stab_cut = dealt_dam.type_damage( DT_CUT ) + dealt_dam.type_damage( DT_STAB ); + if (!armor_block) { + if( stab_cut > 0 && has_flag( MF_VENOM ) ) { + target.add_msg_if_player( m_bad, _("You're poisoned!") ); + target.add_effect( effect_poison, 3_minutes ); + } - if( stab_cut > 0 && has_flag( MF_VENOM ) ) { - target.add_msg_if_player( m_bad, _("You're poisoned!") ); - target.add_effect( effect_poison, 3_minutes ); - } - - if( stab_cut > 0 && has_flag( MF_BADVENOM ) ) { - target.add_msg_if_player(m_bad, _("You feel poison flood your body, wracking you with pain...")); - target.add_effect( effect_badpoison, 4_minutes ); - } + if( stab_cut > 0 && has_flag( MF_BADVENOM ) ) { + target.add_msg_if_player(m_bad, _("You feel poison flood your body, wracking you with pain...")); + target.add_effect( effect_badpoison, 4_minutes ); + } - if( stab_cut > 0 && has_flag( MF_PARALYZE ) ) { - target.add_msg_if_player(m_bad, _("You feel poison enter your body!")); - target.add_effect( effect_paralyzepoison, 10_minutes ); - } + if( stab_cut > 0 && has_flag( MF_PARALYZE ) ) { + target.add_msg_if_player(m_bad, _("You feel poison enter your body!")); + target.add_effect( effect_paralyzepoison, 10_minutes ); + } - if( total_dealt > 6 && stab_cut > 0 && has_flag( MF_BLEED ) ) { - // Maybe should only be if DT_CUT > 6... Balance question - target.add_effect( effect_bleed, 6_minutes, bp_hit ); + if( total_dealt > 6 && stab_cut > 0 && has_flag( MF_BLEED ) ) { + // Maybe should only be if DT_CUT > 6... Balance question + target.add_effect( effect_bleed, 6_minutes, bp_hit ); + } } } From 370b3d9547e31ec6de74e56f1a200a7ec916de16 Mon Sep 17 00:00:00 2001 From: Night-Pryanik Date: Sat, 19 May 2018 08:40:16 +0400 Subject: [PATCH 33/35] Disinfectant soaked rags and disinfectant soaked cotton balls transforms into rags and cotton balls, respectively --- data/json/items/comestibles/med.json | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/data/json/items/comestibles/med.json b/data/json/items/comestibles/med.json index f23218d50445..107a4b8f0692 100644 --- a/data/json/items/comestibles/med.json +++ b/data/json/items/comestibles/med.json @@ -1303,7 +1303,8 @@ "head_power" : 1, "torso_power" : 4, "bite" : 0.75, - "move_cost" : 100 + "move_cost" : 100, + "used_up_item" : "rag" } }, { @@ -1320,7 +1321,8 @@ "torso_power" : 1, "limb_power" : 1, "bite" : 0.8, - "move_cost" : 300 + "move_cost" : 300, + "used_up_item" : "cotton_ball" } } ] From a614f51b96b291a26a0dd17c3737fbcb3390f856 Mon Sep 17 00:00:00 2001 From: Cyrano7 Date: Fri, 18 May 2018 23:56:36 -0700 Subject: [PATCH 34/35] Revert 'anti-material' --- data/json/items/magazine/50.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/json/items/magazine/50.json b/data/json/items/magazine/50.json index 73b9c9e7f551..40bead0713f8 100644 --- a/data/json/items/magazine/50.json +++ b/data/json/items/magazine/50.json @@ -14,7 +14,7 @@ "type": "MAGAZINE", "name": "Barrett magazine", "//": "Fully loaded 10 magazine, presumably loaded with the 114.7 gram M33 Ball cartridge, weighs 1868.801 grams. Source inetres.com", - "description": "Standard 10-round box magazine for a Barrett M107A1 anti-material rifle.", + "description": "Standard 10-round box magazine for a Barrett M107A1 anti-materiel rifle.", "weight": 727, "volume": 2, "price": 10000, From 356e24a9f7358f1d4d96e7d4056679eb6d08070b Mon Sep 17 00:00:00 2001 From: Night-Pryanik Date: Sat, 19 May 2018 12:45:59 +0400 Subject: [PATCH 35/35] Replaced location field with use of copy-from Also added location field to the turret_blaze abstract. --- data/json/vehicleparts/turret.json | 16 +--------------- data/mods/Tanks/parts.json | 7 ------- .../blazemod/blaze_autoweapons_parts.json | 1 + data/mods/blazemod/blaze_weapons_parts.json | 19 +------------------ 4 files changed, 3 insertions(+), 40 deletions(-) diff --git a/data/json/vehicleparts/turret.json b/data/json/vehicleparts/turret.json index 6786ea9765fe..4af70478f8bf 100644 --- a/data/json/vehicleparts/turret.json +++ b/data/json/vehicleparts/turret.json @@ -5,6 +5,7 @@ "symbol": "t", "durability": 400, "damage_modifier": 80, + "location": "on_roof", "flags": [ "TURRET", "TOOL_NONE" ] }, { @@ -17,7 +18,6 @@ "color": "dark_gray", "broken_color": "dark_gray", "difficulty": 5, - "location": "on_roof", "breaks_into": [ { "item": "flamethrower", "prob": 50 } ], "requirements": { "install": { @@ -39,7 +39,6 @@ "color": "magenta", "broken_color": "magenta", "difficulty": 6, - "location": "on_roof", "breaks_into": [ { "item": "ftk93", "prob": 50 } ], "requirements": { "install": { @@ -60,7 +59,6 @@ "color": "magenta", "broken_color": "magenta", "difficulty": 5, - "location": "on_roof", "breaks_into": [ { "item": "laser_rifle", "prob": 50 } ], "requirements": { "install": { @@ -81,7 +79,6 @@ "color": "cyan", "broken_color": "cyan", "difficulty": 3, - "location": "on_roof", "breaks_into": [ { "item": "m249", "prob": 50 } ], "requirements": { "install": { @@ -102,7 +99,6 @@ "color": "green", "broken_color": "green", "difficulty": 3, - "location": "on_roof", "breaks_into": [ { "item": "abzats", "prob": 50 } ], "requirements": { "install": { @@ -123,7 +119,6 @@ "color": "green", "broken_color": "green", "difficulty": 4, - "location": "on_roof", "breaks_into": [ { "item": "m2browning", "prob": 50 } ], "requirements": { "install": { @@ -144,7 +139,6 @@ "color": "green", "broken_color": "green", "difficulty": 6, - "location": "on_roof", "breaks_into": [ { "item": "m134", "prob": 50 } ], "requirements": { "install": { @@ -165,7 +159,6 @@ "color": "cyan", "broken_color": "cyan", "difficulty": 4, - "location": "on_roof", "breaks_into": [ { "item": "m1918", "prob": 50 } ], "requirements": { "install": { @@ -186,7 +179,6 @@ "color": "cyan", "broken_color": "cyan", "difficulty": 4, - "location": "on_roof", "breaks_into": [ { "item": "m240", "prob": 50 } ], "requirements": { "install": { @@ -207,7 +199,6 @@ "color": "cyan", "broken_color": "cyan", "difficulty": 4, - "location": "on_roof", "breaks_into": [ { "item": "m60", "prob": 50 } ], "requirements": { "install": { @@ -228,7 +219,6 @@ "color": "green", "broken_color": "green", "difficulty": 5, - "location": "on_roof", "breaks_into": [ { "item": "mark19", "prob": 50 } ], "requirements": { "install": { @@ -249,7 +239,6 @@ "color": "white", "broken_color": "white", "difficulty": 6, - "location": "on_roof", "breaks_into": [ { "item": "rm298", "prob": 50 } ], "requirements": { "install": { @@ -270,7 +259,6 @@ "color": "white", "broken_color": "white", "difficulty": 6, - "location": "on_roof", "breaks_into": [ { "item": "rm614_lmg", "prob": 50 } ], "requirements": { "install": { @@ -291,7 +279,6 @@ "color": "light_blue", "broken_color": "light_blue", "difficulty": 5, - "location": "on_roof", "breaks_into": [ { "item": "plasma_rifle", "prob": 50 } ], "requirements": { "install": { @@ -313,7 +300,6 @@ "color": "blue", "broken_color": "blue", "difficulty": 4, - "location": "on_roof", "breaks_into": [ { "item": "watercannon", "prob": 50 } ], "requirements": { "install": { diff --git a/data/mods/Tanks/parts.json b/data/mods/Tanks/parts.json index 02fb4baedba9..c9dce3c464ce 100644 --- a/data/mods/Tanks/parts.json +++ b/data/mods/Tanks/parts.json @@ -19,7 +19,6 @@ "size": 10, "extend": { "flags": [ "MANUAL" ] }, "breaks_into": [ { "item": "tank_gun_manual", "prob": 50 } ], - "location": "on_roof", "requirements": { "install": { "skills": [ [ "mechanics", 5 ], [ "launcher", 2 ] ], "time": 10000 }, "removal": { "skills": [ [ "mechanics", 3 ] ] } @@ -35,7 +34,6 @@ "size": 50, "extend": { "flags": [ "MANUAL" ] }, "breaks_into": [ { "item": "tank_gun_auto", "prob": 50 } ], - "location": "on_roof", "requirements": { "install": { "skills": [ [ "mechanics", 5 ], [ "launcher", 2 ] ], "time": 10000 }, "removal": { "skills": [ [ "mechanics", 3 ] ] } @@ -51,7 +49,6 @@ "size": 100, "extend": { "flags": [ "NEEDS_BATTERY_MOUNT" ] }, "breaks_into": [ { "item": "tank_gun_rws", "prob": 50 } ], - "location": "on_roof", "requirements": { "install": { "skills": [ [ "mechanics", 5 ], [ "launcher", 3 ], [ "electronics", 3 ] ], "time": 10000 }, "removal": { "skills": [ [ "mechanics", 3 ] ] } @@ -67,7 +64,6 @@ "size": 15, "extend": { "flags": [ "MANUAL" ] }, "breaks_into": [ { "item": "howitzer_gun", "prob": 50 } ], - "location": "on_roof", "requirements": { "install": { "skills": [ [ "mechanics", 5 ], [ "launcher", 3 ] ], "time": 10000 }, "removal": { "skills": [ [ "mechanics", 3 ] ] } @@ -83,7 +79,6 @@ "size": 35, "extend": { "flags": [ "MANUAL" ] }, "breaks_into": [ { "item": "atgm_launcher", "prob": 50 } ], - "location": "on_roof", "requirements": { "install": { "skills": [ [ "mechanics", 5 ], [ "launcher", 3 ] ], "time": 10000 }, "removal": { "skills": [ [ "mechanics", 3 ] ] } @@ -99,7 +94,6 @@ "size": 35, "extend": { "flags": [ "NEEDS_BATTERY_MOUNT" ] }, "breaks_into": [ { "item": "atgm_turret", "prob": 50 } ], - "location": "on_roof", "requirements": { "install": { "skills": [ [ "mechanics", 5 ], [ "launcher", 3 ], [ "electronics", 3 ] ], "time": 10000 }, "removal": { "skills": [ [ "mechanics", 3 ] ] } @@ -115,7 +109,6 @@ "size": 50, "extend": { "flags": [ "MANUAL" ] }, "breaks_into": [ { "item": "30mm_autocannon", "prob": 50 } ], - "location": "on_roof", "requirements": { "install": { "skills": [ [ "mechanics", 5 ], [ "rifle", 3 ] ], "time": 10000 }, "removal": { "skills": [ [ "mechanics", 3 ] ] } diff --git a/data/mods/blazemod/blaze_autoweapons_parts.json b/data/mods/blazemod/blaze_autoweapons_parts.json index da57cc7eacd8..e2a19ff9adca 100644 --- a/data/mods/blazemod/blaze_autoweapons_parts.json +++ b/data/mods/blazemod/blaze_autoweapons_parts.json @@ -8,6 +8,7 @@ "broken_color": "dark_gray", "durability": 400, "damage_modifier": 80, + "location": "on_roof", "flags": [ "FOLDABLE", "TURRET", "NEEDS_BATTERY_MOUNT", "TOOL_NONE" ] }, { diff --git a/data/mods/blazemod/blaze_weapons_parts.json b/data/mods/blazemod/blaze_weapons_parts.json index 7ae30de30baf..ead48e4638d1 100644 --- a/data/mods/blazemod/blaze_weapons_parts.json +++ b/data/mods/blazemod/blaze_weapons_parts.json @@ -6,6 +6,7 @@ "broken_symbol": "#", "durability": 400, "damage_modifier": 80, + "location": "on_roof", "flags": [ "FOLDABLE", "TURRET", "MANUAL", "TOOL_NONE" ] }, { @@ -17,7 +18,6 @@ "color": "light_gray", "broken_color": "light_gray", "difficulty": 4, - "location": "on_roof", "folded_volume": 12, "breaks_into": [ { "item": "bolter", "count": [ 0, 1 ] } ] }, @@ -30,7 +30,6 @@ "color": "brown", "broken_color": "brown", "difficulty": 4, - "location": "on_roof", "folded_volume": 22, "breaks_into": [ { "item": "cangun", "count": [ 0, 1 ] } ] }, @@ -43,7 +42,6 @@ "color": "dark_gray", "broken_color": "dark_gray", "difficulty": 5, - "location": "on_roof", "range": 15, "folded_volume": 15, "breaks_into": [ { "item": "gattler", "count": [ 0, 1 ] } ] @@ -57,7 +55,6 @@ "color": "blue", "broken_color": "blue", "difficulty": 6, - "location": "on_roof", "folded_volume": 12, "breaks_into": [ { "item": "lasgun", "count": [ 0, 1 ] } ] }, @@ -70,7 +67,6 @@ "color": "blue", "broken_color": "blue", "difficulty": 6, - "location": "on_roof", "folded_volume": 12, "breaks_into": [ { "item": "lasgun", "count": [ 0, 1 ] } ] }, @@ -83,7 +79,6 @@ "color": "magenta", "broken_color": "magenta", "difficulty": 6, - "location": "on_roof", "folded_volume": 18, "breaks_into": [ { "item": "lasgunx", "count": [ 0, 1 ] } ] }, @@ -97,7 +92,6 @@ "color": "dark_gray", "broken_color": "dark_gray", "difficulty": 5, - "location": "on_roof", "durability": 400, "power": -5, "range": 12, @@ -114,7 +108,6 @@ "color": "red", "broken_color": "red", "difficulty": 4, - "location": "on_roof", "range": 12, "folded_volume": 19, "breaks_into": [ { "item": "rm451_flamethrower", "count": [ 0, 1 ] } ], @@ -129,7 +122,6 @@ "color": "dark_gray", "broken_color": "dark_gray", "difficulty": 5, - "location": "on_roof", "folded_volume": 10, "breaks_into": [ { "item": "nailer", "count": [ 0, 1 ] } ] }, @@ -142,7 +134,6 @@ "color": "blue", "broken_color": "blue", "difficulty": 5, - "location": "on_roof", "folded_volume": 15, "breaks_into": [ { "item": "needler", "count": [ 0, 1 ] } ] }, @@ -155,7 +146,6 @@ "color": "magenta", "broken_color": "magenta", "difficulty": 5, - "location": "on_roof", "range": 14, "folded_volume": 16, "breaks_into": [ { "item": "railturret", "count": [ 0, 1 ] } ] @@ -170,7 +160,6 @@ "color": "dark_gray", "broken_color": "dark_gray", "difficulty": 5, - "location": "on_roof", "durability": 400, "power": -25, "range": 15, @@ -184,7 +173,6 @@ "type": "vehicle_part", "name": "manual rotary speargun", "item": "rslauncher", - "location": "on_roof", "color": "light_gray", "broken_color": "light_gray", "difficulty": 5, @@ -200,7 +188,6 @@ "color": "brown", "broken_color": "brown", "difficulty": 4, - "location": "on_roof", "folded_volume": 20, "breaks_into": [ { "item": "scorpion", "count": [ 0, 1 ] } ] }, @@ -213,7 +200,6 @@ "color": "blue", "broken_color": "blue", "difficulty": 6, - "location": "on_roof", "folded_volume": 14, "breaks_into": [ { "item": "shardgun", "count": [ 0, 1 ] } ] }, @@ -226,7 +212,6 @@ "color": "light_gray", "broken_color": "light_gray", "difficulty": 3, - "location": "on_roof", "folded_volume": 12, "breaks_into": [ { "item": "slauncher", "count": [ 0, 1 ] } ] }, @@ -239,7 +224,6 @@ "color": "blue", "broken_color": "blue", "difficulty": 3, - "location": "on_roof", "folded_volume": 15, "breaks_into": [ { "item": "teslagun", "count": [ 0, 1 ] } ] }, @@ -252,7 +236,6 @@ "color": "light_gray", "broken_color": "light_gray", "difficulty": 4, - "location": "on_roof", "folded_volume": 15, "breaks_into": [ { "item": "tiharturret", "count": [ 0, 1 ] } ] }