Merge remote-tracking branch 'alex/main' into use_delay_ms

This commit is contained in:
Vasilii Rogin 2023-04-24 17:18:34 +03:00
commit 0f1d71de88
37 changed files with 884 additions and 412 deletions

View File

@ -58,7 +58,7 @@ static int audioSoundDecoderReadHandler(void* data, void* buffer, unsigned int s
// AudioOpen // AudioOpen
// 0x41A2EC // 0x41A2EC
int audioOpen(const char* fname, int flags) int audioOpen(const char* fname, int* channels, int* sampleRate)
{ {
char path[80]; char path[80];
snprintf(path, sizeof(path), "%s", fname); snprintf(path, sizeof(path), "%s", fname);
@ -70,28 +70,7 @@ int audioOpen(const char* fname, int flags)
compression = 0; compression = 0;
} }
char mode[4]; File* stream = fileOpen(path, "rb");
memset(mode, 0, 4);
// NOTE: Original implementation is slightly different, it uses separate
// variable to track index where to set 't' and 'b'.
char* pm = mode;
if (flags & 1) {
*pm++ = 'w';
} else if (flags & 2) {
*pm++ = 'w';
*pm++ = '+';
} else {
*pm++ = 'r';
}
if (flags & 0x100) {
*pm++ = 't';
} else if (flags & 0x200) {
*pm++ = 'b';
}
File* stream = fileOpen(path, mode);
if (stream == NULL) { if (stream == NULL) {
debugPrint("AudioOpen: Couldn't open %s for read\n", path); debugPrint("AudioOpen: Couldn't open %s for read\n", path);
return -1; return -1;
@ -121,6 +100,9 @@ int audioOpen(const char* fname, int flags)
audioFile->flags |= AUDIO_COMPRESSED; audioFile->flags |= AUDIO_COMPRESSED;
audioFile->soundDecoder = soundDecoderInit(audioSoundDecoderReadHandler, audioFile->stream, &(audioFile->channels), &(audioFile->sampleRate), &(audioFile->fileSize)); audioFile->soundDecoder = soundDecoderInit(audioSoundDecoderReadHandler, audioFile->stream, &(audioFile->channels), &(audioFile->sampleRate), &(audioFile->fileSize));
audioFile->fileSize *= 2; audioFile->fileSize *= 2;
*channels = audioFile->channels;
*sampleRate = audioFile->sampleRate;
} else { } else {
audioFile->fileSize = fileGetSize(stream); audioFile->fileSize = fileGetSize(stream);
} }

View File

@ -5,7 +5,7 @@ namespace fallout {
typedef bool(AudioQueryCompressedFunc)(char* filePath); typedef bool(AudioQueryCompressedFunc)(char* filePath);
int audioOpen(const char* fname, int mode); int audioOpen(const char* fname, int* channels, int* sampleRate);
int audioClose(int handle); int audioClose(int handle);
int audioRead(int handle, void* buffer, unsigned int size); int audioRead(int handle, void* buffer, unsigned int size);
long audioSeek(int handle, long offset, int origin); long audioSeek(int handle, long offset, int origin);

View File

@ -57,7 +57,7 @@ static int audioFileSoundDecoderReadHandler(void* data, void* buffer, unsigned i
} }
// 0x41A88C // 0x41A88C
int audioFileOpen(const char* fname, int flags) int audioFileOpen(const char* fname, int* channels, int* sampleRate)
{ {
char path[COMPAT_MAX_PATH]; char path[COMPAT_MAX_PATH];
strcpy(path, fname); strcpy(path, fname);
@ -69,28 +69,7 @@ int audioFileOpen(const char* fname, int flags)
compression = 0; compression = 0;
} }
char mode[4]; FILE* stream = compat_fopen(path, "rb");
memset(mode, '\0', 4);
// NOTE: Original implementation is slightly different, it uses separate
// variable to track index where to set 't' and 'b'.
char* pm = mode;
if (flags & 0x01) {
*pm++ = 'w';
} else if (flags & 0x02) {
*pm++ = 'w';
*pm++ = '+';
} else {
*pm++ = 'r';
}
if (flags & 0x0100) {
*pm++ = 't';
} else if (flags & 0x0200) {
*pm++ = 'b';
}
FILE* stream = compat_fopen(path, mode);
if (stream == NULL) { if (stream == NULL) {
return -1; return -1;
} }
@ -119,6 +98,9 @@ int audioFileOpen(const char* fname, int flags)
audioFile->flags |= AUDIO_FILE_COMPRESSED; audioFile->flags |= AUDIO_FILE_COMPRESSED;
audioFile->soundDecoder = soundDecoderInit(audioFileSoundDecoderReadHandler, audioFile->stream, &(audioFile->channels), &(audioFile->sampleRate), &(audioFile->fileSize)); audioFile->soundDecoder = soundDecoderInit(audioFileSoundDecoderReadHandler, audioFile->stream, &(audioFile->channels), &(audioFile->sampleRate), &(audioFile->fileSize));
audioFile->fileSize *= 2; audioFile->fileSize *= 2;
*channels = audioFile->channels;
*sampleRate = audioFile->sampleRate;
} else { } else {
audioFile->fileSize = getFileSize(stream); audioFile->fileSize = getFileSize(stream);
} }

View File

@ -5,7 +5,7 @@ namespace fallout {
typedef bool(AudioFileQueryCompressedFunc)(char* filePath); typedef bool(AudioFileQueryCompressedFunc)(char* filePath);
int audioFileOpen(const char* fname, int flags); int audioFileOpen(const char* fname, int* channels, int* sampleRate);
int audioFileClose(int handle); int audioFileClose(int handle);
int audioFileRead(int handle, void* buf, unsigned int size); int audioFileRead(int handle, void* buf, unsigned int size);
long audioFileSeek(int handle, long offset, int origin); long audioFileSeek(int handle, long offset, int origin);

View File

@ -168,7 +168,7 @@ static bool _combat_call_display = false;
// Accuracy modifiers for hit locations. // Accuracy modifiers for hit locations.
// //
// 0x510954 // 0x510954
static const int _hit_location_penalty[HIT_LOCATION_COUNT] = { static int hit_location_penalty_default[HIT_LOCATION_COUNT] = {
-40, -40,
-30, -30,
-30, -30,
@ -180,6 +180,8 @@ static const int _hit_location_penalty[HIT_LOCATION_COUNT] = {
0, 0,
}; };
static int hit_location_penalty[HIT_LOCATION_COUNT];
// Critical hit tables for every kill type. // Critical hit tables for every kill type.
// //
// 0x510978 // 0x510978
@ -2029,6 +2031,7 @@ int combatInit()
burstModInit(); burstModInit();
unarmedInit(); unarmedInit();
damageModInit(); damageModInit();
combat_reset_hit_location_penalty();
return 0; return 0;
} }
@ -2058,6 +2061,7 @@ void combatReset()
// SFALL // SFALL
criticalsReset(); criticalsReset();
combat_reset_hit_location_penalty();
} }
// 0x420E14 // 0x420E14
@ -3831,7 +3835,7 @@ static int attackCompute(Attack* attack)
roll = _compute_spray(attack, accuracy, &ammoQuantity, &v26, anim); roll = _compute_spray(attack, accuracy, &ammoQuantity, &v26, anim);
} else { } else {
int chance = critterGetStat(attack->attacker, STAT_CRITICAL_CHANCE); int chance = critterGetStat(attack->attacker, STAT_CRITICAL_CHANCE);
roll = randomRoll(accuracy, chance - _hit_location_penalty[attack->defenderHitLocation], NULL); roll = randomRoll(accuracy, chance - hit_location_penalty[attack->defenderHitLocation], NULL);
} }
if (roll == ROLL_FAILURE) { if (roll == ROLL_FAILURE) {
@ -4417,9 +4421,9 @@ static int attackDetermineToHit(Object* attacker, int tile, Object* defender, in
} }
if (isRangedWeapon) { if (isRangedWeapon) {
accuracy += _hit_location_penalty[hitLocation]; accuracy += hit_location_penalty[hitLocation];
} else { } else {
accuracy += _hit_location_penalty[hitLocation] / 2; accuracy += hit_location_penalty[hitLocation] / 2;
} }
if (defender != NULL && (defender->flags & OBJECT_MULTIHEX) != 0) { if (defender != NULL && (defender->flags & OBJECT_MULTIHEX) != 0) {
@ -6798,4 +6802,27 @@ static void damageModCalculateYaam(DamageCalculationContext* context)
} }
} }
int combat_get_hit_location_penalty(int hit_location)
{
if (hit_location >= 0 && hit_location < HIT_LOCATION_COUNT) {
return hit_location_penalty[hit_location];
} else {
return 0;
}
}
void combat_set_hit_location_penalty(int hit_location, int penalty)
{
if (hit_location >= 0 && hit_location < HIT_LOCATION_COUNT) {
hit_location_penalty[hit_location] = penalty;
}
}
void combat_reset_hit_location_penalty()
{
for (int hit_location = 0; hit_location < HIT_LOCATION_COUNT; hit_location++) {
hit_location_penalty[hit_location] = hit_location_penalty_default[hit_location];
}
}
} // namespace fallout } // namespace fallout

View File

@ -71,6 +71,9 @@ int unarmedGetKickHitMode(bool isSecondary);
bool unarmedIsPenetrating(int hitMode); bool unarmedIsPenetrating(int hitMode);
bool damageModGetBonusHthDamageFix(); bool damageModGetBonusHthDamageFix();
bool damageModGetDisplayBonusDamage(); bool damageModGetDisplayBonusDamage();
int combat_get_hit_location_penalty(int hit_location);
void combat_set_hit_location_penalty(int hit_location, int penalty);
void combat_reset_hit_location_penalty();
static inline bool isInCombat() static inline bool isInCombat()
{ {

View File

@ -289,7 +289,7 @@ bool configRead(Config* config, const char* filePath, bool isDb)
} else { } else {
FILE* stream = compat_fopen(filePath, "rt"); FILE* stream = compat_fopen(filePath, "rt");
if (stream != NULL) { if (stream != NULL) {
while (fgets(string, sizeof(string), stream) != NULL) { while (compat_fgets(string, sizeof(string), stream) != NULL) {
configParseLine(config, string); configParseLine(config, string);
} }

View File

@ -447,7 +447,7 @@ int dictionaryLoad(FILE* stream, Dictionary* dictionary, int a3)
return -1; return -1;
} }
if (fgets(entry->key, keyLength + 1, stream) == NULL) { if (compat_fgets(entry->key, keyLength + 1, stream) == NULL) {
return -1; return -1;
} }

View File

@ -157,7 +157,7 @@ static int _gsound_speech_volume_get_set(int volume);
static void speechPause(); static void speechPause();
static void speechResume(); static void speechResume();
static void _gsound_bkg_proc(); static void _gsound_bkg_proc();
static int gameSoundFileOpen(const char* fname, int access); static int gameSoundFileOpen(const char* fname, int* channels, int* sampleRate);
static long _gsound_write_(); static long _gsound_write_();
static long gameSoundFileTellNotImplemented(int handle); static long gameSoundFileTellNotImplemented(int handle);
static int gameSoundFileWrite(int handle, const void* buf, unsigned int size); static int gameSoundFileWrite(int handle, const void* buf, unsigned int size);
@ -1548,12 +1548,8 @@ void _gsound_bkg_proc()
} }
// 0x451A08 // 0x451A08
int gameSoundFileOpen(const char* fname, int flags) int gameSoundFileOpen(const char* fname, int* channels, int* sampleRate)
{ {
if ((flags & 2) != 0) {
return -1;
}
File* stream = fileOpen(fname, "rb"); File* stream = fileOpen(fname, "rb");
if (stream == NULL) { if (stream == NULL) {
return -1; return -1;

View File

@ -2616,4 +2616,37 @@ static void sidePanelsDraw(const char* path, int win, bool isLeading)
internal_free(image); internal_free(image);
} }
// NOTE: Follows Sfall implementation of `GetCurrentAttackMode`. It slightly
// differs from `interfaceGetCurrentHitMode` (can return one of `reload` hit
// modes, the default is `punch`).
//
// 0x45EF6C
bool interface_get_current_attack_mode(int* hit_mode)
{
if (gInterfaceBarWindow == -1) {
return false;
}
switch (gInterfaceItemStates[gInterfaceCurrentHand].action) {
case INTERFACE_ITEM_ACTION_PRIMARY_AIMING:
case INTERFACE_ITEM_ACTION_PRIMARY:
*hit_mode = gInterfaceItemStates[gInterfaceCurrentHand].primaryHitMode;
break;
case INTERFACE_ITEM_ACTION_SECONDARY_AIMING:
case INTERFACE_ITEM_ACTION_SECONDARY:
*hit_mode = gInterfaceItemStates[gInterfaceCurrentHand].secondaryHitMode;
break;
case INTERFACE_ITEM_ACTION_RELOAD:
*hit_mode = gInterfaceCurrentHand == HAND_LEFT
? HIT_MODE_LEFT_WEAPON_RELOAD
: HIT_MODE_RIGHT_WEAPON_RELOAD;
break;
default:
*hit_mode = HIT_MODE_PUNCH;
break;
}
return true;
}
} // namespace fallout } // namespace fallout

View File

@ -69,6 +69,7 @@ void interfaceBarEndButtonsRenderRedLights();
int indicatorBarRefresh(); int indicatorBarRefresh();
bool indicatorBarShow(); bool indicatorBarShow();
bool indicatorBarHide(); bool indicatorBarHide();
bool interface_get_current_attack_mode(int* hit_mode);
unsigned char* customInterfaceBarGetBackgroundImageData(); unsigned char* customInterfaceBarGetBackgroundImageData();

View File

@ -3268,4 +3268,29 @@ bool ProgramValue::isEmpty()
return true; return true;
} }
// Matches Sfall implementation.
bool ProgramValue::isInt()
{
return opcode == VALUE_TYPE_INT;
}
// Matches Sfall implementation.
bool ProgramValue::isFloat()
{
return opcode == VALUE_TYPE_FLOAT;
}
// Matches Sfall implementation.
float ProgramValue::asFloat()
{
switch (opcode) {
case VALUE_TYPE_INT:
return static_cast<float>(integerValue);
case VALUE_TYPE_FLOAT:
return floatValue;
default:
return 0.0;
}
}
} // namespace fallout } // namespace fallout

View File

@ -149,6 +149,9 @@ typedef struct ProgramValue {
}; };
bool isEmpty(); bool isEmpty();
bool isInt();
bool isFloat();
float asFloat();
} ProgramValue; } ProgramValue;
typedef std::vector<ProgramValue> ProgramStack; typedef std::vector<ProgramValue> ProgramStack;

View File

@ -2987,7 +2987,7 @@ static void opGetMessageString(Program* program)
int messageListIndex = programStackPopInteger(program); int messageListIndex = programStackPopInteger(program);
char* string; char* string;
if (messageIndex >= 1) { if (messageIndex >= 0) {
string = _scr_get_msg_str_speech(messageListIndex, messageIndex, 1); string = _scr_get_msg_str_speech(messageListIndex, messageIndex, 1);
if (string == NULL) { if (string == NULL) {
debugPrint("\nError: No message file EXISTS!: index %d, line %d", messageListIndex, messageIndex); debugPrint("\nError: No message file EXISTS!: index %d, line %d", messageListIndex, messageIndex);

View File

@ -54,7 +54,7 @@ static unsigned char* _mouse_fptr = NULL;
static double gMouseSensitivity = 1.0; static double gMouseSensitivity = 1.0;
// 0x51E2AC // 0x51E2AC
static int gMouseButtonsState = 0; static int last_buttons = 0;
// 0x6AC790 // 0x6AC790
static bool gCursorIsHidden; static bool gCursorIsHidden;
@ -415,7 +415,7 @@ void _mouse_info()
} }
x = 0; x = 0;
y = 0; y = 0;
buttons = gMouseButtonsState; buttons = last_buttons;
} }
_mouse_simulate_input(x, y, buttons); _mouse_simulate_input(x, y, buttons);
@ -447,7 +447,7 @@ void _mouse_simulate_input(int delta_x, int delta_y, int buttons)
return; return;
} }
if (delta_x || delta_y || buttons != gMouseButtonsState) { if (delta_x || delta_y || buttons != last_buttons) {
if (gVcrState == 0) { if (gVcrState == 0) {
if (_vcr_buffer_index == VCR_BUFFER_CAPACITY - 1) { if (_vcr_buffer_index == VCR_BUFFER_CAPACITY - 1) {
vcrDump(); vcrDump();
@ -464,13 +464,13 @@ void _mouse_simulate_input(int delta_x, int delta_y, int buttons)
_vcr_buffer_index++; _vcr_buffer_index++;
} }
} else { } else {
if (gMouseButtonsState == 0) { if (last_buttons == 0) {
if (!_mouse_idling) { if (!_mouse_idling) {
_mouse_idle_start_time = getTicks(); _mouse_idle_start_time = getTicks();
_mouse_idling = 1; _mouse_idling = 1;
} }
gMouseButtonsState = 0; last_buttons = 0;
_raw_buttons = 0; _raw_buttons = 0;
gMouseEvent = 0; gMouseEvent = 0;
@ -479,7 +479,7 @@ void _mouse_simulate_input(int delta_x, int delta_y, int buttons)
} }
_mouse_idling = 0; _mouse_idling = 0;
gMouseButtonsState = buttons; last_buttons = buttons;
previousEvent = gMouseEvent; previousEvent = gMouseEvent;
gMouseEvent = 0; gMouseEvent = 0;
@ -703,4 +703,9 @@ void convertMouseWheelToArrowKey(int* keyCodePtr)
} }
} }
int mouse_get_last_buttons()
{
return last_buttons;
}
} // namespace fallout } // namespace fallout

