rodecomp/Source/UI/Window/FuncDrawWNDChar.cpp
ragnarok-clanker c932ad6499 Maintenance: Resubmission of 15134 without the 12 struct/type renames (their new names changed mangled symbols in 17 functions). Rena
Batch #15141 by ragnarok-clanker: no function's compiled code changes; checked with 537 compiled unit(s), including every includer of Source/Client/Input/LanguageTC.h, Source/Client/Mode/CGameMode.h, Source/Client/Mode/CLoginMode.h, Source/Client/Replay.h, Source/Client/Session.h, Source/Contents/AchievementMgr.h, Source/Contents/Actors/CharacterRender.h, Source/Contents/Actors/EffectActors.h, Source/Contents/Actors/GameActor.h, Source/Contents/Actors/Pc.h, Source/Contents/EnchantMgr.h, Source/Contents/GuildInfo.h, Source/Contents/Messenger/MessengerWindows.h, Source/Contents/Schedule/TimeScheduler.h, Source/Contents/Sound/SoundMgr.h, Source/Contents/SpriteLayerDrawing.h, Source/Contents/TableMng.h, Source/Contents/World/SkyBox.h, Source/UI/Chat/UINewChatWnd.h, Source/UI/Text/CreateLineInfo.h, Source/UI/UIGradeEnchantWnd.h, Source/UI/Window/UINewSelectCharWnd.h, Source/UI/Window/UINewSkillListWnd.h, Source/UI/Window/UIPetInfoWnd.h, Source/UI/Window/UIReplayRECControlWnd.h, Source/UI/Window/UIWindowMgr.h, Source/UI/Window/UIWindowMgr.inl.
2026-11-09 12:21:09 +00:00

425 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(), bodyPaletteName(),
headPaletteName(), m_layers(), hideHeadLayer(0), offsetX(0), offsetY(0), customRendering(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;
if (direction < 0) {
direction += 8;
}
direction += 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.GetCurrentSex(), &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->customRendering == 1) {
continue;
}
offset.x += body.x;
offset.y += body.y;
break;
}
case 3:
case 4:
case 5:
case 6: {
if (!this->hideHeadLayer && 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->bodyPaletteName))->palette;
}
} else if (layer == 2 && this->m_headPalette > 0) {
palette = static_cast<CPaletteRes*>(ResourceMgr::Instance().Load(this->headPaletteName))->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.rotation), 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->surface));
}
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->doubleWidth + 1, image->doubleHeight + 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;
if (g_session.m_newRobeDrawOnTop == 1) {
bool drawOnTop = true;
if (this->m_flags == 1) {
job = g_session.GetRidingJob(job);
}
int layer = job;
if (g_session.m_hotkeyScript->Call(std::string("_New_DrawOnTop", 14), "ddddd>bd", this->m_robe, this->m_sex, job, this->m_action, this->m_motion, &drawOnTop, &layer) == 1) {
return drawOnTop;
}
} else {
bool drawOnTop = true;
if (g_session.m_hotkeyScript->Call(std::string("DrawOnTop", 9), "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;
int layer = 1;
CActRes* act = this->m_resources.find(layer) != this->m_resources.end() ? this->m_resources[layer].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->attachmentId == baseAnchor->attachmentId) {
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->offsetY = offset.y;
this->offsetX = offset.x;
}
}
this->hideHeadLayer = 1;
return offset;
}
// OFFSET: 0x007E5310
// FUNCTION: fn_09732
FuncDrawOffset FuncDrawWNDChar::LayerAnchorOffset(int layer, const ActionFrame** frame, CActRes* act) {
const ActionFrame* current = *frame;
FuncDrawOffset offset;
const auto resourcesEnd = this->m_resources.end();
int bodyLayer = 2;
CActRes* bodyAct = this->m_resources.find(bodyLayer) != resourcesEnd ? this->m_resources[bodyLayer].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->attachmentId == baseAnchor->attachmentId) {
offset.x = baseAnchor->x - frameAnchor->x;
offset.y = baseAnchor->y - frameAnchor->y;
}
}
offset.x += this->offsetX;
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->offsetY;
}
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.GetCurrentSex() == 0) {
offset.y -= 4;
}
}
return offset;
}
// OFFSET: 0x007E5A30
// FUNCTION: fn_09733
__declspec(safebuffers) void FuncDrawWNDChar::LoadResources(const std::list<int>& layers) {
for (int layer : layers) {
std::string sprName;
std::string actName;
switch (layer) {
case 1: {
int job = this->m_flags == 1 ? g_session.GetRidingJob(this->m_job) : this->m_job;
sprName = static_cast<const std::string&>(g_session.GetBodySprName(job, this->m_sex, this->m_body, this->m_shield));
actName = static_cast<const std::string&>(GetBodyActionResourceName(job, this->m_sex, this->m_body, this->m_shield));
this->bodyPaletteName = static_cast<const std::string&>(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 = static_cast<const std::string&>(g_session.GetHeadSprName(this->m_job, this->m_sex, &head));
actName = static_cast<const std::string&>(g_session.GetHeadActName(this->m_job, this->m_sex, &head));
this->m_head = static_cast<short>(head);
this->headPaletteName = static_cast<const std::string&>(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 = static_cast<const std::string&>(g_session.GetAccessorySprName(this->m_job, this->m_sex, this->m_head, this->m_layers[layer]));
actName = static_cast<const std::string&>(g_session.GetAccessoryActName(this->m_job, this->m_sex, this->m_head, this->m_layers[layer]));
break;
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;
}
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->customRendering = 1;
return;
}
}
}