fallout2-ce/src/skilldex.cc

412 lines
12 KiB
C++
Raw Normal View History

2022-05-19 01:51:26 -07:00
#include "skilldex.h"
2022-06-18 22:18:05 -07:00
#include "art.h"
2022-05-19 01:51:26 -07:00
#include "color.h"
#include "core.h"
#include "cycle.h"
#include "debug.h"
#include "draw.h"
#include "game.h"
#include "game_mouse.h"
#include "game_sound.h"
2022-06-18 22:18:05 -07:00
#include "geometry.h"
#include "interface.h"
2022-05-19 01:51:26 -07:00
#include "map.h"
#include "memory.h"
2022-06-18 22:18:05 -07:00
#include "message.h"
2022-05-19 01:51:26 -07:00
#include "object.h"
2022-05-28 02:34:49 -07:00
#include "platform_compat.h"
2022-05-19 01:51:26 -07:00
#include "skill.h"
#include "text_font.h"
#include "window_manager.h"
#include <stdio.h>
#include <string.h>
2022-05-20 17:10:45 -07:00
#define SKILLDEX_WINDOW_RIGHT_MARGIN 4
#define SKILLDEX_WINDOW_BOTTOM_MARGIN 6
2022-06-18 22:18:05 -07:00
#define SKILLDEX_SKILL_BUTTON_BUFFER_COUNT (SKILLDEX_SKILL_COUNT * 2)
typedef enum SkilldexFrm {
SKILLDEX_FRM_BACKGROUND,
SKILLDEX_FRM_BUTTON_ON,
SKILLDEX_FRM_BUTTON_OFF,
SKILLDEX_FRM_LITTLE_RED_BUTTON_UP,
SKILLDEX_FRM_LITTLE_RED_BUTTON_DOWN,
SKILLDEX_FRM_BIG_NUMBERS,
SKILLDEX_FRM_COUNT,
} SkilldexFrm;
typedef enum SkilldexSkill {
SKILLDEX_SKILL_SNEAK,
SKILLDEX_SKILL_LOCKPICK,
SKILLDEX_SKILL_STEAL,
SKILLDEX_SKILL_TRAPS,
SKILLDEX_SKILL_FIRST_AID,
SKILLDEX_SKILL_DOCTOR,
SKILLDEX_SKILL_SCIENCE,
SKILLDEX_SKILL_REPAIR,
SKILLDEX_SKILL_COUNT,
} SkilldexSkill;
static int skilldexWindowInit();
static void skilldexWindowFree();
2022-05-19 01:51:26 -07:00
// 0x51D43C
2022-06-18 22:18:05 -07:00
static bool gSkilldexWindowIsoWasEnabled = false;
2022-05-19 01:51:26 -07:00
// 0x51D440
2022-06-18 22:18:05 -07:00
static const int gSkilldexFrmIds[SKILLDEX_FRM_COUNT] = {
2022-05-19 01:51:26 -07:00
121,
119,
120,
8,
9,
170,
};
// Maps Skilldex options into skills.
//
// 0x51D458
2022-06-18 22:18:05 -07:00
static const int gSkilldexSkills[SKILLDEX_SKILL_COUNT] = {
2022-05-19 01:51:26 -07:00
SKILL_SNEAK,
SKILL_LOCKPICK,
SKILL_STEAL,
SKILL_TRAPS,
SKILL_FIRST_AID,
SKILL_DOCTOR,
SKILL_SCIENCE,
SKILL_REPAIR,
};
// 0x668088
2022-06-18 22:18:05 -07:00
static Size gSkilldexFrmSizes[SKILLDEX_FRM_COUNT];
2022-05-19 01:51:26 -07:00
// 0x6680B8
2022-06-18 22:18:05 -07:00
static unsigned char* gSkilldexButtonsData[SKILLDEX_SKILL_BUTTON_BUFFER_COUNT];
2022-05-19 01:51:26 -07:00
// skilldex.msg
// 0x6680F8
2022-06-18 22:18:05 -07:00
static MessageList gSkilldexMessageList;
2022-05-19 01:51:26 -07:00
// 0x668100
2022-06-18 22:18:05 -07:00
static MessageListItem gSkilldexMessageListItem;
2022-05-19 01:51:26 -07:00
// 0x668110
2022-06-18 22:18:05 -07:00
static unsigned char* gSkilldexFrmData[SKILLDEX_FRM_COUNT];
2022-05-19 01:51:26 -07:00
// 0x668128
2022-06-18 22:18:05 -07:00
static CacheEntry* gSkilldexFrmHandles[SKILLDEX_FRM_COUNT];
2022-05-19 01:51:26 -07:00
// 0x668140
2022-06-18 22:18:05 -07:00
static int gSkilldexWindow;
2022-05-19 01:51:26 -07:00
// 0x668144
2022-06-18 22:18:05 -07:00
static unsigned char* gSkilldexWindowBuffer;
2022-05-19 01:51:26 -07:00
// 0x668148
2022-06-18 22:18:05 -07:00
static int gSkilldexWindowOldFont;
2022-05-19 01:51:26 -07:00
// skilldex_select
// 0x4ABFD0
int skilldexOpen()
{
if (skilldexWindowInit() == -1) {
debugPrint("\n ** Error loading skilldex dialog data! **\n");
return -1;
}
int rc = -1;
while (rc == -1) {
int keyCode = _get_input();
if (keyCode == KEY_ESCAPE || keyCode == 500 || _game_user_wants_to_quit != 0) {
rc = 0;
} else if (keyCode == KEY_RETURN) {
soundPlayFile("ib1p1xx1");
rc = 0;
} else if (keyCode >= 501 && keyCode <= 509) {
rc = keyCode - 500;
}
}
if (rc != 0) {
coreDelay(1000 / 9);
}
skilldexWindowFree();
return rc;
}
// 0x4AC054
2022-06-18 22:18:05 -07:00
static int skilldexWindowInit()
2022-05-19 01:51:26 -07:00
{
gSkilldexWindowOldFont = fontGetCurrent();
gSkilldexWindowIsoWasEnabled = false;
gameMouseObjectsHide();
gameMouseSetCursor(MOUSE_CURSOR_ARROW);
if (!messageListInit(&gSkilldexMessageList)) {
return -1;
}
2022-05-28 02:34:49 -07:00
char path[COMPAT_MAX_PATH];
2022-05-19 01:51:26 -07:00
sprintf(path, "%s%s", asc_5186C8, "skilldex.msg");
if (!messageListLoad(&gSkilldexMessageList, path)) {
return -1;
}
int frmIndex;
for (frmIndex = 0; frmIndex < SKILLDEX_FRM_COUNT; frmIndex++) {
int fid = buildFid(OBJ_TYPE_INTERFACE, gSkilldexFrmIds[frmIndex], 0, 0, 0);
2022-05-19 01:51:26 -07:00
gSkilldexFrmData[frmIndex] = artLockFrameDataReturningSize(fid, &(gSkilldexFrmHandles[frmIndex]), &(gSkilldexFrmSizes[frmIndex].width), &(gSkilldexFrmSizes[frmIndex].height));
if (gSkilldexFrmData[frmIndex] == NULL) {
break;
}
}
if (frmIndex < SKILLDEX_FRM_COUNT) {
while (--frmIndex >= 0) {
artUnlock(gSkilldexFrmHandles[frmIndex]);
}
messageListFree(&gSkilldexMessageList);
return -1;
}
bool cycle = false;
int buttonDataIndex;
for (buttonDataIndex = 0; buttonDataIndex < SKILLDEX_SKILL_BUTTON_BUFFER_COUNT; buttonDataIndex++) {
2022-05-21 08:22:03 -07:00
gSkilldexButtonsData[buttonDataIndex] = (unsigned char*)internal_malloc(gSkilldexFrmSizes[SKILLDEX_FRM_BUTTON_ON].height * gSkilldexFrmSizes[SKILLDEX_FRM_BUTTON_ON].width + 512);
2022-05-19 01:51:26 -07:00
if (gSkilldexButtonsData[buttonDataIndex] == NULL) {
break;
}
// NOTE: Original code uses bitwise XOR.
cycle = !cycle;
unsigned char* data;
int size;
if (cycle) {
size = gSkilldexFrmSizes[SKILLDEX_FRM_BUTTON_OFF].width * gSkilldexFrmSizes[SKILLDEX_FRM_BUTTON_OFF].height;
data = gSkilldexFrmData[SKILLDEX_FRM_BUTTON_OFF];
} else {
size = gSkilldexFrmSizes[SKILLDEX_FRM_BUTTON_ON].width * gSkilldexFrmSizes[SKILLDEX_FRM_BUTTON_ON].height;
data = gSkilldexFrmData[SKILLDEX_FRM_BUTTON_ON];
}
memcpy(gSkilldexButtonsData[buttonDataIndex], data, size);
}
if (buttonDataIndex < SKILLDEX_SKILL_BUTTON_BUFFER_COUNT) {
while (--buttonDataIndex >= 0) {
internal_free(gSkilldexButtonsData[buttonDataIndex]);
}
for (int index = 0; index < SKILLDEX_FRM_COUNT; index++) {
artUnlock(gSkilldexFrmHandles[index]);
}
messageListFree(&gSkilldexMessageList);
return -1;
}
// Maintain original position relative to centered interface bar.
int skilldexWindowX = (screenGetWidth() - 640) / 2 + 640 - gSkilldexFrmSizes[SKILLDEX_FRM_BACKGROUND].width - SKILLDEX_WINDOW_RIGHT_MARGIN;
int skilldexWindowY = screenGetHeight() - INTERFACE_BAR_HEIGHT - 1 - gSkilldexFrmSizes[SKILLDEX_FRM_BACKGROUND].height - SKILLDEX_WINDOW_BOTTOM_MARGIN;
2022-05-20 17:10:45 -07:00
gSkilldexWindow = windowCreate(skilldexWindowX,
skilldexWindowY,
2022-05-19 01:51:26 -07:00
gSkilldexFrmSizes[SKILLDEX_FRM_BACKGROUND].width,
gSkilldexFrmSizes[SKILLDEX_FRM_BACKGROUND].height,
256,
WINDOW_FLAG_0x10 | WINDOW_FLAG_0x02);
if (gSkilldexWindow == -1) {
for (int index = 0; index < SKILLDEX_SKILL_BUTTON_BUFFER_COUNT; index++) {
internal_free(gSkilldexButtonsData[index]);
}
for (int index = 0; index < SKILLDEX_FRM_COUNT; index++) {
artUnlock(gSkilldexFrmHandles[index]);
}
messageListFree(&gSkilldexMessageList);
return -1;
}
gSkilldexWindowIsoWasEnabled = isoDisable();
colorCycleDisable();
gameMouseSetCursor(MOUSE_CURSOR_ARROW);
gSkilldexWindowBuffer = windowGetBuffer(gSkilldexWindow);
memcpy(gSkilldexWindowBuffer,
gSkilldexFrmData[SKILLDEX_FRM_BACKGROUND],
gSkilldexFrmSizes[SKILLDEX_FRM_BACKGROUND].width * gSkilldexFrmSizes[SKILLDEX_FRM_BACKGROUND].height);
fontSetCurrent(103);
// Render "SKILLDEX" title.
char* title = getmsg(&gSkilldexMessageList, &gSkilldexMessageListItem, 100);
fontDrawText(gSkilldexWindowBuffer + 14 * gSkilldexFrmSizes[SKILLDEX_FRM_BACKGROUND].width + 55,
title,
gSkilldexFrmSizes[SKILLDEX_FRM_BACKGROUND].width,
gSkilldexFrmSizes[SKILLDEX_FRM_BACKGROUND].width,
_colorTable[18979]);
// Render skill values.
int valueY = 48;
for (int index = 0; index < SKILLDEX_SKILL_COUNT; index++) {
int value = skillGetValue(gDude, gSkilldexSkills[index]);
2022-08-06 08:21:47 -07:00
// SFALL: Fix for negative values.
//
// NOTE: Sfall's fix is different. It simply renders 0 even if
// calculated skill level is negative (which can be the case when
// playing on Hard difficulty + low Agility). For unknown reason -5 is
// the error code from `skillGetValue`, which is not handled here
// because -5 is also a legitimate skill value.
//
// TODO: Provide other error code in `skillGetValue`.
unsigned char* numbersFrmData = gSkilldexFrmData[SKILLDEX_FRM_BIG_NUMBERS];
if (value < 0) {
// First half of the bignum.frm is white, second half is red.
numbersFrmData += gSkilldexFrmSizes[SKILLDEX_FRM_BIG_NUMBERS].width / 2;
value = -value;
2022-05-19 01:51:26 -07:00
}
int hundreds = value / 100;
2022-08-06 08:21:47 -07:00
blitBufferToBuffer(numbersFrmData + 14 * hundreds,
2022-05-19 01:51:26 -07:00
14,
24,
336,
gSkilldexWindowBuffer + gSkilldexFrmSizes[SKILLDEX_FRM_BACKGROUND].width * valueY + 110,
gSkilldexFrmSizes[SKILLDEX_FRM_BACKGROUND].width);
int tens = (value % 100) / 10;
2022-08-06 08:21:47 -07:00
blitBufferToBuffer(numbersFrmData + 14 * tens,
2022-05-19 01:51:26 -07:00
14,
24,
336,
gSkilldexWindowBuffer + gSkilldexFrmSizes[SKILLDEX_FRM_BACKGROUND].width * valueY + 124,
gSkilldexFrmSizes[SKILLDEX_FRM_BACKGROUND].width);
int ones = (value % 100) % 10;
2022-08-06 08:21:47 -07:00
blitBufferToBuffer(numbersFrmData + 14 * ones,
2022-05-19 01:51:26 -07:00
14,
24,
336,
gSkilldexWindowBuffer + gSkilldexFrmSizes[SKILLDEX_FRM_BACKGROUND].width * valueY + 138,
gSkilldexFrmSizes[SKILLDEX_FRM_BACKGROUND].width);
valueY += 36;
}
// Render skill buttons.
int lineHeight = fontGetLineHeight();
int buttonY = 45;
int nameY = ((gSkilldexFrmSizes[SKILLDEX_FRM_BUTTON_OFF].height - lineHeight) / 2) + 1;
for (int index = 0; index < SKILLDEX_SKILL_COUNT; index++) {
char name[MESSAGE_LIST_ITEM_FIELD_MAX_SIZE];
strcpy(name, getmsg(&gSkilldexMessageList, &gSkilldexMessageListItem, 102 + index));
int nameX = ((gSkilldexFrmSizes[SKILLDEX_FRM_BUTTON_OFF].width - fontGetStringWidth(name)) / 2) + 1;
if (nameX < 0) {
nameX = 0;
}
fontDrawText(gSkilldexButtonsData[index * 2] + gSkilldexFrmSizes[SKILLDEX_FRM_BUTTON_ON].width * nameY + nameX,
name,
gSkilldexFrmSizes[SKILLDEX_FRM_BUTTON_ON].width,
gSkilldexFrmSizes[SKILLDEX_FRM_BUTTON_ON].width,
_colorTable[18979]);
fontDrawText(gSkilldexButtonsData[index * 2 + 1] + gSkilldexFrmSizes[SKILLDEX_FRM_BUTTON_OFF].width * nameY + nameX,
name,
gSkilldexFrmSizes[SKILLDEX_FRM_BUTTON_OFF].width,
gSkilldexFrmSizes[SKILLDEX_FRM_BUTTON_OFF].width,
_colorTable[14723]);
int btn = buttonCreate(gSkilldexWindow,
15,
buttonY,
gSkilldexFrmSizes[SKILLDEX_FRM_BUTTON_OFF].width,
gSkilldexFrmSizes[SKILLDEX_FRM_BUTTON_OFF].height,
-1,
-1,
-1,
501 + index,
gSkilldexButtonsData[index * 2],
gSkilldexButtonsData[index * 2 + 1],
NULL,
BUTTON_FLAG_TRANSPARENT);
if (btn != -1) {
buttonSetCallbacks(btn, _gsound_lrg_butt_press, _gsound_lrg_butt_release);
}
buttonY += 36;
}
// Render "CANCEL" button.
char* cancel = getmsg(&gSkilldexMessageList, &gSkilldexMessageListItem, 101);
fontDrawText(gSkilldexWindowBuffer + gSkilldexFrmSizes[SKILLDEX_FRM_BACKGROUND].width * 337 + 72,
cancel,
gSkilldexFrmSizes[SKILLDEX_FRM_BACKGROUND].width,
gSkilldexFrmSizes[SKILLDEX_FRM_BACKGROUND].width,
_colorTable[18979]);
int cancelBtn = buttonCreate(gSkilldexWindow,
48,
338,
gSkilldexFrmSizes[SKILLDEX_FRM_LITTLE_RED_BUTTON_UP].width,
gSkilldexFrmSizes[SKILLDEX_FRM_LITTLE_RED_BUTTON_UP].height,
-1,
-1,
-1,
500,
gSkilldexFrmData[SKILLDEX_FRM_LITTLE_RED_BUTTON_UP],
gSkilldexFrmData[SKILLDEX_FRM_LITTLE_RED_BUTTON_DOWN],
NULL,
BUTTON_FLAG_TRANSPARENT);
if (cancelBtn != -1) {
buttonSetCallbacks(cancelBtn, _gsound_red_butt_press, _gsound_red_butt_release);
}
windowRefresh(gSkilldexWindow);
return 0;
}
// 0x4AC67C
2022-06-18 22:18:05 -07:00
static void skilldexWindowFree()
2022-05-19 01:51:26 -07:00
{
windowDestroy(gSkilldexWindow);
for (int index = 0; index < SKILLDEX_SKILL_BUTTON_BUFFER_COUNT; index++) {
internal_free(gSkilldexButtonsData[index]);
}
for (int index = 0; index < SKILLDEX_FRM_COUNT; index++) {
artUnlock(gSkilldexFrmHandles[index]);
}
messageListFree(&gSkilldexMessageList);
fontSetCurrent(gSkilldexWindowOldFont);
if (gSkilldexWindowIsoWasEnabled) {
isoEnable();
}
colorCycleEnable();
gameMouseSetCursor(MOUSE_CURSOR_ARROW);
}