Files
sfall/sfall/FalloutEngine/EngineUtils.cpp
T
NovaRain 6ecc1fcd1f Corrected the code for multihex retargeting tile
Small optimization to sfall function code.
2020-06-28 23:11:51 +08:00

600 lines
17 KiB
C++

/*
* sfall
* Copyright (C) 2008-2016 The sfall team
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <cstdint>
#include "Functions.h"
#include "FunctionOffsets.h"
#include "Structs.h"
#include "Variables.h"
#include "VariableOffsets.h"
#include "EngineUtils.h"
// TODO: split these functions into several files
namespace fo
{
static MessageNode messageBuf;
const char* GetMessageStr(const MessageList* fileAddr, long messageId) {
return fo::func::getmsg(fileAddr, &messageBuf, messageId);
}
const char* MessageSearch(const MessageList* fileAddr, long messageId) {
messageBuf.number = messageId;
if (fo::func::message_search(fileAddr, &messageBuf) == 1) {
return messageBuf.message;
}
return nullptr;
}
long AnimCodeByWeapon(GameObject* weapon) {
if (weapon != nullptr) {
Proto* proto = GetProto(weapon->protoId);
if (proto != nullptr && proto->item.type == item_type_weapon) {
return proto->item.weapon.animationCode;
}
}
return 0;
}
Proto* GetProto(long pid) {
Proto* protoPtr;
if (fo::func::proto_ptr(pid, &protoPtr) != -1) {
return protoPtr;
}
return nullptr;
}
bool CritterCopyProto(long pid, long* &proto_dst) {
fo::Proto* protoPtr;
if (fo::func::proto_ptr(pid, &protoPtr) == -1) {
proto_dst = nullptr;
return false;
}
proto_dst = reinterpret_cast<long*>(new int32_t[104]);
memcpy(proto_dst, protoPtr, 416);
return true;
}
void SkillGetTags(long* result, long num) {
if (num > 4) num = 4;
fo::func::skill_get_tags(result, num);
}
void SkillSetTags(long* tags, long num) {
if (num > 4) num = 4;
fo::func::skill_set_tags(tags, num);
}
long __fastcall GetItemType(GameObject* item) {
return fo::func::item_get_type(item);
}
__declspec(noinline) GameObject* GetItemPtrSlot(GameObject* critter, InvenType slot) {
GameObject* itemPtr = nullptr;
switch (slot) {
case fo::INVEN_TYPE_LEFT_HAND:
itemPtr = fo::func::inven_left_hand(critter);
break;
case fo::INVEN_TYPE_RIGHT_HAND:
itemPtr = fo::func::inven_right_hand(critter);
break;
case fo::INVEN_TYPE_WORN:
itemPtr = fo::func::inven_worn(critter);
break;
}
return itemPtr;
}
long& GetActiveItemMode() {
return fo::var::itemButtonItems[fo::var::itemCurrentItem].mode;
}
GameObject* GetActiveItem() {
return fo::var::itemButtonItems[fo::var::itemCurrentItem].item;
}
long GetCurrentAttackMode() {
long hitMode = -1;
if (fo::var::interfaceWindow != -1) {
long activeHand = fo::var::itemCurrentItem; // 0 - left, 1 - right
switch (fo::var::itemButtonItems[activeHand].mode) {
case 1:
case 2: // called shot
hitMode = fo::var::itemButtonItems[activeHand].primaryAttack;
break;
case 3:
case 4: // called shot
hitMode = fo::var::itemButtonItems[activeHand].secondaryAttack;
break;
case 5: // reload mode
hitMode = fo::ATKTYPE_LWEAPON_RELOAD + activeHand;
}
}
return hitMode;
}
bool HeroIsFemale() {
return (fo::func::stat_level(fo::var::obj_dude, fo::Stat::STAT_gender) == fo::Gender::GENDER_FEMALE);
}
long CheckAddictByPid(fo::GameObject* critter, long pid) {
__asm {
mov eax, pid;
mov esi, critter;
call fo::funcoffs::item_d_check_addict_;
}
/* keyword 'return' is not needed, the compiler will do everything correctly */
}
// Checks whether the player is under the influence of negative effects of radiation
long __fastcall IsRadInfluence() {
fo::QueueRadiation* queue = (fo::QueueRadiation*)fo::func::queue_find_first(fo::var::obj_dude, fo::radiation_event);
while (queue) {
if (queue->init && queue->level >= 2) return 1;
queue = (fo::QueueRadiation*)fo::func::queue_find_next(fo::var::obj_dude, fo::radiation_event);
}
return 0;
}
void ToggleNpcFlag(fo::GameObject* npc, long flag, bool set) {
Proto* protoPtr;
if (fo::func::proto_ptr(npc->protoId, &protoPtr) != -1) {
long bit = (1 << flag);
if (set) {
protoPtr->critter.critterFlags |= bit;
} else {
protoPtr->critter.critterFlags &= ~bit;
}
}
}
// Returns the position of party member in the existing table (begins from 1)
long IsPartyMemberByPid(long pid) {
size_t patryCount = fo::var::partyMemberMaxCount;
if (patryCount) {
DWORD* memberPids = fo::var::partyMemberPidList; // pids from party.txt
for (size_t i = 0; i < patryCount; i++) {
if (memberPids[i] == pid) return i + 1;
}
}
return 0;
}
// Returns True if the NPC belongs to the player's potential (set in party.txt) party members (analog of broken isPotentialPartyMember_)
bool IsPartyMember(fo::GameObject* critter) {
if (critter->id < PLAYER_ID) return false;
return (IsPartyMemberByPid(critter->protoId) > 0);
}
// Returns the number of local variables of the object script
long GetScriptLocalVars(long sid) {
fo::ScriptInstance* script = nullptr;
fo::func::scr_ptr(sid, &script);
return (script) ? script->numLocalVars : 0;
}
// Returns window ID by x/y coordinate (hidden windows are ignored)
long __fastcall GetTopWindowID(long xPos, long yPos) {
fo::Window* win = nullptr;
long countWin = *(DWORD*)FO_VAR_num_windows - 1;
for (int n = countWin; n >= 0; n--) {
win = fo::var::window[n];
if (xPos >= win->wRect.left && xPos <= win->wRect.right && yPos >= win->wRect.top && yPos <= win->wRect.bottom) {
if (!(win->flags & fo::WinFlags::Hidden)) {
break;
}
}
}
return win->wID;
}
enum WinNameType {
Inventory = 0, // any inventory window (player/loot/use/barter)
Dialog = 1,
PipBoy = 2,
WorldMap = 3,
IfaceBar = 4, // the interface bar
Character = 5,
Skilldex = 6,
EscMenu = 7, // escape menu
//Automap = 8 // for this window there is no global variable
};
fo::Window* GetWindow(long winType) {
long winID = 0;
switch (winType) {
case WinNameType::Inventory:
winID = fo::var::i_wid;
break;
case WinNameType::Dialog:
winID = fo::var::dialogueBackWindow;
break;
case WinNameType::PipBoy:
winID = fo::var::pip_win;
break;
case WinNameType::WorldMap:
winID = fo::var::wmBkWin;
break;
case WinNameType::IfaceBar:
winID = fo::var::interfaceWindow;
break;
case WinNameType::Character:
winID = fo::var::edit_win;
break;
case WinNameType::Skilldex:
winID = fo::var::skldxwin;
break;
case WinNameType::EscMenu:
winID = fo::var::optnwin;
break;
default:
return (fo::Window*)(-1);
}
return (winID > 0) ? fo::func::GNW_find(winID) : nullptr;
}
static long GetRangeTileNumbers(long sourceTile, long radius, long &outEnd) {
long hexRadius = 200 * (radius + 1);
outEnd = sourceTile + hexRadius;
if (outEnd > 40000) outEnd = 40000;
long startTile = sourceTile - hexRadius;
return (startTile < 0) ? 0 : startTile;
}
// Returns an array of objects within the specified radius from the source tile
void GetObjectsTileRadius(std::vector<fo::GameObject*> &objs, long sourceTile, long radius, long elev, long type) {
long endTile;
for (long tile = GetRangeTileNumbers(sourceTile, radius, endTile); tile < endTile; tile++) {
fo::GameObject* obj = fo::func::obj_find_first_at_tile(elev, tile);
while (obj) {
if (type == -1 || type == obj->Type()) {
bool multiHex = (obj->flags & fo::ObjectFlag::MultiHex) ? true : false;
if (fo::func::tile_dist(sourceTile, obj->tile) <= (radius + multiHex)) {
objs.push_back(obj);
}
}
obj = fo::func::obj_find_next_at_tile();
}
}
}
// Checks the blocking tiles and returns the first blocking object
fo::GameObject* CheckAroundBlockingTiles(fo::GameObject* source, long dstTile) {
long rotation = 5;
do {
long chkTile = fo::func::tile_num_in_direction(dstTile, rotation, 1);
fo::GameObject* obj = fo::func::obj_blocking_at(source, chkTile, source->elevation);
if (obj) return obj;
} while (--rotation >= 0);
return nullptr;
}
fo::GameObject* __fastcall MultiHexMoveIsBlocking(fo::GameObject* source, long dstTile) {
if (fo::func::tile_dist(source->tile, dstTile) > 1) {
return CheckAroundBlockingTiles(source, dstTile);
}
// Checks the blocking arc of adjacent tiles
long dir = fo::func::tile_dir(source->tile, dstTile);
long chkTile = fo::func::tile_num_in_direction(dstTile, dir, 1);
fo::GameObject* obj = fo::func::obj_blocking_at(source, chkTile, source->elevation);
if (obj) return obj;
// +1 direction
long rotation = (dir + 1) % 6;
chkTile = fo::func::tile_num_in_direction(dstTile, rotation, 1);
obj = fo::func::obj_blocking_at(source, chkTile, source->elevation);
if (obj) return obj;
// -1 direction
rotation = (dir + 5) % 6;
chkTile = fo::func::tile_num_in_direction(dstTile, rotation, 1);
obj = fo::func::obj_blocking_at(source, chkTile, source->elevation);
if (obj) return obj;
return nullptr;
}
// Returns the type of the terrain sub tile at the the player's position on the world map
long wmGetCurrentTerrainType() {
long* terrainId = *(long**)FO_VAR_world_subtile;
if (terrainId == nullptr) {
__asm {
lea ebx, terrainId;
mov edx, dword ptr ds:[FO_VAR_world_ypos];
mov eax, dword ptr ds:[FO_VAR_world_xpos];
call fo::funcoffs::wmFindCurSubTileFromPos_;
}
}
return *terrainId;
}
//---------------------------------------------------------
// copy the area from the interface buffer to the data array
void SurfaceCopyToMem(long fromX, long fromY, long width, long height, long fromWidth, BYTE* fromSurface, BYTE* toMem) {
fromSurface += fromY * fromWidth + fromX;
long i = 0;
for (long h = 0; h < height; h++) {
for (long w = 0; w < width; w++) {
toMem[i++] = fromSurface[w];
}
fromSurface += fromWidth;
}
}
// safe copy data from memory to the area of the interface buffer
void DrawToSurface(long toX, long toY, long width, long height, long toWidth, long toHeight, BYTE* toSurface, BYTE* fromMem) {
BYTE* _toSurface = toSurface + (toY * toWidth + toX);
BYTE* endToSurf = (toWidth * toHeight) + toSurface;
long i = 0;
for (long h = 0; h < height; h++) {
for (long w = 0; w < width; w++) {
if (_toSurface + w > endToSurf) return;
if (_toSurface >= toSurface) _toSurface[w] = fromMem[i++];
}
_toSurface += toWidth;
}
}
// safe copy data from surface to surface with mask
void DrawToSurface(long width, long height, long fromX, long fromY, long fromWidth, BYTE* fromSurf,
long toX, long toY, long toWidth, long toHeight, BYTE* toSurf, int maskRef)
{
BYTE* _fromSurf = fromSurf + (fromY * fromWidth + fromX);
BYTE* _toSurf = toSurf + (toY * toWidth + toX);
BYTE* endToSurf = (toWidth * toHeight) + toSurf;
for (long h = 0; h < height; h++) {
for (long w = 0; w < width; w++) {
if (_toSurf + w > endToSurf) return;
if (_toSurf >= toSurf && _fromSurf[w] != maskRef) _toSurf[w] = _fromSurf[w];
}
_fromSurf += fromWidth;
_toSurf += toWidth;
}
}
// safe copy data from surface to surface
void DrawToSurface(long width, long height, long fromX, long fromY, long fromWidth, BYTE* fromSurf,
long toX, long toY, long toWidth, long toHeight, BYTE* toSurf)
{
BYTE* _fromSurf = fromSurf + (fromY * fromWidth + fromX);
BYTE* _toSurf = toSurf + (toY * toWidth + toX);
BYTE* endToSurf = (toWidth * toHeight) + toSurf;
for (long h = 0; h < height; h++) {
for (long w = 0; w < width; w++) {
if (_toSurf + w > endToSurf) return;
if (_toSurf >= toSurf) _toSurf[w] = _fromSurf[w];
}
_fromSurf += fromWidth;
_toSurf += toWidth;
}
}
//---------------------------------------------------------
// print text to surface
void PrintText(char* displayText, BYTE colorIndex, DWORD xPos, DWORD yPos, DWORD txtWidth, DWORD toWidth, BYTE* toSurface) {
DWORD posOffset = yPos * toWidth + xPos;
__asm {
xor eax, eax;
mov al, colorIndex;
mov edx, displayText;
push eax;
mov ebx, txtWidth;
mov eax, toSurface;
mov ecx, toWidth;
add eax, posOffset;
call dword ptr ds:[FO_VAR_text_to_buf];
}
}
void PrintTextFM(char* displayText, BYTE colorIndex, DWORD xPos, DWORD yPos, DWORD txtWidth, DWORD toWidth, BYTE* toSurface) {
DWORD posOffset = yPos * toWidth + xPos;
__asm {
xor eax, eax;
mov al, colorIndex;
mov edx, displayText;
push eax;
mov ebx, txtWidth;
mov eax, toSurface;
mov ecx, toWidth;
add eax, posOffset;
call fo::funcoffs::FMtext_to_buf_;
}
}
//---------------------------------------------------------
//gets the height of the currently selected font
DWORD GetTextHeight() {
// DWORD TxtHeight;
__asm {
call dword ptr ds:[FO_VAR_text_height]; //get text height
// mov TxtHeight, eax;
}
// return TxtHeight;
}
//---------------------------------------------------------
//gets the length of a string using the currently selected font
DWORD GetTextWidth(const char* TextMsg) {
__asm {
mov eax, TextMsg;
call dword ptr ds:[FO_VAR_text_width]; //get text width
}
}
DWORD GetTextWidthFM(const char* TextMsg) {
return fo::func::FMtext_width(TextMsg); //get text width
}
//---------------------------------------------------------
//get width of Char for current font
DWORD GetCharWidth(char charVal) {
__asm {
mov al, charVal;
call dword ptr ds:[FO_VAR_text_char_width];
}
}
DWORD GetCharWidthFM(char charVal) {
__asm {
mov al, charVal;
call fo::funcoffs::FMtext_char_width_;
}
}
//---------------------------------------------------------
//get maximum string length for current font - if all characters were maximum width
DWORD GetMaxTextWidth(const char* TextMsg) {
// DWORD msgWidth;
__asm {
mov eax, TextMsg;
call dword ptr ds:[FO_VAR_text_mono_width];
// mov msgWidth, eax;
}
// return msgWidth;
}
//---------------------------------------------------------
//get number of pixels between characters for current font
DWORD GetCharGapWidth() {
// DWORD gapWidth;
__asm {
call dword ptr ds:[FO_VAR_text_spacing];
// mov gapWidth, eax;
}
// return gapWidth;
}
//---------------------------------------------------------
//get maximum character width for current font
DWORD GetMaxCharWidth() {
// DWORD charWidth = 0;
__asm {
call dword ptr ds:[FO_VAR_text_max];
// mov charWidth, eax;
}
// return charWidth;
}
void RedrawObject(GameObject* obj) {
BoundRect rect;
func::obj_bound(obj, &rect);
func::tile_refresh_rect(&rect, obj->elevation);
}
/////////////////////////////////////////////////////////////////UNLISTED FRM FUNCTIONS////////////////////////////////////////////////////////////////////////
static bool LoadFrmHeader(UnlistedFrm *frmHeader, fo::DbFile* frmStream) {
if (fo::func::db_freadInt(frmStream, &frmHeader->version) == -1)
return false;
else if (fo::func::db_freadShort(frmStream, &frmHeader->FPS) == -1)
return false;
else if (fo::func::db_freadShort(frmStream, &frmHeader->actionFrame) == -1)
return false;
else if (fo::func::db_freadShort(frmStream, &frmHeader->numFrames) == -1)
return false;
else if (fo::func::db_freadShortCount(frmStream, frmHeader->xCentreShift, 6) == -1)
return false;
else if (fo::func::db_freadShortCount(frmStream, frmHeader->yCentreShift, 6) == -1)
return false;
else if (fo::func::db_freadIntCount(frmStream, frmHeader->oriOffset, 6) == -1)
return false;
else if (fo::func::db_freadInt(frmStream, &frmHeader->frameAreaSize) == -1)
return false;
return true;
}
static bool LoadFrmFrame(UnlistedFrm::Frame *frame, fo::DbFile* frmStream) {
//FRMframe *frameHeader = (FRMframe*)frameMEM;
//BYTE* frameBuff = frame + sizeof(FRMframe);
if (fo::func::db_freadShort(frmStream, &frame->width) == -1)
return false;
else if (fo::func::db_freadShort(frmStream, &frame->height) == -1)
return false;
else if (fo::func::db_freadInt(frmStream, &frame->size) == -1)
return false;
else if (fo::func::db_freadShort(frmStream, &frame->x) == -1)
return false;
else if (fo::func::db_freadShort(frmStream, &frame->y) == -1)
return false;
frame->indexBuff = new BYTE[frame->size];
if (fo::func::db_fread(frame->indexBuff, frame->size, 1, frmStream) != 1)
return false;
return true;
}
UnlistedFrm *LoadUnlistedFrm(char *frmName, unsigned int folderRef) {
if (folderRef > fo::OBJ_TYPE_SKILLDEX) return nullptr;
char *artfolder = fo::var::art[folderRef].path; // address of art type name
char frmPath[MAX_PATH];
sprintf_s(frmPath, MAX_PATH, "art\\%s\\%s", artfolder, frmName);
UnlistedFrm *frm = new UnlistedFrm;
auto frmStream = fo::func::xfopen(frmPath, "rb");
if (frmStream != nullptr) {
if (!LoadFrmHeader(frm, frmStream)) {
fo::func::db_fclose(frmStream);
delete frm;
return nullptr;
}
DWORD oriOffset_1st = frm->oriOffset[0];
DWORD oriOffset_new = 0;
frm->frames = new UnlistedFrm::Frame[6 * frm->numFrames];
for (int ori = 0; ori < 6; ori++) {
if (ori == 0 || frm->oriOffset[ori] != oriOffset_1st) {
frm->oriOffset[ori] = oriOffset_new;
for (int fNum = 0; fNum < frm->numFrames; fNum++) {
if (!LoadFrmFrame(&frm->frames[oriOffset_new + fNum], frmStream)) {
fo::func::db_fclose(frmStream);
delete frm;
return nullptr;
}
}
oriOffset_new += frm->numFrames;
} else {
frm->oriOffset[ori] = 0;
}
}
fo::func::db_fclose(frmStream);
} else {
delete frm;
return nullptr;
}
return frm;
}
}