View File

@ -52,6 +52,7 @@ void mouseGetPositionInWindow(int win, int* x, int* y);
bool mouseHitTestInWindow(int win, int left, int top, int right, int bottom); bool mouseHitTestInWindow(int win, int left, int top, int right, int bottom);
void mouseGetWheel(int* x, int* y); void mouseGetWheel(int* x, int* y);
void convertMouseWheelToArrowKey(int* keyCodePtr); void convertMouseWheelToArrowKey(int* keyCodePtr);
int mouse_get_last_buttons();
} // namespace fallout } // namespace fallout

View File

@ -239,6 +239,36 @@ gzFile compat_gzopen(const char* path, const char* mode)
return gzopen(nativePath, mode); return gzopen(nativePath, mode);
} }
char* compat_fgets(char* buffer, int maxCount, FILE* stream)
{
buffer = fgets(buffer, maxCount, stream);
if (buffer != NULL) {
size_t len = strlen(buffer);
if (len >= 2 && buffer[len - 1] == '\n' && buffer[len - 2] == '\r') {
buffer[len - 2] = '\n';
buffer[len - 1] = '\0';
}
}
return buffer;
}
char* compat_gzgets(gzFile stream, char* buffer, int maxCount)
{
buffer = gzgets(stream, buffer, maxCount);
if (buffer != NULL) {
size_t len = strlen(buffer);
if (len >= 2 && buffer[len - 1] == '\n' && buffer[len - 2] == '\r') {
buffer[len - 2] = '\n';
buffer[len - 1] = '\0';
}
}
return buffer;
}
int compat_remove(const char* path) int compat_remove(const char* path)
{ {
char nativePath[COMPAT_MAX_PATH]; char nativePath[COMPAT_MAX_PATH];

View File

@ -35,6 +35,8 @@ int compat_mkdir(const char* path);
unsigned int compat_timeGetTime(); unsigned int compat_timeGetTime();
FILE* compat_fopen(const char* path, const char* mode); FILE* compat_fopen(const char* path, const char* mode);
gzFile compat_gzopen(const char* path, const char* mode); gzFile compat_gzopen(const char* path, const char* mode);
char* compat_fgets(char* buffer, int maxCount, FILE* stream);
char* compat_gzgets(gzFile stream, char* buffer, int maxCount);
int compat_remove(const char* path); int compat_remove(const char* path);
int compat_rename(const char* oldFileName, const char* newFileName); int compat_rename(const char* oldFileName, const char* newFileName);
void compat_windows_path_to_native(char* path); void compat_windows_path_to_native(char* path);

View File

@ -249,6 +249,12 @@ int _proto_list_str(int pid, char* proto_path)
return 0; return 0;
} }
// 0x49E984
size_t proto_size(int type)
{
return type >= 0 && type < OBJ_TYPE_COUNT ? _proto_sizes[type] : 0;
}
// 0x49E99C // 0x49E99C
bool _proto_action_can_use(int pid) bool _proto_action_can_use(int pid)
{ {
@ -1704,12 +1710,10 @@ static int _proto_load_pid(int pid, Proto** protoPtr)
return 0; return 0;
} }
// allocate memory for proto of given type and adds it to proto cache // 0x4A1D98
static int _proto_find_free_subnode(int type, Proto** protoPtr) static int _proto_find_free_subnode(int type, Proto** protoPtr)
{ {
size_t size = (type >= 0 && type < 11) ? _proto_sizes[type] : 0; Proto* proto = (Proto*)internal_malloc(proto_size(type));
Proto* proto = (Proto*)internal_malloc(size);
*protoPtr = proto; *protoPtr = proto;
if (proto == NULL) { if (proto == NULL) {
return -1; return -1;

View File

@ -104,6 +104,7 @@ 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); int _proto_list_str(int pid, char* proto_path);
size_t proto_size(int type);
bool _proto_action_can_use(int pid); bool _proto_action_can_use(int pid);
bool _proto_action_can_use_on(int pid); bool _proto_action_can_use_on(int pid);
bool _proto_action_can_talk_to(int pid); bool _proto_action_can_talk_to(int pid);

View File

@ -29,6 +29,7 @@
#include "proto.h" #include "proto.h"
#include "proto_instance.h" #include "proto_instance.h"
#include "queue.h" #include "queue.h"
#include "sfall_config.h"
#include "stat.h" #include "stat.h"
#include "svga.h" #include "svga.h"
#include "tile.h" #include "tile.h"
@ -265,6 +266,15 @@ static bool _set;
// 0x667750 // 0x667750
static char _tempStr1[20]; static char _tempStr1[20];
static int gStartYear;
static int gStartMonth;
static int gStartDay;
static int gMovieTimerArtimer1;
static int gMovieTimerArtimer2;
static int gMovieTimerArtimer3;
static int gMovieTimerArtimer4;
// TODO: Make unsigned. // TODO: Make unsigned.
// //
// Returns game time in ticks (1/10 second). // Returns game time in ticks (1/10 second).
@ -278,9 +288,9 @@ int gameTimeGetTime()
// 0x4A3338 // 0x4A3338
void gameTimeGetDate(int* monthPtr, int* dayPtr, int* yearPtr) void gameTimeGetDate(int* monthPtr, int* dayPtr, int* yearPtr)
{ {
int year = (gGameTime / GAME_TIME_TICKS_PER_DAY + 24) / 365 + 2241; int year = (gGameTime / GAME_TIME_TICKS_PER_DAY + gStartDay) / 365 + gStartYear;
int month = 6; int month = gStartMonth;
int day = (gGameTime / GAME_TIME_TICKS_PER_DAY + 24) % 365; int day = (gGameTime / GAME_TIME_TICKS_PER_DAY + gStartDay) % 365;
while (1) { while (1) {
int daysInMonth = gGameTimeDaysPerMonth[month]; int daysInMonth = gGameTimeDaysPerMonth[month];
@ -439,7 +449,7 @@ int _scriptsCheckGameEvents(int* moviePtr, int window)
movieFlags = GAME_MOVIE_FADE_IN | GAME_MOVIE_STOP_MUSIC; movieFlags = GAME_MOVIE_FADE_IN | GAME_MOVIE_STOP_MUSIC;
endgame = true; endgame = true;
} else { } else {
if (day >= 360 || gameGetGlobalVar(GVAR_FALLOUT_2) >= 3) { if (day >= gMovieTimerArtimer4 || gameGetGlobalVar(GVAR_FALLOUT_2) >= 3) {
movie = MOVIE_ARTIMER4; movie = MOVIE_ARTIMER4;
if (!gameMovieIsSeen(MOVIE_ARTIMER4)) { if (!gameMovieIsSeen(MOVIE_ARTIMER4)) {
adjustRep = true; adjustRep = true;
@ -447,13 +457,13 @@ int _scriptsCheckGameEvents(int* moviePtr, int window)
wmAreaSetVisibleState(CITY_DESTROYED_ARROYO, 1, 1); wmAreaSetVisibleState(CITY_DESTROYED_ARROYO, 1, 1);
wmAreaMarkVisitedState(CITY_DESTROYED_ARROYO, 2); wmAreaMarkVisitedState(CITY_DESTROYED_ARROYO, 2);
} }
} else if (day >= 270 && gameGetGlobalVar(GVAR_FALLOUT_2) != 3) { } else if (day >= gMovieTimerArtimer3 && gameGetGlobalVar(GVAR_FALLOUT_2) != 3) {
adjustRep = true; adjustRep = true;
movie = MOVIE_ARTIMER3; movie = MOVIE_ARTIMER3;
} else if (day >= 180 && gameGetGlobalVar(GVAR_FALLOUT_2) != 3) { } else if (day >= gMovieTimerArtimer2 && gameGetGlobalVar(GVAR_FALLOUT_2) != 3) {
adjustRep = true; adjustRep = true;
movie = MOVIE_ARTIMER2; movie = MOVIE_ARTIMER2;
} else if (day >= 90 && gameGetGlobalVar(GVAR_FALLOUT_2) != 3) { } else if (day >= gMovieTimerArtimer1 && gameGetGlobalVar(GVAR_FALLOUT_2) != 3) {
adjustRep = true; adjustRep = true;
movie = MOVIE_ARTIMER1; movie = MOVIE_ARTIMER1;
} }
@ -1522,6 +1532,15 @@ int scriptsInit()
messageListRepositorySetStandardMessageList(STANDARD_MESSAGE_LIST_SCRIPT, &gScrMessageList); messageListRepositorySetStandardMessageList(STANDARD_MESSAGE_LIST_SCRIPT, &gScrMessageList);
configGetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_START_YEAR, &gStartYear);
configGetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_START_MONTH, &gStartMonth);
configGetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_START_DAY, &gStartDay);
configGetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_MOVIE_TIMER_ARTIMER1, &gMovieTimerArtimer1);
configGetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_MOVIE_TIMER_ARTIMER2, &gMovieTimerArtimer2);
configGetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_MOVIE_TIMER_ARTIMER3, &gMovieTimerArtimer3);
configGetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_MOVIE_TIMER_ARTIMER4, &gMovieTimerArtimer4);
return 0; return 0;
} }

View File

@ -27,6 +27,9 @@ bool sfallConfigInit(int argc, char** argv)
configSetString(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_DUDE_NATIVE_LOOK_JUMPSUIT_FEMALE_KEY, ""); configSetString(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_DUDE_NATIVE_LOOK_JUMPSUIT_FEMALE_KEY, "");
configSetString(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_DUDE_NATIVE_LOOK_TRIBAL_MALE_KEY, ""); configSetString(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_DUDE_NATIVE_LOOK_TRIBAL_MALE_KEY, "");
configSetString(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_DUDE_NATIVE_LOOK_TRIBAL_FEMALE_KEY, ""); configSetString(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_DUDE_NATIVE_LOOK_TRIBAL_FEMALE_KEY, "");
configSetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_START_YEAR, 2241);
configSetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_START_MONTH, 6);
configSetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_START_DAY, 24);
configSetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_MAIN_MENU_BIG_FONT_COLOR_KEY, 0); configSetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_MAIN_MENU_BIG_FONT_COLOR_KEY, 0);
configSetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_MAIN_MENU_CREDITS_OFFSET_X_KEY, 0); configSetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_MAIN_MENU_CREDITS_OFFSET_X_KEY, 0);
configSetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_MAIN_MENU_CREDITS_OFFSET_Y_KEY, 0); configSetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_MAIN_MENU_CREDITS_OFFSET_Y_KEY, 0);
@ -50,6 +53,10 @@ bool sfallConfigInit(int argc, char** argv)
configSetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_BURST_MOD_TARGET_MULTIPLIER_KEY, SFALL_CONFIG_BURST_MOD_DEFAULT_TARGET_MULTIPLIER); configSetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_BURST_MOD_TARGET_MULTIPLIER_KEY, SFALL_CONFIG_BURST_MOD_DEFAULT_TARGET_MULTIPLIER);
configSetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_BURST_MOD_TARGET_DIVISOR_KEY, SFALL_CONFIG_BURST_MOD_DEFAULT_TARGET_DIVISOR); configSetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_BURST_MOD_TARGET_DIVISOR_KEY, SFALL_CONFIG_BURST_MOD_DEFAULT_TARGET_DIVISOR);
configSetString(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_EXTRA_MESSAGE_LISTS_KEY, ""); configSetString(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_EXTRA_MESSAGE_LISTS_KEY, "");
configSetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_MOVIE_TIMER_ARTIMER1, 90);
configSetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_MOVIE_TIMER_ARTIMER2, 180);
configSetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_MOVIE_TIMER_ARTIMER3, 270);
configSetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_MOVIE_TIMER_ARTIMER4, 360);
char path[COMPAT_MAX_PATH]; char path[COMPAT_MAX_PATH];
char* executable = argv[0]; char* executable = argv[0];

View File

