rodecomp/Source/UI/Window/FuncDrawWNDChar.cpp
ragnarok-clanker 2d55a137fc Maintenance: [forced, user-approved] Family V1: remove the invented global g_luaScript and the session-as-script casts. 0x1738010 is
Batch #10269 by ragnarok-clanker: forced by an admin (2 lower score(s), 6 changed function(s)); checked with 102 compiled unit(s), including every includer of Source/Script/LuaScript.h.
2026-11-03 14:40:29 +00:00

415 lines
15 KiB
C++

#include "UI/Window/FuncDrawWNDChar.h"
#include "Client/Session.h"
#include "Script/LuaScript.h"
#include "Contents/SpriteRidingOffset.h"
#include "Contents/SpriteRidingPolicy.h"
#include "Resource/Sprites/ActionResource.h"
#include "Contents/Configuration/ServiceConfiguration.h"
#include "Contents/Skills/ClientSkills.h"
#include "Render/DeviceContext/DCSurface.h"
#include "Render/DeviceContext/DeviceContext.h"
#include "Render/Renderer.h"
#include "Render/Textures/SpriteAtlas.h"
#include "Render/Textures/SpritePixels.h"
#include "Render/Textures/Texture.h"
#include "Resource/PaletteResource.h"
#include "Resource/ResourceMgr.h"
#include "Resource/Sprites/SpriteResource.h"
#include "Script/LuaScript.h"
#include "UI/Window/UIWindow.h"
// OFFSET: 0x007E3860
// FUNCTION: fn_09724
FuncDrawWNDChar::FuncDrawWNDChar(
UIWindow* window, int x, int y, unsigned char sex, short job, int hairColor, short body, short head, short bottomAccessory, short topAccessory,
short midAccessory, int robe, unsigned char flags, int shield, int bodyStyle, int headPalette, int action, int motion, int time
)
: m_window(window), m_x(x), m_y(y), m_sex(sex), m_job(job), m_hairColor(hairColor), m_body(body), m_head(head), m_bottomAccessory(bottomAccessory),
m_topAccessory(topAccessory), m_midAccessory(midAccessory), m_robe(robe), m_flags(flags), m_shield(shield), m_bodyStyle(bodyStyle),
m_headPalette(headPalette), m_action(action), m_motion(motion), m_time(time), m_helmRobe(false), m_drawOnTop(false), m_resources(), unknown_0050(),
unknown_0068(), m_layers(), unknown_0088(0), unknown_008C(0), unknown_0090(0), unknown_0094(0) {
g_session.m_hotkeyScript->CallTable(std::string("TB_Layer_Priority", 17), std::string("IsHelmRobe", 10), "d>b", this->m_robe, &this->m_helmRobe);
this->m_drawOnTop = this->DrawOnTop();
this->m_layers[5] = this->m_bottomAccessory;
this->m_layers[4] = this->m_topAccessory;
this->m_layers[3] = this->m_midAccessory;
this->m_layers[6] = this->m_robe;
}
// OFFSET: 0x007D4840
// FUNCTION: fn_09582
FuncDrawWNDChar::~FuncDrawWNDChar() = default;
// OFFSET: 0x007E3E60
// FUNCTION: fn_09727
void FuncDrawWNDChar::Draw(bool option) {
std::list<int> layers = this->DrawOrder();
this->LoadResources(layers);
for (int layer : layers) {
if (this->m_resources.find(layer) == this->m_resources.end()) {
continue;
}
std::pair<CSprRes*, CActRes*> resources = this->m_resources[layer];
CSprRes* sprite = resources.first;
CActRes* act = resources.second;
if (!sprite || !act) {
continue;
}
int key = layer;
FuncDrawOffset offset;
if (g_session.IsDoramJob(this->m_job) == true) {
int x = 0;
int y = 0;
int turn = 0;
if (this->m_motion == 2) {
turn = -1;
} else if (this->m_motion == 1) {
turn = 1;
}
int direction = this->m_action % 8 + turn;
if (direction < 0) {
direction = 7;
}
if (direction > 7) {
direction = 0;
}
g_session.m_hotkeyScript->Call("OffsetItemPos_GetOffsetForDoram", "ddd>dd", this->m_layers[key], direction, g_session.SyncUnknown0E98(), &x, &y);
offset.x = x;
offset.y = y;
}
const ActionFrame* frame = &act->Frame(this->m_action, this->m_motion);
switch (layer) {
case 2: {
FuncDrawOffset body = this->BodyAnchorOffset(frame, option);
if (this->unknown_0094 == 1) {
continue;
}
offset.x += body.x;
offset.y += body.y;
break;
}
case 3:
case 4:
case 5:
case 6: {
if (!this->unknown_0088 && this->m_resources.find(2) != this->m_resources.end() && this->m_resources[2].second) {
CActRes* bodyAct = this->m_resources.find(2) != this->m_resources.end() ? this->m_resources[2].second : nullptr;
this->BodyAnchorOffset(&bodyAct->Frame(this->m_action, this->m_motion), option);
}
FuncDrawOffset anchor = this->LayerAnchorOffset(layer, &frame, act);
offset.x += anchor.x;
offset.y += anchor.y;
break;
}
case 0:
case 7: {
int baseMotion = this->m_motion;
int syncedMotion = this->m_motion;
CActRes* layerAct = this->m_resources.find(layer) != this->m_resources.end() ? this->m_resources[layer].second : nullptr;
int action = this->m_action;
if ((action >= 0 && action < 8) || static_cast<unsigned>(action - 16) <= 7) {
baseMotion = 0;
}
CActRes* bodyAct = this->m_resources.find(1) != this->m_resources.end() ? this->m_resources[1].second : nullptr;
g_session.GetSyncedFrame(bodyAct, layerAct, action, baseMotion, this->m_time, &syncedMotion);
frame = &layerAct->Frame(this->m_action, syncedMotion);
break;
}
}
const std::uint32_t* palette = sprite->palette;
if (layer == 1) {
if (this->m_bodyStyle > 0) {
palette = static_cast<CPaletteRes*>(ResourceMgr::Instance().Load(this->unknown_0050))->palette;
}
} else if (layer == 2 && this->m_headPalette > 0) {
palette = static_cast<CPaletteRes*>(ResourceMgr::Instance().Load(this->unknown_0068))->palette;
}
const SpriteFrameCell& cell = frame->Cell(this->IsSpecialLayer(layer));
if (option == true) {
if (cell.image == -1) {
continue;
}
SpriteAtlasRecord record{};
const SpriteRleImage* image = sprite->images[cell.imageType][cell.image];
if (cell.imageType == 0 && !g_renderer->atlas.Find(image, palette, record)) {
g_renderer->atlas.Insert(*GetRenderSystemHolder()->system, image, palette, record, g_serviceType, g_resourceReuseDelay);
}
float scale = GetSkillFactor(this->m_job);
int offsetX = static_cast<int>(static_cast<float>(offset.x) * scale);
int offsetY = static_cast<int>(static_cast<float>(offset.y) * scale);
UIWindow* window = this->m_window;
window->AddSpriteCellOverlay(
this->m_x + offsetX, this->m_y + offsetY, reinterpret_cast<int>(act), reinterpret_cast<int>(sprite), image, &cell, scale,
static_cast<float>(cell.unknown_001C), window->Color(), palette, 1.0f
);
} else {
if (cell.image == -1) {
continue;
}
SpriteAtlasRecord record{};
const SpriteRleImage* image = sprite->images[cell.imageType][cell.image];
CTexture* texture;
if (cell.imageType == 0) {
texture = g_renderer->atlas.Find(image, palette, record);
if (!texture) {
texture = g_renderer->atlas.Insert(*GetRenderSystemHolder()->system, image, palette, record, g_serviceType, g_resourceReuseDelay);
}
} else {
texture = static_cast<CTexture*>(const_cast<void*>(image->unknown_0008));
}
int x = this->m_x + cell.x + offset.x;
int y = cell.y + this->m_y + offset.y;
int clip = y < 0 ? y : 0;
int top = y < 0 ? 0 : y;
if (clip <= top) {
this->m_window->geometry.surface->DrawSurface(
x, top, texture, static_cast<int>(record.u0 * 256.0f), static_cast<int>(record.v0 * 256.0f - static_cast<float>(clip)), image->width,
image->height + clip, cell.mirrored, image->unknown_0004 + 1, image->unknown_0006 + 1, 0, 0
);
}
}
}
}
// OFFSET: 0x007E4680
// FUNCTION: fn_09728
std::list<int> FuncDrawWNDChar::DrawOrder() {
std::map<int, int> defaultPriorities;
defaultPriorities[3] = 4;
defaultPriorities[4] = 3;
defaultPriorities[5] = 2;
defaultPriorities[6] = 8;
std::map<int, int> order;
for (int layer = 0; layer < 8; ++layer) {
switch (layer) {
case 0:
if (this->m_robe <= 0 || this->m_drawOnTop) {
break;
}
[[fallthrough]];
case 1:
order[layer] = layer;
break;
case 2:
order[3] = 2;
break;
case 6:
if (!this->m_helmRobe) {
break;
}
[[fallthrough]];
case 3:
case 4:
case 5:
if (this->m_layers[layer] > 0) {
int priority = -1;
g_session.m_hotkeyScript->Call("GetLayerPriority", "ddd>d", 0, defaultPriorities[layer], this->m_layers[layer], &priority);
order[priority] = layer;
}
break;
case 7:
if (this->m_robe > 0 && this->m_drawOnTop) {
order[g_session.IsTopLayerRobe(this->m_robe) ? 400 : 2] = layer;
}
break;
}
}
std::list<int> layers;
if (this->m_drawOnTop) {
for (std::map<int, int>::reverse_iterator it = order.rbegin(); it != order.rend(); ++it) {
if (it->first < 0) {
layers.push_back(it->second);
} else {
layers.push_front(it->second);
}
}
} else {
for (std::map<int, int>::iterator it = order.begin(); it != order.end(); ++it) {
layers.push_back(it->second);
}
}
return layers;
}
// OFFSET: 0x007E4FB0
// FUNCTION: fn_09729
bool FuncDrawWNDChar::DrawOnTop() {
int job = this->m_job;
bool drawOnTop = true;
if (g_session.m_newRobeDrawOnTop == 1) {
if (this->m_flags == 1) {
job = g_session.GetRidingJob(job);
}
int layer = job;
if (g_session.m_hotkeyScript->Call("_New_DrawOnTop", "ddddd>bd", this->m_robe, this->m_sex, job, this->m_action, this->m_motion, &drawOnTop, &layer) == 1) {
return drawOnTop;
}
} else if (g_session.m_hotkeyScript->Call("DrawOnTop", "ddddd>b", this->m_robe, this->m_sex, job, this->m_action, this->m_motion, &drawOnTop) == 1) {
return drawOnTop;
}
return true;
}
// OFFSET: 0x007E5120
// FUNCTION: fn_09730
int FuncDrawWNDChar::IsSpecialLayer(int layer) {
const auto found = this->m_layers.find(layer);
if (found != this->m_layers.end() && g_session.IsSpecialMonsterJob(found->second) == true) {
return 1;
}
return layer == 2;
}
// OFFSET: 0x007E5190
// FUNCTION: fn_09731
FuncDrawOffset FuncDrawWNDChar::BodyAnchorOffset(const ActionFrame* frame, bool keepScale) {
FuncDrawOffset offset;
CActRes* act = this->m_resources.find(1) != this->m_resources.end() ? this->m_resources[1].second : nullptr;
const ActionFrame& base = act->Frame(this->m_action, this->m_motion);
if (frame->anchorCount > 0 && base.anchorCount > 0) {
const ActionAnchor* frameAnchor = frame->anchors.data();
const ActionAnchor* baseAnchor = base.anchors.data();
if (frameAnchor->unknown_0008 == baseAnchor->unknown_0008) {
if (this->m_shield != 0) {
if (keepScale == true) {
offset.x += baseAnchor->x - frameAnchor->x;
offset.y += baseAnchor->y - frameAnchor->y;
} else {
offset.x = static_cast<int>(static_cast<float>(baseAnchor->x) / base.Cell(0).scaleX - static_cast<float>(frameAnchor->x) + static_cast<float>(offset.x));
float baseY = static_cast<float>(base.anchors.data()->y);
offset.y = static_cast<int>(baseY / base.Cell(0).scaleY - static_cast<float>(frame->anchors.data()->y) + static_cast<float>(offset.y));
}
} else {
offset.x = baseAnchor->x - frameAnchor->x;
offset.y = baseAnchor->y - frameAnchor->y;
}
this->unknown_0090 = offset.y;
this->unknown_008C = offset.x;
}
}
this->unknown_0088 = 1;
return offset;
}
// OFFSET: 0x007E5310
// FUNCTION: fn_09732
FuncDrawOffset FuncDrawWNDChar::LayerAnchorOffset(int layer, const ActionFrame** frame, CActRes* act) {
const ActionFrame* current = *frame;
FuncDrawOffset offset;
CActRes* bodyAct = this->m_resources.find(2) != this->m_resources.end() ? this->m_resources[2].second : nullptr;
const ActionFrame& base = bodyAct->Frame(this->m_action, this->m_motion);
int syncedMotion = 0;
const ActionFrame* synced = g_session.GetSyncedFrame(bodyAct, act, this->m_action, this->m_motion, this->m_time, &syncedMotion);
if (synced && syncedMotion != this->m_motion) {
*frame = synced;
current = synced;
}
if (current->anchorCount > 0 && base.anchorCount > 0) {
const ActionAnchor* baseAnchor = base.anchors.data();
const ActionAnchor* frameAnchor = current->anchors.data();
if (frameAnchor->unknown_0008 == baseAnchor->unknown_0008) {
offset.x = baseAnchor->x - frameAnchor->x;
offset.y = baseAnchor->y - frameAnchor->y;
}
}
offset.x += this->unknown_008C;
int ridingOffset;
if (IsIgnoredRidingState(this->m_layers[layer], this->m_job, this->m_action - this->m_action % 8, this->m_flags) == true) {
ridingOffset = SpriteRidingOffset(&base, this->m_action, this->m_motion, this->m_job, this->m_sex, this->m_body);
} else {
ridingOffset = this->unknown_0090;
}
offset.y += ridingOffset;
if (layer == 4) {
short accessory = this->m_topAccessory;
if (accessory == 181 || accessory == 197) {
offset.y -= 7;
} else if (accessory == 200 && g_session.SyncUnknown0E98() == 0) {
offset.y -= 4;
}
}
return offset;
}
// OFFSET: 0x007E5A30
// FUNCTION: fn_09733
void FuncDrawWNDChar::LoadResources(const std::list<int>& layers) {
for (int layer : layers) {
std::string sprName;
std::string actName;
switch (layer) {
case 0:
case 7:
if (this->m_shield == 0) {
sprName = g_session.GetRobeSprName(this->m_job, this->m_hairColor, this->m_sex, this->m_robe, this->m_flags, this->m_body);
actName = g_session.GetRobeActName(this->m_job, this->m_hairColor, this->m_sex, this->m_robe, this->m_flags, this->m_body);
}
break;
case 1: {
int job = this->m_flags == 1 ? g_session.GetRidingJob(this->m_job) : this->m_job;
sprName = g_session.GetBodySprName(job, this->m_sex, this->m_body, this->m_shield);
actName = GetBodyActionResourceName(job, this->m_sex, this->m_body, this->m_shield);
this->unknown_0050 = g_session.GetBodyPalName(job, this->m_sex, this->m_bodyStyle, this->m_body, this->m_shield, 0);
break;
}
case 2: {
int head = this->m_head;
sprName = g_session.GetHeadSprName(this->m_job, this->m_sex, &head);
actName = g_session.GetHeadActName(this->m_job, this->m_sex, &head);
this->m_head = static_cast<short>(head);
this->unknown_0068 = g_session.GetHeadPalName(this->m_head, this->m_job, this->m_sex, this->m_headPalette);
break;
}
case 3:
case 4:
case 5:
case 6:
sprName = g_session.GetAccessorySprName(this->m_job, this->m_sex, this->m_head, this->m_layers[layer]);
actName = g_session.GetAccessoryActName(this->m_job, this->m_sex, this->m_head, this->m_layers[layer]);
break;
}
CSprRes* sprite = static_cast<CSprRes*>(ResourceMgr::Instance().Load(sprName));
CActRes* act = static_cast<CActRes*>(ResourceMgr::Instance().Load(actName));
if (sprite && act) {
std::pair<CSprRes*, CActRes*>& entry = this->m_resources[layer];
entry.first = sprite;
entry.second = act;
}
}
for (int layer = 3; layer <= 5; ++layer) {
if (g_session.IsSpecialMonsterJob(this->m_layers[layer]) == true) {
this->unknown_0094 = 1;
return;
}
}
}