mirror of
https://github.com/ErikasKontenis/SabrehavenServer.git
synced 2025-10-14 06:34:55 +02:00
Resolve "Merge the best from 7.40 branch"
This commit is contained in:
397
SabrehavenOTClient/modules/gamelib/const.lua
Normal file
397
SabrehavenOTClient/modules/gamelib/const.lua
Normal file
@@ -0,0 +1,397 @@
|
||||
-- @docconsts @{
|
||||
|
||||
FloorHigher = 0
|
||||
FloorLower = 15
|
||||
|
||||
SkullNone = 0
|
||||
SkullYellow = 1
|
||||
SkullGreen = 2
|
||||
SkullWhite = 3
|
||||
SkullRed = 4
|
||||
SkullBlack = 5
|
||||
SkullOrange = 6
|
||||
|
||||
ShieldNone = 0
|
||||
ShieldWhiteYellow = 1
|
||||
ShieldWhiteBlue = 2
|
||||
ShieldBlue = 3
|
||||
ShieldYellow = 4
|
||||
ShieldBlueSharedExp = 5
|
||||
ShieldYellowSharedExp = 6
|
||||
ShieldBlueNoSharedExpBlink = 7
|
||||
ShieldYellowNoSharedExpBlink = 8
|
||||
ShieldBlueNoSharedExp = 9
|
||||
ShieldYellowNoSharedExp = 10
|
||||
ShieldGray = 11
|
||||
|
||||
EmblemNone = 0
|
||||
EmblemGreen = 1
|
||||
EmblemRed = 2
|
||||
EmblemBlue = 3
|
||||
EmblemMember = 4
|
||||
EmblemOther = 5
|
||||
|
||||
VipIconFirst = 0
|
||||
VipIconLast = 10
|
||||
|
||||
Directions = {
|
||||
North = 0,
|
||||
East = 1,
|
||||
South = 2,
|
||||
West = 3,
|
||||
NorthEast = 4,
|
||||
SouthEast = 5,
|
||||
SouthWest = 6,
|
||||
NorthWest = 7
|
||||
}
|
||||
|
||||
Skill = {
|
||||
Fist = 0,
|
||||
Club = 1,
|
||||
Sword = 2,
|
||||
Axe = 3,
|
||||
Distance = 4,
|
||||
Shielding = 5,
|
||||
Fishing = 6,
|
||||
CriticalChance = 7,
|
||||
CriticalDamage = 8,
|
||||
LifeLeechChance = 9,
|
||||
LifeLeechAmount = 10,
|
||||
ManaLeechChance = 11,
|
||||
ManaLeechAmount = 12
|
||||
}
|
||||
|
||||
North = Directions.North
|
||||
East = Directions.East
|
||||
South = Directions.South
|
||||
West = Directions.West
|
||||
NorthEast = Directions.NorthEast
|
||||
SouthEast = Directions.SouthEast
|
||||
SouthWest = Directions.SouthWest
|
||||
NorthWest = Directions.NorthWest
|
||||
|
||||
FightOffensive = 1
|
||||
FightBalanced = 2
|
||||
FightDefensive = 3
|
||||
|
||||
DontChase = 0
|
||||
ChaseOpponent = 1
|
||||
|
||||
PVPWhiteDove = 0
|
||||
PVPWhiteHand = 1
|
||||
PVPYellowHand = 2
|
||||
PVPRedFist = 3
|
||||
|
||||
GameProtocolChecksum = 1
|
||||
GameAccountNames = 2
|
||||
GameChallengeOnLogin = 3
|
||||
GamePenalityOnDeath = 4
|
||||
GameNameOnNpcTrade = 5
|
||||
GameDoubleFreeCapacity = 6
|
||||
GameDoubleExperience = 7
|
||||
GameTotalCapacity = 8
|
||||
GameSkillsBase = 9
|
||||
GamePlayerRegenerationTime = 10
|
||||
GameChannelPlayerList = 11
|
||||
GamePlayerMounts = 12
|
||||
GameEnvironmentEffect = 13
|
||||
GameCreatureEmblems = 14
|
||||
GameItemAnimationPhase = 15
|
||||
GameMagicEffectU16 = 16
|
||||
GamePlayerMarket = 17
|
||||
GameSpritesU32 = 18
|
||||
GameTileAddThingWithStackpos = 19
|
||||
GameOfflineTrainingTime = 20
|
||||
GamePurseSlot = 21
|
||||
GameFormatCreatureName = 22
|
||||
GameSpellList = 23
|
||||
GameClientPing = 24
|
||||
GameExtendedClientPing = 25
|
||||
GameDoubleHealth = 28
|
||||
GameDoubleSkills = 29
|
||||
GameChangeMapAwareRange = 30
|
||||
GameMapMovePosition = 31
|
||||
GameAttackSeq = 32
|
||||
GameBlueNpcNameColor = 33
|
||||
GameDiagonalAnimatedText = 34
|
||||
GameLoginPending = 35
|
||||
GameNewSpeedLaw = 36
|
||||
GameForceFirstAutoWalkStep = 37
|
||||
GameMinimapRemove = 38
|
||||
GameDoubleShopSellAmount = 39
|
||||
GameContainerPagination = 40
|
||||
GameThingMarks = 41
|
||||
GameLooktypeU16 = 42
|
||||
GamePlayerStamina = 43
|
||||
GamePlayerAddons = 44
|
||||
GameMessageStatements = 45
|
||||
GameMessageLevel = 46
|
||||
GameNewFluids = 47
|
||||
GamePlayerStateU16 = 48
|
||||
GameNewOutfitProtocol = 49
|
||||
GamePVPMode = 50
|
||||
GameWritableDate = 51
|
||||
GameAdditionalVipInfo = 52
|
||||
GameBaseSkillU16 = 53
|
||||
GameCreatureIcons = 54
|
||||
GameHideNpcNames = 55
|
||||
GameSpritesAlphaChannel = 56
|
||||
GamePremiumExpiration = 57
|
||||
GameBrowseField = 58
|
||||
GameEnhancedAnimations = 59
|
||||
GameOGLInformation = 60
|
||||
GameMessageSizeCheck = 61
|
||||
GamePreviewState = 62
|
||||
GameLoginPacketEncryption = 63
|
||||
GameClientVersion = 64
|
||||
GameContentRevision = 65
|
||||
GameExperienceBonus = 66
|
||||
GameAuthenticator = 67
|
||||
GameUnjustifiedPoints = 68
|
||||
GameSessionKey = 69
|
||||
GameDeathType = 70
|
||||
GameIdleAnimations = 71
|
||||
GameKeepUnawareTiles = 72
|
||||
GameIngameStore = 73
|
||||
GameIngameStoreHighlights = 74
|
||||
GameIngameStoreServiceType = 75
|
||||
GameAdditionalSkills = 76
|
||||
GameDistanceEffectU16 = 77
|
||||
GamePrey = 78
|
||||
GameDoubleMagicLevel = 79
|
||||
|
||||
GameExtendedOpcode = 80
|
||||
GameMinimapLimitedToSingleFloor = 81
|
||||
GameSendWorldName = 82
|
||||
|
||||
GameDoubleLevel = 83
|
||||
GameDoubleSoul = 84
|
||||
GameDoublePlayerGoodsMoney = 85
|
||||
GameCreatureWalkthrough = 86 -- add Walkthrough for versions less than 854, unpass = msg->getU8(); in protocolgameparse.cpp
|
||||
GameDoubleTradeMoney = 87
|
||||
GameSequencedPackets = 88
|
||||
GameTibia12Protocol = 89
|
||||
|
||||
GameNewWalking = 90
|
||||
GameSlowerManualWalking = 91
|
||||
GameItemTooltip = 93
|
||||
|
||||
GameBot = 95
|
||||
GameBiggerMapCache = 96
|
||||
GameForceLight = 97
|
||||
GameNoDebug = 98
|
||||
GameBotProtection = 99
|
||||
|
||||
GameCreatureDirectionPassable = 100
|
||||
GameFasterAnimations = 101
|
||||
GameCenteredOutfits = 102
|
||||
GameSendIdentifiers = 103
|
||||
GameWingsAndAura = 104
|
||||
GamePlayerStateU32 = 105
|
||||
GameOutfitShaders = 106
|
||||
GameForceAllowItemHotkeys = 107
|
||||
GameCountU16 = 108
|
||||
GameDrawAuraOnTop = 109
|
||||
|
||||
GamePacketSizeU32 = 110
|
||||
GamePacketCompression = 111
|
||||
|
||||
GameOldInformationBar = 112
|
||||
GameHealthInfoBackground = 113
|
||||
GameWingOffset = 114
|
||||
GameAuraFrontAndBack = 115 -- To use that: First layer is bottom/back, second (blend layer) is top/front
|
||||
|
||||
GameMapDrawGroundFirst = 116 -- useful for big auras & wings
|
||||
GameMapIgnoreCorpseCorrection = 117
|
||||
GameDontCacheFiles = 118 -- doesn't work with encryption and compression
|
||||
GameBigAurasCenter = 119 -- Automatic negative offset for aura bigger than 32x32
|
||||
|
||||
LastGameFeature = 130
|
||||
|
||||
TextColors = {
|
||||
red = '#f55e5e', --'#c83200'
|
||||
orange = '#f36500', --'#c87832'
|
||||
yellow = '#ffff00', --'#e6c832'
|
||||
green = '#00EB00', --'#3fbe32'
|
||||
lightblue = '#5ff7f7',
|
||||
blue = '#9f9dfd',
|
||||
--blue1 = '#6e50dc',
|
||||
--blue2 = '#3264c8',
|
||||
--blue3 = '#0096c8',
|
||||
white = '#ffffff', --'#bebebe'
|
||||
}
|
||||
|
||||
MessageModes = {
|
||||
None = 0,
|
||||
Say = 1,
|
||||
Whisper = 2,
|
||||
Yell = 3,
|
||||
PrivateFrom = 4,
|
||||
PrivateTo = 5,
|
||||
ChannelManagement = 6,
|
||||
Channel = 7,
|
||||
ChannelHighlight = 8,
|
||||
Spell = 9,
|
||||
NpcFrom = 10,
|
||||
NpcTo = 11,
|
||||
GamemasterBroadcast = 12,
|
||||
GamemasterChannel = 13,
|
||||
GamemasterPrivateFrom = 14,
|
||||
GamemasterPrivateTo = 15,
|
||||
Login = 16,
|
||||
Warning = 17,
|
||||
Game = 18,
|
||||
Failure = 19,
|
||||
Look = 20,
|
||||
DamageDealed = 21,
|
||||
DamageReceived = 22,
|
||||
Heal = 23,
|
||||
Exp = 24,
|
||||
DamageOthers = 25,
|
||||
HealOthers = 26,
|
||||
ExpOthers = 27,
|
||||
Status = 28,
|
||||
Loot = 29,
|
||||
TradeNpc = 30,
|
||||
Guild = 31,
|
||||
PartyManagement = 32,
|
||||
Party = 33,
|
||||
BarkLow = 34,
|
||||
BarkLoud = 35,
|
||||
Report = 36,
|
||||
HotkeyUse = 37,
|
||||
TutorialHint = 38,
|
||||
Thankyou = 39,
|
||||
Market = 40,
|
||||
Mana = 41,
|
||||
BeyondLast = 42,
|
||||
MonsterYell = 43,
|
||||
MonsterSay = 44,
|
||||
Red = 45,
|
||||
Blue = 46,
|
||||
RVRChannel = 47,
|
||||
RVRAnswer = 48,
|
||||
RVRContinue = 49,
|
||||
GameHighlight = 50,
|
||||
NpcFromStartBlock = 51,
|
||||
Last = 52,
|
||||
Invalid = 255,
|
||||
}
|
||||
|
||||
OTSERV_RSA = "1091201329673994292788609605089955415282375029027981291234687579" ..
|
||||
"3726629149257644633073969600111060390723088861007265581882535850" ..
|
||||
"3429057592827629436413108566029093628212635953836686562675849720" ..
|
||||
"6207862794310902180176810615217550567108238764764442605581471797" ..
|
||||
"07119674283982419152118103759076030616683978566631413"
|
||||
|
||||
CIPSOFT_RSA = "1321277432058722840622950990822933849527763264961655079678763618" ..
|
||||
"4334395343554449668205332383339435179772895415509701210392836078" ..
|
||||
"6959821132214473291575712138800495033169914814069637740318278150" ..
|
||||
"2907336840325241747827401343576296990629870233111328210165697754" ..
|
||||
"88792221429527047321331896351555606801473202394175817"
|
||||
|
||||
-- set to the latest Tibia.pic signature to make otclient compatible with official tibia
|
||||
PIC_SIGNATURE = 0x56C5DDE7
|
||||
|
||||
OsTypes = {
|
||||
Linux = 1,
|
||||
Windows = 2,
|
||||
Flash = 3,
|
||||
OtclientLinux = 10,
|
||||
OtclientWindows = 11,
|
||||
OtclientMac = 12,
|
||||
}
|
||||
|
||||
PathFindResults = {
|
||||
Ok = 0,
|
||||
Position = 1,
|
||||
Impossible = 2,
|
||||
TooFar = 3,
|
||||
NoWay = 4,
|
||||
}
|
||||
|
||||
PathFindFlags = {
|
||||
AllowNullTiles = 1,
|
||||
AllowCreatures = 2,
|
||||
AllowNonPathable = 4,
|
||||
AllowNonWalkable = 8,
|
||||
}
|
||||
|
||||
VipState = {
|
||||
Offline = 0,
|
||||
Online = 1,
|
||||
Pending = 2,
|
||||
}
|
||||
|
||||
ExtendedIds = {
|
||||
Activate = 0,
|
||||
Locale = 1,
|
||||
Ping = 2,
|
||||
Sound = 3,
|
||||
Game = 4,
|
||||
Particles = 5,
|
||||
MapShader = 6,
|
||||
NeedsUpdate = 7
|
||||
}
|
||||
|
||||
PreviewState = {
|
||||
Default = 0,
|
||||
Inactive = 1,
|
||||
Active = 2
|
||||
}
|
||||
|
||||
Blessings = {
|
||||
None = 0,
|
||||
Adventurer = 1,
|
||||
SpiritualShielding = 2,
|
||||
EmbraceOfTibia = 4,
|
||||
FireOfSuns = 8,
|
||||
WisdomOfSolitude = 16,
|
||||
SparkOfPhoenix = 32
|
||||
}
|
||||
|
||||
DeathType = {
|
||||
Regular = 0,
|
||||
Blessed = 1
|
||||
}
|
||||
|
||||
ProductType = {
|
||||
Other = 0,
|
||||
NameChange = 1
|
||||
}
|
||||
|
||||
StoreErrorType = {
|
||||
NoError = -1,
|
||||
PurchaseError = 0,
|
||||
NetworkError = 1,
|
||||
HistoryError = 2,
|
||||
TransferError = 3,
|
||||
Information = 4
|
||||
}
|
||||
|
||||
StoreState = {
|
||||
None = 0,
|
||||
New = 1,
|
||||
Sale = 2,
|
||||
Timed = 3
|
||||
}
|
||||
|
||||
AccountStatus = {
|
||||
Ok = 0,
|
||||
Frozen = 1,
|
||||
Suspended = 2,
|
||||
}
|
||||
|
||||
SubscriptionStatus = {
|
||||
Free = 0,
|
||||
Premium = 1,
|
||||
}
|
||||
|
||||
ChannelEvent = {
|
||||
Join = 0,
|
||||
Leave = 1,
|
||||
Invite = 2,
|
||||
Exclude = 3,
|
||||
}
|
||||
|
||||
-- @}
|
176
SabrehavenOTClient/modules/gamelib/creature.lua
Normal file
176
SabrehavenOTClient/modules/gamelib/creature.lua
Normal file
@@ -0,0 +1,176 @@
|
||||
-- @docclass Creature
|
||||
|
||||
-- @docconsts @{
|
||||
|
||||
SkullNone = 0
|
||||
SkullYellow = 1
|
||||
SkullGreen = 2
|
||||
SkullWhite = 3
|
||||
SkullRed = 4
|
||||
SkullBlack = 5
|
||||
SkullOrange = 6
|
||||
|
||||
ShieldNone = 0
|
||||
ShieldWhiteYellow = 1
|
||||
ShieldWhiteBlue = 2
|
||||
ShieldBlue = 3
|
||||
ShieldYellow = 4
|
||||
ShieldBlueSharedExp = 5
|
||||
ShieldYellowSharedExp = 6
|
||||
ShieldBlueNoSharedExpBlink = 7
|
||||
ShieldYellowNoSharedExpBlink = 8
|
||||
ShieldBlueNoSharedExp = 9
|
||||
ShieldYellowNoSharedExp = 10
|
||||
|
||||
EmblemNone = 0
|
||||
EmblemGreen = 1
|
||||
EmblemRed = 2
|
||||
EmblemBlue = 3
|
||||
|
||||
NpcIconNone = 0
|
||||
NpcIconChat = 1
|
||||
NpcIconTrade = 2
|
||||
NpcIconQuest = 3
|
||||
NpcIconTradeQuest = 4
|
||||
|
||||
CreatureTypePlayer = 0
|
||||
CreatureTypeMonster = 1
|
||||
CreatureTypeNpc = 2
|
||||
CreatureTypeSummonOwn = 3
|
||||
CreatureTypeSummonOther = 4
|
||||
|
||||
-- @}
|
||||
|
||||
function getNextSkullId(skullId)
|
||||
if skullId == SkullRed or skullId == SkullBlack then
|
||||
return SkullBlack
|
||||
end
|
||||
return SkullRed
|
||||
end
|
||||
|
||||
function getSkullImagePath(skullId)
|
||||
local path
|
||||
if skullId == SkullYellow then
|
||||
path = '/images/game/skulls/skull_yellow'
|
||||
elseif skullId == SkullGreen then
|
||||
path = '/images/game/skulls/skull_green'
|
||||
elseif skullId == SkullWhite then
|
||||
path = '/images/game/skulls/skull_white'
|
||||
elseif skullId == SkullRed then
|
||||
path = '/images/game/skulls/skull_red'
|
||||
elseif skullId == SkullBlack then
|
||||
path = '/images/game/skulls/skull_black'
|
||||
elseif skullId == SkullOrange then
|
||||
path = '/images/game/skulls/skull_orange'
|
||||
end
|
||||
return path
|
||||
end
|
||||
|
||||
function getShieldImagePathAndBlink(shieldId)
|
||||
local path, blink
|
||||
if shieldId == ShieldWhiteYellow then
|
||||
path, blink = '/images/game/shields/shield_yellow_white', false
|
||||
elseif shieldId == ShieldWhiteBlue then
|
||||
path, blink = '/images/game/shields/shield_blue_white', false
|
||||
elseif shieldId == ShieldBlue then
|
||||
path, blink = '/images/game/shields/shield_blue', false
|
||||
elseif shieldId == ShieldYellow then
|
||||
path, blink = '/images/game/shields/shield_yellow', false
|
||||
elseif shieldId == ShieldBlueSharedExp then
|
||||
path, blink = '/images/game/shields/shield_blue_shared', false
|
||||
elseif shieldId == ShieldYellowSharedExp then
|
||||
path, blink = '/images/game/shields/shield_yellow_shared', false
|
||||
elseif shieldId == ShieldBlueNoSharedExpBlink then
|
||||
path, blink = '/images/game/shields/shield_blue_not_shared', true
|
||||
elseif shieldId == ShieldYellowNoSharedExpBlink then
|
||||
path, blink = '/images/game/shields/shield_yellow_not_shared', true
|
||||
elseif shieldId == ShieldBlueNoSharedExp then
|
||||
path, blink = '/images/game/shields/shield_blue_not_shared', false
|
||||
elseif shieldId == ShieldYellowNoSharedExp then
|
||||
path, blink = '/images/game/shields/shield_yellow_not_shared', false
|
||||
elseif shieldId == ShieldGray then
|
||||
path, blink = '/images/game/shields/shield_gray', false
|
||||
end
|
||||
return path, blink
|
||||
end
|
||||
|
||||
function getEmblemImagePath(emblemId)
|
||||
local path
|
||||
if emblemId == EmblemGreen then
|
||||
path = '/images/game/emblems/emblem_green'
|
||||
elseif emblemId == EmblemRed then
|
||||
path = '/images/game/emblems/emblem_red'
|
||||
elseif emblemId == EmblemBlue then
|
||||
path = '/images/game/emblems/emblem_blue'
|
||||
elseif emblemId == EmblemMember then
|
||||
path = '/images/game/emblems/emblem_member'
|
||||
elseif emblemId == EmblemOther then
|
||||
path = '/images/game/emblems/emblem_other'
|
||||
end
|
||||
return path
|
||||
end
|
||||
|
||||
function getTypeImagePath(creatureType)
|
||||
local path
|
||||
if creatureType == CreatureTypeSummonOwn then
|
||||
path = '/images/game/creaturetype/summon_own'
|
||||
elseif creatureType == CreatureTypeSummonOther then
|
||||
path = '/images/game/creaturetype/summon_other'
|
||||
end
|
||||
return path
|
||||
end
|
||||
|
||||
function getIconImagePath(iconId)
|
||||
local path
|
||||
if iconId == NpcIconChat then
|
||||
path = '/images/game/npcicons/icon_chat'
|
||||
elseif iconId == NpcIconTrade then
|
||||
path = '/images/game/npcicons/icon_trade'
|
||||
elseif iconId == NpcIconQuest then
|
||||
path = '/images/game/npcicons/icon_quest'
|
||||
elseif iconId == NpcIconTradeQuest then
|
||||
path = '/images/game/npcicons/icon_tradequest'
|
||||
end
|
||||
return path
|
||||
end
|
||||
|
||||
function Creature:onSkullChange(skullId)
|
||||
local imagePath = getSkullImagePath(skullId)
|
||||
if imagePath then
|
||||
self:setSkullTexture(imagePath)
|
||||
end
|
||||
end
|
||||
|
||||
function Creature:onShieldChange(shieldId)
|
||||
local imagePath, blink = getShieldImagePathAndBlink(shieldId)
|
||||
if imagePath then
|
||||
self:setShieldTexture(imagePath, blink)
|
||||
end
|
||||
end
|
||||
|
||||
function Creature:onEmblemChange(emblemId)
|
||||
local imagePath = getEmblemImagePath(emblemId)
|
||||
if imagePath then
|
||||
self:setEmblemTexture(imagePath)
|
||||
end
|
||||
end
|
||||
|
||||
function Creature:onTypeChange(typeId)
|
||||
local imagePath = getTypeImagePath(typeId)
|
||||
if imagePath then
|
||||
self:setTypeTexture(imagePath)
|
||||
end
|
||||
end
|
||||
|
||||
function Creature:onIconChange(iconId)
|
||||
local imagePath = getIconImagePath(iconId)
|
||||
if imagePath then
|
||||
self:setIconTexture(imagePath)
|
||||
end
|
||||
end
|
||||
|
||||
function Creature:setOutfitShader(shader)
|
||||
local outfit = self:getOutfit()
|
||||
outfit.shader = shader
|
||||
self:setOutfit(outfit)
|
||||
end
|
118
SabrehavenOTClient/modules/gamelib/game.lua
Normal file
118
SabrehavenOTClient/modules/gamelib/game.lua
Normal file
@@ -0,0 +1,118 @@
|
||||
function g_game.getRsa()
|
||||
return G.currentRsa
|
||||
end
|
||||
|
||||
function g_game.findPlayerItem(itemId, subType)
|
||||
local localPlayer = g_game.getLocalPlayer()
|
||||
if localPlayer then
|
||||
for slot = InventorySlotFirst, InventorySlotLast do
|
||||
local item = localPlayer:getInventoryItem(slot)
|
||||
if item and item:getId() == itemId and (subType == -1 or item:getSubType() == subType) then
|
||||
return item
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return g_game.findItemInContainers(itemId, subType)
|
||||
end
|
||||
|
||||
function g_game.chooseRsa(host)
|
||||
if G.currentRsa ~= CIPSOFT_RSA and G.currentRsa ~= OTSERV_RSA then return end
|
||||
if host:ends('.tibia.com') or host:ends('.cipsoft.com') then
|
||||
g_game.setRsa(CIPSOFT_RSA)
|
||||
|
||||
if g_app.getOs() == 'windows' then
|
||||
g_game.setCustomOs(OsTypes.Windows)
|
||||
else
|
||||
g_game.setCustomOs(OsTypes.Linux)
|
||||
end
|
||||
else
|
||||
if G.currentRsa == CIPSOFT_RSA then
|
||||
g_game.setCustomOs(-1)
|
||||
end
|
||||
g_game.setRsa(OTSERV_RSA)
|
||||
end
|
||||
|
||||
-- Hack fix to resolve some 760 login issues
|
||||
if g_game.getClientVersion() <= 760 then
|
||||
g_game.setCustomOs(2)
|
||||
end
|
||||
end
|
||||
|
||||
function g_game.setRsa(rsa, e)
|
||||
e = e or '65537'
|
||||
g_crypt.rsaSetPublicKey(rsa, e)
|
||||
G.currentRsa = rsa
|
||||
end
|
||||
|
||||
function g_game.isOfficialTibia()
|
||||
return G.currentRsa == CIPSOFT_RSA
|
||||
end
|
||||
|
||||
function g_game.getSupportedClients()
|
||||
return {
|
||||
740, 741, 750, 760, 770, 772,
|
||||
780, 781, 782, 790, 792,
|
||||
|
||||
800, 810, 811, 820, 821, 822,
|
||||
830, 831, 840, 842, 850, 853,
|
||||
854, 855, 857, 860, 861, 862,
|
||||
870, 871,
|
||||
|
||||
900, 910, 920, 931, 940, 943,
|
||||
944, 951, 952, 953, 954, 960,
|
||||
961, 963, 970, 971, 972, 973,
|
||||
980, 981, 982, 983, 984, 985,
|
||||
986,
|
||||
|
||||
1000, 1001, 1002, 1010, 1011,
|
||||
1012, 1013, 1020, 1021, 1022,
|
||||
1030, 1031, 1032, 1033, 1034,
|
||||
1035, 1036, 1037, 1038, 1039,
|
||||
1040, 1041, 1050, 1051, 1052,
|
||||
1053, 1054, 1055, 1056, 1057,
|
||||
1058, 1059, 1060, 1061, 1062,
|
||||
1063, 1064, 1070, 1071, 1072,
|
||||
1073, 1074, 1075, 1076, 1080,
|
||||
1081, 1082, 1090, 1091, 1092,
|
||||
1093, 1094, 1095, 1096, 1097,
|
||||
1098, 1099
|
||||
}
|
||||
end
|
||||
|
||||
-- The client version and protocol version where
|
||||
-- unsynchronized for some releases, not sure if this
|
||||
-- will be the normal standard.
|
||||
|
||||
-- Client Version: Publicly given version when
|
||||
-- downloading Cipsoft client.
|
||||
|
||||
-- Protocol Version: Previously was the same as
|
||||
-- the client version, but was unsychronized in some
|
||||
-- releases, now it needs to be verified and added here
|
||||
-- if it does not match the client version.
|
||||
|
||||
-- Reason for defining both: The server now requires a
|
||||
-- Client version and Protocol version from the client.
|
||||
|
||||
-- Important: Use getClientVersion for specific protocol
|
||||
-- features to ensure we are using the proper version.
|
||||
|
||||
function g_game.getClientProtocolVersion(client)
|
||||
local clients = {
|
||||
[980] = 971,
|
||||
[981] = 973,
|
||||
[982] = 974,
|
||||
[983] = 975,
|
||||
[984] = 976,
|
||||
[985] = 977,
|
||||
[986] = 978,
|
||||
[1001] = 979,
|
||||
[1002] = 980
|
||||
}
|
||||
return clients[client] or client
|
||||
end
|
||||
|
||||
if not G.currentRsa then
|
||||
g_game.setRsa(OTSERV_RSA)
|
||||
end
|
23
SabrehavenOTClient/modules/gamelib/gamelib.otmod
Normal file
23
SabrehavenOTClient/modules/gamelib/gamelib.otmod
Normal file
@@ -0,0 +1,23 @@
|
||||
Module
|
||||
name: gamelib
|
||||
description: Contains game related classes
|
||||
author: OTClient team
|
||||
website: https://github.com/edubart/otclient
|
||||
|
||||
@onLoad: |
|
||||
dofile 'const'
|
||||
dofile 'util'
|
||||
dofile 'protocol'
|
||||
dofile 'protocollogin'
|
||||
dofile 'protocolgame'
|
||||
dofile 'position'
|
||||
dofile 'game'
|
||||
|
||||
dofile 'creature'
|
||||
dofile 'player'
|
||||
dofile 'market'
|
||||
dofile 'textmessages'
|
||||
dofile 'thing'
|
||||
dofile 'spells'
|
||||
|
||||
dofiles 'ui'
|
202
SabrehavenOTClient/modules/gamelib/market.lua
Normal file
202
SabrehavenOTClient/modules/gamelib/market.lua
Normal file
@@ -0,0 +1,202 @@
|
||||
MarketMaxAmount = 2000
|
||||
MarketMaxAmountStackable = 64000
|
||||
MarketMaxPrice = 999999999
|
||||
MarketMaxOffers = 100
|
||||
|
||||
MarketAction = {
|
||||
Buy = 0,
|
||||
Sell = 1
|
||||
}
|
||||
|
||||
MarketRequest = {
|
||||
MyOffers = 0xFFFE,
|
||||
MyHistory = 0xFFFF
|
||||
}
|
||||
|
||||
MarketOfferState = {
|
||||
Active = 0,
|
||||
Cancelled = 1,
|
||||
Expired = 2,
|
||||
Accepted = 3,
|
||||
AcceptedEx = 255
|
||||
}
|
||||
|
||||
MarketCategory = {
|
||||
All = 0,
|
||||
Armors = 1,
|
||||
Amulets = 2,
|
||||
Boots = 3,
|
||||
Containers = 4,
|
||||
Decoration = 5,
|
||||
Food = 6,
|
||||
HelmetsHats = 7,
|
||||
Legs = 8,
|
||||
Others = 9,
|
||||
Potions = 10,
|
||||
Rings = 11,
|
||||
Runes = 12,
|
||||
Shields = 13,
|
||||
Tools = 14,
|
||||
Valuables = 15,
|
||||
Ammunition = 16,
|
||||
Axes = 17,
|
||||
Clubs = 18,
|
||||
DistanceWeapons = 19,
|
||||
Swords = 20,
|
||||
WandsRods = 21,
|
||||
PremiumScrolls = 22,
|
||||
TibiaCoins = 23,
|
||||
CreatureProducs = 24,
|
||||
Unknown1 = 25,
|
||||
Unknown2 = 26,
|
||||
StashRetrieve = 27,
|
||||
Unknown3 = 28,
|
||||
Unknown4 = 29,
|
||||
Gold = 30,
|
||||
Unassigned = 31,
|
||||
MetaWeapons = 255
|
||||
}
|
||||
|
||||
MarketCategory.First = MarketCategory.Armors
|
||||
MarketCategory.Last = MarketCategory.Unassigned
|
||||
|
||||
MarketCategoryWeapons = {
|
||||
[MarketCategory.Ammunition] = { slots = {255} },
|
||||
[MarketCategory.Axes] = { slots = {255, InventorySlotOther, InventorySlotLeft} },
|
||||
[MarketCategory.Clubs] = { slots = {255, InventorySlotOther, InventorySlotLeft} },
|
||||
[MarketCategory.DistanceWeapons] = { slots = {255, InventorySlotOther, InventorySlotLeft} },
|
||||
[MarketCategory.Swords] = { slots = {255, InventorySlotOther, InventorySlotLeft} },
|
||||
[MarketCategory.WandsRods] = { slots = {255, InventorySlotOther, InventorySlotLeft} }
|
||||
}
|
||||
|
||||
MarketCategoryStrings = {
|
||||
[0] = 'All',
|
||||
[1] = 'Armors',
|
||||
[2] = 'Amulets',
|
||||
[3] = 'Boots',
|
||||
[4] = 'Containers',
|
||||
[5] = 'Decoration',
|
||||
[6] = 'Food',
|
||||
[7] = 'Helmets and Hats',
|
||||
[8] = 'Legs',
|
||||
[9] = 'Others',
|
||||
[10] = 'Potions',
|
||||
[11] = 'Rings',
|
||||
[12] = 'Runes',
|
||||
[13] = 'Shields',
|
||||
[14] = 'Tools',
|
||||
[15] = 'Valuables',
|
||||
[16] = 'Ammunition',
|
||||
[17] = 'Axes',
|
||||
[18] = 'Clubs',
|
||||
[19] = 'Distance Weapons',
|
||||
[20] = 'Swords',
|
||||
[21] = 'Wands and Rods',
|
||||
[22] = 'Premium Scrolls',
|
||||
[23] = 'Tibia Coins',
|
||||
[24] = 'Creature Products',
|
||||
[25] = 'Unknown 1',
|
||||
[26] = 'Unknown 2',
|
||||
[27] = 'Stash Retrieve',
|
||||
[28] = 'Unknown 3',
|
||||
[29] = 'Unknown 4',
|
||||
[30] = 'Gold',
|
||||
[31] = 'Unassigned',
|
||||
[255] = 'Weapons'
|
||||
}
|
||||
|
||||
function getMarketCategoryName(id)
|
||||
if table.haskey(MarketCategoryStrings, id) then
|
||||
return MarketCategoryStrings[id]
|
||||
end
|
||||
end
|
||||
|
||||
function getMarketCategoryId(name)
|
||||
local id = table.find(MarketCategoryStrings, name)
|
||||
if id then
|
||||
return id
|
||||
end
|
||||
end
|
||||
|
||||
MarketItemDescription = {
|
||||
Armor = 1,
|
||||
Attack = 2,
|
||||
Container = 3,
|
||||
Defense = 4,
|
||||
General = 5,
|
||||
DecayTime = 6,
|
||||
Combat = 7,
|
||||
MinLevel = 8,
|
||||
MinMagicLevel = 9,
|
||||
Vocation = 10,
|
||||
Rune = 11,
|
||||
Ability = 12,
|
||||
Charges = 13,
|
||||
WeaponName = 14,
|
||||
Weight = 15,
|
||||
Imbuements = 16
|
||||
}
|
||||
|
||||
MarketItemDescription.First = MarketItemDescription.Armor
|
||||
MarketItemDescription.Last = MarketItemDescription.Weight
|
||||
|
||||
MarketItemDescriptionStrings = {
|
||||
[1] = 'Armor',
|
||||
[2] = 'Attack',
|
||||
[3] = 'Container',
|
||||
[4] = 'Defense',
|
||||
[5] = 'Description',
|
||||
[6] = 'Use Time',
|
||||
[7] = 'Combat',
|
||||
[8] = 'Min Level',
|
||||
[9] = 'Min Magic Level',
|
||||
[10] = 'Vocation',
|
||||
[11] = 'Rune',
|
||||
[12] = 'Ability',
|
||||
[13] = 'Charges',
|
||||
[14] = 'Weapon Type',
|
||||
[15] = 'Weight',
|
||||
[16] = 'Imbuements'
|
||||
}
|
||||
|
||||
function getMarketDescriptionName(id)
|
||||
if table.haskey(MarketItemDescriptionStrings, id) then
|
||||
return MarketItemDescriptionStrings[id]
|
||||
end
|
||||
end
|
||||
|
||||
function getMarketDescriptionId(name)
|
||||
local id = table.find(MarketItemDescriptionStrings, name)
|
||||
if id then
|
||||
return id
|
||||
end
|
||||
end
|
||||
|
||||
MarketSlotFilters = {
|
||||
[InventorySlotOther] = "Two-Handed",
|
||||
[InventorySlotLeft] = "One-Handed",
|
||||
[255] = "Any"
|
||||
}
|
||||
|
||||
MarketFilters = {
|
||||
Vocation = 1,
|
||||
Level = 2,
|
||||
Depot = 3,
|
||||
SearchAll = 4
|
||||
}
|
||||
|
||||
MarketFilters.First = MarketFilters.Vocation
|
||||
MarketFilters.Last = MarketFilters.Depot
|
||||
|
||||
function getMarketSlotFilterId(name)
|
||||
local id = table.find(MarketSlotFilters, name)
|
||||
if id then
|
||||
return id
|
||||
end
|
||||
end
|
||||
|
||||
function getMarketSlotFilterName(id)
|
||||
if table.haskey(MarketSlotFilters, id) then
|
||||
return MarketSlotFilters[id]
|
||||
end
|
||||
end
|
151
SabrehavenOTClient/modules/gamelib/player.lua
Normal file
151
SabrehavenOTClient/modules/gamelib/player.lua
Normal file
@@ -0,0 +1,151 @@
|
||||
-- @docclass Player
|
||||
|
||||
PlayerStates = {
|
||||
None = 0,
|
||||
Poison = 1,
|
||||
Burn = 2,
|
||||
Energy = 4,
|
||||
Drunk = 8,
|
||||
ManaShield = 16,
|
||||
Paralyze = 32,
|
||||
Haste = 64,
|
||||
Swords = 128,
|
||||
Drowning = 256,
|
||||
Freezing = 512,
|
||||
Dazzled = 1024,
|
||||
Cursed = 2048,
|
||||
PartyBuff = 4096,
|
||||
PzBlock = 8192,
|
||||
Pz = 16384,
|
||||
Bleeding = 32768,
|
||||
Hungry = 65536
|
||||
}
|
||||
|
||||
InventorySlotOther = 0
|
||||
InventorySlotHead = 1
|
||||
InventorySlotNeck = 2
|
||||
InventorySlotBack = 3
|
||||
InventorySlotBody = 4
|
||||
InventorySlotRight = 5
|
||||
InventorySlotLeft = 6
|
||||
InventorySlotLeg = 7
|
||||
InventorySlotFeet = 8
|
||||
InventorySlotFinger = 9
|
||||
InventorySlotAmmo = 10
|
||||
InventorySlotPurse = 11
|
||||
|
||||
InventorySlotFirst = 1
|
||||
InventorySlotLast = 10
|
||||
|
||||
function Player:isPartyLeader()
|
||||
local shield = self:getShield()
|
||||
return (shield == ShieldWhiteYellow or
|
||||
shield == ShieldYellow or
|
||||
shield == ShieldYellowSharedExp or
|
||||
shield == ShieldYellowNoSharedExpBlink or
|
||||
shield == ShieldYellowNoSharedExp)
|
||||
end
|
||||
|
||||
function Player:isPartyMember()
|
||||
local shield = self:getShield()
|
||||
return (shield == ShieldWhiteYellow or
|
||||
shield == ShieldYellow or
|
||||
shield == ShieldYellowSharedExp or
|
||||
shield == ShieldYellowNoSharedExpBlink or
|
||||
shield == ShieldYellowNoSharedExp or
|
||||
shield == ShieldBlueSharedExp or
|
||||
shield == ShieldBlueNoSharedExpBlink or
|
||||
shield == ShieldBlueNoSharedExp or
|
||||
shield == ShieldBlue)
|
||||
end
|
||||
|
||||
function Player:isPartySharedExperienceActive()
|
||||
local shield = self:getShield()
|
||||
return (shield == ShieldYellowSharedExp or
|
||||
shield == ShieldYellowNoSharedExpBlink or
|
||||
shield == ShieldYellowNoSharedExp or
|
||||
shield == ShieldBlueSharedExp or
|
||||
shield == ShieldBlueNoSharedExpBlink or
|
||||
shield == ShieldBlueNoSharedExp)
|
||||
end
|
||||
|
||||
function Player:hasVip(creatureName)
|
||||
for id, vip in pairs(g_game.getVips()) do
|
||||
if (vip[1] == creatureName) then return true end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function Player:isMounted()
|
||||
local outfit = self:getOutfit()
|
||||
return outfit.mount ~= nil and outfit.mount > 0
|
||||
end
|
||||
|
||||
function Player:toggleMount()
|
||||
if g_game.getFeature(GamePlayerMounts) then
|
||||
g_game.mount(not self:isMounted())
|
||||
end
|
||||
end
|
||||
|
||||
function Player:mount()
|
||||
if g_game.getFeature(GamePlayerMounts) then
|
||||
g_game.mount(true)
|
||||
end
|
||||
end
|
||||
|
||||
function Player:dismount()
|
||||
if g_game.getFeature(GamePlayerMounts) then
|
||||
g_game.mount(false)
|
||||
end
|
||||
end
|
||||
|
||||
function Player:getItem(itemId, subType)
|
||||
return g_game.findPlayerItem(itemId, subType or -1)
|
||||
end
|
||||
|
||||
function Player:getItems(itemId, subType)
|
||||
local subType = subType or -1
|
||||
|
||||
local items = {}
|
||||
for i=InventorySlotFirst,InventorySlotLast do
|
||||
local item = self:getInventoryItem(i)
|
||||
if item and item:getId() == itemId and (subType == -1 or item:getSubType() == subType) then
|
||||
table.insert(items, item)
|
||||
end
|
||||
end
|
||||
|
||||
for i, container in pairs(g_game.getContainers()) do
|
||||
for j, item in pairs(container:getItems()) do
|
||||
if item:getId() == itemId and (subType == -1 or item:getSubType() == subType) then
|
||||
item.container = container
|
||||
table.insert(items, item)
|
||||
end
|
||||
end
|
||||
end
|
||||
return items
|
||||
end
|
||||
|
||||
function Player:getItemsCount(itemId)
|
||||
local items, count = self:getItems(itemId), 0
|
||||
for i=1,#items do
|
||||
count = count + items[i]:getCount()
|
||||
end
|
||||
return count
|
||||
end
|
||||
|
||||
function Player:hasState(state, states)
|
||||
if not states then
|
||||
states = self:getStates()
|
||||
end
|
||||
|
||||
for i = 1, 32 do
|
||||
local pow = math.pow(2, i-1)
|
||||
if pow > states then break end
|
||||
|
||||
local states = bit32.band(states, pow)
|
||||
if states == state then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
37
SabrehavenOTClient/modules/gamelib/position.lua
Normal file
37
SabrehavenOTClient/modules/gamelib/position.lua
Normal file
@@ -0,0 +1,37 @@
|
||||
Position = {}
|
||||
|
||||
function Position.equals(pos1, pos2)
|
||||
return pos1.x == pos2.x and pos1.y == pos2.y and pos1.z == pos2.z
|
||||
end
|
||||
|
||||
function Position.greaterThan(pos1, pos2, orEqualTo)
|
||||
if orEqualTo then
|
||||
return pos1.x >= pos2.x or pos1.y >= pos2.y or pos1.z >= pos2.z
|
||||
else
|
||||
return pos1.x > pos2.x or pos1.y > pos2.y or pos1.z > pos2.z
|
||||
end
|
||||
end
|
||||
|
||||
function Position.lessThan(pos1, pos2, orEqualTo)
|
||||
if orEqualTo then
|
||||
return pos1.x <= pos2.x or pos1.y <= pos2.y or pos1.z <= pos2.z
|
||||
else
|
||||
return pos1.x < pos2.x or pos1.y < pos2.y or pos1.z < pos2.z
|
||||
end
|
||||
end
|
||||
|
||||
function Position.isInRange(pos1, pos2, xRange, yRange)
|
||||
return math.abs(pos1.x-pos2.x) <= xRange and math.abs(pos1.y-pos2.y) <= yRange and pos1.z == pos2.z;
|
||||
end
|
||||
|
||||
function Position.isValid(pos)
|
||||
return not (pos.x == 65535 and pos.y == 65535 and pos.z == 255)
|
||||
end
|
||||
|
||||
function Position.distance(pos1, pos2)
|
||||
return math.sqrt(math.pow((pos2.x - pos1.x), 2) + math.pow((pos2.y - pos1.y), 2))
|
||||
end
|
||||
|
||||
function Position.manhattanDistance(pos1, pos2)
|
||||
return math.abs(pos2.x - pos1.x) + math.abs(pos2.y - pos1.y)
|
||||
end
|
206
SabrehavenOTClient/modules/gamelib/protocol.lua
Normal file
206
SabrehavenOTClient/modules/gamelib/protocol.lua
Normal file
@@ -0,0 +1,206 @@
|
||||
GameServerOpcodes = {
|
||||
GameServerInitGame = 10,
|
||||
GameServerGMActions = 11,
|
||||
GameServerEnterGame = 15,
|
||||
GameServerLoginError = 20,
|
||||
GameServerLoginAdvice = 21,
|
||||
GameServerLoginWait = 22,
|
||||
GameServerAddCreature = 23,
|
||||
GameServerPingBack = 29,
|
||||
GameServerPing = 30,
|
||||
GameServerChallenge = 31,
|
||||
GameServerDeath = 40,
|
||||
|
||||
-- all in game opcodes must be greater than 50
|
||||
GameServerFirstGameOpcode = 50,
|
||||
|
||||
-- otclient ONLY
|
||||
GameServerExtendedOpcode = 50,
|
||||
|
||||
-- NOTE: add any custom opcodes in this range
|
||||
-- 51 - 99
|
||||
|
||||
-- original tibia ONLY
|
||||
GameServerFullMap = 100,
|
||||
GameServerMapTopRow = 101,
|
||||
GameServerMapRightRow = 102,
|
||||
GameServerMapBottomRow = 103,
|
||||
GameServerMapLeftRow = 104,
|
||||
GameServerUpdateTile = 105,
|
||||
GameServerCreateOnMap = 106,
|
||||
GameServerChangeOnMap = 107,
|
||||
GameServerDeleteOnMap = 108,
|
||||
GameServerMoveCreature = 109,
|
||||
GameServerOpenContainer = 110,
|
||||
GameServerCloseContainer = 111,
|
||||
GameServerCreateContainer = 112,
|
||||
GameServerChangeInContainer = 113,
|
||||
GameServerDeleteInContainer = 114,
|
||||
GameServerSetInventory = 120,
|
||||
GameServerDeleteInventory = 121,
|
||||
GameServerOpenNpcTrade = 122,
|
||||
GameServerPlayerGoods = 123,
|
||||
GameServerCloseNpcTrade = 124,
|
||||
GameServerOwnTrade = 125,
|
||||
GameServerCounterTrade = 126,
|
||||
GameServerCloseTrade = 127,
|
||||
GameServerAmbient = 130,
|
||||
GameServerGraphicalEffect = 131,
|
||||
GameServerTextEffect = 132,
|
||||
GameServerMissleEffect = 133,
|
||||
GameServerMarkCreature = 134,
|
||||
GameServerTrappers = 135,
|
||||
GameServerCreatureHealth = 140,
|
||||
GameServerCreatureLight = 141,
|
||||
GameServerCreatureOutfit = 142,
|
||||
GameServerCreatureSpeed = 143,
|
||||
GameServerCreatureSkull = 144,
|
||||
GameServerCreatureParty = 145,
|
||||
GameServerCreatureUnpass = 146,
|
||||
GameServerEditText = 150,
|
||||
GameServerEditList = 151,
|
||||
GameServerPlayerDataBasic = 159, -- 910
|
||||
GameServerPlayerData = 160,
|
||||
GameServerPlayerSkills = 161,
|
||||
GameServerPlayerState = 162,
|
||||
GameServerClearTarget = 163,
|
||||
GameServerSpellDelay = 164, -- 870
|
||||
GameServerSpellGroupDelay = 165, -- 870
|
||||
GameServerMultiUseDelay = 166, -- 870
|
||||
GameServerTalk = 170,
|
||||
GameServerChannels = 171,
|
||||
GameServerOpenChannel = 172,
|
||||
GameServerOpenPrivateChannel = 173,
|
||||
GameServerRuleViolationChannel = 174,
|
||||
GameServerRuleViolationRemove = 175,
|
||||
GameServerRuleViolationCancel = 176,
|
||||
GameServerRuleViolationLock = 177,
|
||||
GameServerOpenOwnChannel = 178,
|
||||
GameServerCloseChannel = 179,
|
||||
GameServerTextMessage = 180,
|
||||
GameServerCancelWalk = 181,
|
||||
GameServerWalkWait = 182,
|
||||
GameServerFloorChangeUp = 190,
|
||||
GameServerFloorChangeDown = 191,
|
||||
GameServerChooseOutfit = 200,
|
||||
GameServerVipAdd = 210,
|
||||
GameServerVipLogin = 211,
|
||||
GameServerVipLogout = 212,
|
||||
GameServerTutorialHint = 220,
|
||||
GameServerAutomapFlag = 221,
|
||||
GameServerCoinBalance = 223, -- 1080
|
||||
GameServerStoreError = 224, -- 1080
|
||||
GameServerRequestPurchaseData = 225, -- 1080
|
||||
GameServerQuestLog = 240,
|
||||
GameServerQuestLine = 241,
|
||||
GameServerCoinBalanceUpdating = 242, -- 1080
|
||||
GameServerChannelEvent = 243, -- 910
|
||||
GameServerItemInfo = 244, -- 910
|
||||
GameServerPlayerInventory = 245, -- 910
|
||||
GameServerMarketEnter = 246, -- 944
|
||||
GameServerMarketLeave = 247, -- 944
|
||||
GameServerMarketDetail = 248, -- 944
|
||||
GameServerMarketBrowse = 249, -- 944
|
||||
GameServerShowModalDialog = 250, -- 960
|
||||
GameServerStore = 251, -- 1080
|
||||
GameServerStoreOffers = 252, -- 1080
|
||||
GameServerStoreTransactionHistory = 253, -- 1080
|
||||
GameServerStoreCompletePurchase = 254 -- 1080
|
||||
}
|
||||
|
||||
ClientOpcodes = {
|
||||
ClientEnterAccount = 1,
|
||||
ClientEnterGame = 10,
|
||||
ClientLeaveGame = 20,
|
||||
ClientPing = 29,
|
||||
ClientPingBack = 30,
|
||||
|
||||
-- all in game opcodes must be equal or greater than 50
|
||||
ClientFirstGameOpcode = 50,
|
||||
|
||||
-- otclient ONLY
|
||||
ClientExtendedOpcode = 50,
|
||||
|
||||
-- NOTE: add any custom opcodes in this range
|
||||
-- 51 - 99
|
||||
|
||||
-- original tibia ONLY
|
||||
ClientAutoWalk = 100,
|
||||
ClientWalkNorth = 101,
|
||||
ClientWalkEast = 102,
|
||||
ClientWalkSouth = 103,
|
||||
ClientWalkWest = 104,
|
||||
ClientStop = 105,
|
||||
ClientWalkNorthEast = 106,
|
||||
ClientWalkSouthEast = 107,
|
||||
ClientWalkSouthWest = 108,
|
||||
ClientWalkNorthWest = 109,
|
||||
ClientTurnNorth = 111,
|
||||
ClientTurnEast = 112,
|
||||
ClientTurnSouth = 113,
|
||||
ClientTurnWest = 114,
|
||||
ClientEquipItem = 119, -- 910
|
||||
ClientMove = 120,
|
||||
ClientInspectNpcTrade = 121,
|
||||
ClientBuyItem = 122,
|
||||
ClientSellItem = 123,
|
||||
ClientCloseNpcTrade = 124,
|
||||
ClientRequestTrade = 125,
|
||||
ClientInspectTrade = 126,
|
||||
ClientAcceptTrade = 127,
|
||||
ClientRejectTrade = 128,
|
||||
ClientUseItem = 130,
|
||||
ClientUseItemWith = 131,
|
||||
ClientUseOnCreature = 132,
|
||||
ClientRotateItem = 133,
|
||||
ClientCloseContainer = 135,
|
||||
ClientUpContainer = 136,
|
||||
ClientEditText = 137,
|
||||
ClientEditList = 138,
|
||||
ClientLook = 140,
|
||||
ClientTalk = 150,
|
||||
ClientRequestChannels = 151,
|
||||
ClientJoinChannel = 152,
|
||||
ClientLeaveChannel = 153,
|
||||
ClientOpenPrivateChannel = 154,
|
||||
ClientCloseNpcChannel = 158,
|
||||
ClientChangeFightModes = 160,
|
||||
ClientAttack = 161,
|
||||
ClientFollow = 162,
|
||||
ClientInviteToParty = 163,
|
||||
ClientJoinParty = 164,
|
||||
ClientRevokeInvitation = 165,
|
||||
ClientPassLeadership = 166,
|
||||
ClientLeaveParty = 167,
|
||||
ClientShareExperience = 168,
|
||||
ClientDisbandParty = 169,
|
||||
ClientOpenOwnChannel = 170,
|
||||
ClientInviteToOwnChannel = 171,
|
||||
ClientExcludeFromOwnChannel = 172,
|
||||
ClientCancelAttackAndFollow = 190,
|
||||
ClientRefreshContainer = 202,
|
||||
ClientRequestOutfit = 210,
|
||||
ClientChangeOutfit = 211,
|
||||
ClientMount = 212, -- 870
|
||||
ClientAddVip = 220,
|
||||
ClientRemoveVip = 221,
|
||||
ClientBugReport = 230,
|
||||
ClientRuleViolation = 231,
|
||||
ClientDebugReport = 232,
|
||||
ClientTransferCoins = 239, -- 1080
|
||||
ClientRequestQuestLog = 240,
|
||||
ClientRequestQuestLine = 241,
|
||||
ClientNewRuleViolation = 242, -- 910
|
||||
ClientRequestItemInfo = 243, -- 910
|
||||
ClientMarketLeave = 244, -- 944
|
||||
ClientMarketBrowse = 245, -- 944
|
||||
ClientMarketCreate = 246, -- 944
|
||||
ClientMarketCancel = 247, -- 944
|
||||
ClientMarketAccept = 248, -- 944
|
||||
ClientAnswerModalDialog = 249, -- 960
|
||||
ClientOpenStore = 250, -- 1080
|
||||
ClientRequestStoreOffers = 251, -- 1080
|
||||
ClientBuyStoreOffer = 252, -- 1080
|
||||
ClientOpenTransactionHistory = 253, -- 1080
|
||||
ClientRequestTransactionHistory = 254 -- 1080
|
||||
}
|
138
SabrehavenOTClient/modules/gamelib/protocolgame.lua
Normal file
138
SabrehavenOTClient/modules/gamelib/protocolgame.lua
Normal file
@@ -0,0 +1,138 @@
|
||||
local opcodeCallbacks = {}
|
||||
local extendedCallbacks = {}
|
||||
local extendedJSONCallbacks = {}
|
||||
local extendedJSONData = {}
|
||||
local maxPacketSize = 65000
|
||||
|
||||
function ProtocolGame:onOpcode(opcode, msg)
|
||||
for i, callback in pairs(opcodeCallbacks) do
|
||||
if i == opcode then
|
||||
callback(self, msg)
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function ProtocolGame:onExtendedOpcode(opcode, buffer)
|
||||
local callback = extendedCallbacks[opcode]
|
||||
if callback then
|
||||
callback(self, opcode, buffer)
|
||||
end
|
||||
|
||||
callback = extendedJSONCallbacks[opcode]
|
||||
if callback then
|
||||
local status = buffer:sub(1,1) -- O - just one message, S - start, P - part, E - end
|
||||
local data = buffer:sub(2)
|
||||
if status ~= "E" and status ~= "P" then
|
||||
extendedJSONData[opcode] = ""
|
||||
end
|
||||
if status ~= "S" and status ~= "P" and status ~= "E" then
|
||||
extendedJSONData[opcode] = buffer
|
||||
else
|
||||
extendedJSONData[opcode] = extendedJSONData[opcode] .. data
|
||||
end
|
||||
if status ~= "S" and status ~= "P" then
|
||||
local json_status, json_data = pcall(function() return json.decode(extendedJSONData[opcode]) end)
|
||||
extendedJSONData[opcode] = nil
|
||||
if not json_status then
|
||||
error("Invalid data in extended JSON opcode (" .. json_status .. "): " .. json_data)
|
||||
return
|
||||
end
|
||||
callback(self, opcode, json_data)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function ProtocolGame.registerOpcode(opcode, callback)
|
||||
if opcodeCallbacks[opcode] then
|
||||
error('opcode ' .. opcode .. ' already registered will be overriden')
|
||||
end
|
||||
|
||||
opcodeCallbacks[opcode] = callback
|
||||
end
|
||||
|
||||
function ProtocolGame.unregisterOpcode(opcode)
|
||||
opcodeCallbacks[opcode] = nil
|
||||
end
|
||||
|
||||
function ProtocolGame.registerExtendedOpcode(opcode, callback)
|
||||
if not callback or type(callback) ~= 'function' then
|
||||
error('Invalid callback.')
|
||||
end
|
||||
|
||||
if opcode < 0 or opcode > 255 then
|
||||
error('Invalid opcode. Range: 0-255')
|
||||
end
|
||||
|
||||
if extendedCallbacks[opcode] then
|
||||
error('Opcode is already taken.')
|
||||
end
|
||||
|
||||
extendedCallbacks[opcode] = callback
|
||||
end
|
||||
|
||||
function ProtocolGame.unregisterExtendedOpcode(opcode)
|
||||
if opcode < 0 or opcode > 255 then
|
||||
error('Invalid opcode. Range: 0-255')
|
||||
end
|
||||
|
||||
if not extendedCallbacks[opcode] then
|
||||
error('Opcode is not registered.')
|
||||
end
|
||||
|
||||
extendedCallbacks[opcode] = nil
|
||||
end
|
||||
|
||||
function ProtocolGame.registerExtendedJSONOpcode(opcode, callback)
|
||||
if not callback or type(callback) ~= 'function' then
|
||||
error('Invalid callback.')
|
||||
end
|
||||
|
||||
if opcode < 0 or opcode > 255 then
|
||||
error('Invalid opcode. Range: 0-255')
|
||||
end
|
||||
|
||||
if extendedJSONCallbacks[opcode] then
|
||||
error('Opcode is already taken.')
|
||||
end
|
||||
|
||||
extendedJSONCallbacks[opcode] = callback
|
||||
end
|
||||
|
||||
function ProtocolGame.unregisterExtendedJSONOpcode(opcode)
|
||||
if opcode < 0 or opcode > 255 then
|
||||
error('Invalid opcode. Range: 0-255')
|
||||
end
|
||||
|
||||
if not extendedJSONCallbacks[opcode] then
|
||||
error('Opcode is not registered.')
|
||||
end
|
||||
|
||||
extendedJSONCallbacks[opcode] = nil
|
||||
end
|
||||
|
||||
function ProtocolGame:sendExtendedJSONOpcode(opcode, data)
|
||||
if opcode < 0 or opcode > 255 then
|
||||
error('Invalid opcode. Range: 0-255')
|
||||
end
|
||||
|
||||
if type(data) ~= "table" then
|
||||
error('Invalid data type, should be table')
|
||||
end
|
||||
|
||||
local buffer = json.encode(data)
|
||||
local s = {}
|
||||
for i=1, #buffer, maxPacketSize do
|
||||
s[#s+1] = buffer:sub(i,i+maxPacketSize - 1)
|
||||
end
|
||||
if #s == 1 then
|
||||
self:sendExtendedOpcode(opcode, s[1])
|
||||
return
|
||||
end
|
||||
self:sendExtendedOpcode(opcode, "S" .. s[1])
|
||||
for i=2,#s - 1 do
|
||||
self:sendExtendedOpcode(opcode, "P" .. s[i])
|
||||
end
|
||||
self:sendExtendedOpcode(opcode, "E" .. s[#s])
|
||||
end
|
300
SabrehavenOTClient/modules/gamelib/protocollogin.lua
Normal file
300
SabrehavenOTClient/modules/gamelib/protocollogin.lua
Normal file
@@ -0,0 +1,300 @@
|
||||
-- @docclass
|
||||
ProtocolLogin = extends(Protocol, "ProtocolLogin")
|
||||
|
||||
LoginServerError = 10
|
||||
LoginServerTokenSuccess = 12
|
||||
LoginServerTokenError = 13
|
||||
LoginServerUpdate = 17
|
||||
LoginServerMotd = 20
|
||||
LoginServerUpdateNeeded = 30
|
||||
LoginServerSessionKey = 40
|
||||
LoginServerCharacterList = 100
|
||||
LoginServerExtendedCharacterList = 101
|
||||
LoginServerProxyList = 110
|
||||
|
||||
-- Since 10.76
|
||||
LoginServerRetry = 10
|
||||
LoginServerErrorNew = 11
|
||||
|
||||
function ProtocolLogin:login(host, port, accountName, accountPassword, authenticatorToken, stayLogged)
|
||||
if string.len(host) == 0 or port == nil or port == 0 then
|
||||
signalcall(self.onLoginError, self, tr("You must enter a valid server address and port."))
|
||||
return
|
||||
end
|
||||
|
||||
self.accountName = accountName
|
||||
self.accountPassword = accountPassword
|
||||
self.authenticatorToken = authenticatorToken
|
||||
self.stayLogged = stayLogged
|
||||
self.connectCallback = self.sendLoginPacket
|
||||
|
||||
self:connect(host, port)
|
||||
end
|
||||
|
||||
function ProtocolLogin:cancelLogin()
|
||||
self:disconnect()
|
||||
end
|
||||
|
||||
function ProtocolLogin:sendLoginPacket()
|
||||
local msg = OutputMessage.create()
|
||||
msg:addU8(ClientOpcodes.ClientEnterAccount)
|
||||
msg:addU16(g_game.getOs())
|
||||
if g_game.getCustomProtocolVersion() > 0 then
|
||||
msg:addU16(g_game.getCustomProtocolVersion())
|
||||
else
|
||||
msg:addU16(g_game.getProtocolVersion())
|
||||
end
|
||||
|
||||
if g_game.getFeature(GameClientVersion) then
|
||||
msg:addU32(g_game.getClientVersion())
|
||||
end
|
||||
|
||||
if g_game.getFeature(GameContentRevision) then
|
||||
msg:addU16(g_things.getContentRevision())
|
||||
msg:addU16(0)
|
||||
else
|
||||
msg:addU32(g_things.getDatSignature())
|
||||
end
|
||||
msg:addU32(g_sprites.getSprSignature())
|
||||
msg:addU32(PIC_SIGNATURE)
|
||||
|
||||
if g_game.getFeature(GamePreviewState) then
|
||||
msg:addU8(0)
|
||||
end
|
||||
|
||||
local offset = msg:getMessageSize()
|
||||
if g_game.getFeature(GameLoginPacketEncryption) then
|
||||
-- first RSA byte must be 0
|
||||
msg:addU8(0)
|
||||
|
||||
-- xtea key
|
||||
self:generateXteaKey()
|
||||
local xteaKey = self:getXteaKey()
|
||||
msg:addU32(xteaKey[1])
|
||||
msg:addU32(xteaKey[2])
|
||||
msg:addU32(xteaKey[3])
|
||||
msg:addU32(xteaKey[4])
|
||||
end
|
||||
|
||||
if g_game.getFeature(GameAccountNames) then
|
||||
msg:addString(self.accountName)
|
||||
else
|
||||
msg:addU32(tonumber(self.accountName))
|
||||
msg:addU32(tonumber(self.accountName))
|
||||
end
|
||||
|
||||
msg:addString(self.accountPassword)
|
||||
|
||||
if self.getLoginExtendedData then
|
||||
local data = self:getLoginExtendedData()
|
||||
msg:addString(data)
|
||||
else
|
||||
msg:addString("OTCv8")
|
||||
local version = g_app.getVersion():split(" ")[1]:gsub("%.", "")
|
||||
if version:len() == 2 then
|
||||
version = version .. "0"
|
||||
end
|
||||
msg:addU16(tonumber(version))
|
||||
end
|
||||
|
||||
local paddingBytes = g_crypt.rsaGetSize() - (msg:getMessageSize() - offset)
|
||||
assert(paddingBytes >= 0)
|
||||
for i = 1, paddingBytes do
|
||||
msg:addU8(math.random(0, 0xff))
|
||||
end
|
||||
|
||||
if g_game.getFeature(GameLoginPacketEncryption) then
|
||||
msg:encryptRsa()
|
||||
end
|
||||
|
||||
if g_game.getFeature(GameOGLInformation) then
|
||||
msg:addU8(1) --unknown
|
||||
msg:addU8(1) --unknown
|
||||
|
||||
if g_game.getClientVersion() >= 1072 then
|
||||
msg:addString(string.format('%s %s', g_graphics.getVendor(), g_graphics.getRenderer()))
|
||||
else
|
||||
msg:addString(g_graphics.getRenderer())
|
||||
end
|
||||
msg:addString(g_graphics.getVersion())
|
||||
end
|
||||
|
||||
-- add RSA encrypted auth token
|
||||
if g_game.getFeature(GameAuthenticator) then
|
||||
offset = msg:getMessageSize()
|
||||
|
||||
-- first RSA byte must be 0
|
||||
msg:addU8(0)
|
||||
msg:addString(self.authenticatorToken)
|
||||
|
||||
if g_game.getFeature(GameSessionKey) then
|
||||
msg:addU8(booleantonumber(self.stayLogged))
|
||||
end
|
||||
|
||||
paddingBytes = g_crypt.rsaGetSize() - (msg:getMessageSize() - offset)
|
||||
assert(paddingBytes >= 0)
|
||||
for i = 1, paddingBytes do
|
||||
msg:addU8(math.random(0, 0xff))
|
||||
end
|
||||
|
||||
msg:encryptRsa()
|
||||
end
|
||||
|
||||
if g_game.getFeature(GamePacketSizeU32) then
|
||||
self:enableBigPackets()
|
||||
end
|
||||
|
||||
if g_game.getFeature(GameProtocolChecksum) then
|
||||
self:enableChecksum()
|
||||
end
|
||||
|
||||
self:send(msg)
|
||||
if g_game.getFeature(GameLoginPacketEncryption) then
|
||||
self:enableXteaEncryption()
|
||||
end
|
||||
self:recv()
|
||||
end
|
||||
|
||||
function ProtocolLogin:onConnect()
|
||||
self.gotConnection = true
|
||||
self:connectCallback()
|
||||
self.connectCallback = nil
|
||||
end
|
||||
|
||||
function ProtocolLogin:onRecv(msg)
|
||||
while not msg:eof() do
|
||||
local opcode = msg:getU8()
|
||||
if opcode == LoginServerErrorNew then
|
||||
self:parseError(msg)
|
||||
elseif opcode == LoginServerError then
|
||||
self:parseError(msg)
|
||||
elseif opcode == LoginServerMotd then
|
||||
self:parseMotd(msg)
|
||||
elseif opcode == LoginServerUpdateNeeded then
|
||||
signalcall(self.onLoginError, self, tr("Client needs update."))
|
||||
elseif opcode == LoginServerTokenSuccess then
|
||||
local unknown = msg:getU8()
|
||||
elseif opcode == LoginServerTokenError then
|
||||
-- TODO: prompt for token here
|
||||
local unknown = msg:getU8()
|
||||
signalcall(self.onLoginError, self, tr("Invalid authentification token."))
|
||||
elseif opcode == LoginServerCharacterList then
|
||||
self:parseCharacterList(msg)
|
||||
elseif opcode == LoginServerExtendedCharacterList then
|
||||
self:parseExtendedCharacterList(msg)
|
||||
elseif opcode == LoginServerUpdate then
|
||||
local signature = msg:getString()
|
||||
signalcall(self.onUpdateNeeded, self, signature)
|
||||
elseif opcode == LoginServerSessionKey then
|
||||
self:parseSessionKey(msg)
|
||||
elseif opcode == LoginServerProxyList then
|
||||
local proxies = {}
|
||||
local proxiesCount = msg:getU8()
|
||||
for i=1, proxiesCount do
|
||||
local host = msg:getString()
|
||||
local port = msg:getU16()
|
||||
local priority = msg:getU16()
|
||||
table.insert(proxies, {host=host, port=port, priority=priority})
|
||||
end
|
||||
signalcall(self.onProxyList, self, proxies)
|
||||
else
|
||||
self:parseOpcode(opcode, msg)
|
||||
end
|
||||
end
|
||||
self:disconnect()
|
||||
end
|
||||
|
||||
function ProtocolLogin:parseError(msg)
|
||||
local errorMessage = msg:getString()
|
||||
signalcall(self.onLoginError, self, errorMessage)
|
||||
end
|
||||
|
||||
function ProtocolLogin:parseMotd(msg)
|
||||
local motd = msg:getString()
|
||||
signalcall(self.onMotd, self, motd)
|
||||
end
|
||||
|
||||
function ProtocolLogin:parseSessionKey(msg)
|
||||
local sessionKey = msg:getString()
|
||||
signalcall(self.onSessionKey, self, sessionKey)
|
||||
end
|
||||
|
||||
function ProtocolLogin:parseCharacterList(msg)
|
||||
local characters = {}
|
||||
|
||||
if g_game.getClientVersion() > 1010 then
|
||||
local worlds = {}
|
||||
|
||||
local worldsCount = msg:getU8()
|
||||
for i=1, worldsCount do
|
||||
local world = {}
|
||||
local worldId = msg:getU8()
|
||||
world.worldName = msg:getString()
|
||||
world.worldIp = msg:getString()
|
||||
world.worldPort = msg:getU16()
|
||||
world.previewState = msg:getU8()
|
||||
worlds[worldId] = world
|
||||
end
|
||||
|
||||
local charactersCount = msg:getU8()
|
||||
for i=1, charactersCount do
|
||||
local character = {}
|
||||
local worldId = msg:getU8()
|
||||
character.name = msg:getString()
|
||||
character.worldName = worlds[worldId].worldName
|
||||
character.worldIp = worlds[worldId].worldIp
|
||||
character.worldPort = worlds[worldId].worldPort
|
||||
character.previewState = worlds[worldId].previewState
|
||||
characters[i] = character
|
||||
end
|
||||
|
||||
else
|
||||
local charactersCount = msg:getU8()
|
||||
for i=1,charactersCount do
|
||||
local character = {}
|
||||
character.name = msg:getString()
|
||||
character.worldName = msg:getString()
|
||||
character.worldIp = iptostring(msg:getU32())
|
||||
character.worldPort = msg:getU16()
|
||||
|
||||
if g_game.getFeature(GamePreviewState) then
|
||||
character.previewState = msg:getU8()
|
||||
end
|
||||
|
||||
characters[i] = character
|
||||
end
|
||||
end
|
||||
|
||||
local account = {}
|
||||
if g_game.getProtocolVersion() > 1077 then
|
||||
account.status = msg:getU8()
|
||||
account.subStatus = msg:getU8()
|
||||
|
||||
account.premDays = msg:getU32()
|
||||
if account.premDays ~= 0 and account.premDays ~= 65535 then
|
||||
account.premDays = math.floor((account.premDays - os.time()) / 86400)
|
||||
end
|
||||
else
|
||||
account.status = AccountStatus.Ok
|
||||
account.premDays = msg:getU16()
|
||||
account.subStatus = account.premDays > 0 and SubscriptionStatus.Premium or SubscriptionStatus.Free
|
||||
end
|
||||
|
||||
signalcall(self.onCharacterList, self, characters, account)
|
||||
end
|
||||
|
||||
function ProtocolLogin:parseExtendedCharacterList(msg)
|
||||
local characters = msg:getTable()
|
||||
local account = msg:getTable()
|
||||
local otui = msg:getString()
|
||||
signalcall(self.onCharacterList, self, characters, account, otui)
|
||||
end
|
||||
|
||||
function ProtocolLogin:parseOpcode(opcode, msg)
|
||||
signalcall(self.onOpcode, self, opcode, msg)
|
||||
end
|
||||
|
||||
function ProtocolLogin:onError(msg, code)
|
||||
local text = translateNetworkError(code, self:isConnecting(), msg)
|
||||
signalcall(self.onLoginError, self, text)
|
||||
end
|
441
SabrehavenOTClient/modules/gamelib/spells.lua
Normal file
441
SabrehavenOTClient/modules/gamelib/spells.lua
Normal file
@@ -0,0 +1,441 @@
|
||||
SpelllistSettings = {
|
||||
['Default'] = {
|
||||
iconFile = '/images/game/spells/defaultspells',
|
||||
iconSize = {width = 32, height = 32},
|
||||
spellListWidth = 210,
|
||||
spellWindowWidth = 550,
|
||||
spellOrder = {'Animate Dead', 'Annihilation', 'Avalanche', 'Berserk', 'Blood Rage', 'Brutal Strike', 'Cancel Invisibility', 'Challenge', 'Chameleon', 'Charge', 'Conjure Arrow', 'Conjure Bolt', 'Conjure Explosive Arrow', 'Conjure Piercing Bolt', 'Conjure Poisoned Arrow', 'Conjure Power Bolt', 'Conjure Sniper Arrow', 'Convince Creature', 'Creature Illusion', 'Cure Bleeding', 'Cure Burning', 'Cure Curse', 'Cure Electrification', 'Cure Poison', 'Cure Poison Rune', 'Curse', 'Death Strike', 'Desintegrate', 'Destroy Field', 'Divine Caldera', 'Divine Healing', 'Divine Missile', 'Electrify', 'Enchant Party', 'Enchant Spear', 'Enchant Staff', 'Energy Beam', 'Energy Field', 'Energy Strike', 'Energy Wall', 'Energy Wave', 'Energybomb', 'Envenom', 'Eternal Winter', 'Ethereal Spear', 'Explosion', 'Fierce Berserk', 'Find Person', 'Fire Field', 'Fire Wall', 'Fire Wave', 'Fireball', 'Firebomb', 'Flame Strike', 'Food', 'Front Sweep', 'Great Energy Beam', 'Great Fireball', 'Great Light', 'Groundshaker', 'Haste', 'Heal Friend', 'Heal Party', 'Heavy Magic Missile', 'Hells Core', 'Holy Flash', 'Holy Missile', 'Ice Strike', 'Ice Wave', 'Icicle', 'Ignite', 'Inflict Wound', 'Intense Healing', 'Intense Healing Rune', 'Intense Recovery', 'Intense Wound Cleansing', 'Invisibility', 'Levitate', 'Light', 'Light Healing', 'Light Magic Missile', 'Lightning', 'Magic Rope', 'Magic Shield', 'Magic Wall', 'Mass Healing', 'Paralyze', 'Physical Strike', 'Poison Bomb', 'Poison Field', 'Poison Wall', 'Protect Party', 'Protector', 'Rage of the Skies', 'Recovery', 'Salvation', 'Sharpshooter', 'Soulfire', 'Stalagmite', 'Stone Shower', 'Strong Energy Strike', 'Strong Ethereal Spear', 'Strong Flame Strike', 'Strong Haste', 'Strong Ice Strike', 'Strong Ice Wave', 'Strong Terra Strike', 'Sudden Death', 'Summon Creature', 'Swift Foot', 'Terra Strike', 'Terra Wave', 'Thunderstorm', 'Train Party', 'Ultimate Energy Strike', 'Ultimate Flame Strike', 'Ultimate Healing', 'Ultimate Healing Rune', 'Ultimate Ice Strike', 'Ultimate Light', 'Ultimate Terra Strike', 'Whirlwind Throw', 'Wild Growth', 'Wound Cleansing', 'Wrath of Nature'}
|
||||
}--[[,
|
||||
|
||||
['Sample'] = {
|
||||
iconFile = '/images/game/spells/sample',
|
||||
iconSize = {width = 64, height = 64},
|
||||
spellOrder = {'Critical Strike', 'Firefly', 'Fire Breath', 'Moonglaives', 'Wind Walk'}
|
||||
}]]
|
||||
}
|
||||
|
||||
SpellInfo = {
|
||||
['Default'] = {
|
||||
['Death Strike'] = {id = 87, words = 'exori mort', exhaustion = 2000, premium = true, type = 'Instant', icon = 'deathstrike', mana = 20, level = 16, soul = 0, group = {[1] = 2000}, vocations = {1, 5}},
|
||||
['Flame Strike'] = {id = 89, words = 'exori flam', exhaustion = 2000, premium = true, type = 'Instant', icon = 'flamestrike', mana = 20, level = 14, soul = 0, group = {[1] = 2000}, vocations = {1, 2, 5, 6}},
|
||||
['Strong Flame Strike'] = {id = 150, words = 'exori gran flam', exhaustion = 8000, premium = true, type = 'Instant', icon = 'strongflamestrike', mana = 60, level = 70, soul = 0, group = {[1] = 2000, [4] = 8000}, vocations = {1, 5}},
|
||||
['Ultimate Flame Strike'] = {id = 154, words = 'exori max flam', exhaustion = 30000, premium = true, type = 'Instant', icon = 'ultimateflamestrike', mana = 100, level = 90, soul = 0, group = {[1] = 4000}, vocations = {1, 5}},
|
||||
['Energy Strike'] = {id = 88, words = 'exori vis', exhaustion = 2000, premium = true, type = 'Instant', icon = 'energystrike', mana = 20, level = 12, soul = 0, group = {[1] = 2000}, vocations = {1, 2, 5, 6}},
|
||||
['Strong Energy Strike'] = {id = 151, words = 'exori gran vis', exhaustion = 8000, premium = true, type = 'Instant', icon = 'strongenergystrike', mana = 60, level = 80, soul = 0, group = {[1] = 2000, [4] = 8000}, vocations = {1, 5}},
|
||||
['Ultimate Energy Strike'] = {id = 155, words = 'exori max vis', exhaustion = 30000, premium = true, type = 'Instant', icon = 'ultimateenergystrike', mana = 100, level = 100,soul = 0, group = {[1] = 4000}, vocations = {1, 5}},
|
||||
['Whirlwind Throw'] = {id = 107, words = 'exori hur', exhaustion = 6000, premium = true, type = 'Instant', icon = 'whirlwindthrow', mana = 40, level = 28, soul = 0, group = {[1] = 2000}, vocations = {4, 8}},
|
||||
['Fire Wave'] = {id = 19, words = 'exevo flam hur', exhaustion = 4000, premium = false, type = 'Instant', icon = 'firewave', mana = 25, level = 18, soul = 0, group = {[1] = 2000}, vocations = {1, 5}},
|
||||
['Ethereal Spear'] = {id = 111, words = 'exori con', exhaustion = 2000, premium = true, type = 'Instant', icon = 'etherealspear', mana = 25, level = 23, soul = 0, group = {[1] = 2000}, vocations = {3, 7}},
|
||||
['Strong Ethereal Spear'] = {id = 57, words = 'exori gran con', exhaustion = 8000, premium = true, type = 'Instant', icon = 'strongetherealspear', mana = 55, level = 90, soul = 0, group = {[1] = 2000}, vocations = {3, 7}},
|
||||
['Energy Beam'] = {id = 22, words = 'exevo vis lux', exhaustion = 4000, premium = false, type = 'Instant', icon = 'energybeam', mana = 40, level = 23, soul = 0, group = {[1] = 2000}, vocations = {1, 5}},
|
||||
['Great Energy Beam'] = {id = 23, words = 'exevo gran vis lux', exhaustion = 6000, premium = false, type = 'Instant', icon = 'greatenergybeam', mana = 110, level = 29, soul = 0, group = {[1] = 2000}, vocations = {1, 5}},
|
||||
['Groundshaker'] = {id = 106, words = 'exori mas', exhaustion = 8000, premium = true, type = 'Instant', icon = 'groundshaker', mana = 160, level = 33, soul = 0, group = {[1] = 2000}, vocations = {4, 8}},
|
||||
['Berserk'] = {id = 80, words = 'exori', exhaustion = 4000, premium = true, type = 'Instant', icon = 'berserk', mana = 115, level = 35, soul = 0, group = {[1] = 2000}, vocations = {4, 8}},
|
||||
['Annihilation'] = {id = 62, words = 'exori gran ico', exhaustion = 30000, premium = true, type = 'Instant', icon = 'annihilation', mana = 300, level = 110,soul = 0, group = {[1] = 4000}, vocations = {4, 8}},
|
||||
['Brutal Strike'] = {id = 61, words = 'exori ico', exhaustion = 6000, premium = true, type = 'Instant', icon = 'brutalstrike', mana = 30, level = 16, soul = 0, group = {[1] = 2000}, vocations = {4, 8}},
|
||||
['Front Sweep'] = {id = 59, words = 'exori min', exhaustion = 6000, premium = true, type = 'Instant', icon = 'frontsweep', mana = 200, level = 70, soul = 0, group = {[1] = 2000}, vocations = {4, 8}},
|
||||
['Inflict Wound'] = {id = 141, words = 'utori kor', exhaustion = 30000, premium = true, type = 'Instant', icon = 'inflictwound', mana = 30, level = 40, soul = 0, group = {[1] = 2000}, vocations = {4, 8}},
|
||||
['Ignite'] = {id = 138, words = 'utori flam', exhaustion = 30000, premium = true, type = 'Instant', icon = 'ignite', mana = 30, level = 26, soul = 0, group = {[1] = 2000}, vocations = {1, 5}},
|
||||
['Lightning'] = {id = 149, words = 'exori amp vis', exhaustion = 8000, premium = true, type = 'Instant', icon = 'lightning', mana = 60, level = 55, soul = 0, group = {[1] = 2000, [4] = 8000}, vocations = {1, 5}},
|
||||
['Curse'] = {id = 139, words = 'utori mort', exhaustion = 50000, premium = true, type = 'Instant', icon = 'curse', mana = 30, level = 75, soul = 0, group = {[1] = 2000}, vocations = {1, 5}},
|
||||
['Electrify'] = {id = 140, words = 'utori vis', exhaustion = 30000, premium = true, type = 'Instant', icon = 'electrify', mana = 30, level = 34, soul = 0, group = {[1] = 2000}, vocations = {1, 5}},
|
||||
['Energy Wave'] = {id = 13, words = 'exevo mort hur', exhaustion = 8000, premium = false, type = 'Instant', icon = 'energywave', mana = 170, level = 38, soul = 0, group = {[1] = 2000}, vocations = {1, 5}},
|
||||
['Rage of the Skies'] = {id = 119, words = 'exevo gran mas flam', exhaustion = 40000, premium = true, type = 'Instant', icon = 'rageoftheskies', mana = 600, level = 55, soul = 0, group = {[1] = 4000}, vocations = {1, 5}},
|
||||
['Fierce Berserk'] = {id = 105, words = 'exori gran', exhaustion = 6000, premium = true, type = 'Instant', icon = 'fierceberserk', mana = 340, level = 90, soul = 0, group = {[1] = 2000}, vocations = {4, 8}},
|
||||
['Hells Core'] = {id = 24, words = 'exevo gran mas vis', exhaustion = 40000, premium = true, type = 'Instant', icon = 'hellscore', mana = 1100, level = 60, soul = 0, group = {[1] = 4000}, vocations = {1, 5}},
|
||||
['Holy Flash'] = {id = 143, words = 'utori san', exhaustion = 40000, premium = true, type = 'Instant', icon = 'holyflash', mana = 30, level = 70, soul = 0, group = {[1] = 2000}, vocations = {3, 7}},
|
||||
['Divine Missile'] = {id = 122, words = 'exori san', exhaustion = 2000, premium = true, type = 'Instant', icon = 'divinemissile', mana = 20, level = 40, soul = 0, group = {[1] = 2000}, vocations = {3, 7}},
|
||||
['Divine Caldera'] = {id = 124, words = 'exevo mas san', exhaustion = 4000, premium = true, type = 'Instant', icon = 'divinecaldera', mana = 160, level = 50, soul = 0, group = {[1] = 2000}, vocations = {3, 7}},
|
||||
['Physical Strike'] = {id = 148, words = 'exori moe ico', exhaustion = 2000, premium = true, type = 'Instant', icon = 'physicalstrike', mana = 20, level = 16, soul = 0, group = {[1] = 2000}, vocations = {2, 6}},
|
||||
['Eternal Winter'] = {id = 118, words = 'exevo gran mas frigo', exhaustion = 40000, premium = true, type = 'Instant', icon = 'eternalwinter', mana = 1050, level = 60, soul = 0, group = {[1] = 4000}, vocations = {2, 6}},
|
||||
['Ice Strike'] = {id = 112, words = 'exori frigo', exhaustion = 2000, premium = true, type = 'Instant', icon = 'icestrike', mana = 20, level = 15, soul = 0, group = {[1] = 2000}, vocations = {1, 5, 2, 6}},
|
||||
['Strong Ice Strike'] = {id = 152, words = 'exori gran frigo', exhaustion = 8000, premium = true, type = 'Instant', icon = 'strongicestrike', mana = 60, level = 80, soul = 0, group = {[1] = 2000, [4] = 8000}, vocations = {2, 6}},
|
||||
['Ultimate Ice Strike'] = {id = 156, words = 'exori max frigo', exhaustion = 30000, premium = true, type = 'Instant', icon = 'ultimateicestrike', mana = 100, level = 100,soul = 0, group = {[1] = 4000}, vocations = {2, 6}},
|
||||
['Ice Wave'] = {id = 121, words = 'exevo frigo hur', exhaustion = 4000, premium = false, type = 'Instant', icon = 'icewave', mana = 25, level = 18, soul = 0, group = {[1] = 2000}, vocations = {2, 6}},
|
||||
['Strong Ice Wave'] = {id = 43, words = 'exevo gran frigo hur', exhaustion = 8000, premium = true, type = 'Instant', icon = 'strongicewave', mana = 170, level = 40, soul = 0, group = {[1] = 2000}, vocations = {2, 6}},
|
||||
['Envenom'] = {id = 142, words = 'utori pox', exhaustion = 40000, premium = true, type = 'Instant', icon = 'envenom', mana = 30, level = 50, soul = 0, group = {[1] = 2000}, vocations = {2, 6}},
|
||||
['Terra Strike'] = {id = 113, words = 'exori tera', exhaustion = 2000, premium = true, type = 'Instant', icon = 'terrastrike', mana = 20, level = 13, soul = 0, group = {[1] = 2000}, vocations = {1, 5, 2, 6}},
|
||||
['Strong Terra Strike'] = {id = 153, words = 'exori gran tera', exhaustion = 8000, premium = true, type = 'Instant', icon = 'strongterrastrike', mana = 60, level = 70, soul = 0, group = {[1] = 2000, [4] = 8000}, vocations = {2, 6}},
|
||||
['Ultimate Terra Strike'] = {id = 157, words = 'exori max tera', exhaustion = 30000, premium = true, type = 'Instant', icon = 'ultimateterrastrike', mana = 100, level = 90, soul = 0, group = {[1] = 4000}, vocations = {2, 6}},
|
||||
['Terra Wave'] = {id = 120, words = 'exevo tera hur', exhaustion = 4000, premium = false, type = 'Instant', icon = 'terrawave', mana = 210, level = 38, soul = 0, group = {[1] = 2000}, vocations = {2, 6}},
|
||||
['Wrath of Nature'] = {id = 56, words = 'exevo gran mas tera', exhaustion = 40000, premium = true, type = 'Instant', icon = 'wrathofnature', mana = 700, level = 55, soul = 0, group = {[1] = 4000}, vocations = {2, 6}},
|
||||
['Light Healing'] = {id = 1, words = 'exura', exhaustion = 1000, premium = false, type = 'Instant', icon = 'lighthealing', mana = 20, level = 9, soul = 0, group = {[2] = 1000}, vocations = {1, 2, 3, 5, 6, 7}},
|
||||
['Wound Cleansing'] = {id = 123, words = 'exura ico', exhaustion = 1000, premium = false, type = 'Instant', icon = 'woundcleansing', mana = 40, level = 10, soul = 0, group = {[2] = 1000}, vocations = {4, 8}},
|
||||
['Intense Wound Cleansing'] = {id = 158, words = 'exura gran ico', exhaustion = 600000,premium = true, type = 'Instant', icon = 'intensewoundcleansing', mana = 200, level = 80, soul = 0, group = {[2] = 1000}, vocations = {4, 8}},
|
||||
['Cure Bleeding'] = {id = 144, words = 'exana kor', exhaustion = 6000, premium = true, type = 'Instant', icon = 'curebleeding', mana = 30, level = 30, soul = 0, group = {[2] = 1000}, vocations = {4, 8}},
|
||||
['Cure Electrification'] = {id = 146, words = 'exana vis', exhaustion = 6000, premium = true, type = 'Instant', icon = 'curseelectrification', mana = 30, level = 22, soul = 0, group = {[2] = 1000}, vocations = {2, 6}},
|
||||
['Cure Poison'] = {id = 29, words = 'exana pox', exhaustion = 6000, premium = false, type = 'Instant', icon = 'curepoison', mana = 30, level = 10, soul = 0, group = {[2] = 1000}, vocations = {1, 2, 3, 4, 5, 6, 7, 8}},
|
||||
['Cure Burning'] = {id = 145, words = 'exana flam', exhaustion = 6000, premium = true, type = 'Instant', icon = 'cureburning', mana = 30, level = 30, soul = 0, group = {[2] = 1000}, vocations = {2, 6}},
|
||||
['Cure Curse'] = {id = 147, words = 'exana mort', exhaustion = 6000, premium = true, type = 'Instant', icon = 'curecurse', mana = 40, level = 80, soul = 0, group = {[2] = 1000}, vocations = {3, 7}},
|
||||
['Recovery'] = {id = 159, words = 'utura', exhaustion = 60000, premium = true, type = 'Instant', icon = 'recovery', mana = 75, level = 50, soul = 0, group = {[2] = 1000}, vocations = {4, 8, 3, 7}},
|
||||
['Intense Recovery'] = {id = 160, words = 'utura gran', exhaustion = 60000, premium = true, type = 'Instant', icon = 'intenserecovery', mana = 165, level = 100,soul = 0, group = {[2] = 1000}, vocations = {4, 8, 3, 7}},
|
||||
['Salvation'] = {id = 36, words = 'exura gran san', exhaustion = 1000, premium = true, type = 'Instant', icon = 'salvation', mana = 210, level = 60, soul = 0, group = {[2] = 1000}, vocations = {3, 7}},
|
||||
['Intense Healing'] = {id = 2, words = 'exura gran', exhaustion = 1000, premium = false, type = 'Instant', icon = 'intensehealing', mana = 70, level = 20, soul = 0, group = {[2] = 1000}, vocations = {1, 2, 3, 5, 6, 7}},
|
||||
['Heal Friend'] = {id = 84, words = 'exura sio', exhaustion = 1000, premium = true, type = 'Instant', icon = 'healfriend', mana = 140, level = 18, soul = 0, group = {[2] = 1000}, vocations = {2, 6}},
|
||||
['Ultimate Healing'] = {id = 3, words = 'exura vita', exhaustion = 1000, premium = false, type = 'Instant', icon = 'ultimatehealing', mana = 160, level = 30, soul = 0, group = {[2] = 1000}, vocations = {1, 2, 5, 6}},
|
||||
['Mass Healing'] = {id = 82, words = 'exura gran mas res', exhaustion = 2000, premium = true, type = 'Instant', icon = 'masshealing', mana = 150, level = 36, soul = 0, group = {[2] = 1000}, vocations = {2, 6}},
|
||||
['Divine Healing'] = {id = 125, words = 'exura san', exhaustion = 1000, premium = false, type = 'Instant', icon = 'divinehealing', mana = 160, level = 35, soul = 0, group = {[2] = 1000}, vocations = {3, 7}},
|
||||
['Light'] = {id = 10, words = 'utevo lux', exhaustion = 2000, premium = false, type = 'Instant', icon = 'light', mana = 20, level = 8, soul = 0, group = {[3] = 2000}, vocations = {1, 2, 3, 4, 5, 6, 7, 8}},
|
||||
['Find Person'] = {id = 20, words = 'exiva', exhaustion = 2000, premium = false, type = 'Instant', icon = 'findperson', mana = 20, level = 8, soul = 0, group = {[3] = 2000}, vocations = {1, 2, 3, 4, 5, 6, 7, 8}},
|
||||
['Magic Rope'] = {id = 76, words = 'exani tera', exhaustion = 2000, premium = true, type = 'Instant', icon = 'magicrope', mana = 20, level = 9, soul = 0, group = {[3] = 2000}, vocations = {1, 2, 3, 4, 5, 6, 7, 8}},
|
||||
['Levitate'] = {id = 81, words = 'exani hur', exhaustion = 2000, premium = true, type = 'Instant', icon = 'levitate', mana = 50, level = 12, soul = 0, group = {[3] = 2000}, vocations = {1, 2, 3, 4, 5, 6, 7, 8}},
|
||||
['Great Light'] = {id = 11, words = 'utevo gran lux', exhaustion = 2000, premium = false, type = 'Instant', icon = 'greatlight', mana = 60, level = 13, soul = 0, group = {[3] = 2000}, vocations = {1, 2, 3, 4, 5, 6, 7, 8}},
|
||||
['Magic Shield'] = {id = 44, words = 'utamo vita', exhaustion = 2000, premium = false, type = 'Instant', icon = 'magicshield', mana = 50, level = 14, soul = 0, group = {[3] = 2000}, vocations = {1, 2, 5, 6}},
|
||||
['Haste'] = {id = 6, words = 'utani hur', exhaustion = 2000, premium = true, type = 'Instant', icon = 'haste', mana = 60, level = 14, soul = 0, group = {[3] = 2000}, vocations = {1, 2, 3, 4, 5, 6, 7, 8}},
|
||||
['Charge'] = {id = 131, words = 'utani tempo hur', exhaustion = 2000, premium = true, type = 'Instant', icon = 'charge', mana = 100, level = 25, soul = 0, group = {[3] = 2000}, vocations = {4, 8}},
|
||||
['Swift Foot'] = {id = 134, words = 'utamo tempo san', exhaustion = 2000, premium = true, type = 'Instant', icon = 'swiftfoot', mana = 400, level = 55, soul = 0, group = {[1] = 10000, [3] = 2000}, vocations = {3, 7}},
|
||||
['Challenge'] = {id = 93, words = 'exeta res', exhaustion = 2000, premium = true, type = 'Instant', icon = 'challenge', mana = 30, level = 20, soul = 0, group = {[3] = 2000}, vocations = {8}},
|
||||
['Strong Haste'] = {id = 39, words = 'utani gran hur', exhaustion = 2000, premium = true, type = 'Instant', icon = 'stronghaste', mana = 100, level = 20, soul = 0, group = {[3] = 2000}, vocations = {1, 2, 5, 6}},
|
||||
['Creature Illusion'] = {id = 38, words = 'utevo res ina', exhaustion = 2000, premium = false, type = 'Instant', icon = 'creatureillusion', mana = 100, level = 23, soul = 0, group = {[3] = 2000}, vocations = {1, 2, 5, 6}},
|
||||
['Ultimate Light'] = {id = 75, words = 'utevo vis lux', exhaustion = 2000, premium = true, type = 'Instant', icon = 'ultimatelight', mana = 140, level = 26, soul = 0, group = {[3] = 2000}, vocations = {1, 2, 5, 6}},
|
||||
['Cancel Invisibility'] = {id = 90, words = 'exana ina', exhaustion = 2000, premium = true, type = 'Instant', icon = 'cancelinvisibility', mana = 200, level = 26, soul = 0, group = {[3] = 2000}, vocations = {3, 7}},
|
||||
['Invisibility'] = {id = 45, words = 'utana vid', exhaustion = 2000, premium = false, type = 'Instant', icon = 'invisible', mana = 440, level = 35, soul = 0, group = {[3] = 2000}, vocations = {1, 2, 5, 6}},
|
||||
['Sharpshooter'] = {id = 135, words = 'utito tempo san', exhaustion = 2000, premium = true, type = 'Instant', icon = 'sharpshooter', mana = 450, level = 60, soul = 0, group = {[2] = 10000, [3] = 10000}, vocations = {3, 7}},
|
||||
['Protector'] = {id = 132, words = 'utamo tempo', exhaustion = 2000, premium = true, type = 'Instant', icon = 'protector', mana = 200, level = 55, soul = 0, group = {[1] = 10000, [3] = 2000}, vocations = {4, 8}},
|
||||
['Blood Rage'] = {id = 133, words = 'utito tempo', exhaustion = 2000, premium = true, type = 'Instant', icon = 'bloodrage', mana = 290, level = 60, soul = 0, group = {[3] = 2000}, vocations = {4, 8}},
|
||||
['Train Party'] = {id = 126, words = 'utito mas sio', exhaustion = 2000, premium = true, type = 'Instant', icon = 'trainparty', mana = 'Var.', level = 32, soul = 0, group = {[3] = 2000}, vocations = {8}},
|
||||
['Protect Party'] = {id = 127, words = 'utamo mas sio', exhaustion = 2000, premium = true, type = 'Instant', icon = 'protectparty', mana = 'Var.', level = 32, soul = 0, group = {[3] = 2000}, vocations = {7}},
|
||||
['Heal Party'] = {id = 128, words = 'utura mas sio', exhaustion = 2000, premium = true, type = 'Instant', icon = 'healparty', mana = 'Var.', level = 32, soul = 0, group = {[3] = 2000}, vocations = {6}},
|
||||
['Enchant Party'] = {id = 129, words = 'utori mas sio', exhaustion = 2000, premium = true, type = 'Instant', icon = 'enchantparty', mana = 'Var.', level = 32, soul = 0, group = {[3] = 2000}, vocations = {5}},
|
||||
['Summon Creature'] = {id = 9, words = 'utevo res', exhaustion = 2000, premium = false, type = 'Instant', icon = 'summoncreature', mana = 'Var.', level = 25, soul = 0, group = {[3] = 2000}, vocations = {1, 2, 5, 6}},
|
||||
['Conjure Arrow'] = {id = 51, words = 'exevo con', exhaustion = 2000, premium = false, type = 'Conjure', icon = 'conjurearrow', mana = 100, level = 13, soul = 1, group = {[3] = 2000}, vocations = {3, 7}},
|
||||
['Food'] = {id = 42, words = 'exevo pan', exhaustion = 2000, premium = false, type = 'Instant', icon = 'food', mana = 120, level = 14, soul = 1, group = {[3] = 2000}, vocations = {2, 6}},
|
||||
['Conjure Poisoned Arrow'] = {id = 48, words = 'exevo con pox', exhaustion = 2000, premium = false, type = 'Conjure', icon = 'poisonedarrow', mana = 130, level = 16, soul = 2, group = {[3] = 2000}, vocations = {3, 7}},
|
||||
['Conjure Bolt'] = {id = 79, words = 'exevo con mort', exhaustion = 2000, premium = false, type = 'Conjure', icon = 'conjurebolt', mana = 140, level = 17, soul = 2, group = {[3] = 2000}, vocations = {3, 7}},
|
||||
['Conjure Sniper Arrow'] = {id = 108, words = 'exevo con hur', exhaustion = 2000, premium = false, type = 'Conjure', icon = 'sniperarrow', mana = 160, level = 24, soul = 3, group = {[3] = 2000}, vocations = {3, 7}},
|
||||
['Conjure Explosive Arrow'] = {id = 49, words = 'exevo con flam', exhaustion = 2000, premium = false, type = 'Conjure', icon = 'explosivearrow', mana = 290, level = 25, soul = 3, group = {[3] = 2000}, vocations = {3, 7}},
|
||||
['Conjure Piercing Bolt'] = {id = 109, words = 'exevo con grav', exhaustion = 2000, premium = false, type = 'Conjure', icon = 'piercingbolt', mana = 180, level = 33, soul = 3, group = {[3] = 2000}, vocations = {3, 7}},
|
||||
['Enchant Staff'] = {id = 92, words = 'exeta vis', exhaustion = 2000, premium = false, type = 'Conjure', icon = 'enchantstaff', mana = 80, level = 41, soul = 0, group = {[3] = 2000}, vocations = {5}},
|
||||
['Enchant Spear'] = {id = 110, words = 'exeta con', exhaustion = 2000, premium = false, type = 'Conjure', icon = 'enchantspear', mana = 350, level = 45, soul = 3, group = {[3] = 2000}, vocations = {3, 7}},
|
||||
['Conjure Power Bolt'] = {id = 95, words = 'exevo con vis', exhaustion = 2000, premium = false, type = 'Conjure', icon = 'powerbolt', mana = 800, level = 59, soul = 3, group = {[3] = 2000}, vocations = {7}},
|
||||
['Poison Field'] = {id = 26, words = 'adevo grav pox', exhaustion = 2000, premium = false, type = 'Conjure', icon = 'poisonfield', mana = 200, level = 14, soul = 1, group = {[3] = 2000}, vocations = {1, 2, 5, 6}},
|
||||
['Light Magic Missile'] = {id = 7, words = 'adori min vis', exhaustion = 2000, premium = false, type = 'Conjure', icon = 'lightmagicmissile', mana = 120, level = 15, soul = 1, group = {[3] = 2000}, vocations = {1, 2, 5, 6}},
|
||||
['Fire Field'] = {id = 25, words = 'adevo grav flam', exhaustion = 2000, premium = false, type = 'Conjure', icon = 'firefield', mana = 240, level = 15, soul = 1, group = {[3] = 2000}, vocations = {1, 2, 5, 6}},
|
||||
['Fireball'] = {id = 15, words = 'adori flam', exhaustion = 2000, premium = false, type = 'Conjure', icon = 'fireball', mana = 460, level = 27, soul = 3, group = {[3] = 2000}, vocations = {1, 5}},
|
||||
['Energy Field'] = {id = 27, words = 'adevo grav vis', exhaustion = 2000, premium = false, type = 'Conjure', icon = 'energyfield', mana = 320, level = 18, soul = 2, group = {[3] = 2000}, vocations = {1, 2, 5, 6}},
|
||||
['Stalagmite'] = {id = 77, words = 'adori tera', exhaustion = 2000, premium = false, type = 'Conjure', icon = 'stalagmite', mana = 400, level = 24, soul = 2, group = {[3] = 2000}, vocations = {1, 5, 2, 6}},
|
||||
['Great Fireball'] = {id = 16, words = 'adori mas flam', exhaustion = 2000, premium = false, type = 'Conjure', icon = 'greatfireball', mana = 530, level = 30, soul = 3, group = {[3] = 2000}, vocations = {1, 5}},
|
||||
['Heavy Magic Missile'] = {id = 8, words = 'adori vis', exhaustion = 2000, premium = false, type = 'Conjure', icon = 'heavymagicmissile', mana = 350, level = 25, soul = 2, group = {[3] = 2000}, vocations = {1, 5, 2, 6}},
|
||||
['Poison Bomb'] = {id = 91, words = 'adevo mas pox', exhaustion = 2000, premium = false, type = 'Conjure', icon = 'poisonbomb', mana = 520, level = 25, soul = 2, group = {[3] = 2000}, vocations = {2, 6}},
|
||||
['Firebomb'] = {id = 17, words = 'adevo mas flam', exhaustion = 2000, premium = false, type = 'Conjure', icon = 'firebomb', mana = 600, level = 27, soul = 3, group = {[3] = 2000}, vocations = {1, 2, 5, 6}},
|
||||
['Soulfire'] = {id = 50, words = 'adevo res flam', exhaustion = 2000, premium = false, type = 'Conjure', icon = 'soulfire', mana = 600, level = 27, soul = 3, group = {[3] = 2000}, vocations = {1, 2, 5, 6}},
|
||||
['Poison Wall'] = {id = 32, words = 'adevo mas grav pox', exhaustion = 2000, premium = false, type = 'Conjure', icon = 'poisonwall', mana = 640, level = 29, soul = 3, group = {[3] = 2000}, vocations = {1, 2, 5, 6}},
|
||||
['Explosion'] = {id = 18, words = 'adevo mas hur', exhaustion = 2000, premium = false, type = 'Conjure', icon = 'explosion', mana = 570, level = 31, soul = 3, group = {[3] = 2000}, vocations = {1, 2, 5, 6}},
|
||||
['Fire Wall'] = {id = 28, words = 'adevo mas grav flam', exhaustion = 2000, premium = false, type = 'Conjure', icon = 'firewall', mana = 780, level = 33, soul = 3, group = {[3] = 2000}, vocations = {1, 2, 5, 6}},
|
||||
['Energybomb'] = {id = 55, words = 'adevo mas vis', exhaustion = 2000, premium = false, type = 'Conjure', icon = 'energybomb', mana = 880, level = 37, soul = 5, group = {[3] = 2000}, vocations = {1, 5}},
|
||||
['Energy Wall'] = {id = 33, words = 'adevo mas grav vis', exhaustion = 2000, premium = false, type = 'Conjure', icon = 'energywall', mana = 1000, level = 41, soul = 5, group = {[3] = 2000}, vocations = {1, 2, 5, 6}},
|
||||
['Sudden Death'] = {id = 21, words = 'adori gran mort', exhaustion = 2000, premium = false, type = 'Conjure', icon = 'suddendeath', mana = 985, level = 45, soul = 5, group = {[3] = 2000}, vocations = {1, 5}},
|
||||
['Cure Poison Rune'] = {id = 31, words = 'adana pox', exhaustion = 2000, premium = false, type = 'Conjure', icon = 'antidote', mana = 200, level = 15, soul = 1, group = {[3] = 2000}, vocations = {2, 6}},
|
||||
['Intense Healing Rune'] = {id = 4, words = 'adura gran', exhaustion = 2000, premium = false, type = 'Conjure', icon = 'intensehealingrune', mana = 240, level = 15, soul = 2, group = {[3] = 2000}, vocations = {2, 6}},
|
||||
['Ultimate Healing Rune'] = {id = 5, words = 'adura vita', exhaustion = 2000, premium = false, type = 'Conjure', icon = 'ultimatehealingrune', mana = 400, level = 24, soul = 3, group = {[3] = 2000}, vocations = {2, 6}},
|
||||
['Convince Creature'] = {id = 12, words = 'adeta sio', exhaustion = 2000, premium = false, type = 'Conjure', icon = 'convincecreature', mana = 200, level = 16, soul = 3, group = {[3] = 2000}, vocations = {2, 6}},
|
||||
['Animate Dead'] = {id = 83, words = 'adana mort', exhaustion = 2000, premium = false, type = 'Conjure', icon = 'animatedead', mana = 600, level = 27, soul = 5, group = {[3] = 2000}, vocations = {1, 2, 5, 6}},
|
||||
['Chameleon'] = {id = 14, words = 'adevo ina', exhaustion = 2000, premium = false, type = 'Conjure', icon = 'chameleon', mana = 600, level = 27, soul = 2, group = {[3] = 2000}, vocations = {2, 6}},
|
||||
['Destroy Field'] = {id = 30, words = 'adito grav', exhaustion = 2000, premium = false, type = 'Conjure', icon = 'destroyfield', mana = 120, level = 17, soul = 2, group = {[3] = 2000}, vocations = {1, 2, 3, 5, 6, 7}},
|
||||
['Desintegrate'] = {id = 78, words = 'adito tera', exhaustion = 2000, premium = false, type = 'Conjure', icon = 'desintegrate', mana = 200, level = 21, soul = 3, group = {[3] = 2000}, vocations = {1, 2, 3, 5, 6, 7}},
|
||||
['Magic Wall'] = {id = 86, words = 'adevo grav tera', exhaustion = 2000, premium = false, type = 'Conjure', icon = 'magicwall', mana = 750, level = 32, soul = 5, group = {[3] = 2000}, vocations = {1, 5}},
|
||||
['Wild Growth'] = {id = 94, words = 'adevo grav vita', exhaustion = 2000, premium = false, type = 'Conjure', icon = 'wildgrowth', mana = 600, level = 27, soul = 5, group = {[3] = 2000}, vocations = {2, 6}},
|
||||
['Paralyze'] = {id = 54, words = 'adana ani', exhaustion = 2000, premium = false, type = 'Conjure', icon = 'paralyze', mana = 1400, level = 54, soul = 3, group = {[3] = 2000}, vocations = {2, 6}},
|
||||
['Icicle'] = {id = 114, words = 'adori frigo', exhaustion = 2000, premium = false, type = 'Conjure', icon = 'icicle', mana = 460, level = 28, soul = 3, group = {[3] = 2000}, vocations = {2, 6}},
|
||||
['Avalanche'] = {id = 115, words = 'adori mas frigo', exhaustion = 2000, premium = false, type = 'Conjure', icon = 'avalanche', mana = 530, level = 30, soul = 3, group = {[3] = 2000}, vocations = {2, 6}},
|
||||
['Stone Shower'] = {id = 116, words = 'adori mas tera', exhaustion = 2000, premium = false, type = 'Conjure', icon = 'stoneshower', mana = 430, level = 28, soul = 3, group = {[3] = 2000}, vocations = {2, 6}},
|
||||
['Thunderstorm'] = {id = 117, words = 'adori mas vis', exhaustion = 2000, premium = false, type = 'Conjure', icon = 'thunderstorm', mana = 430, level = 28, soul = 3, group = {[3] = 2000}, vocations = {1, 5}},
|
||||
['Holy Missile'] = {id = 130, words = 'adori san', exhaustion = 2000, premium = false, type = 'Conjure', icon = 'holymissile', mana = 350, level = 27, soul = 3, group = {[3] = 2000}, vocations = {3, 7}}
|
||||
}--[[,
|
||||
|
||||
['Sample'] = {
|
||||
['Wind Walk'] = {id = 1, words = 'windwalk', description = 'Run at enormous speed.', exhaustion = 2000, premium = false, type = 'Instant', icon = 1, mana = 50, level = 10, soul = 0, group = {[3] = 2000}, vocations = {1, 2}},
|
||||
['Fire Breath'] = {id = 2, words = 'firebreath', description = 'A strong firewave.', exhaustion = 2000, premium = false, type = 'Instant', icon = 2, mana = 350, level = 27, soul = 0, group = {[1] = 2000}, vocations = {4, 8}},
|
||||
['Moonglaives'] = {id = 3, words = 'moonglaives', description = 'Throw moonglaives around you.', exhaustion = 2000, premium = false, type = 'Instant', icon = 3, mana = 90, level = 55, soul = 0, group = {[1] = 2000}, vocations = {3, 7}},
|
||||
['Critical Strike'] = {id = 4, words = 'criticalstrike', description = 'Land a critical strike.', exhaustion = 2000, premium = false, type = 'Instant', icon = 4, mana = 350, level = 27, soul = 0, group = {[1] = 2000}, vocations = {3, 4, 7, 8}},
|
||||
['Firefly'] = {id = 5, words = 'firefly', description = 'Summon a angry firefly', exhaustion = 2000, premium = false, type = 'Instant', icon = 5, mana = 350, level = 27, soul = 0, group = {[1] = 2000}, vocations = {1, 2, 5, 6}}
|
||||
}]]
|
||||
}
|
||||
|
||||
-- ['const_name'] = {client_id, TFS_id}
|
||||
-- Conversion from TFS icon id to the id used by client (icons.png order)
|
||||
SpellIcons = {
|
||||
['intenserecovery'] = {16, 160},
|
||||
['recovery'] = {15, 159},
|
||||
['intensewoundcleansing'] = {4, 158},
|
||||
['ultimateterrastrike'] = {37, 157},
|
||||
['ultimateicestrike'] = {34, 156},
|
||||
['ultimateenergystrike'] = {31, 155},
|
||||
['ultimateflamestrike'] = {28, 154},
|
||||
['strongterrastrike'] = {36, 153},
|
||||
['strongicestrike'] = {33, 152},
|
||||
['strongenergystrike'] = {30, 151},
|
||||
['strongflamestrike'] = {27, 150},
|
||||
['lightning'] = {51, 149},
|
||||
['physicalstrike'] = {17, 148},
|
||||
['curecurse'] = {11, 147},
|
||||
['curseelectrification'] = {14, 146},
|
||||
['cureburning'] = {13, 145},
|
||||
['curebleeding'] = {12, 144},
|
||||
['holyflash'] = {53, 143},
|
||||
['envenom'] = {58, 142},
|
||||
['inflictwound'] = {57, 141},
|
||||
['electrify'] = {56, 140},
|
||||
['curse'] = {54, 139},
|
||||
['ignite'] = {55, 138},
|
||||
-- [[ 136 / 137 Unknown ]]
|
||||
['sharpshooter'] = {121, 135},
|
||||
['swiftfoot'] = {119, 134},
|
||||
['bloodrage'] = {96, 133},
|
||||
['protector'] = {122, 132},
|
||||
['charge'] = {98, 131},
|
||||
['holymissile'] = {76, 130},
|
||||
['enchantparty'] = {113, 129},
|
||||
['healparty'] = {126, 128},
|
||||
['protectparty'] = {123, 127},
|
||||
['trainparty'] = {120, 126},
|
||||
['divinehealing'] = {2, 125},
|
||||
['divinecaldera'] = {40, 124},
|
||||
['woundcleansing'] = {3, 123},
|
||||
['divinemissile'] = {39, 122},
|
||||
['icewave'] = {45, 121},
|
||||
['terrawave'] = {47, 120},
|
||||
['rageoftheskies'] = {52, 119},
|
||||
['eternalwinter'] = {50, 118},
|
||||
['thunderstorm'] = {63, 117},
|
||||
['stoneshower'] = {65, 116},
|
||||
['avalanche'] = {92, 115},
|
||||
['icicle'] = {75, 114},
|
||||
['terrastrike'] = {35, 113},
|
||||
['icestrike'] = {32, 112},
|
||||
['etherealspear'] = {18, 111},
|
||||
['enchantspear'] = {104, 110},
|
||||
['piercingbolt'] = {110, 109},
|
||||
['sniperarrow'] = {112, 108},
|
||||
['whirlwindthrow'] = {19, 107},
|
||||
['groundshaker'] = {25, 106},
|
||||
['fierceberserk'] = {22, 105},
|
||||
-- [[ 96 - 104 Unknown ]]
|
||||
['powerbolt'] = {108, 95},
|
||||
['wildgrowth'] = {61, 94},
|
||||
['challenge'] = {97, 93},
|
||||
['enchantstaff'] = {103, 92},
|
||||
['poisonbomb'] = {70, 91},
|
||||
['cancelinvisibility'] = {95, 90},
|
||||
['flamestrike'] = {26, 89},
|
||||
['energystrike'] = {29, 88},
|
||||
['deathstrike'] = {38, 87},
|
||||
['magicwall'] = {72, 86},
|
||||
['healfriend'] = {8, 84},
|
||||
['animatedead'] = {93, 83},
|
||||
['masshealing'] = {9, 82},
|
||||
['levitate'] = {125, 81},
|
||||
['berserk'] = {21, 80},
|
||||
['conjurebolt'] = {107, 79},
|
||||
['desintegrate'] = {88, 78},
|
||||
['stalagmite'] = {66, 77},
|
||||
['magicrope'] = {105, 76},
|
||||
['ultimatelight'] = {115, 75},
|
||||
-- [[ 71 - 64 TFS House Commands ]]
|
||||
-- [[ 63 - 70 Unknown ]]
|
||||
['annihilation'] = {24, 62},
|
||||
['brutalstrike'] = {23, 61},
|
||||
-- [[ 60 Unknown ]]
|
||||
['frontsweep'] = {20, 59},
|
||||
-- [[ 58 Unknown ]]
|
||||
['strongetherealspear'] = {59, 57},
|
||||
['wrathofnature'] = {48, 56},
|
||||
['energybomb'] = {86, 55},
|
||||
['paralyze'] = {71, 54},
|
||||
-- [[ 53 Unknown ]]
|
||||
-- [[ 52 TFS Retrieve Friend ]]
|
||||
['conjurearrow'] = {106, 51},
|
||||
['soulfire'] = {67, 50},
|
||||
['explosivearrow'] = {109, 49},
|
||||
['poisonedarrow'] = {111, 48},
|
||||
-- [[ 46 / 47 Unknown ]]
|
||||
['invisible'] = {94, 45},
|
||||
['magicshield'] = {124, 44},
|
||||
['strongicewave'] = {46, 43},
|
||||
['food'] = {99, 42},
|
||||
-- [[ 40 / 41 Unknown ]]
|
||||
['stronghaste'] = {102, 39},
|
||||
['creatureillusion'] = {100, 38},
|
||||
-- [[ 37 TFS Move ]]
|
||||
['salvation'] = {60, 36},
|
||||
-- [[ 34 / 35 Unknown ]]
|
||||
['energywall'] = {84, 33},
|
||||
['poisonwall'] = {68, 32},
|
||||
['antidote'] = {10, 31},
|
||||
['destroyfield'] = {87, 30},
|
||||
['curepoison'] = {10, 29},
|
||||
['firewall'] = {80, 28},
|
||||
['energyfield'] = {85, 27},
|
||||
['poisonfield'] = {69, 26},
|
||||
['firefield'] = {81, 25},
|
||||
['hellscore'] = {49, 24},
|
||||
['greatenergybeam'] = {42, 23},
|
||||
['energybeam'] = {41, 22},
|
||||
['suddendeath'] = {64, 21},
|
||||
['findperson'] = {114, 20},
|
||||
['firewave'] = {44, 19},
|
||||
['explosion'] = {83, 18},
|
||||
['firebomb'] = {82, 17},
|
||||
['greatfireball'] = {78, 16},
|
||||
['fireball'] = {79, 15},
|
||||
['chameleon'] = {91, 14},
|
||||
['energywave'] = {43, 13},
|
||||
['convincecreature'] = {90, 12},
|
||||
['greatlight'] = {116, 11},
|
||||
['light'] = {117, 10},
|
||||
['summoncreature'] = {118, 9},
|
||||
['heavymagicmissile'] = {77, 8},
|
||||
['lightmagicmissile'] = {73, 7},
|
||||
['haste'] = {101, 6},
|
||||
['ultimatehealingrune'] = {62, 5},
|
||||
['intensehealingrune'] = {74, 4},
|
||||
['ultimatehealing'] = {1, 3},
|
||||
['intensehealing'] = {7, 2},
|
||||
['lighthealing'] = {6, 1}
|
||||
}
|
||||
|
||||
VocationNames = {
|
||||
[1] = 'Sorcerer',
|
||||
[2] = 'Druid',
|
||||
[3] = 'Paladin',
|
||||
[4] = 'Knight',
|
||||
[5] = 'Master Sorcerer',
|
||||
[6] = 'Elder Druid',
|
||||
[7] = 'Royal Paladin',
|
||||
[8] = 'Elite Knight'
|
||||
}
|
||||
|
||||
SpellGroups = {
|
||||
[1] = 'Attack',
|
||||
[2] = 'Healing',
|
||||
[3] = 'Support',
|
||||
[4] = 'Special'
|
||||
}
|
||||
|
||||
Spells = {}
|
||||
|
||||
function Spells.getClientId(spellName)
|
||||
local profile = Spells.getSpellProfileByName(spellName)
|
||||
|
||||
local id = SpellInfo[profile][spellName].icon
|
||||
if not tonumber(id) and SpellIcons[id] then
|
||||
return SpellIcons[id][1]
|
||||
end
|
||||
return tonumber(id)
|
||||
end
|
||||
|
||||
function Spells.getServerId(spellName)
|
||||
local profile = Spells.getSpellProfileByName(spellName)
|
||||
|
||||
local id = SpellInfo[profile][spellName].icon
|
||||
if not tonumber(id) and SpellIcons[id] then
|
||||
return SpellIcons[id][2]
|
||||
end
|
||||
return tonumber(id)
|
||||
end
|
||||
|
||||
function Spells.getSpellByName(name)
|
||||
return SpellInfo[Spells.getSpellProfileByName(name)][name]
|
||||
end
|
||||
|
||||
function Spells.getSpellByWords(words)
|
||||
local words = words:lower():trim()
|
||||
for profile,data in pairs(SpellInfo) do
|
||||
for k,spell in pairs(data) do
|
||||
if spell.words == words then
|
||||
return spell, profile, k
|
||||
end
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
function Spells.getSpellByIcon(iconId)
|
||||
for profile,data in pairs(SpellInfo) do
|
||||
for k,spell in pairs(data) do
|
||||
if spell.id == iconId then
|
||||
return spell, profile, k
|
||||
end
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
function Spells.getSpellIconIds()
|
||||
local ids = {}
|
||||
for profile,data in pairs(SpellInfo) do
|
||||
for k,spell in pairs(data) do
|
||||
table.insert(ids, spell.id)
|
||||
end
|
||||
end
|
||||
return ids
|
||||
end
|
||||
|
||||
function Spells.getSpellProfileById(id)
|
||||
for profile,data in pairs(SpellInfo) do
|
||||
for k,spell in pairs(data) do
|
||||
if spell.id == id then
|
||||
return profile
|
||||
end
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
function Spells.getSpellProfileByWords(words)
|
||||
for profile,data in pairs(SpellInfo) do
|
||||
for k,spell in pairs(data) do
|
||||
if spell.words == words then
|
||||
return profile
|
||||
end
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
function Spells.getSpellProfileByName(spellName)
|
||||
for profile,data in pairs(SpellInfo) do
|
||||
if table.findbykey(data, spellName:trim(), true) then
|
||||
return profile
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
function Spells.getSpellsByVocationId(vocId)
|
||||
local spells = {}
|
||||
for profile,data in pairs(SpellInfo) do
|
||||
for k,spell in pairs(data) do
|
||||
if table.contains(spell.vocations, vocId) then
|
||||
table.insert(spells, spell)
|
||||
end
|
||||
end
|
||||
end
|
||||
return spells
|
||||
end
|
||||
|
||||
function Spells.filterSpellsByGroups(spells, groups)
|
||||
local filtered = {}
|
||||
for v,spell in pairs(spells) do
|
||||
local spellGroups = Spells.getGroupIds(spell)
|
||||
if table.equals(spellGroups, groups) then
|
||||
table.insert(filtered, spell)
|
||||
end
|
||||
end
|
||||
return filtered
|
||||
end
|
||||
|
||||
function Spells.getGroupIds(spell)
|
||||
local groups = {}
|
||||
for k,_ in pairs(spell.group) do
|
||||
table.insert(groups, k)
|
||||
end
|
||||
return groups
|
||||
end
|
||||
|
||||
function Spells.getImageClip(id, profile)
|
||||
return (((id-1)%12)*SpelllistSettings[profile].iconSize.width) .. ' '
|
||||
.. ((math.ceil(id/12)-1)*SpelllistSettings[profile].iconSize.height) .. ' '
|
||||
.. SpelllistSettings[profile].iconSize.width .. ' '
|
||||
.. SpelllistSettings[profile].iconSize.height
|
||||
end
|
30
SabrehavenOTClient/modules/gamelib/textmessages.lua
Normal file
30
SabrehavenOTClient/modules/gamelib/textmessages.lua
Normal file
@@ -0,0 +1,30 @@
|
||||
local messageModeCallbacks = {}
|
||||
|
||||
function g_game.onTextMessage(messageMode, message)
|
||||
local callbacks = messageModeCallbacks[messageMode]
|
||||
if not callbacks or #callbacks == 0 then
|
||||
perror(string.format('Unhandled onTextMessage message mode %i: %s', messageMode, message))
|
||||
return
|
||||
end
|
||||
|
||||
for _, callback in pairs(callbacks) do
|
||||
callback(messageMode, message)
|
||||
end
|
||||
end
|
||||
|
||||
function registerMessageMode(messageMode, callback)
|
||||
if not messageModeCallbacks[messageMode] then
|
||||
messageModeCallbacks[messageMode] = {}
|
||||
end
|
||||
|
||||
table.insert(messageModeCallbacks[messageMode], callback)
|
||||
return true
|
||||
end
|
||||
|
||||
function unregisterMessageMode(messageMode, callback)
|
||||
if not messageModeCallbacks[messageMode] then
|
||||
return false
|
||||
end
|
||||
|
||||
return table.removevalue(messageModeCallbacks[messageMode], callback)
|
||||
end
|
49
SabrehavenOTClient/modules/gamelib/thing.lua
Normal file
49
SabrehavenOTClient/modules/gamelib/thing.lua
Normal file
@@ -0,0 +1,49 @@
|
||||
ThingCategoryItem = 0
|
||||
ThingCategoryCreature = 1
|
||||
ThingCategoryEffect = 2
|
||||
ThingCategoryMissile = 3
|
||||
ThingInvalidCategory = 4
|
||||
ThingLastCategory = ThingInvalidCategory
|
||||
|
||||
ThingAttrGround = 0
|
||||
ThingAttrGroundBorder = 1
|
||||
ThingAttrOnBottom = 2
|
||||
ThingAttrOnTop = 3
|
||||
ThingAttrContainer = 4
|
||||
ThingAttrStackable = 5
|
||||
ThingAttrForceUse = 6
|
||||
ThingAttrMultiUse = 7
|
||||
ThingAttrWritable = 8
|
||||
ThingAttrWritableOnce = 9
|
||||
ThingAttrFluidContainer = 10
|
||||
ThingAttrSplash = 11
|
||||
ThingAttrNotWalkable = 12
|
||||
ThingAttrNotMoveable = 13
|
||||
ThingAttrBlockProjectile = 14
|
||||
ThingAttrNotPathable = 15
|
||||
ThingAttrPickupable = 16
|
||||
ThingAttrHangable = 17
|
||||
ThingAttrHookSouth = 18
|
||||
ThingAttrHookEast = 19
|
||||
ThingAttrRotateable = 20
|
||||
ThingAttrLight = 21
|
||||
ThingAttrDontHide = 22
|
||||
ThingAttrTranslucent = 23
|
||||
ThingAttrDisplacement = 24
|
||||
ThingAttrElevation = 25
|
||||
ThingAttrLyingCorpse = 26
|
||||
ThingAttrAnimateAlways = 27
|
||||
ThingAttrMinimapColor = 28
|
||||
ThingAttrLensHelp = 29
|
||||
ThingAttrFullGround = 30
|
||||
ThingAttrLook = 31
|
||||
ThingAttrCloth = 32
|
||||
ThingAttrMarket = 33
|
||||
ThingAttrNoMoveAnimation = 253 -- >= 1010, value = 16
|
||||
ThingAttrChargeable = 254 -- deprecated
|
||||
ThingLastAttr = 255
|
||||
|
||||
SpriteMaskRed = 1
|
||||
SpriteMaskGreen = 2
|
||||
SpriteMaskBlue = 3
|
||||
SpriteMaskYellow = 4
|
156
SabrehavenOTClient/modules/gamelib/ui/uicreaturebutton.lua
Normal file
156
SabrehavenOTClient/modules/gamelib/ui/uicreaturebutton.lua
Normal file
@@ -0,0 +1,156 @@
|
||||
-- @docclass
|
||||
UICreatureButton = extends(UIWidget, "UICreatureButton")
|
||||
|
||||
local CreatureButtonColors = {
|
||||
onIdle = {notHovered = '#888888', hovered = '#FFFFFF' },
|
||||
onTargeted = {notHovered = '#FF0000', hovered = '#FF8888' },
|
||||
onFollowed = {notHovered = '#00FF00', hovered = '#88FF88' }
|
||||
}
|
||||
|
||||
local LifeBarColors = {} -- Must be sorted by percentAbove
|
||||
table.insert(LifeBarColors, {percentAbove = 92, color = '#00BC00' } )
|
||||
table.insert(LifeBarColors, {percentAbove = 60, color = '#50A150' } )
|
||||
table.insert(LifeBarColors, {percentAbove = 30, color = '#A1A100' } )
|
||||
table.insert(LifeBarColors, {percentAbove = 8, color = '#BF0A0A' } )
|
||||
table.insert(LifeBarColors, {percentAbove = 3, color = '#910F0F' } )
|
||||
table.insert(LifeBarColors, {percentAbove = -1, color = '#850C0C' } )
|
||||
|
||||
function UICreatureButton.create()
|
||||
local button = UICreatureButton.internalCreate()
|
||||
button:setFocusable(false)
|
||||
button.creature = nil
|
||||
button.isHovered = false
|
||||
return button
|
||||
end
|
||||
|
||||
function UICreatureButton:setCreature(creature)
|
||||
self.creature = creature
|
||||
end
|
||||
|
||||
function UICreatureButton:getCreature()
|
||||
return self.creature
|
||||
end
|
||||
|
||||
function UICreatureButton:getCreatureId()
|
||||
return self.creature:getId()
|
||||
end
|
||||
|
||||
function UICreatureButton:setup(id)
|
||||
self.lifeBarWidget = self:getChildById('lifeBar')
|
||||
self.creatureWidget = self:getChildById('creature')
|
||||
self.labelWidget = self:getChildById('label')
|
||||
self.skullWidget = self:getChildById('skull')
|
||||
self.emblemWidget = self:getChildById('emblem')
|
||||
end
|
||||
|
||||
function UICreatureButton:update()
|
||||
local color = CreatureButtonColors.onIdle
|
||||
local show = false
|
||||
if self.creature == g_game.getAttackingCreature() then
|
||||
color = CreatureButtonColors.onTargeted
|
||||
elseif self.creature == g_game.getFollowingCreature() then
|
||||
color = CreatureButtonColors.onFollowed
|
||||
end
|
||||
color = self.isHovered and color.hovered or color.notHovered
|
||||
|
||||
if self.color == color then
|
||||
return
|
||||
end
|
||||
self.color = color
|
||||
|
||||
if color ~= CreatureButtonColors.onIdle.notHovered then
|
||||
self.creatureWidget:setBorderWidth(1)
|
||||
self.creatureWidget:setBorderColor(color)
|
||||
self.labelWidget:setColor(color)
|
||||
else
|
||||
self.creatureWidget:setBorderWidth(0)
|
||||
self.labelWidget:setColor(color)
|
||||
end
|
||||
end
|
||||
|
||||
function UICreatureButton:creatureSetup(creature)
|
||||
if self.creature ~= creature then
|
||||
self.creature = creature
|
||||
self.creatureWidget:setCreature(creature)
|
||||
if self.creatureName ~= creature:getName() then
|
||||
self.creatureName = creature:getName()
|
||||
self.labelWidget:setText(creature:getName())
|
||||
end
|
||||
end
|
||||
|
||||
self:updateLifeBarPercent()
|
||||
self:updateSkull()
|
||||
self:updateEmblem()
|
||||
self:update()
|
||||
end
|
||||
|
||||
function UICreatureButton:updateSkull()
|
||||
if not self.creature then
|
||||
return
|
||||
end
|
||||
local skullId = self.creature:getSkull()
|
||||
if skullId == self.skullId then
|
||||
return
|
||||
end
|
||||
self.skullId = skullId
|
||||
|
||||
if skullId ~= SkullNone then
|
||||
self.skullWidget:setWidth(self.skullWidget:getHeight())
|
||||
local imagePath = getSkullImagePath(skullId)
|
||||
self.skullWidget:setImageSource(imagePath)
|
||||
self.labelWidget:setMarginLeft(5)
|
||||
else
|
||||
self.skullWidget:setWidth(0)
|
||||
if self.creature:getEmblem() == EmblemNone then
|
||||
self.labelWidget:setMarginLeft(2)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function UICreatureButton:updateEmblem()
|
||||
if not self.creature then
|
||||
return
|
||||
end
|
||||
local emblemId = self.creature:getEmblem()
|
||||
if self.emblemId == emblemId then
|
||||
return
|
||||
end
|
||||
self.emblemId = emblemId
|
||||
|
||||
if emblemId ~= EmblemNone then
|
||||
self.emblemWidget:setWidth(self.emblemWidget:getHeight())
|
||||
local imagePath = getEmblemImagePath(emblemId)
|
||||
self.emblemWidget:setImageSource(imagePath)
|
||||
self.emblemWidget:setMarginLeft(5)
|
||||
self.labelWidget:setMarginLeft(5)
|
||||
else
|
||||
self.emblemWidget:setWidth(0)
|
||||
self.emblemWidget:setMarginLeft(0)
|
||||
if self.creature:getSkull() == SkullNone then
|
||||
self.labelWidget:setMarginLeft(2)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function UICreatureButton:updateLifeBarPercent()
|
||||
if not self.creature then
|
||||
return
|
||||
end
|
||||
local percent = self.creature:getHealthPercent()
|
||||
if self.percent == percent then
|
||||
return
|
||||
end
|
||||
|
||||
self.percent = percent
|
||||
self.lifeBarWidget:setPercent(percent)
|
||||
|
||||
local color
|
||||
for i, v in pairs(LifeBarColors) do
|
||||
if percent > v.percentAbove then
|
||||
color = v.color
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
self.lifeBarWidget:setBackgroundColor(color)
|
||||
end
|
137
SabrehavenOTClient/modules/gamelib/ui/uiitem.lua
Normal file
137
SabrehavenOTClient/modules/gamelib/ui/uiitem.lua
Normal file
@@ -0,0 +1,137 @@
|
||||
function UIItem:onDragEnter(mousePos)
|
||||
if self:isVirtual() then return false end
|
||||
|
||||
local item = self:getItem()
|
||||
if not item then return false end
|
||||
|
||||
self:setBorderWidth(1)
|
||||
self.currentDragThing = item
|
||||
g_mouse.pushCursor('target')
|
||||
return true
|
||||
end
|
||||
|
||||
function UIItem:onDragLeave(droppedWidget, mousePos)
|
||||
if self:isVirtual() then return false end
|
||||
self.currentDragThing = nil
|
||||
g_mouse.popCursor('target')
|
||||
self:setBorderWidth(0)
|
||||
self.hoveredWho = nil
|
||||
return true
|
||||
end
|
||||
|
||||
function UIItem:onDrop(widget, mousePos, forced)
|
||||
if not self:canAcceptDrop(widget, mousePos) and not forced then return false end
|
||||
|
||||
local item = widget.currentDragThing
|
||||
if not item or not item:isItem() then return false end
|
||||
|
||||
if self.selectable then
|
||||
if item:isPickupable() then
|
||||
self:setItem(Item.create(item:getId(), item:getCountOrSubType()))
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
local toPos = self.position
|
||||
|
||||
local itemPos = item:getPosition()
|
||||
if itemPos.x == toPos.x and itemPos.y == toPos.y and itemPos.z == toPos.z then return false end
|
||||
|
||||
if item:getCount() > 1 then
|
||||
modules.game_interface.moveStackableItem(item, toPos)
|
||||
else
|
||||
g_game.move(item, toPos, 1)
|
||||
end
|
||||
|
||||
self:setBorderWidth(0)
|
||||
return true
|
||||
end
|
||||
|
||||
function UIItem:onDestroy()
|
||||
if self == g_ui.getDraggingWidget() and self.hoveredWho then
|
||||
self.hoveredWho:setBorderWidth(0)
|
||||
end
|
||||
|
||||
if self.hoveredWho then
|
||||
self.hoveredWho = nil
|
||||
end
|
||||
end
|
||||
|
||||
function UIItem:onHoverChange(hovered)
|
||||
UIWidget.onHoverChange(self, hovered)
|
||||
|
||||
if self:isVirtual() or not self:isDraggable() then return end
|
||||
|
||||
local draggingWidget = g_ui.getDraggingWidget()
|
||||
if draggingWidget and self ~= draggingWidget then
|
||||
local gotMap = draggingWidget:getClassName() == 'UIGameMap'
|
||||
local gotItem = draggingWidget:getClassName() == 'UIItem' and not draggingWidget:isVirtual()
|
||||
if hovered and (gotItem or gotMap) then
|
||||
self:setBorderWidth(1)
|
||||
draggingWidget.hoveredWho = self
|
||||
else
|
||||
self:setBorderWidth(0)
|
||||
draggingWidget.hoveredWho = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function UIItem:onMouseRelease(mousePosition, mouseButton)
|
||||
if self.cancelNextRelease then
|
||||
self.cancelNextRelease = false
|
||||
return true
|
||||
end
|
||||
|
||||
if self:isVirtual() then return false end
|
||||
|
||||
local item = self:getItem()
|
||||
if not item or not self:containsPoint(mousePosition) then return false end
|
||||
|
||||
if modules.client_options.getOption('classicControl') and not g_app.isMobile() and
|
||||
((g_mouse.isPressed(MouseLeftButton) and mouseButton == MouseRightButton) or
|
||||
(g_mouse.isPressed(MouseRightButton) and mouseButton == MouseLeftButton)) then
|
||||
g_game.look(item)
|
||||
self.cancelNextRelease = true
|
||||
return true
|
||||
elseif modules.game_interface.processMouseAction(mousePosition, mouseButton, nil, item, item, nil, nil) then
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function UIItem:canAcceptDrop(widget, mousePos)
|
||||
if not self.selectable and (self:isVirtual() or not self:isDraggable()) then return false end
|
||||
if not widget or not widget.currentDragThing then return false end
|
||||
|
||||
local children = rootWidget:recursiveGetChildrenByPos(mousePos)
|
||||
for i=1,#children do
|
||||
local child = children[i]
|
||||
if child == self then
|
||||
return true
|
||||
elseif not child:isPhantom() then
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
error('Widget ' .. self:getId() .. ' not in drop list.')
|
||||
return false
|
||||
end
|
||||
|
||||
function UIItem:onClick(mousePos)
|
||||
if not self.selectable or not self.editable then
|
||||
return
|
||||
end
|
||||
|
||||
if modules.game_itemselector then
|
||||
modules.game_itemselector.show(self)
|
||||
end
|
||||
end
|
||||
|
||||
function UIItem:onItemChange()
|
||||
local tooltip = nil
|
||||
if self:getItem() and self:getItem():getTooltip():len() > 0 then
|
||||
tooltip = self:getItem():getTooltip()
|
||||
end
|
||||
self:setTooltip(tooltip)
|
||||
end
|
316
SabrehavenOTClient/modules/gamelib/ui/uiminimap.lua
Normal file
316
SabrehavenOTClient/modules/gamelib/ui/uiminimap.lua
Normal file
@@ -0,0 +1,316 @@
|
||||
function UIMinimap:onCreate()
|
||||
self.autowalk = true
|
||||
end
|
||||
|
||||
function UIMinimap:onSetup()
|
||||
self.flagWindow = nil
|
||||
self.flags = {}
|
||||
self.alternatives = {}
|
||||
self.onAddAutomapFlag = function(pos, icon, description) self:addFlag(pos, icon, description) end
|
||||
self.onRemoveAutomapFlag = function(pos, icon, description) self:removeFlag(pos, icon, description) end
|
||||
connect(g_game, {
|
||||
onAddAutomapFlag = self.onAddAutomapFlag,
|
||||
onRemoveAutomapFlag = self.onRemoveAutomapFlag,
|
||||
})
|
||||
end
|
||||
|
||||
function UIMinimap:onDestroy()
|
||||
for _,widget in pairs(self.alternatives) do
|
||||
widget:destroy()
|
||||
end
|
||||
self.alternatives = {}
|
||||
disconnect(g_game, {
|
||||
onAddAutomapFlag = self.onAddAutomapFlag,
|
||||
onRemoveAutomapFlag = self.onRemoveAutomapFlag,
|
||||
})
|
||||
self:destroyFlagWindow()
|
||||
self.flags = {}
|
||||
end
|
||||
|
||||
function UIMinimap:onVisibilityChange()
|
||||
if not self:isVisible() then
|
||||
self:destroyFlagWindow()
|
||||
end
|
||||
end
|
||||
|
||||
function UIMinimap:onCameraPositionChange(cameraPos)
|
||||
if self.cross then
|
||||
self:setCrossPosition(self.cross.pos)
|
||||
end
|
||||
end
|
||||
|
||||
function UIMinimap:hideFloor()
|
||||
self.floorUpWidget:hide()
|
||||
self.floorDownWidget:hide()
|
||||
end
|
||||
|
||||
function UIMinimap:hideZoom()
|
||||
self.zoomInWidget:hide()
|
||||
self.zoomOutWidget:hide()
|
||||
end
|
||||
|
||||
function UIMinimap:disableAutoWalk()
|
||||
self.autowalk = false
|
||||
end
|
||||
|
||||
function UIMinimap:load()
|
||||
local settings = g_settings.getNode('Minimap')
|
||||
if settings then
|
||||
if settings.flags then
|
||||
for _,flag in pairs(settings.flags) do
|
||||
self:addFlag(flag.position, flag.icon, flag.description)
|
||||
end
|
||||
end
|
||||
self:setZoom(settings.zoom)
|
||||
end
|
||||
end
|
||||
|
||||
function UIMinimap:save()
|
||||
local settings = { flags={} }
|
||||
for _,flag in pairs(self.flags) do
|
||||
if not flag.temporary then
|
||||
table.insert(settings.flags, {
|
||||
position = flag.pos,
|
||||
icon = flag.icon,
|
||||
description = flag.description,
|
||||
})
|
||||
end
|
||||
end
|
||||
settings.zoom = self:getZoom()
|
||||
g_settings.setNode('Minimap', settings)
|
||||
end
|
||||
|
||||
local function onFlagMouseRelease(widget, pos, button)
|
||||
if button == MouseRightButton then
|
||||
local menu = g_ui.createWidget('PopupMenu')
|
||||
menu:setGameMenu(true)
|
||||
menu:addOption(tr('Delete mark'), function() widget:destroy() end)
|
||||
menu:display(pos)
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function UIMinimap:setCrossPosition(pos)
|
||||
local cross = self.cross
|
||||
if not self.cross then
|
||||
cross = g_ui.createWidget('MinimapCross', self)
|
||||
cross:setIcon('/images/game/minimap/cross')
|
||||
self.cross = cross
|
||||
end
|
||||
|
||||
pos.z = self:getCameraPosition().z
|
||||
cross.pos = pos
|
||||
if pos then
|
||||
self:centerInPosition(cross, pos)
|
||||
else
|
||||
cross:breakAnchors()
|
||||
end
|
||||
end
|
||||
|
||||
function UIMinimap:addFlag(pos, icon, description, temporary)
|
||||
if not pos or not icon then return end
|
||||
local flag = self:getFlag(pos, icon, description)
|
||||
if flag or not icon then
|
||||
return
|
||||
end
|
||||
temporary = temporary or false
|
||||
|
||||
flag = g_ui.createWidget('MinimapFlag')
|
||||
self:insertChild(1, flag)
|
||||
flag.pos = pos
|
||||
flag.description = description
|
||||
flag.icon = icon
|
||||
flag.temporary = temporary
|
||||
if type(tonumber(icon)) == 'number' then
|
||||
flag:setIcon('/images/game/minimap/flag' .. icon)
|
||||
else
|
||||
flag:setIcon(resolvepath(icon, 1))
|
||||
end
|
||||
flag:setTooltip(description)
|
||||
flag.onMouseRelease = onFlagMouseRelease
|
||||
flag.onDestroy = function() table.removevalue(self.flags, flag) end
|
||||
table.insert(self.flags, flag)
|
||||
self:centerInPosition(flag, pos)
|
||||
end
|
||||
|
||||
function UIMinimap:addAlternativeWidget(widget, pos, maxZoom)
|
||||
widget.pos = pos
|
||||
widget.maxZoom = maxZoom or 0
|
||||
widget.minZoom = minZoom
|
||||
table.insert(self.alternatives, widget)
|
||||
end
|
||||
|
||||
function UIMinimap:setAlternativeWidgetsVisible(show)
|
||||
local layout = self:getLayout()
|
||||
layout:disableUpdates()
|
||||
for _,widget in pairs(self.alternatives) do
|
||||
if show then
|
||||
self:insertChild(1, widget)
|
||||
self:centerInPosition(widget, widget.pos)
|
||||
else
|
||||
self:removeChild(widget)
|
||||
end
|
||||
end
|
||||
layout:enableUpdates()
|
||||
layout:update()
|
||||
end
|
||||
|
||||
function UIMinimap:onZoomChange(zoom)
|
||||
for _,widget in pairs(self.alternatives) do
|
||||
if (not widget.minZoom or widget.minZoom >= zoom) and widget.maxZoom <= zoom then
|
||||
widget:show()
|
||||
else
|
||||
widget:hide()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function UIMinimap:getFlag(pos)
|
||||
for _,flag in pairs(self.flags) do
|
||||
if flag.pos.x == pos.x and flag.pos.y == pos.y and flag.pos.z == pos.z then
|
||||
return flag
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
function UIMinimap:removeFlag(pos, icon, description)
|
||||
local flag = self:getFlag(pos)
|
||||
if flag then
|
||||
flag:destroy()
|
||||
end
|
||||
end
|
||||
|
||||
function UIMinimap:reset()
|
||||
self:setZoom(0)
|
||||
if self.cross then
|
||||
self:setCameraPosition(self.cross.pos)
|
||||
end
|
||||
end
|
||||
|
||||
function UIMinimap:move(x, y)
|
||||
local cameraPos = self:getCameraPosition()
|
||||
local scale = self:getScale()
|
||||
if scale > 1 then scale = 1 end
|
||||
local dx = x/scale
|
||||
local dy = y/scale
|
||||
local pos = {x = cameraPos.x - dx, y = cameraPos.y - dy, z = cameraPos.z}
|
||||
self:setCameraPosition(pos)
|
||||
end
|
||||
|
||||
function UIMinimap:onMouseWheel(mousePos, direction)
|
||||
local keyboardModifiers = g_keyboard.getModifiers()
|
||||
if direction == MouseWheelUp and keyboardModifiers == KeyboardNoModifier then
|
||||
self:zoomIn()
|
||||
elseif direction == MouseWheelDown and keyboardModifiers == KeyboardNoModifier then
|
||||
self:zoomOut()
|
||||
elseif direction == MouseWheelDown and keyboardModifiers == KeyboardCtrlModifier then
|
||||
self:floorUp(1)
|
||||
elseif direction == MouseWheelUp and keyboardModifiers == KeyboardCtrlModifier then
|
||||
self:floorDown(1)
|
||||
end
|
||||
end
|
||||
|
||||
function UIMinimap:onMousePress(pos, button)
|
||||
if not self:isDragging() then
|
||||
self.allowNextRelease = true
|
||||
end
|
||||
end
|
||||
|
||||
function UIMinimap:onMouseRelease(pos, button)
|
||||
if not self.allowNextRelease then return true end
|
||||
self.allowNextRelease = false
|
||||
|
||||
local mapPos = self:getTilePosition(pos)
|
||||
if not mapPos then return end
|
||||
|
||||
if button == MouseLeftButton then
|
||||
local player = g_game.getLocalPlayer()
|
||||
if self.autowalk then
|
||||
player:autoWalk(mapPos)
|
||||
end
|
||||
return true
|
||||
elseif button == MouseRightButton then
|
||||
local menu = g_ui.createWidget('PopupMenu')
|
||||
menu:setGameMenu(true)
|
||||
menu:addOption(tr('Create mark'), function() self:createFlagWindow(mapPos) end)
|
||||
menu:display(pos)
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function UIMinimap:onDragEnter(pos)
|
||||
self.dragReference = pos
|
||||
self.dragCameraReference = self:getCameraPosition()
|
||||
return true
|
||||
end
|
||||
|
||||
function UIMinimap:onDragMove(pos, moved)
|
||||
local scale = self:getScale()
|
||||
local dx = (self.dragReference.x - pos.x)/scale
|
||||
local dy = (self.dragReference.y - pos.y)/scale
|
||||
local pos = {x = self.dragCameraReference.x + dx, y = self.dragCameraReference.y + dy, z = self.dragCameraReference.z}
|
||||
self:setCameraPosition(pos)
|
||||
return true
|
||||
end
|
||||
|
||||
function UIMinimap:onDragLeave(widget, pos)
|
||||
return true
|
||||
end
|
||||
|
||||
function UIMinimap:onStyleApply(styleName, styleNode)
|
||||
for name,value in pairs(styleNode) do
|
||||
if name == 'autowalk' then
|
||||
self.autowalk = value
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function UIMinimap:createFlagWindow(pos)
|
||||
if self.flagWindow then return end
|
||||
if not pos then return end
|
||||
|
||||
self.flagWindow = g_ui.createWidget('MinimapFlagWindow', rootWidget)
|
||||
|
||||
local positionLabel = self.flagWindow:getChildById('position')
|
||||
local description = self.flagWindow:getChildById('description')
|
||||
local okButton = self.flagWindow:getChildById('okButton')
|
||||
local cancelButton = self.flagWindow:getChildById('cancelButton')
|
||||
|
||||
positionLabel:setText(string.format('%i, %i, %i', pos.x, pos.y, pos.z))
|
||||
|
||||
local flagRadioGroup = UIRadioGroup.create()
|
||||
for i=0,19 do
|
||||
local checkbox = self.flagWindow:getChildById('flag' .. i)
|
||||
checkbox.icon = i
|
||||
flagRadioGroup:addWidget(checkbox)
|
||||
end
|
||||
|
||||
flagRadioGroup:selectWidget(flagRadioGroup:getFirstWidget())
|
||||
|
||||
local successFunc = function()
|
||||
self:addFlag(pos, flagRadioGroup:getSelectedWidget().icon, description:getText())
|
||||
self:destroyFlagWindow()
|
||||
end
|
||||
|
||||
local cancelFunc = function()
|
||||
self:destroyFlagWindow()
|
||||
end
|
||||
|
||||
okButton.onClick = successFunc
|
||||
cancelButton.onClick = cancelFunc
|
||||
|
||||
self.flagWindow.onEnter = successFunc
|
||||
self.flagWindow.onEscape = cancelFunc
|
||||
|
||||
self.flagWindow.onDestroy = function() flagRadioGroup:destroy() end
|
||||
end
|
||||
|
||||
function UIMinimap:destroyFlagWindow()
|
||||
if self.flagWindow then
|
||||
self.flagWindow:destroy()
|
||||
self.flagWindow = nil
|
||||
end
|
||||
end
|
11
SabrehavenOTClient/modules/gamelib/util.lua
Normal file
11
SabrehavenOTClient/modules/gamelib/util.lua
Normal file
@@ -0,0 +1,11 @@
|
||||
function postostring(pos)
|
||||
return pos.x .. " " .. pos.y .. " " .. pos.z
|
||||
end
|
||||
|
||||
function dirtostring(dir)
|
||||
for k,v in pairs(Directions) do
|
||||
if v == dir then
|
||||
return k
|
||||
end
|
||||
end
|
||||
end
|
Reference in New Issue
Block a user