@ -13,6 +13,9 @@ namespace fallout {
#define SFALL_CONFIG_DUDE_NATIVE_LOOK_JUMPSUIT_FEMALE_KEY "FemaleDefaultModel" #define SFALL_CONFIG_DUDE_NATIVE_LOOK_JUMPSUIT_FEMALE_KEY "FemaleDefaultModel"
#define SFALL_CONFIG_DUDE_NATIVE_LOOK_TRIBAL_MALE_KEY "MaleStartModel" #define SFALL_CONFIG_DUDE_NATIVE_LOOK_TRIBAL_MALE_KEY "MaleStartModel"
#define SFALL_CONFIG_DUDE_NATIVE_LOOK_TRIBAL_FEMALE_KEY "FemaleStartModel" #define SFALL_CONFIG_DUDE_NATIVE_LOOK_TRIBAL_FEMALE_KEY "FemaleStartModel"
#define SFALL_CONFIG_START_YEAR "StartYear"
#define SFALL_CONFIG_START_MONTH "StartMonth"
#define SFALL_CONFIG_START_DAY "StartDay"
#define SFALL_CONFIG_MAIN_MENU_BIG_FONT_COLOR_KEY "MainMenuBigFontColour" #define SFALL_CONFIG_MAIN_MENU_BIG_FONT_COLOR_KEY "MainMenuBigFontColour"
#define SFALL_CONFIG_MAIN_MENU_CREDITS_OFFSET_X_KEY "MainMenuCreditsOffsetX" #define SFALL_CONFIG_MAIN_MENU_CREDITS_OFFSET_X_KEY "MainMenuCreditsOffsetX"
#define SFALL_CONFIG_MAIN_MENU_CREDITS_OFFSET_Y_KEY "MainMenuCreditsOffsetY" #define SFALL_CONFIG_MAIN_MENU_CREDITS_OFFSET_Y_KEY "MainMenuCreditsOffsetY"
@ -42,6 +45,10 @@ namespace fallout {
#define SFALL_CONFIG_PLASTIC_EXPLOSIVE_MIN_DAMAGE_KEY "PlasticExplosive_DmgMin" #define SFALL_CONFIG_PLASTIC_EXPLOSIVE_MIN_DAMAGE_KEY "PlasticExplosive_DmgMin"
#define SFALL_CONFIG_PLASTIC_EXPLOSIVE_MAX_DAMAGE_KEY "PlasticExplosive_DmgMax" #define SFALL_CONFIG_PLASTIC_EXPLOSIVE_MAX_DAMAGE_KEY "PlasticExplosive_DmgMax"
#define SFALL_CONFIG_EXPLOSION_EMITS_LIGHT_KEY "ExplosionsEmitLight" #define SFALL_CONFIG_EXPLOSION_EMITS_LIGHT_KEY "ExplosionsEmitLight"
#define SFALL_CONFIG_MOVIE_TIMER_ARTIMER1 "MovieTimer_artimer1"
#define SFALL_CONFIG_MOVIE_TIMER_ARTIMER2 "MovieTimer_artimer2"
#define SFALL_CONFIG_MOVIE_TIMER_ARTIMER3 "MovieTimer_artimer3"
#define SFALL_CONFIG_MOVIE_TIMER_ARTIMER4 "MovieTimer_artimer4"
#define SFALL_CONFIG_CITY_REPUTATION_LIST_KEY "CityRepsList" #define SFALL_CONFIG_CITY_REPUTATION_LIST_KEY "CityRepsList"
#define SFALL_CONFIG_UNARMED_FILE_KEY "UnarmedFile" #define SFALL_CONFIG_UNARMED_FILE_KEY "UnarmedFile"
#define SFALL_CONFIG_DAMAGE_MOD_FORMULA_KEY "DamageFormula" #define SFALL_CONFIG_DAMAGE_MOD_FORMULA_KEY "DamageFormula"

View File

@ -1,19 +1,25 @@
#include "sfall_opcodes.h" #include "sfall_opcodes.h"
#include "animation.h"
#include "art.h" #include "art.h"
#include "combat.h" #include "combat.h"
#include "debug.h" #include "debug.h"
#include "game.h" #include "game.h"
#include "input.h"
#include "interface.h" #include "interface.h"
#include "interpreter.h" #include "interpreter.h"
#include "item.h" #include "item.h"
#include "message.h" #include "message.h"
#include "mouse.h" #include "mouse.h"
#include "object.h" #include "object.h"
#include "proto.h"
#include "scripts.h"
#include "sfall_global_vars.h" #include "sfall_global_vars.h"
#include "sfall_lists.h" #include "sfall_lists.h"
#include "stat.h" #include "stat.h"
#include "svga.h" #include "svga.h"
#include "tile.h"
#include "worldmap.h"
namespace fallout { namespace fallout {
@ -39,6 +45,17 @@ static void opReadByte(Program* program)
programStackPushInteger(program, value); programStackPushInteger(program, value);
} }
// set_pc_base_stat
static void op_set_pc_base_stat(Program* program)
{
// CE: Implementation is different. Sfall changes value directly on the
// dude's proto, without calling |critterSetBaseStat|. This function has
// important call to update derived stats, which is not present in Sfall.
int value = programStackPopInteger(program);
int stat = programStackPopInteger(program);
critterSetBaseStat(gDude, stat, value);
}
// set_pc_extra_stat // set_pc_extra_stat
static void opSetPcBonusStat(Program* program) static void opSetPcBonusStat(Program* program)
{ {
@ -50,6 +67,16 @@ static void opSetPcBonusStat(Program* program)
critterSetBonusStat(gDude, stat, value); critterSetBonusStat(gDude, stat, value);
} }
// get_pc_base_stat
static void op_get_pc_base_stat(Program* program)
{
// CE: Implementation is different. Sfall obtains value directly from
// dude's proto. This can have unforeseen consequences when dealing with
// current stats.
int stat = programStackPopInteger(program);
programStackPushInteger(program, critterGetBaseStat(gDude, stat));
}
// get_pc_extra_stat // get_pc_extra_stat
static void opGetPcBonusStat(Program* program) static void opGetPcBonusStat(Program* program)
{ {
@ -58,6 +85,28 @@ static void opGetPcBonusStat(Program* program)
programStackPushInteger(program, value); programStackPushInteger(program, value);
} }
// get_year
static void op_get_year(Program* program)
{
int year;
gameTimeGetDate(nullptr, nullptr, &year);
programStackPushInteger(program, year);
}
// in_world_map
static void op_in_world_map(Program* program)
{
programStackPushInteger(program, GameMode::isInGameMode(GameMode::kWorldmap) ? 1 : 0);
}
// set_world_map_pos
static void op_set_world_map_pos(Program* program)
{
int y = programStackPopInteger(program);
int x = programStackPopInteger(program);
wmSetPartyWorldPos(x, y);
}
// active_hand // active_hand
static void opGetCurrentHand(Program* program) static void opGetCurrentHand(Program* program)
{ {
@ -100,6 +149,103 @@ static void opGetGameMode(Program* program)
programStackPushInteger(program, GameMode::getCurrentGameMode()); programStackPushInteger(program, GameMode::getCurrentGameMode());
} }
// get_uptime
static void op_get_uptime(Program* program)
{
programStackPushInteger(program, getTicks());
}
// set_car_current_town
static void op_set_car_current_town(Program* program)
{
int area = programStackPopInteger(program);
wmCarSetCurrentArea(area);
}
// get_bodypart_hit_modifier
static void op_get_bodypart_hit_modifier(Program* program)
{
int hit_location = programStackPopInteger(program);
programStackPushInteger(program, combat_get_hit_location_penalty(hit_location));
}
// set_bodypart_hit_modifier
static void op_set_bodypart_hit_modifier(Program* program)
{
int penalty = programStackPopInteger(program);
int hit_location = programStackPopInteger(program);
combat_set_hit_location_penalty(hit_location, penalty);
}
// sqrt
static void op_sqrt(Program* program)
{
ProgramValue programValue = programStackPopValue(program);
programStackPushFloat(program, sqrtf(programValue.asFloat()));
}
// abs
static void op_abs(Program* program)
{
ProgramValue programValue = programStackPopValue(program);
if (programValue.isInt()) {
programStackPushInteger(program, abs(programValue.integerValue));
} else {
programStackPushFloat(program, abs(programValue.asFloat()));
}
}
// get_proto_data
static void op_get_proto_data(Program* program)
{
size_t offset = static_cast<size_t>(programStackPopInteger(program));
int pid = programStackPopInteger(program);
Proto* proto;
if (protoGetProto(pid, &proto) != 0) {
debugPrint("op_get_proto_data: bad proto %d", pid);
programStackPushInteger(program, -1);
return;
}
// CE: Make sure the requested offset is within memory bounds and is
// properly aligned.
if (offset + sizeof(int) > proto_size(PID_TYPE(pid)) || offset % sizeof(int) != 0) {
debugPrint("op_get_proto_data: bad offset %d", offset);
programStackPushInteger(program, -1);
return;
}
int value = *reinterpret_cast<int*>(reinterpret_cast<unsigned char*>(proto) + offset);
programStackPushInteger(program, value);
}
// set_proto_data
static void op_set_proto_data(Program* program)
{
int value = programStackPopInteger(program);
size_t offset = static_cast<size_t>(programStackPopInteger(program));
int pid = programStackPopInteger(program);
Proto* proto;
if (protoGetProto(pid, &proto) != 0) {
debugPrint("op_set_proto_data: bad proto %d", pid);
programStackPushInteger(program, -1);
return;
}
// CE: Make sure the requested offset is within memory bounds and is
// properly aligned.
if (offset + sizeof(int) > proto_size(PID_TYPE(pid)) || offset % sizeof(int) != 0) {
debugPrint("op_set_proto_data: bad offset %d", offset);
programStackPushInteger(program, -1);
return;
}
*reinterpret_cast<int*>(reinterpret_cast<unsigned char*>(proto) + offset) = value;
}
// list_begin // list_begin
static void opListBegin(Program* program) static void opListBegin(Program* program)
{ {
@ -251,6 +397,14 @@ static void opGetMouseY(Program* program)
programStackPushInteger(program, y); programStackPushInteger(program, y);
} }
// get_mouse_buttons
static void op_get_mouse_buttons(Program* program)
{
// CE: Implementation is slightly different - it does not handle middle
// mouse button.
programStackPushInteger(program, mouse_get_last_buttons());
}
// get_screen_width // get_screen_width
static void opGetScreenWidth(Program* program) static void opGetScreenWidth(Program* program)
{ {
@ -263,6 +417,17 @@ static void opGetScreenHeight(Program* program)
programStackPushInteger(program, screenGetHeight()); programStackPushInteger(program, screenGetHeight());
} }
// get_attack_type
static void op_get_attack_type(Program* program)
{
int hit_mode;
if (interface_get_current_attack_mode(&hit_mode)) {
programStackPushInteger(program, hit_mode);
} else {
programStackPushInteger(program, -1);
}
}
// atoi // atoi
static void opParseInt(Program* program) static void opParseInt(Program* program)
{ {
@ -270,6 +435,24 @@ static void opParseInt(Program* program)
programStackPushInteger(program, static_cast<int>(strtol(string, nullptr, 0))); programStackPushInteger(program, static_cast<int>(strtol(string, nullptr, 0)));
} }
// atof
static void op_atof(Program* program)
{
const char* string = programStackPopString(program);
programStackPushFloat(program, static_cast<float>(atof(string)));
}
// tile_under_cursor
static void op_tile_under_cursor(Program* program)
{
int x;
int y;
mouseGetPosition(&x, &y);
int tile = tileFromScreenXY(x, y, gElevation);
programStackPushInteger(program, tile);
}
// strlen // strlen
static void opGetStringLength(Program* program) static void opGetStringLength(Program* program)
{ {
@ -277,6 +460,22 @@ static void opGetStringLength(Program* program)
programStackPushInteger(program, static_cast<int>(strlen(string))); programStackPushInteger(program, static_cast<int>(strlen(string)));
} }
// pow (^)
static void op_power(Program* program)
{
ProgramValue expValue = programStackPopValue(program);
ProgramValue baseValue = programStackPopValue(program);
// CE: Implementation is slightly different, check.
float result = powf(baseValue.asFloat(), expValue.asFloat());
if (baseValue.isInt() && expValue.isInt()) {
programStackPushInteger(program, static_cast<int>(result));
} else {
programStackPushFloat(program, result);
}
}
// message_str_game // message_str_game
static void opGetMessage(Program* program) static void opGetMessage(Program* program)
{ {
@ -298,6 +497,61 @@ static void opRound(Program* program)
programStackPushInteger(program, integerValue); programStackPushInteger(program, integerValue);
} }
enum BlockType {
BLOCKING_TYPE_BLOCK,
BLOCKING_TYPE_SHOOT,
BLOCKING_TYPE_AI,
BLOCKING_TYPE_SIGHT,
BLOCKING_TYPE_SCROLL,
};
PathBuilderCallback* get_blocking_func(int type)
{
switch (type) {
case BLOCKING_TYPE_SHOOT:
return _obj_shoot_blocking_at;
case BLOCKING_TYPE_AI:
return _obj_ai_blocking_at;
case BLOCKING_TYPE_SIGHT:
return _obj_sight_blocking_at;
default:
return _obj_blocking_at;
}
}
// obj_blocking_line
static void op_make_straight_path(Program* program)
{
int type = programStackPopInteger(program);
int dest = programStackPopInteger(program);
Object* object = static_cast<Object*>(programStackPopPointer(program));
int flags = type == BLOCKING_TYPE_SHOOT ? 32 : 0;
Object* obstacle = nullptr;
_make_straight_path_func(object, object->tile, dest, nullptr, &obstacle, flags, get_blocking_func(type));
programStackPushPointer(program, obstacle);
}
// obj_blocking_tile
static void op_obj_blocking_at(Program* program)
{
int type = programStackPopInteger(program);
int elevation = programStackPopInteger(program);
int tile = programStackPopInteger(program);
PathBuilderCallback* func = get_blocking_func(type);
Object* obstacle = func(NULL, tile, elevation);
if (obstacle != NULL) {
if (type == BLOCKING_TYPE_SHOOT) {
if ((obstacle->flags & OBJECT_SHOOT_THRU) != 0) {
obstacle = nullptr;
}
}
}
programStackPushPointer(program, obstacle);
}
// art_exists // art_exists
static void opArtExists(Program* program) static void opArtExists(Program* program)
{ {
@ -305,15 +559,50 @@ static void opArtExists(Program* program)
programStackPushInteger(program, artExists(fid)); programStackPushInteger(program, artExists(fid));
} }
// div (/)
static void op_div(Program* program)
{
ProgramValue divisorValue = programStackPopValue(program);
ProgramValue dividendValue = programStackPopValue(program);
if (divisorValue.integerValue == 0) {
debugPrint("Division by zero");
// TODO: Looks like execution is not halted in Sfall's div, check.
programStackPushInteger(program, 0);
return;
}
if (dividendValue.isFloat() || divisorValue.isFloat()) {
programStackPushFloat(program, dividendValue.asFloat() / divisorValue.asFloat());
} else {
// Unsigned divison.
programStackPushInteger(program, static_cast<unsigned int>(dividendValue.integerValue) / static_cast<unsigned int>(divisorValue.integerValue));
}
}
void sfallOpcodesInit() void sfallOpcodesInit()
{ {
interpreterRegisterOpcode(0x8156, opReadByte); interpreterRegisterOpcode(0x8156, opReadByte);
interpreterRegisterOpcode(0x815A, op_set_pc_base_stat);
interpreterRegisterOpcode(0x815B, opSetPcBonusStat); interpreterRegisterOpcode(0x815B, opSetPcBonusStat);
interpreterRegisterOpcode(0x815C, op_get_pc_base_stat);
interpreterRegisterOpcode(0x815D, opGetPcBonusStat); interpreterRegisterOpcode(0x815D, opGetPcBonusStat);
interpreterRegisterOpcode(0x8163, op_get_year);
interpreterRegisterOpcode(0x8170, op_in_world_map);
interpreterRegisterOpcode(0x8172, op_set_world_map_pos);
interpreterRegisterOpcode(0x8193, opGetCurrentHand); interpreterRegisterOpcode(0x8193, opGetCurrentHand);
interpreterRegisterOpcode(0x819D, opSetGlobalVar); interpreterRegisterOpcode(0x819D, opSetGlobalVar);
interpreterRegisterOpcode(0x819E, opGetGlobalInt); interpreterRegisterOpcode(0x819E, opGetGlobalInt);
interpreterRegisterOpcode(0x81AF, opGetGameMode); interpreterRegisterOpcode(0x81AF, opGetGameMode);
interpreterRegisterOpcode(0x81B3, op_get_uptime);
interpreterRegisterOpcode(0x81B6, op_set_car_current_town);
interpreterRegisterOpcode(0x81DF, op_get_bodypart_hit_modifier);
interpreterRegisterOpcode(0x81E0, op_set_bodypart_hit_modifier);
interpreterRegisterOpcode(0x81EC, op_sqrt);
interpreterRegisterOpcode(0x81ED, op_abs);
interpreterRegisterOpcode(0x8204, op_get_proto_data);
interpreterRegisterOpcode(0x8205, op_set_proto_data);
interpreterRegisterOpcode(0x820D, opListBegin); interpreterRegisterOpcode(0x820D, opListBegin);
interpreterRegisterOpcode(0x820E, opListNext); interpreterRegisterOpcode(0x820E, opListNext);
interpreterRegisterOpcode(0x820F, opListEnd); interpreterRegisterOpcode(0x820F, opListEnd);
@ -326,13 +615,21 @@ void sfallOpcodesInit()
interpreterRegisterOpcode(0x821A, opSetWeaponAmmoCount); interpreterRegisterOpcode(0x821A, opSetWeaponAmmoCount);
interpreterRegisterOpcode(0x821C, opGetMouseX); interpreterRegisterOpcode(0x821C, opGetMouseX);
interpreterRegisterOpcode(0x821D, opGetMouseY); interpreterRegisterOpcode(0x821D, opGetMouseY);
interpreterRegisterOpcode(0x821E, op_get_mouse_buttons);
interpreterRegisterOpcode(0x8220, opGetScreenWidth); interpreterRegisterOpcode(0x8220, opGetScreenWidth);
interpreterRegisterOpcode(0x8221, opGetScreenHeight); interpreterRegisterOpcode(0x8221, opGetScreenHeight);
interpreterRegisterOpcode(0x8228, op_get_attack_type);
interpreterRegisterOpcode(0x8237, opParseInt); interpreterRegisterOpcode(0x8237, opParseInt);
interpreterRegisterOpcode(0x8238, op_atof);
interpreterRegisterOpcode(0x824B, op_tile_under_cursor);
interpreterRegisterOpcode(0x824F, opGetStringLength); interpreterRegisterOpcode(0x824F, opGetStringLength);
interpreterRegisterOpcode(0x8263, op_power);
interpreterRegisterOpcode(0x826B, opGetMessage); interpreterRegisterOpcode(0x826B, opGetMessage);
interpreterRegisterOpcode(0x8267, opRound); interpreterRegisterOpcode(0x8267, opRound);
interpreterRegisterOpcode(0x826E, op_make_straight_path);
interpreterRegisterOpcode(0x826F, op_obj_blocking_at);
interpreterRegisterOpcode(0x8274, opArtExists); interpreterRegisterOpcode(0x8274, opArtExists);
interpreterRegisterOpcode(0x827F, op_div);
} }
void sfallOpcodesExit() void sfallOpcodesExit()

View File

@ -1,5 +1,6 @@
#include "sound.h" #include "sound.h"
#include <fcntl.h>
#include <limits.h> #include <limits.h>
#include <math.h> #include <math.h>
#include <stdlib.h> #include <stdlib.h>
@ -8,7 +9,6 @@
#ifdef _WIN32 #ifdef _WIN32
#include <io.h> #include <io.h>
#else #else
#include <fcntl.h>
#include <unistd.h> #include <unistd.h>
#endif #endif
@ -49,7 +49,7 @@ static long soundFileSize(int fileHandle);
static long soundTellData(int fileHandle); static long soundTellData(int fileHandle);
static int soundWriteData(int fileHandle, const void* buf, unsigned int size); static int soundWriteData(int fileHandle, const void* buf, unsigned int size);
static int soundReadData(int fileHandle, void* buf, unsigned int size); static int soundReadData(int fileHandle, void* buf, unsigned int size);
static int soundOpenData(const char* filePath, int flags); static int soundOpenData(const char* filePath, int* channels, int* sampleRate);
static long soundSeekData(int fileHandle, long offset, int origin); static long soundSeekData(int fileHandle, long offset, int origin);
static int soundCloseData(int fileHandle); static int soundCloseData(int fileHandle);
static char* soundFileManglerDefaultImpl(char* fname); static char* soundFileManglerDefaultImpl(char* fname);
@ -223,8 +223,16 @@ static int soundReadData(int fileHandle, void* buf, unsigned int size)
} }
// 0x4AC768 // 0x4AC768
static int soundOpenData(const char* filePath, int flags) static int soundOpenData(const char* filePath, int* channels, int* sampleRate)
{ {
int flags;
#ifdef _WIN32
flags = _O_RDONLY | _O_BINARY;
#else
flags = O_RDONLY;
#endif
return open(filePath, flags); return open(filePath, flags);
} }
@ -608,7 +616,7 @@ int soundLoad(Sound* sound, char* filePath)
return gSoundLastError; return gSoundLastError;
} }
sound->io.fd = sound->io.open(gSoundFileNameMangler(filePath), 0x0200); sound->io.fd = sound->io.open(gSoundFileNameMangler(filePath), &(sound->channels), &(sound->rate));
if (sound->io.fd == -1) { if (sound->io.fd == -1) {
gSoundLastError = SOUND_FILE_NOT_FOUND; gSoundLastError = SOUND_FILE_NOT_FOUND;
return gSoundLastError; return gSoundLastError;

View File

@ -46,7 +46,7 @@ typedef enum SoundError {
SOUND_ERR_COUNT, SOUND_ERR_COUNT,
} SoundError; } SoundError;
typedef int SoundOpenProc(const char* filePath, int flags); typedef int SoundOpenProc(const char* filePath, int* channels, int* sampleRate);
typedef int SoundCloseProc(int fileHandle); typedef int SoundCloseProc(int fileHandle);
typedef int SoundReadProc(int fileHandle, void* buf, unsigned int size); typedef int SoundReadProc(int fileHandle, void* buf, unsigned int size);
typedef int SoundWriteProc(int fileHandle, const void* buf, unsigned int size); typedef int SoundWriteProc(int fileHandle, const void* buf, unsigned int size);

View File

@ -34,13 +34,15 @@ static int ReadBand_Fmt24(SoundDecoder* soundDecoder, int offset, int bits);
static int ReadBand_Fmt26(SoundDecoder* soundDecoder, int offset, int bits); static int ReadBand_Fmt26(SoundDecoder* soundDecoder, int offset, int bits);
static int ReadBand_Fmt27(SoundDecoder* soundDecoder, int offset, int bits); static int ReadBand_Fmt27(SoundDecoder* soundDecoder, int offset, int bits);
static int ReadBand_Fmt29(SoundDecoder* soundDecoder, int offset, int bits); static int ReadBand_Fmt29(SoundDecoder* soundDecoder, int offset, int bits);
static int ReadBands(SoundDecoder* ptr); static bool ReadBands(SoundDecoder* soundDecoder);
static void untransform_subband0(unsigned char* a1, unsigned char* a2, int a3, int a4); static void untransform_subband0(unsigned char* a1, unsigned char* a2, int a3, int a4);
static void untransform_subband(unsigned char* a1, unsigned char* a2, int a3, int a4); static void untransform_subband(unsigned char* a1, unsigned char* a2, int a3, int a4);
static void untransform_all(SoundDecoder* soundDecoder); static void untransform_all(SoundDecoder* soundDecoder);
static bool soundDecoderFill(SoundDecoder* soundDecoder);
static inline void soundDecoderRequireBits(SoundDecoder* soundDecoder, int bits); static inline void soundDecoderRequireBits(SoundDecoder* soundDecoder, int bits);
static inline void soundDecoderDropBits(SoundDecoder* soundDecoder, int bits); static inline void soundDecoderDropBits(SoundDecoder* soundDecoder, int bits);
static int ReadBand_Fmt31(SoundDecoder* soundDecoder, int offset, int bits);
// 0x51E328 // 0x51E328
static int gSoundDecodersCount = 0; static int gSoundDecodersCount = 0;
@ -78,7 +80,7 @@ static ReadBandFunc _ReadBand_tbl[32] = {
ReadBand_Fail, ReadBand_Fail,
ReadBand_Fmt29, ReadBand_Fmt29,
ReadBand_Fail, ReadBand_Fail,
ReadBand_Fail, ReadBand_Fmt31,
}; };
// 0x6AD960 // 0x6AD960
@ -751,7 +753,7 @@ static int ReadBand_Fmt29(SoundDecoder* soundDecoder, int offset, int bits)
} }
// 0x4D493C // 0x4D493C
static int ReadBands(SoundDecoder* soundDecoder) static bool ReadBands(SoundDecoder* soundDecoder)
{ {
int v9; int v9;
int v15; int v15;
@ -797,10 +799,10 @@ static int ReadBands(SoundDecoder* soundDecoder)
fn = _ReadBand_tbl[bits]; fn = _ReadBand_tbl[bits];
if (!fn(soundDecoder, index, bits)) { if (!fn(soundDecoder, index, bits)) {
return 0; return false;
} }
} }
return 1; return true;
} }
// 0x4D4ADC // 0x4D4ADC
@ -1043,53 +1045,67 @@ static void untransform_all(SoundDecoder* soundDecoder)
} }
} }
// NOTE: Inlined.
//
// 0x4D4F58
static bool soundDecoderFill(SoundDecoder* soundDecoder)
{
// CE: Implementation is slightly different. `ReadBands` now handles new
// Fmt31 used in some Russian localizations. The appropriate handler acts as
// both decoder and transformer, so there is no need to untransform bands
// once again. This approach assumes band 31 is never used by standard acms
// and mods.
if (ReadBands(soundDecoder)) {
untransform_all(soundDecoder);
}
soundDecoder->file_cnt -= soundDecoder->total_samples;
soundDecoder->samp_ptr = soundDecoder->samples;
soundDecoder->samp_cnt = soundDecoder->total_samples;
if (soundDecoder->file_cnt < 0) {
soundDecoder->samp_cnt += soundDecoder->file_cnt;
soundDecoder->file_cnt = 0;
}
return true;
}
// 0x4D4FA0 // 0x4D4FA0
size_t soundDecoderDecode(SoundDecoder* soundDecoder, void* buffer, size_t size) size_t soundDecoderDecode(SoundDecoder* soundDecoder, void* buffer, size_t size)
{ {
unsigned char* dest; unsigned char* dest;
unsigned char* v5; unsigned char* samp_ptr;
int v6; int samp_cnt;
int v4;
dest = (unsigned char*)buffer; dest = (unsigned char*)buffer;
v4 = 0; samp_ptr = soundDecoder->samp_ptr;
v5 = soundDecoder->samp_ptr; samp_cnt = soundDecoder->samp_cnt;
v6 = soundDecoder->samp_cnt;
size_t bytesRead; size_t bytesRead;
for (bytesRead = 0; bytesRead < size; bytesRead += 2) { for (bytesRead = 0; bytesRead < size; bytesRead += 2) {
if (!v6) { if (samp_cnt == 0) {
if (!soundDecoder->file_cnt) { if (soundDecoder->file_cnt == 0) {
break; break;
} }
if (!ReadBands(soundDecoder)) { // NOTE: Uninline.
if (!soundDecoderFill(soundDecoder)) {
break; break;
} }
untransform_all(soundDecoder); samp_ptr = soundDecoder->samp_ptr;
samp_cnt = soundDecoder->samp_cnt;
soundDecoder->file_cnt -= soundDecoder->total_samples;
soundDecoder->samp_ptr = soundDecoder->samples;
soundDecoder->samp_cnt = soundDecoder->total_samples;
if (soundDecoder->file_cnt < 0) {
soundDecoder->samp_cnt += soundDecoder->file_cnt;
soundDecoder->file_cnt = 0;
}
v5 = soundDecoder->samp_ptr;
v6 = soundDecoder->samp_cnt;
} }
int v13 = *(int*)v5; int sample = *(int*)samp_ptr;
v5 += 4; samp_ptr += 4;
*(unsigned short*)(dest + bytesRead) = (v13 >> soundDecoder->levels) & 0xFFFF; *(unsigned short*)(dest + bytesRead) = (sample >> soundDecoder->levels) & 0xFFFF;
v6--; samp_cnt--;
} }
soundDecoder->samp_ptr = v5; soundDecoder->samp_ptr = samp_ptr;
soundDecoder->samp_cnt = v6; soundDecoder->samp_cnt = samp_cnt;
return bytesRead; return bytesRead;
} }
@ -1259,4 +1275,22 @@ static inline void soundDecoderDropBits(SoundDecoder* soundDecoder, int bits)
soundDecoder->bits -= bits; soundDecoder->bits -= bits;
} }
static int ReadBand_Fmt31(SoundDecoder* soundDecoder, int offset, int bits)
{
int* samples = (int*)soundDecoder->samples;
int remaining_samples = soundDecoder->total_samples;
while (remaining_samples != 0) {
soundDecoderRequireBits(soundDecoder, 16);
int value = soundDecoder->hold & 0xFFFF;
soundDecoderDropBits(soundDecoder, 16);
*samples++ = (value << 16) >> (16 - soundDecoder->levels);
remaining_samples--;
}
return 0;
}
} // namespace fallout } // namespace fallout

