diff --git a/src/combat_ai.cc b/src/combat_ai.cc index 701a718..eeb731e 100644 --- a/src/combat_ai.cc +++ b/src/combat_ai.cc @@ -701,6 +701,30 @@ static int aiPacketWrite(File* stream, AiPacket* ai) return 0; } +// 0x428058 +int combat_ai_num() +{ + return gAiPacketsLength; +} + +// 0x428060 +char* combat_ai_name(int packet_num) +{ + int index; + + if (packet_num < 0 || packet_num >= gAiPacketsLength) { + return NULL; + } + + for (index = 0; index < gAiPacketsLength; index++) { + if (gAiPackets[index].packet_num == packet_num) { + return gAiPackets[index].name; + } + } + + return NULL; +} + // Get ai from object // // 0x4280B4 diff --git a/src/combat_ai.h b/src/combat_ai.h index 12cf21d..1e11dfb 100644 --- a/src/combat_ai.h +++ b/src/combat_ai.h @@ -30,6 +30,8 @@ void aiReset(); int aiExit(); int aiLoad(File* stream); int aiSave(File* stream); +int combat_ai_num(); +char* combat_ai_name(int packet_num); int aiGetAreaAttackMode(Object* obj); int aiGetRunAwayMode(Object* obj); int aiGetBestWeapon(Object* obj); diff --git a/src/critter.cc b/src/critter.cc index 4439eb0..c576549 100644 --- a/src/critter.cc +++ b/src/critter.cc @@ -1396,4 +1396,40 @@ bool _critter_flag_check(int pid, int flag) return (proto->critter.data.flags & flag) != 0; } +// 0x42E6F0 +void critter_flag_set(int pid, int flag) +{ + Proto* proto; + + if (pid == -1) { + return; + } + + if (PID_TYPE(pid) != OBJ_TYPE_CRITTER) { + return; + } + + protoGetProto(pid, &proto); + + proto->critter.data.flags |= flag; +} + +// 0x42E71C +void critter_flag_unset(int pid, int flag) +{ + Proto* proto; + + if (pid == -1) { + return; + } + + if (PID_TYPE(pid) != OBJ_TYPE_CRITTER) { + return; + } + + protoGetProto(pid, &proto); + + proto->critter.data.flags &= ~flag; +} + } // namespace fallout diff --git a/src/critter.h b/src/critter.h index 7423834..ef4f873 100644 --- a/src/critter.h +++ b/src/critter.h @@ -70,6 +70,8 @@ int critterGetMovementPointCostAdjustedForCrippledLegs(Object* critter, int a2); bool critterIsEncumbered(Object* critter); bool critterIsFleeing(Object* a1); bool _critter_flag_check(int pid, int flag); +void critter_flag_set(int pid, int flag); +void critter_flag_unset(int pid, int flag); } // namespace fallout diff --git a/src/game_config.cc b/src/game_config.cc index dde349d..7063ec7 100644 --- a/src/game_config.cc +++ b/src/game_config.cc @@ -125,10 +125,26 @@ bool gameConfigInit(bool isMapper, int argc, char** argv) char* ch = strrchr(executable, '\\'); if (ch != NULL) { *ch = '\0'; - snprintf(gGameConfigFilePath, sizeof(gGameConfigFilePath), "%s\\%s", executable, GAME_CONFIG_FILE_NAME); + if (isMapper) { + snprintf(gGameConfigFilePath, + sizeof(gGameConfigFilePath), + "%s\\%s", + executable, + MAPPER_CONFIG_FILE_NAME); + } else { + snprintf(gGameConfigFilePath, + sizeof(gGameConfigFilePath), + "%s\\%s", + executable, + GAME_CONFIG_FILE_NAME); + } *ch = '\\'; } else { - strcpy(gGameConfigFilePath, GAME_CONFIG_FILE_NAME); + if (isMapper) { + strcpy(gGameConfigFilePath, MAPPER_CONFIG_FILE_NAME); + } else { + strcpy(gGameConfigFilePath, GAME_CONFIG_FILE_NAME); + } } // Read contents of `fallout2.cfg` into config. The values from the file diff --git a/src/game_config.h b/src/game_config.h index a444762..78a1299 100644 --- a/src/game_config.h +++ b/src/game_config.h @@ -5,8 +5,8 @@ namespace fallout { -// The file name of the main config file. #define GAME_CONFIG_FILE_NAME "fallout2.cfg" +#define MAPPER_CONFIG_FILE_NAME "mapper2.cfg" #define GAME_CONFIG_SYSTEM_KEY "system" #define GAME_CONFIG_PREFERENCES_KEY "preferences" diff --git a/src/game_mouse.cc b/src/game_mouse.cc index 9f82fcd..fca7c79 100644 --- a/src/game_mouse.cc +++ b/src/game_mouse.cc @@ -1328,6 +1328,12 @@ int gameMouseGetCursor() return gGameMouseCursor; } +// 0x44C9F0 +void gmouse_set_mapper_mode(int mode) +{ + _gmouse_mapper_mode = mode; +} + // 0x44C9F8 void _gmouse_3d_enable_modes() { diff --git a/src/game_mouse.h b/src/game_mouse.h index fc65633..2b99efe 100644 --- a/src/game_mouse.h +++ b/src/game_mouse.h @@ -86,6 +86,7 @@ void gameMouseRefresh(); void _gmouse_handle_event(int mouseX, int mouseY, int mouseState); int gameMouseSetCursor(int cursor); int gameMouseGetCursor(); +void gmouse_set_mapper_mode(int mode); void gameMouseSetMode(int a1); int gameMouseGetMode(); void gameMouseCycleMode(); diff --git a/src/graph_lib.cc b/src/graph_lib.cc index 08a65dd..6a432bd 100644 --- a/src/graph_lib.cc +++ b/src/graph_lib.cc @@ -52,6 +52,14 @@ unsigned char HighRGB(unsigned char color) return std::max(std::max(r, g), b); } +// 0x44ED98 +int load_lbm_to_buf(const char* path, unsigned char* buffer, int a3, int a4, int a5, int a6, int a7) +{ + // TODO: Incomplete. + + return -1; +} + // 0x44F250 int graphCompress(unsigned char* a1, unsigned char* a2, int a3) { diff --git a/src/graph_lib.h b/src/graph_lib.h index 4364973..8278c6a 100644 --- a/src/graph_lib.h +++ b/src/graph_lib.h @@ -4,6 +4,7 @@ namespace fallout { unsigned char HighRGB(unsigned char color); +int load_lbm_to_buf(const char* path, unsigned char* buffer, int a3, int a4, int a5, int a6, int a7); int graphCompress(unsigned char* a1, unsigned char* a2, int a3); int graphDecompress(unsigned char* a1, unsigned char* a2, int a3); void grayscalePaletteUpdate(int a1, int a2); diff --git a/src/loadsave.cc b/src/loadsave.cc index 90f09c3..a0fa76b 100644 --- a/src/loadsave.cc +++ b/src/loadsave.cc @@ -169,7 +169,6 @@ static int _GameMap2Slot(File* stream); static int _SlotMap2Game(File* stream); static int _mygets(char* dest, File* stream); static int _copy_file(const char* existingFileName, const char* newFileName); -static int _MapDirErase(const char* path, const char* extension); static int _SaveBackup(); static int _RestoreSave(); static int _LoadObjDudeCid(File* stream); @@ -340,9 +339,9 @@ void _InitLoadSave() _slot_cursor = 0; _patches = settings.system.master_patches_path.c_str(); - _MapDirErase("MAPS\\", "SAV"); - _MapDirErase(PROTO_DIR_NAME "\\" CRITTERS_DIR_NAME "\\", PROTO_FILE_EXT); - _MapDirErase(PROTO_DIR_NAME "\\" ITEMS_DIR_NAME "\\", PROTO_FILE_EXT); + MapDirErase("MAPS\\", "SAV"); + MapDirErase(PROTO_DIR_NAME "\\" CRITTERS_DIR_NAME "\\", PROTO_FILE_EXT); + MapDirErase(PROTO_DIR_NAME "\\" ITEMS_DIR_NAME "\\", PROTO_FILE_EXT); configGetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_AUTO_QUICK_SAVE, &quickSaveSlots); if (quickSaveSlots > 0 && quickSaveSlots <= 10) { @@ -353,9 +352,9 @@ void _InitLoadSave() // 0x47B85C void _ResetLoadSave() { - _MapDirErase("MAPS\\", "SAV"); - _MapDirErase(PROTO_DIR_NAME "\\" CRITTERS_DIR_NAME "\\", PROTO_FILE_EXT); - _MapDirErase(PROTO_DIR_NAME "\\" ITEMS_DIR_NAME "\\", PROTO_FILE_EXT); + MapDirErase("MAPS\\", "SAV"); + MapDirErase(PROTO_DIR_NAME "\\" CRITTERS_DIR_NAME "\\", PROTO_FILE_EXT); + MapDirErase(PROTO_DIR_NAME "\\" ITEMS_DIR_NAME "\\", PROTO_FILE_EXT); } // SaveGame @@ -1570,7 +1569,7 @@ static int lsgPerformSaveGame() debugPrint("\nLOADSAVE: ** Error opening save game for writing! **\n"); _RestoreSave(); snprintf(_gmpath, sizeof(_gmpath), "%s\\%s%.2d\\", "SAVEGAME", "SLOT", _slot_cursor + 1); - _MapDirErase(_gmpath, "BAK"); + MapDirErase(_gmpath, "BAK"); _partyMemberUnPrepSave(); backgroundSoundResume(); return -1; @@ -1583,7 +1582,7 @@ static int lsgPerformSaveGame() fileClose(_flptr); _RestoreSave(); snprintf(_gmpath, sizeof(_gmpath), "%s\\%s%.2d\\", "SAVEGAME", "SLOT", _slot_cursor + 1); - _MapDirErase(_gmpath, "BAK"); + MapDirErase(_gmpath, "BAK"); _partyMemberUnPrepSave(); backgroundSoundResume(); return -1; @@ -1597,7 +1596,7 @@ static int lsgPerformSaveGame() fileClose(_flptr); _RestoreSave(); snprintf(_gmpath, sizeof(_gmpath), "%s\\%s%.2d\\", "SAVEGAME", "SLOT", _slot_cursor + 1); - _MapDirErase(_gmpath, "BAK"); + MapDirErase(_gmpath, "BAK"); _partyMemberUnPrepSave(); backgroundSoundResume(); return -1; @@ -1678,7 +1677,7 @@ static int lsgPerformSaveGame() } snprintf(_gmpath, sizeof(_gmpath), "%s\\%s%.2d\\", "SAVEGAME", "SLOT", _slot_cursor + 1); - _MapDirErase(_gmpath, "BAK"); + MapDirErase(_gmpath, "BAK"); gLoadSaveMessageListItem.num = 140; if (messageListGetItem(&gLoadSaveMessageList, &gLoadSaveMessageListItem)) { @@ -1771,7 +1770,7 @@ static int lsgLoadGameInSlot(int slot) } snprintf(_str, sizeof(_str), "%s\\", "MAPS"); - _MapDirErase(_str, "BAK"); + MapDirErase(_str, "BAK"); _proto_dude_update_gender(); // Game Loaded. @@ -2487,7 +2486,7 @@ static int _GameMap2Slot(File* stream) snprintf(_gmpath, sizeof(_gmpath), "%s\\%s%.2d\\", "SAVEGAME", "SLOT", _slot_cursor + 1); - if (_MapDirErase(_gmpath, "SAV") == -1) { + if (MapDirErase(_gmpath, "SAV") == -1) { fileNameListFree(&fileNameList, 0); return -1; } @@ -2566,19 +2565,19 @@ static int _SlotMap2Game(File* stream) snprintf(_str0, sizeof(_str0), "%s\\", PROTO_DIR_NAME "\\" CRITTERS_DIR_NAME); - if (_MapDirErase(_str0, PROTO_FILE_EXT) == -1) { + if (MapDirErase(_str0, PROTO_FILE_EXT) == -1) { debugPrint("LOADSAVE: returning 3\n"); return -1; } snprintf(_str0, sizeof(_str0), "%s\\", PROTO_DIR_NAME "\\" ITEMS_DIR_NAME); - if (_MapDirErase(_str0, PROTO_FILE_EXT) == -1) { + if (MapDirErase(_str0, PROTO_FILE_EXT) == -1) { debugPrint("LOADSAVE: returning 4\n"); return -1; } snprintf(_str0, sizeof(_str0), "%s\\", "MAPS"); - if (_MapDirErase(_str0, "SAV") == -1) { + if (MapDirErase(_str0, "SAV") == -1) { debugPrint("LOADSAVE: returning 5\n"); return -1; } @@ -2749,11 +2748,11 @@ void lsgInit() { char path[COMPAT_MAX_PATH]; snprintf(path, sizeof(path), "%s\\", "MAPS"); - _MapDirErase(path, "SAV"); + MapDirErase(path, "SAV"); } // 0x480040 -static int _MapDirErase(const char* relativePath, const char* extension) +int MapDirErase(const char* relativePath, const char* extension) { char path[COMPAT_MAX_PATH]; snprintf(path, sizeof(path), "%s*.%s", relativePath, extension); diff --git a/src/loadsave.h b/src/loadsave.h index 32e1263..567548e 100644 --- a/src/loadsave.h +++ b/src/loadsave.h @@ -20,6 +20,7 @@ int lsgSaveGame(int mode); int lsgLoadGame(int mode); bool _isLoadingGame(); void lsgInit(); +int MapDirErase(const char* path, const char* extension); int _MapDirEraseFile_(const char* a1, const char* a2); } // namespace fallout diff --git a/src/map.cc b/src/map.cc index 8b07d6f..333935f 100644 --- a/src/map.cc +++ b/src/map.cc @@ -108,7 +108,7 @@ int* gMapLocalVars = NULL; // map_vars // 0x51956C -static int* gMapGlobalVars = NULL; +int* gMapGlobalVars = NULL; // local_vars_num // 0x519570 @@ -116,7 +116,7 @@ int gMapLocalVarsLength = 0; // map_vars_num // 0x519574 -static int gMapGlobalVarsLength = 0; +int gMapGlobalVarsLength = 0; // Current elevation. // diff --git a/src/map.h b/src/map.h index 09247f8..70e8d7f 100644 --- a/src/map.h +++ b/src/map.h @@ -69,7 +69,9 @@ typedef void IsoWindowRefreshProc(Rect* rect); extern int gMapSid; extern int* gMapLocalVars; +extern int* gMapGlobalVars; extern int gMapLocalVarsLength; +extern int gMapGlobalVarsLength; extern int gElevation; extern MessageList gMapMessageList; diff --git a/src/mapper/map_func.cc b/src/mapper/map_func.cc new file mode 100644 index 0000000..b9f297d --- /dev/null +++ b/src/mapper/map_func.cc @@ -0,0 +1,17 @@ +#include "mapper/map_func.h" + +namespace fallout { + +// 0x4825B0 +void setup_map_dirs() +{ + // TODO: Incomplete. +} + +// 0x4826B4 +void copy_proto_lists() +{ + // TODO: Incomplete. +} + +} // namespace fallout diff --git a/src/mapper/map_func.h b/src/mapper/map_func.h new file mode 100644 index 0000000..1292604 --- /dev/null +++ b/src/mapper/map_func.h @@ -0,0 +1,11 @@ +#ifndef FALLOUT_MAPPER_MAP_FUNC_H_ +#define FALLOUT_MAPPER_MAP_FUNC_H_ + +namespace fallout { + +void setup_map_dirs(); +void copy_proto_lists(); + +} // namespace fallout + +#endif /* FALLOUT_MAPPER_MAP_FUNC_H_ */ diff --git a/src/mapper/mapper.cc b/src/mapper/mapper.cc new file mode 100644 index 0000000..8afe3eb --- /dev/null +++ b/src/mapper/mapper.cc @@ -0,0 +1,1495 @@ +#include "mapper/mapper.h" + +#include + +#include "animation.h" +#include "art.h" +#include "color.h" +#include "debug.h" +#include "draw.h" +#include "game.h" +#include "game_mouse.h" +#include "graph_lib.h" +#include "inventory.h" +#include "kb.h" +#include "loadsave.h" +#include "mapper/map_func.h" +#include "mapper/mp_proto.h" +#include "mapper/mp_targt.h" +#include "mapper/mp_text.h" +#include "memory.h" +#include "mouse.h" +#include "object.h" +#include "proto.h" +#include "settings.h" +#include "svga.h" +#include "tile.h" +#include "window_manager.h" +#include "window_manager_private.h" + +namespace fallout { + +static void MapperInit(); +static int mapper_edit_init(int argc, char** argv); +static void mapper_edit_exit(); +static int bookmarkInit(); +static int bookmarkExit(); +static void bookmarkHide(); +static void bookmarkUnHide(); +static int categoryInit(); +static int categoryExit(); +static int categoryHide(); +static int categoryToggleState(); +static int categoryUnhide(); +static bool proto_user_is_librarian(); +static void edit_mapper(); +static void mapper_load_toolbar(int a1, int a2); +static void redraw_toolname(); +static void clear_toolname(); +static void update_high_obj_name(Object* obj); +static int mapper_mark_exit_grid(); +static void mapper_mark_all_exit_grids(); + +// TODO: Underlying menu/pulldown interface wants menu items to be non-const, +// needs some refactoring. + +static char kSeparator[] = ""; + +static char kNew[] = " New "; +static char kOpen[] = " Open "; +static char kSave[] = " Save "; +static char kSaveAs[] = " Save As... "; +static char kInfo[] = " Info "; +static char kOpenFromText[] = " Open From Text "; +static char kQuit[] = " Quit "; + +static char kCreatePattern[] = " Create Pattern "; +static char kUsePattern[] = " Use Pattern "; +static char kMoveMap[] = " Move Map "; +static char kMoveMapElev[] = " Move Map Elev "; +static char kCopyMapElev[] = " Copy Map Elev "; +static char kEditObjDude[] = " Edit Obj_dude "; +static char kFlushCache[] = " Flush Cache "; +static char kToggleAnimStepping[] = " Toggle Anim Stepping "; +static char kFixMapObjectsToPids[] = " Fix map-objects to pids "; +static char kSetBookmark[] = " Set Bookmark "; +static char kToggleBlockObjView[] = " Toggle Block Obj View "; +static char kToggleClickToScroll[] = " Toggle Click-To-Scroll "; +static char kSetExitGridData[] = " Set Exit-Grid Data "; +static char kMarkExitGrids[] = " Mark Exit-Grids "; +static char kMarkAllExitGrids[] = " Mark *ALL* Exit Grids "; +static char kClearMapLevel[] = " *Clear Map Level* "; +static char kCreateAllMapTexts[] = " Create ALL MAP TEXTS "; +static char kRebuildAllMaps[] = " Rebuild ALL MAPS "; + +static char kListAllScripts[] = " List all Scripts "; +static char kSetStartHex[] = " Set Start Hex "; +static char kPlaceSpatialScript[] = " Place Spatial Script "; +static char kDeleteSpatialScript[] = " Delete Spatial Script "; +static char kDeleteAllSpatialScripts[] = " Delete *ALL* Spatial SCRIPTS! "; +static char kCreateScript[] = " Create Script "; +static char kSetMapScript[] = " Set Map Script "; +static char kShowMapScript[] = " Show Map Script "; + +static char kRebuildWeapons[] = " Rebuild Weapons "; +static char kRebuildProtoList[] = " Rebuild Proto List "; +static char kRebuildAll[] = " Rebuild ALL "; +static char kRebuildBinary[] = " Rebuild Binary "; +static char kArtToProtos[] = " Art => New Protos "; +static char kSwapPrototypse[] = " Swap Prototypes "; + +static char kTmpMapName[] = "TMP$MAP#.MAP"; + +// 0x559648 +char* menu_0[] = { + kNew, + kOpen, + kSave, + kSaveAs, + kSeparator, + kInfo, + kOpenFromText, + kQuit, +}; + +// 0x559668 +char* menu_1[] = { + kCreatePattern, + kUsePattern, + kSeparator, + kMoveMap, + kMoveMapElev, + kCopyMapElev, + kSeparator, + kEditObjDude, + kFlushCache, + kToggleAnimStepping, + kFixMapObjectsToPids, + kSetBookmark, + kToggleBlockObjView, + kToggleClickToScroll, + kSetExitGridData, + kMarkExitGrids, + kMarkAllExitGrids, + kClearMapLevel, + kSeparator, + kCreateAllMapTexts, + kRebuildAllMaps, +}; + +// 0x5596BC +char* menu_2[] = { + kListAllScripts, + kSetStartHex, + kPlaceSpatialScript, + kDeleteSpatialScript, + kDeleteAllSpatialScripts, + kCreateScript, + kSetMapScript, + kShowMapScript, +}; + +// 0x5596DC +char* menu_3[] = { + kRebuildWeapons, + kRebuildProtoList, + kRebuildAll, + kRebuildBinary, + kSeparator, + kArtToProtos, + kSwapPrototypse, +}; + +// 0x5596F8 +char** menu_names[] = { + menu_0, + menu_1, + menu_2, + menu_3, +}; + +// 0x559748 +MapTransition mapInfo = { -1, -1, 0, 0 }; + +// 0x559880 +int max_art_buttons = 7; + +// 0x559884 +int art_scale_width = 49; + +// 0x559888 +int art_scale_height = 48; + +// 0x5598A4 +static char* tmp_map_name = kTmpMapName; + +// 0x5598A8 +static int bookmarkWin = -1; + +// 0x5598AC +static int categoryWin = -1; + +// 0x5598B0 +static bool categoryIsHidden = false; + +// 0x6EAA40 +int menu_val_0[8]; + +// 0x6EAA60 +int menu_val_2[8]; + +// 0x6EAA80 +unsigned char e_num[4][19 * 26]; + +// 0x6EC408 +int menu_val_1[21]; + +// 0x6EC468 +unsigned char* art_shape; + +// 0x6EC46C +int to_paint_bid; + +// 0x6EC470 +int edit_bid; + +// 0x6EC474 +int paste_bid; + +// 0x6EC478 +int misc_bid; + +// 0x6EC47C +int tile_bid; + +// 0x6EC480 +int copy_bid; + +// 0x6EC484 +int delete_bid; + +// 0x6EC488 +int wall_bid; + +// 0x6EC48C +int obj_bid; + +// 0x6EC490 +int to_topdown_bid; + +// 0x6EC494 +int roof_bid; + +// 0x6EC498 +int hex_bid; + +// 0x6EC49C +int to_iso_bid; + +// 0x6EC4A0 +int scen_bid; + +// 0x6EC4A4 +int crit_bid; + +// 0x6EC4A8 +unsigned char* tool; + +// 0x6EC4AC +int tool_win; + +// 0x6EC4B0 +int menu_bar; + +// 0x6EC4B4 +unsigned char* lbm_buf; + +// 0x6EC4B8 +unsigned char height_inc_up[18 * 23]; + +// 0x6EC656 +unsigned char height_dec_up[18 * 23]; + +// 0x6EC7F4 +unsigned char height_dec_down[18 * 23]; + +// 0x6EC992 +unsigned char height_inc_down[18 * 23]; + +// 0x6ECB30 +unsigned char obj_down[66 * 13]; + +// 0x6ECE8A +unsigned char to_iso_down[58 * 13]; + +// 0x6ED17C +unsigned char scen_up[66 * 13]; + +// 0x6ED4D6 +unsigned char roof_up[58 * 13]; + +// 0x6ED7C8 +unsigned char crit_down[66 * 13]; + +// 0x6EDB22 +unsigned char obj_up[66 * 13]; + +// 0x6EDE7C +unsigned char crit_up[66 * 13]; + +// 0x6EE1D6 +unsigned char to_topdown_down[58 * 13]; + +// 0x6EE4C8 +unsigned char hex_up[58 * 13]; + +// 0x6EE7BA +unsigned char hex_down[58 * 13]; + +// 0x6EEAAC +unsigned char to_topdown_up[58 * 13]; + +// 0x6EED9E +unsigned char scen_down[66 * 13]; + +// 0x6EF0F8 +unsigned char edec_down[18 * 23]; + +// 0x6EF296 +unsigned char to_iso_up[58 * 13]; + +// 0x6EF588 +unsigned char roof_down[58 * 13]; + +// 0x6EF87A +unsigned char r_up[18 * 23]; + +// 0x6EFA18 +unsigned char einc_down[18 * 23]; + +// 0x6EFBB6 +unsigned char shift_l_up[18 * 23]; + +// 0x6EFD54 +unsigned char edec_up[18 * 23]; + +// 0x6EFEF2 +unsigned char shift_r_up[18 * 23]; + +// 0x6F0090 +unsigned char shift_r_down[18 * 23]; + +// 0x6F022E +unsigned char r_down[18 * 23]; + +// 0x6F03CC +unsigned char einc_up[18 * 23]; + +// 0x6F056A +unsigned char l_down[18 * 23]; + +// 0x6F0708 +unsigned char shift_l_down[18 * 23]; + +// 0x6F08A6 +unsigned char l_up[18 * 23]; + +// 0x6F0A44 +unsigned char to_edit_up[45 * 43]; + +// 0x6F11D3 +unsigned char erase_up[45 * 43]; + +// 0x6F1962 +unsigned char copy_group_up[45 * 43]; + +// 0x6F20F1 +unsigned char to_paint_down[45 * 43]; + +// 0x6F2880 +unsigned char erase_down[45 * 43]; + +// 0x6F300F +unsigned char copy_group_down[45 * 43]; + +// 0x6F379E +unsigned char to_edit_down[45 * 43]; + +// 0x6F3F2D +unsigned char copy_up[49 * 19]; + +// 0x6F42D0 +unsigned char misc_down[53 * 18]; + +// 0x6F4581 +unsigned char wall_down[53 * 18]; + +// 0x6F4832 +unsigned char delete_up[49 * 19]; + +// 0x6F4BD5 +unsigned char edit_up[49 * 19]; + +// 0x6F4F78 +unsigned char tile_up[53 * 18]; + +// 0x6F5229 +unsigned char edit_down[49 * 19]; + +// 0x6F55CC +unsigned char paste_down[49 * 19]; + +// 0x6F596F +unsigned char delete_down[49 * 19]; + +// 0x6F5D12 +unsigned char tile_down[53 * 18]; + +// 0x6F5FC3 +unsigned char copy_down[49 * 19]; + +// 0x6F6366 +unsigned char misc_up[53 * 18]; + +// 0x6F6617 +unsigned char paste_up[49 * 19]; + +// 0x6F69BA +unsigned char to_paint_up[1935]; + +// 0x6F7149 +unsigned char wall_up[53 * 18]; + +// 0x6F73FA +bool draw_mode; + +// 0x6F73FB +bool view_mode; + +// gnw_main +// 0x485DD0 +int mapper_main(int argc, char** argv) +{ + MapperInit(); + + if (mapper_edit_init(argc, argv) == -1) { + mem_check(); + return 0; + } + + edit_mapper(); + mapper_edit_exit(); + mem_check(); + + return 0; +} + +// 0x485E00 +void MapperInit() +{ + menu_val_0[0] = KEY_ALT_N; + menu_val_0[1] = KEY_ALT_O; + menu_val_0[2] = KEY_ALT_S; + menu_val_0[3] = KEY_ALT_A; + menu_val_0[4] = KEY_ESCAPE; + menu_val_0[5] = KEY_ALT_K; + menu_val_0[6] = KEY_ALT_I; + menu_val_0[7] = KEY_ESCAPE; + + menu_val_1[0] = KEY_ALT_U; + menu_val_1[1] = KEY_ALT_Y; + menu_val_1[2] = KEY_ESCAPE; + menu_val_1[3] = KEY_ALT_G; + menu_val_1[4] = 4186; + menu_val_1[5] = 4188; + menu_val_1[6] = KEY_ESCAPE; + menu_val_1[7] = KEY_ALT_B; + menu_val_1[8] = KEY_ALT_E; + menu_val_1[9] = KEY_ALT_D; + menu_val_1[10] = KEY_LOWERCASE_B; + menu_val_1[11] = 2165; + menu_val_1[12] = 3123; + menu_val_1[13] = KEY_ALT_Z; + menu_val_1[14] = 5677; + menu_val_1[15] = 5678; + menu_val_1[16] = 5679; + menu_val_1[17] = 5666; + menu_val_1[18] = KEY_ESCAPE; + menu_val_1[19] = 5406; + menu_val_1[20] = 5405; + + menu_val_2[0] = KEY_LOWERCASE_I; + menu_val_2[1] = 5400; + menu_val_2[2] = KEY_LOWERCASE_S; + menu_val_2[3] = KEY_CTRL_F8; + menu_val_2[4] = 5410; + menu_val_2[5] = KEY_GRAVE; + menu_val_2[6] = KEY_ALT_W; + menu_val_2[7] = 5544; +} + +// 0x485F94 +int mapper_edit_init(int argc, char** argv) +{ + int index; + + if (gameInitWithOptions("FALLOUT Mapper", true, 2, 0, argc, argv) == -1) { + return -1; + } + + tileEnable(); + gmouse_set_mapper_mode(true); + settings.system.executable = "mapper"; + + if (settings.mapper.override_librarian) { + can_modify_protos = true; + target_override_protection(); + } + + setup_map_dirs(); + mapper_load_toolbar(4, 0); + + max_art_buttons = (_scr_size.right - _scr_size.left - 136) / 50; + art_shape = (unsigned char*)internal_malloc(art_scale_height * art_scale_width); + if (art_shape == NULL) { + printf("Can't malloc memory!!\n"); + exit(1); + } + + menu_bar = windowCreate(0, + 0, + rectGetWidth(&_scr_size), + 16, + _colorTable[0], + WINDOW_HIDDEN); + _win_register_menu_bar(menu_bar, + 0, + 0, + rectGetWidth(&_scr_size), + 16, + 260, + _colorTable[8456]); + _win_register_menu_pulldown(menu_bar, + 8, + "FILE", + 289, + 8, + menu_names[0], + 260, + _colorTable[8456]); + _win_register_menu_pulldown(menu_bar, + 40, + "TOOLS", + 303, + 21, + menu_names[1], + 260, + _colorTable[8456]); + _win_register_menu_pulldown(menu_bar, + 80, + "SCRIPTS", + 276, + 8, + menu_names[2], + 260, + _colorTable[8456]); + + if (can_modify_protos) { + _win_register_menu_pulldown(menu_bar, + 130, + "LIBRARIAN", + 292, + 6, + &(menu_1[14]), + 260, + _colorTable[8456]); + } + + tool_win = windowCreate(0, + _scr_size.bottom - 99, + rectGetWidth(&_scr_size), + 100, + 256, + 0); + tool = windowGetBuffer(tool_win); + + lbm_buf = (unsigned char*)internal_malloc(640 * 480); + load_lbm_to_buf("data\\mapper2.lbm", + lbm_buf, + rectGetWidth(&_scr_size), + 0, + 0, + _scr_size.right - _scr_size.left, + 479); + + // + blitBufferToBuffer(lbm_buf + 380 * rectGetWidth(&_scr_size), + rectGetWidth(&_scr_size), + 100, + rectGetWidth(&_scr_size), + tool, + rectGetWidth(&_scr_size)); + + // + blitBufferToBuffer(lbm_buf + 406 * (rectGetWidth(&_scr_size)) + 101, + 18, + 23, + rectGetWidth(&_scr_size), + l_up, + 18); + blitBufferToBuffer(lbm_buf + 253 * (rectGetWidth(&_scr_size)) + 101, + 18, + 23, + rectGetWidth(&_scr_size), + l_down, + 18); + buttonCreate(tool_win, + 101, + 26, + 18, + 23, + -1, + -1, + 45, + -1, + l_up, + l_down, + NULL, + 0); + + // + blitBufferToBuffer(lbm_buf + 406 * (rectGetWidth(&_scr_size)) + 622, + 18, + 23, + rectGetWidth(&_scr_size), + r_up, + 18); + blitBufferToBuffer(lbm_buf + 253 * (rectGetWidth(&_scr_size)) + 622, + 18, + 23, + rectGetWidth(&_scr_size), + r_down, + 18); + buttonCreate(tool_win, + _scr_size.right - 18, + 1, + 18, + 23, + -1, + -1, + 61, + -1, + r_up, + r_down, + NULL, + 0); + + // + blitBufferToBuffer(lbm_buf + 381 * (rectGetWidth(&_scr_size)) + 101, + 18, + 23, + rectGetWidth(&_scr_size), + shift_l_up, + 18); + blitBufferToBuffer(lbm_buf + 228 * (rectGetWidth(&_scr_size)) + 101, + 18, + 23, + rectGetWidth(&_scr_size), + shift_l_down, + 18); + buttonCreate(tool_win, + 101, + 1, + 18, + 23, + -1, + -1, + 95, + -1, + shift_l_up, + shift_l_down, + NULL, + 0); + + // + blitBufferToBuffer(lbm_buf + 381 * (rectGetWidth(&_scr_size)) + 622, + 18, + 23, + rectGetWidth(&_scr_size), + shift_r_up, + 18); + blitBufferToBuffer(lbm_buf + 228 * (rectGetWidth(&_scr_size)) + 622, + 18, + 23, + rectGetWidth(&_scr_size), + shift_r_down, + 18); + buttonCreate(tool_win, + _scr_size.right - 18, + 1, + 18, + 23, + -1, + -1, + 43, + -1, + shift_r_up, + shift_r_down, + NULL, + 0); + + // + for (index = 0; index < max_art_buttons; index++) { + int btn = buttonCreate(tool_win, + index * (art_scale_width + 1) + 121, + 1, + art_scale_width, + art_scale_height, + index + max_art_buttons + 161, + 58, + 160 + index, + -1, + NULL, + NULL, + NULL, + 0); + buttonSetRightMouseCallbacks(btn, 160 + index, -1, NULL, NULL); + } + + // ELEVATION INC + blitBufferToBuffer(lbm_buf + 431 * (rectGetWidth(&_scr_size)) + 1, + 18, + 23, + rectGetWidth(&_scr_size), + einc_up, + 18); + blitBufferToBuffer(lbm_buf + 325 * (rectGetWidth(&_scr_size)) + 1, + 18, + 23, + rectGetWidth(&_scr_size), + einc_down, + 18); + buttonCreate(tool_win, + 1, + 51, + 18, + 23, + -1, + -1, + 329, + -1, + einc_up, + einc_down, + NULL, + 0); + + // ELEVATION DEC + blitBufferToBuffer(lbm_buf + 456 * (rectGetWidth(&_scr_size)) + 1, + 18, + 23, + rectGetWidth(&_scr_size), + edec_up, + 18); + blitBufferToBuffer(lbm_buf + 350 * (rectGetWidth(&_scr_size)) + 1, + 18, + 23, + rectGetWidth(&_scr_size), + edec_down, + 18); + buttonCreate(tool_win, + 1, + 76, + 18, + 23, + -1, + -1, + 337, + -1, + edec_up, + edec_down, + NULL, + 0); + + // ELEVATION + for (index = 0; index < 4; index++) { + blitBufferToBuffer(lbm_buf + 293 * rectGetWidth(&_scr_size) + 19 * index, + 19, + 26, + rectGetWidth(&_scr_size), + e_num[1], + 19); + } + + view_mode = false; + + // + blitBufferToBuffer(lbm_buf + 169 * (rectGetWidth(&_scr_size)) + 64, + 58, + 13, + rectGetWidth(&_scr_size), + to_iso_up, + 58); + blitBufferToBuffer(lbm_buf + 108 * (rectGetWidth(&_scr_size)) + 64, + 58, + 13, + rectGetWidth(&_scr_size), + to_iso_down, + 58); + + // ROOF + blitBufferToBuffer(lbm_buf + 464 * (rectGetWidth(&_scr_size)) + 64, + 58, + 13, + rectGetWidth(&_scr_size), + roof_up, + 58); + blitBufferToBuffer(lbm_buf + 358 * (rectGetWidth(&_scr_size)) + 64, + 58, + 13, + rectGetWidth(&_scr_size), + roof_down, + 58); + roof_bid = buttonCreate(tool_win, + 64, + 69, + 58, + 13, + -1, + -1, + 'r', + 'r', + roof_up, + roof_down, + NULL, + BUTTON_FLAG_0x01); + + if (tileRoofIsVisible()) { + tile_toggle_roof(false); + } + + // HEX + blitBufferToBuffer(lbm_buf + 464 * (rectGetWidth(&_scr_size)) + 64, + 58, + 13, + rectGetWidth(&_scr_size), + hex_up, + 58); + blitBufferToBuffer(lbm_buf + 358 * (rectGetWidth(&_scr_size)) + 64, + 58, + 13, + rectGetWidth(&_scr_size), + hex_down, + 58); + hex_bid = buttonCreate(tool_win, + 64, + 84, + 58, + 13, + -1, + -1, + 350, + 350, + hex_up, + hex_down, + NULL, + BUTTON_FLAG_0x01); + + // OBJ + blitBufferToBuffer(lbm_buf + 434 * (rectGetWidth(&_scr_size)) + 125, + 66, + 13, + rectGetWidth(&_scr_size), + obj_up, + 66); + blitBufferToBuffer(lbm_buf + 328 * (rectGetWidth(&_scr_size)) + 125, + 66, + 13, + rectGetWidth(&_scr_size), + obj_down, + 66); + obj_bid = buttonCreate(tool_win, + 125, + 54, + 66, + 13, + -1, + -1, + 350, + 350, + obj_up, + obj_down, + NULL, + BUTTON_FLAG_0x01); + + // CRIT + blitBufferToBuffer(lbm_buf + 449 * (rectGetWidth(&_scr_size)) + 125, + 66, + 13, + rectGetWidth(&_scr_size), + crit_up, + 66); + blitBufferToBuffer(lbm_buf + 343 * (rectGetWidth(&_scr_size)) + 125, + 66, + 13, + rectGetWidth(&_scr_size), + crit_down, + 66); + crit_bid = buttonCreate(tool_win, + 125, + 69, + 66, + 13, + -1, + -1, + 351, + 351, + crit_up, + crit_down, + NULL, + BUTTON_FLAG_0x01); + + // SCEN + blitBufferToBuffer(lbm_buf + 434 * (rectGetWidth(&_scr_size)) + 194, + 53, + 13, + rectGetWidth(&_scr_size), + scen_up, + 53); + blitBufferToBuffer(lbm_buf + 328 * (rectGetWidth(&_scr_size)) + 194, + 53, + 13, + rectGetWidth(&_scr_size), + scen_down, + 53); + scen_bid = buttonCreate(tool_win, + 125, + 84, + 66, + 13, + -1, + -1, + 352, + 352, + scen_up, + scen_down, + NULL, + BUTTON_FLAG_0x01); + + // WALL + blitBufferToBuffer(lbm_buf + 434 * (rectGetWidth(&_scr_size)) + 194, + 53, + 13, + rectGetWidth(&_scr_size), + wall_up, + 53); + blitBufferToBuffer(lbm_buf + 328 * (rectGetWidth(&_scr_size)) + 194, + 53, + 13, + rectGetWidth(&_scr_size), + wall_down, + 53); + wall_bid = buttonCreate(tool_win, + 194, + 54, + 53, + 13, + -1, + -1, + 355, + 355, + wall_up, + wall_down, + NULL, + BUTTON_FLAG_0x01); + + // MISC + blitBufferToBuffer(lbm_buf + 464 * (rectGetWidth(&_scr_size)) + 194, + 53, + 13, + rectGetWidth(&_scr_size), + misc_up, + 53); + blitBufferToBuffer(lbm_buf + 358 * (rectGetWidth(&_scr_size)) + 194, + 53, + 13, + rectGetWidth(&_scr_size), + misc_down, + 53); + misc_bid = buttonCreate(tool_win, + 194, + 84, + 53, + 13, + -1, + -1, + 355, + 355, + misc_up, + misc_down, + NULL, + BUTTON_FLAG_0x01); + + // HEIGHT INC + blitBufferToBuffer(lbm_buf + 431 * rectGetWidth(&_scr_size) + 251, + 18, + 23, + rectGetWidth(&_scr_size), + height_inc_up, + 18); + blitBufferToBuffer(lbm_buf + 325 * rectGetWidth(&_scr_size) + 251, + 18, + 23, + rectGetWidth(&_scr_size), + height_inc_down, + 18); + buttonCreate(tool_win, + 251, + 51, + 18, + 23, + -1, + -1, + 371, + -1, + height_dec_up, + height_dec_down, + NULL, + 0); + + // HEIGHT DEC + blitBufferToBuffer(lbm_buf + 456 * rectGetWidth(&_scr_size) + 251, + 18, + 23, + rectGetWidth(&_scr_size), + height_dec_up, + 18); + blitBufferToBuffer(lbm_buf + 350 * rectGetWidth(&_scr_size) + 251, + 18, + 23, + rectGetWidth(&_scr_size), + height_dec_down, + 18); + buttonCreate(tool_win, + 251, + 76, + 18, + 23, + -1, + -1, + 371, + -1, + height_dec_up, + height_dec_down, + NULL, + 0); + + // ARROWS + for (index = 0; index < ROTATION_COUNT; index++) { + } + + // COPY + blitBufferToBuffer(lbm_buf + 435 * (rectGetWidth(&_scr_size)) + 325, + 49, + 19, + rectGetWidth(&_scr_size), + copy_up, + 49); + blitBufferToBuffer(lbm_buf + 329 * (rectGetWidth(&_scr_size)) + 325, + 49, + 19, + rectGetWidth(&_scr_size), + copy_down, + 49); + copy_bid = buttonCreate(tool_win, + 325, + 55, + 49, + 19, + -1, + -1, + 99, + -1, + copy_up, + copy_down, + 0, + 0); + + // PASTE + blitBufferToBuffer(lbm_buf + 457 * (rectGetWidth(&_scr_size)) + 325, + 49, + 19, + rectGetWidth(&_scr_size), + paste_up, + 49); + blitBufferToBuffer(lbm_buf + 351 * (rectGetWidth(&_scr_size)) + 325, + 49, + 19, + rectGetWidth(&_scr_size), + paste_down, + 49); + paste_bid = buttonCreate(tool_win, + 325, + 77, + 49, + 19, + -1, + -1, + 67, + -1, + paste_up, + paste_down, + NULL, + 0); + + // EDIT + blitBufferToBuffer(lbm_buf + 435 * (rectGetWidth(&_scr_size)) + 378, + 49, + 19, + rectGetWidth(&_scr_size), + edit_up, + 49); + blitBufferToBuffer(lbm_buf + 329 * (rectGetWidth(&_scr_size)) + 378, + 49, + 19, + rectGetWidth(&_scr_size), + edit_down, + 49); + edit_bid = buttonCreate(tool_win, + 378, + 55, + 49, + 19, + -1, + -1, + 101, + -1, + edit_up, + edit_down, + NULL, + 0); + + // DELETE + blitBufferToBuffer(lbm_buf + 457 * rectGetWidth(&_scr_size) + 378, + 49, + 19, + rectGetWidth(&_scr_size), + delete_up, + 49); + blitBufferToBuffer(lbm_buf + 351 * rectGetWidth(&_scr_size) + 378, + 49, + 19, + rectGetWidth(&_scr_size), + delete_down, + 49); + delete_bid = buttonCreate(tool_win, + 378, + 77, + 49, + 19, + -1, + -1, + 339, + -1, + delete_up, + delete_down, + NULL, + 0); + + draw_mode = false; + + blitBufferToBuffer(lbm_buf + 169 * rectGetWidth(&_scr_size) + 430, + 45, + 43, + rectGetWidth(&_scr_size), + to_edit_up, + 45); + blitBufferToBuffer(lbm_buf + 108 * rectGetWidth(&_scr_size) + 430, + 45, + 43, + rectGetWidth(&_scr_size), + to_edit_down, + 45); + + blitBufferToBuffer(lbm_buf + 169 * rectGetWidth(&_scr_size) + 327, + 45, + 43, + rectGetWidth(&_scr_size), + copy_group_up, + 45); + blitBufferToBuffer(lbm_buf + 108 * rectGetWidth(&_scr_size) + 327, + 45, + 43, + rectGetWidth(&_scr_size), + copy_group_down, + 45); + + blitBufferToBuffer(lbm_buf + 169 * rectGetWidth(&_scr_size) + 379, + 45, + 43, + rectGetWidth(&_scr_size), + erase_up, + 45); + blitBufferToBuffer(lbm_buf + 108 * rectGetWidth(&_scr_size) + 379, + 45, + 43, + rectGetWidth(&_scr_size), + erase_down, + 45); + + internal_free(lbm_buf); + windowRefresh(tool_win); + + if (bookmarkInit() == -1) { + debugPrint("\nbookmarkInit() Failed!"); + } + + if (categoryInit() == -1) { + debugPrint("\ncategoryInit() Failed!"); + } + + tileScrollBlockingDisable(); + tileScrollLimitingDisable(); + init_mapper_protos(); + _map_init(); + target_init(); + mouseShowCursor(); + + if (settings.mapper.rebuild_protos) { + proto_build_all_texts(); + settings.mapper.rebuild_protos = false; + } + + return 0; +} + +// 0x48752C +void mapper_edit_exit() +{ + remove(tmp_map_name); + remove("\\fallout\\cd\\data\\maps\\TMP$MAP#.MAP"); + remove("\\fallout\\cd\\data\\maps\\TMP$MAP#.CFG"); + + MapDirErase("MAPS\\", "SAV"); + + if (can_modify_protos) { + copy_proto_lists(); + + // NOTE: There is a call to an ambiguous function at `0x4B9ACC`, likely + // `proto_save`. + } + + target_exit(); + _map_exit(); + bookmarkExit(); + categoryExit(); + + windowDestroy(tool_win); + tool = NULL; + + windowDestroy(menu_bar); + + internal_free(art_shape); + gameExit(); +} + +// 0x4875B4 +int bookmarkInit() +{ + return 0; +} + +// 0x4875B8 +int bookmarkExit() +{ + if (bookmarkWin == -1) { + return -1; + } + + windowDestroy(bookmarkWin); + bookmarkWin = -1; + + return 0; +} + +// 0x4875E0 +void bookmarkHide() +{ + if (bookmarkWin != -1) { + windowHide(bookmarkWin); + } +} + +// 0x4875F8 +void bookmarkUnHide() +{ + if (bookmarkWin != -1) { + windowShow(bookmarkWin); + } +} + +// 0x4875B4 +int categoryInit() +{ + return 0; +} + +// 0x487700 +int categoryExit() +{ + if (categoryWin == -1) { + return -1; + } + + windowDestroy(categoryWin); + categoryWin = -1; + + return 0; +} + +// 0x487728 +int categoryHide() +{ + if (categoryWin == -1) { + return -1; + } + + windowHide(categoryWin); + categoryIsHidden = true; + + return 0; +} + +// 0x487768 +int categoryToggleState() +{ + if (categoryIsHidden) { + return categoryUnhide(); + } else { + return categoryHide(); + } +} + +// 0x487774 +int categoryUnhide() +{ + if (categoryWin == -1) { + return -1; + } + + windowShow(categoryWin); + categoryIsHidden = false; + + return 0; +} + +// 0x487784 +bool proto_user_is_librarian() +{ + if (!settings.mapper.librarian) { + return false; + } + + can_modify_protos = true; + return true; +} + +// 0x4877D0 +void edit_mapper() +{ + // TODO: Incomplete. +} + +// 0x48AFFC +void mapper_load_toolbar(int a1, int a2) +{ + // TODO: Incomplete. +} + +// 0x48B16C +void print_toolbar_name(int object_type) +{ + Rect rect; + char name[80]; + + rect.left = 0; + rect.top = 0; + rect.right = 0; + rect.bottom = 22; + bufferFill(tool + 2 + 2 * (_scr_size.right - _scr_size.left) + 2, + 96, + _scr_size.right - _scr_size.left + 1, + 19, + _colorTable[21140]); + + sprintf(name, "%s", artGetObjectTypeName(object_type)); + name[0] = toupper(name[0]); + windowDrawText(tool_win, name, 0, 7, 7, _colorTable[32747] | 0x2000000); + windowRefreshRect(tool_win, &rect); +} + +// 0x48B230 +void redraw_toolname() +{ + Rect rect; + + rect.left = _scr_size.right - _scr_size.left - 149; + rect.top = 60; + rect.right = _scr_size.right - _scr_size.left + 1; + rect.bottom = 95; + windowRefreshRect(tool_win, &rect); +} + +// 0x48B278 +void clear_toolname() +{ + windowDrawText(tool_win, "", 120, _scr_size.right - _scr_size.left - 149, 60, 260); + windowDrawText(tool_win, "", 120, _scr_size.right - _scr_size.left - 149, 70, 260); + windowDrawText(tool_win, "", 120, _scr_size.right - _scr_size.left - 149, 80, 260); + redraw_toolname(); +} + +// 0x48B5BC +void update_high_obj_name(Object* obj) +{ + Proto* proto; + + if (protoGetProto(obj->pid, &proto) != -1) { + windowDrawText(tool_win, protoGetName(obj->pid), 120, _scr_size.right - _scr_size.left - 149, 60, 260); + windowDrawText(tool_win, "", 120, _scr_size.right - _scr_size.left - 149, 70, 260); + windowDrawText(tool_win, "", 120, _scr_size.right - _scr_size.left - 149, 80, 260); + redraw_toolname(); + } +} + +// 0x48C604 +int mapper_inven_unwield(Object* obj, int right_hand) +{ + Object* item; + int fid; + + reg_anim_begin(ANIMATION_REQUEST_RESERVED); + + if (right_hand) { + item = critterGetItem2(obj); + } else { + item = critterGetItem1(obj); + } + + if (item != NULL) { + item->flags &= ~OBJECT_IN_ANY_HAND; + } + + animationRegisterAnimate(obj, ANIM_PUT_AWAY, 0); + + fid = buildFid(OBJ_TYPE_CRITTER, obj->fid & 0xFFF, 0, 0, (obj->fid & 0x70000000) >> 28); + animationRegisterSetFid(obj, fid, 0); + + return reg_anim_end(); +} + +// 0x48C678 +int mapper_mark_exit_grid() +{ + int y; + int x; + int tile; + Object* obj; + + for (y = -2000; y != 2000; y += 200) { + for (x = -10; x < 10; x++) { + tile = gGameMouseBouncingCursor->tile + y + x; + + obj = objectFindFirstAtElevation(gElevation); + while (obj != NULL) { + if (isExitGridPid(obj->pid)) { + obj->data.misc.map = mapInfo.map; + obj->data.misc.tile = mapInfo.tile; + obj->data.misc.elevation = mapInfo.elevation; + obj->data.misc.rotation = mapInfo.rotation; + } + obj = objectFindNextAtElevation(); + } + } + } + + return 0; +} + +// 0x48C704 +void mapper_mark_all_exit_grids() +{ + Object* obj; + + obj = objectFindFirstAtElevation(gElevation); + while (obj != NULL) { + if (isExitGridPid(obj->pid)) { + obj->data.misc.map = mapInfo.map; + obj->data.misc.tile = mapInfo.tile; + obj->data.misc.elevation = mapInfo.elevation; + obj->data.misc.rotation = mapInfo.rotation; + } + obj = objectFindNextAtElevation(); + } +} + +} // namespace fallout diff --git a/src/mapper/mapper.h b/src/mapper/mapper.h new file mode 100644 index 0000000..4ae73cf --- /dev/null +++ b/src/mapper/mapper.h @@ -0,0 +1,23 @@ +#ifndef FALLOUT_MAPPER_MAPPER_H_ +#define FALLOUT_MAPPER_MAPPER_H_ + +#include "map.h" +#include "obj_types.h" + +namespace fallout { + +extern MapTransition mapInfo; + +extern int menu_val_0[8]; +extern int menu_val_2[8]; +extern int menu_val_1[21]; +extern unsigned char* tool; +extern int tool_win; + +int mapper_main(int argc, char** argv); +void print_toolbar_name(int object_type); +int mapper_inven_unwield(Object* obj, int right_hand); + +} // namespace fallout + +#endif /* FALLOUT_MAPPER_MAPPER_H_ */ diff --git a/src/mapper/mp_proto.cc b/src/mapper/mp_proto.cc new file mode 100644 index 0000000..513fb57 --- /dev/null +++ b/src/mapper/mp_proto.cc @@ -0,0 +1,438 @@ +#include "mapper/mp_proto.h" + +#include + +#include "color.h" +#include "combat_ai.h" +#include "critter.h" +#include "input.h" +#include "kb.h" +#include "mapper/mp_targt.h" +#include "memory.h" +#include "proto.h" +#include "svga.h" +#include "window_manager.h" +#include "window_manager_private.h" + +namespace fallout { + +#define CRITTER_FLAG_COUNT 10 + +#define YES 0 +#define NO 1 + +static int proto_choose_container_flags(Proto* proto); +static void proto_critter_flags_redraw(int win, int pid); +static int proto_critter_flags_modify(int pid); +static int mp_pick_kill_type(); + +static char kYes[] = "YES"; +static char kNo[] = "NO"; + +// 0x559B94 +static const char* wall_light_strs[] = { + "North/South", + "East/West", + "North Corner", + "South Corner", + "East Corner", + "West Corner", +}; + +// 0x559C50 +static char* yesno[] = { + kYes, + kNo, +}; + +// 0x559C58 +int edit_window_color = 1; + +// 0x559C60 +bool can_modify_protos = false; + +// 0x559C6C +static int critFlagList[CRITTER_FLAG_COUNT] = { + CRITTER_NO_STEAL, + CRITTER_NO_DROP, + CRITTER_NO_LIMBS, + CRITTER_NO_AGE, + CRITTER_NO_HEAL, + CRITTER_INVULNERABLE, + CRITTER_FLAT, + CRITTER_SPECIAL_DEATH, + CRITTER_LONG_LIMBS, + CRITTER_NO_KNOCKBACK, +}; + +// 0x559C94 +static const char* critFlagStrs[CRITTER_FLAG_COUNT] = { + "_Steal", + "_Drop", + "_Limbs", + "_Ages", + "_Heal", + "Invuln.,", + "_Flattens", + "Special", + "Rng", + "_Knock", +}; + +// 0x4922F8 +void init_mapper_protos() +{ + edit_window_color = _colorTable[10570]; + can_modify_protos = target_overriden(); +} + +// 0x492840 +int proto_choose_container_flags(Proto* proto) +{ + int win = windowCreate(320, + 185, + 220, + 205, + edit_window_color, + WINDOW_MOVE_ON_TOP); + if (win == -1) { + return -1; + } + + _win_register_text_button(win, + 10, + 11, + -1, + -1, + -1, + '1', + "Magic Hands Grnd", + 0); + + if ((proto->item.data.container.openFlags & 0x1) != 0) { + windowDrawText(win, + yesno[YES], + 50, + 125, + 15, + _colorTable[32747] | 0x10000); + } else { + windowDrawText(win, + yesno[NO], + 50, + 125, + 15, + _colorTable[32747] | 0x10000); + } + + _win_register_text_button(win, + 10, + 32, + -1, + -1, + -1, + '2', + "Cannot Pick Up", + 0); + + if (_proto_action_can_pickup(proto->pid)) { + windowDrawText(win, + yesno[YES], + 50, + 125, + 36, + _colorTable[32747] | 0x10000); + } else { + windowDrawText(win, + yesno[NO], + 50, + 125, + 36, + _colorTable[32747] | 0x10000); + } + + windowDrawBorder(win); + windowRefresh(win); + + while (1) { + sharedFpsLimiter.mark(); + + int input = inputGetInput(); + if (input == KEY_ESCAPE + || input == KEY_BAR + || input == KEY_RETURN) { + break; + } + + if (input == '1') { + proto->item.data.container.openFlags ^= 0x1; + + if ((proto->item.data.container.openFlags & 0x1) != 0) { + windowDrawText(win, + yesno[YES], + 50, + 125, + 15, + _colorTable[32747] | 0x10000); + } else { + windowDrawText(win, + yesno[NO], + 50, + 125, + 15, + _colorTable[32747] | 0x10000); + } + + windowRefresh(win); + } else if (input == '2') { + proto->item.extendedFlags ^= 0x8000; + + if (_proto_action_can_pickup(proto->pid)) { + windowDrawText(win, + yesno[YES], + 50, + 125, + 36, + _colorTable[32747] | 0x10000); + } else { + windowDrawText(win, + yesno[NO], + 50, + 125, + 36, + _colorTable[32747] | 0x10000); + } + + windowRefresh(win); + } + + renderPresent(); + sharedFpsLimiter.throttle(); + } + + windowDestroy(win); + + return 0; +} + +// 0x495438 +const char* proto_wall_light_str(int flags) +{ + if ((flags & 0x8000000) != 0) { + return wall_light_strs[1]; + } + + if ((flags & 0x10000000) != 0) { + return wall_light_strs[2]; + } + + if ((flags & 0x20000000) != 0) { + return wall_light_strs[3]; + } + + if ((flags & 0x40000000) != 0) { + return wall_light_strs[4]; + } + + if ((flags & 0x80000000) != 0) { + return wall_light_strs[5]; + } + + return wall_light_strs[0]; +} + +// 0x4960B8 +void proto_critter_flags_redraw(int win, int pid) +{ + int index; + int color; + int x = 110; + + for (index = 0; index < CRITTER_FLAG_COUNT; index++) { + if (_critter_flag_check(pid, critFlagList[index])) { + color = _colorTable[992]; + } else { + color = _colorTable[10570]; + } + + windowDrawText(win, critFlagStrs[index], 44, x, 195, color | 0x10000); + x += 48; + } +} + +// 0x496120 +int proto_critter_flags_modify(int pid) +{ + Proto* proto; + int rc; + int flags = 0; + int index; + + if (protoGetProto(pid, &proto) == -1) { + return -1; + } + + rc = win_yes_no("Can't be stolen from?", 340, 200, _colorTable[15855]); + if (rc == -1) { + return -1; + } + + if (rc == 1) { + flags |= CRITTER_NO_STEAL; + } + + rc = win_yes_no("Can't Drop items?", 340, 200, _colorTable[15855]); + if (rc == -1) { + return -1; + } + + if (rc == 1) { + flags |= CRITTER_NO_DROP; + } + + rc = win_yes_no("Can't lose limbs?", 340, 200, _colorTable[15855]); + if (rc == -1) { + return -1; + } + + if (rc == 1) { + flags |= CRITTER_NO_LIMBS; + } + + rc = win_yes_no("Dead Bodies Can't Age?", 340, 200, _colorTable[15855]); + if (rc == -1) { + return -1; + } + + if (rc == 1) { + flags |= CRITTER_NO_AGE; + } + + rc = win_yes_no("Can't Heal by Aging?", 340, 200, _colorTable[15855]); + if (rc == -1) { + return -1; + } + + if (rc == 1) { + flags |= CRITTER_NO_HEAL; + } + + rc = win_yes_no("Is Invlunerable????", 340, 200, _colorTable[15855]); + if (rc == -1) { + return -1; + } + + if (rc == 1) { + flags |= CRITTER_INVULNERABLE; + } + + rc = win_yes_no("Can't Flatten on Death?", 340, 200, _colorTable[15855]); + if (rc == -1) { + return -1; + } + + if (rc == 1) { + flags |= CRITTER_FLAT; + } + + rc = win_yes_no("Has Special Death?", 340, 200, _colorTable[15855]); + if (rc == -1) { + return -1; + } + + if (rc == 1) { + flags |= CRITTER_SPECIAL_DEATH; + } + + rc = win_yes_no("Has Extra Hand-To-Hand Range?", 340, 200, _colorTable[15855]); + if (rc == -1) { + return -1; + } + + if (rc == 1) { + flags |= CRITTER_LONG_LIMBS; + } + + rc = win_yes_no("Can't be knocked back?", 340, 200, _colorTable[15855]); + if (rc == -1) { + return -1; + } + + if (rc == 1) { + flags |= CRITTER_NO_KNOCKBACK; + } + + if (!can_modify_protos) { + win_timed_msg("Can't modify protos!", _colorTable[31744] | 0x10000); + return -1; + } + + for (index = 0; index < CRITTER_FLAG_COUNT; index++) { + if ((critFlagList[index] & flags) != 0) { + critter_flag_set(pid, critFlagList[index]); + } else { + critter_flag_unset(pid, critFlagList[index]); + } + } + + return 0; +} + +// 0x497520 +int mp_pick_kill_type() +{ + char* names[KILL_TYPE_COUNT]; + int index; + + for (index = 0; index < KILL_TYPE_COUNT; index++) { + names[index] = killTypeGetName(index); + } + + return _win_list_select("Kill Type", + names, + KILL_TYPE_COUNT, + NULL, + 50, + 100, + _colorTable[15855]); +} + +// 0x497568 +int proto_pick_ai_packet(int* value) +{ + int count; + char** names; + int index; + int rc; + + count = combat_ai_num(); + if (count <= 0) { + return -1; + } + + names = (char**)internal_malloc(sizeof(char*) * count); + for (index = 0; index < count; index++) { + names[index] = (char*)internal_malloc(strlen(combat_ai_name(index)) + 1); + strcpy(names[index], combat_ai_name(index)); + } + + rc = _win_list_select("AI Packet", + names, + count, + NULL, + 50, + 100, + _colorTable[15855]); + if (rc != -1) { + *value = rc; + } + + for (index = 0; index < count; index++) { + internal_free(names[index]); + } + + internal_free(names); + return 0; +} + +} // namespace fallout diff --git a/src/mapper/mp_proto.h b/src/mapper/mp_proto.h new file mode 100644 index 0000000..e6a520e --- /dev/null +++ b/src/mapper/mp_proto.h @@ -0,0 +1,14 @@ +#ifndef FALLOUT_MAPPER_MP_PROTO_H_ +#define FALLOUT_MAPPER_MP_PROTO_H_ + +namespace fallout { + +extern bool can_modify_protos; + +void init_mapper_protos(); +const char* proto_wall_light_str(int flags); +int proto_pick_ai_packet(int* value); + +} // namespace fallout + +#endif /* FALLOUT_MAPPER_MP_PROTO_H_ */ diff --git a/src/mapper/mp_scrpt.cc b/src/mapper/mp_scrpt.cc new file mode 100644 index 0000000..eb2f9c6 --- /dev/null +++ b/src/mapper/mp_scrpt.cc @@ -0,0 +1,90 @@ +#include "mapper/mp_scrpt.h" + +#include "art.h" +#include "object.h" +#include "scripts.h" +#include "tile.h" + +namespace fallout { + +// 0x49B170 +int map_scr_remove_spatial(int tile, int elevation) +{ + Script* scr; + Object* obj; + Rect rect; + + scr = scriptGetFirstSpatialScript(elevation); + while (scr != NULL) { + if (builtTileGetTile(scr->sp.built_tile) == tile) { + scriptRemove(scr->sid); + + scr = scriptGetFirstSpatialScript(elevation); + continue; + } + + scr = scriptGetNextSpatialScript(); + } + + obj = objectFindFirstAtElevation(elevation); + while (obj != NULL) { + if (obj->tile == tile && buildFid(OBJ_TYPE_INTERFACE, 3, 0, 0, 0) == obj->fid) { + objectDestroy(obj, &rect); + tileWindowRefreshRect(&rect, elevation); + + obj = objectFindFirstAtElevation(elevation); + continue; + } + + obj = objectFindNextAtElevation(); + } + + return 0; +} + +// 0x49B214 +int map_scr_remove_all_spatials() +{ + int elevation; + Script* scr; + Object* obj; + int sid; + + for (elevation = 0; elevation < ELEVATION_COUNT; elevation++) { + scr = scriptGetFirstSpatialScript(elevation); + while (scr != NULL) { + scriptRemove(scr->sid); + + scr = scriptGetFirstSpatialScript(elevation); + } + + obj = objectFindFirstAtElevation(elevation); + while (obj != NULL) { + if (buildFid(OBJ_TYPE_INTERFACE, 3, 0, 0, 0) == obj->fid) { + objectDestroy(obj, NULL); + + obj = objectFindFirstAtElevation(elevation); + continue; + } + + obj = objectFindNextAtElevation(); + } + } + + tileWindowRefresh(); + + for (sid = 0; sid < 15000; sid++) { + if (scriptGetScript(sid, &scr) != -1) { + if (scr->owner != NULL) { + if (scr->owner->pid == 0x500000C) { + scr->owner->sid = -1; + scriptRemove(sid); + } + } + } + } + + return 0; +} + +} // namespace fallout diff --git a/src/mapper/mp_scrpt.h b/src/mapper/mp_scrpt.h new file mode 100644 index 0000000..64c2d59 --- /dev/null +++ b/src/mapper/mp_scrpt.h @@ -0,0 +1,11 @@ +#ifndef FALLOUT_MAPPER_MP_SCRPTR_H_ +#define FALLOUT_MAPPER_MP_SCRPTR_H_ + +namespace fallout { + +int map_scr_remove_spatial(int tile, int elevation); +int map_scr_remove_all_spatials(); + +} // namespace fallout + +#endif /* FALLOUT_MAPPER_MP_SCRPTR_H_ */ diff --git a/src/mapper/mp_targt.cc b/src/mapper/mp_targt.cc new file mode 100644 index 0000000..b654011 --- /dev/null +++ b/src/mapper/mp_targt.cc @@ -0,0 +1,154 @@ +#include "mapper/mp_targt.h" + +#include + +#include "art.h" +#include "game.h" +#include "map.h" +#include "proto.h" +#include "window_manager_private.h" + +namespace fallout { + +// 0x53F354 +static char default_target_path_base[] = "\\fallout2\\dev\\proto\\"; + +// 0x559CD0 +static char* target_path_base = default_target_path_base; + +// 0x559DBC +static bool tgt_overriden = false; + +// 0x49B2F0 +void target_override_protection() +{ + // TODO: Incomplete. +} + +// 0x49B2F0 +bool target_overriden() +{ + return tgt_overriden; +} + +// 0x49B34C +void target_make_path(char* path, int pid) +{ + if (_cd_path_base[0] != '\0' && _cd_path_base[1] == ':') { + strncpy(path, _cd_path_base, 2); + strcat(path, target_path_base); + } else { + strcpy(path, target_path_base); + } + + if (pid != -1) { + strcat(path, artGetObjectTypeName(PID_TYPE(pid))); + } +} + +// 0x49B424 +int target_init() +{ + // TODO: Incomplete. + + return 0; +} + +// 0x49B434 +int target_exit() +{ + // TODO: Incomplete. + + return 0; +} + +// 0x49BD98 +int pick_rot() +{ + int value; + win_get_num_i(&value, + -1, + 5, + false, + "Rotation", + 100, + 100); + return value; +} + +// 0x49BDD0 +int target_pick_global_var(int* value_ptr) +{ + int value; + int rc; + + if (gGameGlobalVarsLength == 0) { + return -1; + } + + rc = win_get_num_i(&value, + 0, + gGameGlobalVarsLength - 1, + false, + "Global Variable Index #:", + 100, + 100); + if (rc == -1) { + return -1; + } + + *value_ptr = value; + return 0; +} + +// 0x49BE20 +int target_pick_map_var(int* value_ptr) +{ + int value; + int rc; + + if (gMapGlobalVarsLength == 0) { + return -1; + } + + rc = win_get_num_i(&value, + 0, + gMapGlobalVarsLength - 1, + false, + "Map Variable Index #:", + 100, + 100); + if (rc == -1) { + return -1; + } + + *value_ptr = value; + return 0; +} + +// 0x49BE70 +int target_pick_local_var(int* value_ptr) +{ + int value; + int rc; + + if (gMapLocalVarsLength == 0) { + return -1; + } + + rc = win_get_num_i(&value, + 0, + gMapLocalVarsLength - 1, + false, + "Local Variable Index #:", + 100, + 100); + if (rc == -1) { + return -1; + } + + *value_ptr = value; + return 0; +} + +} // namespace fallout diff --git a/src/mapper/mp_targt.h b/src/mapper/mp_targt.h new file mode 100644 index 0000000..2a0578d --- /dev/null +++ b/src/mapper/mp_targt.h @@ -0,0 +1,18 @@ +#ifndef FALLOUT_MAPPER_MP_TARGT_H_ +#define FALLOUT_MAPPER_MP_TARGT_H_ + +namespace fallout { + +void target_override_protection(); +bool target_overriden(); +void target_make_path(char* path, int pid); +int target_init(); +int target_exit(); +int pick_rot(); +int target_pick_global_var(int* value_ptr); +int target_pick_map_var(int* value_ptr); +int target_pick_local_var(int* value_ptr); + +} // namespace fallout + +#endif /* FALLOUT_MAPPER_MP_TARGT_H_ */ diff --git a/src/mapper/mp_text.cc b/src/mapper/mp_text.cc new file mode 100644 index 0000000..3d40fb8 --- /dev/null +++ b/src/mapper/mp_text.cc @@ -0,0 +1,13 @@ +#include "mapper/mp_text.h" + +namespace fallout { + +// 0x49DAC4 +int proto_build_all_texts() +{ + // TODO: Incomplete. + + return 0; +} + +} // namespace fallout diff --git a/src/mapper/mp_text.h b/src/mapper/mp_text.h new file mode 100644 index 0000000..b275878 --- /dev/null +++ b/src/mapper/mp_text.h @@ -0,0 +1,10 @@ +#ifndef FALLOUT_MAPPER_MP_TEXT_H_ +#define FALLOUT_MAPPER_MP_TEXT_H_ + +namespace fallout { + +int proto_build_all_texts(); + +} // namespace fallout + +#endif /* FALLOUT_MAPPER_MP_TEXT_H_ */ diff --git a/src/memory.cc b/src/memory.cc index 5f15047..c51ff39 100644 --- a/src/memory.cc +++ b/src/memory.cc @@ -32,7 +32,6 @@ typedef struct MemoryBlockFooter { static void* memoryBlockMallocImpl(size_t size); static void* memoryBlockReallocImpl(void* ptr, size_t size); static void memoryBlockFreeImpl(void* ptr); -static void memoryBlockPrintStats(); static void* mem_prep_block(void* block, size_t size); static void memoryBlockValidate(void* block); @@ -176,10 +175,8 @@ static void memoryBlockFreeImpl(void* ptr) } } -// NOTE: Not used. -// // 0x4C5C5C -static void memoryBlockPrintStats() +void mem_check() { if (gMallocProc == memoryBlockMallocImpl) { debugPrint("Current memory allocated: %6d blocks, %9u bytes total\n", gMemoryBlocksCurrentCount, gMemoryBlocksCurrentSize); diff --git a/src/memory.h b/src/memory.h index facaa54..96b0364 100644 --- a/src/memory.h +++ b/src/memory.h @@ -9,6 +9,7 @@ char* internal_strdup(const char* string); void* internal_malloc(size_t size); void* internal_realloc(void* ptr, size_t size); void internal_free(void* ptr); +void mem_check(); } // namespace fallout diff --git a/src/proto.cc b/src/proto.cc index efb05b0..001217f 100644 --- a/src/proto.cc +++ b/src/proto.cc @@ -24,7 +24,6 @@ namespace fallout { -static int _proto_critter_init(Proto* a1, int a2); static int objectCritterCombatDataRead(CritterCombatData* data, File* stream); static int objectCritterCombatDataWrite(CritterCombatData* data, File* stream); static int _proto_update_gen(Object* obj); @@ -39,8 +38,8 @@ static int _proto_load_pid(int pid, Proto** out_proto); static int _proto_find_free_subnode(int type, Proto** out_ptr); static void _proto_remove_some_list(int type); static void _proto_remove_list(int type); -static int _proto_new_id(int a1); -static int _proto_max_id(int a1); +static int _proto_new_id(int type); +static int _proto_max_id(int type); // 0x50CF3C static char _aProto_0[] = "proto\\"; @@ -187,8 +186,8 @@ static char** _perk_code_strs; // 0x6648BC static char** _critter_stats_list; -// NOTE: Inlined. -void _proto_make_path(char* path, int pid) +// 0x49E270 +void proto_make_path(char* path, int pid) { strcpy(path, _cd_path_base); strcat(path, _proto_path_base); @@ -211,7 +210,7 @@ int _proto_list_str(int pid, char* proto_path) } char path[COMPAT_MAX_PATH]; - _proto_make_path(path, pid); + proto_make_path(path, pid); strcat(path, "\\"); strcat(path, artGetObjectTypeName(PID_TYPE(pid))); strcat(path, ".lst"); @@ -370,32 +369,143 @@ char* protoGetDescription(int pid) return protoGetMessage(pid, PROTOTYPE_MESSAGE_DESCRIPTION); } +// 0x49EB2C +int proto_item_init(Proto* proto, int a2) +{ + int v1 = a2 & 0xFFFFFF; + + proto->item.pid = -1; + proto->item.messageId = 100 * v1; + proto->item.fid = buildFid(OBJ_TYPE_ITEM, v1 - 1, 0, 0, 0); + if (!artExists(proto->item.fid)) { + proto->item.fid = buildFid(OBJ_TYPE_ITEM, 0, 0, 0, 0); + } + proto->item.lightDistance = 0; + proto->item.lightIntensity = 0; + proto->item.flags = 0xA0000008; + proto->item.extendedFlags = 0xA000; + proto->item.sid = -1; + proto->item.type = ITEM_TYPE_MISC; + proto_item_subdata_init(proto, proto->item.type); + proto->item.material = 1; + proto->item.size = 1; + proto->item.weight = 10; + proto->item.cost = 0; + proto->item.inventoryFid = -1; + proto->item.field_80 = '0'; + + return 0; +} + +// 0x49EBFC +int proto_item_subdata_init(Proto* proto, int type) +{ + int index; + + switch (type) { + case ITEM_TYPE_ARMOR: + proto->item.data.armor.armorClass = 0; + + for (index = 0; index < DAMAGE_TYPE_COUNT; index++) { + proto->item.data.armor.damageResistance[index] = 0; + proto->item.data.armor.damageThreshold[index] = 0; + } + + proto->item.data.armor.perk = -1; + proto->item.data.armor.maleFid = -1; + proto->item.data.armor.femaleFid = -1; + break; + case ITEM_TYPE_CONTAINER: + proto->item.data.container.openFlags = 0; + proto->item.data.container.maxSize = 250; + proto->item.extendedFlags |= 0x800; + break; + case ITEM_TYPE_DRUG: + proto->item.data.drug.stat[0] = STAT_STRENGTH; + proto->item.data.drug.stat[1] = -1; + proto->item.data.drug.stat[2] = -1; + proto->item.data.drug.amount[0] = 0; + proto->item.data.drug.amount[1] = 0; + proto->item.data.drug.amount[2] = 0; + proto->item.data.drug.duration1 = 0; + proto->item.data.drug.amount1[0] = 0; + proto->item.data.drug.amount1[1] = 0; + proto->item.data.drug.amount1[2] = 0; + proto->item.data.drug.duration2 = 0; + proto->item.data.drug.amount2[0] = 0; + proto->item.data.drug.amount2[1] = 0; + proto->item.data.drug.amount2[2] = 0; + proto->item.data.drug.addictionChance = 0; + proto->item.data.drug.withdrawalEffect = 0; + proto->item.data.drug.withdrawalOnset = 0; + proto->item.extendedFlags |= 0x1000; + break; + case ITEM_TYPE_WEAPON: + proto->item.data.weapon.animationCode = 0; + proto->item.data.weapon.minDamage = 0; + proto->item.data.weapon.maxDamage = 0; + proto->item.data.weapon.damageType = 0; + proto->item.data.weapon.maxRange1 = 0; + proto->item.data.weapon.maxRange2 = 0; + proto->item.data.weapon.projectilePid = -1; + proto->item.data.weapon.minStrength = 0; + proto->item.data.weapon.actionPointCost1 = 0; + proto->item.data.weapon.actionPointCost2 = 0; + proto->item.data.weapon.criticalFailureType = 0; + proto->item.data.weapon.perk = -1; + proto->item.data.weapon.rounds = 0; + proto->item.data.weapon.caliber = 0; + proto->item.data.weapon.ammoTypePid = -1; + proto->item.data.weapon.ammoCapacity = 0; + proto->item.data.weapon.soundCode = 0; + break; + case ITEM_TYPE_AMMO: + proto->item.data.ammo.caliber = 0; + proto->item.data.ammo.quantity = 20; + proto->item.data.ammo.armorClassModifier = 0; + proto->item.data.ammo.damageResistanceModifier = 0; + proto->item.data.ammo.damageMultiplier = 1; + proto->item.data.ammo.damageDivisor = 1; + break; + case ITEM_TYPE_MISC: + proto->item.data.misc.powerTypePid = -1; + proto->item.data.misc.powerType = 20; + break; + case ITEM_TYPE_KEY: + proto->item.data.key.keyCode = -1; + proto->item.extendedFlags |= 0x1000; + break; + } + + return 0; +} + // 0x49EDB4 -static int _proto_critter_init(Proto* a1, int a2) +int proto_critter_init(Proto* proto, int pid) { if (!_protos_been_initialized) { return -1; } - int v1 = a2 & 0xFFFFFF; + int num = pid & 0xFFFFFF; - a1->pid = -1; - a1->messageId = 100 * v1; - a1->fid = buildFid(OBJ_TYPE_CRITTER, v1 - 1, 0, 0, 0); - a1->critter.lightDistance = 0; - a1->critter.lightIntensity = 0; - a1->critter.flags = 0x20000000; - a1->critter.extendedFlags = 0x6000; - a1->critter.sid = -1; - a1->critter.data.flags = 0; - a1->critter.data.bodyType = 0; - a1->critter.headFid = -1; - a1->critter.aiPacket = 1; - if (!artExists(a1->fid)) { - a1->fid = buildFid(OBJ_TYPE_CRITTER, 0, 0, 0, 0); + proto->pid = -1; + proto->messageId = 100 * num; + proto->fid = buildFid(OBJ_TYPE_CRITTER, num - 1, 0, 0, 0); + proto->critter.lightDistance = 0; + proto->critter.lightIntensity = 0; + proto->critter.flags = 0x20000000; + proto->critter.extendedFlags = 0x6000; + proto->critter.sid = -1; + proto->critter.data.flags = 0; + proto->critter.data.bodyType = 0; + proto->critter.headFid = -1; + proto->critter.aiPacket = 1; + if (!artExists(proto->fid)) { + proto->fid = buildFid(OBJ_TYPE_CRITTER, 0, 0, 0, 0); } - CritterProtoData* data = &(a1->critter.data); + CritterProtoData* data = &(proto->critter.data); data->experience = 60; data->killType = 0; data->damageType = 0; @@ -828,6 +938,165 @@ int _proto_dude_init(const char* path) return 0; } +// 0x49FBBC +int proto_scenery_init(Proto* proto, int pid) +{ + int num = pid & 0xFFFFFF; + + proto->scenery.pid = -1; + proto->scenery.messageId = 100 * num; + proto->scenery.fid = buildFid(OBJ_TYPE_SCENERY, num - 1, 0, 0, 0); + if (!artExists(proto->scenery.fid)) { + proto->scenery.fid = buildFid(OBJ_TYPE_SCENERY, 0, 0, 0, 0); + } + proto->scenery.lightDistance = 0; + proto->scenery.lightIntensity = 0; + proto->scenery.flags = 0; + proto->scenery.extendedFlags = 0x2000; + proto->scenery.sid = -1; + proto->scenery.type = SCENERY_TYPE_GENERIC; + proto_scenery_subdata_init(proto, proto->scenery.type); + proto->scenery.field_2C = -1; + proto->scenery.field_34 = '0'; + + return 0; +} + +// 0x49FC74 +int proto_scenery_subdata_init(Proto* proto, int type) +{ + switch (type) { + case SCENERY_TYPE_DOOR: + proto->scenery.data.door.openFlags = 0; + proto->scenery.extendedFlags |= 0x800; + break; + case SCENERY_TYPE_STAIRS: + proto->scenery.data.stairs.field_0 = -1; + proto->scenery.data.stairs.field_4 = -1; + proto->scenery.extendedFlags |= 0x800; + break; + case SCENERY_TYPE_ELEVATOR: + proto->scenery.data.elevator.type = -1; + proto->scenery.data.elevator.level = -1; + proto->scenery.extendedFlags |= 0x800; + break; + case SCENERY_TYPE_LADDER_UP: + proto->scenery.data.ladder.field_0 = -1; + proto->scenery.extendedFlags |= 0x800; + break; + case SCENERY_TYPE_LADDER_DOWN: + proto->scenery.data.ladder.field_0 = -1; + proto->scenery.extendedFlags |= 0x800; + break; + } + + return 0; +} + +// 0x49FCFC +int proto_wall_init(Proto* proto, int pid) +{ + int num = pid & 0xFFFFFF; + + proto->wall.pid = -1; + proto->wall.messageId = 100 * num; + proto->wall.fid = buildFid(OBJ_TYPE_WALL, num - 1, 0, 0, 0); + if (!artExists(proto->wall.fid)) { + proto->wall.fid = buildFid(OBJ_TYPE_WALL, 0, 0, 0, 0); + } + proto->wall.lightDistance = 0; + proto->wall.lightIntensity = 0; + proto->wall.flags = 0; + proto->wall.extendedFlags = 0x2000; + proto->wall.sid = -1; + proto->wall.material = 1; + + return 0; +} + +// 0x49FD84 +int proto_tile_init(Proto* proto, int pid) +{ + int num = pid & 0xFFFFFF; + + proto->tile.pid = -1; + proto->tile.messageId = 100 * num; + proto->tile.fid = buildFid(OBJ_TYPE_TILE, num - 1, 0, 0, 0); + if (!artExists(proto->tile.fid)) { + proto->tile.fid = buildFid(OBJ_TYPE_TILE, 0, 0, 0, 0); + } + proto->tile.flags = 0; + proto->tile.extendedFlags = 0x2000; + proto->tile.sid = -1; + proto->tile.material = 1; + + return 0; +} + +// 0x49FDFC +int proto_misc_init(Proto* proto, int pid) +{ + int num = pid & 0xFFFFFF; + + proto->misc.pid = -1; + proto->misc.messageId = 100 * num; + proto->misc.fid = buildFid(OBJ_TYPE_MISC, num - 1, 0, 0, 0); + if (!artExists(proto->misc.fid)) { + proto->misc.fid = buildFid(OBJ_TYPE_MISC, 0, 0, 0, 0); + } + proto->misc.lightDistance = 0; + proto->misc.lightIntensity = 0; + proto->misc.flags = 0; + proto->misc.extendedFlags = 0; + + return 0; +} + +// 0x49FE74 +int proto_copy_proto(int srcPid, int dstPid) +{ + int srcType; + int dstType; + Proto* src; + Proto* dst; + + srcType = PID_TYPE(srcPid); + dstType = PID_TYPE(dstPid); + if (srcType != dstType) { + return -1; + } + + if (protoGetProto(srcPid, &src) == -1) { + return -1; + } + + if (protoGetProto(dstPid, &dst) == -1) { + return -1; + } + + memcpy(dst, src, _proto_sizes[srcType]); + dst->pid = dstPid; + + return 0; +} + +// 0x49FEDC +bool proto_is_subtype(Proto* proto, int subtype) +{ + if (subtype == -1) { + return true; + } + + switch (PID_TYPE(proto->pid)) { + case OBJ_TYPE_ITEM: + return proto->item.type == subtype; + case OBJ_TYPE_SCENERY: + return proto->scenery.type == subtype; + } + + return false; +} + // proto_data_member // 0x49FFD8 int protoGetDataMember(int pid, int member, ProtoDataMemberValue* value) @@ -1083,7 +1352,7 @@ int protoInit() compat_mkdir(path); // TODO: Get rid of cast. - _proto_critter_init((Proto*)&gDudeProto, 0x1000000); + proto_critter_init((Proto*)&gDudeProto, 0x1000000); gDudeProto.pid = 0x1000000; gDudeProto.fid = buildFid(OBJ_TYPE_CRITTER, 1, 0, 0, 0); @@ -1202,7 +1471,7 @@ void protoReset() int i; // TODO: Get rid of cast. - _proto_critter_init((Proto*)&gDudeProto, 0x1000000); + proto_critter_init((Proto*)&gDudeProto, 0x1000000); gDudeProto.pid = 0x1000000; gDudeProto.fid = buildFid(OBJ_TYPE_CRITTER, 1, 0, 0, 0); @@ -1251,7 +1520,7 @@ static int _proto_header_load() ptr->max_entries_num = 1; char path[COMPAT_MAX_PATH]; - _proto_make_path(path, index << 24); + proto_make_path(path, index << 24); strcat(path, "\\"); strcat(path, artGetObjectTypeName(index)); strcat(path, ".lst"); @@ -1661,7 +1930,7 @@ int _proto_save_pid(int pid) } char path[260]; - _proto_make_path(path, pid); + proto_make_path(path, pid); strcat(path, "\\"); _proto_list_str(pid, path + strlen(path)); @@ -1682,7 +1951,7 @@ int _proto_save_pid(int pid) static int _proto_load_pid(int pid, Proto** protoPtr) { char path[COMPAT_MAX_PATH]; - _proto_make_path(path, pid); + proto_make_path(path, pid); strcat(path, "\\"); if (_proto_list_str(pid, path + strlen(path)) == -1) { @@ -1761,6 +2030,48 @@ static int _proto_find_free_subnode(int type, Proto** protoPtr) return 0; } +// 0x4A1E90 +int proto_new(int* pid, int type) +{ + Proto* proto; + + if (_proto_find_free_subnode(type, &proto) == -1) { + return -1; + } + + *pid = _proto_new_id(type) | (type << 24); + switch (type) { + case OBJ_TYPE_ITEM: + proto_item_init(proto, *pid); + proto->item.pid = *pid; + break; + case OBJ_TYPE_CRITTER: + proto_critter_init(proto, *pid); + proto->critter.pid = *pid; + break; + case OBJ_TYPE_SCENERY: + proto_scenery_init(proto, *pid); + proto->scenery.pid = *pid; + break; + case OBJ_TYPE_WALL: + proto_wall_init(proto, *pid); + proto->wall.pid = *pid; + break; + case OBJ_TYPE_TILE: + proto_tile_init(proto, *pid); + proto->tile.pid = *pid; + break; + case OBJ_TYPE_MISC: + proto_misc_init(proto, *pid); + proto->misc.pid = *pid; + break; + default: + return -1; + } + + return 0; +} + // Evict top most proto cache block. // // 0x4A2040 @@ -1850,18 +2161,18 @@ int protoGetProto(int pid, Proto** protoPtr) } // 0x4A21DC -static int _proto_new_id(int a1) +static int _proto_new_id(int type) { - int result = _protoLists[a1].max_entries_num; - _protoLists[a1].max_entries_num = result + 1; + int result = _protoLists[type].max_entries_num; + _protoLists[type].max_entries_num = result + 1; return result; } // 0x4A2214 -static int _proto_max_id(int a1) +static int _proto_max_id(int type) { - return _protoLists[a1].max_entries_num; + return _protoLists[type].max_entries_num; } // 0x4A22C0 diff --git a/src/proto.h b/src/proto.h index d6f13e6..f00eef9 100644 --- a/src/proto.h +++ b/src/proto.h @@ -102,7 +102,7 @@ extern char _cd_path_base[COMPAT_MAX_PATH]; extern MessageList gProtoMessageList; extern char* _proto_none_str; -void _proto_make_path(char* path, int pid); +void proto_make_path(char* path, int pid); int _proto_list_str(int pid, char* proto_path); size_t proto_size(int type); bool _proto_action_can_use(int pid); @@ -112,19 +112,30 @@ int _proto_action_can_pickup(int pid); char* protoGetMessage(int pid, int message); char* protoGetName(int pid); char* protoGetDescription(int pid); +int proto_item_init(Proto* proto, int pid); +int proto_item_subdata_init(Proto* proto, int type); +int proto_critter_init(Proto* proto, int pid); void objectDataReset(Object* obj); int objectDataRead(Object* obj, File* stream); int objectDataWrite(Object* obj, File* stream); int _proto_update_init(Object* obj); int _proto_dude_update_gender(); int _proto_dude_init(const char* path); +int proto_scenery_init(Proto* proto, int pid); +int proto_scenery_subdata_init(Proto* proto, int type); +int proto_wall_init(Proto* proto, int pid); +int proto_tile_init(Proto* proto, int pid); +int proto_misc_init(Proto* proto, int pid); +int proto_copy_proto(int srcPid, int dstPid); +bool proto_is_subtype(Proto* proto, int subtype); int protoGetDataMember(int pid, int member, ProtoDataMemberValue* value); int protoInit(); void protoReset(); void protoExit(); int _proto_save_pid(int pid); +int proto_new(int* pid, int type); void _proto_remove_all(); -int protoGetProto(int pid, Proto** out_proto); +int protoGetProto(int pid, Proto** protoPtr); int _ResetPlayer(); static bool isExitGridPid(int pid) diff --git a/src/scripts.cc b/src/scripts.cc index d1b3713..a3abc34 100644 --- a/src/scripts.cc +++ b/src/scripts.cc @@ -913,8 +913,8 @@ int scriptsHandleRequests() } } - if ((gScriptsRequests & SCRIPT_REQUEST_0x02) != 0) { - gScriptsRequests &= ~SCRIPT_REQUEST_0x02; + if ((gScriptsRequests & SCRIPT_REQUEST_TOWN_MAP) != 0) { + gScriptsRequests &= ~SCRIPT_REQUEST_TOWN_MAP; wmTownMap(); } @@ -1128,6 +1128,16 @@ void _scripts_request_combat_locked(CombatStartData* a1) gScriptsRequests |= (SCRIPT_REQUEST_0x0400 | SCRIPT_REQUEST_COMBAT); } +// 0x4A461C +void scripts_request_townmap() +{ + if (isInCombat()) { + _game_user_wants_to_quit = 1; + } + + gScriptsRequests |= SCRIPT_REQUEST_TOWN_MAP; +} + // request_world_map() // 0x4A4644 void scriptsRequestWorldMap() diff --git a/src/scripts.h b/src/scripts.h index c583a2c..7738328 100644 --- a/src/scripts.h +++ b/src/scripts.h @@ -25,7 +25,7 @@ namespace fallout { typedef enum ScriptRequests { SCRIPT_REQUEST_COMBAT = 0x01, - SCRIPT_REQUEST_0x02 = 0x02, + SCRIPT_REQUEST_TOWN_MAP = 0x02, SCRIPT_REQUEST_WORLD_MAP = 0x04, SCRIPT_REQUEST_ELEVATOR = 0x08, SCRIPT_REQUEST_EXPLOSION = 0x10, @@ -175,6 +175,7 @@ int scriptsHandleRequests(); int _scripts_check_state_in_combat(); int scriptsRequestCombat(CombatStartData* combat); void _scripts_request_combat_locked(CombatStartData* combat); +void scripts_request_townmap(); void scriptsRequestWorldMap(); int scriptsRequestElevator(Object* a1, int a2); int scriptsRequestExplosion(int tile, int elevation, int minDamage, int maxDamage); diff --git a/src/tile.cc b/src/tile.cc index af68a39..4bee912 100644 --- a/src/tile.cc +++ b/src/tile.cc @@ -654,6 +654,17 @@ static void tileRefreshGame(Rect* rect, int elevation) gTileWindowRefreshProc(&rectToUpdate); } +// 0x4B1634 +void tile_toggle_roof(bool refresh) +{ + gTileRoofIsVisible = !gTileRoofIsVisible; + + if (refresh) { + // NOTE: Uninline. + tileWindowRefresh(); + } +} + // 0x4B166C int tileRoofIsVisible() { diff --git a/src/tile.h b/src/tile.h index ae227d2..97acc8f 100644 --- a/src/tile.h +++ b/src/tile.h @@ -26,6 +26,7 @@ void tileEnable(); void tileWindowRefreshRect(Rect* rect, int elevation); void tileWindowRefresh(); int tileSetCenter(int tile, int flags); +void tile_toggle_roof(bool refresh); int tileRoofIsVisible(); int tileToScreenXY(int tile, int* x, int* y, int elevation); int tileFromScreenXY(int x, int y, int elevation, bool ignoreBounds = false); diff --git a/src/window_manager.cc b/src/window_manager.cc index f3d01ce..f014295 100644 --- a/src/window_manager.cc +++ b/src/window_manager.cc @@ -1257,19 +1257,18 @@ Button* buttonGetButton(int btn, Window** windowPtr) } // 0x4D7A34 -int _GNW_check_menu_bars(int a1) +int _GNW_check_menu_bars(int input) { if (!gWindowSystemInitialized) { return -1; } - int v1 = a1; for (int index = gWindowsLength - 1; index >= 1; index--) { Window* window = gWindows[index]; if (window->menuBar != NULL) { for (int pulldownIndex = 0; pulldownIndex < window->menuBar->pulldownsLength; pulldownIndex++) { - if (v1 == window->menuBar->pulldowns[pulldownIndex].keyCode) { - v1 = _GNW_process_menu(window->menuBar, pulldownIndex); + if (input == window->menuBar->pulldowns[pulldownIndex].keyCode) { + input = _GNW_process_menu(window->menuBar, pulldownIndex); break; } } @@ -1280,7 +1279,7 @@ int _GNW_check_menu_bars(int a1) } } - return v1; + return input; } // 0x4D69DC diff --git a/src/window_manager.h b/src/window_manager.h index 99180d7..2f76563 100644 --- a/src/window_manager.h +++ b/src/window_manager.h @@ -177,7 +177,7 @@ int windowGetWidth(int win); int windowGetHeight(int win); int windowGetRect(int win, Rect* rect); int _win_check_all_buttons(); -int _GNW_check_menu_bars(int a1); +int _GNW_check_menu_bars(int input); void programWindowSetTitle(const char* title); bool showMesageBox(const char* str); int buttonCreate(int win, int x, int y, int width, int height, int mouseEnterEventCode, int mouseExitEventCode, int mouseDownEventCode, int mouseUpEventCode, unsigned char* up, unsigned char* dn, unsigned char* hover, int flags); diff --git a/src/window_manager_private.cc b/src/window_manager_private.cc index 2f4c706..9c5f04e 100644 --- a/src/window_manager_private.cc +++ b/src/window_manager_private.cc @@ -20,6 +20,7 @@ namespace fallout { /// Maximum number of timed messages. static constexpr int kTimedMsgs = 5; +static int get_num_i(int win, int* value, int max_chars_wcursor, bool clear, bool allow_negative, int x, int y); static void tm_watch_msgs(); static void tm_kill_msg(); static void tm_kill_out_of_order(int queueIndex); @@ -612,7 +613,7 @@ int _win_get_str(char* dest, int length, const char* title, int x, int y) windowRefresh(win); - _win_input_str(win, + int rc = _win_input_str(win, dest, length, 16, @@ -622,7 +623,7 @@ int _win_get_str(char* dest, int length, const char* title, int x, int y) windowDestroy(win); - return 0; + return rc; } // 0x4DB920 @@ -1215,11 +1216,333 @@ int _win_input_str(int win, char* dest, int maxLength, int x, int y, int textCol return 0; } +// 0x4DCD68 +int win_get_num_i(int* value, int min, int max, bool clear, const char* title, int x, int y) +{ + if (!gWindowSystemInitialized) { + return -1; + } + + if (max < min) { + return -1; + } + + if (*value < min) { + *value = min; + } else if (*value > max) { + *value = max; + } + + int original = *value; + int max_chars_wcursor = _calc_max_field_chars_wcursor(min, max); + if (max_chars_wcursor == -1) { + return -1; + } + + int v2 = fontGetMonospacedCharacterWidth() * max_chars_wcursor; + + int width = fontGetStringWidth(title); + if (width < v2) { + width = v2; + } + + width += 16; + if (width < 160) { + width = 160; + } + + int height = 5 * fontGetLineHeight() + 16; + int v3 = (width - v2) / 2; + int v4 = fontGetLineHeight(); + int v5 = fontGetLineHeight() + 2; + + int win = windowCreate(x, y, width, height, 0x100, WINDOW_MODAL | WINDOW_MOVE_ON_TOP); + if (win == -1) { + return -1; + } + + windowDrawBorder(win); + windowFill(win, v3, v4 + 14, v2, v5, 0x100 | 1); + windowDrawText(win, title, width - 16, 8, 8, 0x100 | 5); + + bufferDrawRectShadowed(windowGetBuffer(win), + width, + v3 - 2, + v4 + 12, + v3 + v2 + 1, + v4 + 14 + v5 - 1, + _colorTable[_GNW_wcolor[2]], + _colorTable[_GNW_wcolor[1]]); + + _win_register_text_button(win, + width / 2 - 72, + height - fontGetLineHeight() - 14, + -1, + -1, + -1, + KEY_RETURN, + "Done", + 0); + + _win_register_text_button(win, + width / 2 + 16, + height - fontGetLineHeight() - 14, + -1, + -1, + -1, + KEY_ESCAPE, + "Cancel", + 0); + + char* hint = (char*)internal_malloc(80); + if (hint == NULL) { + return -1; + } + + sprintf(hint, "Please enter a number between %d and %d.", min, max); + windowRefresh(win); + + int rc; + while (1) { + rc = get_num_i(win, value, max_chars_wcursor, clear, min < 0, v3, v4 + 14); + if (*value >= min && *value <= max) { + break; + } + + if (rc == -1) { + break; + } + + _win_msg(hint, x - 70, y + 100, 0x100 | 6); + *value = original; + } + + internal_free(hint); + windowDestroy(win); + + return rc; +} + // 0x4DBD04 int process_pull_down(int win, Rect* rect, char** items, int itemsLength, int foregroundColor, int backgroundColor, MenuBar* menuBar, int pulldownIndex) { - // TODO: Incomplete. - return -1; + if (menuBar != NULL) { + unsigned char* parentWindowBuffer = windowGetWindow(menuBar->win)->buffer; + MenuPulldown* pulldown = &(menuBar->pulldowns[pulldownIndex]); + + int x = pulldown->rect.left; + int y = pulldown->rect.top; + int width = pulldown->rect.right - x + 1; + int height = pulldown->rect.bottom - y + 1; + + int color1 = menuBar->foregroundColor; + if ((color1 & 0xFF00) != 0) { + int colorIndex = (color1 & 0xFF) - 1; + color1 = (color1 & ~0xFFFF) | _colorTable[_GNW_wcolor[colorIndex]]; + } + + int color2 = menuBar->backgroundColor; + if ((color2 & 0xFF00) != 0) { + int colorIndex = (color2 & 0xFF) - 1; + color2 = (color2 & ~0xFFFF) | _colorTable[_GNW_wcolor[colorIndex]]; + } + + _swap_color_buf(parentWindowBuffer + width * y + x, + width, + height, + windowGetWidth(menuBar->win), + color1, + color2); + windowRefreshRect(menuBar->win, &(pulldown->rect)); + } + + unsigned char* windowBuffer = windowGetWindow(win)->buffer; + int width = rectGetWidth(rect); + int height = rectGetHeight(rect); + + int focusedIndex = -1; + int rc; + int mx1; + int my1; + int mx2; + int my2; + int input; + + mouseGetPosition(&mx1, &my1); + + while (1) { + sharedFpsLimiter.mark(); + + input = inputGetInput(); + if (input != -1) { + break; + } + + mouseGetPosition(&mx2, &my2); + + if (mx2 < mx1 - 4 + || mx2 > mx1 + 4 + || my2 < my1 - 4 + || my2 > my1 + 4) { + break; + } + + renderPresent(); + sharedFpsLimiter.throttle(); + } + + while (1) { + sharedFpsLimiter.mark(); + + mouseGetPosition(&mx2, &my2); + + if (input == -2) { + if (menuBar != NULL) { + if (_mouse_click_in(menuBar->rect.left, menuBar->rect.top, menuBar->rect.right, menuBar->rect.bottom)) { + int index; + for (index = 0; index < menuBar->pulldownsLength; index++) { + MenuPulldown* pulldown = &(menuBar->pulldowns[index]); + if (_mouse_click_in(pulldown->rect.left, pulldown->rect.top, pulldown->rect.right, pulldown->rect.bottom)) { + break; + } + } + + if (index < menuBar->pulldownsLength && index != pulldownIndex) { + rc = -2 - index; + break; + } + } + } + } + + if ((mouseGetEvent() & MOUSE_EVENT_LEFT_BUTTON_UP) != 0 + || ((mouseGetEvent() & MOUSE_EVENT_LEFT_BUTTON_DOWN) != 0 + && (mouseGetEvent() & MOUSE_EVENT_LEFT_BUTTON_REPEAT) == 0)) { + if (_mouse_click_in(rect->left, rect->top + 8, rect->right, rect->bottom - 9)) { + rc = focusedIndex; + } else { + rc = -1; + } + break; + } + + bool done = false; + switch (input) { + case KEY_ESCAPE: + rc = -1; + done = true; + break; + case KEY_RETURN: + rc = focusedIndex; + done = true; + break; + case KEY_ARROW_LEFT: + if (menuBar != NULL && pulldownIndex > 0) { + rc = -2 - (pulldownIndex - 1); + done = true; + } + break; + case KEY_ARROW_RIGHT: + if (menuBar != NULL && pulldownIndex < menuBar->pulldownsLength - 1) { + rc = -2 - (pulldownIndex + 1); + done = true; + } + break; + case KEY_ARROW_UP: + while (focusedIndex > 0) { + focusedIndex--; + + if (items[focusedIndex][0] != '\0') { + break; + } + } + input = -3; + break; + case KEY_ARROW_DOWN: + while (focusedIndex < itemsLength - 1) { + focusedIndex++; + + if (items[focusedIndex][0] != '\0') { + break; + } + } + input = -3; + break; + default: + if (mx2 != mx1 || my2 != my1) { + if (_mouse_click_in(rect->left, rect->top + 8, rect->right, rect->bottom - 9)) { + input = (my2 - rect->top - 8) / fontGetLineHeight(); + if (input != -1) { + focusedIndex = items[input][0] != '\0' ? input : -1; + input = -3; + } + } + + mx1 = mx2; + my1 = my2; + } + break; + } + + if (done) { + break; + } + + if (input == -3) { + windowFill(win, 2, 8, width - 4, height - 16, backgroundColor); + _win_text(win, items, itemsLength, width - 4, 2, 8, foregroundColor); + + if (focusedIndex != -1) { + _lighten_buf(windowBuffer + (focusedIndex * fontGetLineHeight() + 8) * width + 2, + width - 4, + fontGetLineHeight(), + width); + } + + windowRefresh(win); + } + + input = inputGetInput(); + + renderPresent(); + sharedFpsLimiter.throttle(); + } + + if (menuBar != NULL) { + unsigned char* parentWindowBuffer = windowGetWindow(menuBar->win)->buffer; + MenuPulldown* pulldown = &(menuBar->pulldowns[pulldownIndex]); + + int x = pulldown->rect.left; + int y = pulldown->rect.top; + int width = pulldown->rect.right - x + 1; + int height = pulldown->rect.bottom - y + 1; + + int color1 = menuBar->foregroundColor; + if ((color1 & 0xFF00) != 0) { + int colorIndex = (color1 & 0xFF) - 1; + color1 = (color1 & ~0xFFFF) | _colorTable[_GNW_wcolor[colorIndex]]; + } + + int color2 = menuBar->backgroundColor; + if ((color2 & 0xFF00) != 0) { + int colorIndex = (color2 & 0xFF) - 1; + color2 = (color2 & ~0xFFFF) | _colorTable[_GNW_wcolor[colorIndex]]; + } + + _swap_color_buf(parentWindowBuffer + width * y + x, + width, + height, + windowGetWidth(menuBar->win), + color1, + color2); + windowRefreshRect(menuBar->win, &(pulldown->rect)); + + renderPresent(); + } + + windowDestroy(win); + + return rc; } // 0x4DC930 @@ -1285,6 +1608,116 @@ size_t _calc_max_field_chars_wcursor(int value1, int value2) return std::max(len1, len2) + 1; } +// 0x4DD0AC +int get_num_i(int win, int* value, int max_chars_wcursor, bool clear, bool allow_negative, int x, int y) +{ + bool first_press = false; + + Window* window = windowGetWindow(win); + if (window == NULL) { + return -1; + } + + int original = *value; + + int width = max_chars_wcursor * fontGetMonospacedCharacterWidth(); + int height = fontGetLineHeight(); + + char* string = (char*)internal_malloc(max_chars_wcursor + 1); + + if (clear) { + string[0] = '\0'; + } else { + snprintf(string, + max_chars_wcursor + 1, + "%d", + *value); + } + + int cursorPos = strlen(string); + string[cursorPos] = '_'; + string[cursorPos + 1] = '\0'; + + windowDrawText(win, string, width, x, y, 0x100 | 4); + + Rect rect; + rect.left = x; + rect.top = y; + rect.right = x + width; + rect.bottom = y + height; + windowRefreshRect(win, &rect); + + bool done = false; + while (cursorPos <= max_chars_wcursor && !done) { + sharedFpsLimiter.mark(); + + int input = inputGetInput(); + if (input == KEY_RETURN) { + done = true; + } else if (input == KEY_BACKSPACE) { + if (cursorPos > 0) { + int stringWidth = fontGetStringWidth(string); + if (first_press) { + string[0] = '_'; + string[1] = '\0'; + cursorPos = 1; + first_press = false; + } else { + string[cursorPos - 1] = '_'; + string[cursorPos] = '\0'; + cursorPos--; + } + + windowFill(win, x, y, stringWidth, height, 0x100 | 1); + windowDrawText(win, string, width, x, y, 0x100 | 4); + windowRefreshRect(win, &rect); + } + } else if (input == KEY_ESCAPE) { + *value = original; + internal_free(string); + + return -1; + } else if (input == KEY_ARROW_LEFT) { + if (cursorPos > 0) { + int stringWidth = fontGetStringWidth(string); + string[cursorPos - 1] = '_'; + string[cursorPos] = '\0'; + windowFill(win, x, y, stringWidth, height, 0x100 | 1); + windowDrawText(win, string, width, x, y, 0x100 | 4); + windowRefreshRect(win, &rect); + + first_press = false; + cursorPos--; + } + } else { + if (cursorPos != max_chars_wcursor - 1) { + if ((input == '-' && allow_negative) + || (input >= '0' && input <= '9')) { + string[cursorPos] = input; + string[cursorPos + 1] = '_'; + string[cursorPos + 2] = '\0'; + + int stringWidth = fontGetStringWidth(string); + windowFill(win, x, y, stringWidth, height, 0x100 | 1); + windowDrawText(win, string, width, x, y, 0x100 | 4); + windowRefreshRect(win, &rect); + + first_press = false; + cursorPos++; + } + } + } + + renderPresent(); + sharedFpsLimiter.throttle(); + } + + *value = atoi(string); + internal_free(string); + + return 0; +} + // 0x4DD3EC void _GNW_intr_init() { diff --git a/src/window_manager_private.h b/src/window_manager_private.h index c4e00e4..cfdecc7 100644 --- a/src/window_manager_private.h +++ b/src/window_manager_private.h @@ -30,6 +30,7 @@ int _win_width_needed(char** fileNameList, int fileNameListLength); int _win_input_str(int win, char* dest, int maxLength, int x, int y, int textColor, int backgroundColor); int process_pull_down(int win, Rect* rect, char** items, int itemsLength, int a5, int a6, MenuBar* menuBar, int pulldownIndex); int _GNW_process_menu(MenuBar* menuBar, int pulldownIndex); +int win_get_num_i(int* value, int min, int max, bool clear, const char* title, int x, int y); size_t _calc_max_field_chars_wcursor(int value1, int value2); void _GNW_intr_init(); void _GNW_intr_exit();