Add VIP cards and the VIP panel (mt_vip)
Cards 38001 (7 days) and 38002 (30 days) extend all seven original premium services of the account; the server side is metin2-server port_premium.h (docs/FEATURE-VIP.md). - mt_vip: /mt_vip 1 status, VIP button by the minimap, remaining time per service, chat line on card use. - tools/server/vip-card: 40250 ships both cards with size 0 (two share a cell, neither moves); give them one cell, stacking, a log and no NPC sale, and name them on the server. - locale_proto_patch: optional item shape table (size, flags, anti flags) so the client proto matches; data/zh_40250/item_shapes_server.tsv. - Item mall (9010): 7 days 5,000,000, 30 days 18,000,000 gold. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
b14fb2ffe9
commit
5dcac9cbd4
@@ -2,7 +2,9 @@
|
||||
// Runtime loading remains the original 40250 path and binary format.
|
||||
// An optional item limits table (vnum, limit type 0, value 0, limit type 1, value 1) mirrors the few
|
||||
// item_proto.txt limit changes made on our server (tools/server/*), so the tooltip states what the
|
||||
// server enforces. An optional 14-row stone overlay restores extracted +6 stone data.
|
||||
// server enforces. An optional 14-row stone overlay restores extracted +6 stone data. An optional shape table
|
||||
// (vnum, size, flags, anti flags) mirrors items the server gives a new use (tools/server/vip-card), so they
|
||||
// take a cell, stack and refuse NPC sale as the server does.
|
||||
#include "GameLib/StdAfx.h"
|
||||
#include "GameLib/ItemData.h"
|
||||
#include "UserInterface/StdAfx.h"
|
||||
@@ -25,6 +27,7 @@ namespace {
|
||||
using Item = CItemData::TItemTable;
|
||||
using Mob = CPythonNonPlayer::TMobTable;
|
||||
std::map<DWORD, std::array<long, 11>> stones;
|
||||
std::map<DWORD, std::array<DWORD, 3>> shapes;
|
||||
|
||||
constexpr std::array<DWORD, 4> item_key{{173217, 72619434, 408587239, 27973291}};
|
||||
constexpr std::array<DWORD, 4> mob_key{{4813894, 18955, 552631, 6822045}};
|
||||
@@ -109,6 +112,12 @@ void apply_limits<Item>(Item& record, const std::map<DWORD, Limits>& limits, std
|
||||
record.aApplies[0].bType = s[3]; record.aApplies[0].lValue = s[4];
|
||||
for (int i = 0; i < 6; ++i) record.alValues[i] = s[5+i];
|
||||
}
|
||||
const auto shape = shapes.find(record.dwVnum);
|
||||
if (shape != shapes.end()) {
|
||||
record.bSize = static_cast<BYTE>(shape->second[0]);
|
||||
record.dwFlags = shape->second[1];
|
||||
record.dwAntiFlags = shape->second[2];
|
||||
}
|
||||
const auto found = limits.find(record.dwVnum);
|
||||
if (found == limits.end()) return;
|
||||
std::memcpy(record.aLimits, found->second.data(), sizeof(record.aLimits));
|
||||
@@ -120,6 +129,8 @@ bool limits_field(std::size_t, DWORD, const std::map<DWORD, Limits>&) { return f
|
||||
|
||||
template <>
|
||||
bool limits_field<Item>(std::size_t offset, DWORD vnum, const std::map<DWORD, Limits>& limits) {
|
||||
if (shapes.count(vnum) && (offset == offsetof(Item, bSize) ||
|
||||
(offset >= offsetof(Item, dwAntiFlags) && offset < offsetof(Item, dwFlags)+sizeof(Item::dwFlags)))) return true;
|
||||
if (stones.count(vnum) && (offset == offsetof(Item, bType) || offset == offsetof(Item, bSubType) ||
|
||||
(offset >= offsetof(Item, dwWearFlags) && offset < offsetof(Item, dwWearFlags)+sizeof(Item::dwWearFlags)) ||
|
||||
(offset >= offsetof(Item, aApplies) && offset < offsetof(Item, aApplies)+sizeof(Item::aApplies)) ||
|
||||
@@ -199,14 +210,25 @@ std::vector<BYTE> patch(const char* source, const char* names_path,
|
||||
} // namespace
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
if (argc != 7 && argc != 8 && argc != 9) {
|
||||
if (argc < 7 || argc > 10) {
|
||||
std::cerr << "usage: locale_proto_patch item_proto mob_proto item_names_gbk mob_names_gbk out_item out_mob"
|
||||
" [item_limits [item_stones]]\n";
|
||||
" [item_limits [item_stones [item_shapes]]]\n";
|
||||
return 2;
|
||||
}
|
||||
try {
|
||||
static CLZO lzo;
|
||||
if (argc == 9) {
|
||||
if (argc == 10) {
|
||||
std::ifstream stream(argv[9]);
|
||||
if (!stream) throw std::runtime_error("cannot read item shapes");
|
||||
std::string line;
|
||||
while (std::getline(stream, line)) {
|
||||
if (line.empty() || line[0] == '#') continue;
|
||||
std::istringstream row(line); DWORD vnum; std::array<DWORD,3> values{};
|
||||
if (!(row >> vnum >> values[0] >> values[1] >> values[2]) || values[0] < 1 || values[0] > 3 ||
|
||||
!shapes.emplace(vnum, values).second) throw std::runtime_error("bad item shape row: " + line);
|
||||
}
|
||||
}
|
||||
if (argc >= 9) {
|
||||
std::ifstream stream(argv[8]);
|
||||
if (!stream) throw std::runtime_error("cannot read stone overlay");
|
||||
std::string line;
|
||||
|
||||
Reference in New Issue
Block a user