View File

@ -154,7 +154,7 @@ void soundEffectsCacheFlush()
// sfxc_cached_open // sfxc_cached_open
// 0x4A915C // 0x4A915C
int soundEffectsCacheFileOpen(const char* fname, int mode) int soundEffectsCacheFileOpen(const char* fname, int* channels, int* sampleRate)
{ {
if (_sfxc_files_open >= SOUND_EFFECTS_MAX_COUNT) { if (_sfxc_files_open >= SOUND_EFFECTS_MAX_COUNT) {
return -1; return -1;

View File

@ -11,7 +11,7 @@ int soundEffectsCacheInit(int cache_size, const char* effectsPath);
void soundEffectsCacheExit(); void soundEffectsCacheExit();
int soundEffectsCacheInitialized(); int soundEffectsCacheInitialized();
void soundEffectsCacheFlush(); void soundEffectsCacheFlush();
int soundEffectsCacheFileOpen(const char* fname, int mode); int soundEffectsCacheFileOpen(const char* fname, int* channels, int* sampleRate);
int soundEffectsCacheFileClose(int handle); int soundEffectsCacheFileClose(int handle);
int soundEffectsCacheFileRead(int handle, void* buf, unsigned int size); int soundEffectsCacheFileRead(int handle, void* buf, unsigned int size);
int soundEffectsCacheFileWrite(int handle, const void* buf, unsigned int size); int soundEffectsCacheFileWrite(int handle, const void* buf, unsigned int size);

View File

@ -5,6 +5,7 @@
#include <string.h> #include <string.h>
#include <algorithm> #include <algorithm>
#include <stack>
#include "art.h" #include "art.h"
#include "color.h" #include "color.h"
@ -49,11 +50,16 @@ typedef struct UpsideDownTriangle {
int field_8; int field_8;
} UpsideDownTriangle; } UpsideDownTriangle;
struct roof_fill_task {
int x;
int y;
};
static void tileSetBorder(int windowWidth, int windowHeight, int hexGridWidth, int hexGridHeight); static void tileSetBorder(int windowWidth, int windowHeight, int hexGridWidth, int hexGridHeight);
static void tileRefreshMapper(Rect* rect, int elevation); static void tileRefreshMapper(Rect* rect, int elevation);
static void tileRefreshGame(Rect* rect, int elevation); static void tileRefreshGame(Rect* rect, int elevation);
static void roof_fill_on(int x, int y, int elevation); static void roof_fill_push_task_if_in_bounds(std::stack<roof_fill_task>& tasks_stack, int x, int y);
static void roof_fill_off(int x, int y, int elevation); static void roof_fill_off_process_task(std::stack<roof_fill_task>& tasks_stack, int elevation, bool on);
static void tileRenderRoof(int fid, int x, int y, Rect* rect, int light); static void tileRenderRoof(int fid, int x, int y, Rect* rect, int light);
static void _draw_grid(int tile, int elevation, Rect* rect); static void _draw_grid(int tile, int elevation, Rect* rect);
static void tileRenderFloor(int fid, int x, int y, Rect* rect); static void tileRenderFloor(int fid, int x, int y, Rect* rect);
@ -1258,27 +1264,39 @@ void tileRenderRoofsInRect(Rect* rect, int elevation)
} }
} }
// 0x4B22D0 static void roof_fill_push_task_if_in_bounds(std::stack<roof_fill_task>& tasks_stack, int x, int y)
static void roof_fill_on(int x, int y, int elevation)
{ {
if (x >= 0 && x < gSquareGridWidth && y >= 0 && y < gSquareGridHeight) { if (x >= 0 && x < gSquareGridWidth && y >= 0 && y < gSquareGridHeight) {
int squareTileIndex = gSquareGridWidth * y + x; tasks_stack.push(roof_fill_task { x, y });
int squareTile = gTileSquares[elevation]->field_0[squareTileIndex]; };
int roof = (squareTile >> 16) & 0xFFFF; };
int id = roof & 0xFFF; static void roof_fill_off_process_task(std::stack<roof_fill_task>& tasks_stack, int elevation, bool on)
if (buildFid(OBJ_TYPE_TILE, id, 0, 0, 0) != buildFid(OBJ_TYPE_TILE, 1, 0, 0, 0)) { {
int flag = (roof & 0xF000) >> 12; auto [x, y] = tasks_stack.top();
if ((flag & 0x01) != 0) { tasks_stack.pop();
int squareTileIndex = gSquareGridWidth * y + x;
int squareTile = gTileSquares[elevation]->field_0[squareTileIndex];
int roof = (squareTile >> 16) & 0xFFFF;
int id = roof & 0xFFF;
if (buildFid(OBJ_TYPE_TILE, id, 0, 0, 0) != buildFid(OBJ_TYPE_TILE, 1, 0, 0, 0)) {
int flag = (roof & 0xF000) >> 12;
if (on ? ((flag & 0x01) != 0) : ((flag & 0x03) == 0)) {
if (on) {
flag &= ~0x01; flag &= ~0x01;
} else {
gTileSquares[elevation]->field_0[squareTileIndex] = (squareTile & 0xFFFF) | (((flag << 12) | id) << 16); flag |= 0x01;
roof_fill_on(x - 1, y, elevation);
roof_fill_on(x + 1, y, elevation);
roof_fill_on(x, y - 1, elevation);
roof_fill_on(x, y + 1, elevation);
} }
gTileSquares[elevation]->field_0[squareTileIndex] = (squareTile & 0xFFFF) | (((flag << 12) | id) << 16);
roof_fill_push_task_if_in_bounds(tasks_stack, x - 1, y);
roof_fill_push_task_if_in_bounds(tasks_stack, x + 1, y);
roof_fill_push_task_if_in_bounds(tasks_stack, x, y - 1);
roof_fill_push_task_if_in_bounds(tasks_stack, x, y + 1);
} }
} }
} }
@ -1286,35 +1304,12 @@ static void roof_fill_on(int x, int y, int elevation)
// 0x4B23D4 // 0x4B23D4
void tile_fill_roof(int x, int y, int elevation, bool on) void tile_fill_roof(int x, int y, int elevation, bool on)
{ {
if (on) { std::stack<roof_fill_task> tasks_stack;
roof_fill_on(x, y, elevation);
} else {
roof_fill_off(x, y, elevation);
}
}
// 0x4B23DC roof_fill_push_task_if_in_bounds(tasks_stack, x, y);
static void roof_fill_off(int x, int y, int elevation)
{
if (x >= 0 && x < gSquareGridWidth && y >= 0 && y < gSquareGridHeight) {
int squareTileIndex = gSquareGridWidth * y + x;
int squareTile = gTileSquares[elevation]->field_0[squareTileIndex];
int roof = (squareTile >> 16) & 0xFFFF;
int id = roof & 0xFFF; while (!tasks_stack.empty()) {
if (buildFid(OBJ_TYPE_TILE, id, 0, 0, 0) != buildFid(OBJ_TYPE_TILE, 1, 0, 0, 0)) { roof_fill_off_process_task(tasks_stack, elevation, on);
int flag = (roof & 0xF000) >> 12;
if ((flag & 0x03) == 0) {
flag |= 0x01;
gTileSquares[elevation]->field_0[squareTileIndex] = (squareTile & 0xFFFF) | (((flag << 12) | id) << 16);
roof_fill_off(x - 1, y, elevation);
roof_fill_off(x + 1, y, elevation);
roof_fill_off(x, y - 1, elevation);
roof_fill_off(x, y + 1, elevation);
}
}
} }
} }

