AVA2 unlock P4: sash (mt_sash windows/slot/tooltips, sash module, CHR_EQUIPPART_SASH, item_scale)
Client side of metin2-server FEATURE-AVA2-UNLOCK P4: HEADER_CG_SASH 211 / HEADER_GC_SASH 215, a fifth awPart WORD in the character packets (old servers break), the sash part attached by scale per job/sex, the combine and absorb windows in script mt_sash, and quest tools/server/sash on 20016. The ava2items pack keeps the four grade-4 sashes plus their effects, acce window images and item_scale.txt. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
0d2a9ebe36
commit
4d7306792e
@@ -37,6 +37,7 @@
|
||||
#include "UserInterface/PythonQuest.h"
|
||||
#include "UserInterface/PythonMessenger.h"
|
||||
#include "UserInterface/PythonSafeBox.h"
|
||||
#include "UserInterface/PythonSash.h"
|
||||
#include "UserInterface/PythonGuild.h"
|
||||
#include "UserInterface/MarkManager.h"
|
||||
#include "UserInterface/GuildMarkDownloader.h"
|
||||
@@ -153,6 +154,7 @@ struct GameSingletons
|
||||
CPythonQuest pyQuest;
|
||||
CPythonMessenger pyManager;
|
||||
CPythonSafeBox pySafeBox;
|
||||
CPythonSash pySash; // PORT: AVA2 sash
|
||||
CPythonGuild pyGuild;
|
||||
CGuildMarkManager kGuildMarkManager;
|
||||
CGuildMarkDownloader kGuildMarkDownloader;
|
||||
@@ -911,6 +913,7 @@ bool run_main_script_body(const char* lpCmdLine, std::string* error)
|
||||
initBackground();
|
||||
initMessenger();
|
||||
initsafebox();
|
||||
initSash();
|
||||
initguild();
|
||||
initServerStateChecker();
|
||||
initRenderTarget(); // PORT: AVA2 model preview
|
||||
@@ -1013,6 +1016,7 @@ namespace PythonBoot
|
||||
#include "BattlePassScript.inc"
|
||||
#include "AppliesScript.inc"
|
||||
#include "ArmorShapesScript.inc"
|
||||
#include "SashScript.inc"
|
||||
|
||||
static void install_script_module(const char* c_szModule, const char* c_szScript)
|
||||
{
|
||||
@@ -1097,6 +1101,7 @@ bool RunMainScript(const char* lpCmdLine, std::string* error)
|
||||
install_script_module("mt_battlepass", c_szBattlePassScript);
|
||||
install_script_module("mt_applies", c_szAppliesScript);
|
||||
install_script_module("mt_shapes", c_szArmorShapesScript);
|
||||
install_script_module("mt_sash", c_szSashScript);
|
||||
if (g_touch_input)
|
||||
install_script_module("mt_mobile_hud", c_szMobileHUDScript);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,612 @@
|
||||
// PORT: AVA2 sash (披风) windows and tooltips, as module mt_sash. The C++ side is the AVA2 sash module
|
||||
// (PythonSash.cpp, CG/GC_SASH packets, PART_SASH model + item_scale.txt); this script is AVA2 root/uisash.py
|
||||
// (CombineWindow/AbsorbWindow verbatim, their uiscript dicts served through ui.execfile), the glue AVA2 put
|
||||
// into game.py/interfacemodule.py/uiinventory.py (ActSash/AlertSash, right click adds to the open window,
|
||||
// attached slots lit, costume window sash slot) and uitooltip.py's absorption lines (CalcSashValue,
|
||||
// SetSashResultItem/SetSashResultAbsItem). Server messages arrive as "MTSASH <code>" commands
|
||||
// (metin2-server port_sash.cpp). Assets ship in the ava2items pack (tools/server/ava2-items, SASH_*).
|
||||
static const char c_szSashScript[] = R"PY(# -*- coding: utf-8 -*-
|
||||
import app
|
||||
import chat
|
||||
import chrmgr
|
||||
import constInfo
|
||||
import game
|
||||
import item
|
||||
import localeInfo
|
||||
import mouseModule
|
||||
import player
|
||||
import sash
|
||||
import ui
|
||||
import uiInventory
|
||||
import uiToolTip
|
||||
|
||||
L=player.AutoHuntLocalize
|
||||
|
||||
MESSAGES={'already_open':'披风窗口已经打开','absorb_open':'请先关闭披风吸收窗口','combine_open':'请先关闭披风合成窗口',
|
||||
'locked':'物品已锁定,无法放入','grade':'只能放入相同品级的披风','max_abs':'该披风的吸收率已达上限',
|
||||
'absorb_type':'只能吸收武器或铠甲','absorbed':'该披风已经吸收过物品','gold':'金币不足',
|
||||
'success':'成功!','fail':'失败,作为材料的披风已消失'}
|
||||
ABS_CHANGED='合成完成,吸收率变为 %d%%'
|
||||
ALERT_COMBINE='合成失败时,作为材料的第二件披风将会消失。'
|
||||
ALERT_ABSORB='吸收后,被吸收的物品将会消失。'
|
||||
ABSORB_CHANCE='吸收率:%d%%'
|
||||
ABSORB_CHANCE2='吸收率:%d%% ~ %d%%'
|
||||
|
||||
COMBINE_SCRIPT='''# -*- coding: utf-8 -*-
|
||||
import player
|
||||
import uiScriptLocale
|
||||
L=player.AutoHuntLocalize
|
||||
window = {
|
||||
"name" : "Sash_CombineWindow",
|
||||
"x" : (SCREEN_WIDTH - 215)/2, "y" : (SCREEN_HEIGHT - 270)/2,
|
||||
"style" : ("movable", "float",),
|
||||
"width" : 215, "height" : 270,
|
||||
"children" : (
|
||||
{
|
||||
"name" : "board", "type" : "board", "style" : ("attach",),
|
||||
"x" : 0, "y" : 0, "width" : 215, "height" : 270,
|
||||
"children" : (
|
||||
{
|
||||
"name" : "TitleBar", "type" : "titlebar", "style" : ("attach",),
|
||||
"x" : 6, "y" : 6, "width" : 200, "color" : "yellow",
|
||||
"children" : (
|
||||
{ "name" : "TitleName", "type" : "text", "x" : 95, "y" : 3, "text" : L("披风合成"), "text_horizontal_align" : "center" },
|
||||
),
|
||||
},
|
||||
{
|
||||
"name" : "Sash_Combine", "type" : "image", "x" : 9, "y" : 35,
|
||||
"image" : uiScriptLocale.LOCALE_UISCRIPT_PATH + "acce/acce_combine.tga",
|
||||
"children" : (
|
||||
{
|
||||
"name" : "SashSlot", "type" : "slot", "x" : 3, "y" : 3, "width" : 200, "height" : 150,
|
||||
"slot" : (
|
||||
{"index":0, "x":78, "y":7, "width":32, "height":32},
|
||||
{"index":1, "x":78, "y":60, "width":32, "height":32},
|
||||
{"index":2, "x":78, "y":115, "width":32, "height":32},
|
||||
),
|
||||
},
|
||||
{ "name" : "Main", "type" : "text", "text" : L("主披风"), "text_horizontal_align" : "center", "x" : 85 + 12, "y" : 7 + 36 },
|
||||
{ "name" : "serve", "type" : "text", "text" : L("材料披风"), "text_horizontal_align" : "center", "x" : 85 + 12, "y" : 60 + 38 },
|
||||
{ "name" : "Result", "type" : "text", "text" : L("合成结果"), "text_horizontal_align" : "center", "x" : 85 + 12, "y" : 115 + 40 },
|
||||
),
|
||||
},
|
||||
{ "name" : "NeedMoney", "type" : "text", "text" : "", "text_horizontal_align" : "center", "x" : 105, "y" : 215 },
|
||||
{
|
||||
"name" : "AcceptButton", "type" : "button", "x" : 40, "y" : 235, "text" : uiScriptLocale.OK,
|
||||
"default_image" : "d:/ymir work/ui/public/middle_button_01.sub",
|
||||
"over_image" : "d:/ymir work/ui/public/middle_button_02.sub",
|
||||
"down_image" : "d:/ymir work/ui/public/middle_button_03.sub",
|
||||
},
|
||||
{
|
||||
"name" : "CancelButton", "type" : "button", "x" : 114, "y" : 235, "text" : uiScriptLocale.CANCEL,
|
||||
"default_image" : "d:/ymir work/ui/public/middle_button_01.sub",
|
||||
"over_image" : "d:/ymir work/ui/public/middle_button_02.sub",
|
||||
"down_image" : "d:/ymir work/ui/public/middle_button_03.sub",
|
||||
},
|
||||
),
|
||||
},
|
||||
),
|
||||
}
|
||||
'''
|
||||
|
||||
ABSORB_SCRIPT='''# -*- coding: utf-8 -*-
|
||||
import player
|
||||
import uiScriptLocale
|
||||
L=player.AutoHuntLocalize
|
||||
window = {
|
||||
"name" : "Sash_AbsorbtionWindow",
|
||||
"x" : (SCREEN_WIDTH - 205)/2, "y" : (SCREEN_HEIGHT - 270)/2,
|
||||
"style" : ("movable", "float",),
|
||||
"width" : 205, "height" : 270,
|
||||
"children" : (
|
||||
{
|
||||
"name" : "board", "type" : "board", "style" : ("attach",),
|
||||
"x" : 0, "y" : 0, "width" : 205, "height" : 270,
|
||||
"children" : (
|
||||
{
|
||||
"name" : "TitleBar", "type" : "titlebar", "style" : ("attach",),
|
||||
"x" : 6, "y" : 6, "width" : 190, "color" : "yellow",
|
||||
"children" : (
|
||||
{ "name" : "TitleName", "type" : "text", "x" : 95, "y" : 3, "text" : L("披风吸收"), "text_horizontal_align" : "center" },
|
||||
),
|
||||
},
|
||||
{
|
||||
"name" : "Sash_Combine", "type" : "image", "x" : 9, "y" : 35,
|
||||
"image" : uiScriptLocale.LOCALE_UISCRIPT_PATH + "acce/acce_absorb.tga",
|
||||
"children" : (
|
||||
{
|
||||
"name" : "SashSlot", "type" : "slot", "x" : 3, "y" : 3, "width" : 190, "height" : 200,
|
||||
"slot" : (
|
||||
{"index":0, "x":26, "y":41, "width":31, "height":31},
|
||||
{"index":1, "x":125, "y":8, "width":31, "height":96},
|
||||
{"index":2, "x":75, "y":126, "width":31, "height":31},
|
||||
),
|
||||
},
|
||||
),
|
||||
},
|
||||
{
|
||||
"name" : "AcceptButton", "type" : "button", "x" : 40, "y" : 235, "text" : uiScriptLocale.OK,
|
||||
"default_image" : "d:/ymir work/ui/public/middle_button_01.sub",
|
||||
"over_image" : "d:/ymir work/ui/public/middle_button_02.sub",
|
||||
"down_image" : "d:/ymir work/ui/public/middle_button_03.sub",
|
||||
},
|
||||
{
|
||||
"name" : "CancelButton", "type" : "button", "x" : 114, "y" : 235, "text" : uiScriptLocale.CANCEL,
|
||||
"default_image" : "d:/ymir work/ui/public/middle_button_01.sub",
|
||||
"over_image" : "d:/ymir work/ui/public/middle_button_02.sub",
|
||||
"down_image" : "d:/ymir work/ui/public/middle_button_03.sub",
|
||||
},
|
||||
),
|
||||
},
|
||||
),
|
||||
}
|
||||
'''
|
||||
|
||||
SCRIPTS={'sash_combinewindow.py':COMBINE_SCRIPT,'sash_absorbwindow.py':ABSORB_SCRIPT}
|
||||
# Costume window: the sash slot left of the hair slot (CostumeSlot coordinates).
|
||||
COSTUME_SASH_POS=(13,8)
|
||||
|
||||
g_windows=None
|
||||
g_effects=False
|
||||
|
||||
|
||||
def CalcSashValue(value,abs):
|
||||
# AVA2 uitooltip.CalcSashValue; the server scales applies the same way (port_sash.cpp).
|
||||
if not value:return 0
|
||||
valueCalc=round(value*abs)/100
|
||||
valueCalc+=0.5
|
||||
valueCalc=int(valueCalc)+1 if valueCalc>0 else int(valueCalc)
|
||||
return 1 if (valueCalc<=0 and value>0) else valueCalc
|
||||
|
||||
|
||||
def IsSash(itemVnum):
|
||||
item.SelectItem(itemVnum)
|
||||
return item.GetItemType()==item.ITEM_TYPE_COSTUME and item.GetItemSubType()==item.COSTUME_TYPE_SASH
|
||||
|
||||
|
||||
def IsSashWindowOpen():
|
||||
return bool(g_windows) and (g_windows[0].IsOpened() or g_windows[1].IsOpened())
|
||||
|
||||
|
||||
class CombineWindow(ui.ScriptWindow):
|
||||
def __init__(self):
|
||||
ui.ScriptWindow.__init__(self)
|
||||
self.isLoaded=0;self.PositionOut=0;self.PositionStartX=0;self.PositionStartY=0
|
||||
self.tooltipItem=None
|
||||
|
||||
def __del__(self):
|
||||
ui.ScriptWindow.__del__(self)
|
||||
|
||||
def Destroy(self):
|
||||
self.ClearDictionary()
|
||||
self.titleBar=None;self.btnAccept=None;self.btnCancel=None;self.sashSlot=None;self.needMoney=None;self.Result=None
|
||||
self.tooltipItem=None;self.PositionOut=0;self.PositionStartX=0;self.PositionStartY=0
|
||||
|
||||
def LoadWindow(self):
|
||||
if self.isLoaded:return
|
||||
self.isLoaded=1
|
||||
try:
|
||||
pyScrLoader=ui.PythonScriptLoader()
|
||||
pyScrLoader.LoadScriptFile(self,"uiscript/sash_combinewindow.py")
|
||||
except:
|
||||
import exception
|
||||
exception.Abort("Sash_CombineWindow.LoadDialog.LoadScript")
|
||||
try:
|
||||
self.board=self.GetChild("board")
|
||||
self.titleBar=self.GetChild("TitleBar")
|
||||
self.btnAccept=self.GetChild("AcceptButton")
|
||||
self.btnCancel=self.GetChild("CancelButton")
|
||||
self.needMoney=self.GetChild("NeedMoney")
|
||||
self.Result=self.GetChild("Result")
|
||||
self.sashSlot=self.GetChild("SashSlot")
|
||||
except:
|
||||
import exception
|
||||
exception.Abort("Sash_CombineWindow.LoadDialog.BindObject")
|
||||
self.sashSlot.SetSelectEmptySlotEvent(ui.__mem_func__(self.OnSelectEmptySlot))
|
||||
self.sashSlot.SetUnselectItemSlotEvent(ui.__mem_func__(self.OnSelectItemSlot))
|
||||
self.sashSlot.SetUseSlotEvent(ui.__mem_func__(self.OnSelectItemSlot))
|
||||
self.sashSlot.SetOverInItemEvent(ui.__mem_func__(self.OnOverInItem))
|
||||
self.sashSlot.SetOverOutItemEvent(ui.__mem_func__(self.OnOverOutItem))
|
||||
self.titleBar.SetCloseEvent(ui.__mem_func__(self.OnClose))
|
||||
self.btnCancel.SetEvent(ui.__mem_func__(self.OnClose))
|
||||
self.btnAccept.SetEvent(ui.__mem_func__(self.OnAccept))
|
||||
|
||||
def SetItemToolTip(self,itemTooltip):
|
||||
self.tooltipItem=itemTooltip
|
||||
|
||||
def IsOpened(self):
|
||||
return bool(self.IsShow() and self.isLoaded)
|
||||
|
||||
def SetPrice(self):
|
||||
self.needMoney.SetText(L('合成费用:')+localeInfo.NumberToMoneyString(sash.GetPrice()))
|
||||
|
||||
def Open(self):
|
||||
self.PositionOut=0
|
||||
(self.PositionStartX,self.PositionStartY,z)=player.GetMainCharacterPosition()
|
||||
self.SetPrice()
|
||||
for i in xrange(sash.WINDOW_MAX_MATERIALS+1):
|
||||
self.sashSlot.ClearSlot(i)
|
||||
self.Show()
|
||||
|
||||
def Close(self):
|
||||
if self.tooltipItem:self.tooltipItem.HideToolTip()
|
||||
self.Hide()
|
||||
|
||||
def OnClose(self):
|
||||
sash.SendCloseRequest()
|
||||
|
||||
def OnPressEscapeKey(self):
|
||||
self.OnClose()
|
||||
return True
|
||||
|
||||
def OnAccept(self):
|
||||
sash.SendRefineRequest()
|
||||
|
||||
def OnUpdate(self):
|
||||
LIMIT_RANGE=sash.LIMIT_RANGE
|
||||
(x,y,z)=player.GetMainCharacterPosition()
|
||||
if abs(x-self.PositionStartX)>=LIMIT_RANGE or abs(y-self.PositionStartY)>=LIMIT_RANGE:
|
||||
if not self.PositionOut:
|
||||
self.PositionOut+=1
|
||||
self.OnClose()
|
||||
|
||||
def OnSelectEmptySlot(self,selectedSlotPos):
|
||||
isAttached=mouseModule.mouseController.isAttached()
|
||||
if not isAttached or selectedSlotPos==sash.WINDOW_MAX_MATERIALS:return
|
||||
attachedSlotType=mouseModule.mouseController.GetAttachedType()
|
||||
attachedSlotPos=mouseModule.mouseController.GetAttachedSlotNumber()
|
||||
attachedInvenType=player.SlotTypeToInvenType(attachedSlotType)
|
||||
mouseModule.mouseController.DeattachObject()
|
||||
if attachedSlotType==player.SLOT_TYPE_INVENTORY and attachedInvenType==player.INVENTORY:
|
||||
sash.Add(attachedInvenType,attachedSlotPos,selectedSlotPos)
|
||||
|
||||
def OnSelectItemSlot(self,selectedSlotPos):
|
||||
if selectedSlotPos==sash.WINDOW_MAX_MATERIALS:return
|
||||
mouseModule.mouseController.DeattachObject()
|
||||
sash.Remove(selectedSlotPos)
|
||||
|
||||
def OnOverInItem(self,selectedSlotPos):
|
||||
if self.tooltipItem:
|
||||
if selectedSlotPos==sash.WINDOW_MAX_MATERIALS:
|
||||
(isHere,iCell)=sash.GetAttachedItem(0)
|
||||
if isHere:self.tooltipItem.SetSashResultItem(iCell)
|
||||
else:
|
||||
(isHere,iCell)=sash.GetAttachedItem(selectedSlotPos)
|
||||
if isHere:self.tooltipItem.SetInventoryItem(iCell)
|
||||
|
||||
def OnOverOutItem(self):
|
||||
if self.tooltipItem:self.tooltipItem.HideToolTip()
|
||||
|
||||
def Refresh(self,iAct):
|
||||
self.SetPrice()
|
||||
self.sashSlot.ClearSlot(sash.WINDOW_MAX_MATERIALS)
|
||||
for i in xrange(sash.WINDOW_MAX_MATERIALS):
|
||||
self.sashSlot.ClearSlot(i)
|
||||
(isHere,iCell)=sash.GetAttachedItem(i)
|
||||
if isHere:
|
||||
self.sashSlot.SetItemSlot(i,player.GetItemIndex(iCell),0)
|
||||
if i==int(sash.WINDOW_MAX_MATERIALS-1):
|
||||
(itemVnum,MinAbs,MaxAbs)=sash.GetResultItem()
|
||||
if not itemVnum:break
|
||||
self.sashSlot.SetItemSlot(i+1,itemVnum,0)
|
||||
break
|
||||
self.sashSlot.RefreshSlot()
|
||||
|
||||
|
||||
class AbsorbWindow(CombineWindow):
|
||||
# AVA2 AbsorbWindow: CombineWindow without the price line, a two-item result tooltip.
|
||||
def LoadWindow(self):
|
||||
if self.isLoaded:return
|
||||
self.isLoaded=1
|
||||
try:
|
||||
pyScrLoader=ui.PythonScriptLoader()
|
||||
pyScrLoader.LoadScriptFile(self,"uiscript/sash_absorbwindow.py")
|
||||
except:
|
||||
import exception
|
||||
exception.Abort("Sash_AbsorbtionWindow.LoadDialog.LoadScript")
|
||||
try:
|
||||
self.titleBar=self.GetChild("TitleBar")
|
||||
self.btnAccept=self.GetChild("AcceptButton")
|
||||
self.btnCancel=self.GetChild("CancelButton")
|
||||
self.sashSlot=self.GetChild("SashSlot")
|
||||
except:
|
||||
import exception
|
||||
exception.Abort("Sash_AbsorbtionWindow.LoadDialog.BindObject")
|
||||
self.sashSlot.SetSelectEmptySlotEvent(ui.__mem_func__(self.OnSelectEmptySlot))
|
||||
self.sashSlot.SetUnselectItemSlotEvent(ui.__mem_func__(self.OnSelectItemSlot))
|
||||
self.sashSlot.SetUseSlotEvent(ui.__mem_func__(self.OnSelectItemSlot))
|
||||
self.sashSlot.SetOverInItemEvent(ui.__mem_func__(self.OnOverInItem))
|
||||
self.sashSlot.SetOverOutItemEvent(ui.__mem_func__(self.OnOverOutItem))
|
||||
self.titleBar.SetCloseEvent(ui.__mem_func__(self.OnClose))
|
||||
self.btnCancel.SetEvent(ui.__mem_func__(self.OnClose))
|
||||
self.btnAccept.SetEvent(ui.__mem_func__(self.OnAccept))
|
||||
|
||||
def SetPrice(self):
|
||||
pass
|
||||
|
||||
def OnOverInItem(self,selectedSlotPos):
|
||||
if self.tooltipItem:
|
||||
if selectedSlotPos==sash.WINDOW_MAX_MATERIALS:
|
||||
(isHere1,iCell1)=sash.GetAttachedItem(0)
|
||||
(isHere2,iCell2)=sash.GetAttachedItem(1)
|
||||
if isHere1 and isHere2:self.tooltipItem.SetSashResultAbsItem(iCell1,iCell2)
|
||||
else:
|
||||
(isHere,iCell)=sash.GetAttachedItem(selectedSlotPos)
|
||||
if isHere:self.tooltipItem.SetInventoryItem(iCell)
|
||||
|
||||
|
||||
# --- tooltip (AVA2 uitooltip.py ENABLE_SASH_SYSTEM parts) -------------------------------------------------
|
||||
Tip=uiToolTip.ItemToolTip
|
||||
_addItemData=Tip.AddItemData
|
||||
_appendAffect=Tip._ItemToolTip__AppendAffectInformation
|
||||
_appendAttribute=Tip._ItemToolTip__AppendAttributeInformation
|
||||
|
||||
|
||||
def AppendAbsorbedItem(self,itemAbsorbedVnum,absChance):
|
||||
# Attack/defence and applies of the absorbed item, scaled by the absorption rate.
|
||||
item.SelectItem(itemAbsorbedVnum)
|
||||
itemType=item.GetItemType()
|
||||
if itemType==item.ITEM_TYPE_WEAPON:
|
||||
attack=[item.GetValue(3),item.GetValue(4),item.GetValue(5)]
|
||||
magic=[item.GetValue(1),item.GetValue(2),item.GetValue(5)]
|
||||
lines=[]
|
||||
minPower,maxPower,addPower=[CalcSashValue(v,absChance) for v in attack]
|
||||
if maxPower>minPower:lines.append(localeInfo.TOOLTIP_ITEM_ATT_POWER%(minPower+addPower,maxPower+addPower))
|
||||
else:lines.append(localeInfo.TOOLTIP_ITEM_ATT_POWER_ONE_ARG%(minPower+addPower))
|
||||
minMagic,maxMagic,addPower=[CalcSashValue(v,absChance) for v in magic]
|
||||
if magic[0]>0 or magic[1]>0:
|
||||
if maxMagic>minMagic:magicLine=localeInfo.TOOLTIP_ITEM_MAGIC_ATT_POWER%(minMagic+addPower,maxMagic+addPower)
|
||||
else:magicLine=localeInfo.TOOLTIP_ITEM_MAGIC_ATT_POWER_ONE_ARG%(minMagic+addPower)
|
||||
if item.GetItemSubType()==item.WEAPON_FAN:lines.insert(0,magicLine)
|
||||
else:lines.append(magicLine)
|
||||
for line in lines:self.AppendTextLine(line,self.POSITIVE_COLOR)
|
||||
elif itemType==item.ITEM_TYPE_ARMOR:
|
||||
defGrade=CalcSashValue(item.GetValue(1),absChance)
|
||||
defBonus=CalcSashValue(item.GetValue(5)*2,absChance)
|
||||
if defGrade>0:
|
||||
self.AppendSpace(5)
|
||||
self.AppendTextLine(localeInfo.TOOLTIP_ITEM_DEF_GRADE%(defGrade+defBonus),self.GetChangeTextLineColor(defGrade))
|
||||
magicDefencePower=CalcSashValue(item.GetValue(0),absChance)
|
||||
if magicDefencePower>0:
|
||||
self.AppendTextLine(localeInfo.TOOLTIP_ITEM_MAGIC_DEF_POWER%magicDefencePower,self.GetChangeTextLineColor(magicDefencePower))
|
||||
for i in xrange(item.ITEM_APPLY_MAX_NUM):
|
||||
item.SelectItem(itemAbsorbedVnum)
|
||||
(affectType,affectValue)=item.GetAffect(i)
|
||||
affectValue=CalcSashValue(affectValue,absChance)
|
||||
affectString=self._ItemToolTip__GetAffectString(affectType,affectValue)
|
||||
if affectString and affectValue>0:
|
||||
self.AppendTextLine(affectString,self.GetChangeTextLineColor(affectValue))
|
||||
|
||||
|
||||
def AppendScaledAttributes(self,attrSlot,absChance):
|
||||
if not attrSlot:return
|
||||
for i in xrange(player.ATTRIBUTE_SLOT_MAX_NUM):
|
||||
type,value=attrSlot[i][0],attrSlot[i][1]
|
||||
if not value:continue
|
||||
value=CalcSashValue(value,absChance)
|
||||
affectString=self._ItemToolTip__GetAffectString(type,value)
|
||||
if affectString and value>0:
|
||||
self.AppendTextLine(affectString,self._ItemToolTip__GetAttributeColor(i,value))
|
||||
|
||||
|
||||
def AddItemData(self,itemVnum,metinSlot,attrSlot=0,flags=0,unbindTime=0):
|
||||
self.mtSash=None
|
||||
if metinSlot and IsSash(itemVnum):
|
||||
self.mtSash=(itemVnum,int(metinSlot[sash.ABSORPTION_SOCKET]),int(metinSlot[sash.ABSORBED_SOCKET]))
|
||||
try:
|
||||
return _addItemData(self,itemVnum,metinSlot,attrSlot,flags,unbindTime)
|
||||
finally:
|
||||
self.mtSash=None
|
||||
|
||||
|
||||
def AppendAffectInformation(self):
|
||||
_appendAffect(self)
|
||||
state=getattr(self,'mtSash',None)
|
||||
if not state:return
|
||||
itemVnum,absChance,itemAbsorbedVnum=state
|
||||
self.AppendTextLine(L(ABSORB_CHANCE%absChance),self.CONDITION_COLOR)
|
||||
if itemAbsorbedVnum:AppendAbsorbedItem(self,itemAbsorbedVnum,absChance)
|
||||
item.SelectItem(itemVnum)
|
||||
|
||||
|
||||
def AppendAttributeInformation(self,attrSlot):
|
||||
state=getattr(self,'mtSash',None)
|
||||
if not state:return _appendAttribute(self,attrSlot)
|
||||
AppendScaledAttributes(self,attrSlot,state[1])
|
||||
item.SelectItem(state[0])
|
||||
|
||||
|
||||
def SetSashResultItem(self,slotIndex,window_type=player.INVENTORY):
|
||||
(itemVnum,MinAbs,MaxAbs)=sash.GetResultItem()
|
||||
if not itemVnum:return
|
||||
self.ClearToolTip()
|
||||
metinSlot=[player.GetItemMetinSocket(window_type,slotIndex,i) for i in xrange(player.METIN_SOCKET_MAX_NUM)]
|
||||
attrSlot=[player.GetItemAttribute(window_type,slotIndex,i) for i in xrange(player.ATTRIBUTE_SLOT_MAX_NUM)]
|
||||
if not IsSash(itemVnum):return
|
||||
itemDesc=item.GetItemDescription()
|
||||
self._ItemToolTip__AdjustMaxWidth(attrSlot,itemDesc)
|
||||
self._ItemToolTip__SetItemTitle(itemVnum,metinSlot,attrSlot)
|
||||
self.AppendDescription(itemDesc,26)
|
||||
self.AppendDescription(item.GetItemSummary(),26,self.CONDITION_COLOR)
|
||||
self._ItemToolTip__AppendLimitInformation()
|
||||
if MinAbs==MaxAbs:self.AppendTextLine(L(ABSORB_CHANCE%MinAbs),self.CONDITION_COLOR)
|
||||
else:self.AppendTextLine(L(ABSORB_CHANCE2%(MinAbs,MaxAbs)),self.CONDITION_COLOR)
|
||||
itemAbsorbedVnum=int(metinSlot[sash.ABSORBED_SOCKET])
|
||||
if itemAbsorbedVnum:AppendAbsorbedItem(self,itemAbsorbedVnum,MaxAbs)
|
||||
AppendScaledAttributes(self,attrSlot,MaxAbs)
|
||||
item.SelectItem(itemVnum)
|
||||
self.AppendWearableInformation()
|
||||
self.ShowToolTip()
|
||||
|
||||
|
||||
def SetSashResultAbsItem(self,slotIndex1,slotIndex2,window_type=player.INVENTORY):
|
||||
itemVnumSash=player.GetItemIndex(window_type,slotIndex1)
|
||||
itemVnumTarget=player.GetItemIndex(window_type,slotIndex2)
|
||||
if not itemVnumSash or not itemVnumTarget:return
|
||||
self.ClearToolTip()
|
||||
if not IsSash(itemVnumSash):return
|
||||
metinSlot=[player.GetItemMetinSocket(window_type,slotIndex1,i) for i in xrange(player.METIN_SOCKET_MAX_NUM)]
|
||||
attrSlot=[player.GetItemAttribute(window_type,slotIndex2,i) for i in xrange(player.ATTRIBUTE_SLOT_MAX_NUM)]
|
||||
absChance=int(metinSlot[sash.ABSORPTION_SOCKET])
|
||||
itemDesc=item.GetItemDescription()
|
||||
self._ItemToolTip__AdjustMaxWidth(attrSlot,itemDesc)
|
||||
self._ItemToolTip__SetItemTitle(itemVnumSash,metinSlot,attrSlot)
|
||||
self.AppendDescription(itemDesc,26)
|
||||
self.AppendDescription(item.GetItemSummary(),26,self.CONDITION_COLOR)
|
||||
item.SelectItem(itemVnumSash)
|
||||
self._ItemToolTip__AppendLimitInformation()
|
||||
self.AppendTextLine(L(ABSORB_CHANCE%absChance),self.CONDITION_COLOR)
|
||||
AppendAbsorbedItem(self,itemVnumTarget,absChance)
|
||||
AppendScaledAttributes(self,attrSlot,absChance)
|
||||
item.SelectItem(itemVnumSash)
|
||||
self.AppendWearableInformation()
|
||||
self.ShowToolTip()
|
||||
|
||||
|
||||
Tip.AddItemData=AddItemData
|
||||
Tip._ItemToolTip__AppendAffectInformation=AppendAffectInformation
|
||||
Tip._ItemToolTip__AppendAttributeInformation=AppendAttributeInformation
|
||||
Tip.SetSashResultItem=SetSashResultItem
|
||||
Tip.SetSashResultAbsItem=SetSashResultAbsItem
|
||||
|
||||
|
||||
# --- uiscripts: the two sash windows and the costume window's sash slot ------------------------------------
|
||||
def FindChild(node,name):
|
||||
if node.get("name")==name:return node
|
||||
for child in node.get("children",()):
|
||||
found=FindChild(child,name)
|
||||
if found:return found
|
||||
return None
|
||||
|
||||
|
||||
def PatchCostumeWindow(window):
|
||||
base=FindChild(window,"Costume_Base");slot=FindChild(window,"CostumeSlot")
|
||||
if not base or not slot:return
|
||||
x,y=COSTUME_SASH_POS
|
||||
frame={"name":"SashSlotBase","type":"image","x":slot["x"]+x,"y":slot["y"]+y,"image":"d:/ymir work/ui/public/Slot_Base.sub"}
|
||||
base["children"]=(frame,)+tuple(base.get("children",()))
|
||||
slot["slot"]=tuple(slot["slot"])+({"index":item.COSTUME_SLOT_SASH,"x":x,"y":y,"width":32,"height":32},)
|
||||
|
||||
|
||||
_execfile=ui.execfile
|
||||
|
||||
def ExecFile(fileName,dict):
|
||||
name=fileName.replace("\\","/").split("/")[-1].lower()
|
||||
if name in SCRIPTS:
|
||||
exec(SCRIPTS[name],dict)
|
||||
return
|
||||
_execfile(fileName,dict)
|
||||
if name=="costumewindow.py" and "window" in dict:PatchCostumeWindow(dict["window"])
|
||||
|
||||
ui.execfile=ExecFile
|
||||
|
||||
_refreshCostume=uiInventory.CostumeWindow.RefreshCostumeSlot
|
||||
|
||||
def RefreshCostumeSlot(self):
|
||||
_refreshCostume(self)
|
||||
self.wndEquip.SetItemSlot(item.COSTUME_SLOT_SASH,player.GetItemIndex(item.COSTUME_SLOT_SASH),0)
|
||||
self.wndEquip.RefreshSlot()
|
||||
|
||||
uiInventory.CostumeWindow.RefreshCostumeSlot=RefreshCostumeSlot
|
||||
|
||||
|
||||
# --- inventory: right click adds to the open sash window, attached items are lit ----------------------------
|
||||
_useItemSlot=uiInventory.InventoryWindow.UseItemSlot
|
||||
_refreshBag=uiInventory.InventoryWindow.RefreshBagSlotWindow
|
||||
|
||||
def UseItemSlot(self,slotIndex):
|
||||
if IsSashWindowOpen() and app.GetCursor()!=app.SELL:
|
||||
cell=self._InventoryWindow__InventoryLocalSlotPosToGlobalSlotPos(slotIndex)
|
||||
if not player.IsEquipmentSlot(cell):
|
||||
sash.Add(player.INVENTORY,cell,255)
|
||||
return
|
||||
return _useItemSlot(self,slotIndex)
|
||||
|
||||
|
||||
def AutoPotionActive(cell):
|
||||
return constInfo.IS_AUTO_POTION(player.GetItemIndex(cell)) and player.GetItemMetinSocket(cell,0)!=0
|
||||
|
||||
|
||||
def RefreshBagSlotWindow(self):
|
||||
_refreshBag(self)
|
||||
attached=set()
|
||||
if IsSashWindowOpen():
|
||||
for j in xrange(sash.WINDOW_MAX_MATERIALS):
|
||||
(isHere,iCell)=sash.GetAttachedItem(j)
|
||||
if isHere:attached.add(iCell)
|
||||
lit=getattr(self,'mtSashLit',set())
|
||||
now=set()
|
||||
for i in xrange(player.INVENTORY_PAGE_SIZE):
|
||||
cell=self._InventoryWindow__InventoryLocalSlotPosToGlobalSlotPos(i)
|
||||
if cell in attached:
|
||||
self.wndItem.ActivateSlot(i);now.add(i)
|
||||
elif i in lit and not AutoPotionActive(cell):
|
||||
self.wndItem.DeactivateSlot(i)
|
||||
self.mtSashLit=now
|
||||
self.wndItem.RefreshSlot()
|
||||
|
||||
uiInventory.InventoryWindow.UseItemSlot=UseItemSlot
|
||||
uiInventory.InventoryWindow.RefreshBagSlotWindow=RefreshBagSlotWindow
|
||||
|
||||
|
||||
# --- game window: ActSash/AlertSash (called by the GC_SASH packet), MTSASH messages, effects -----------------
|
||||
def RegisterEffects():
|
||||
global g_effects
|
||||
if g_effects:return
|
||||
g_effects=True
|
||||
chrmgr.RegisterEffect(chrmgr.EFFECT_SASH_SUCCEDED,"","d:/ymir work/effect/etc/buff/buff_item6.mse")
|
||||
chrmgr.RegisterEffect(chrmgr.EFFECT_SASH_EQUIP,"","d:/ymir work/effect/etc/buff/buff_item7.mse")
|
||||
chrmgr.RegisterEffect(chrmgr.EFFECT_REFINED+chrmgr.EFFECT_SASH,"Bip01","d:/ymir work/pc/common/effect/armor/acc_01.mse")
|
||||
|
||||
|
||||
_open=game.GameWindow.Open
|
||||
_close=game.GameWindow.Close
|
||||
_command=game.GameWindow.BINARY_ServerCommand_Run
|
||||
|
||||
def Open(self):
|
||||
global g_windows
|
||||
_open(self)
|
||||
RegisterEffects()
|
||||
g_windows=(CombineWindow(),AbsorbWindow())
|
||||
for window in g_windows:
|
||||
window.LoadWindow();window.Hide()
|
||||
if self.interface:window.SetItemToolTip(self.interface.tooltipItem)
|
||||
|
||||
def Close(self):
|
||||
global g_windows
|
||||
if g_windows:
|
||||
for window in g_windows:window.Close();window.Destroy()
|
||||
g_windows=None
|
||||
_close(self)
|
||||
|
||||
def ActSash(self,iAct,bWindow):
|
||||
if not g_windows or not self.interface:return
|
||||
window=g_windows[0 if bWindow else 1]
|
||||
inventory=self.interface.wndInventory
|
||||
if iAct==1:
|
||||
if not window.IsOpened():window.Open()
|
||||
if inventory and not inventory.IsShow():inventory.Show()
|
||||
elif iAct==2:
|
||||
if window.IsOpened():window.Close()
|
||||
elif iAct==3 or iAct==4:
|
||||
if window.IsOpened():window.Refresh(iAct)
|
||||
if inventory:inventory.RefreshBagSlotWindow()
|
||||
|
||||
def AlertSash(self,bWindow):
|
||||
self.PopupMessage(L(ALERT_COMBINE if bWindow else ALERT_ABSORB))
|
||||
|
||||
def Command(self,line):
|
||||
if line.startswith('MTSASH '):
|
||||
words=line.split()
|
||||
if len(words)==3 and words[1]=='abs' and words[2].isdigit():text=ABS_CHANGED%int(words[2])
|
||||
else:text=MESSAGES.get(words[1] if len(words)>1 else '')
|
||||
if text:chat.AppendChat(chat.CHAT_TYPE_INFO,L(text))
|
||||
return 1
|
||||
return _command(self,line)
|
||||
|
||||
game.GameWindow.Open=Open
|
||||
game.GameWindow.Close=Close
|
||||
game.GameWindow.ActSash=ActSash
|
||||
game.GameWindow.AlertSash=AlertSash
|
||||
game.GameWindow.BINARY_ServerCommand_Run=Command
|
||||
)PY";
|
||||
@@ -760,6 +760,12 @@ bool LoadLocaleData(const char* localePath)
|
||||
Tracenf("LoadLocaleData - LoadItemDesc(%s) Error", szItemDesc);
|
||||
}
|
||||
|
||||
// PORT: AVA2 sash part scale (ENABLE_SASH_SYSTEM)
|
||||
char szItemScale[256];
|
||||
snprintf(szItemScale, sizeof(szItemScale), "%s/item_scale.txt", localePath);
|
||||
if (!rkItemMgr.LoadItemScale(szItemScale))
|
||||
Tracenf("LoadLocaleData: error while loading %s.", szItemScale);
|
||||
|
||||
if (!rkNPCMgr.LoadNonPlayerData(szMobProto))
|
||||
{
|
||||
TraceError("LoadLocaleData - LoadMobProto(%s) Error", szMobProto);
|
||||
|
||||
@@ -685,21 +685,50 @@ void CGraphicThingInstance::DeformAll()
|
||||
{
|
||||
m_bUpdated = true;
|
||||
|
||||
CGrannyLODController::FDeformAll deform;
|
||||
deform.mc_pWorldMatrix = &m_worldMatrix;
|
||||
std::for_each(m_LODControllerVector.begin(), m_LODControllerVector.end(), deform);
|
||||
D3DXMATRIX matScaled;
|
||||
for (int i = 0; i < (int)m_LODControllerVector.size(); ++i)
|
||||
{
|
||||
CGrannyLODController* pkLOD = m_LODControllerVector[i];
|
||||
if (pkLOD->isModelInstance())
|
||||
pkLOD->DeformAll(__GetPartWorldMatrix(i, matScaled));
|
||||
}
|
||||
}
|
||||
|
||||
void CGraphicThingInstance::SetPartScale(int iPart, float fX, float fY, float fZ)
|
||||
{
|
||||
D3DXMATRIX matScale;
|
||||
D3DXMatrixScaling(&matScale, fX, fY, fZ);
|
||||
m_partScaleMap[iPart] = matScale;
|
||||
}
|
||||
|
||||
void CGraphicThingInstance::ClearPartScale(int iPart)
|
||||
{
|
||||
m_partScaleMap.erase(iPart);
|
||||
}
|
||||
|
||||
const D3DXMATRIX * CGraphicThingInstance::__GetPartWorldMatrix(int iPart, D3DXMATRIX & rScaled) const
|
||||
{
|
||||
if (m_partScaleMap.empty())
|
||||
return &m_worldMatrix;
|
||||
|
||||
std::map<int, D3DXMATRIX>::const_iterator it = m_partScaleMap.find(iPart);
|
||||
if (it == m_partScaleMap.end())
|
||||
return &m_worldMatrix;
|
||||
|
||||
D3DXMatrixMultiply(&rScaled, &it->second, &m_worldMatrix);
|
||||
return &rScaled;
|
||||
}
|
||||
|
||||
void CGraphicThingInstance::DeformNoSkin()
|
||||
{
|
||||
m_bUpdated = true;
|
||||
|
||||
std::vector<CGrannyLODController*>::iterator i;
|
||||
for (i=m_LODControllerVector.begin(); i!=m_LODControllerVector.end(); ++i)
|
||||
D3DXMATRIX matScaled;
|
||||
for (int i = 0; i < (int)m_LODControllerVector.size(); ++i)
|
||||
{
|
||||
CGrannyLODController* pkLOD=*i;
|
||||
CGrannyLODController* pkLOD=m_LODControllerVector[i];
|
||||
if (pkLOD->isModelInstance())
|
||||
pkLOD->DeformNoSkin(&m_worldMatrix);
|
||||
pkLOD->DeformNoSkin(__GetPartWorldMatrix(i, matScaled));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -707,9 +736,13 @@ void CGraphicThingInstance::OnDeform()
|
||||
{
|
||||
m_bUpdated = true;
|
||||
|
||||
CGrannyLODController::FDeform deform;
|
||||
deform.mc_pWorldMatrix = &m_worldMatrix;
|
||||
std::for_each(m_LODControllerVector.begin(), m_LODControllerVector.end(), deform);
|
||||
D3DXMATRIX matScaled;
|
||||
for (int i = 0; i < (int)m_LODControllerVector.size(); ++i)
|
||||
{
|
||||
CGrannyLODController* pkLOD = m_LODControllerVector[i];
|
||||
if (pkLOD->isModelInstance())
|
||||
pkLOD->Deform(__GetPartWorldMatrix(i, matScaled));
|
||||
}
|
||||
}
|
||||
|
||||
void CGraphicThingInstance::__SetLocalTime(float fLocalTime)
|
||||
@@ -938,6 +971,7 @@ void CGraphicThingInstance::OnClear()
|
||||
{
|
||||
stl_wipe(m_LODControllerVector);
|
||||
stl_wipe_second(m_roMotionThingMap);
|
||||
m_partScaleMap.clear();
|
||||
|
||||
for (DWORD d = 0; d < m_modelThingSetVector.size(); ++d)
|
||||
m_modelThingSetVector[d].Clear();
|
||||
|
||||
@@ -34,6 +34,11 @@ class CGraphicThingInstance : public CGraphicObjectInstance
|
||||
|
||||
void UpdateLODLevel();
|
||||
void UpdateTime();
|
||||
// PORT: AVA2 ENABLE_OBJ_SCALLING for the sash part: the part is deformed with Scale * world,
|
||||
// i.e. scaled about the actor origin in actor space (item_scale.txt).
|
||||
void SetPartScale(int iPart, float fX, float fY, float fZ);
|
||||
void ClearPartScale(int iPart);
|
||||
|
||||
void DeformAll(); // 모든 LOD 디폼
|
||||
|
||||
bool LessRenderOrder(CGraphicThingInstance* pkThingInst);
|
||||
@@ -139,6 +144,9 @@ class CGraphicThingInstance : public CGraphicObjectInstance
|
||||
std::vector<CGrannyLODController *> m_LODControllerVector;
|
||||
std::vector<TModelThingSet> m_modelThingSetVector;
|
||||
std::map<DWORD, CGraphicThing::TRef *> m_roMotionThingMap;
|
||||
std::map<int, D3DXMATRIX> m_partScaleMap; // PORT: AVA2 sash scale
|
||||
|
||||
const D3DXMATRIX * __GetPartWorldMatrix(int iPart, D3DXMATRIX & rScaled) const;
|
||||
|
||||
protected:
|
||||
virtual void OnUpdateCollisionData(const CStaticCollisionDataVector * pscdVector);
|
||||
|
||||
@@ -243,6 +243,7 @@ class CActorInstance : public IActorInstance, public IFlyTargetableObject
|
||||
|
||||
bool SetRace(DWORD eRace);
|
||||
void SetHair(DWORD eHair);
|
||||
void AttachSash(CItemData * pItemData, float fSpecular = 0.0f); // PORT: AVA2 sash
|
||||
void SetVirtualID(DWORD dwVID);
|
||||
|
||||
void SetShape(DWORD eShape, float fSpecular=0.0f);
|
||||
|
||||
@@ -624,3 +624,44 @@ void CActorInstance::__ClearAttachingEffect()
|
||||
}
|
||||
m_AttachingEffectList.clear();
|
||||
}
|
||||
|
||||
// PORT: AVA2 ActorInstanceAttach.cpp AttachSash (ENABLE_SASH_SYSTEM). The sash is a model part on
|
||||
// "Bip01 Spine2" that plays its own first motion (the cloth sway).
|
||||
void CActorInstance::AttachSash(CItemData * pItemData, float fSpecular)
|
||||
{
|
||||
if (!pItemData)
|
||||
{
|
||||
RegisterModelThing(CRaceData::PART_SASH, NULL);
|
||||
SetModelInstance(CRaceData::PART_SASH, CRaceData::PART_SASH, 0);
|
||||
RefreshActorInstance();
|
||||
return;
|
||||
}
|
||||
|
||||
RegisterModelThing(CRaceData::PART_SASH, pItemData->GetModelThing());
|
||||
SetModelInstance(CRaceData::PART_SASH, CRaceData::PART_SASH, 0);
|
||||
AttachModelInstance(CRaceData::PART_MAIN, "Bip01 Spine2", CRaceData::PART_SASH);
|
||||
|
||||
if (fSpecular > 0.0f)
|
||||
{
|
||||
SMaterialData kMaterialData;
|
||||
kMaterialData.pImage = NULL;
|
||||
kMaterialData.isSpecularEnable = TRUE;
|
||||
kMaterialData.fSpecularPower = fSpecular;
|
||||
kMaterialData.bSphereMapIndex = 1;
|
||||
SetMaterialData(CRaceData::PART_SASH, NULL, kMaterialData);
|
||||
}
|
||||
|
||||
if (CRaceData::PART_SASH >= m_LODControllerVector.size())
|
||||
return;
|
||||
|
||||
if (CGrannyLODController* pLODController = m_LODControllerVector[CRaceData::PART_SASH])
|
||||
{
|
||||
if (CGrannyModelInstance* pSashModelInstance = pLODController->GetModelInstance())
|
||||
{
|
||||
CGraphicThing* pItemGraphicThing = pItemData->GetModelThing();
|
||||
if (pItemGraphicThing && pItemGraphicThing->GetMotionCount() > 0)
|
||||
if (CGrannyMotion* pItemMotion = pItemGraphicThing->GetMotionPointer(0))
|
||||
pSashModelInstance->SetMotionPointer(pItemMotion);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -215,6 +215,43 @@ void CItemData::SetItemTableData(TItemTable * pItemTable)
|
||||
memcpy(&m_ItemTable, pItemTable, sizeof(TItemTable));
|
||||
}
|
||||
|
||||
// PORT: AVA2 ItemData.cpp SetItemScale/GetItemScale. The position columns are dropped: AVA2 feeds
|
||||
// them to SetScalePosition, which its deform never reads. JOB_WOLFMAN rows are skipped (no wolfman).
|
||||
void CItemData::SetItemScale(const std::string & strJob, const std::string & strSex, const std::string & strScaleX, const std::string & strScaleY, const std::string & strScaleZ)
|
||||
{
|
||||
DWORD dwPos;
|
||||
if (strJob == "JOB_WARRIOR")
|
||||
dwPos = NRaceData::JOB_WARRIOR;
|
||||
else if (strJob == "JOB_ASSASSIN")
|
||||
dwPos = NRaceData::JOB_ASSASSIN;
|
||||
else if (strJob == "JOB_SURA")
|
||||
dwPos = NRaceData::JOB_SURA;
|
||||
else if (strJob == "JOB_SHAMAN")
|
||||
dwPos = NRaceData::JOB_SHAMAN;
|
||||
else
|
||||
return;
|
||||
|
||||
dwPos += 1;
|
||||
if (strSex == "F")
|
||||
dwPos += 5;
|
||||
|
||||
m_aScaleInfo[dwPos].bSet = true;
|
||||
m_aScaleInfo[dwPos].fScaleX = float(atof(strScaleX.c_str()) / 100.0f);
|
||||
m_aScaleInfo[dwPos].fScaleY = float(atof(strScaleY.c_str()) / 100.0f);
|
||||
m_aScaleInfo[dwPos].fScaleZ = float(atof(strScaleZ.c_str()) / 100.0f);
|
||||
}
|
||||
|
||||
bool CItemData::GetItemScale(DWORD dwPos, float & fScaleX, float & fScaleY, float & fScaleZ)
|
||||
{
|
||||
if (dwPos >= sizeof(m_aScaleInfo) / sizeof(m_aScaleInfo[0]) || !m_aScaleInfo[dwPos].bSet)
|
||||
return false;
|
||||
|
||||
fScaleX = m_aScaleInfo[dwPos].fScaleX;
|
||||
fScaleY = m_aScaleInfo[dwPos].fScaleY;
|
||||
fScaleZ = m_aScaleInfo[dwPos].fScaleZ;
|
||||
return true;
|
||||
}
|
||||
|
||||
const CItemData::TItemTable* CItemData::GetTable() const
|
||||
{
|
||||
return &m_ItemTable;
|
||||
@@ -452,6 +489,7 @@ void CItemData::Clear()
|
||||
m_pLODModelThingVector.clear();
|
||||
|
||||
memset(&m_ItemTable, 0, sizeof(m_ItemTable));
|
||||
memset(m_aScaleInfo, 0, sizeof(m_aScaleInfo));
|
||||
}
|
||||
|
||||
CItemData::CItemData()
|
||||
|
||||
@@ -102,6 +102,7 @@ class CItemData
|
||||
{
|
||||
COSTUME_BODY, //0 갑옷(main look)
|
||||
COSTUME_HAIR, //1 헤어(탈착가능)
|
||||
COSTUME_SASH = 3, // PORT: AVA2 sash (2 is AVA2 COSTUME_MOUNT)
|
||||
COSTUME_NUM_TYPES,
|
||||
};
|
||||
|
||||
@@ -495,6 +496,9 @@ class CItemData
|
||||
//BOOL LoadItemData(const char * c_szFileName);
|
||||
void SetDefaultItemData(const char * c_szIconFileName, const char * c_szModelFileName = NULL);
|
||||
void SetItemTableData(TItemTable * pItemTable);
|
||||
// PORT: AVA2 item_scale.txt (sash part scale per job/sex). Slot = job + 1, + 5 when female.
|
||||
void SetItemScale(const std::string & strJob, const std::string & strSex, const std::string & strScaleX, const std::string & strScaleY, const std::string & strScaleZ);
|
||||
bool GetItemScale(DWORD dwPos, float & fScaleX, float & fScaleY, float & fScaleZ);
|
||||
|
||||
protected:
|
||||
void __LoadFiles();
|
||||
@@ -518,6 +522,12 @@ class CItemData
|
||||
NRaceData::TAttachingDataVector m_AttachingDataVector;
|
||||
DWORD m_dwVnum;
|
||||
TItemTable m_ItemTable;
|
||||
struct SScaleInfo
|
||||
{
|
||||
bool bSet;
|
||||
float fScaleX, fScaleY, fScaleZ;
|
||||
};
|
||||
SScaleInfo m_aScaleInfo[11];
|
||||
|
||||
public:
|
||||
static void DestroySystem();
|
||||
|
||||
@@ -402,3 +402,39 @@ CItemManager::~CItemManager()
|
||||
{
|
||||
Destroy();
|
||||
}
|
||||
|
||||
// PORT: AVA2 ItemManager.cpp LoadItemScale (ENABLE_SASH_SYSTEM). Each row covers vnum .. vnum + 4;
|
||||
// unlike AVA2 (MakeItemData) only vnums already in the item table are touched.
|
||||
bool CItemManager::LoadItemScale(const char * c_szFileName)
|
||||
{
|
||||
const VOID* pvData;
|
||||
CMappedFile kFile;
|
||||
if (!CEterPackManager::Instance().Get(kFile, c_szFileName, &pvData))
|
||||
return false;
|
||||
|
||||
CMemoryTextFileLoader kTextFileLoader;
|
||||
kTextFileLoader.Bind(kFile.Size(), pvData);
|
||||
|
||||
CTokenVector kTokenVector;
|
||||
for (DWORD i = 0; i < kTextFileLoader.GetLineCount(); ++i)
|
||||
{
|
||||
if (!kTextFileLoader.SplitLineByTab(i, &kTokenVector))
|
||||
continue;
|
||||
|
||||
if (kTokenVector.size() < ITEMSCALE_NUM)
|
||||
{
|
||||
TraceError("LoadItemScale: invalid line %d (%s).", i, c_szFileName);
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int j = 0; j < 5; ++j)
|
||||
{
|
||||
TItemMap::iterator f = m_ItemMap.find(atoi(kTokenVector[ITEMSCALE_VNUM].c_str()) + j);
|
||||
if (m_ItemMap.end() == f)
|
||||
continue;
|
||||
f->second->SetItemScale(kTokenVector[ITEMSCALE_JOB], kTokenVector[ITEMSCALE_SEX], kTokenVector[ITEMSCALE_SCALE_X], kTokenVector[ITEMSCALE_SCALE_Y], kTokenVector[ITEMSCALE_SCALE_Z]);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -13,6 +13,20 @@ class CItemManager : public CSingleton<CItemManager>
|
||||
ITEMDESC_COL_SUMM,
|
||||
ITEMDESC_COL_NUM,
|
||||
};
|
||||
// PORT: AVA2 item_scale.txt columns
|
||||
enum EItemScaleColumn
|
||||
{
|
||||
ITEMSCALE_VNUM,
|
||||
ITEMSCALE_JOB,
|
||||
ITEMSCALE_SEX,
|
||||
ITEMSCALE_SCALE_X,
|
||||
ITEMSCALE_SCALE_Y,
|
||||
ITEMSCALE_SCALE_Z,
|
||||
ITEMSCALE_POSITION_X,
|
||||
ITEMSCALE_POSITION_Y,
|
||||
ITEMSCALE_POSITION_Z,
|
||||
ITEMSCALE_NUM,
|
||||
};
|
||||
|
||||
public:
|
||||
typedef std::map<DWORD, CItemData*> TItemMap;
|
||||
@@ -31,6 +45,7 @@ class CItemManager : public CSingleton<CItemManager>
|
||||
|
||||
/////
|
||||
bool LoadItemDesc(const char* c_szFileName);
|
||||
bool LoadItemScale(const char* c_szFileName); // PORT: AVA2 sash
|
||||
bool LoadItemList(const char* c_szFileName);
|
||||
bool LoadItemTable(const char* c_szFileName);
|
||||
CItemData * MakeItemData(DWORD dwIndex);
|
||||
|
||||
@@ -23,6 +23,7 @@ class CRaceData
|
||||
PART_HEAD,
|
||||
PART_WEAPON_LEFT,
|
||||
PART_HAIR,
|
||||
PART_SASH, // PORT: AVA2 sash ("Bip01 Spine2", item vnum - 85000)
|
||||
|
||||
PART_MAX_NUM,
|
||||
};
|
||||
|
||||
@@ -104,6 +104,7 @@ enum EDragonSoulStepTypes
|
||||
const DWORD c_Costume_Slot_Hair = c_Costume_Slot_Start + 1;
|
||||
const DWORD c_Costume_Slot_Count = 2;
|
||||
const DWORD c_Costume_Slot_End = c_Costume_Slot_Start + c_Costume_Slot_Count;
|
||||
const DWORD c_Costume_Slot_Sash = c_Equipment_Start + 24; // PORT: AVA2 sash, server WEAR_COSTUME_SASH (after WEAR_BELT)
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
@@ -756,6 +756,7 @@ bool CInstanceBase::Create(const SCreateData& c_rkCreateData)
|
||||
{
|
||||
SetHair(c_rkCreateData.m_dwHair);
|
||||
SetWeapon(c_rkCreateData.m_dwWeapon);
|
||||
SetSash(c_rkCreateData.m_dwSash); // PORT: AVA2 sash
|
||||
}
|
||||
|
||||
__Create_SetName(c_rkCreateData);
|
||||
@@ -2592,6 +2593,80 @@ void CInstanceBase::SetHair(DWORD eHair)
|
||||
m_GraphicThingInstance.SetHair(eHair);
|
||||
}
|
||||
|
||||
// PORT: AVA2 InstanceBase.cpp SetSash/ChangeSash/ClearSashEffect (ENABLE_SASH_SYSTEM + ENABLE_OBJ_SCALLING).
|
||||
// Differences: part 0 detaches the model (AVA2 looked up item 85000 and returned early, leaving the old
|
||||
// sash on), and only the item_scale.txt scale is applied (its position columns never reached the deform).
|
||||
void CInstanceBase::SetSash(DWORD dwSash)
|
||||
{
|
||||
if (!IsPC() && GetRace() > 8)
|
||||
return;
|
||||
|
||||
if (IsPC() && IsPoly())
|
||||
return;
|
||||
|
||||
m_awPart[CRaceData::PART_SASH] = dwSash;
|
||||
|
||||
ClearSashEffect();
|
||||
m_GraphicThingInstance.ClearPartScale(CRaceData::PART_SASH);
|
||||
|
||||
if (!dwSash)
|
||||
{
|
||||
m_GraphicThingInstance.AttachSash(NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
dwSash += 85000;
|
||||
|
||||
float fSpecular = 65.0f;
|
||||
if (dwSash > 86000)
|
||||
{
|
||||
fSpecular += 35;
|
||||
|
||||
m_dwSashEffect = EFFECT_REFINED + EFFECT_SASH;
|
||||
__EffectContainer_AttachEffect(m_dwSashEffect);
|
||||
}
|
||||
|
||||
fSpecular /= 100.0f;
|
||||
|
||||
CItemData * pItemData;
|
||||
if (!CItemManager::Instance().GetItemDataPointer(dwSash, &pItemData))
|
||||
{
|
||||
m_GraphicThingInstance.AttachSash(NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
m_GraphicThingInstance.AttachSash(pItemData, fSpecular);
|
||||
|
||||
DWORD dwRace = GetRace(), dwPos = RaceToJob(dwRace), dwSex = RaceToSex(dwRace);
|
||||
dwPos += 1;
|
||||
if (dwSex == 0)
|
||||
dwPos += 5;
|
||||
|
||||
float fScaleX, fScaleY, fScaleZ;
|
||||
if (pItemData->GetItemScale(dwPos, fScaleX, fScaleY, fScaleZ))
|
||||
m_GraphicThingInstance.SetPartScale(CRaceData::PART_SASH, fScaleX, fScaleY, fScaleZ);
|
||||
}
|
||||
|
||||
void CInstanceBase::ChangeSash(DWORD dwSash)
|
||||
{
|
||||
if (!IsPC() && GetRace() > 7)
|
||||
return;
|
||||
|
||||
if (GetPart(CRaceData::PART_SASH) == dwSash)
|
||||
return;
|
||||
|
||||
SetSash(dwSash);
|
||||
}
|
||||
|
||||
void CInstanceBase::ClearSashEffect()
|
||||
{
|
||||
if (!m_dwSashEffect)
|
||||
return;
|
||||
|
||||
__EffectContainer_DetachEffect(m_dwSashEffect);
|
||||
m_dwSashEffect = 0;
|
||||
}
|
||||
|
||||
void CInstanceBase::ChangeHair(DWORD eHair)
|
||||
{
|
||||
if (!HAIR_COLOR_ENABLE)
|
||||
@@ -2815,6 +2890,7 @@ bool CInstanceBase::ChangeArmor(DWORD dwArmor)
|
||||
DWORD dwVID = GetVirtualID();
|
||||
DWORD dwRace = GetRace();
|
||||
DWORD eHair = GetPart(CRaceData::PART_HAIR);
|
||||
DWORD dwSash = GetPart(CRaceData::PART_SASH); // PORT: AVA2 sash
|
||||
DWORD eWeapon = GetPart(CRaceData::PART_WEAPON);
|
||||
float fRot = GetRotation();
|
||||
float fAdvRot = GetAdvancingRotation();
|
||||
@@ -2835,6 +2911,7 @@ bool CInstanceBase::ChangeArmor(DWORD dwArmor)
|
||||
|
||||
SetArmor(dwArmor);
|
||||
SetHair(eHair);
|
||||
SetSash(dwSash); // PORT: AVA2 sash
|
||||
SetWeapon(eWeapon);
|
||||
|
||||
SetRotation(fRot);
|
||||
@@ -3102,6 +3179,7 @@ void CInstanceBase::__Initialize()
|
||||
m_swordRefineEffectRight = 0;
|
||||
m_swordRefineEffectLeft = 0;
|
||||
m_armorRefineEffect = 0;
|
||||
m_dwSashEffect = 0; // PORT: AVA2 sash
|
||||
|
||||
m_sAlignment = 0;
|
||||
m_byPKMode = 0;
|
||||
|
||||
@@ -26,6 +26,7 @@ class CInstanceBase
|
||||
DWORD m_dwWeapon;
|
||||
DWORD m_dwHair;
|
||||
DWORD m_dwMountVnum;
|
||||
DWORD m_dwSash; // PORT: AVA2 sash part (item vnum - 85000)
|
||||
|
||||
short m_sAlignment;
|
||||
BYTE m_byPKMode;
|
||||
@@ -293,6 +294,7 @@ class CInstanceBase
|
||||
|
||||
EFFECT_BODYARMOR_SPECIAL, // 갑옷 4-2-1
|
||||
EFFECT_BODYARMOR_SPECIAL2, // 갑옷 4-2-2
|
||||
EFFECT_SASH, // PORT: AVA2 refined sash glow (acc_01.mse)
|
||||
|
||||
EFFECT_REFINED_NUM,
|
||||
};
|
||||
@@ -370,6 +372,8 @@ class CInstanceBase
|
||||
EFFECT_HALLOWEEN_CANDY_EQUIP, // 할로윈 사탕 착용 순간에 발동하는 이펙트
|
||||
EFFECT_HAPPINESS_RING_EQUIP, // 행복의 반지 착용 순간에 발동하는 이펙트
|
||||
EFFECT_LOVE_PENDANT_EQUIP, // 행복의 반지 착용 순간에 발동하는 이펙트
|
||||
EFFECT_SASH_SUCCEDED, // PORT: AVA2 sash
|
||||
EFFECT_SASH_EQUIP,
|
||||
EFFECT_TEMP,
|
||||
EFFECT_NUM,
|
||||
};
|
||||
@@ -500,6 +504,8 @@ class CInstanceBase
|
||||
bool ChangeArmor(DWORD dwArmor);
|
||||
void ChangeWeapon(DWORD eWeapon);
|
||||
void ChangeHair(DWORD eHair);
|
||||
void SetSash(DWORD dwSash); // PORT: AVA2 sash
|
||||
void ChangeSash(DWORD dwSash);
|
||||
void ChangeGuild(DWORD dwGuildID);
|
||||
DWORD GetWeaponType();
|
||||
|
||||
@@ -909,6 +915,7 @@ class CInstanceBase
|
||||
UINT __GetRefinedEffect(CItemData* pItem);
|
||||
void __ClearWeaponRefineEffect();
|
||||
void __ClearArmorRefineEffect();
|
||||
void ClearSashEffect(); // PORT: AVA2 sash
|
||||
|
||||
protected:
|
||||
void __AttachSelectEffect();
|
||||
@@ -977,6 +984,7 @@ class CInstanceBase
|
||||
DWORD m_swordRefineEffectRight;
|
||||
DWORD m_swordRefineEffectLeft;
|
||||
DWORD m_armorRefineEffect;
|
||||
DWORD m_dwSashEffect; // PORT: AVA2 sash
|
||||
|
||||
struct SMoveAfterFunc
|
||||
{
|
||||
|
||||
@@ -77,6 +77,7 @@ void SNetworkActorData::__copy__(const SNetworkActorData& src)
|
||||
m_dwArmor = src.m_dwArmor;
|
||||
m_dwWeapon = src.m_dwWeapon;
|
||||
m_dwHair = src.m_dwHair;
|
||||
m_dwSash = src.m_dwSash; // PORT: AVA2 sash
|
||||
|
||||
m_dwOwnerVID = src.m_dwOwnerVID;
|
||||
|
||||
@@ -104,6 +105,7 @@ SNetworkActorData::SNetworkActorData()
|
||||
m_dwArmor=0;
|
||||
m_dwWeapon=0;
|
||||
m_dwHair=0;
|
||||
m_dwSash=0; // PORT: AVA2 sash
|
||||
m_dwEmpireID=0;
|
||||
|
||||
m_dwOwnerVID=0;
|
||||
@@ -356,6 +358,7 @@ CInstanceBase* CNetworkActorManager::__AppendCharacterManagerActor(SNetworkActor
|
||||
kCreateData.m_dwArmor=rkNetActorData.m_dwArmor;
|
||||
kCreateData.m_dwWeapon=rkNetActorData.m_dwWeapon;
|
||||
kCreateData.m_dwHair=rkNetActorData.m_dwHair;
|
||||
kCreateData.m_dwSash=rkNetActorData.m_dwSash; // PORT: AVA2 sash
|
||||
kCreateData.m_isMain=__IsMainActorVID(dwVID);
|
||||
|
||||
CInstanceBase* pOldInstance = rkChrMgr.GetInstancePtr(dwVID);
|
||||
@@ -473,6 +476,7 @@ void CNetworkActorManager::UpdateActor(const SNetworkUpdateActorData& c_rkNetUpd
|
||||
pkInstFind->ChangeArmor(c_rkNetUpdateActorData.m_dwArmor);
|
||||
pkInstFind->ChangeWeapon(c_rkNetUpdateActorData.m_dwWeapon);
|
||||
pkInstFind->ChangeHair(c_rkNetUpdateActorData.m_dwHair);
|
||||
pkInstFind->ChangeSash(c_rkNetUpdateActorData.m_dwSash); // PORT: AVA2 sash
|
||||
pkInstFind->ChangeGuild(c_rkNetUpdateActorData.m_dwGuildID);
|
||||
pkInstFind->SetAffectFlagContainer(c_rkNetUpdateActorData.m_kAffectFlags);
|
||||
pkInstFind->SetMoveSpeed(c_rkNetUpdateActorData.m_dwMovSpd);
|
||||
@@ -502,6 +506,7 @@ void CNetworkActorManager::UpdateActor(const SNetworkUpdateActorData& c_rkNetUpd
|
||||
rkNetActorData.m_dwArmor=c_rkNetUpdateActorData.m_dwArmor;
|
||||
rkNetActorData.m_dwWeapon=c_rkNetUpdateActorData.m_dwWeapon;
|
||||
rkNetActorData.m_dwHair=c_rkNetUpdateActorData.m_dwHair;
|
||||
rkNetActorData.m_dwSash=c_rkNetUpdateActorData.m_dwSash; // PORT: AVA2 sash
|
||||
rkNetActorData.m_sAlignment=c_rkNetUpdateActorData.m_sAlignment;
|
||||
rkNetActorData.m_byPKMode=c_rkNetUpdateActorData.m_byPKMode;
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ struct SNetworkActorData
|
||||
DWORD m_dwArmor;
|
||||
DWORD m_dwWeapon;
|
||||
DWORD m_dwHair;
|
||||
DWORD m_dwSash; // PORT: AVA2 sash
|
||||
|
||||
DWORD m_dwOwnerVID;
|
||||
|
||||
@@ -85,6 +86,7 @@ struct SNetworkUpdateActorData
|
||||
DWORD m_dwArmor;
|
||||
DWORD m_dwWeapon;
|
||||
DWORD m_dwHair;
|
||||
DWORD m_dwSash; // PORT: AVA2 sash
|
||||
DWORD m_dwMovSpd;
|
||||
DWORD m_dwAtkSpd;
|
||||
short m_sAlignment;
|
||||
@@ -100,6 +102,7 @@ struct SNetworkUpdateActorData
|
||||
m_dwArmor=0;
|
||||
m_dwWeapon=0;
|
||||
m_dwHair=0;
|
||||
m_dwSash=0;
|
||||
m_dwMovSpd=0;
|
||||
m_dwAtkSpd=0;
|
||||
m_sAlignment=0;
|
||||
|
||||
@@ -154,6 +154,7 @@ enum
|
||||
|
||||
HEADER_CG_DRAGON_SOUL_REFINE = 205,
|
||||
HEADER_CG_STATE_CHECKER = 206,
|
||||
HEADER_CG_SASH = 211, // PORT: AVA2 sash (metin2-server FEATURE-AVA2-UNLOCK P4)
|
||||
|
||||
#ifdef __AUCTION__
|
||||
HEADER_CG_AUCTION_CMD = 205,
|
||||
@@ -358,6 +359,7 @@ enum
|
||||
HEADER_GC_SPECIFIC_EFFECT = 208,
|
||||
HEADER_GC_DRAGON_SOUL_REFINE = 209,
|
||||
HEADER_GC_RESPOND_CHANNELSTATUS = 210,
|
||||
HEADER_GC_SASH = 215, // PORT: AVA2 sash
|
||||
|
||||
HEADER_GC_KEY_AGREEMENT_COMPLETED = 0xfa, // _IMPROVED_PACKET_ENCRYPTION_
|
||||
HEADER_GC_KEY_AGREEMENT = 0xfb, // _IMPROVED_PACKET_ENCRYPTION_
|
||||
@@ -1285,6 +1287,7 @@ enum ECharacterEquipmentPart
|
||||
CHR_EQUIPPART_WEAPON,
|
||||
CHR_EQUIPPART_HEAD,
|
||||
CHR_EQUIPPART_HAIR,
|
||||
CHR_EQUIPPART_SASH, // PORT: AVA2 sash, item vnum - 85000
|
||||
|
||||
CHR_EQUIPPART_NUM,
|
||||
};
|
||||
@@ -2401,6 +2404,8 @@ enum SPECIAL_EFFECT
|
||||
SE_EQUIP_HALLOWEEN_CANDY, // 할로윈 사탕을 착용(-_-;)한 순간에 발동하는 이펙트
|
||||
SE_EQUIP_HAPPINESS_RING, // 크리스마스 행복의 반지를 착용하는 순간에 발동하는 이펙트
|
||||
SE_EQUIP_LOVE_PENDANT, // 발렌타인 사랑의 팬던트(71145) 착용할 때 이펙트 (발동이펙트임, 지속이펙트 아님)
|
||||
SE_EFFECT_SASH_SUCCEDED, // PORT: AVA2 sash combine success
|
||||
SE_EFFECT_SASH_EQUIP,
|
||||
};
|
||||
|
||||
typedef struct SPacketGCSpecialEffect
|
||||
@@ -2812,4 +2817,52 @@ typedef struct SChannelStatus
|
||||
BYTE bStatus;
|
||||
} TChannelStatus;
|
||||
|
||||
// PORT: AVA2 sash window. HEADER_CG_SASH and HEADER_GC_SASH share the layout; the server sends the
|
||||
// combine (bWindow true) and absorb windows' state, messages come as "MTSASH <code>" server commands.
|
||||
enum ESashInfo
|
||||
{
|
||||
SASH_ABSORPTION_SOCKET = 0,
|
||||
SASH_ABSORBED_SOCKET = 1,
|
||||
SASH_WINDOW_MAX_MATERIALS = 2,
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
SASH_SUBHEADER_GC_OPEN = 0,
|
||||
SASH_SUBHEADER_GC_CLOSE,
|
||||
SASH_SUBHEADER_GC_ADDED,
|
||||
SASH_SUBHEADER_GC_REMOVED,
|
||||
SASH_SUBHEADER_CG_REFINED,
|
||||
SASH_SUBHEADER_CG_CLOSE = 0,
|
||||
SASH_SUBHEADER_CG_ADD,
|
||||
SASH_SUBHEADER_CG_REMOVE,
|
||||
SASH_SUBHEADER_CG_REFINE,
|
||||
};
|
||||
|
||||
typedef struct SPacketSash
|
||||
{
|
||||
BYTE header;
|
||||
BYTE subheader;
|
||||
bool bWindow;
|
||||
DWORD dwPrice;
|
||||
BYTE bPos;
|
||||
TItemPos tPos;
|
||||
DWORD dwItemVnum;
|
||||
DWORD dwMinAbs;
|
||||
DWORD dwMaxAbs;
|
||||
} TPacketSash;
|
||||
|
||||
typedef struct SSashMaterial
|
||||
{
|
||||
BYTE bHere;
|
||||
WORD wCell;
|
||||
} TSashMaterial;
|
||||
|
||||
typedef struct SSashResult
|
||||
{
|
||||
DWORD dwItemVnum;
|
||||
DWORD dwMinAbs;
|
||||
DWORD dwMaxAbs;
|
||||
} TSashResult;
|
||||
|
||||
#pragma pack(pop)
|
||||
|
||||
@@ -840,5 +840,8 @@ void initchrmgr()
|
||||
PyModule_AddIntConstant(poModule, "EFFECT_HALLOWEEN_CANDY_EQUIP", CInstanceBase::EFFECT_HALLOWEEN_CANDY_EQUIP);
|
||||
PyModule_AddIntConstant(poModule, "EFFECT_HAPPINESS_RING_EQUIP", CInstanceBase::EFFECT_HAPPINESS_RING_EQUIP);
|
||||
PyModule_AddIntConstant(poModule, "EFFECT_LOVE_PENDANT_EQUIP", CInstanceBase::EFFECT_LOVE_PENDANT_EQUIP);
|
||||
PyModule_AddIntConstant(poModule, "EFFECT_SASH_SUCCEDED", CInstanceBase::EFFECT_SASH_SUCCEDED); // PORT: AVA2 sash
|
||||
PyModule_AddIntConstant(poModule, "EFFECT_SASH_EQUIP", CInstanceBase::EFFECT_SASH_EQUIP);
|
||||
PyModule_AddIntConstant(poModule, "EFFECT_SASH", CInstanceBase::EFFECT_SASH); // refined index, EFFECT_REFINED + EFFECT_SASH
|
||||
|
||||
}
|
||||
|
||||
@@ -84,6 +84,7 @@ PyObject * chrCreateInstance(PyObject* poSelf, PyObject* poArgs)
|
||||
kCreateData.m_dwArmor=8;
|
||||
kCreateData.m_dwWeapon=0;
|
||||
kCreateData.m_dwHair=0;
|
||||
kCreateData.m_dwSash=0; // PORT: AVA2 sash
|
||||
kCreateData.m_isMain=false;
|
||||
|
||||
PyObject* poHorse=PyDict_GetItemString(poDict, "horse");
|
||||
@@ -533,6 +534,21 @@ PyObject * chrRefresh(PyObject* poSelf, PyObject* poArgs)
|
||||
return Py_BuildNone();
|
||||
}
|
||||
|
||||
// PORT: AVA2 PythonCharacterModule.cpp chrSetSash (ENABLE_SASH_SYSTEM)
|
||||
PyObject * chrSetSash(PyObject* poSelf, PyObject* poArgs)
|
||||
{
|
||||
int dwSash;
|
||||
if (!PyTuple_GetInteger(poArgs, 0, &dwSash))
|
||||
return Py_BuildException();
|
||||
|
||||
CInstanceBase * pkInst = CPythonCharacterManager::Instance().GetSelectedInstancePtr();
|
||||
if (!pkInst)
|
||||
return Py_BuildNone();
|
||||
|
||||
pkInst->SetSash(dwSash);
|
||||
return Py_BuildNone();
|
||||
}
|
||||
|
||||
PyObject * chrMountHorse(PyObject* poSelf, PyObject* poArgs)
|
||||
{
|
||||
CInstanceBase * pkInst = CPythonCharacterManager::Instance().GetSelectedInstancePtr();
|
||||
@@ -1211,6 +1227,7 @@ PyObject * chrtestSetRideMan(PyObject* poSelf, PyObject* poArgs)
|
||||
kCreateData.m_dwRace = 0;
|
||||
kCreateData.m_dwArmor = 0;
|
||||
kCreateData.m_dwHair = 100;
|
||||
kCreateData.m_dwSash = 0; // PORT: AVA2 sash
|
||||
kCreateData.m_dwMovSpd = 100;
|
||||
kCreateData.m_dwAtkSpd = 100;
|
||||
kCreateData.m_dwMountVnum = imount;
|
||||
@@ -1227,6 +1244,7 @@ void initchr()
|
||||
{
|
||||
{ "DismountHorse", chrDismountHorse, METH_VARARGS },
|
||||
{ "MountHorse", chrMountHorse, METH_VARARGS },
|
||||
{ "SetSash", chrSetSash, METH_VARARGS }, // PORT: AVA2 sash
|
||||
|
||||
{ "Destroy", chrDestroy, METH_VARARGS },
|
||||
{ "Update", chrUpdate, METH_VARARGS },
|
||||
@@ -1438,6 +1456,7 @@ void initchr()
|
||||
PyModule_AddIntConstant(poModule, "PART_WEAPON", CRaceData::PART_WEAPON);
|
||||
PyModule_AddIntConstant(poModule, "PART_HEAD", CRaceData::PART_HEAD);
|
||||
PyModule_AddIntConstant(poModule, "PART_WEAPON_LEFT", CRaceData::PART_WEAPON_LEFT);
|
||||
PyModule_AddIntConstant(poModule, "PART_SASH", CRaceData::PART_SASH); // PORT: AVA2 sash
|
||||
|
||||
/////
|
||||
|
||||
|
||||
@@ -640,6 +640,8 @@ void initItem()
|
||||
PyModule_AddIntConstant(poModule, "COSTUME_SLOT_BODY", c_Costume_Slot_Body);
|
||||
PyModule_AddIntConstant(poModule, "COSTUME_SLOT_HAIR", c_Costume_Slot_Hair);
|
||||
PyModule_AddIntConstant(poModule, "COSTUME_SLOT_END", c_Costume_Slot_End);
|
||||
PyModule_AddIntConstant(poModule, "COSTUME_TYPE_SASH", CItemData::COSTUME_SASH); // PORT: AVA2 sash
|
||||
PyModule_AddIntConstant(poModule, "COSTUME_SLOT_SASH", c_Costume_Slot_Sash);
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_NEW_EQUIPMENT_SYSTEM
|
||||
|
||||
@@ -184,6 +184,7 @@ class CMainPacketHeaderMap : public CNetworkPacketHeaderMap
|
||||
Set(HEADER_GC_HYBRIDCRYPT_SDB, CNetworkPacketHeaderMap::TPacketType(sizeof(TPacketGCHybridSDB), DYNAMIC_SIZE_PACKET));
|
||||
Set(HEADER_GC_SPECIFIC_EFFECT, CNetworkPacketHeaderMap::TPacketType(sizeof(TPacketGCSpecificEffect), STATIC_SIZE_PACKET));
|
||||
Set(HEADER_GC_DRAGON_SOUL_REFINE, CNetworkPacketHeaderMap::TPacketType(sizeof(TPacketGCDragonSoulRefine), STATIC_SIZE_PACKET));
|
||||
Set(HEADER_GC_SASH, CNetworkPacketHeaderMap::TPacketType(sizeof(TPacketSash), STATIC_SIZE_PACKET)); // PORT: AVA2 sash
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
@@ -267,6 +267,12 @@ class CPythonNetworkStream : public CNetworkStream, public CSingleton<CPythonNet
|
||||
// 용홍석 강화
|
||||
bool SendDragonSoulRefinePacket(BYTE bRefineType, TItemPos* pos);
|
||||
|
||||
// PORT: AVA2 sash
|
||||
bool SendSashClosePacket();
|
||||
bool SendSashAddPacket(TItemPos tPos, BYTE bPos);
|
||||
bool SendSashRemovePacket(BYTE bPos);
|
||||
bool SendSashRefinePacket();
|
||||
|
||||
// Handshake
|
||||
bool RecvHandshakePacket();
|
||||
bool RecvHandshakeOKPacket();
|
||||
@@ -545,6 +551,7 @@ class CPythonNetworkStream : public CNetworkStream, public CSingleton<CPythonNet
|
||||
|
||||
// 용혼석 관련
|
||||
bool RecvDragonSoulRefine();
|
||||
bool RecvSashPacket(bool bReturn = false); // PORT: AVA2 sash
|
||||
|
||||
// MiniMap Info
|
||||
bool RecvNPCList();
|
||||
|
||||
@@ -630,6 +630,10 @@ void CPythonNetworkStream::GamePhase()
|
||||
ret = RecvDragonSoulRefine();
|
||||
break;
|
||||
|
||||
case HEADER_GC_SASH: // PORT: AVA2 sash
|
||||
ret = RecvSashPacket();
|
||||
break;
|
||||
|
||||
default:
|
||||
ret = RecvDefaultPacket(header);
|
||||
break;
|
||||
|
||||
@@ -113,6 +113,7 @@ bool CPythonNetworkStream::RecvCharacterAppendPacket()
|
||||
kNetActorData.m_dwArmor=0;/*chrAddPacket.awPart[CHR_EQUIPPART_ARMOR]*/;
|
||||
kNetActorData.m_dwWeapon=0;/*chrAddPacket.awPart[CHR_EQUIPPART_WEAPON]*/;
|
||||
kNetActorData.m_dwHair=0;/*chrAddPacket.awPart[CHR_EQUIPPART_HAIR]*/;
|
||||
kNetActorData.m_dwSash=0; // PORT: AVA2 sash
|
||||
kNetActorData.m_dwMountVnum=0;/*chrAddPacket.dwMountVnum*/;
|
||||
|
||||
kNetActorData.m_dwLevel = 0; // 몬스터 레벨 표시 안함
|
||||
@@ -160,6 +161,7 @@ bool CPythonNetworkStream::RecvCharacterAdditionalInfo()
|
||||
kNetActorData.m_dwArmor=chrInfoPacket.awPart[CHR_EQUIPPART_ARMOR];
|
||||
kNetActorData.m_dwWeapon=chrInfoPacket.awPart[CHR_EQUIPPART_WEAPON];
|
||||
kNetActorData.m_dwHair=chrInfoPacket.awPart[CHR_EQUIPPART_HAIR];
|
||||
kNetActorData.m_dwSash=chrInfoPacket.awPart[CHR_EQUIPPART_SASH]; // PORT: AVA2 sash
|
||||
kNetActorData.m_dwMountVnum=chrInfoPacket.dwMountVnum;
|
||||
|
||||
__RecvCharacterAppendPacket(&kNetActorData);
|
||||
@@ -193,6 +195,7 @@ bool CPythonNetworkStream::RecvCharacterAppendPacketNew()
|
||||
kNetActorData.m_dwArmor=chrAddPacket.awPart[CHR_EQUIPPART_ARMOR];
|
||||
kNetActorData.m_dwWeapon=chrAddPacket.awPart[CHR_EQUIPPART_WEAPON];
|
||||
kNetActorData.m_dwHair=chrAddPacket.awPart[CHR_EQUIPPART_HAIR];
|
||||
kNetActorData.m_dwSash=chrAddPacket.awPart[CHR_EQUIPPART_SASH]; // PORT: AVA2 sash
|
||||
kNetActorData.m_dwStateFlags=chrAddPacket.bStateFlag;
|
||||
kNetActorData.m_dwVID=chrAddPacket.dwVID;
|
||||
kNetActorData.m_dwMountVnum=chrAddPacket.dwMountVnum;
|
||||
@@ -221,6 +224,7 @@ bool CPythonNetworkStream::RecvCharacterUpdatePacket()
|
||||
kNetUpdateActorData.m_dwArmor=chrUpdatePacket.awPart[CHR_EQUIPPART_ARMOR];
|
||||
kNetUpdateActorData.m_dwWeapon=chrUpdatePacket.awPart[CHR_EQUIPPART_WEAPON];
|
||||
kNetUpdateActorData.m_dwHair=chrUpdatePacket.awPart[CHR_EQUIPPART_HAIR];
|
||||
kNetUpdateActorData.m_dwSash=chrUpdatePacket.awPart[CHR_EQUIPPART_SASH]; // PORT: AVA2 sash
|
||||
kNetUpdateActorData.m_dwVID=chrUpdatePacket.dwVID;
|
||||
kNetUpdateActorData.m_kAffectFlags.CopyData(0, sizeof(chrUpdatePacket.dwAffectFlag[0]), &chrUpdatePacket.dwAffectFlag[0]);
|
||||
kNetUpdateActorData.m_kAffectFlags.CopyData(32, sizeof(chrUpdatePacket.dwAffectFlag[1]), &chrUpdatePacket.dwAffectFlag[1]);
|
||||
@@ -246,6 +250,7 @@ bool CPythonNetworkStream::RecvCharacterUpdatePacketNew()
|
||||
kNetUpdateActorData.m_dwArmor=chrUpdatePacket.awPart[CHR_EQUIPPART_ARMOR];
|
||||
kNetUpdateActorData.m_dwWeapon=chrUpdatePacket.awPart[CHR_EQUIPPART_WEAPON];
|
||||
kNetUpdateActorData.m_dwHair=chrUpdatePacket.awPart[CHR_EQUIPPART_HAIR];
|
||||
kNetUpdateActorData.m_dwSash=chrUpdatePacket.awPart[CHR_EQUIPPART_SASH]; // PORT: AVA2 sash
|
||||
kNetUpdateActorData.m_dwVID=chrUpdatePacket.dwVID;
|
||||
kNetUpdateActorData.m_kAffectFlags.CopyData(0, sizeof(chrUpdatePacket.dwAffectFlag[0]), &chrUpdatePacket.dwAffectFlag[0]);
|
||||
kNetUpdateActorData.m_kAffectFlags.CopyData(32, sizeof(chrUpdatePacket.dwAffectFlag[1]), &chrUpdatePacket.dwAffectFlag[1]);
|
||||
|
||||
@@ -858,6 +858,13 @@ bool CPythonNetworkStream::RecvSpecialEffect()
|
||||
case SE_EQUIP_LOVE_PENDANT:
|
||||
effect = CInstanceBase::EFFECT_LOVE_PENDANT_EQUIP;
|
||||
break;
|
||||
// PORT: AVA2 sash
|
||||
case SE_EFFECT_SASH_SUCCEDED:
|
||||
effect = CInstanceBase::EFFECT_SASH_SUCCEDED;
|
||||
break;
|
||||
case SE_EFFECT_SASH_EQUIP:
|
||||
effect = CInstanceBase::EFFECT_SASH_EQUIP;
|
||||
break;
|
||||
|
||||
|
||||
default:
|
||||
|
||||
@@ -0,0 +1,159 @@
|
||||
#include "StdAfx.h"
|
||||
#include "PythonNetworkStream.h"
|
||||
#include "PythonSash.h"
|
||||
#include "Packet.h"
|
||||
|
||||
// PORT: AVA2 PythonNetworkStreamPhaseGame.cpp sash packets (ENABLE_SASH_SYSTEM) verbatim.
|
||||
bool CPythonNetworkStream::RecvSashPacket(bool bReturn)
|
||||
{
|
||||
TPacketSash sPacket;
|
||||
if (!Recv(sizeof(sPacket), &sPacket))
|
||||
return bReturn;
|
||||
|
||||
bReturn = true;
|
||||
switch (sPacket.subheader)
|
||||
{
|
||||
case SASH_SUBHEADER_GC_OPEN:
|
||||
{
|
||||
CPythonSash::Instance().Clear();
|
||||
PyCallClassMemberFunc(m_apoPhaseWnd[PHASE_WINDOW_GAME], "ActSash", Py_BuildValue("(ib)", 1, sPacket.bWindow));
|
||||
}
|
||||
break;
|
||||
case SASH_SUBHEADER_GC_CLOSE:
|
||||
{
|
||||
CPythonSash::Instance().Clear();
|
||||
PyCallClassMemberFunc(m_apoPhaseWnd[PHASE_WINDOW_GAME], "ActSash", Py_BuildValue("(ib)", 2, sPacket.bWindow));
|
||||
}
|
||||
break;
|
||||
case SASH_SUBHEADER_GC_ADDED:
|
||||
{
|
||||
CPythonSash::Instance().AddMaterial(sPacket.dwPrice, sPacket.bPos, sPacket.tPos);
|
||||
if (sPacket.bPos == 1)
|
||||
{
|
||||
CPythonSash::Instance().AddResult(sPacket.dwItemVnum, sPacket.dwMinAbs, sPacket.dwMaxAbs);
|
||||
PyCallClassMemberFunc(m_apoPhaseWnd[PHASE_WINDOW_GAME], "AlertSash", Py_BuildValue("(b)", sPacket.bWindow));
|
||||
}
|
||||
|
||||
PyCallClassMemberFunc(m_apoPhaseWnd[PHASE_WINDOW_GAME], "ActSash", Py_BuildValue("(ib)", 3, sPacket.bWindow));
|
||||
}
|
||||
break;
|
||||
case SASH_SUBHEADER_GC_REMOVED:
|
||||
{
|
||||
CPythonSash::Instance().RemoveMaterial(sPacket.dwPrice, sPacket.bPos);
|
||||
if (sPacket.bPos == 0)
|
||||
CPythonSash::Instance().RemoveMaterial(sPacket.dwPrice, 1);
|
||||
|
||||
PyCallClassMemberFunc(m_apoPhaseWnd[PHASE_WINDOW_GAME], "ActSash", Py_BuildValue("(ib)", 4, sPacket.bWindow));
|
||||
}
|
||||
break;
|
||||
case SASH_SUBHEADER_CG_REFINED:
|
||||
{
|
||||
if (sPacket.dwMaxAbs == 0)
|
||||
CPythonSash::Instance().RemoveMaterial(sPacket.dwPrice, 1);
|
||||
else
|
||||
{
|
||||
CPythonSash::Instance().RemoveMaterial(sPacket.dwPrice, 0);
|
||||
CPythonSash::Instance().RemoveMaterial(sPacket.dwPrice, 1);
|
||||
}
|
||||
|
||||
PyCallClassMemberFunc(m_apoPhaseWnd[PHASE_WINDOW_GAME], "ActSash", Py_BuildValue("(ib)", 4, sPacket.bWindow));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
TraceError("CPythonNetworkStream::RecvSashPacket: unknown subheader %d\n.", sPacket.subheader);
|
||||
break;
|
||||
}
|
||||
|
||||
return bReturn;
|
||||
}
|
||||
|
||||
bool CPythonNetworkStream::SendSashClosePacket()
|
||||
{
|
||||
if (!__CanActMainInstance())
|
||||
return true;
|
||||
|
||||
TItemPos tPos;
|
||||
tPos.window_type = INVENTORY;
|
||||
tPos.cell = 0;
|
||||
|
||||
TPacketSash sPacket;
|
||||
sPacket.header = HEADER_CG_SASH;
|
||||
sPacket.subheader = SASH_SUBHEADER_CG_CLOSE;
|
||||
sPacket.dwPrice = 0;
|
||||
sPacket.bPos = 0;
|
||||
sPacket.tPos = tPos;
|
||||
sPacket.dwItemVnum = 0;
|
||||
sPacket.dwMinAbs = 0;
|
||||
sPacket.dwMaxAbs = 0;
|
||||
if (!Send(sizeof(sPacket), &sPacket))
|
||||
return false;
|
||||
return SendSequence();
|
||||
}
|
||||
|
||||
bool CPythonNetworkStream::SendSashAddPacket(TItemPos tPos, BYTE bPos)
|
||||
{
|
||||
if (!__CanActMainInstance())
|
||||
return true;
|
||||
|
||||
TPacketSash sPacket;
|
||||
sPacket.header = HEADER_CG_SASH;
|
||||
sPacket.subheader = SASH_SUBHEADER_CG_ADD;
|
||||
sPacket.dwPrice = 0;
|
||||
sPacket.bPos = bPos;
|
||||
sPacket.tPos = tPos;
|
||||
sPacket.dwItemVnum = 0;
|
||||
sPacket.dwMinAbs = 0;
|
||||
sPacket.dwMaxAbs = 0;
|
||||
if (!Send(sizeof(sPacket), &sPacket))
|
||||
return false;
|
||||
|
||||
return SendSequence();
|
||||
}
|
||||
|
||||
bool CPythonNetworkStream::SendSashRemovePacket(BYTE bPos)
|
||||
{
|
||||
if (!__CanActMainInstance())
|
||||
return true;
|
||||
|
||||
TItemPos tPos;
|
||||
tPos.window_type = INVENTORY;
|
||||
tPos.cell = 0;
|
||||
|
||||
TPacketSash sPacket;
|
||||
sPacket.header = HEADER_CG_SASH;
|
||||
sPacket.subheader = SASH_SUBHEADER_CG_REMOVE;
|
||||
sPacket.dwPrice = 0;
|
||||
sPacket.bPos = bPos;
|
||||
sPacket.tPos = tPos;
|
||||
sPacket.dwItemVnum = 0;
|
||||
sPacket.dwMinAbs = 0;
|
||||
sPacket.dwMaxAbs = 0;
|
||||
if (!Send(sizeof(sPacket), &sPacket))
|
||||
return false;
|
||||
|
||||
return SendSequence();
|
||||
}
|
||||
|
||||
bool CPythonNetworkStream::SendSashRefinePacket()
|
||||
{
|
||||
if (!__CanActMainInstance())
|
||||
return true;
|
||||
|
||||
TItemPos tPos;
|
||||
tPos.window_type = INVENTORY;
|
||||
tPos.cell = 0;
|
||||
|
||||
TPacketSash sPacket;
|
||||
sPacket.header = HEADER_CG_SASH;
|
||||
sPacket.subheader = SASH_SUBHEADER_CG_REFINE;
|
||||
sPacket.dwPrice = 0;
|
||||
sPacket.bPos = 0;
|
||||
sPacket.tPos = tPos;
|
||||
sPacket.dwItemVnum = 0;
|
||||
sPacket.dwMinAbs = 0;
|
||||
sPacket.dwMaxAbs = 0;
|
||||
if (!Send(sizeof(sPacket), &sPacket))
|
||||
return false;
|
||||
|
||||
return SendSequence();
|
||||
}
|
||||
@@ -422,6 +422,34 @@ void CPythonPlayer::__UpdateBattleStatus()
|
||||
m_playerStatus.SetPoint(POINT_MAX_MAGIC_WEP, m_dwWeaponMaxMagicPower+m_dwWeaponAddPower);
|
||||
m_playerStatus.SetPoint(POINT_MIN_ATK, __GetTotalAtk(m_dwWeaponMinPower, m_dwWeaponAddPower));
|
||||
m_playerStatus.SetPoint(POINT_MAX_ATK, __GetTotalAtk(m_dwWeaponMaxPower, m_dwWeaponAddPower));
|
||||
|
||||
// PORT: AVA2 PythonPlayer.cpp (ENABLE_SASH_SYSTEM): an equipped sash that absorbed a weapon adds
|
||||
// (value3|4 + value5) and magic (value1|2 + value5) x absorption% to the shown attack, as the
|
||||
// server adds it in Item_GetDamage / SetPolyVarForAttack.
|
||||
const TItemData * pSashData = GetItemData(TItemPos(INVENTORY, c_Costume_Slot_Sash));
|
||||
if (pSashData)
|
||||
{
|
||||
long lAbsorbedVnum = pSashData->alSockets[SASH_ABSORBED_SOCKET];
|
||||
CItemData * pAbsorbed;
|
||||
if (lAbsorbedVnum != 0 && CItemManager::Instance().GetItemDataPointer(lAbsorbedVnum, &pAbsorbed))
|
||||
{
|
||||
if (pAbsorbed->GetType() == CItemData::ITEM_TYPE_WEAPON)
|
||||
{
|
||||
double dAbs = static_cast<double>(pSashData->alSockets[SASH_ABSORPTION_SOCKET]) / 100.0;
|
||||
long lMinWep = static_cast<long>((pAbsorbed->GetValue(3) + pAbsorbed->GetValue(5)) * dAbs + 0.5);
|
||||
long lMaxWep = static_cast<long>((pAbsorbed->GetValue(4) + pAbsorbed->GetValue(5)) * dAbs + 0.5);
|
||||
long lMinMag = static_cast<long>((pAbsorbed->GetValue(1) + pAbsorbed->GetValue(5)) * dAbs + 0.5);
|
||||
long lMaxMag = static_cast<long>((pAbsorbed->GetValue(2) + pAbsorbed->GetValue(5)) * dAbs + 0.5);
|
||||
|
||||
m_playerStatus.SetPoint(POINT_MIN_WEP, m_playerStatus.GetPoint(POINT_MIN_WEP) + lMinWep);
|
||||
m_playerStatus.SetPoint(POINT_MAX_WEP, m_playerStatus.GetPoint(POINT_MAX_WEP) + lMaxWep);
|
||||
m_playerStatus.SetPoint(POINT_MIN_MAGIC_WEP, m_playerStatus.GetPoint(POINT_MIN_MAGIC_WEP) + lMinMag);
|
||||
m_playerStatus.SetPoint(POINT_MAX_MAGIC_WEP, m_playerStatus.GetPoint(POINT_MAX_MAGIC_WEP) + lMaxMag);
|
||||
m_playerStatus.SetPoint(POINT_MIN_ATK, m_playerStatus.GetPoint(POINT_MIN_ATK) + lMinWep);
|
||||
m_playerStatus.SetPoint(POINT_MAX_ATK, m_playerStatus.GetPoint(POINT_MAX_ATK) + lMaxWep);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CPythonPlayer::SetStatus(DWORD dwType, long lValue)
|
||||
|
||||
@@ -0,0 +1,189 @@
|
||||
#include "StdAfx.h"
|
||||
|
||||
// PORT: AVA2 PythonSash.cpp (ENABLE_SASH_SYSTEM) verbatim; the sash module is driven by mt_sash.
|
||||
|
||||
#include "PythonSash.h"
|
||||
#include "PythonNetworkStream.h"
|
||||
|
||||
CPythonSash::CPythonSash()
|
||||
{
|
||||
Clear();
|
||||
}
|
||||
|
||||
CPythonSash::~CPythonSash()
|
||||
{
|
||||
Clear();
|
||||
}
|
||||
|
||||
void CPythonSash::Clear()
|
||||
{
|
||||
dwPrice = 0;
|
||||
|
||||
ZeroMemory(&m_vSashResult, sizeof(m_vSashResult));
|
||||
|
||||
m_vSashMaterials.clear();
|
||||
m_vSashMaterials.resize(SASH_WINDOW_MAX_MATERIALS);
|
||||
for (BYTE bPos = 0; bPos < m_vSashMaterials.size(); ++bPos)
|
||||
{
|
||||
TSashMaterial tMaterial;
|
||||
tMaterial.bHere = 0;
|
||||
tMaterial.wCell = 0;
|
||||
m_vSashMaterials[bPos] = tMaterial;
|
||||
}
|
||||
}
|
||||
|
||||
void CPythonSash::AddMaterial(DWORD dwRefPrice, BYTE bPos, TItemPos tPos)
|
||||
{
|
||||
if (bPos >= SASH_WINDOW_MAX_MATERIALS)
|
||||
return;
|
||||
|
||||
if (bPos == 0)
|
||||
dwPrice = dwRefPrice;
|
||||
|
||||
TSashMaterial tMaterial;
|
||||
tMaterial.bHere = 1;
|
||||
tMaterial.wCell = tPos.cell;
|
||||
m_vSashMaterials[bPos] = tMaterial;
|
||||
}
|
||||
|
||||
void CPythonSash::AddResult(DWORD dwItemVnum, DWORD dwMinAbs, DWORD dwMaxAbs)
|
||||
{
|
||||
TSashResult tResult;
|
||||
tResult.dwItemVnum = dwItemVnum;
|
||||
tResult.dwMinAbs = dwMinAbs;
|
||||
tResult.dwMaxAbs = dwMaxAbs;
|
||||
m_vSashResult = tResult;
|
||||
}
|
||||
|
||||
void CPythonSash::RemoveMaterial(DWORD dwRefPrice, BYTE bPos)
|
||||
{
|
||||
if (bPos >= SASH_WINDOW_MAX_MATERIALS)
|
||||
return;
|
||||
|
||||
if (bPos == 1)
|
||||
dwPrice = dwRefPrice;
|
||||
|
||||
TSashMaterial tMaterial;
|
||||
tMaterial.bHere = 0;
|
||||
tMaterial.wCell = 0;
|
||||
m_vSashMaterials[bPos] = tMaterial;
|
||||
}
|
||||
|
||||
bool CPythonSash::GetAttachedItem(BYTE bPos, BYTE & bHere, WORD & wCell)
|
||||
{
|
||||
if (bPos >= SASH_WINDOW_MAX_MATERIALS)
|
||||
return false;
|
||||
|
||||
bHere = m_vSashMaterials[bPos].bHere;
|
||||
wCell = m_vSashMaterials[bPos].wCell;
|
||||
return true;
|
||||
}
|
||||
|
||||
void CPythonSash::GetResultItem(DWORD & dwItemVnum, DWORD & dwMinAbs, DWORD & dwMaxAbs)
|
||||
{
|
||||
dwItemVnum = m_vSashResult.dwItemVnum;
|
||||
dwMinAbs = m_vSashResult.dwMinAbs;
|
||||
dwMaxAbs = m_vSashResult.dwMaxAbs;
|
||||
}
|
||||
|
||||
PyObject * SendSashCloseRequest(PyObject * poSelf, PyObject * poArgs)
|
||||
{
|
||||
CPythonNetworkStream & rkNetStream = CPythonNetworkStream::Instance();
|
||||
rkNetStream.SendSashClosePacket();
|
||||
return Py_BuildNone();
|
||||
}
|
||||
|
||||
PyObject * SendSashAdd(PyObject * poSelf, PyObject * poArgs)
|
||||
{
|
||||
BYTE bPos;
|
||||
TItemPos tPos;
|
||||
if (!PyTuple_GetInteger(poArgs, 0, &tPos.window_type))
|
||||
return Py_BuildException();
|
||||
else if (!PyTuple_GetInteger(poArgs, 1, &tPos.cell))
|
||||
return Py_BuildException();
|
||||
else if (!PyTuple_GetInteger(poArgs, 2, &bPos))
|
||||
return Py_BuildException();
|
||||
|
||||
CPythonNetworkStream & rkNetStream = CPythonNetworkStream::Instance();
|
||||
rkNetStream.SendSashAddPacket(tPos, bPos);
|
||||
return Py_BuildNone();
|
||||
}
|
||||
|
||||
PyObject * SendSashRemove(PyObject * poSelf, PyObject * poArgs)
|
||||
{
|
||||
BYTE bPos;
|
||||
if (!PyTuple_GetInteger(poArgs, 0, &bPos))
|
||||
return Py_BuildException();
|
||||
|
||||
CPythonNetworkStream & rkNetStream = CPythonNetworkStream::Instance();
|
||||
rkNetStream.SendSashRemovePacket(bPos);
|
||||
return Py_BuildNone();
|
||||
}
|
||||
|
||||
PyObject * GetSashPrice(PyObject * poSelf, PyObject * poArgs)
|
||||
{
|
||||
return Py_BuildValue("i", CPythonSash::Instance().GetPrice());
|
||||
}
|
||||
|
||||
PyObject * GetSashAttachedItem(PyObject * poSelf, PyObject * poArgs)
|
||||
{
|
||||
BYTE bPos;
|
||||
if (!PyTuple_GetInteger(poArgs, 0, &bPos))
|
||||
return Py_BuildException();
|
||||
|
||||
BYTE bHere;
|
||||
WORD wCell;
|
||||
bool bGet = CPythonSash::Instance().GetAttachedItem(bPos, bHere, wCell);
|
||||
if (!bGet)
|
||||
{
|
||||
bHere = 0;
|
||||
wCell = 0;
|
||||
}
|
||||
|
||||
return Py_BuildValue("ii", bHere, wCell);
|
||||
}
|
||||
|
||||
PyObject * GetSashResultItem(PyObject * poSelf, PyObject * poArgs)
|
||||
{
|
||||
DWORD dwItemVnum, dwMinAbs, dwMaxAbs;
|
||||
CPythonSash::Instance().GetResultItem(dwItemVnum, dwMinAbs, dwMaxAbs);
|
||||
return Py_BuildValue("iii", dwItemVnum, dwMinAbs, dwMaxAbs);
|
||||
}
|
||||
|
||||
PyObject * SendSashRefineRequest(PyObject * poSelf, PyObject * poArgs)
|
||||
{
|
||||
BYTE bHere;
|
||||
WORD wCell;
|
||||
bool bGet = CPythonSash::Instance().GetAttachedItem(1, bHere, wCell);
|
||||
if (bGet)
|
||||
{
|
||||
if (bHere)
|
||||
{
|
||||
CPythonNetworkStream & rkNetStream = CPythonNetworkStream::Instance();
|
||||
rkNetStream.SendSashRefinePacket();
|
||||
}
|
||||
}
|
||||
|
||||
return Py_BuildNone();
|
||||
}
|
||||
|
||||
void initSash()
|
||||
{
|
||||
static PyMethodDef functions[] = {
|
||||
{"SendCloseRequest", SendSashCloseRequest, METH_VARARGS},
|
||||
{"Add", SendSashAdd, METH_VARARGS},
|
||||
{"Remove", SendSashRemove, METH_VARARGS},
|
||||
{"GetPrice", GetSashPrice, METH_VARARGS},
|
||||
{"GetAttachedItem", GetSashAttachedItem, METH_VARARGS},
|
||||
{"GetResultItem", GetSashResultItem, METH_VARARGS},
|
||||
{"SendRefineRequest", SendSashRefineRequest, METH_VARARGS},
|
||||
{NULL, NULL, NULL},
|
||||
};
|
||||
|
||||
PyObject* pModule = Py_InitModule("sash", functions);
|
||||
PyModule_AddIntConstant(pModule, "ABSORPTION_SOCKET", SASH_ABSORPTION_SOCKET);
|
||||
PyModule_AddIntConstant(pModule, "ABSORBED_SOCKET", SASH_ABSORBED_SOCKET);
|
||||
PyModule_AddIntConstant(pModule, "WINDOW_MAX_MATERIALS", SASH_WINDOW_MAX_MATERIALS);
|
||||
PyModule_AddIntConstant(pModule, "CLEAN_ATTR_VALUE_FIELD", 0);
|
||||
PyModule_AddIntConstant(pModule, "LIMIT_RANGE", 1000);
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
|
||||
// PORT: AVA2 sash window state (ENABLE_SASH_SYSTEM), fed by HEADER_GC_SASH.
|
||||
#include "Packet.h"
|
||||
|
||||
class CPythonSash : public CSingleton<CPythonSash>
|
||||
{
|
||||
public:
|
||||
DWORD dwPrice;
|
||||
typedef std::vector<TSashMaterial> TSashMaterials;
|
||||
|
||||
public:
|
||||
CPythonSash();
|
||||
virtual ~CPythonSash();
|
||||
|
||||
void Clear();
|
||||
void AddMaterial(DWORD dwRefPrice, BYTE bPos, TItemPos tPos);
|
||||
void AddResult(DWORD dwItemVnum, DWORD dwMinAbs, DWORD dwMaxAbs);
|
||||
void RemoveMaterial(DWORD dwRefPrice, BYTE bPos);
|
||||
DWORD GetPrice() {return dwPrice;}
|
||||
bool GetAttachedItem(BYTE bPos, BYTE & bHere, WORD & wCell);
|
||||
void GetResultItem(DWORD & dwItemVnum, DWORD & dwMinAbs, DWORD & dwMaxAbs);
|
||||
|
||||
protected:
|
||||
TSashResult m_vSashResult;
|
||||
TSashMaterials m_vSashMaterials;
|
||||
};
|
||||
@@ -68,6 +68,7 @@ void initskill();
|
||||
void initBackground();
|
||||
void initMessenger();
|
||||
void initsafebox();
|
||||
void initSash(); // PORT: AVA2 sash
|
||||
void initguild();
|
||||
void initServerStateChecker();
|
||||
void initRenderTarget(); // PORT: AVA2 CPythonRenderTargetModule
|
||||
|
||||
@@ -43,7 +43,7 @@ inline constexpr int ID_MAX_NUM = 30; // Packet.h -> login[31]
|
||||
inline constexpr int PASS_MAX_NUM = 16; // -> passwd[17]
|
||||
inline constexpr int PRIVATE_CODE_LENGTH = 8;
|
||||
inline constexpr int POINT_MAX_NUM = 255; // common/length.h:58
|
||||
inline constexpr int CHR_EQUIPPART_NUM = 4; // ARMOR/WEAPON/HEAD/HAIR
|
||||
inline constexpr int CHR_EQUIPPART_NUM = 5; // ARMOR/WEAPON/HEAD/HAIR + AVA2 SASH (metin2-server P4)
|
||||
inline constexpr int LOGIN_STATUS_MAX_LEN = 8; // -> szStatus[9]
|
||||
inline constexpr int KEY_AGREEMENT_MAX_DATA_LEN = 256;
|
||||
inline constexpr int ITEM_SOCKET_MAX_NUM = 3; // common/item_length.h
|
||||
@@ -645,7 +645,7 @@ struct GCCharacterAdd2 { // packet_add_char2 — name + parts + guild
|
||||
uint32_t mount_vnum;
|
||||
};
|
||||
static_assert(sizeof(GCCharacterAdd2) ==
|
||||
1 + 4 + (CHARACTER_NAME_MAX_LEN + 1) + 4 + 12 + 1 + 2 + 8 + 1 + 1 + 1 + 8 + 1 + 4 + 2 + 1 + 4); // 120
|
||||
1 + 4 + (CHARACTER_NAME_MAX_LEN + 1) + 4 + 12 + 1 + 2 + 2 * CHR_EQUIPPART_NUM + 1 + 1 + 1 + 8 + 1 + 4 + 2 + 1 + 4); // 122
|
||||
|
||||
struct GCCharacterDel { uint8_t header; uint32_t vid; };
|
||||
static_assert(sizeof(GCCharacterDel) == 5);
|
||||
@@ -873,7 +873,7 @@ struct GCCharacterUpdate { // packet_update_char (header 19)
|
||||
uint8_t pk_mode;
|
||||
uint32_t mount_vnum;
|
||||
};
|
||||
static_assert(sizeof(GCCharacterUpdate) == 1 + 4 + 8 + 1 + 1 + 1 + 8 + 4 + 2 + 1 + 4); // 35
|
||||
static_assert(sizeof(GCCharacterUpdate) == 1 + 4 + 2 * CHR_EQUIPPART_NUM + 1 + 1 + 1 + 8 + 4 + 2 + 1 + 4); // 37
|
||||
|
||||
struct GCStun { uint8_t header; uint32_t vid; }; // header 13
|
||||
struct GCDead { uint8_t header; uint32_t vid; }; // header 14
|
||||
@@ -1036,7 +1036,7 @@ struct GCCharAddInfo {
|
||||
uint8_t pk_mode;
|
||||
uint32_t mount_vnum;
|
||||
};
|
||||
static_assert(sizeof(GCCharAddInfo) == 1 + 4 + (CHARACTER_NAME_MAX_LEN + 1) + 8 + 1 + 4 + 4 + 2 + 1 + 4); // 54
|
||||
static_assert(sizeof(GCCharAddInfo) == 1 + 4 + (CHARACTER_NAME_MAX_LEN + 1) + 2 * CHR_EQUIPPART_NUM + 1 + 4 + 4 + 2 + 1 + 4); // 56
|
||||
|
||||
// --- GC skills / quickslots ---
|
||||
struct PlayerSkill3 { // TPlayerSkill (common/tables.h) — time_t is 32-bit on the -m32 server
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Stage the AVA2 drop items that the 203 server lacks as ordinary 40250 items (server rows + client patch pack).
|
||||
Only types/subtypes/flags the 40250 server understands survive; items of AVA2-only systems (pets, pendants,
|
||||
new rings, mounts, sashes, costume enchants, talisman bonus items, new skill books) become plain ITEM_NONE
|
||||
new rings, mounts, costume enchants, talisman bonus items, new skill books) become plain ITEM_NONE
|
||||
items until those systems are ported. Usage: see build_zh_locale.build_ava2_items and --help.
|
||||
"""
|
||||
import argparse,json,re,shutil,subprocess
|
||||
@@ -17,8 +17,15 @@ ARMOR_SHAPES={32:'%s_7_1'}
|
||||
RACE_PATHS={'warrior':('pc','pc2'),'assassin':('pc2','pc'),'sura':('pc','pc2'),'shaman':('pc2','pc')} # (male, female)
|
||||
# Kept with their 40250 meaning; everything else becomes ITEM_NONE (see module doc).
|
||||
KEEP_TYPES={'ITEM_MATERIAL','ITEM_QUEST','ITEM_GIFTBOX','ITEM_NONE','ITEM_TREASURE_BOX','ITEM_TREASURE_KEY'}
|
||||
KEEP_SUBTYPES={('ITEM_WEAPON',s) for s in ('WEAPON_SWORD','WEAPON_DAGGER','WEAPON_BOW','WEAPON_TWO_HANDED','WEAPON_BELL','WEAPON_FAN')}|{('ITEM_ARMOR','ARMOR_FOOTS'),('ITEM_ARMOR','ARMOR_BODY'),('ITEM_METIN','METIN_NORMAL')}
|
||||
DESC={'ITEM_METIN':'可镶嵌到武器的空孔中。','ITEM_MATERIAL':'制作与升级材料。','ITEM_QUEST':'任务物品。','ITEM_GIFTBOX':'打开后可获得物品。','ITEM_TREASURE_BOX':'需要对应的钥匙才能打开。','ITEM_TREASURE_KEY':'可以打开对应的宝箱。','ITEM_WEAPON':'','ITEM_ARMOR':''}
|
||||
KEEP_SUBTYPES={('ITEM_WEAPON',s) for s in ('WEAPON_SWORD','WEAPON_DAGGER','WEAPON_BOW','WEAPON_TWO_HANDED','WEAPON_BELL','WEAPON_FAN')}|{('ITEM_ARMOR','ARMOR_FOOTS'),('ITEM_ARMOR','ARMOR_BODY'),('ITEM_METIN','METIN_NORMAL')}|{('ITEM_COSTUME','COSTUME_SASH')}
|
||||
DESC={'ITEM_METIN':'可镶嵌到武器的空孔中。','ITEM_MATERIAL':'制作与升级材料。','ITEM_QUEST':'任务物品。','ITEM_GIFTBOX':'打开后可获得物品。','ITEM_TREASURE_BOX':'需要对应的钥匙才能打开。','ITEM_TREASURE_KEY':'可以打开对应的宝箱。','ITEM_WEAPON':'','ITEM_ARMOR':'','ITEM_COSTUME':'披风。可在披风工匠处合成升级,或吸收武器、铠甲的部分属性。'}
|
||||
# Sash system (metin2-server docs/FEATURE-AVA2-UNLOCK.md P4): effects registered by the client's mt_sash script
|
||||
# (files they reference are staged too), the window images, and item_scale.txt (model scale per job/sex; a row
|
||||
# covers vnum..vnum+4).
|
||||
SASH_EFFECTS=('d:/ymir work/effect/etc/buff/buff_item6.mse','d:/ymir work/effect/etc/buff/buff_item7.mse',
|
||||
'd:/ymir work/pc/common/effect/armor/acc_01.mse')
|
||||
SASH_LOCALE={'locale/en/ui/acce/acce_combine.tga':'locale/zh/ui/acce/acce_combine.tga',
|
||||
'locale/en/ui/acce/acce_absorb.tga':'locale/zh/ui/acce/acce_absorb.tga'}
|
||||
LOCKED='该物品所属的系统尚未开放,暂时无法使用。'
|
||||
|
||||
def enums(proto_reader):
|
||||
@@ -90,6 +97,21 @@ def shape_models():
|
||||
out+=['d:/ymir work/%s/%s/%s%s'%(d,race,stem%race,e) for e in ('.gr2','.dds','_lod_01.gr2','_lod_02.gr2','_lod_03.gr2')]
|
||||
return out
|
||||
|
||||
def effect_files(files):
|
||||
"""SASH_EFFECTS and every file (mde/textures/models) they name, recursively; names are absolute
|
||||
(d:/ymir work/...) or relative to the referring file."""
|
||||
out=[];todo=list(SASH_EFFECTS)
|
||||
while todo:
|
||||
q=todo.pop()
|
||||
if q in out:continue
|
||||
if q not in files:raise SystemExit('missing AVA2 effect file %s'%q)
|
||||
out.append(q)
|
||||
if q.endswith(('.mse','.mde')):
|
||||
for name in re.findall(rb'[\w \-:./\\]+?\.(?:dds|tga|jpg|mde|gr2|spt|msf)',files[q].read_bytes(),re.I):
|
||||
name=name.decode('latin1').strip().strip('"').lower().replace('\\','/')
|
||||
todo.append(name if name.startswith('d:/') else q.rsplit('/',1)[0]+'/'+name.split('/')[-1])
|
||||
return out
|
||||
|
||||
def pack_index(pack_maker):
|
||||
"""ArchivedPath (lowercase) -> source file, from the AVA2 pack manifests."""
|
||||
index={}
|
||||
@@ -134,7 +156,7 @@ def prepare(ava,proto_reader,locale,output,tool,packer,wrap=lambda text:text):
|
||||
if q not in files:raise SystemExit('missing AVA2 asset %s for %d'%(q,v))
|
||||
target=assets/q;target.parent.mkdir(parents=True,exist_ok=True)
|
||||
if not target.exists():shutil.copy2(files[q],target);copied+=1
|
||||
for q in shape_models():
|
||||
for q in shape_models()+effect_files(files):
|
||||
if q not in files:raise SystemExit('missing AVA2 asset %s'%q)
|
||||
target=assets/q;target.parent.mkdir(parents=True,exist_ok=True)
|
||||
if not target.exists():shutil.copy2(files[q],target);copied+=1
|
||||
@@ -142,6 +164,11 @@ def prepare(ava,proto_reader,locale,output,tool,packer,wrap=lambda text:text):
|
||||
(output/'items-client.tsv').write_bytes(b'\n'.join(client)+b'\n')
|
||||
target=assets/'locale/zh';target.mkdir(parents=True,exist_ok=True)
|
||||
subprocess.run([str(tool),str(locale/'item_proto'),str(output/'items-client.tsv'),str(target/'item_proto')],check=True)
|
||||
for source,dest in SASH_LOCALE.items():
|
||||
(assets/dest).parent.mkdir(parents=True,exist_ok=True);shutil.copy2(files[source],assets/dest);copied+=1
|
||||
scale=[l for l in (pack_maker/'locale/locale/en/item_scale.txt').read_bytes().splitlines()
|
||||
if l[:5].isdigit() and wanted & set(range(int(l[:5]),int(l[:5])+5))]
|
||||
(target/'item_scale.txt').write_bytes(b'\n'.join(scale)+b'\n')
|
||||
for filename in ('item_list.txt','itemdesc.txt'):
|
||||
result=[l for l in (locale/filename).read_bytes().splitlines() if not (l.split(b'\t',1)[0].isdigit() and int(l.split(b'\t',1)[0]) in wanted)]
|
||||
for v in sorted(wanted):
|
||||
|
||||
@@ -46,6 +46,25 @@ class ConvertTests(unittest.TestCase):
|
||||
self.assertEqual(s[5],'ANTI_ASSASSIN | ANTI_SURA | ANTI_MUDANG');self.assertEqual(s[27],'32') # value3 = armor shape
|
||||
models=m.shape_models();self.assertEqual(len(models),40)
|
||||
self.assertIn('d:/ymir work/pc2/warrior/warrior_7_1_lod_03.gr2',models)
|
||||
def test_sash_kept(self):
|
||||
s,c,kept=m.convert(row(85004,'ITEM_COSTUME','COSTUME_SASH',wear='WEAR_COSTUME_BODY',
|
||||
values=('4','0','0','0','0','0')),self.E,{85004})
|
||||
self.assertTrue(kept);self.assertEqual(s[2:4],['ITEM_COSTUME','COSTUME_SASH']);self.assertEqual(s[24],'4') # value0 = grade
|
||||
self.assertEqual(c[2],3) # COSTUME_SASH, after BODY/HAIR/MOUNT
|
||||
def test_sash_effect_closure(self):
|
||||
import tempfile
|
||||
with tempfile.TemporaryDirectory() as d:
|
||||
d=Path(d);files={}
|
||||
def put(name,data=b'x'):
|
||||
f=d/str(len(files));f.write_bytes(data);files[name]=f
|
||||
put(m.SASH_EFFECTS[0],b'MeshFileName "buff_item1.mde"\nTextureFileName "d:/ymir work/effect/a.dds"')
|
||||
for q in m.SASH_EFFECTS[1:]:put(q)
|
||||
put('d:/ymir work/effect/etc/buff/buff_item1.mde',b'"light010.dds"');put('d:/ymir work/effect/a.dds')
|
||||
put('d:/ymir work/effect/etc/buff/light010.dds')
|
||||
got=set(m.effect_files(files))
|
||||
self.assertEqual(got,set(files))
|
||||
del files['d:/ymir work/effect/etc/buff/light010.dds']
|
||||
with self.assertRaises(SystemExit):m.effect_files(files)
|
||||
|
||||
def test_names_cover_ranges(self):
|
||||
n=m.names();self.assertEqual(len(n),514);self.assertEqual(n[54100],'武器外观强化石')
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
# 披风 (sash) quest
|
||||
|
||||
AVA2 `Globales/sash.lua` ported to 王铁匠 (20016; 203 has no NPC 60003):
|
||||
披风说明 / 披风合成 (`pc.open_sash(true)`) / 属性吸收 (`pc.open_sash(false)`).
|
||||
|
||||
Needs the sash game binary (server `port_sash.cpp`, CG 211 / GC 215) and a client with `mt_sash`.
|
||||
|
||||
sh install.sh # on the server, from this directory; then restart the game cores
|
||||
|
||||
Adds `pc.open_sash` to quest_functions, compiles only sash.quest in an isolated staging dir, copies its
|
||||
objects (state/sash + 20016/chat/sash.start.{0,1,2}.*) into the live tree, and adds it to locale_list.
|
||||
Backups go to /root/backup-sash/<time>.
|
||||
Executable
+35
@@ -0,0 +1,35 @@
|
||||
#!/bin/sh
|
||||
# Installs quest sash (README.md): run on the server from this directory, then restart the game cores.
|
||||
# Adds pc.open_sash to quest_functions (needs the sash game binary; an older core would fail the call, not the boot),
|
||||
# compiles only sash.quest in an isolated staging directory and copies its objects into the live quest tree.
|
||||
set -eu
|
||||
SERVER=${SERVER:-/usr/metin2/server}
|
||||
LOCALE=${LOCALE:-newcibn}
|
||||
QUEST="$SERVER/share/locale/$LOCALE/quest"
|
||||
HERE=$(cd "$(dirname "$0")" && pwd)
|
||||
BACKUP=/root/backup-sash/$(date +%Y%m%d-%H%M%S)
|
||||
export LC_ALL=C
|
||||
|
||||
mkdir -p "$BACKUP/stage"
|
||||
cp -p "$QUEST/quest_functions" "$QUEST/locale_list" "$BACKUP/"
|
||||
[ -f "$QUEST/sash.quest" ] && cp -p "$QUEST/sash.quest" "$BACKUP/"
|
||||
echo "backup: $BACKUP"
|
||||
|
||||
# A GBK trail byte 0x5C would be a backslash to qc.
|
||||
iconv -f UTF-8 -t GBK "$HERE/sash.quest" > "$BACKUP/sash.gbk"
|
||||
if grep -q '\\' "$BACKUP/sash.gbk"; then echo "sash.quest: backslash byte in GBK" >&2; exit 1; fi
|
||||
cp "$QUEST/quest_functions" "$BACKUP/stage/"
|
||||
grep -qx 'pc.open_sash' "$BACKUP/stage/quest_functions" || echo 'pc.open_sash' >> "$BACKUP/stage/quest_functions"
|
||||
cp "$BACKUP/sash.gbk" "$BACKUP/stage/sash.quest"
|
||||
(cd "$BACKUP/stage" && "$QUEST/qc" sash.quest) > "$BACKUP/qc.log" 2>&1 || { cat "$BACKUP/qc.log" >&2; exit 1; }
|
||||
test -s "$BACKUP/stage/object/state/sash"
|
||||
[ "$(cd "$BACKUP/stage/object/20016/chat" && ls | grep -c '^sash\.')" -eq 9 ] || { echo "unexpected qc output" >&2; exit 1; }
|
||||
|
||||
cat "$BACKUP/stage/quest_functions" > "$QUEST/quest_functions"
|
||||
cat "$BACKUP/sash.gbk" > "$QUEST/sash.quest"
|
||||
grep -qx 'sash.quest' "$QUEST/locale_list" || echo 'sash.quest' >> "$QUEST/locale_list"
|
||||
(cd "$BACKUP/stage/object" && find . -type f) | while read -r f; do
|
||||
mkdir -p "$QUEST/object/$(dirname "$f")"
|
||||
cp "$BACKUP/stage/object/$f" "$QUEST/object/$f"
|
||||
done
|
||||
echo "done; restart the game cores"
|
||||
@@ -0,0 +1,50 @@
|
||||
-- 披风 (AVA2 Globales/sash.lua): 合成 / 属性吸收 窗口由 pc.open_sash(true/false) 打开。
|
||||
-- AVA2 用 NPC 60003, 203 没有这个 NPC, 挂在 王铁匠 20016。
|
||||
quest sash begin
|
||||
state start begin
|
||||
when 20016.chat."披风说明" begin
|
||||
say_title(mob_name(20016))
|
||||
say("")
|
||||
say("披风可以吸收武器或防具的属性.")
|
||||
say("")
|
||||
say("[披风合成]")
|
||||
say("放入两件同等级的披风合成, 成功后")
|
||||
say("得到吸收率更高的披风, 失败则材料消失.")
|
||||
say("")
|
||||
say("[属性吸收]")
|
||||
say("放入披风和一件武器或防具, 披风按吸收率")
|
||||
say("获得其属性, 被吸收的装备会消失.")
|
||||
say("")
|
||||
end
|
||||
|
||||
when 20016.chat."披风合成" begin
|
||||
say_title(mob_name(20016))
|
||||
say("")
|
||||
say("要打开披风合成窗口吗?")
|
||||
say("")
|
||||
local confirm = select("确定", "取消")
|
||||
if confirm == 2 then
|
||||
return
|
||||
end
|
||||
|
||||
setskin(NOWINDOW)
|
||||
pc.open_sash(true)
|
||||
end
|
||||
|
||||
when 20016.chat."属性吸收" begin
|
||||
say_title(mob_name(20016))
|
||||
say("")
|
||||
say("被吸收的装备会消失, 披风原有的吸收")
|
||||
say("属性会被替换.")
|
||||
say("要打开属性吸收窗口吗?")
|
||||
say("")
|
||||
local confirm = select("确定", "取消")
|
||||
if confirm == 2 then
|
||||
return
|
||||
end
|
||||
|
||||
setskin(NOWINDOW)
|
||||
pc.open_sash(false)
|
||||
end
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user