View File

@ -25,8 +25,8 @@ namespace fallout {
#define MAX_WINDOW_COUNT (50) #define MAX_WINDOW_COUNT (50)
// The maximum number of radio groups. // The maximum number of button groups.
#define RADIO_GROUP_LIST_CAPACITY (64) #define BUTTON_GROUP_LIST_CAPACITY (64)
static void windowFree(int win); static void windowFree(int win);
static void _win_buffering(bool a1); static void _win_buffering(bool a1);
@ -39,13 +39,13 @@ static int paletteOpenFileImpl(const char* path, int flags);
static int paletteReadFileImpl(int fd, void* buf, size_t count); static int paletteReadFileImpl(int fd, void* buf, size_t count);
static int paletteCloseFileImpl(int fd); static int paletteCloseFileImpl(int fd);
static Button* buttonCreateInternal(int win, int x, int y, int width, int height, int mouseEnterEventCode, int mouseExitEventCode, int mouseDownEventCode, int mouseUpEventCode, int flags, unsigned char* up, unsigned char* dn, unsigned char* hover); static Button* buttonCreateInternal(int win, int x, int y, int width, int height, int mouseEnterEventCode, int mouseExitEventCode, int mouseDownEventCode, int mouseUpEventCode, int flags, unsigned char* up, unsigned char* dn, unsigned char* hover);
static int _GNW_check_buttons(Window* window, int* out_a2); static int _GNW_check_buttons(Window* window, int* keyCodePtr);
static bool _button_under_mouse(Button* button, Rect* rect); static bool _button_under_mouse(Button* button, Rect* rect);
static void buttonFree(Button* ptr); static void buttonFree(Button* ptr);
static int button_new_id(); static int button_new_id();
static int _win_group_check_buttons(int a1, int* a2, int a3, void (*a4)(int)); static int _win_group_check_buttons(int buttonCount, int* btns, int maxChecked, RadioButtonCallback* func);
static int _button_check_group(Button* button); static int _button_check_group(Button* button);
static void _button_draw(Button* button, Window* window, unsigned char* data, int a4, Rect* a5, int a6); static void _button_draw(Button* button, Window* window, unsigned char* data, bool draw, Rect* bound, bool sound);
static void _GNW_button_refresh(Window* window, Rect* rect); static void _GNW_button_refresh(Window* window, Rect* rect);
// 0x50FA30 // 0x50FA30
@ -112,7 +112,7 @@ static int _doing_refresh_all;
static void* _GNW_texture; static void* _GNW_texture;
// 0x6ADF40 // 0x6ADF40
static RadioGroup gRadioGroups[RADIO_GROUP_LIST_CAPACITY]; static ButtonGroup gButtonGroups[BUTTON_GROUP_LIST_CAPACITY];
// 0x4D5C30 // 0x4D5C30
int windowManagerInit(VideoSystemInitProc* videoSystemInitProc, VideoSystemExitProc* videoSystemExitProc, int a3) int windowManagerInit(VideoSystemInitProc* videoSystemInitProc, VideoSystemExitProc* videoSystemExitProc, int a3)
@ -504,15 +504,12 @@ void windowDrawBorder(int win)
} }
// 0x4D684C // 0x4D684C
void windowDrawText(int win, const char* str, int a3, int x, int y, int a6) void windowDrawText(int win, const char* str, int width, int x, int y, int color)
{ {
int v7;
int v14;
unsigned char* buf; unsigned char* buf;
int v27; int textColor;
Window* window = windowGetWindow(win); Window* window = windowGetWindow(win);
v7 = a3;
if (!gWindowSystemInitialized) { if (!gWindowSystemInitialized) {
return; return;
@ -522,52 +519,51 @@ void windowDrawText(int win, const char* str, int a3, int x, int y, int a6)
return; return;
} }
if (a3 == 0) { if (width == 0) {
if (a6 & 0x040000) { if (color & 0x040000) {
v7 = fontGetMonospacedStringWidth(str); width = fontGetMonospacedStringWidth(str);
} else { } else {
v7 = fontGetStringWidth(str); width = fontGetStringWidth(str);
} }
} }
if (v7 + x > window->width) { if (width + x > window->width) {
if (!(a6 & 0x04000000)) { if (!(color & 0x04000000)) {
return; return;
} }
v7 = window->width - x; width = window->width - x;
} }
buf = window->buffer + x + y * window->width; buf = window->buffer + x + y * window->width;
v14 = fontGetLineHeight(); if (fontGetLineHeight() + y > window->height) {
if (v14 + y > window->height) {
return; return;
} }
if (!(a6 & 0x02000000)) { if (!(color & 0x02000000)) {
if (window->color == 256 && _GNW_texture != NULL) { if (window->color == 256 && _GNW_texture != NULL) {
_buf_texture(buf, v7, fontGetLineHeight(), window->width, _GNW_texture, window->tx + x, window->ty + y); _buf_texture(buf, width, fontGetLineHeight(), window->width, _GNW_texture, window->tx + x, window->ty + y);
} else { } else {
bufferFill(buf, v7, fontGetLineHeight(), window->width, window->color); bufferFill(buf, width, fontGetLineHeight(), window->width, window->color);
} }
} }
if (a6 & 0xFF00) { if (color & 0xFF00) {
int t = (a6 & 0xFF00) >> 8; int t = (color & 0xFF00) >> 8;
v27 = (a6 & ~0xFFFF) | _colorTable[_GNW_wcolor[t]]; textColor = (color & ~0xFFFF) | _colorTable[_GNW_wcolor[t]];
} else { } else {
v27 = a6; textColor = color;
} }
fontDrawText(buf, str, v7, window->width, v27); fontDrawText(buf, str, width, window->width, textColor);
if (a6 & 0x01000000) { if (color & 0x01000000) {
// TODO: Check. // TODO: Check.
Rect rect; Rect rect;
rect.left = window->rect.left + x; rect.left = window->rect.left + x;
rect.top = window->rect.top + y; rect.top = window->rect.top + y;
rect.right = rect.left + v7; rect.right = rect.left + width;
rect.bottom = rect.top + fontGetLineHeight(); rect.bottom = rect.top + fontGetLineHeight();
_GNW_win_refresh(window, &rect, NULL); _GNW_win_refresh(window, &rect, NULL);
} }
@ -628,7 +624,7 @@ void windowDrawRect(int win, int left, int top, int right, int bottom, int color
} }
// 0x4D6CC8 // 0x4D6CC8
void windowFill(int win, int x, int y, int width, int height, int a6) void windowFill(int win, int x, int y, int width, int height, int color)
{ {
Window* window = windowGetWindow(win); Window* window = windowGetWindow(win);
@ -640,19 +636,19 @@ void windowFill(int win, int x, int y, int width, int height, int a6)
return; return;
} }
if (a6 == 256) { if (color == 256) {
if (_GNW_texture != NULL) { if (_GNW_texture != NULL) {
_buf_texture(window->buffer + window->width * y + x, width, height, window->width, _GNW_texture, x + window->tx, y + window->ty); _buf_texture(window->buffer + window->width * y + x, width, height, window->width, _GNW_texture, x + window->tx, y + window->ty);
} else { } else {
a6 = _colorTable[_GNW_wcolor[0]] & 0xFF; color = _colorTable[_GNW_wcolor[0]] & 0xFF;
} }
} else if ((a6 & 0xFF00) != 0) { } else if ((color & 0xFF00) != 0) {
int v1 = (a6 & 0xFF00) >> 8; int v1 = (color & 0xFF00) >> 8;
a6 = (a6 & ~0xFFFF) | _colorTable[_GNW_wcolor[v1]]; color = (color & ~0xFFFF) | _colorTable[_GNW_wcolor[v1]];
} }
if (a6 < 256) { if (color < 256) {
bufferFill(window->buffer + window->width * y + x, width, height, window->width, a6); bufferFill(window->buffer + window->width * y + x, width, height, window->width, color);
} }
} }
@ -1367,12 +1363,12 @@ int buttonCreate(int win, int x, int y, int width, int height, int mouseEnterEve
return -1; return -1;
} }
Button* button = buttonCreateInternal(win, x, y, width, height, mouseEnterEventCode, mouseExitEventCode, mouseDownEventCode, mouseUpEventCode, flags | BUTTON_FLAG_0x010000, up, dn, hover); Button* button = buttonCreateInternal(win, x, y, width, height, mouseEnterEventCode, mouseExitEventCode, mouseDownEventCode, mouseUpEventCode, flags | BUTTON_FLAG_GRAPHIC, up, dn, hover);
if (button == NULL) { if (button == NULL) {
return -1; return -1;
} }
_button_draw(button, window, button->normalImage, 0, NULL, 0); _button_draw(button, window, button->normalImage, false, NULL, false);
return button->id; return button->id;
} }
@ -1469,7 +1465,7 @@ int _win_register_text_button(int win, int x, int y, int mouseEnterEventCode, in
return -1; return -1;
} }
_button_draw(button, window, button->normalImage, 0, NULL, 0); _button_draw(button, window, button->normalImage, false, NULL, false);
return button->id; return button->id;
} }
@ -1494,7 +1490,7 @@ int _win_register_button_disable(int btn, unsigned char* up, unsigned char* down
} }
// 0x4D86A8 // 0x4D86A8
int _win_register_button_image(int btn, unsigned char* up, unsigned char* down, unsigned char* hover, int a5) int _win_register_button_image(int btn, unsigned char* up, unsigned char* down, unsigned char* hover, bool draw)
{ {
if (!gWindowSystemInitialized) { if (!gWindowSystemInitialized) {
return -1; return -1;
@ -1510,7 +1506,7 @@ int _win_register_button_image(int btn, unsigned char* up, unsigned char* down,
return -1; return -1;
} }
if (!(button->flags & BUTTON_FLAG_0x010000)) { if (!(button->flags & BUTTON_FLAG_GRAPHIC)) {
return -1; return -1;
} }
@ -1527,7 +1523,7 @@ int _win_register_button_image(int btn, unsigned char* up, unsigned char* down,
button->pressedImage = down; button->pressedImage = down;
button->hoverImage = hover; button->hoverImage = hover;
_button_draw(button, window, button->currentImage, a5, NULL, 0); _button_draw(button, window, button->currentImage, draw, NULL, false);
return 0; return 0;
} }
@ -1590,7 +1586,7 @@ int buttonSetRightMouseCallbacks(int btn, int rightMouseDownEventCode, int right
// These callbacks can be triggered several times during tracking if mouse leaves button's rectangle without releasing mouse buttons. // These callbacks can be triggered several times during tracking if mouse leaves button's rectangle without releasing mouse buttons.
// //
// 0x4D87F8 // 0x4D87F8
int buttonSetCallbacks(int btn, ButtonCallback* onPressed, ButtonCallback* onUnpressed) int buttonSetCallbacks(int btn, ButtonCallback* pressSoundFunc, ButtonCallback* releaseSoundFunc)
{ {
if (!gWindowSystemInitialized) { if (!gWindowSystemInitialized) {
return -1; return -1;
@ -1601,8 +1597,8 @@ int buttonSetCallbacks(int btn, ButtonCallback* onPressed, ButtonCallback* onUnp
return -1; return -1;
} }
button->onPressed = onPressed; button->pressSoundFunc = pressSoundFunc;
button->onUnpressed = onUnpressed; button->releaseSoundFunc = releaseSoundFunc;
return 0; return 0;
} }
@ -1676,9 +1672,9 @@ Button* buttonCreateInternal(int win, int x, int y, int width, int height, int m
button->leftMouseUpProc = NULL; button->leftMouseUpProc = NULL;
button->rightMouseDownProc = NULL; button->rightMouseDownProc = NULL;
button->rightMouseUpProc = NULL; button->rightMouseUpProc = NULL;
button->onPressed = NULL; button->pressSoundFunc = NULL;
button->onUnpressed = NULL; button->releaseSoundFunc = NULL;
button->radioGroup = NULL; button->buttonGroup = NULL;
button->prev = NULL; button->prev = NULL;
button->next = window->buttonListHead; button->next = window->buttonListHead;
@ -1702,7 +1698,7 @@ bool _win_button_down(int btn)
return false; return false;
} }
if ((button->flags & BUTTON_FLAG_0x01) != 0 && (button->flags & BUTTON_FLAG_0x020000) != 0) { if ((button->flags & BUTTON_FLAG_0x01) != 0 && (button->flags & BUTTON_FLAG_CHECKED) != 0) {
return true; return true;
} }
@ -1751,10 +1747,10 @@ int _GNW_check_buttons(Window* window, int* keyCodePtr)
*keyCodePtr = prevHoveredButton->mouseExitEventCode; *keyCodePtr = prevHoveredButton->mouseExitEventCode;
} }
if ((prevHoveredButton->flags & BUTTON_FLAG_0x01) && (prevHoveredButton->flags & BUTTON_FLAG_0x020000)) { if ((prevHoveredButton->flags & BUTTON_FLAG_0x01) && (prevHoveredButton->flags & BUTTON_FLAG_CHECKED)) {
_button_draw(prevHoveredButton, window, prevHoveredButton->pressedImage, 1, NULL, 1); _button_draw(prevHoveredButton, window, prevHoveredButton->pressedImage, true, NULL, true);
} else { } else {
_button_draw(prevHoveredButton, window, prevHoveredButton->normalImage, 1, NULL, 1); _button_draw(prevHoveredButton, window, prevHoveredButton->normalImage, true, NULL, true);
} }
window->hoveredButton = NULL; window->hoveredButton = NULL;
@ -1778,10 +1774,10 @@ int _GNW_check_buttons(Window* window, int* keyCodePtr)
*keyCodePtr = prevClickedButton->mouseEnterEventCode; *keyCodePtr = prevClickedButton->mouseEnterEventCode;
} }
if ((prevClickedButton->flags & BUTTON_FLAG_0x01) && (prevClickedButton->flags & BUTTON_FLAG_0x020000)) { if ((prevClickedButton->flags & BUTTON_FLAG_0x01) && (prevClickedButton->flags & BUTTON_FLAG_CHECKED)) {
_button_draw(prevClickedButton, window, prevClickedButton->pressedImage, 1, NULL, 1); _button_draw(prevClickedButton, window, prevClickedButton->pressedImage, true, NULL, true);
} else { } else {
_button_draw(prevClickedButton, window, prevClickedButton->normalImage, 1, NULL, 1); _button_draw(prevClickedButton, window, prevClickedButton->normalImage, true, NULL, true);
} }
window->hoveredButton = prevClickedButton; window->hoveredButton = prevClickedButton;
@ -1812,10 +1808,10 @@ int _GNW_check_buttons(Window* window, int* keyCodePtr)
*keyCodePtr = v28->mouseExitEventCode; *keyCodePtr = v28->mouseExitEventCode;
} }
if ((v28->flags & BUTTON_FLAG_0x01) && (v28->flags & BUTTON_FLAG_0x020000)) { if ((v28->flags & BUTTON_FLAG_0x01) && (v28->flags & BUTTON_FLAG_CHECKED)) {
_button_draw(v28, v26, v28->pressedImage, 1, NULL, 1); _button_draw(v28, v26, v28->pressedImage, true, NULL, true);
} else { } else {
_button_draw(v28, v26, v28->normalImage, 1, NULL, 1); _button_draw(v28, v26, v28->normalImage, true, NULL, true);
} }
v26->clickedButton = NULL; v26->clickedButton = NULL;
@ -1857,10 +1853,10 @@ int _GNW_check_buttons(Window* window, int* keyCodePtr)
if ((button->flags & BUTTON_FLAG_0x01) != 0) { if ((button->flags & BUTTON_FLAG_0x01) != 0) {
if ((button->flags & BUTTON_FLAG_0x02) != 0) { if ((button->flags & BUTTON_FLAG_0x02) != 0) {
if ((button->flags & BUTTON_FLAG_0x020000) != 0) { if ((button->flags & BUTTON_FLAG_CHECKED) != 0) {
if (!(button->flags & BUTTON_FLAG_0x04)) { if (!(button->flags & BUTTON_FLAG_0x04)) {
if (button->radioGroup != NULL) { if (button->buttonGroup != NULL) {
button->radioGroup->field_4--; button->buttonGroup->currChecked--;
} }
if ((mouseEvent & MOUSE_EVENT_LEFT_BUTTON_DOWN) != 0) { if ((mouseEvent & MOUSE_EVENT_LEFT_BUTTON_DOWN) != 0) {
@ -1871,7 +1867,7 @@ int _GNW_check_buttons(Window* window, int* keyCodePtr)
cb = button->rightMouseUpProc; cb = button->rightMouseUpProc;
} }
button->flags &= ~BUTTON_FLAG_0x020000; button->flags &= ~BUTTON_FLAG_CHECKED;
} }
} else { } else {
if (_button_check_group(button) == -1) { if (_button_check_group(button) == -1) {
@ -1887,7 +1883,7 @@ int _GNW_check_buttons(Window* window, int* keyCodePtr)
cb = button->rightMouseDownProc; cb = button->rightMouseDownProc;
} }
button->flags |= BUTTON_FLAG_0x020000; button->flags |= BUTTON_FLAG_CHECKED;
} }
} }
} else { } else {
@ -1905,7 +1901,7 @@ int _GNW_check_buttons(Window* window, int* keyCodePtr)
} }
} }
_button_draw(button, window, button->pressedImage, 1, NULL, 1); _button_draw(button, window, button->pressedImage, true, NULL, true);
break; break;
} }
@ -1916,10 +1912,10 @@ int _GNW_check_buttons(Window* window, int* keyCodePtr)
if (v49->flags & BUTTON_FLAG_0x01) { if (v49->flags & BUTTON_FLAG_0x01) {
if (!(v49->flags & BUTTON_FLAG_0x02)) { if (!(v49->flags & BUTTON_FLAG_0x02)) {
if (v49->flags & BUTTON_FLAG_0x020000) { if (v49->flags & BUTTON_FLAG_CHECKED) {
if (!(v49->flags & BUTTON_FLAG_0x04)) { if (!(v49->flags & BUTTON_FLAG_0x04)) {
if (v49->radioGroup != NULL) { if (v49->buttonGroup != NULL) {
v49->radioGroup->field_4--; v49->buttonGroup->currChecked--;
} }
if ((mouseEvent & MOUSE_EVENT_LEFT_BUTTON_UP) != 0) { if ((mouseEvent & MOUSE_EVENT_LEFT_BUTTON_UP) != 0) {
@ -1930,12 +1926,12 @@ int _GNW_check_buttons(Window* window, int* keyCodePtr)
cb = button->rightMouseUpProc; cb = button->rightMouseUpProc;
} }
button->flags &= ~BUTTON_FLAG_0x020000; button->flags &= ~BUTTON_FLAG_CHECKED;
} }
} else { } else {
if (_button_check_group(v49) == -1) { if (_button_check_group(v49) == -1) {
button = NULL; button = NULL;
_button_draw(v49, window, v49->normalImage, 1, NULL, 1); _button_draw(v49, window, v49->normalImage, true, NULL, true);
break; break;
} }
@ -1947,13 +1943,13 @@ int _GNW_check_buttons(Window* window, int* keyCodePtr)
cb = v49->rightMouseDownProc; cb = v49->rightMouseDownProc;
} }
v49->flags |= BUTTON_FLAG_0x020000; v49->flags |= BUTTON_FLAG_CHECKED;
} }
} }
} else { } else {
if (v49->flags & BUTTON_FLAG_0x020000) { if (v49->flags & BUTTON_FLAG_CHECKED) {
if (v49->radioGroup != NULL) { if (v49->buttonGroup != NULL) {
v49->radioGroup->field_4--; v49->buttonGroup->currChecked--;
} }
} }
@ -1967,9 +1963,9 @@ int _GNW_check_buttons(Window* window, int* keyCodePtr)
} }
if (button->hoverImage != NULL) { if (button->hoverImage != NULL) {
_button_draw(button, window, button->hoverImage, 1, NULL, 1); _button_draw(button, window, button->hoverImage, true, NULL, true);
} else { } else {
_button_draw(button, window, button->normalImage, 1, NULL, 1); _button_draw(button, window, button->normalImage, true, NULL, true);
} }
break; break;
} }
@ -1982,7 +1978,7 @@ int _GNW_check_buttons(Window* window, int* keyCodePtr)
cb = button->mouseEnterProc; cb = button->mouseEnterProc;
} }
_button_draw(button, window, button->hoverImage, 1, NULL, 1); _button_draw(button, window, button->hoverImage, true, NULL, true);
} }
break; break;
} }
@ -1995,7 +1991,7 @@ int _GNW_check_buttons(Window* window, int* keyCodePtr)
&& (mouseEvent & MOUSE_EVENT_ANY_BUTTON_DOWN) != 0 && (mouseEvent & MOUSE_EVENT_ANY_BUTTON_DOWN) != 0
&& (mouseEvent & MOUSE_EVENT_ANY_BUTTON_REPEAT) == 0) { && (mouseEvent & MOUSE_EVENT_ANY_BUTTON_REPEAT) == 0) {
_win_drag(window->id); _win_drag(window->id);
_button_draw(button, window, button->normalImage, 1, NULL, 1); _button_draw(button, window, button->normalImage, true, NULL, true);
} }
} else if ((window->flags & WINDOW_FLAG_0x80) != 0) { } else if ((window->flags & WINDOW_FLAG_0x80) != 0) {
v25 |= mouseEvent << 8; v25 |= mouseEvent << 8;
@ -2023,13 +2019,13 @@ int _GNW_check_buttons(Window* window, int* keyCodePtr)
*keyCodePtr = prevHoveredButton->mouseExitEventCode; *keyCodePtr = prevHoveredButton->mouseExitEventCode;
unsigned char* data; unsigned char* data;
if ((prevHoveredButton->flags & BUTTON_FLAG_0x01) && (prevHoveredButton->flags & BUTTON_FLAG_0x020000)) { if ((prevHoveredButton->flags & BUTTON_FLAG_0x01) && (prevHoveredButton->flags & BUTTON_FLAG_CHECKED)) {
data = prevHoveredButton->pressedImage; data = prevHoveredButton->pressedImage;
} else { } else {
data = prevHoveredButton->normalImage; data = prevHoveredButton->normalImage;
} }
_button_draw(prevHoveredButton, window, data, 1, NULL, 1); _button_draw(prevHoveredButton, window, data, true, NULL, true);
window->hoveredButton = NULL; window->hoveredButton = NULL;
} }
@ -2142,7 +2138,7 @@ int buttonDestroy(int btn)
// 0x4D9374 // 0x4D9374
void buttonFree(Button* button) void buttonFree(Button* button)
{ {
if ((button->flags & BUTTON_FLAG_0x010000) == 0) { if ((button->flags & BUTTON_FLAG_GRAPHIC) == 0) {
if (button->normalImage != NULL) { if (button->normalImage != NULL) {
internal_free(button->normalImage); internal_free(button->normalImage);
} }
@ -2168,15 +2164,15 @@ void buttonFree(Button* button)
} }
} }
RadioGroup* radioGroup = button->radioGroup; ButtonGroup* buttonGroup = button->buttonGroup;
if (radioGroup != NULL) { if (buttonGroup != NULL) {
for (int index = 0; index < radioGroup->buttonsLength; index++) { for (int index = 0; index < buttonGroup->buttonsLength; index++) {
if (button == radioGroup->buttons[index]) { if (button == buttonGroup->buttons[index]) {
for (; index < radioGroup->buttonsLength - 1; index++) { for (; index < buttonGroup->buttonsLength - 1; index++) {
radioGroup->buttons[index] = radioGroup->buttons[index + 1]; buttonGroup->buttons[index] = buttonGroup->buttons[index + 1];
} }
radioGroup->buttonsLength--; buttonGroup->buttonsLength--;
break; break;
} }
@ -2216,7 +2212,7 @@ int buttonEnable(int btn)
if ((button->flags & BUTTON_FLAG_DISABLED) != 0) { if ((button->flags & BUTTON_FLAG_DISABLED) != 0) {
button->flags &= ~BUTTON_FLAG_DISABLED; button->flags &= ~BUTTON_FLAG_DISABLED;
_button_draw(button, window, button->currentImage, 1, NULL, 0); _button_draw(button, window, button->currentImage, true, NULL, false);
} }
return 0; return 0;
@ -2238,7 +2234,7 @@ int buttonDisable(int btn)
if ((button->flags & BUTTON_FLAG_DISABLED) == 0) { if ((button->flags & BUTTON_FLAG_DISABLED) == 0) {
button->flags |= BUTTON_FLAG_DISABLED; button->flags |= BUTTON_FLAG_DISABLED;
_button_draw(button, window, button->currentImage, 1, NULL, 0); _button_draw(button, window, button->currentImage, true, NULL, false);
if (button == window->hoveredButton) { if (button == window->hoveredButton) {
if (window->hoveredButton->mouseExitEventCode != -1) { if (window->hoveredButton->mouseExitEventCode != -1) {
@ -2252,7 +2248,7 @@ int buttonDisable(int btn)
} }
// 0x4D9554 // 0x4D9554
int _win_set_button_rest_state(int btn, bool a2, int a3) int _win_set_button_rest_state(int btn, bool checked, int flags)
{ {
if (!gWindowSystemInitialized) { if (!gWindowSystemInitialized) {
return -1; return -1;
@ -2267,30 +2263,30 @@ int _win_set_button_rest_state(int btn, bool a2, int a3)
if ((button->flags & BUTTON_FLAG_0x01) != 0) { if ((button->flags & BUTTON_FLAG_0x01) != 0) {
int keyCode = -1; int keyCode = -1;
if ((button->flags & BUTTON_FLAG_0x020000) != 0) { if ((button->flags & BUTTON_FLAG_CHECKED) != 0) {
if (!a2) { if (!checked) {
button->flags &= ~BUTTON_FLAG_0x020000; button->flags &= ~BUTTON_FLAG_CHECKED;
if ((a3 & 0x02) == 0) { if ((flags & 0x02) == 0) {
_button_draw(button, window, button->normalImage, 1, NULL, 0); _button_draw(button, window, button->normalImage, true, NULL, false);
} }
if (button->radioGroup != NULL) { if (button->buttonGroup != NULL) {
button->radioGroup->field_4--; button->buttonGroup->currChecked--;
} }
keyCode = button->leftMouseUpEventCode; keyCode = button->leftMouseUpEventCode;
} }
} else { } else {
if (a2) { if (checked) {
button->flags |= BUTTON_FLAG_0x020000; button->flags |= BUTTON_FLAG_CHECKED;
if ((a3 & 0x02) == 0) { if ((flags & 0x02) == 0) {
_button_draw(button, window, button->pressedImage, 1, NULL, 0); _button_draw(button, window, button->pressedImage, true, NULL, false);
} }
if (button->radioGroup != NULL) { if (button->buttonGroup != NULL) {
button->radioGroup->field_4++; button->buttonGroup->currChecked++;
} }
keyCode = button->lefMouseDownEventCode; keyCode = button->lefMouseDownEventCode;
@ -2298,7 +2294,7 @@ int _win_set_button_rest_state(int btn, bool a2, int a3)
} }
if (keyCode != -1) { if (keyCode != -1) {
if ((a3 & 0x01) != 0) { if ((flags & 0x01) != 0) {
enqueueInputEvent(keyCode); enqueueInputEvent(keyCode);
} }
} }
@ -2308,20 +2304,20 @@ int _win_set_button_rest_state(int btn, bool a2, int a3)
} }
// 0x4D962C // 0x4D962C
int _win_group_check_buttons(int buttonCount, int* btns, int a3, void (*a4)(int)) int _win_group_check_buttons(int buttonCount, int* btns, int maxChecked, RadioButtonCallback* func)
{ {
if (!gWindowSystemInitialized) { if (!gWindowSystemInitialized) {
return -1; return -1;
} }
if (buttonCount >= RADIO_GROUP_BUTTON_LIST_CAPACITY) { if (buttonCount >= BUTTON_GROUP_BUTTON_LIST_CAPACITY) {
return -1; return -1;
} }
for (int groupIndex = 0; groupIndex < RADIO_GROUP_LIST_CAPACITY; groupIndex++) { for (int groupIndex = 0; groupIndex < BUTTON_GROUP_LIST_CAPACITY; groupIndex++) {
RadioGroup* radioGroup = &(gRadioGroups[groupIndex]); ButtonGroup* buttonGroup = &(gButtonGroups[groupIndex]);
if (radioGroup->buttonsLength == 0) { if (buttonGroup->buttonsLength == 0) {
radioGroup->field_4 = 0; buttonGroup->currChecked = 0;
for (int buttonIndex = 0; buttonIndex < buttonCount; buttonIndex++) { for (int buttonIndex = 0; buttonIndex < buttonCount; buttonIndex++) {
Button* button = buttonGetButton(btns[buttonIndex], NULL); Button* button = buttonGetButton(btns[buttonIndex], NULL);
@ -2329,18 +2325,18 @@ int _win_group_check_buttons(int buttonCount, int* btns, int a3, void (*a4)(int)
return -1; return -1;
} }
radioGroup->buttons[buttonIndex] = button; buttonGroup->buttons[buttonIndex] = button;
button->radioGroup = radioGroup; button->buttonGroup = buttonGroup;
if ((button->flags & BUTTON_FLAG_0x020000) != 0) { if ((button->flags & BUTTON_FLAG_CHECKED) != 0) {
radioGroup->field_4++; buttonGroup->currChecked++;
} }
} }
radioGroup->buttonsLength = buttonCount; buttonGroup->buttonsLength = buttonCount;
radioGroup->field_0 = a3; buttonGroup->maxChecked = maxChecked;
radioGroup->field_8 = a4; buttonGroup->func = func;
return 0; return 0;
} }
} }
@ -2360,11 +2356,11 @@ int _win_group_radio_buttons(int count, int* btns)
} }
Button* button = buttonGetButton(btns[0], NULL); Button* button = buttonGetButton(btns[0], NULL);
RadioGroup* radioGroup = button->radioGroup; ButtonGroup* buttonGroup = button->buttonGroup;
for (int index = 0; index < radioGroup->buttonsLength; index++) { for (int index = 0; index < buttonGroup->buttonsLength; index++) {
Button* v1 = radioGroup->buttons[index]; Button* v1 = buttonGroup->buttons[index];
v1->flags |= BUTTON_FLAG_0x040000; v1->flags |= BUTTON_FLAG_RADIO;
} }
return 0; return 0;
@ -2373,20 +2369,20 @@ int _win_group_radio_buttons(int count, int* btns)
// 0x4D9744 // 0x4D9744
int _button_check_group(Button* button) int _button_check_group(Button* button)
{ {
if (button->radioGroup == NULL) { if (button->buttonGroup == NULL) {
return 0; return 0;
} }
if ((button->flags & BUTTON_FLAG_0x040000) != 0) { if ((button->flags & BUTTON_FLAG_RADIO) != 0) {
if (button->radioGroup->field_4 > 0) { if (button->buttonGroup->currChecked > 0) {
for (int index = 0; index < button->radioGroup->buttonsLength; index++) { for (int index = 0; index < button->buttonGroup->buttonsLength; index++) {
Button* v1 = button->radioGroup->buttons[index]; Button* v1 = button->buttonGroup->buttons[index];
if ((v1->flags & BUTTON_FLAG_0x020000) != 0) { if ((v1->flags & BUTTON_FLAG_CHECKED) != 0) {
v1->flags &= ~BUTTON_FLAG_0x020000; v1->flags &= ~BUTTON_FLAG_CHECKED;
Window* window; Window* window;
buttonGetButton(v1->id, &window); buttonGetButton(v1->id, &window);
_button_draw(v1, window, v1->normalImage, 1, NULL, 1); _button_draw(v1, window, v1->normalImage, true, NULL, true);
if (v1->leftMouseUpProc != NULL) { if (v1->leftMouseUpProc != NULL) {
v1->leftMouseUpProc(v1->id, v1->leftMouseUpEventCode); v1->leftMouseUpProc(v1->id, v1->leftMouseUpEventCode);
@ -2395,30 +2391,30 @@ int _button_check_group(Button* button)
} }
} }
if ((button->flags & BUTTON_FLAG_0x020000) == 0) { if ((button->flags & BUTTON_FLAG_CHECKED) == 0) {
button->radioGroup->field_4++; button->buttonGroup->currChecked++;
} }
return 0; return 0;
} }
if (button->radioGroup->field_4 < button->radioGroup->field_0) { if (button->buttonGroup->currChecked < button->buttonGroup->maxChecked) {
if ((button->flags & BUTTON_FLAG_0x020000) == 0) { if ((button->flags & BUTTON_FLAG_CHECKED) == 0) {
button->radioGroup->field_4++; button->buttonGroup->currChecked++;
} }
return 0; return 0;
} }
if (button->radioGroup->field_8 != NULL) { if (button->buttonGroup->func != NULL) {
button->radioGroup->field_8(button->id); button->buttonGroup->func(button->id);
} }
return -1; return -1;
} }
// 0x4D9808 // 0x4D9808
void _button_draw(Button* button, Window* window, unsigned char* data, int a4, Rect* a5, int a6) void _button_draw(Button* button, Window* window, unsigned char* data, bool draw, Rect* bound, bool sound)
{ {
unsigned char* previousImage = NULL; unsigned char* previousImage = NULL;
if (data != NULL) { if (data != NULL) {
@ -2427,8 +2423,8 @@ void _button_draw(Button* button, Window* window, unsigned char* data, int a4, R
rectOffset(&v2, window->rect.left, window->rect.top); rectOffset(&v2, window->rect.left, window->rect.top);
Rect v3; Rect v3;
if (a5 != NULL) { if (bound != NULL) {
if (rectIntersection(&v2, a5, &v2) == -1) { if (rectIntersection(&v2, bound, &v2) == -1) {
return; return;
} }
@ -2438,7 +2434,7 @@ void _button_draw(Button* button, Window* window, unsigned char* data, int a4, R
rectCopy(&v3, &(button->rect)); rectCopy(&v3, &(button->rect));
} }
if (data == button->normalImage && (button->flags & BUTTON_FLAG_0x020000)) { if (data == button->normalImage && (button->flags & BUTTON_FLAG_CHECKED)) {
data = button->pressedImage; data = button->pressedImage;
} }
@ -2461,7 +2457,7 @@ void _button_draw(Button* button, Window* window, unsigned char* data, int a4, R
} }
if (data) { if (data) {
if (a4 == 0) { if (!draw) {
int width = button->rect.right - button->rect.left + 1; int width = button->rect.right - button->rect.left + 1;
if ((button->flags & BUTTON_FLAG_TRANSPARENT) != 0) { if ((button->flags & BUTTON_FLAG_TRANSPARENT) != 0) {
blitBufferToBufferTrans( blitBufferToBufferTrans(
@ -2485,18 +2481,18 @@ void _button_draw(Button* button, Window* window, unsigned char* data, int a4, R
previousImage = button->currentImage; previousImage = button->currentImage;
button->currentImage = data; button->currentImage = data;
if (a4 != 0) { if (draw) {
_GNW_win_refresh(window, &v2, 0); _GNW_win_refresh(window, &v2, 0);
} }
} }
} }
if (a6) { if (sound) {
if (previousImage != data) { if (previousImage != data) {
if (data == button->pressedImage && button->onPressed != NULL) { if (data == button->pressedImage && button->pressSoundFunc != NULL) {
button->onPressed(button->id, button->lefMouseDownEventCode); button->pressSoundFunc(button->id, button->lefMouseDownEventCode);
} else if (data == button->normalImage && button->onUnpressed != NULL) { } else if (data == button->normalImage && button->releaseSoundFunc != NULL) {
button->onUnpressed(button->id, button->leftMouseUpEventCode); button->releaseSoundFunc(button->id, button->leftMouseUpEventCode);
} }
} }
} }
@ -2513,7 +2509,7 @@ void _GNW_button_refresh(Window* window, Rect* rect)
} }
while (button != NULL) { while (button != NULL) {
_button_draw(button, window, button->currentImage, 0, rect, 0); _button_draw(button, window, button->currentImage, false, rect, false);
button = button->prev; button = button->prev;
} }
} }
@ -2531,7 +2527,7 @@ int _win_button_press_and_release(int btn)
return -1; return -1;
} }
_button_draw(button, window, button->pressedImage, 1, NULL, 1); _button_draw(button, window, button->pressedImage, true, NULL, true);
if (button->leftMouseDownProc != NULL) { if (button->leftMouseDownProc != NULL) {
button->leftMouseDownProc(btn, button->lefMouseDownEventCode); button->leftMouseDownProc(btn, button->lefMouseDownEventCode);
@ -2545,7 +2541,7 @@ int _win_button_press_and_release(int btn)
} }
} }
_button_draw(button, window, button->normalImage, 1, NULL, 1); _button_draw(button, window, button->normalImage, true, NULL, true);
if (button->leftMouseUpProc != NULL) { if (button->leftMouseUpProc != NULL) {
button->leftMouseUpProc(btn, button->leftMouseUpEventCode); button->leftMouseUpProc(btn, button->leftMouseUpEventCode);

View File

@ -8,7 +8,7 @@
namespace fallout { namespace fallout {
// The maximum number of buttons in one radio group. // The maximum number of buttons in one radio group.
#define RADIO_GROUP_BUTTON_LIST_CAPACITY (64) #define BUTTON_GROUP_BUTTON_LIST_CAPACITY (64)
typedef enum WindowManagerErr { typedef enum WindowManagerErr {
WINDOW_MANAGER_OK = 0, WINDOW_MANAGER_OK = 0,
@ -55,9 +55,9 @@ typedef enum ButtonFlags {
BUTTON_FLAG_0x10 = 0x10, BUTTON_FLAG_0x10 = 0x10,
BUTTON_FLAG_TRANSPARENT = 0x20, BUTTON_FLAG_TRANSPARENT = 0x20,
BUTTON_FLAG_0x40 = 0x40, BUTTON_FLAG_0x40 = 0x40,
BUTTON_FLAG_0x010000 = 0x010000, BUTTON_FLAG_GRAPHIC = 0x010000,
BUTTON_FLAG_0x020000 = 0x020000, BUTTON_FLAG_CHECKED = 0x020000,
BUTTON_FLAG_0x040000 = 0x040000, BUTTON_FLAG_RADIO = 0x040000,
BUTTON_FLAG_RIGHT_MOUSE_BUTTON_CONFIGURED = 0x080000, BUTTON_FLAG_RIGHT_MOUSE_BUTTON_CONFIGURED = 0x080000,
} ButtonFlags; } ButtonFlags;
@ -66,8 +66,8 @@ typedef struct MenuPulldown {
int keyCode; int keyCode;
int itemsLength; int itemsLength;
char** items; char** items;
int field_1C; int foregroundColor;
int field_20; int backgroundColor;
} MenuPulldown; } MenuPulldown;
typedef struct MenuBar { typedef struct MenuBar {
@ -75,14 +75,14 @@ typedef struct MenuBar {
Rect rect; Rect rect;
int pulldownsLength; int pulldownsLength;
MenuPulldown pulldowns[15]; MenuPulldown pulldowns[15];
int borderColor; int foregroundColor;
int backgroundColor; int backgroundColor;
} MenuBar; } MenuBar;
typedef void WindowBlitProc(unsigned char* src, int width, int height, int srcPitch, unsigned char* dest, int destPitch); typedef void WindowBlitProc(unsigned char* src, int width, int height, int srcPitch, unsigned char* dest, int destPitch);
typedef struct Button Button; typedef struct Button Button;
typedef struct RadioGroup RadioGroup; typedef struct ButtonGroup ButtonGroup;
typedef struct Window { typedef struct Window {
int id; int id;
@ -102,6 +102,7 @@ typedef struct Window {
} Window; } Window;
typedef void ButtonCallback(int btn, int keyCode); typedef void ButtonCallback(int btn, int keyCode);
typedef void RadioButtonCallback(int btn);
typedef struct Button { typedef struct Button {
int id; int id;
@ -127,20 +128,20 @@ typedef struct Button {
ButtonCallback* leftMouseUpProc; ButtonCallback* leftMouseUpProc;
ButtonCallback* rightMouseDownProc; ButtonCallback* rightMouseDownProc;
ButtonCallback* rightMouseUpProc; ButtonCallback* rightMouseUpProc;
ButtonCallback* onPressed; ButtonCallback* pressSoundFunc;
ButtonCallback* onUnpressed; ButtonCallback* releaseSoundFunc;
RadioGroup* radioGroup; ButtonGroup* buttonGroup;
Button* prev; Button* prev;
Button* next; Button* next;
} Button; } Button;
typedef struct RadioGroup { typedef struct ButtonGroup {
int field_0; int maxChecked;
int field_4; int currChecked;
void (*field_8)(int); RadioButtonCallback* func;
int buttonsLength; int buttonsLength;
Button* buttons[RADIO_GROUP_BUTTON_LIST_CAPACITY]; Button* buttons[BUTTON_GROUP_BUTTON_LIST_CAPACITY];
} RadioGroup; } ButtonGroup;
typedef int(VideoSystemInitProc)(); typedef int(VideoSystemInitProc)();
typedef void(VideoSystemExitProc)(); typedef void(VideoSystemExitProc)();
@ -157,7 +158,7 @@ void windowDrawText(int win, const char* str, int a3, int x, int y, int a6);
void _win_text(int win, char** fileNameList, int fileNameListLength, int maxWidth, int x, int y, int flags); void _win_text(int win, char** fileNameList, int fileNameListLength, int maxWidth, int x, int y, int flags);
void windowDrawLine(int win, int left, int top, int right, int bottom, int color); void windowDrawLine(int win, int left, int top, int right, int bottom, int color);
void windowDrawRect(int win, int left, int top, int right, int bottom, int color); void windowDrawRect(int win, int left, int top, int right, int bottom, int color);
void windowFill(int win, int x, int y, int width, int height, int a6); void windowFill(int win, int x, int y, int width, int height, int color);
void windowShow(int win); void windowShow(int win);
void windowHide(int win); void windowHide(int win);
void windowRefresh(int win); void windowRefresh(int win);
@ -178,10 +179,10 @@ 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); 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);
int _win_register_text_button(int win, int x, int y, int mouseEnterEventCode, int mouseExitEventCode, int mouseDownEventCode, int mouseUpEventCode, const char* title, int flags); int _win_register_text_button(int win, int x, int y, int mouseEnterEventCode, int mouseExitEventCode, int mouseDownEventCode, int mouseUpEventCode, const char* title, int flags);
int _win_register_button_disable(int btn, unsigned char* up, unsigned char* down, unsigned char* hover); int _win_register_button_disable(int btn, unsigned char* up, unsigned char* down, unsigned char* hover);
int _win_register_button_image(int btn, unsigned char* up, unsigned char* down, unsigned char* hover, int a5); int _win_register_button_image(int btn, unsigned char* up, unsigned char* down, unsigned char* hover, bool draw);
int buttonSetMouseCallbacks(int btn, ButtonCallback* mouseEnterProc, ButtonCallback* mouseExitProc, ButtonCallback* mouseDownProc, ButtonCallback* mouseUpProc); int buttonSetMouseCallbacks(int btn, ButtonCallback* mouseEnterProc, ButtonCallback* mouseExitProc, ButtonCallback* mouseDownProc, ButtonCallback* mouseUpProc);
int buttonSetRightMouseCallbacks(int btn, int rightMouseDownEventCode, int rightMouseUpEventCode, ButtonCallback* rightMouseDownProc, ButtonCallback* rightMouseUpProc); int buttonSetRightMouseCallbacks(int btn, int rightMouseDownEventCode, int rightMouseUpEventCode, ButtonCallback* rightMouseDownProc, ButtonCallback* rightMouseUpProc);
int buttonSetCallbacks(int btn, ButtonCallback* onPressed, ButtonCallback* onUnpressed); int buttonSetCallbacks(int btn, ButtonCallback* pressSoundFunc, ButtonCallback* releaseSoundFunc);
int buttonSetMask(int btn, unsigned char* mask); int buttonSetMask(int btn, unsigned char* mask);
bool _win_button_down(int btn); bool _win_button_down(int btn);
int buttonGetWindowId(int btn); int buttonGetWindowId(int btn);
@ -189,8 +190,8 @@ int _win_last_button_winID();
int buttonDestroy(int btn); int buttonDestroy(int btn);
int buttonEnable(int btn); int buttonEnable(int btn);
int buttonDisable(int btn); int buttonDisable(int btn);
int _win_set_button_rest_state(int btn, bool a2, int a3); int _win_set_button_rest_state(int btn, bool checked, int flags);
int _win_group_radio_buttons(int a1, int* a2); int _win_group_radio_buttons(int buttonCount, int* btns);
int _win_button_press_and_release(int btn); int _win_button_press_and_release(int btn);
} // namespace fallout } // namespace fallout

View File

@ -79,13 +79,13 @@ static int _currx;
char gProgramWindowTitle[256]; char gProgramWindowTitle[256];
// 0x4DA6C0 // 0x4DA6C0
int _win_list_select(const char* title, char** fileList, int fileListLength, ListSelectionHandler* callback, int x, int y, int a7) int _win_list_select(const char* title, char** fileList, int fileListLength, ListSelectionHandler* callback, int x, int y, int color)
{ {
return _win_list_select_at(title, fileList, fileListLength, callback, x, y, a7, 0); return _win_list_select_at(title, fileList, fileListLength, callback, x, y, color, 0);
} }
// 0x4DA70C // 0x4DA70C
int _win_list_select_at(const char* title, char** items, int itemsLength, ListSelectionHandler* callback, int x, int y, int a7, int a8) int _win_list_select_at(const char* title, char** items, int itemsLength, ListSelectionHandler* callback, int x, int y, int color, int start)
{ {
if (!gWindowSystemInitialized) { if (!gWindowSystemInitialized) {
return -1; return -1;
@ -170,8 +170,8 @@ int _win_list_select_at(const char* title, char** items, int itemsLength, ListSe
windowWidth, windowWidth,
_colorTable[_GNW_wcolor[0]]); _colorTable[_GNW_wcolor[0]]);
int scrollOffset = a8; int scrollOffset = start;
if (a8 < 0 || a8 >= itemsLength) { if (start < 0 || start >= itemsLength) {
scrollOffset = 0; scrollOffset = 0;
} }
@ -189,14 +189,13 @@ int _win_list_select_at(const char* title, char** items, int itemsLength, ListSe
selectedItemIndex = 0; selectedItemIndex = 0;
} }
char** itemsTO = items + a8;
_win_text(win, _win_text(win,
items + a8, items + start,
itemsLength < listViewCapacity ? itemsLength : listViewCapacity, itemsLength < listViewCapacity ? itemsLength : listViewCapacity,
listViewWidth, listViewWidth,
listViewX, listViewX,
listViewY, listViewY,
a7 | 0x2000000); color | 0x2000000);
_lighten_buf(listViewBuffer + windowWidth * selectedItemIndex * fontGetLineHeight(), _lighten_buf(listViewBuffer + windowWidth * selectedItemIndex * fontGetLineHeight(),
listViewWidth, listViewWidth,
@ -452,7 +451,7 @@ int _win_list_select_at(const char* title, char** items, int itemsLength, ListSe
listViewWidth, listViewWidth,
listViewX, listViewX,
listViewY, listViewY,
a7 | 0x2000000); color | 0x2000000);
_lighten_buf(listViewBuffer + windowWidth * selectedItemIndex * fontGetLineHeight(), _lighten_buf(listViewBuffer + windowWidth * selectedItemIndex * fontGetLineHeight(),
listViewWidth, listViewWidth,
@ -500,19 +499,19 @@ int _win_list_select_at(const char* title, char** items, int itemsLength, ListSe
windowWidth, windowWidth,
_colorTable[_GNW_wcolor[0]]); _colorTable[_GNW_wcolor[0]]);
int color; int textColor;
if ((a7 & 0xFF00) != 0) { if ((color & 0xFF00) != 0) {
int colorIndex = (a7 & 0xFF) - 1; int colorIndex = (color & 0xFF) - 1;
color = (a7 & ~0xFFFF) | _colorTable[_GNW_wcolor[colorIndex]]; textColor = (color & ~0xFFFF) | _colorTable[_GNW_wcolor[colorIndex]];
} else { } else {
color = a7; textColor = color;
} }
fontDrawText(listViewBuffer + windowWidth * previousSelectedItemIndex * fontGetLineHeight(), fontDrawText(listViewBuffer + windowWidth * previousSelectedItemIndex * fontGetLineHeight(),
items[scrollOffset + previousSelectedItemIndex], items[scrollOffset + previousSelectedItemIndex],
windowWidth, windowWidth,
windowWidth, windowWidth,
color); textColor);
_GNW_win_refresh(window, &itemRect, NULL); _GNW_win_refresh(window, &itemRect, NULL);
} }
@ -619,7 +618,7 @@ int _win_get_str(char* dest, int length, const char* title, int x, int y)
} }
// 0x4DBA98 // 0x4DBA98
int _win_msg(const char* string, int x, int y, int flags) int _win_msg(const char* string, int x, int y, int color)
{ {
if (!gWindowSystemInitialized) { if (!gWindowSystemInitialized) {
return -1; return -1;
@ -644,16 +643,16 @@ int _win_msg(const char* string, int x, int y, int flags)
Window* window = windowGetWindow(win); Window* window = windowGetWindow(win);
unsigned char* windowBuffer = window->buffer; unsigned char* windowBuffer = window->buffer;
int color; int textColor;
if ((flags & 0xFF00) != 0) { if ((color & 0xFF00) != 0) {
int index = (flags & 0xFF) - 1; int index = (color & 0xFF) - 1;
color = _colorTable[_GNW_wcolor[index]]; textColor = _colorTable[_GNW_wcolor[index]];
color |= flags & ~0xFFFF; textColor |= color & ~0xFFFF;
} else { } else {
color = flags; textColor = color;
} }
fontDrawText(windowBuffer + windowWidth * 8 + 16, string, windowWidth, windowWidth, color); fontDrawText(windowBuffer + windowWidth * 8 + 16, string, windowWidth, windowWidth, textColor);
_win_register_text_button(win, _win_register_text_button(win,
windowWidth / 2 - 32, windowWidth / 2 - 32,
@ -679,23 +678,23 @@ int _win_msg(const char* string, int x, int y, int flags)
} }
// 0x4DBBC4 // 0x4DBBC4
int _win_pull_down(char** items, int itemsLength, int x, int y, int a5) int _win_pull_down(char** items, int itemsLength, int x, int y, int color)
{ {
if (!gWindowSystemInitialized) { if (!gWindowSystemInitialized) {
return -1; return -1;
} }
Rect rect; Rect rect;
int win = _create_pull_down(items, itemsLength, x, y, a5, _colorTable[_GNW_wcolor[0]], &rect); int win = _create_pull_down(items, itemsLength, x, y, color, _colorTable[_GNW_wcolor[0]], &rect);
if (win == -1) { if (win == -1) {
return -1; return -1;
} }
return sub_4DBD04(win, &rect, items, itemsLength, a5, _colorTable[_GNW_wcolor[0]], NULL, -1); return process_pull_down(win, &rect, items, itemsLength, color, _colorTable[_GNW_wcolor[0]], NULL, -1);
} }
// 0x4DBC34 // 0x4DBC34
int _create_pull_down(char** stringList, int stringListLength, int x, int y, int a5, int a6, Rect* rect) int _create_pull_down(char** stringList, int stringListLength, int x, int y, int foregroundColor, int backgroundColor, Rect* rect)
{ {
int windowHeight = stringListLength * fontGetLineHeight() + 16; int windowHeight = stringListLength * fontGetLineHeight() + 16;
int windowWidth = _win_width_needed(stringList, stringListLength) + 4; int windowWidth = _win_width_needed(stringList, stringListLength) + 4;
@ -703,14 +702,14 @@ int _create_pull_down(char** stringList, int stringListLength, int x, int y, int
return -1; return -1;
} }
int win = windowCreate(x, y, windowWidth, windowHeight, a6, WINDOW_MODAL | WINDOW_MOVE_ON_TOP); int win = windowCreate(x, y, windowWidth, windowHeight, backgroundColor, WINDOW_MODAL | WINDOW_MOVE_ON_TOP);
if (win == -1) { if (win == -1) {
return -1; return -1;
} }
_win_text(win, stringList, stringListLength, windowWidth - 4, 2, 8, a5); _win_text(win, stringList, stringListLength, windowWidth - 4, 2, 8, foregroundColor);
windowDrawRect(win, 0, 0, windowWidth - 1, windowHeight - 1, _colorTable[0]); windowDrawRect(win, 0, 0, windowWidth - 1, windowHeight - 1, _colorTable[0]);
windowDrawRect(win, 1, 1, windowWidth - 2, windowHeight - 2, a5); windowDrawRect(win, 1, 1, windowWidth - 2, windowHeight - 2, foregroundColor);
windowRefresh(win); windowRefresh(win);
windowGetRect(win, rect); windowGetRect(win, rect);
@ -841,7 +840,7 @@ void _win_debug_delete(int btn, int keyCode)
} }
// 0x4DC674 // 0x4DC674
int _win_register_menu_bar(int win, int x, int y, int width, int height, int borderColor, int backgroundColor) int _win_register_menu_bar(int win, int x, int y, int width, int height, int foregroundColor, int backgroundColor)
{ {
Window* window = windowGetWindow(win); Window* window = windowGetWindow(win);
@ -878,17 +877,17 @@ int _win_register_menu_bar(int win, int x, int y, int width, int height, int bor
menuBar->rect.right = right - 1; menuBar->rect.right = right - 1;
menuBar->rect.bottom = bottom - 1; menuBar->rect.bottom = bottom - 1;
menuBar->pulldownsLength = 0; menuBar->pulldownsLength = 0;
menuBar->borderColor = borderColor; menuBar->foregroundColor = foregroundColor;
menuBar->backgroundColor = backgroundColor; menuBar->backgroundColor = backgroundColor;
windowFill(win, x, y, width, height, backgroundColor); windowFill(win, x, y, width, height, backgroundColor);
windowDrawRect(win, x, y, right - 1, bottom - 1, borderColor); windowDrawRect(win, x, y, right - 1, bottom - 1, foregroundColor);
return 0; return 0;
} }
// 0x4DC768 // 0x4DC768
int _win_register_menu_pulldown(int win, int x, char* title, int keyCode, int itemsLength, char** items, int a7, int a8) int _win_register_menu_pulldown(int win, int x, char* title, int keyCode, int itemsLength, char** items, int foregroundColor, int backgroundColor)
{ {
Window* window = windowGetWindow(win); Window* window = windowGetWindow(win);
@ -928,7 +927,7 @@ int _win_register_menu_pulldown(int win, int x, char* title, int keyCode, int it
return -1; return -1;
} }
windowDrawText(win, title, 0, titleX, titleY, window->menuBar->borderColor | 0x2000000); windowDrawText(win, title, 0, titleX, titleY, window->menuBar->foregroundColor | 0x2000000);
MenuPulldown* pulldown = &(window->menuBar->pulldowns[window->menuBar->pulldownsLength]); MenuPulldown* pulldown = &(window->menuBar->pulldowns[window->menuBar->pulldownsLength]);
pulldown->rect.left = titleX; pulldown->rect.left = titleX;
@ -938,8 +937,8 @@ int _win_register_menu_pulldown(int win, int x, char* title, int keyCode, int it
pulldown->keyCode = keyCode; pulldown->keyCode = keyCode;
pulldown->itemsLength = itemsLength; pulldown->itemsLength = itemsLength;
pulldown->items = items; pulldown->items = items;
pulldown->field_1C = a7; pulldown->foregroundColor = foregroundColor;
pulldown->field_20 = a8; pulldown->backgroundColor = backgroundColor;
window->menuBar->pulldownsLength++; window->menuBar->pulldownsLength++;
@ -1120,7 +1119,7 @@ int _win_input_str(int win, char* dest, int maxLength, int x, int y, int textCol
} }
// 0x4DBD04 // 0x4DBD04
int sub_4DBD04(int win, Rect* rect, char** items, int itemsLength, int a5, int a6, MenuBar* menuBar, int pulldownIndex) int process_pull_down(int win, Rect* rect, char** items, int itemsLength, int foregroundColor, int backgroundColor, MenuBar* menuBar, int pulldownIndex)
{ {
// TODO: Incomplete. // TODO: Incomplete.
return -1; return -1;
@ -1143,15 +1142,15 @@ int _GNW_process_menu(MenuBar* menuBar, int pulldownIndex)
pulldown->itemsLength, pulldown->itemsLength,
pulldown->rect.left, pulldown->rect.left,
menuBar->rect.bottom + 1, menuBar->rect.bottom + 1,
pulldown->field_1C, pulldown->foregroundColor,
pulldown->field_20, pulldown->backgroundColor,
&rect); &rect);
if (win == -1) { if (win == -1) {
_curr_menu = NULL; _curr_menu = NULL;
return -1; return -1;
} }
keyCode = sub_4DBD04(win, &rect, pulldown->items, pulldown->itemsLength, pulldown->field_1C, pulldown->field_20, menuBar, pulldownIndex); keyCode = process_pull_down(win, &rect, pulldown->items, pulldown->itemsLength, pulldown->foregroundColor, pulldown->backgroundColor, menuBar, pulldownIndex);
if (keyCode < -1) { if (keyCode < -1) {
pulldownIndex = -2 - keyCode; pulldownIndex = -2 - keyCode;
} }
@ -1168,20 +1167,20 @@ int _GNW_process_menu(MenuBar* menuBar, int pulldownIndex)
return keyCode; return keyCode;
} }
// Calculates max length of string needed to represent a1 or a2. // Calculates max length of string needed to represent `value` or `value2`.
// //
// 0x4DD03C // 0x4DD03C
size_t _calc_max_field_chars_wcursor(int a1, int a2) size_t _calc_max_field_chars_wcursor(int value1, int value2)
{ {
char* str = (char*)internal_malloc(17); char* str = (char*)internal_malloc(17);
if (str == NULL) { if (str == NULL) {
return -1; return -1;
} }
snprintf(str, 17, "%d", a1); snprintf(str, 17, "%d", value1);
size_t len1 = strlen(str); size_t len1 = strlen(str);
snprintf(str, 17, "%d", a2); snprintf(str, 17, "%d", value2);
size_t len2 = strlen(str); size_t len2 = strlen(str);
internal_free(str); internal_free(str);
@ -1272,7 +1271,7 @@ void _tm_kill_msg()
} }
// 0x4DD744 // 0x4DD744
void _tm_kill_out_of_order(int a1) void _tm_kill_out_of_order(int queueIndex)
{ {
int v7; int v7;
int v6; int v6;
@ -1281,16 +1280,16 @@ void _tm_kill_out_of_order(int a1)
return; return;
} }
if (!_tm_index_active(a1)) { if (!_tm_index_active(queueIndex)) {
return; return;
} }
windowDestroy(_tm_queue[a1].field_4); windowDestroy(_tm_queue[queueIndex].field_4);
_tm_location[_tm_queue[a1].field_8].field_0 = 0; _tm_location[_tm_queue[queueIndex].field_8].field_0 = 0;
if (a1 != _tm_kill) { if (queueIndex != _tm_kill) {
v6 = a1; v6 = queueIndex;
do { do {
v7 = v6 - 1; v7 = v6 - 1;
if (v7 < 0) { if (v7 < 0) {
@ -1317,35 +1316,35 @@ void _tm_kill_out_of_order(int a1)
void _tm_click_response(int btn) void _tm_click_response(int btn)
{ {
int win; int win;
int v3; int queueIndex;
if (_tm_kill == -1) { if (_tm_kill == -1) {
return; return;
} }
win = buttonGetWindowId(btn); win = buttonGetWindowId(btn);
v3 = _tm_kill; queueIndex = _tm_kill;
while (win != _tm_queue[v3].field_4) { while (win != _tm_queue[queueIndex].field_4) {
v3++; queueIndex++;
if (v3 == 5) { if (queueIndex == 5) {
v3 = 0; queueIndex = 0;
} }
if (v3 == _tm_kill || !_tm_index_active(v3)) if (queueIndex == _tm_kill || !_tm_index_active(queueIndex))
return; return;
} }
_tm_kill_out_of_order(v3); _tm_kill_out_of_order(queueIndex);
} }
// 0x4DD870 // 0x4DD870
int _tm_index_active(int a1) int _tm_index_active(int queueIndex)
{ {
if (_tm_kill != _tm_add) { if (_tm_kill != _tm_add) {
if (_tm_kill >= _tm_add) { if (_tm_kill >= _tm_add) {
if (a1 >= _tm_add && a1 < _tm_kill) if (queueIndex >= _tm_add && queueIndex < _tm_kill)
return 0; return 0;
} else if (a1 < _tm_kill || a1 >= _tm_add) { } else if (queueIndex < _tm_kill || queueIndex >= _tm_add) {
return 0; return 0;
} }
} }

View File

@ -13,30 +13,30 @@ typedef void(ListSelectionHandler)(char** items, int index);
extern char gProgramWindowTitle[256]; extern char gProgramWindowTitle[256];
int _win_list_select(const char* title, char** fileList, int fileListLength, ListSelectionHandler* callback, int x, int y, int a7); int _win_list_select(const char* title, char** fileList, int fileListLength, ListSelectionHandler* callback, int x, int y, int color);
int _win_list_select_at(const char* title, char** items, int itemsLength, ListSelectionHandler* callback, int x, int y, int a7, int a8); int _win_list_select_at(const char* title, char** items, int itemsLength, ListSelectionHandler* callback, int x, int y, int color, int start);
int _win_get_str(char* dest, int length, const char* title, int x, int y); int _win_get_str(char* dest, int length, const char* title, int x, int y);
int _win_msg(const char* string, int x, int y, int flags); int _win_msg(const char* string, int x, int y, int color);
int _win_pull_down(char** items, int itemsLength, int x, int y, int a5); int _win_pull_down(char** items, int itemsLength, int x, int y, int color);
int _create_pull_down(char** stringList, int stringListLength, int x, int y, int a5, int a6, Rect* rect); int _create_pull_down(char** stringList, int stringListLength, int x, int y, int foregroundColor, int backgroundColor, Rect* rect);
int _win_debug(char* string); int _win_debug(char* string);
void _win_debug_delete(int btn, int keyCode); void _win_debug_delete(int btn, int keyCode);
int _win_register_menu_bar(int win, int x, int y, int width, int height, int borderColor, int backgroundColor); int _win_register_menu_bar(int win, int x, int y, int width, int height, int foregroundColor, int backgroundColor);
int _win_register_menu_pulldown(int win, int x, char* title, int keyCode, int itemsLength, char** items, int a7, int a8); int _win_register_menu_pulldown(int win, int x, char* title, int keyCode, int itemsLength, char** items, int foregroundColor, int backgroundColor);
void _win_delete_menu_bar(int win); void _win_delete_menu_bar(int win);
int _find_first_letter(int ch, char** stringList, int stringListLength); int _find_first_letter(int ch, char** stringList, int stringListLength);
int _win_width_needed(char** fileNameList, int fileNameListLength); 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 _win_input_str(int win, char* dest, int maxLength, int x, int y, int textColor, int backgroundColor);
int sub_4DBD04(int win, Rect* rect, char** items, int itemsLength, int a5, int a6, MenuBar* menuBar, int pulldownIndex); 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 _GNW_process_menu(MenuBar* menuBar, int pulldownIndex);
size_t _calc_max_field_chars_wcursor(int a1, int a2); size_t _calc_max_field_chars_wcursor(int value1, int value2);
void _GNW_intr_init(); void _GNW_intr_init();
void _GNW_intr_exit(); void _GNW_intr_exit();
void _tm_watch_msgs(); void _tm_watch_msgs();
void _tm_kill_msg(); void _tm_kill_msg();
void _tm_kill_out_of_order(int a1); void _tm_kill_out_of_order(int queueIndex);
void _tm_click_response(int btn); void _tm_click_response(int btn);
int _tm_index_active(int a1); int _tm_index_active(int queueIndex);
} // namespace fallout } // namespace fallout

View File

@ -6592,4 +6592,15 @@ void wmBlinkRndEncounterIcon(bool special)
wmGenData.encounterIconIsVisible = false; wmGenData.encounterIconIsVisible = false;
} }
void wmSetPartyWorldPos(int x, int y)
{
wmGenData.worldPosX = x;
wmGenData.worldPosY = y;
}
void wmCarSetCurrentArea(int area)
{
wmGenData.currentCarAreaId = area;
}
} // namespace fallout } // namespace fallout

View File

@ -277,6 +277,9 @@ int wmSetMapMusic(int mapIdx, const char* name);
int wmMatchAreaContainingMapIdx(int mapIdx, int* areaIdxPtr); int wmMatchAreaContainingMapIdx(int mapIdx, int* areaIdxPtr);
int wmTeleportToArea(int areaIdx); int wmTeleportToArea(int areaIdx);
void wmSetPartyWorldPos(int x, int y);
void wmCarSetCurrentArea(int area);
} // namespace fallout } // namespace fallout
#endif /* WORLD_MAP_H */ #endif /* WORLD_MAP_H */

View File

@ -234,10 +234,10 @@ char* xfileReadString(char* string, int size, XFile* stream)
result = dfileReadString(string, size, stream->dfile); result = dfileReadString(string, size, stream->dfile);
break; break;
case XFILE_TYPE_GZFILE: case XFILE_TYPE_GZFILE:
result = gzgets(stream->gzfile, string, size); result = compat_gzgets(stream->gzfile, string, size);
break; break;
default: default:
result = fgets(string, size, stream->file); result = compat_fgets(string, size, stream->file);
break; break;
} }