2 Commits

Author SHA1 Message Date
Jan Nedbal
b5d046c91a docs: link make-release.sh from update-manager doc 2026-04-14 13:52:27 +02:00
Jan Nedbal
c2804fe1a6 scripts: add make-release.sh 2026-04-14 13:52:23 +02:00
60 changed files with 1558 additions and 2148 deletions

View File

@@ -0,0 +1,524 @@
BoundingSphereRadius 0.000000
BoundingSpherePosition 0.000000 0.000000 0.000000
Group Particle
{
StartTime 0.176000
List TimeEventPosition
{
0.000000 "MOVING_TYPE_DIRECT" 0.000000 0.000000 0.000000
}
StaticRotation 0.000000 0.000000 0.000000
Group EmitterProperty
{
MaxEmissionCount 1
CycleLength 10.000000
CycleLoopEnable 1
LoopCount 3
EmitterShape 0
EmitterAdvancedType 0
EmitterEmitFromEdgeFlag 0
EmittingDirection 0.080000 0.000000 0.000000
List TimeEventEmittingSize
{
0.000000 0.000000
}
List TimeEventEmittingAngularVelocity
{
0.000000 0.000000
}
List TimeEventEmittingDirectionX
{
0.000000 5.000000
}
List TimeEventEmittingDirectionY
{
0.000000 -50.000000
0.100000 -20.000000
}
List TimeEventEmittingDirectionZ
{
0.000000 10.000000
0.050000 100.000000
0.100000 1000.000000
}
List TimeEventEmittingVelocity
{
0.000000 15.000000
}
List TimeEventEmissionCountPerSecond
{
0.000000 1000.000000
}
List TimeEventLifeTime
{
0.000000 10.000000
}
List TimeEventSizeX
{
0.000000 60.000000
}
List TimeEventSizeY
{
0.000000 36.000000
}
}
Group ParticleProperty
{
SrcBlendType 5
DestBlendType 6
ColorOperationType 4
BillboardType 1
RotationType 0
RotationSpeed 0.000000
RotationRandomStartingBegin 0
RotationRandomStartingEnd 0
AttachEnable 1
StretchEnable 0
TexAniType 1
TexAniDelay 0.040000
TexAniRandomStartFrameEnable 0
EnableFrustum 0
List TimeEventGravity
{
}
List TimeEventAirResistance
{
}
List TimeEventScaleX
{
0.000000 1.000000
}
List TimeEventScaleY
{
0.000000 1.000000
}
List TimeEventColorRed
{
0.000000 1.000000
}
List TimeEventColorGreen
{
0.000000 1.000000
}
List TimeEventColorBlue
{
0.000000 1.000000
}
List TimeEventAlpha
{
0.000000 1.000000
0.737143 1.000000
1.000000 0.000000
}
List TimeEventRotation
{
0.000000 0.000000
}
List TextureFiles
{
"D:\Ymir Work\effect\pet\halloween_2022_coffin_bat_01.dds"
"D:\Ymir Work\effect\pet\halloween_2022_coffin_bat_02.dds"
"D:\Ymir Work\effect\pet\halloween_2022_coffin_bat_03.dds"
}
}
}
Group Particle
{
StartTime 0.072000
List TimeEventPosition
{
0.000000 "MOVING_TYPE_DIRECT" 0.000000 0.000000 0.000000
}
StaticRotation 0.000000 0.000000 0.000000
Group EmitterProperty
{
MaxEmissionCount 1
CycleLength 10.000000
CycleLoopEnable 1
LoopCount 3
EmitterShape 0
EmitterAdvancedType 0
EmitterEmitFromEdgeFlag 0
EmittingDirection 0.080000 0.000000 0.000000
List TimeEventEmittingSize
{
0.000000 0.000000
}
List TimeEventEmittingAngularVelocity
{
0.000000 0.000000
}
List TimeEventEmittingDirectionX
{
0.000000 15.000000
}
List TimeEventEmittingDirectionY
{
0.000000 -50.000000
0.100000 -20.000000
}
List TimeEventEmittingDirectionZ
{
0.000000 10.000000
0.050000 50.000000
0.100000 1000.000000
}
List TimeEventEmittingVelocity
{
0.000000 15.000000
}
List TimeEventEmissionCountPerSecond
{
0.000000 1000.000000
}
List TimeEventLifeTime
{
0.000000 10.000000
}
List TimeEventSizeX
{
0.000000 72.000000
}
List TimeEventSizeY
{
0.000000 48.000000
}
}
Group ParticleProperty
{
SrcBlendType 5
DestBlendType 6
ColorOperationType 4
BillboardType 1
RotationType 0
RotationSpeed 0.000000
RotationRandomStartingBegin 0
RotationRandomStartingEnd 0
AttachEnable 1
StretchEnable 0
TexAniType 1
TexAniDelay 0.040000
TexAniRandomStartFrameEnable 0
EnableFrustum 0
List TimeEventGravity
{
}
List TimeEventAirResistance
{
}
List TimeEventScaleX
{
0.000000 1.000000
}
List TimeEventScaleY
{
0.000000 1.000000
}
List TimeEventColorRed
{
0.000000 1.000000
}
List TimeEventColorGreen
{
0.000000 1.000000
}
List TimeEventColorBlue
{
0.000000 1.000000
}
List TimeEventAlpha
{
0.000000 1.000000
0.737143 1.000000
1.000000 0.000000
}
List TimeEventRotation
{
0.000000 0.000000
}
List TextureFiles
{
"D:\Ymir Work\effect\pet\halloween_2022_coffin_bat_01.dds"
"D:\Ymir Work\effect\pet\halloween_2022_coffin_bat_02.dds"
"D:\Ymir Work\effect\pet\halloween_2022_coffin_bat_03.dds"
}
}
}
Group Particle
{
StartTime 0.000000
List TimeEventPosition
{
0.000000 "MOVING_TYPE_DIRECT" 0.000000 0.000000 0.000000
}
StaticRotation 0.000000 0.000000 0.000000
Group EmitterProperty
{
MaxEmissionCount 1
CycleLength 10.000000
CycleLoopEnable 1
LoopCount 3
EmitterShape 0
EmitterAdvancedType 0
EmitterEmitFromEdgeFlag 0
EmittingDirection 0.080000 0.000000 0.000000
List TimeEventEmittingSize
{
0.000000 0.000000
}
List TimeEventEmittingAngularVelocity
{
0.000000 0.000000
}
List TimeEventEmittingDirectionX
{
0.000000 50.000000
}
List TimeEventEmittingDirectionY
{
0.000000 -50.000000
0.100000 -20.000000
}
List TimeEventEmittingDirectionZ
{
0.000000 10.000000
0.050000 100.000000
0.100000 1000.000000
}
List TimeEventEmittingVelocity
{
0.000000 5.000000
0.083429 15.000000
}
List TimeEventEmissionCountPerSecond
{
0.000000 1000.000000
}
List TimeEventLifeTime
{
0.000000 10.000000
}
List TimeEventSizeX
{
0.000000 60.000000
}
List TimeEventSizeY
{
0.000000 36.000000
}
}
Group ParticleProperty
{
SrcBlendType 5
DestBlendType 6
ColorOperationType 4
BillboardType 1
RotationType 0
RotationSpeed 0.000000
RotationRandomStartingBegin 0
RotationRandomStartingEnd 0
AttachEnable 1
StretchEnable 0
TexAniType 1
TexAniDelay 0.040000
TexAniRandomStartFrameEnable 0
EnableFrustum 0
List TimeEventGravity
{
}
List TimeEventAirResistance
{
}
List TimeEventScaleX
{
0.000000 1.000000
}
List TimeEventScaleY
{
0.000000 1.000000
}
List TimeEventColorRed
{
0.000000 1.000000
}
List TimeEventColorGreen
{
0.000000 1.000000
}
List TimeEventColorBlue
{
0.000000 1.000000
}
List TimeEventAlpha
{
0.000000 1.000000
0.737143 1.000000
1.000000 0.000000
}
List TimeEventRotation
{
0.000000 0.000000
}
List TextureFiles
{
"D:\Ymir Work\effect\pet\halloween_2022_coffin_bat_01.dds"
"D:\Ymir Work\effect\pet\halloween_2022_coffin_bat_02.dds"
"D:\Ymir Work\effect\pet\halloween_2022_coffin_bat_03.dds"
}
}
}
Group Particle
{
StartTime 0.000000
List TimeEventPosition
{
0.000000 "MOVING_TYPE_DIRECT" 0.000000 0.000000 0.000000
0.216000 "MOVING_TYPE_DIRECT" 0.000000 -20.000000 20.000000
}
StaticRotation 0.000000 0.000000 0.000000
Group EmitterProperty
{
MaxEmissionCount 30
CycleLength 10.000000
CycleLoopEnable 1
LoopCount 3
EmitterShape 3
EmitterAdvancedType 1
EmittingRadius 80.000000
EmitterEmitFromEdgeFlag 0
EmittingDirection 0.010000 0.010000 0.010000
List TimeEventEmittingSize
{
0.000000 0.000000
}
List TimeEventEmittingAngularVelocity
{
0.000000 0.000000
}
List TimeEventEmittingDirectionX
{
0.000000 0.000000
}
List TimeEventEmittingDirectionY
{
0.000000 0.000000
}
List TimeEventEmittingDirectionZ
{
0.000000 20.000000
}
List TimeEventEmittingVelocity
{
0.000000 1.000000
}
List TimeEventEmissionCountPerSecond
{
0.000000 1000.000000
}
List TimeEventLifeTime
{
0.000000 10.000000
}
List TimeEventSizeX
{
0.000000 128.000000
}
List TimeEventSizeY
{
0.000000 128.000000
}
}
Group ParticleProperty
{
SrcBlendType 5
DestBlendType 6
ColorOperationType 4
BillboardType 1
RotationType 4
RotationSpeed 56.000000
RotationRandomStartingBegin 360
RotationRandomStartingEnd 0
AttachEnable 0
StretchEnable 0
TexAniType 0
TexAniDelay 0.040000
TexAniRandomStartFrameEnable 0
EnableFrustum 0
List TimeEventGravity
{
}
List TimeEventAirResistance
{
0.000000 0.000000
0.034286 0.100000
}
List TimeEventScaleX
{
0.000000 0.500000
1.000000 1.000000
}
List TimeEventScaleY
{
0.000000 0.500000
1.000000 1.000000
}
List TimeEventColorRed
{
0.000000 0.819608
}
List TimeEventColorGreen
{
0.000000 0.780392
}
List TimeEventColorBlue
{
0.000000 0.682353
}
List TimeEventAlpha
{
0.000000 1.000000
0.112821 0.000000
}
List TimeEventRotation
{
0.000000 0.000000
}
List TextureFiles
{
"D:\Ymir Work\effect\monster2\smoke_dirt1.dds"
}
}
}

View File

@@ -0,0 +1,654 @@
BoundingSphereRadius 0.000000
BoundingSpherePosition 0.000000 0.000000 0.000000
Group Particle
{
StartTime 0.176000
List TimeEventPosition
{
0.000000 "MOVING_TYPE_DIRECT" 0.000000 0.000000 0.000000
}
StaticRotation 0.000000 0.000000 0.000000
Group EmitterProperty
{
MaxEmissionCount 1
CycleLength 20.000000
CycleLoopEnable 1
LoopCount 2
EmitterShape 0
EmitterAdvancedType 0
EmitterEmitFromEdgeFlag 0
EmittingDirection 0.080000 0.000000 0.000000
List TimeEventEmittingSize
{
0.000000 0.000000
}
List TimeEventEmittingAngularVelocity
{
0.000000 0.000000
}
List TimeEventEmittingDirectionX
{
0.000000 5.000000
}
List TimeEventEmittingDirectionY
{
0.000000 50.000000
0.100000 20.000000
}
List TimeEventEmittingDirectionZ
{
0.000000 10.000000
0.050000 100.000000
0.100000 1000.000000
}
List TimeEventEmittingVelocity
{
0.000000 15.000000
}
List TimeEventEmissionCountPerSecond
{
0.000000 1000.000000
}
List TimeEventLifeTime
{
0.000000 20.000000
}
List TimeEventSizeX
{
0.000000 60.000000
}
List TimeEventSizeY
{
0.000000 36.000000
}
}
Group ParticleProperty
{
SrcBlendType 5
DestBlendType 6
ColorOperationType 4
BillboardType 1
RotationType 0
RotationSpeed 0.000000
RotationRandomStartingBegin 0
RotationRandomStartingEnd 0
AttachEnable 1
StretchEnable 0
TexAniType 1
TexAniDelay 0.040000
TexAniRandomStartFrameEnable 0
EnableFrustum 0
List TimeEventGravity
{
}
List TimeEventAirResistance
{
}
List TimeEventScaleX
{
0.000000 1.000000
}
List TimeEventScaleY
{
0.000000 1.000000
}
List TimeEventColorRed
{
0.000000 1.000000
}
List TimeEventColorGreen
{
0.000000 1.000000
}
List TimeEventColorBlue
{
0.000000 1.000000
}
List TimeEventAlpha
{
0.000000 1.000000
0.376923 1.000000
0.500000 0.000000
}
List TimeEventRotation
{
0.000000 0.000000
}
List TextureFiles
{
"D:\Ymir Work\effect\pet\halloween_2022_coffin_bat_01.dds"
"D:\Ymir Work\effect\pet\halloween_2022_coffin_bat_02.dds"
"D:\Ymir Work\effect\pet\halloween_2022_coffin_bat_03.dds"
}
}
}
Group Particle
{
StartTime 0.072000
List TimeEventPosition
{
0.000000 "MOVING_TYPE_DIRECT" 0.000000 0.000000 0.000000
}
StaticRotation 0.000000 0.000000 0.000000
Group EmitterProperty
{
MaxEmissionCount 1
CycleLength 20.000000
CycleLoopEnable 1
LoopCount 2
EmitterShape 0
EmitterAdvancedType 0
EmitterEmitFromEdgeFlag 0
EmittingDirection 0.080000 0.000000 0.000000
List TimeEventEmittingSize
{
0.000000 0.000000
}
List TimeEventEmittingAngularVelocity
{
0.000000 0.000000
}
List TimeEventEmittingDirectionX
{
0.000000 15.000000
}
List TimeEventEmittingDirectionY
{
0.000000 50.000000
0.100000 20.000000
}
List TimeEventEmittingDirectionZ
{
0.000000 10.000000
0.050000 50.000000
0.100000 1000.000000
}
List TimeEventEmittingVelocity
{
0.000000 15.000000
}
List TimeEventEmissionCountPerSecond
{
0.000000 1000.000000
}
List TimeEventLifeTime
{
0.000000 20.000000
}
List TimeEventSizeX
{
0.000000 72.000000
}
List TimeEventSizeY
{
0.000000 48.000000
}
}
Group ParticleProperty
{
SrcBlendType 5
DestBlendType 6
ColorOperationType 4
BillboardType 1
RotationType 0
RotationSpeed 0.000000
RotationRandomStartingBegin 0
RotationRandomStartingEnd 0
AttachEnable 1
StretchEnable 0
TexAniType 1
TexAniDelay 0.040000
TexAniRandomStartFrameEnable 0
EnableFrustum 0
List TimeEventGravity
{
}
List TimeEventAirResistance
{
}
List TimeEventScaleX
{
0.000000 1.000000
}
List TimeEventScaleY
{
0.000000 1.000000
}
List TimeEventColorRed
{
0.000000 1.000000
}
List TimeEventColorGreen
{
0.000000 1.000000
}
List TimeEventColorBlue
{
0.000000 1.000000
}
List TimeEventAlpha
{
0.000000 1.000000
0.387179 1.000000
0.517949 0.000000
}
List TimeEventRotation
{
0.000000 0.000000
}
List TextureFiles
{
"D:\Ymir Work\effect\pet\halloween_2022_coffin_bat_01.dds"
"D:\Ymir Work\effect\pet\halloween_2022_coffin_bat_02.dds"
"D:\Ymir Work\effect\pet\halloween_2022_coffin_bat_03.dds"
}
}
}
Group Particle
{
StartTime 0.072000
List TimeEventPosition
{
0.000000 "MOVING_TYPE_DIRECT" 0.000000 0.000000 0.000000
}
StaticRotation 0.000000 0.000000 0.000000
Group EmitterProperty
{
MaxEmissionCount 1
CycleLength 20.000000
CycleLoopEnable 1
LoopCount 2
EmitterShape 0
EmitterAdvancedType 0
EmitterEmitFromEdgeFlag 0
EmittingDirection 0.080000 0.000000 0.000000
List TimeEventEmittingSize
{
0.000000 0.000000
}
List TimeEventEmittingAngularVelocity
{
0.000000 0.000000
}
List TimeEventEmittingDirectionX
{
0.000000 15.000000
}
List TimeEventEmittingDirectionY
{
0.000000 50.000000
0.100000 20.000000
}
List TimeEventEmittingDirectionZ
{
0.000000 10.000000
0.050000 50.000000
0.100000 1000.000000
}
List TimeEventEmittingVelocity
{
0.000000 15.000000
}
List TimeEventEmissionCountPerSecond
{
0.000000 1000.000000
}
List TimeEventLifeTime
{
0.000000 20.000000
}
List TimeEventSizeX
{
0.000000 72.000000
}
List TimeEventSizeY
{
0.000000 48.000000
}
}
Group ParticleProperty
{
SrcBlendType 5
DestBlendType 6
ColorOperationType 4
BillboardType 1
RotationType 0
RotationSpeed 0.000000
RotationRandomStartingBegin 0
RotationRandomStartingEnd 0
AttachEnable 1
StretchEnable 0
TexAniType 1
TexAniDelay 0.040000
TexAniRandomStartFrameEnable 0
EnableFrustum 0
List TimeEventGravity
{
}
List TimeEventAirResistance
{
}
List TimeEventScaleX
{
0.000000 1.000000
}
List TimeEventScaleY
{
0.000000 1.000000
}
List TimeEventColorRed
{
0.000000 1.000000
}
List TimeEventColorGreen
{
0.000000 1.000000
}
List TimeEventColorBlue
{
0.000000 1.000000
}
List TimeEventAlpha
{
0.000000 1.000000
0.405128 1.000000
0.505128 0.000000
}
List TimeEventRotation
{
0.000000 0.000000
}
List TextureFiles
{
"D:\Ymir Work\effect\pet\halloween_2022_coffin_bat_01.dds"
"D:\Ymir Work\effect\pet\halloween_2022_coffin_bat_02.dds"
"D:\Ymir Work\effect\pet\halloween_2022_coffin_bat_03.dds"
}
}
}
Group Particle
{
StartTime 0.000000
List TimeEventPosition
{
0.000000 "MOVING_TYPE_DIRECT" 0.000000 0.000000 0.000000
}
StaticRotation 0.000000 0.000000 0.000000
Group EmitterProperty
{
MaxEmissionCount 1
CycleLength 20.000000
CycleLoopEnable 1
LoopCount 2
EmitterShape 0
EmitterAdvancedType 0
EmitterEmitFromEdgeFlag 0
EmittingDirection 0.100000 0.000000 0.050000
List TimeEventEmittingSize
{
0.000000 0.000000
}
List TimeEventEmittingAngularVelocity
{
0.000000 0.000000
}
List TimeEventEmittingDirectionX
{
0.000000 50.000000
}
List TimeEventEmittingDirectionY
{
0.000000 50.000000
0.100000 20.000000
}
List TimeEventEmittingDirectionZ
{
0.000000 10.000000
0.050000 100.000000
0.100000 1000.000000
}
List TimeEventEmittingVelocity
{
0.000000 5.000000
0.083429 15.000000
}
List TimeEventEmissionCountPerSecond
{
0.000000 1000.000000
}
List TimeEventLifeTime
{
0.000000 20.000000
}
List TimeEventSizeX
{
0.000000 60.000000
}
List TimeEventSizeY
{
0.000000 36.000000
}
}
Group ParticleProperty
{
SrcBlendType 5
DestBlendType 6
ColorOperationType 4
BillboardType 1
RotationType 0
RotationSpeed 0.000000
RotationRandomStartingBegin 0
RotationRandomStartingEnd 0
AttachEnable 1
StretchEnable 0
TexAniType 1
TexAniDelay 0.040000
TexAniRandomStartFrameEnable 0
EnableFrustum 0
List TimeEventGravity
{
}
List TimeEventAirResistance
{
}
List TimeEventScaleX
{
0.000000 1.000000
}
List TimeEventScaleY
{
0.000000 1.000000
}
List TimeEventColorRed
{
0.000000 1.000000
}
List TimeEventColorGreen
{
0.000000 1.000000
}
List TimeEventColorBlue
{
0.000000 1.000000
}
List TimeEventAlpha
{
0.000000 1.000000
0.382051 1.000000
0.500000 0.000000
}
List TimeEventRotation
{
0.000000 0.000000
}
List TextureFiles
{
"D:\Ymir Work\effect\pet\halloween_2022_coffin_bat_01.dds"
"D:\Ymir Work\effect\pet\halloween_2022_coffin_bat_02.dds"
"D:\Ymir Work\effect\pet\halloween_2022_coffin_bat_03.dds"
}
}
}
Group Particle
{
StartTime 0.000000
List TimeEventPosition
{
0.000000 "MOVING_TYPE_DIRECT" 0.000000 0.000000 0.000000
0.216000 "MOVING_TYPE_DIRECT" 0.000000 -20.000000 20.000000
}
StaticRotation 0.000000 0.000000 0.000000
Group EmitterProperty
{
MaxEmissionCount 30
CycleLength 20.000000
CycleLoopEnable 1
LoopCount 2
EmitterShape 3
EmitterAdvancedType 1
EmittingRadius 80.000000
EmitterEmitFromEdgeFlag 0
EmittingDirection 0.010000 0.010000 0.010000
List TimeEventEmittingSize
{
0.000000 0.000000
}
List TimeEventEmittingAngularVelocity
{
0.000000 0.000000
}
List TimeEventEmittingDirectionX
{
0.000000 0.000000
}
List TimeEventEmittingDirectionY
{
0.000000 0.000000
}
List TimeEventEmittingDirectionZ
{
0.000000 20.000000
}
List TimeEventEmittingVelocity
{
0.000000 1.000000
}
List TimeEventEmissionCountPerSecond
{
0.000000 1000.000000
}
List TimeEventLifeTime
{
0.000000 20.000000
}
List TimeEventSizeX
{
0.000000 128.000000
}
List TimeEventSizeY
{
0.000000 128.000000
}
}
Group ParticleProperty
{
SrcBlendType 5
DestBlendType 6
ColorOperationType 4
BillboardType 1
RotationType 4
RotationSpeed 56.000000
RotationRandomStartingBegin 360
RotationRandomStartingEnd 0
AttachEnable 0
StretchEnable 0
TexAniType 0
TexAniDelay 0.040000
TexAniRandomStartFrameEnable 0
EnableFrustum 0
List TimeEventGravity
{
}
List TimeEventAirResistance
{
0.000000 0.000000
0.034286 0.100000
}
List TimeEventScaleX
{
0.000000 0.500000
1.000000 1.000000
}
List TimeEventScaleY
{
0.000000 0.500000
1.000000 1.000000
}
List TimeEventColorRed
{
0.000000 0.819608
}
List TimeEventColorGreen
{
0.000000 0.780392
}
List TimeEventColorBlue
{
0.000000 0.682353
}
List TimeEventAlpha
{
0.000000 1.000000
0.050000 0.000000
}
List TimeEventRotation
{
0.000000 0.000000
}
List TextureFiles
{
"D:\Ymir Work\effect\monster2\smoke_dirt1.dds"
}
}
}

View File

@@ -0,0 +1,35 @@
BoundingSphereRadius 400.000000
BoundingSpherePosition -180.000000 -60.000000 650.000000
Group Mesh
{
StartTime 0.000000
List TimeEventPosition
{
0.000000 "MOVING_TYPE_DIRECT" 0.000000 0.000000 0.000000
}
MeshFileName "mushroomA_01.mde"
MeshAnimationLoopEnable 1
MeshAnimationLoopCount 0
MeshAnimationFrameDelay 0.020000
MeshElementCount 1
Group MeshElement00
{
BillboardType 0
BlendingEnable 1
BlendingSrcType 5
BlendingDestType 2
TextureAnimationLoopEnable 1
TextureAnimationFrameDelay 0.020000
TextureAnimationStartFrame 0
ColorOperationType 5
ColorFactor 0.168627 0.643137 0.360784 1.000000
List TimeEventAlpha
{
}
}
}

View File

@@ -0,0 +1,35 @@
BoundingSphereRadius 500.000000
BoundingSpherePosition 100.000000 0.000000 550.000000
Group Mesh
{
StartTime 0.000000
List TimeEventPosition
{
0.000000 "MOVING_TYPE_DIRECT" 0.000000 0.000000 0.000000
}
MeshFileName "mushroomA_03.mde"
MeshAnimationLoopEnable 1
MeshAnimationLoopCount 0
MeshAnimationFrameDelay 0.020000
MeshElementCount 1
Group MeshElement00
{
BillboardType 0
BlendingEnable 1
BlendingSrcType 5
BlendingDestType 2
TextureAnimationLoopEnable 1
TextureAnimationFrameDelay 0.020000
TextureAnimationStartFrame 0
ColorOperationType 5
ColorFactor 0.184314 0.643137 0.462745 1.000000
List TimeEventAlpha
{
}
}
}

View File

@@ -0,0 +1,35 @@
BoundingSphereRadius 330.000000
BoundingSpherePosition -160.000000 0.000000 370.000000
Group Mesh
{
StartTime 0.000000
List TimeEventPosition
{
0.000000 "MOVING_TYPE_DIRECT" 0.000000 0.000000 0.000000
}
MeshFileName "mushroomA_04.mde"
MeshAnimationLoopEnable 1
MeshAnimationLoopCount 0
MeshAnimationFrameDelay 0.020000
MeshElementCount 1
Group MeshElement00
{
BillboardType 0
BlendingEnable 1
BlendingSrcType 5
BlendingDestType 2
TextureAnimationLoopEnable 1
TextureAnimationFrameDelay 0.020000
TextureAnimationStartFrame 0
ColorOperationType 5
ColorFactor 0.200000 0.521569 0.227451 1.000000
List TimeEventAlpha
{
}
}
}

View File

@@ -0,0 +1,36 @@
BoundingSphereRadius 2500.000000
BoundingSpherePosition 0.000000 0.000000 1800.000000
Group Mesh
{
StartTime 0.000000
List TimeEventPosition
{
0.000000 "MOVING_TYPE_DIRECT" 0.000000 0.000000 0.000000
}
MeshFileName "turtle_statue_tree_roof_light01.mde"
MeshAnimationLoopEnable 1
MeshAnimationLoopCount 0
MeshAnimationFrameDelay 0.020000
MeshElementCount 1
Group MeshElement00
{
BillboardType 0
BlendingEnable 1
BlendingSrcType 5
BlendingDestType 2
TextureAnimationLoopEnable 1
TextureAnimationFrameDelay 0.020000
TextureAnimationStartFrame 0
ColorOperationType 5
ColorFactor 0.772549 0.733333 0.188235 1.000000
List TimeEventAlpha
{
0.046667 0.577320
}
}
}

View File

@@ -1,4 +1,5 @@
ScriptType MotionData
MotionFileName "D:\Ymir Work\monster\orc_lord\30_1.GR2"
MotionFileName "D:\Ymir Work\monster\orc_lord\32_1.GR2"
MotionDuration 0.833333

View File

@@ -33,9 +33,6 @@ import uiAffectShower
import uiPlayerGauge
import uiCharacter
import uiTarget
import uiAutopickup
import uiStoneQueue
import uiSwitchbot
# PRIVATE_SHOP_PRICE_LIST
import uiPrivateShopBuilder
@@ -58,157 +55,6 @@ from _weakref import proxy
SCREENSHOT_CWDSAVE = False
SCREENSHOT_DIR = None
def _WriteHeadlessMapTrace(message):
if os.environ.get("M2_HEADLESS_SCENARIO", "").strip().lower() != "map_load":
return
try:
open("log/headless_map_load_trace.txt", "a").write("%s\n" % message)
except:
pass
def _GetHeadlessScenario():
return os.environ.get("M2_HEADLESS_SCENARIO", "").strip().lower()
def _WriteHeadlessTrace(message):
scenario = _GetHeadlessScenario()
if scenario == "map_load":
tracePath = "log/headless_map_load_trace.txt"
elif scenario == "gm_teleport":
tracePath = "log/headless_gm_teleport_trace.txt"
else:
return
try:
open(tracePath, "a").write("%s\n" % message)
except:
pass
def _GetHeadlessFloat(name, default):
try:
return float(os.environ.get(name, str(default)))
except (TypeError, ValueError):
return default
def _GetHeadlessWarpSteps():
rawValue = os.environ.get("M2_HEADLESS_WARP_STEPS", "").strip()
steps = []
if not rawValue:
return steps
for rawStep in rawValue.split("|"):
rawStep = rawStep.strip()
if not rawStep:
continue
parts = [part.strip() for part in rawStep.split(",")]
if len(parts) != 3:
_WriteHeadlessTrace("Invalid warp step=%s" % rawStep)
continue
mapName = parts[0]
try:
globalX = int(parts[1])
globalY = int(parts[2])
except:
_WriteHeadlessTrace("Invalid warp coords step=%s" % rawStep)
continue
steps.append({
"map_name": mapName,
"global_x": globalX,
"global_y": globalY,
})
return steps
HEADLESS_GM_STATE = {
"initialized": False,
"steps": [],
"index": 0,
"phase": "idle",
"command_at": 0.0,
"deadline": 0.0,
"quit_requested": False,
}
def _HeadlessGMEnabled():
return _GetHeadlessScenario() == "gm_teleport"
def _HeadlessGMCommandDelay():
return _GetHeadlessFloat("M2_HEADLESS_COMMAND_DELAY", 5.0)
def _HeadlessGMWarpTimeout():
return _GetHeadlessFloat("M2_HEADLESS_WARP_TIMEOUT", 20.0)
def _HeadlessGMSettleDelay():
return _GetHeadlessFloat("M2_HEADLESS_SETTLE_DELAY", 2.0)
def _HeadlessGMRequestQuit(message):
global HEADLESS_GM_STATE
if message:
_WriteHeadlessTrace(message)
if HEADLESS_GM_STATE.get("quit_requested"):
return
HEADLESS_GM_STATE["quit_requested"] = True
app.Exit()
class _HeadlessDummy(object):
def __call__(self, *args, **kwargs):
return None
def __getattr__(self, name):
return self
def __nonzero__(self):
return False
_HEADLESS_DUMMY = _HeadlessDummy()
class _HeadlessInterface(object):
def MakeInterface(self):
return None
def ShowDefaultWindows(self):
return None
def HideAllWindows(self):
return None
def Close(self):
return None
def BUILD_OnUpdate(self):
return None
def OpenWhisperDialog(self, *args):
return None
def SetMapName(self, *args):
return None
def __getattr__(self, name):
return _HEADLESS_DUMMY
cameraDistance = 1550.0
cameraPitch = 27.0
cameraRotation = 0.0
@@ -218,7 +64,6 @@ testAlignment = 0
class GameWindow(ui.ScriptWindow):
def __init__(self, stream):
_WriteHeadlessTrace("GameWindow.__init__ begin")
ui.ScriptWindow.__init__(self, "GAME")
self.SetWindowName("game")
net.SetPhaseWindow(net.PHASE_WINDOW_GAME, self)
@@ -227,7 +72,6 @@ class GameWindow(ui.ScriptWindow):
self.quickSlotPageIndex = 0
self.lastPKModeSendedTime = 0
self.pressNumber = None
self.headlessGmEnabled = _HeadlessGMEnabled()
self.guildWarQuestionDialog = None
self.interface = None
@@ -238,16 +82,9 @@ class GameWindow(ui.ScriptWindow):
self.playerGauge = None
self.stream = stream
if self.headlessGmEnabled:
self.interface = _HeadlessInterface()
_WriteHeadlessTrace("GameWindow.__init__ interface_headless")
else:
self.interface = interfaceModule.Interface()
_WriteHeadlessTrace("GameWindow.__init__ interface")
self.interface.MakeInterface()
_WriteHeadlessTrace("GameWindow.__init__ interface_made")
self.interface.ShowDefaultWindows()
_WriteHeadlessTrace("GameWindow.__init__ default_windows")
self.interface = interfaceModule.Interface()
self.interface.MakeInterface()
self.interface.ShowDefaultWindows()
self.curtain = uiPhaseCurtain.PhaseCurtain()
self.curtain.speed = 0.03
@@ -275,7 +112,6 @@ class GameWindow(ui.ScriptWindow):
self.__ServerCommand_Build()
self.__ProcessPreservedServerCommand()
_WriteHeadlessTrace("GameWindow.__init__ done")
def __del__(self):
player.SetGameWindow(0)
@@ -297,10 +133,6 @@ class GameWindow(ui.ScriptWindow):
self.enableXMasBoom = False
self.startTimeXMasBoom = 0.0
self.indexXMasBoom = 0
currentMap = background.GetCurrentMapName()
_WriteHeadlessTrace("GameWindow.Open current_map=%s" % currentMap)
if self.headlessGmEnabled:
self.__HeadlessGMOnOpen(currentMap)
global cameraDistance, cameraPitch, cameraRotation, cameraHeight
@@ -388,7 +220,6 @@ class GameWindow(ui.ScriptWindow):
self.currentCubeNPC = 0
def Close(self):
_WriteHeadlessTrace("GameWindow.Close begin current_map=%s" % background.GetCurrentMapName())
self.Hide()
global cameraDistance, cameraPitch, cameraRotation, cameraHeight
@@ -467,7 +298,6 @@ class GameWindow(ui.ScriptWindow):
app.HideCursor()
print("---------------------------------------------------------------------------- CLOSE GAME WINDOW")
_WriteHeadlessTrace("GameWindow.Close end current_map=%s" % background.GetCurrentMapName())
def __BuildKeyDict(self):
onPressKeyDict = {}
@@ -1026,7 +856,6 @@ class GameWindow(ui.ScriptWindow):
# SHOW_LOCAL_MAP_NAME
def ShowMapName(self, mapName, x, y):
_WriteHeadlessTrace("ShowMapName map=%s x=%d y=%d" % (mapName, x, y))
if self.mapNameShower:
self.mapNameShower.ShowMapName(mapName, x, y)
@@ -1636,104 +1465,8 @@ class GameWindow(ui.ScriptWindow):
def OnMouseMiddleButtonUp(self):
player.SetMouseMiddleButtonState(player.MBS_CLICK)
def __HeadlessGMOnOpen(self, currentMap):
global HEADLESS_GM_STATE
state = HEADLESS_GM_STATE
now = app.GetTime()
if not state["initialized"]:
state["steps"] = _GetHeadlessWarpSteps()
state["index"] = 0
state["phase"] = "waiting_command"
state["command_at"] = now + _HeadlessGMCommandDelay()
state["deadline"] = 0.0
state["quit_requested"] = False
state["initialized"] = True
_WriteHeadlessTrace("GM ready current_map=%s steps=%d" % (currentMap, len(state["steps"])))
if not state["steps"]:
state["phase"] = "failed"
_HeadlessGMRequestQuit("No warp steps configured")
return
if state["phase"] != "waiting_open":
return
if state["index"] >= len(state["steps"]):
state["phase"] = "success"
_HeadlessGMRequestQuit("Scenario success current_map=%s" % currentMap)
return
expectedMap = state["steps"][state["index"]]["map_name"]
if currentMap != expectedMap:
state["phase"] = "failed"
_HeadlessGMRequestQuit("Warp open mismatch index=%d expected=%s current_map=%s" % (state["index"], expectedMap, currentMap))
return
_WriteHeadlessTrace("Warp arrived index=%d map=%s" % (state["index"], currentMap))
state["index"] += 1
if state["index"] >= len(state["steps"]):
state["phase"] = "success"
_HeadlessGMRequestQuit("Scenario success current_map=%s" % currentMap)
return
state["phase"] = "settling"
state["command_at"] = now + _HeadlessGMSettleDelay()
state["deadline"] = 0.0
def __HeadlessGMOnUpdate(self):
global HEADLESS_GM_STATE
if not self.headlessGmEnabled:
return
state = HEADLESS_GM_STATE
if not state["initialized"] or state["quit_requested"]:
return
now = app.GetTime()
if state["phase"] == "waiting_command":
if state["index"] >= len(state["steps"]):
state["phase"] = "success"
_HeadlessGMRequestQuit("Scenario success current_map=%s" % background.GetCurrentMapName())
return
if now < state["command_at"]:
return
if 0 == player.GetMainCharacterIndex():
return
step = state["steps"][state["index"]]
meterX = int(step["global_x"] / 100)
meterY = int(step["global_y"] / 100)
command = "/warp %d %d" % (meterX, meterY)
_WriteHeadlessTrace("Warp send index=%d map=%s meter_x=%d meter_y=%d current_map=%s" % (state["index"], step["map_name"], meterX, meterY, background.GetCurrentMapName()))
net.SendChatPacket(command)
state["phase"] = "waiting_open"
state["deadline"] = now + _HeadlessGMWarpTimeout()
return
if state["phase"] == "settling":
if now >= state["command_at"]:
state["phase"] = "waiting_command"
return
if state["phase"] == "waiting_open" and now > state["deadline"]:
if state["index"] < len(state["steps"]):
expectedMap = state["steps"][state["index"]]["map_name"]
else:
expectedMap = ""
state["phase"] = "failed"
_HeadlessGMRequestQuit("Warp timeout index=%d expected=%s current_map=%s" % (state["index"], expectedMap, background.GetCurrentMapName()))
return
def OnUpdate(self):
app.UpdateGame()
self.__HeadlessGMOnUpdate()
if self.mapNameShower.IsShow():
self.mapNameShower.Update()
@@ -2180,10 +1913,6 @@ class GameWindow(ui.ScriptWindow):
"PartyRequestDenied" : self.__PartyRequestDenied,
"horse_state" : self.__Horse_UpdateState,
"hide_horse_state" : self.__Horse_HideState,
"AutoPickupState" : self.__AutoPickupState,
"StoneQueueState" : self.__StoneQueueState,
"SwitchbotState" : self.__SwitchbotState,
"SwitchbotSlot" : self.__SwitchbotSlot,
"WarUC" : self.__GuildWar_UpdateMemberCount,
"test_server" : self.__EnableTestServerFlag,
"mall" : self.__InGameShop_Show,
@@ -2231,18 +1960,6 @@ class GameWindow(ui.ScriptWindow):
def PartyHealReady(self):
self.interface.PartyHealReady()
def __AutoPickupState(self, enabled, mode, mask, vip):
uiAutopickup.SetAutoPickupState(int(enabled), int(mode), int(mask), int(vip))
def __StoneQueueState(self, maxCount, active, current, success, fail):
uiStoneQueue.SetStoneQueueState(int(maxCount), int(active), int(current), int(success), int(fail))
def __SwitchbotState(self, speedIndex, activeCount, scrollCount, etaSeconds):
uiSwitchbot.SetSwitchbotState(int(speedIndex), int(activeCount), int(scrollCount), int(etaSeconds))
def __SwitchbotSlot(self, slotIndex, active, itemCell, attrType, minValue, attempts):
uiSwitchbot.SetSwitchbotSlotState(int(slotIndex), int(active), int(itemCell), int(attrType), int(minValue), int(attempts))
def AskSafeboxPassword(self):
self.interface.AskSafeboxPassword()
@@ -2512,3 +2229,4 @@ class GameWindow(ui.ScriptWindow):
def SkillClearCoolTime(self, slotIndex):
self.interface.SkillClearCoolTime(slotIndex)

View File

@@ -25,9 +25,6 @@ import uiSystem
import uiRestart
import uiToolTip
import uiMiniMap
import uiBiolog
import uiTeleport
import uiSwitchbot
import uiParty
import uiSafebox
import uiGuild
@@ -78,9 +75,6 @@ class Interface(object):
self.wndChat = None
self.wndMessenger = None
self.wndMiniMap = None
self.wndBiolog = None
self.wndTeleport = None
self.wndSwitchbot = None
self.wndGuild = None
self.wndGuildBuilding = None
@@ -186,9 +180,6 @@ class Interface(object):
wndDragonSoulRefine = None
wndMiniMap = uiMiniMap.MiniMap()
wndBiolog = uiBiolog.BiologWindow()
wndTeleport = uiTeleport.TeleportWindow()
wndSwitchbot = uiSwitchbot.SwitchbotWindow()
wndSafebox = uiSafebox.SafeboxWindow()
# ITEM_MALL
@@ -204,14 +195,8 @@ class Interface(object):
self.wndDragonSoul = wndDragonSoul
self.wndDragonSoulRefine = wndDragonSoulRefine
self.wndMiniMap = wndMiniMap
self.wndBiolog = wndBiolog
self.wndTeleport = wndTeleport
self.wndSwitchbot = wndSwitchbot
self.wndSafebox = wndSafebox
self.wndChatLog = wndChatLog
self.wndMiniMap.SetBiologButtonEvent(ui.__mem_func__(self.ToggleBiologWindow))
self.wndMiniMap.SetTeleportButtonEvent(ui.__mem_func__(self.ToggleTeleportWindow))
self.wndMiniMap.SetSwitchbotButtonEvent(ui.__mem_func__(self.ToggleSwitchbotWindow))
if app.ENABLE_DRAGON_SOUL_SYSTEM:
self.wndDragonSoul.SetDragonSoulRefineWindow(self.wndDragonSoulRefine)
@@ -424,11 +409,6 @@ class Interface(object):
if self.wndMiniMap:
self.wndMiniMap.Destroy()
if self.wndBiolog:
self.wndBiolog.Hide()
if self.wndSwitchbot:
self.wndSwitchbot.Hide()
if self.wndSafebox:
self.wndSafebox.Destroy()
@@ -519,8 +499,6 @@ class Interface(object):
del self.tooltipItem
del self.tooltipSkill
del self.wndMiniMap
del self.wndBiolog
del self.wndSwitchbot
del self.wndSafebox
del self.wndMall
del self.wndParty
@@ -880,9 +858,6 @@ class Interface(object):
if self.wndMiniMap:
self.wndMiniMap.Hide()
if self.wndBiolog:
self.wndBiolog.Hide()
if self.wndMessenger:
self.wndMessenger.Hide()
@@ -967,33 +942,6 @@ class Interface(object):
def MiniMapScaleDown(self):
self.wndMiniMap.ScaleDown()
def ToggleBiologWindow(self):
if False == self.wndBiolog.IsShow():
(miniMapX, miniMapY) = self.wndMiniMap.GetGlobalPosition()
self.wndBiolog.SetPosition(max(10, miniMapX - self.wndBiolog.GetWidth() - 10), miniMapY + 8)
self.wndBiolog.Show()
self.wndBiolog.SetTop()
else:
self.wndBiolog.Hide()
def ToggleTeleportWindow(self):
if False == self.wndTeleport.IsShow():
(miniMapX, miniMapY) = self.wndMiniMap.GetGlobalPosition()
self.wndTeleport.SetPosition(max(10, miniMapX - self.wndTeleport.GetWidth() - 10), miniMapY + 24)
self.wndTeleport.Show()
self.wndTeleport.SetTop()
else:
self.wndTeleport.Hide()
def ToggleSwitchbotWindow(self):
if False == self.wndSwitchbot.IsShow():
(miniMapX, miniMapY) = self.wndMiniMap.GetGlobalPosition()
self.wndSwitchbot.SetPosition(max(10, miniMapX - self.wndSwitchbot.GetWidth() - 10), miniMapY + 40)
self.wndSwitchbot.Show()
self.wndSwitchbot.SetTop()
else:
self.wndSwitchbot.Hide()
def ToggleCharacterWindow(self, state):
if False == player.IsObserverMode():
if False == self.wndCharacter.IsShow():

View File

@@ -1,5 +1,3 @@
import os
import ui
import uiScriptLocale
import net
@@ -37,25 +35,6 @@ import uiOption
import uiRestart
####################################
def _GetHeadlessScenario():
return os.environ.get("M2_HEADLESS_SCENARIO", "").strip().lower()
def _WriteHeadlessTrace(message):
scenario = _GetHeadlessScenario()
if scenario == "map_load":
tracePath = "log/headless_map_load_trace.txt"
elif scenario == "gm_teleport":
tracePath = "log/headless_gm_teleport_trace.txt"
else:
return
try:
open(tracePath, "a").write("%s\n" % message)
except:
pass
class LoadingWindow(ui.ScriptWindow):
def __init__(self, stream):
print("NEW LOADING WINDOW -------------------------------------------------------------------------------")
@@ -239,7 +218,6 @@ class LoadingWindow(ui.ScriptWindow):
try:
runFunc()
except:
_WriteHeadlessTrace("LoadData failure step=%d" % progress)
self.errMsg.Show()
self.loadStepList=[]
@@ -324,9 +302,7 @@ class LoadingWindow(ui.ScriptWindow):
emotion.RegisterEmotionIcons()
def __LoadMap(self):
_WriteHeadlessTrace("LoadMap begin global_x=%d global_y=%d" % (self.playerX, self.playerY))
net.Warp(self.playerX, self.playerY)
_WriteHeadlessTrace("LoadMap current_map=%s" % background.GetCurrentMapName())
def __LoadSound(self):
playerSettingModule.LoadGameData("SOUND")
@@ -361,7 +337,6 @@ class LoadingWindow(ui.ScriptWindow):
# END_OF_GUILD_BUILDING
def __StartGame(self):
_WriteHeadlessTrace("StartGame begin current_map=%s" % background.GetCurrentMapName())
background.SetViewDistanceSet(background.DISTANCE0, 25600)
"""
background.SetViewDistanceSet(background.DISTANCE1, 19200)
@@ -374,7 +349,6 @@ class LoadingWindow(ui.ScriptWindow):
app.SetGlobalCenterPosition(self.playerX, self.playerY)
net.StartGame()
_WriteHeadlessTrace("StartGame queued current_map=%s" % background.GetCurrentMapName())
def _ReloadTitleNames():
for i in range(len(localeInfo.TITLE_NAME_LIST)):

View File

@@ -1,4 +1,3 @@
import os
import dbg
import app
import net
@@ -19,10 +18,6 @@ import ime
import uiScriptLocale
import debugInfo
def _AllowHeadlessLoginInfo():
return os.environ.get("M2_HEADLESS_SCENARIO", "").strip().lower() == "gm_teleport"
# Multi-language hot-reload system
from uilocaleselector import LocaleSelector
@@ -720,7 +715,7 @@ class LoginWindow(ui.ScriptWindow):
def __LoadLoginInfo(self, loginInfoFileName):
# This should not work in production
if not debugInfo.IsDebugMode() and not _AllowHeadlessLoginInfo():
if not debugInfo.IsDebugMode():
app.loggined = FALSE
else:
try:

View File

@@ -1,8 +1,6 @@
###################################################################################################
# Network
import os
import app
import chr
import dbg
@@ -20,25 +18,6 @@ import uiPhaseCurtain
import localeInfo
def _GetHeadlessScenario():
return os.environ.get("M2_HEADLESS_SCENARIO", "").strip().lower()
def _WriteHeadlessTrace(message):
scenario = _GetHeadlessScenario()
if scenario == "map_load":
tracePath = "log/headless_map_load_trace.txt"
elif scenario == "gm_teleport":
tracePath = "log/headless_gm_teleport_trace.txt"
else:
return
try:
open(tracePath, "a").write("%s\n" % message)
except:
pass
class PopupDialog(ui.ScriptWindow):
# MR-15: Multiline dialog messages
BASE_HEIGHT = 105
@@ -188,7 +167,6 @@ class MainStream(object):
if newPhaseWindow:
newPhaseWindow.Open()
_WriteHeadlessTrace("MainStream.ChangePhase opened=%s" % newPhaseWindow.__class__.__name__)
self.curPhaseWindow=newPhaseWindow
@@ -259,7 +237,7 @@ class MainStream(object):
try:
import introLoading
loadingPhaseWindow=introLoading.LoadingWindow(self)
loadingPhaseWindow.DEBUG_LoadData(x, y)
loadingPhaseWindow.LoadData(x, y)
self.SetPhaseWindow(loadingPhaseWindow)
except:
import exception
@@ -278,10 +256,8 @@ class MainStream(object):
def SetGamePhase(self):
try:
import game
_WriteHeadlessTrace("MainStream.SetGamePhase begin current_map=%s" % background.GetCurrentMapName())
self.popupWindow.Close()
self.SetPhaseWindow(game.GameWindow(self))
_WriteHeadlessTrace("MainStream.SetGamePhase queued current_map=%s" % background.GetCurrentMapName())
except:
raise
import exception

View File

@@ -659,7 +659,6 @@ def __LoadGameWarriorEx(race, path):
## Bone
chrmgr.RegisterAttachingBoneName(chr.PART_WEAPON, "equip_right_hand")
chrmgr.RegisterAttachingBoneName(chr.PART_ACCE, "Bip01 Spine2")
def __LoadGameAssassinEx(race, path):
## Assassin
@@ -873,7 +872,6 @@ def __LoadGameAssassinEx(race, path):
chrmgr.RegisterAttachingBoneName(chr.PART_WEAPON, "equip_right")
chrmgr.RegisterAttachingBoneName(chr.PART_WEAPON_LEFT, "equip_left")
chrmgr.RegisterAttachingBoneName(chr.PART_ACCE, "Bip01 Spine2")
def __LoadGameSuraEx(race, path):
## Sura
@@ -1191,7 +1189,6 @@ def __LoadGameShamanEx(race, path):
chrmgr.RegisterAttachingBoneName(chr.PART_WEAPON, "equip_right")
chrmgr.RegisterAttachingBoneName(chr.PART_WEAPON_LEFT, "equip_left")
chrmgr.RegisterAttachingBoneName(chr.PART_ACCE, "Bip01 Spine2")
def __LoadGameSkill():
@@ -1459,4 +1456,4 @@ def SetGuildBuilding(race, name, grade):
chrmgr.SetPathName("d:/ymir work/guild/building/%s/" % name)
chrmgr.LoadRaceData("%s%02d.msm" % (name, grade))
chrmgr.RegisterMotionMode(chr.MOTION_MODE_GENERAL)
#chrmgr.RegisterMotionData(chr.MOTION_MODE_GENERAL, chr.MOTION_DEAD, name + "_destruction.msa")
#chrmgr.RegisterMotionData(chr.MOTION_MODE_GENERAL, chr.MOTION_DEAD, name + "_destruction.msa")

View File

@@ -1,5 +1,3 @@
import os
import dbg
import app
import localeInfo
@@ -16,66 +14,6 @@ import stringCommander
#bind_me(locals().values())
def _GetHeadlessScenario():
return os.environ.get("M2_HEADLESS_SCENARIO", "").strip().lower()
def _WriteHeadlessTrace(message):
scenario = _GetHeadlessScenario()
if scenario == "map_load":
tracePath = "log/headless_map_load_trace.txt"
elif scenario == "gm_teleport":
tracePath = "log/headless_gm_teleport_trace.txt"
else:
return
try:
open(tracePath, "a").write("%s\n" % message)
except:
pass
def _GetHeadlessInt(name, default):
try:
return int(os.environ.get(name, str(default)))
except (TypeError, ValueError):
return default
def _ApplyRenderFPSOverride():
rawValue = os.environ.get("M2_RENDER_FPS", "").strip()
if not rawValue:
return
try:
fps = int(rawValue)
except (TypeError, ValueError):
_WriteHeadlessTrace("Invalid M2_RENDER_FPS=%s" % rawValue)
return
app.SetFPS(fps)
_WriteHeadlessTrace("Render FPS override=%d" % fps)
def _SetInitialPhase(mainStream):
scenario = _GetHeadlessScenario()
if scenario == "map_load":
mapName = os.environ.get("M2_HEADLESS_MAP_NAME", "").strip()
globalX = _GetHeadlessInt("M2_HEADLESS_GLOBAL_X", 460800)
globalY = _GetHeadlessInt("M2_HEADLESS_GLOBAL_Y", 960000)
_WriteHeadlessTrace("Scenario begin map=%s global_x=%d global_y=%d" % (mapName, globalX, globalY))
mainStream.SetTestGamePhase(globalX, globalY)
return
if scenario == "gm_teleport":
_WriteHeadlessTrace("Scenario begin gm_teleport")
mainStream.SetLoginPhase()
return
mainStream.SetLoginPhase()
def RunApp():
musicInfo.LoadLastPlayFieldMusic()
@@ -98,7 +36,6 @@ def RunApp():
return
app.SetCamera(1500.0, 30.0, 0.0, 180.0)
_ApplyRenderFPSOverride()
#Gets and sets the floating-point control word
#app.SetControlFP()
@@ -110,7 +47,8 @@ def RunApp():
mainStream.Create()
#mainStream.SetLoadingPhase()
_SetInitialPhase(mainStream)
mainStream.SetLoginPhase()
#mainStream.SetSelectCharacterPhase()
#mainStream.SetCreateCharacterPhase()
#mainStream.SetSelectEmpirePhase()
@@ -120,3 +58,4 @@ def RunApp():
mainStream.Destroy()
RunApp()

View File

@@ -1,145 +0,0 @@
import net
import ui
STATE_ENABLED = 0
STATE_MODE = 0
STATE_MASK = 31
STATE_VIP = 0
OPEN_WINDOWS = []
FILTERS = (
(1, "Weapons"),
(2, "Armor"),
(4, "Yang"),
(8, "Stones"),
(16, "Materials"),
)
def SetAutoPickupState(enabled, mode, mask, vip):
global STATE_ENABLED
global STATE_MODE
global STATE_MASK
global STATE_VIP
STATE_ENABLED = 1 if int(enabled) else 0
STATE_MODE = 1 if int(mode) else 0
STATE_MASK = int(mask) & 31
STATE_VIP = 1 if int(vip) else 0
for window in OPEN_WINDOWS:
try:
window.ApplyState()
except:
pass
class AutoPickupWindow(ui.BoardWithTitleBar):
def __init__(self):
ui.BoardWithTitleBar.__init__(self)
OPEN_WINDOWS.append(self)
self.filterButtons = {}
self.AddFlag("float")
self.AddFlag("movable")
self.SetSize(280, 248)
self.SetTitleName("Auto Pickup")
self.SetCloseEvent(self.Hide)
self.__CreateChildren()
self.Hide()
def __del__(self):
if self in OPEN_WINDOWS:
OPEN_WINDOWS.remove(self)
ui.BoardWithTitleBar.__del__(self)
def Destroy(self):
if self in OPEN_WINDOWS:
OPEN_WINDOWS.remove(self)
self.ClearDictionary()
self.filterButtons = {}
def __CreateChildren(self):
self.statusLine = self.__CreateLine(15, 36)
self.rangeLine = self.__CreateLine(15, 56)
self.modeLine = self.__CreateLine(15, 76)
self.noteLine = self.__CreateLine(15, 96)
self.enableButton = self.__CreateButton(170, 34, 90, "Enable")
self.enableButton.SetEvent(self.__ToggleEnabled)
self.whitelistButton = self.__CreateButton(15, 118, 118, "Whitelist")
self.whitelistButton.SetEvent(self.__SetMode, 0)
self.blacklistButton = self.__CreateButton(142, 118, 118, "Blacklist")
self.blacklistButton.SetEvent(self.__SetMode, 1)
for index, filterData in enumerate(FILTERS):
(bit, label) = filterData
button = self.__CreateButton(15, 150 + index * 18, 245, label)
button.SetEvent(self.__ToggleFilter, bit)
self.filterButtons[bit] = button
def __CreateLine(self, x, y):
textLine = ui.TextLine()
textLine.SetParent(self)
textLine.SetPosition(x, y)
textLine.SetOutline()
textLine.Show()
return textLine
def __CreateButton(self, x, y, width, text):
button = ui.Button()
button.SetParent(self)
button.SetPosition(x, y)
button.SetUpVisual("d:/ymir work/ui/public/small_thin_button_01.sub")
button.SetOverVisual("d:/ymir work/ui/public/small_thin_button_02.sub")
button.SetDownVisual("d:/ymir work/ui/public/small_thin_button_03.sub")
button.SetDisableVisual("d:/ymir work/ui/public/small_thin_button_01.sub")
button.SetSize(width, 17)
button.SetText(text)
button.Show()
return button
def __Send(self, command):
net.SendChatPacket(command, 0)
def __ToggleEnabled(self):
SetAutoPickupState(0 if STATE_ENABLED else 1, STATE_MODE, STATE_MASK, STATE_VIP)
self.__Send("/autopickup enable %d" % STATE_ENABLED)
def __SetMode(self, mode):
SetAutoPickupState(STATE_ENABLED, mode, STATE_MASK, STATE_VIP)
self.__Send("/autopickup mode %s" % ("blacklist" if mode else "whitelist"))
def __ToggleFilter(self, bit):
mask = STATE_MASK ^ bit
SetAutoPickupState(STATE_ENABLED, STATE_MODE, mask, STATE_VIP)
self.__Send("/autopickup mask %d" % STATE_MASK)
def ApplyState(self):
self.statusLine.SetText("Status: %s" % ("Enabled" if STATE_ENABLED else "Disabled"))
self.rangeLine.SetText("Range: %s" % ("VIP 300 / Free 220" if STATE_VIP else "Free 220"))
self.modeLine.SetText("Mode: %s" % ("Blacklist" if STATE_MODE else "Whitelist"))
self.noteLine.SetText("Only nearby owned drops are valid")
self.enableButton.SetText("Disable" if STATE_ENABLED else "Enable")
if STATE_MODE == 0:
self.whitelistButton.Down()
self.blacklistButton.SetUp()
else:
self.whitelistButton.SetUp()
self.blacklistButton.Down()
for (bit, label) in FILTERS:
prefix = "[x]" if (STATE_MASK & bit) else "[ ]"
self.filterButtons[bit].SetText("%s %s" % (prefix, label))
def Show(self):
self.__Send("/autopickup sync")
self.ApplyState()
ui.BoardWithTitleBar.Show(self)

View File

@@ -1,145 +0,0 @@
import time
import net
import player
import quest
import ui
class BiologWindow(ui.BoardWithTitleBar):
TITLE_PREFIX = "Biolog Stage "
STAGE_TARGETS = {
1 : 10,
2 : 15,
3 : 20,
4 : 20,
5 : 25,
6 : 30,
7 : 30,
8 : 40,
9 : 40,
}
def __init__(self):
ui.BoardWithTitleBar.__init__(self)
self.lastRefreshTime = 0.0
self.AddFlag("float")
self.AddFlag("movable")
self.SetSize(230, 165)
self.SetTitleName("Biolog")
self.SetCloseEvent(self.Hide)
self.__CreateChildren()
self.Hide()
def __del__(self):
ui.BoardWithTitleBar.__del__(self)
def __CreateChildren(self):
self.statusLine = self.__CreateValueLine(15, 36)
self.stageLine = self.__CreateValueLine(15, 58)
self.itemLine = self.__CreateValueLine(15, 80)
self.progressLine = self.__CreateValueLine(15, 102)
self.cooldownLine = self.__CreateValueLine(15, 124)
submitButton = ui.Button()
submitButton.SetParent(self)
submitButton.SetPosition(134, 132)
submitButton.SetUpVisual("d:/ymir work/ui/public/small_thin_button_01.sub")
submitButton.SetOverVisual("d:/ymir work/ui/public/small_thin_button_02.sub")
submitButton.SetDownVisual("d:/ymir work/ui/public/small_thin_button_03.sub")
submitButton.SetText("Submit")
submitButton.SetEvent(self.__OnSubmit)
submitButton.Show()
self.submitButton = submitButton
def __CreateValueLine(self, x, y):
textLine = ui.TextLine()
textLine.SetParent(self)
textLine.SetPosition(x, y)
textLine.SetOutline()
textLine.Show()
return textLine
def __GetBiologData(self):
questCount = min(quest.GetQuestCount(), quest.QUEST_MAX_NUM)
for questIndex in range(questCount):
(questName, questIcon, questCounterName, questCounterValue) = quest.GetQuestData(questIndex)
if not questName.startswith(self.TITLE_PREFIX):
continue
try:
stageIndex = int(questName[len(self.TITLE_PREFIX):])
except:
continue
totalRequired = self.STAGE_TARGETS.get(stageIndex, questCounterValue)
(clockName, clockValue) = quest.GetQuestLastTime(questIndex)
return {
"stage" : stageIndex,
"itemName" : questCounterName,
"remaining" : max(questCounterValue, 0),
"required" : max(totalRequired, 0),
"clockName" : clockName,
"clockValue" : max(clockValue, 0),
}
return None
def __FormatCooldown(self, seconds):
hours = seconds // 3600
minutes = (seconds // 60) % 60
return "%02d:%02d:%02d" % (hours, minutes, seconds % 60)
def __RefreshLockedState(self):
if player.GetStatus(player.LEVEL) < 30:
self.statusLine.SetText("Status: Unlocks at level 30")
else:
self.statusLine.SetText("Status: No active biolog stage")
self.stageLine.SetText("Stage: -")
self.itemLine.SetText("Item: -")
self.progressLine.SetText("Progress: -")
self.cooldownLine.SetText("Cooldown: -")
self.submitButton.Hide()
def Refresh(self):
biologData = self.__GetBiologData()
if not biologData:
self.__RefreshLockedState()
return
submitted = max(biologData["required"] - biologData["remaining"], 0)
self.statusLine.SetText("Status: %s" % ("Ready" if biologData["clockValue"] <= 0 else "Cooldown"))
self.stageLine.SetText("Stage: %d / 9" % biologData["stage"])
self.itemLine.SetText("Item: %s" % biologData["itemName"])
self.progressLine.SetText("Progress: %d / %d" % (submitted, biologData["required"]))
if biologData["clockValue"] > 0 and len(biologData["clockName"]) > 0:
self.cooldownLine.SetText("Cooldown: %s" % self.__FormatCooldown(biologData["clockValue"]))
else:
self.cooldownLine.SetText("Cooldown: Ready")
self.submitButton.Show()
def Show(self):
self.Refresh()
ui.BoardWithTitleBar.Show(self)
def OnUpdate(self):
currentTime = time.time()
if currentTime - self.lastRefreshTime < 0.2:
return
self.lastRefreshTime = currentTime
self.Refresh()
def __OnSubmit(self):
try:
net.SendBiologSubmit()
except AttributeError:
net.SendChatPacket("/biolog_submit", 0)
self.lastRefreshTime = 0.0
self.Refresh()

View File

@@ -1,233 +0,0 @@
import item
import net
import player
import ui
STATE_MAX = 3
STATE_ACTIVE = 0
STATE_CURRENT = 0
STATE_SUCCESS = 0
STATE_FAIL = 0
OPEN_WINDOWS = []
def SetStoneQueueState(maxCount, active, current, success, fail):
global STATE_MAX
global STATE_ACTIVE
global STATE_CURRENT
global STATE_SUCCESS
global STATE_FAIL
STATE_MAX = max(1, int(maxCount))
STATE_ACTIVE = 1 if int(active) else 0
STATE_CURRENT = max(0, int(current))
STATE_SUCCESS = max(0, int(success))
STATE_FAIL = max(0, int(fail))
for window in OPEN_WINDOWS:
try:
window.Refresh()
except:
pass
class StoneQueueWindow(ui.BoardWithTitleBar):
PAGE_SIZE = 20
def __init__(self):
ui.BoardWithTitleBar.__init__(self)
OPEN_WINDOWS.append(self)
self.scrollSlotPos = -1
self.selectedSlots = []
self.eligibleSlots = []
self.pageIndex = 0
self.candidateButtons = []
self.queueLines = []
self.AddFlag("float")
self.AddFlag("movable")
self.SetSize(340, 355)
self.SetTitleName("Stone Queue")
self.SetCloseEvent(self.Hide)
self.__CreateChildren()
self.Hide()
def __del__(self):
if self in OPEN_WINDOWS:
OPEN_WINDOWS.remove(self)
ui.BoardWithTitleBar.__del__(self)
def Destroy(self):
if self in OPEN_WINDOWS:
OPEN_WINDOWS.remove(self)
self.ClearDictionary()
self.selectedSlots = []
self.eligibleSlots = []
self.candidateButtons = []
self.queueLines = []
def __CreateChildren(self):
self.scrollLine = self.__CreateLine(15, 36)
self.statusLine = self.__CreateLine(15, 56)
self.progressLine = self.__CreateLine(15, 76)
self.resultLine = self.__CreateLine(15, 96)
self.prevButton = self.__CreateButton(15, 118, 45, "Prev")
self.prevButton.SetEvent(self.__ChangePage, -1)
self.pageLine = self.__CreateLine(127, 122)
self.nextButton = self.__CreateButton(280, 118, 45, "Next")
self.nextButton.SetEvent(self.__ChangePage, 1)
for row in range(5):
for column in range(4):
button = self.__CreateButton(15 + column * 78, 145 + row * 24, 72, "-")
self.candidateButtons.append(button)
for index in range(8):
line = self.__CreateLine(15, 272 + index * 10)
self.queueLines.append(line)
self.startButton = self.__CreateButton(185, 328, 65, "Start")
self.startButton.SetEvent(self.__StartQueue)
self.cancelButton = self.__CreateButton(260, 328, 65, "Cancel")
self.cancelButton.SetEvent(self.__CancelQueue)
def __CreateLine(self, x, y):
textLine = ui.TextLine()
textLine.SetParent(self)
textLine.SetPosition(x, y)
textLine.SetOutline()
textLine.Show()
return textLine
def __CreateButton(self, x, y, width, text):
button = ui.Button()
button.SetParent(self)
button.SetPosition(x, y)
button.SetUpVisual("d:/ymir work/ui/public/small_thin_button_01.sub")
button.SetOverVisual("d:/ymir work/ui/public/small_thin_button_02.sub")
button.SetDownVisual("d:/ymir work/ui/public/small_thin_button_03.sub")
button.SetDisableVisual("d:/ymir work/ui/public/small_thin_button_01.sub")
button.SetSize(width, 17)
button.SetText(text)
button.Show()
return button
def __BuildEligibleSlots(self):
self.eligibleSlots = []
if self.scrollSlotPos < 0:
return
scrollIndex = player.GetItemIndex(self.scrollSlotPos)
for slot in range(player.INVENTORY_PAGE_SIZE * player.INVENTORY_PAGE_COUNT):
if player.REFINE_OK != player.CanRefine(scrollIndex, slot):
continue
targetIndex = player.GetItemIndex(slot)
if targetIndex == 0:
continue
item.SelectItem(targetIndex)
if item.GetItemType() != item.ITEM_TYPE_METIN:
continue
self.eligibleSlots.append(slot)
def __GetSlotLabel(self, slot):
itemVnum = player.GetItemIndex(slot)
if itemVnum == 0:
return "Slot %d" % slot
item.SelectItem(itemVnum)
return "%d:+%d" % (slot, player.GetItemGrade(slot))
def __RefreshCandidates(self):
start = self.pageIndex * self.PAGE_SIZE
end = start + self.PAGE_SIZE
pageSlots = self.eligibleSlots[start:end]
totalPages = max(1, (len(self.eligibleSlots) + self.PAGE_SIZE - 1) // self.PAGE_SIZE)
self.pageLine.SetText("Page %d / %d" % (self.pageIndex + 1, totalPages))
for index, button in enumerate(self.candidateButtons):
if index < len(pageSlots):
slot = pageSlots[index]
prefix = "[x]" if slot in self.selectedSlots else "[ ]"
button.SetText("%s %s" % (prefix, self.__GetSlotLabel(slot)))
button.Enable()
button.SetEvent(self.__ToggleSlot, slot)
else:
button.SetText("-")
button.Disable()
self.prevButton.Enable() if self.pageIndex > 0 else self.prevButton.Disable()
self.nextButton.Enable() if end < len(self.eligibleSlots) else self.nextButton.Disable()
def __RefreshQueueLines(self):
for index, line in enumerate(self.queueLines):
if index < len(self.selectedSlots):
line.SetText("Queue %d: %s" % (index + 1, self.__GetSlotLabel(self.selectedSlots[index])))
else:
line.SetText("Queue %d: -" % (index + 1))
def __ToggleSlot(self, slot):
if STATE_ACTIVE:
return
if slot in self.selectedSlots:
self.selectedSlots.remove(slot)
elif len(self.selectedSlots) < STATE_MAX:
self.selectedSlots.append(slot)
self.Refresh()
def __ChangePage(self, delta):
totalPages = max(1, (len(self.eligibleSlots) + self.PAGE_SIZE - 1) // self.PAGE_SIZE)
self.pageIndex = max(0, min(totalPages - 1, self.pageIndex + delta))
self.__RefreshCandidates()
def __StartQueue(self):
if STATE_ACTIVE or self.scrollSlotPos < 0 or not self.selectedSlots:
return
command = "/stone_queue start %d %s" % (
self.scrollSlotPos,
" ".join([str(slot) for slot in self.selectedSlots[:STATE_MAX]]),
)
net.SendChatPacket(command, 0)
def __CancelQueue(self):
net.SendChatPacket("/stone_queue cancel", 0)
def Open(self, scrollSlotPos, targetSlotPos):
self.scrollSlotPos = scrollSlotPos
self.pageIndex = 0
self.selectedSlots = []
self.__BuildEligibleSlots()
if targetSlotPos in self.eligibleSlots:
self.selectedSlots.append(targetSlotPos)
net.SendChatPacket("/stone_queue sync", 0)
self.Refresh()
self.SetTop()
self.Show()
def Refresh(self):
self.scrollLine.SetText("Scroll slot: %d" % self.scrollSlotPos)
self.statusLine.SetText("Status: %s" % ("Running" if STATE_ACTIVE else "Ready"))
self.progressLine.SetText("Progress: %d / %d" % (STATE_CURRENT, len(self.selectedSlots)))
self.resultLine.SetText("Results: %d success / %d fail / max %d" % (STATE_SUCCESS, STATE_FAIL, STATE_MAX))
self.__RefreshCandidates()
self.__RefreshQueueLines()
if STATE_ACTIVE:
self.startButton.Disable()
self.cancelButton.Enable()
else:
self.startButton.Enable()
self.cancelButton.Disable()

View File

@@ -1,455 +0,0 @@
import item
import net
import player
import ui
import uitooltip
STATE_SPEED = 1
STATE_ACTIVE_COUNT = 0
STATE_SCROLL_COUNT = 0
STATE_ETA_SECONDS = 0
STATE_SLOTS = []
OPEN_WINDOWS = []
_AFFECT_HELPER = None
for _slotIndex in range(5):
STATE_SLOTS.append({
"active": 0,
"itemCell": 0,
"attrType": 0,
"minValue": 0,
"attempts": 0,
})
def _GetAffectHelper():
global _AFFECT_HELPER
if _AFFECT_HELPER is None:
_AFFECT_HELPER = uitooltip.ItemToolTip()
return _AFFECT_HELPER
def _GetAffectLabel(attrType):
if attrType <= 0:
return "-"
try:
label = _GetAffectHelper()._ItemToolTip__GetAffectString(attrType, 1)
except:
label = None
if not label:
return "Attr %d" % attrType
return label
ATTR_OPTIONS = (
(item.APPLY_MAX_HP, _GetAffectLabel(item.APPLY_MAX_HP)),
(item.APPLY_MAX_SP, _GetAffectLabel(item.APPLY_MAX_SP)),
(item.APPLY_STR, _GetAffectLabel(item.APPLY_STR)),
(item.APPLY_DEX, _GetAffectLabel(item.APPLY_DEX)),
(item.APPLY_CON, _GetAffectLabel(item.APPLY_CON)),
(item.APPLY_INT, _GetAffectLabel(item.APPLY_INT)),
(item.APPLY_ATT_SPEED, _GetAffectLabel(item.APPLY_ATT_SPEED)),
(item.APPLY_MOV_SPEED, _GetAffectLabel(item.APPLY_MOV_SPEED)),
(item.APPLY_CAST_SPEED, _GetAffectLabel(item.APPLY_CAST_SPEED)),
(item.APPLY_CRITICAL_PCT, _GetAffectLabel(item.APPLY_CRITICAL_PCT)),
(item.APPLY_PENETRATE_PCT, _GetAffectLabel(item.APPLY_PENETRATE_PCT)),
(item.APPLY_ATTBONUS_MONSTER, _GetAffectLabel(item.APPLY_ATTBONUS_MONSTER)),
(item.APPLY_ATTBONUS_DEVIL, _GetAffectLabel(item.APPLY_ATTBONUS_DEVIL)),
(item.APPLY_ATTBONUS_HUMAN, _GetAffectLabel(item.APPLY_ATTBONUS_HUMAN)),
(item.APPLY_STEAL_HP, _GetAffectLabel(item.APPLY_STEAL_HP)),
(item.APPLY_STEAL_SP, _GetAffectLabel(item.APPLY_STEAL_SP)),
(item.APPLY_BLOCK, _GetAffectLabel(item.APPLY_BLOCK)),
(item.APPLY_DODGE, _GetAffectLabel(item.APPLY_DODGE)),
(item.APPLY_ATT_GRADE_BONUS, _GetAffectLabel(item.APPLY_ATT_GRADE_BONUS)),
(item.APPLY_DEF_GRADE_BONUS, _GetAffectLabel(item.APPLY_DEF_GRADE_BONUS)),
(item.APPLY_RESIST_SWORD, _GetAffectLabel(item.APPLY_RESIST_SWORD)),
(item.APPLY_RESIST_TWOHAND, _GetAffectLabel(item.APPLY_RESIST_TWOHAND)),
(item.APPLY_RESIST_DAGGER, _GetAffectLabel(item.APPLY_RESIST_DAGGER)),
(item.APPLY_RESIST_BOW, _GetAffectLabel(item.APPLY_RESIST_BOW)),
(item.APPLY_RESIST_FIRE, _GetAffectLabel(item.APPLY_RESIST_FIRE)),
(item.APPLY_RESIST_ELEC, _GetAffectLabel(item.APPLY_RESIST_ELEC)),
(item.APPLY_RESIST_MAGIC, _GetAffectLabel(item.APPLY_RESIST_MAGIC)),
(item.APPLY_RESIST_WIND, _GetAffectLabel(item.APPLY_RESIST_WIND)),
(item.APPLY_RESIST_ICE, _GetAffectLabel(item.APPLY_RESIST_ICE)),
(item.APPLY_RESIST_EARTH, _GetAffectLabel(item.APPLY_RESIST_EARTH)),
(item.APPLY_RESIST_DARK, _GetAffectLabel(item.APPLY_RESIST_DARK)),
)
SPEED_OPTIONS = (
(0, "Slow / 3s"),
(1, "Normal / 2s"),
(2, "Fast / 1s"),
)
def SetSwitchbotState(speedIndex, activeCount, scrollCount, etaSeconds):
global STATE_SPEED
global STATE_ACTIVE_COUNT
global STATE_SCROLL_COUNT
global STATE_ETA_SECONDS
STATE_SPEED = int(speedIndex)
STATE_ACTIVE_COUNT = max(0, int(activeCount))
STATE_SCROLL_COUNT = max(0, int(scrollCount))
STATE_ETA_SECONDS = max(0, int(etaSeconds))
for window in OPEN_WINDOWS:
try:
window.Refresh()
except:
pass
def SetSwitchbotSlotState(slotIndex, active, itemCell, attrType, minValue, attempts):
slotIndex = int(slotIndex)
if slotIndex < 0 or slotIndex >= len(STATE_SLOTS):
return
STATE_SLOTS[slotIndex]["active"] = 1 if int(active) else 0
STATE_SLOTS[slotIndex]["itemCell"] = int(itemCell)
STATE_SLOTS[slotIndex]["attrType"] = int(attrType)
STATE_SLOTS[slotIndex]["minValue"] = int(minValue)
STATE_SLOTS[slotIndex]["attempts"] = int(attempts)
for window in OPEN_WINDOWS:
try:
window.Refresh()
except:
pass
class SwitchbotWindow(ui.BoardWithTitleBar):
CANDIDATE_PAGE_SIZE = 12
def __init__(self):
ui.BoardWithTitleBar.__init__(self)
OPEN_WINDOWS.append(self)
self.selectedSlotIndex = 0
self.pageIndex = 0
self.candidateSlots = []
self.slotWidgets = []
self.candidateButtons = []
self.speedLabels = {}
self.attrLabels = {}
self.AddFlag("float")
self.AddFlag("movable")
self.SetSize(535, 406)
self.SetTitleName("Switchbot")
self.SetCloseEvent(self.Hide)
self.__CreateChildren()
self.Hide()
def __del__(self):
if self in OPEN_WINDOWS:
OPEN_WINDOWS.remove(self)
ui.BoardWithTitleBar.__del__(self)
def Destroy(self):
if self in OPEN_WINDOWS:
OPEN_WINDOWS.remove(self)
self.ClearDictionary()
self.slotWidgets = []
self.candidateButtons = []
self.candidateSlots = []
self.speedLabels = {}
self.attrLabels = {}
def __CreateChildren(self):
self.statusLine = self.__CreateLine(15, 36)
self.scrollLine = self.__CreateLine(15, 56)
self.etaLine = self.__CreateLine(15, 76)
self.helpLine = self.__CreateLine(15, 96)
self.speedCombo = ui.ComboBox()
self.speedCombo.SetParent(self)
self.speedCombo.SetPosition(398, 34)
self.speedCombo.SetSize(120, 18)
self.speedCombo.SetEvent(self.__OnChangeSpeed)
self.speedCombo.Show()
for (speedIndex, label) in SPEED_OPTIONS:
self.speedCombo.InsertItem(speedIndex, label)
self.speedLabels[speedIndex] = label
for slotIndex in range(5):
baseY = 126 + slotIndex * 38
row = {}
row["selectButton"] = self.__CreateButton(15, baseY, 48, "Slot %d" % (slotIndex + 1))
row["selectButton"].SetEvent(self.__SelectSlot, slotIndex)
row["itemLine"] = self.__CreateLine(72, baseY + 2)
row["statusLine"] = self.__CreateLine(72, baseY + 18)
row["attrCombo"] = ui.ComboBox()
row["attrCombo"].SetParent(self)
row["attrCombo"].SetPosition(220, baseY)
row["attrCombo"].SetSize(165, 18)
row["attrCombo"].SetEvent(lambda attrType, line=slotIndex: self.__SetAttrType(line, attrType))
row["attrCombo"].Show()
for (attrType, label) in ATTR_OPTIONS:
row["attrCombo"].InsertItem(attrType, label)
attrBar = ui.SlotBar()
attrBar.SetParent(self)
attrBar.SetPosition(392, baseY)
attrBar.SetSize(48, 18)
attrBar.Show()
row["valueBar"] = attrBar
row["valueEdit"] = ui.EditLine()
row["valueEdit"].SetParent(attrBar)
row["valueEdit"].SetPosition(3, 3)
row["valueEdit"].SetSize(42, 12)
row["valueEdit"].SetMax(5)
row["valueEdit"].SetNumberMode()
row["valueEdit"].Show()
row["startButton"] = self.__CreateButton(447, baseY, 40, "Start")
row["startButton"].SetEvent(self.__ToggleSlot, slotIndex)
row["clearButton"] = self.__CreateButton(492, baseY, 28, "X")
row["clearButton"].SetEvent(self.__ClearSlot, slotIndex)
self.slotWidgets.append(row)
self.prevButton = self.__CreateButton(15, 330, 44, "Prev")
self.prevButton.SetEvent(self.__ChangePage, -1)
self.pageLine = self.__CreateLine(80, 334)
self.nextButton = self.__CreateButton(170, 330, 44, "Next")
self.nextButton.SetEvent(self.__ChangePage, 1)
for index in range(self.CANDIDATE_PAGE_SIZE):
column = index % 2
row = index // 2
button = self.__CreateButton(15 + column * 155, 356 + row * 18, 148, "-")
button.SetEvent(self.__AssignCandidate, index)
self.candidateButtons.append(button)
self.syncButton = self.__CreateButton(305, 330, 54, "Sync")
self.syncButton.SetEvent(self.__Sync)
self.startAllButton = self.__CreateButton(366, 330, 74, "Start All")
self.startAllButton.SetEvent(self.__StartAll)
self.stopAllButton = self.__CreateButton(446, 330, 74, "Stop All")
self.stopAllButton.SetEvent(self.__StopAll)
def __CreateLine(self, x, y):
textLine = ui.TextLine()
textLine.SetParent(self)
textLine.SetPosition(x, y)
textLine.SetOutline()
textLine.Show()
return textLine
def __CreateButton(self, x, y, width, text):
button = ui.Button()
button.SetParent(self)
button.SetPosition(x, y)
button.SetUpVisual("d:/ymir work/ui/public/small_thin_button_01.sub")
button.SetOverVisual("d:/ymir work/ui/public/small_thin_button_02.sub")
button.SetDownVisual("d:/ymir work/ui/public/small_thin_button_03.sub")
button.SetDisableVisual("d:/ymir work/ui/public/small_thin_button_01.sub")
button.SetSize(width, 17)
button.SetText(text)
button.Show()
return button
def __Send(self, command):
net.SendChatPacket(command, 0)
def __IsEligibleItem(self, slotPos):
itemVnum = player.GetItemIndex(slotPos)
if itemVnum == 0:
return False
item.SelectItem(itemVnum)
if item.GetItemType() == item.ITEM_TYPE_COSTUME:
return False
if item.GetItemType() not in (item.ITEM_TYPE_WEAPON, item.ITEM_TYPE_ARMOR):
return False
for attrIndex in range(player.ATTRIBUTE_SLOT_MAX_NUM):
if player.GetItemAttribute(slotPos, attrIndex)[0] != 0:
return True
return False
def __GetItemLabel(self, slotPos):
itemVnum = player.GetItemIndex(slotPos)
if itemVnum == 0:
return "Slot %d" % slotPos
item.SelectItem(itemVnum)
return "%d: %s" % (slotPos, item.GetItemName())
def __BuildCandidateSlots(self):
self.candidateSlots = []
for slotPos in range(player.INVENTORY_PAGE_SIZE * player.INVENTORY_PAGE_COUNT):
if self.__IsEligibleItem(slotPos):
self.candidateSlots.append(slotPos)
def __FormatEta(self):
if STATE_ETA_SECONDS <= 0:
return "ETA: -"
minutes = STATE_ETA_SECONDS // 60
seconds = STATE_ETA_SECONDS % 60
return "ETA: %02d:%02d" % (minutes, seconds)
def __GetAttrType(self, slotIndex):
return STATE_SLOTS[slotIndex]["attrType"]
def __GetMinValue(self, slotIndex):
text = self.slotWidgets[slotIndex]["valueEdit"].GetText()
if not text:
return STATE_SLOTS[slotIndex]["minValue"]
return int(text)
def __SelectSlot(self, slotIndex):
self.selectedSlotIndex = slotIndex
self.Refresh()
def __SetAttrType(self, slotIndex, attrType):
SetSwitchbotSlotState(
slotIndex,
STATE_SLOTS[slotIndex]["active"],
STATE_SLOTS[slotIndex]["itemCell"],
attrType,
STATE_SLOTS[slotIndex]["minValue"],
STATE_SLOTS[slotIndex]["attempts"],
)
def __AssignCandidate(self, localIndex):
candidateIndex = self.pageIndex * self.CANDIDATE_PAGE_SIZE + localIndex
if candidateIndex < 0 or candidateIndex >= len(self.candidateSlots):
return
slotIndex = self.selectedSlotIndex
SetSwitchbotSlotState(
slotIndex,
0,
self.candidateSlots[candidateIndex],
STATE_SLOTS[slotIndex]["attrType"],
STATE_SLOTS[slotIndex]["minValue"],
0,
)
def __ToggleSlot(self, slotIndex):
slotState = STATE_SLOTS[slotIndex]
if slotState["active"]:
self.__Send("/switchbot stop %d" % slotIndex)
return
itemCell = slotState["itemCell"]
attrType = self.__GetAttrType(slotIndex)
minValue = self.__GetMinValue(slotIndex)
if not self.__IsEligibleItem(itemCell) or attrType <= 0 or minValue <= 0:
return
SetSwitchbotSlotState(slotIndex, 0, itemCell, attrType, minValue, slotState["attempts"])
self.__Send("/switchbot start %d %d %d %d" % (slotIndex, itemCell, attrType, minValue))
def __ClearSlot(self, slotIndex):
self.__Send("/switchbot clear %d" % slotIndex)
def __ChangePage(self, delta):
totalPages = max(1, (len(self.candidateSlots) + self.CANDIDATE_PAGE_SIZE - 1) // self.CANDIDATE_PAGE_SIZE)
self.pageIndex = max(0, min(totalPages - 1, self.pageIndex + delta))
self.Refresh()
def __OnChangeSpeed(self, speedIndex):
self.__Send("/switchbot speed %d" % int(speedIndex))
def __Sync(self):
self.__Send("/switchbot sync")
def __StartAll(self):
for slotIndex in range(len(STATE_SLOTS)):
slotState = STATE_SLOTS[slotIndex]
if slotState["active"]:
continue
itemCell = slotState["itemCell"]
attrType = self.__GetAttrType(slotIndex)
minValue = self.__GetMinValue(slotIndex)
if not self.__IsEligibleItem(itemCell) or attrType <= 0 or minValue <= 0:
continue
self.__Send("/switchbot start %d %d %d %d" % (slotIndex, itemCell, attrType, minValue))
def __StopAll(self):
self.__Send("/switchbot stop_all")
def Refresh(self):
self.__BuildCandidateSlots()
self.statusLine.SetText("Status: %d active slot(s)" % STATE_ACTIVE_COUNT)
self.scrollLine.SetText("Switch items: %d" % STATE_SCROLL_COUNT)
self.etaLine.SetText(self.__FormatEta())
self.helpLine.SetText("Select a row, assign an item, pick a target bonus and min value")
self.speedCombo.SetCurrentItem(self.speedLabels.get(STATE_SPEED, SPEED_OPTIONS[1][1]))
for slotIndex in range(len(self.slotWidgets)):
slotState = STATE_SLOTS[slotIndex]
row = self.slotWidgets[slotIndex]
row["selectButton"].SetText(("> " if self.selectedSlotIndex == slotIndex else "") + "S%d" % (slotIndex + 1))
row["itemLine"].SetText("Item: %s" % ("-" if not self.__IsEligibleItem(slotState["itemCell"]) else self.__GetItemLabel(slotState["itemCell"])))
row["statusLine"].SetText("Target: %s / tries %d / %s" % (
_GetAffectLabel(slotState["attrType"]),
slotState["attempts"],
"Running" if slotState["active"] else "Ready",
))
if slotState["attrType"] > 0:
row["attrCombo"].SetCurrentItem(_GetAffectLabel(slotState["attrType"]))
else:
row["attrCombo"].SetCurrentItem("-")
if slotState["minValue"] > 0 and row["valueEdit"].GetText() != str(slotState["minValue"]):
row["valueEdit"].SetText(str(slotState["minValue"]))
elif slotState["minValue"] <= 0 and row["valueEdit"].GetText():
row["valueEdit"].SetText("")
row["startButton"].SetText("Stop" if slotState["active"] else "Start")
start = self.pageIndex * self.CANDIDATE_PAGE_SIZE
end = start + self.CANDIDATE_PAGE_SIZE
pageSlots = self.candidateSlots[start:end]
totalPages = max(1, (len(self.candidateSlots) + self.CANDIDATE_PAGE_SIZE - 1) // self.CANDIDATE_PAGE_SIZE)
self.pageLine.SetText("Candidates %d / %d" % (self.pageIndex + 1, totalPages))
for localIndex in range(len(self.candidateButtons)):
button = self.candidateButtons[localIndex]
if localIndex < len(pageSlots):
button.SetText(self.__GetItemLabel(pageSlots[localIndex]))
button.Enable()
else:
button.SetText("-")
button.Disable()
if self.pageIndex > 0:
self.prevButton.Enable()
else:
self.prevButton.Disable()
if end < len(self.candidateSlots):
self.nextButton.Enable()
else:
self.nextButton.Disable()
def Show(self):
self.__Sync()
self.Refresh()
ui.BoardWithTitleBar.Show(self)

View File

@@ -1,173 +0,0 @@
import time
import background
import net
import player
import quest
import ui
class TeleportWindow(ui.BoardWithTitleBar):
TITLE = "Teleport System"
PRESET_BUTTONS = (
(1, "Village 1"),
(2, "Village 2"),
(3, "Valley"),
(4, "Desert"),
(5, "Sohan"),
(6, "Fireland"),
(7, "Devil"),
(8, "Cave"),
)
def __init__(self):
ui.BoardWithTitleBar.__init__(self)
self.lastRefreshTime = 0.0
self.slotRows = []
self.AddFlag("float")
self.AddFlag("movable")
self.SetSize(310, 255)
self.SetTitleName("Teleport")
self.SetCloseEvent(self.Hide)
self.__CreateChildren()
self.Hide()
def __del__(self):
ui.BoardWithTitleBar.__del__(self)
def __CreateChildren(self):
self.statusLine = self.__CreateValueLine(15, 36)
self.mapLine = self.__CreateValueLine(15, 56)
self.coordLine = self.__CreateValueLine(15, 76)
self.slotSummaryLine = self.__CreateValueLine(15, 96)
self.cooldownLine = self.__CreateValueLine(15, 116)
for index, presetData in enumerate(self.PRESET_BUTTONS):
(presetId, label) = presetData
row = index // 4
column = index % 4
button = self.__CreateButton(15 + column * 71, 142 + row * 24, 64, label)
button.SetEvent(self.__SendPresetWarp, presetId)
for slot in range(1, 6):
label = self.__CreateValueLine(15, 196 + (slot - 1) * 11)
saveButton = self.__CreateButton(100, 192 + (slot - 1) * 11, 42, "Save")
saveButton.SetEvent(self.__SendSaveSlot, slot)
useButton = self.__CreateButton(148, 192 + (slot - 1) * 11, 42, "Use")
useButton.SetEvent(self.__SendUseSlot, slot)
self.slotRows.append((label, saveButton, useButton))
def __CreateValueLine(self, x, y):
textLine = ui.TextLine()
textLine.SetParent(self)
textLine.SetPosition(x, y)
textLine.SetOutline()
textLine.Show()
return textLine
def __CreateButton(self, x, y, width, text):
button = ui.Button()
button.SetParent(self)
button.SetPosition(x, y)
button.SetUpVisual("d:/ymir work/ui/public/small_thin_button_01.sub")
button.SetOverVisual("d:/ymir work/ui/public/small_thin_button_02.sub")
button.SetDownVisual("d:/ymir work/ui/public/small_thin_button_03.sub")
button.SetDisableVisual("d:/ymir work/ui/public/small_thin_button_01.sub")
button.SetSize(width, 17)
button.SetText(text)
button.Show()
return button
def __GetTeleportQuestData(self):
questCount = min(quest.GetQuestCount(), quest.QUEST_MAX_NUM)
for questIndex in range(questCount):
(questName, questIcon, questCounterName, questCounterValue) = quest.GetQuestData(questIndex)
if questName != self.TITLE:
continue
maxSlots = 3
if "/" in questCounterName:
parts = questCounterName.split("/")
try:
maxSlots = int(parts[-1].strip())
except:
maxSlots = 3
(clockName, clockValue) = quest.GetQuestLastTime(questIndex)
return {
"savedCount" : max(questCounterValue, 0),
"maxSlots" : max(maxSlots, 3),
"clockName" : clockName,
"clockValue" : max(clockValue, 0),
}
return None
def __FormatCooldown(self, seconds):
return "%02d:%02d" % (seconds // 60, seconds % 60)
def __RefreshSlotRows(self, maxSlots):
for slot, row in enumerate(self.slotRows, 1):
(label, saveButton, useButton) = row
if slot <= maxSlots:
label.SetText("Slot %d" % slot)
saveButton.Enable()
useButton.Enable()
else:
label.SetText("Slot %d (VIP)" % slot)
saveButton.Disable()
useButton.Disable()
def Refresh(self):
teleportData = self.__GetTeleportQuestData()
(x, y, z) = player.GetMainCharacterPosition()
currentMap = background.GetCurrentMapName()
self.mapLine.SetText("Map: %s" % currentMap)
self.coordLine.SetText("Coords: %d, %d" % (x // 100, y // 100))
if not teleportData:
self.statusLine.SetText("Status: Waiting for teleport quest")
self.slotSummaryLine.SetText("Saved: -")
self.cooldownLine.SetText("Cooldown: -")
self.__RefreshSlotRows(3)
return
self.statusLine.SetText("Status: %s" % ("Cooldown" if teleportData["clockValue"] > 0 else "Ready"))
self.slotSummaryLine.SetText("Saved: %d / %d" % (teleportData["savedCount"], teleportData["maxSlots"]))
if teleportData["clockValue"] > 0 and len(teleportData["clockName"]) > 0:
self.cooldownLine.SetText("Cooldown: %s" % self.__FormatCooldown(teleportData["clockValue"]))
else:
self.cooldownLine.SetText("Cooldown: Ready")
self.__RefreshSlotRows(teleportData["maxSlots"])
def Show(self):
self.Refresh()
ui.BoardWithTitleBar.Show(self)
def OnUpdate(self):
currentTime = time.time()
if currentTime - self.lastRefreshTime < 0.2:
return
self.lastRefreshTime = currentTime
self.Refresh()
def __SendTeleportCommand(self, action, arg):
net.SendChatPacket("/teleport_system %s %d" % (action, arg), 0)
self.lastRefreshTime = 0.0
self.Refresh()
def __SendSaveSlot(self, slot):
self.__SendTeleportCommand("save", slot)
def __SendUseSlot(self, slot):
self.__SendTeleportCommand("saved", slot)
def __SendPresetWarp(self, presetId):
self.__SendTeleportCommand("preset", presetId)

View File

@@ -8,7 +8,6 @@ import localeInfo
import constInfo
import chrmgr
import player
import uiAutopickup
import uiPrivateShopBuilder # 占쏙옙占쏙옙호
import interfaceModule # 占쏙옙占쏙옙호
@@ -40,14 +39,8 @@ class OptionDialog(ui.ScriptWindow):
self.alwaysShowNameButtonList = []
self.showDamageButtonList = []
self.showsalesTextButtonList = []
self.autoPickupButton = None
self.autoPickupDialog = None
def Destroy(self):
if self.autoPickupDialog:
self.autoPickupDialog.Destroy()
self.autoPickupDialog = None
self.ClearDictionary()
self.__Initialize()
@@ -87,7 +80,6 @@ class OptionDialog(ui.ScriptWindow):
self.showDamageButtonList.append(GetObject("show_damage_off_button"))
self.showsalesTextButtonList.append(GetObject("salestext_on_button"))
self.showsalesTextButtonList.append(GetObject("salestext_off_button"))
self.autoPickupButton = GetObject("autopickup_button")
except:
import exception
@@ -138,7 +130,6 @@ class OptionDialog(ui.ScriptWindow):
self.showsalesTextButtonList[0].SAFE_SetEvent(self.__OnClickSalesTextOnButton)
self.showsalesTextButtonList[1].SAFE_SetEvent(self.__OnClickSalesTextOffButton)
self.autoPickupButton.SAFE_SetEvent(self.__OnClickAutoPickupButton)
self.__ClickRadioButton(self.nameColorModeButtonList, constInfo.GET_CHRNAME_COLOR_INDEX())
self.__ClickRadioButton(self.viewTargetBoardButtonList, constInfo.GET_VIEW_OTHER_EMPIRE_PLAYER_TARGET_BOARD())
@@ -250,15 +241,6 @@ class OptionDialog(ui.ScriptWindow):
def __OnClickSalesTextOffButton(self):
systemSetting.SetShowSalesTextFlag(False)
self.RefreshShowSalesText()
def __OnClickAutoPickupButton(self):
if not self.autoPickupDialog:
self.autoPickupDialog = uiAutopickup.AutoPickupWindow()
if self.autoPickupDialog.IsShow():
self.autoPickupDialog.Hide()
else:
self.autoPickupDialog.Show()
def __CheckPvPProtectedLevelPlayer(self):
if player.GetStatus(player.LEVEL)<constInfo.PVPMODE_PROTECTED_LEVEL:

View File

@@ -11,7 +11,6 @@ import grp
import uiScriptLocale
import uiRefine
import uiAttachMetin
import uiStoneQueue
import uiPickMoney
import uiCommon
import uiPrivateShopBuilder # Prevent ItemMove while private shop is open
@@ -24,12 +23,6 @@ ITEM_FLAG_APPLICABLE = 1 << 14
class CostumeWindow(ui.ScriptWindow):
SLOT_ORDER = (
item.COSTUME_SLOT_BODY,
item.COSTUME_SLOT_HAIR,
item.COSTUME_SLOT_SASH,
)
def __init__(self, wndInventory):
import exception
@@ -94,7 +87,8 @@ class CostumeWindow(ui.ScriptWindow):
def RefreshCostumeSlot(self):
getItemVNum=player.GetItemIndex
for slotNumber in self.SLOT_ORDER:
for i in range(item.COSTUME_SLOT_COUNT):
slotNumber = item.COSTUME_SLOT_START + i
self.wndEquip.SetItemSlot(slotNumber, getItemVNum(slotNumber), 0)
self.wndEquip.RefreshSlot()
@@ -257,13 +251,11 @@ class InventoryWindow(ui.ScriptWindow):
isLoaded = 0
isOpenedCostumeWindowWhenClosingInventory = 0 # Whether costume window was open when closing inventory
isOpenedBeltWindowWhenClosingInventory = 0 # Whether belt inventory was open when closing inventory
sashAbsorbSlot = -1
def __init__(self):
ui.ScriptWindow.__init__(self)
self.isOpenedBeltWindowWhenClosingInventory = 0 # Whether belt inventory was open when closing inventory
self.sashAbsorbSlot = -1
self.__LoadWindow()
@@ -365,10 +357,6 @@ class InventoryWindow(ui.ScriptWindow):
self.attachMetinDialog = uiAttachMetin.AttachMetinDialog()
self.attachMetinDialog.Hide()
## StoneQueueDialog
self.stoneQueueDialog = uiStoneQueue.StoneQueueWindow()
self.stoneQueueDialog.Hide()
## MoneySlot
self.wndMoneySlot.SetEvent(ui.__mem_func__(self.OpenPickMoneyDialog))
@@ -419,9 +407,6 @@ class InventoryWindow(ui.ScriptWindow):
self.attachMetinDialog.Destroy()
self.attachMetinDialog = 0
self.stoneQueueDialog.Destroy()
self.stoneQueueDialog = 0
self.tooltipItem = None
self.wndItem = 0
self.wndEquip = 0
@@ -446,7 +431,6 @@ class InventoryWindow(ui.ScriptWindow):
self.equipmentTab = []
def Hide(self):
self.__AbortSashAbsorb(False)
if constInfo.GET_ITEM_QUESTION_DIALOG_STATUS():
self.OnCloseQuestionDialog()
return
@@ -471,91 +455,6 @@ class InventoryWindow(ui.ScriptWindow):
def Close(self):
self.Hide()
def __AbortSashAbsorb(self, showMessage=True):
if self.sashAbsorbSlot == -1:
return
self.sashAbsorbSlot = -1
if showMessage:
chat.AppendChat(chat.CHAT_TYPE_INFO, "Sash absorb canceled.")
def __IsSashItem(self, slotIndex):
itemVnum = player.GetItemIndex(slotIndex)
if not itemVnum:
return False
item.SelectItem(itemVnum)
return item.GetItemType() == item.ITEM_TYPE_COSTUME and item.GetItemSubType() == item.COSTUME_TYPE_SASH
def __CanAbsorbIntoSash(self, sashSlot, targetSlot):
if sashSlot == targetSlot:
return False
if player.IsEquipmentSlot(sashSlot) or player.IsEquipmentSlot(targetSlot):
return False
if player.GetItemMetinSocket(sashSlot, 0):
return False
targetVnum = player.GetItemIndex(targetSlot)
if not targetVnum:
return False
item.SelectItem(targetVnum)
itemType = item.GetItemType()
itemSubType = item.GetItemSubType()
if itemType == item.ITEM_TYPE_WEAPON:
return itemSubType != item.WEAPON_ARROW
if itemType != item.ITEM_TYPE_ARMOR:
return False
return itemSubType in (
item.ARMOR_BODY,
item.ARMOR_HEAD,
item.ARMOR_SHIELD,
item.ARMOR_WRIST,
item.ARMOR_FOOTS,
item.ARMOR_NECK,
item.ARMOR_EAR,
)
def __BeginSashAbsorb(self, sashSlot):
if player.IsEquipmentSlot(sashSlot):
chat.AppendChat(chat.CHAT_TYPE_INFO, "Unequip the sash before absorbing bonuses.")
return
if player.GetItemMetinSocket(sashSlot, 0):
chat.AppendChat(chat.CHAT_TYPE_INFO, "This sash already contains absorbed bonuses.")
return
if self.sashAbsorbSlot == sashSlot:
self.__AbortSashAbsorb()
return
self.sashAbsorbSlot = sashSlot
chat.AppendChat(chat.CHAT_TYPE_INFO, "Select a weapon or armor piece to absorb into the sash.")
def __OpenSashAbsorbQuestion(self, sashSlot, targetSlot):
targetVnum = player.GetItemIndex(targetSlot)
item.SelectItem(targetVnum)
targetName = item.GetItemName()
self.questionDialog = uiCommon.QuestionDialog()
self.questionDialog.SetText("Absorb bonuses from %s? The source item will be destroyed." % targetName)
self.questionDialog.SetAcceptEvent(ui.__mem_func__(self.__AcceptSashAbsorb))
self.questionDialog.SetCancelEvent(ui.__mem_func__(self.OnCloseQuestionDialog))
self.questionDialog.sashSlot = sashSlot
self.questionDialog.targetSlot = targetSlot
self.questionDialog.Open()
constInfo.SET_ITEM_QUESTION_DIALOG_STATUS(1)
def __AcceptSashAbsorb(self):
net.SendChatPacket("/sash absorb %d %d" % (self.questionDialog.sashSlot, self.questionDialog.targetSlot), 0)
self.__AbortSashAbsorb(False)
self.OnCloseQuestionDialog()
def SetInventoryPage(self, page):
self.inventoryPageIndex = page
self.inventoryTab[1-page].SetUp()
@@ -802,18 +701,6 @@ class InventoryWindow(ui.ScriptWindow):
itemSlotIndex = self.__InventoryLocalSlotPosToGlobalSlotPos(itemSlotIndex)
if self.sashAbsorbSlot != -1 and not mouseModule.mouseController.isAttached():
if itemSlotIndex == self.sashAbsorbSlot:
self.__AbortSashAbsorb()
return
if self.__CanAbsorbIntoSash(self.sashAbsorbSlot, itemSlotIndex):
self.__OpenSashAbsorbQuestion(self.sashAbsorbSlot, itemSlotIndex)
else:
chat.AppendChat(chat.CHAT_TYPE_INFO, "Only unequipped weapon or armor items can be absorbed.")
self.__AbortSashAbsorb(False)
return
if mouseModule.mouseController.isAttached():
attachedSlotType = mouseModule.mouseController.GetAttachedType()
attachedSlotPos = mouseModule.mouseController.GetAttachedSlotNumber()
@@ -954,6 +841,20 @@ class InventoryWindow(ui.ScriptWindow):
scrollIndex = player.GetItemIndex(scrollSlotPos)
targetIndex = player.GetItemIndex(targetSlotPos)
if player.REFINE_OK != player.CanRefine(scrollIndex, targetSlotPos):
return
###########################################################
self.__SendUseItemToItemPacket(scrollSlotPos, targetSlotPos)
#net.SendItemUseToItemPacket(scrollSlotPos, targetSlotPos)
return
###########################################################
###########################################################
#net.SendRequestRefineInfoPacket(targetSlotPos)
#return
###########################################################
result = player.CanRefine(scrollIndex, targetSlotPos)
if player.REFINE_ALREADY_MAX_SOCKET_COUNT == result:
@@ -978,12 +879,7 @@ class InventoryWindow(ui.ScriptWindow):
if player.REFINE_OK != result:
return
item.SelectItem(targetIndex)
if item.GetItemType() == item.ITEM_TYPE_METIN:
self.stoneQueueDialog.Open(scrollSlotPos, targetSlotPos)
return
self.__SendUseItemToItemPacket(scrollSlotPos, targetSlotPos)
self.refineDialog.Open(scrollSlotPos, targetSlotPos)
def DetachMetinFromItem(self, scrollSlotPos, targetSlotPos):
scrollIndex = player.GetItemIndex(scrollSlotPos)
@@ -1310,11 +1206,6 @@ class InventoryWindow(ui.ScriptWindow):
def __UseItem(self, slotIndex):
ItemVNum = player.GetItemIndex(slotIndex)
item.SelectItem(ItemVNum)
if self.__IsSashItem(slotIndex):
self.__BeginSashAbsorb(slotIndex)
return
if item.IsFlag(item.ITEM_FLAG_CONFIRM_WHEN_USE):
self.questionDialog = uiCommon.QuestionDialog()
self.questionDialog.SetText(localeInfo.INVENTORY_REALLY_USE_ITEM)

View File

@@ -222,15 +222,6 @@ class MiniMap(ui.ScriptWindow):
self.tooltipAtlasOpen = MapTextToolTip()
self.tooltipAtlasOpen.SetText(localeInfo.MINIMAP_SHOW_AREAMAP)
self.tooltipAtlasOpen.Show()
self.tooltipBiolog = MapTextToolTip()
self.tooltipBiolog.SetText("Biolog")
self.tooltipBiolog.Show()
self.tooltipTeleport = MapTextToolTip()
self.tooltipTeleport.SetText("Teleport")
self.tooltipTeleport.Show()
self.tooltipSwitchbot = MapTextToolTip()
self.tooltipSwitchbot.SetText("Switchbot")
self.tooltipSwitchbot.Show()
self.tooltipInfo = MapTextToolTip()
self.tooltipInfo.Show()
@@ -268,21 +259,12 @@ class MiniMap(ui.ScriptWindow):
self.MiniMapHideButton = 0
self.MiniMapShowButton = 0
self.AtlasShowButton = 0
self.BiologButton = 0
self.TeleportButton = 0
self.SwitchbotButton = 0
self.biologButtonEvent = None
self.teleportButtonEvent = None
self.switchbotButtonEvent = None
self.tooltipMiniMapOpen = 0
self.tooltipMiniMapClose = 0
self.tooltipScaleUp = 0
self.tooltipScaleDown = 0
self.tooltipAtlasOpen = 0
self.tooltipBiolog = 0
self.tooltipTeleport = 0
self.tooltipSwitchbot = 0
self.tooltipInfo = None
self.serverInfo = None
@@ -364,39 +346,6 @@ class MiniMap(ui.ScriptWindow):
if miniMap.IsAtlas():
self.AtlasShowButton.SetEvent(ui.__mem_func__(self.ShowAtlas))
self.BiologButton = ui.Button()
self.BiologButton.SetParent(self.OpenWindow)
self.BiologButton.SetPosition(9, 111)
self.BiologButton.SetUpVisual("d:/ymir work/ui/public/small_thin_button_01.sub")
self.BiologButton.SetOverVisual("d:/ymir work/ui/public/small_thin_button_02.sub")
self.BiologButton.SetDownVisual("d:/ymir work/ui/public/small_thin_button_03.sub")
self.BiologButton.SetText("Bio")
if self.biologButtonEvent:
self.BiologButton.SetEvent(self.biologButtonEvent)
self.BiologButton.Show()
self.TeleportButton = ui.Button()
self.TeleportButton.SetParent(self.OpenWindow)
self.TeleportButton.SetPosition(9, 132)
self.TeleportButton.SetUpVisual("d:/ymir work/ui/public/small_thin_button_01.sub")
self.TeleportButton.SetOverVisual("d:/ymir work/ui/public/small_thin_button_02.sub")
self.TeleportButton.SetDownVisual("d:/ymir work/ui/public/small_thin_button_03.sub")
self.TeleportButton.SetText("TP")
if self.teleportButtonEvent:
self.TeleportButton.SetEvent(self.teleportButtonEvent)
self.TeleportButton.Show()
self.SwitchbotButton = ui.Button()
self.SwitchbotButton.SetParent(self.OpenWindow)
self.SwitchbotButton.SetPosition(9, 153)
self.SwitchbotButton.SetUpVisual("d:/ymir work/ui/public/small_thin_button_01.sub")
self.SwitchbotButton.SetOverVisual("d:/ymir work/ui/public/small_thin_button_02.sub")
self.SwitchbotButton.SetDownVisual("d:/ymir work/ui/public/small_thin_button_03.sub")
self.SwitchbotButton.SetText("Sw")
if self.switchbotButtonEvent:
self.SwitchbotButton.SetEvent(self.switchbotButtonEvent)
self.SwitchbotButton.Show()
(ButtonPosX, ButtonPosY) = self.MiniMapShowButton.GetGlobalPosition()
self.tooltipMiniMapOpen.SetTooltipPosition(ButtonPosX, ButtonPosY)
@@ -412,15 +361,6 @@ class MiniMap(ui.ScriptWindow):
(ButtonPosX, ButtonPosY) = self.AtlasShowButton.GetGlobalPosition()
self.tooltipAtlasOpen.SetTooltipPosition(ButtonPosX, ButtonPosY)
(ButtonPosX, ButtonPosY) = self.BiologButton.GetGlobalPosition()
self.tooltipBiolog.SetTooltipPosition(ButtonPosX, ButtonPosY)
(ButtonPosX, ButtonPosY) = self.TeleportButton.GetGlobalPosition()
self.tooltipTeleport.SetTooltipPosition(ButtonPosX, ButtonPosY)
(ButtonPosX, ButtonPosY) = self.SwitchbotButton.GetGlobalPosition()
self.tooltipSwitchbot.SetTooltipPosition(ButtonPosX, ButtonPosY)
self.ShowMiniMap()
def Destroy(self):
@@ -501,21 +441,6 @@ class MiniMap(ui.ScriptWindow):
else:
self.tooltipAtlasOpen.Hide()
if True == self.BiologButton.IsIn():
self.tooltipBiolog.Show()
else:
self.tooltipBiolog.Hide()
if True == self.TeleportButton.IsIn():
self.tooltipTeleport.Show()
else:
self.tooltipTeleport.Hide()
if True == self.SwitchbotButton.IsIn():
self.tooltipSwitchbot.Show()
else:
self.tooltipSwitchbot.Hide()
def OnRender(self):
(x, y) = self.GetGlobalPosition()
fx = float(x)
@@ -560,18 +485,3 @@ class MiniMap(ui.ScriptWindow):
self.AtlasWindow.Hide()
else:
self.AtlasWindow.Show()
def SetBiologButtonEvent(self, event):
self.biologButtonEvent = event
if self.BiologButton:
self.BiologButton.SetEvent(event)
def SetTeleportButtonEvent(self, event):
self.teleportButtonEvent = event
if self.TeleportButton:
self.TeleportButton.SetEvent(event)
def SetSwitchbotButtonEvent(self, event):
self.switchbotButtonEvent = event
if self.SwitchbotButton:
self.SwitchbotButton.SetEvent(event)

View File

@@ -22,7 +22,7 @@ class PlayerGauge(ui.Gauge):
self.SetPosition(-100, -100)
ui.Gauge.Hide(self)
def __UpdateScreenPosition(self):
def OnUpdate(self):
playerIndex = player.GetMainCharacterIndex()
(x, y, z)=textTail.GetPosition(playerIndex)
@@ -30,14 +30,6 @@ class PlayerGauge(ui.Gauge):
isChat = textTail.IsChat(playerIndex)
ui.Gauge.SetPosition(self, int(x - self.GetWidth() // 2), int(y + 5) + isChat * 17)
def OnUpdate(self):
self.__UpdateScreenPosition()
def OnRender(self):
# Refresh the anchor every render so the gauge tracks interpolated
# character/text-tail positions on high refresh displays.
self.__UpdateScreenPosition()
def RefreshGauge(self):
self.curHP = player.GetStatus(player.HP)

View File

@@ -94,32 +94,24 @@ class PrivateShopAdvertisementBoard(ui.ThinBoard):
net.SendOnClickPacket(self.vid)
return True
def __UpdateProjectedPosition(self):
def OnUpdate(self):
if not self.vid:
return
projectVID = None
if systemSetting.IsShowSalesText():
projectVID = self.vid
elif player.GetMainCharacterIndex() == self.vid:
projectVID = player.GetMainCharacterIndex()
if projectVID is None:
self.Hide()
return
self.Show()
x, y = chr.GetProjectPosition(projectVID, 220)
self.SetPosition(x - self.GetWidth()/2, y - self.GetHeight()/2)
self.Show()
x, y = chr.GetProjectPosition(self.vid, 220)
self.SetPosition(x - self.GetWidth()/2, y - self.GetHeight()/2)
def OnUpdate(self):
self.__UpdateProjectedPosition()
def OnRender(self):
# Keep the board anchored to the interpolated render position instead
# of the legacy fixed update cadence.
self.__UpdateProjectedPosition()
else:
for key in list(g_privateShopAdvertisementBoardDict.keys()):
if player.GetMainCharacterIndex() == key: # When the private shop is visible and closed, the player's own shop ID changes.
g_privateShopAdvertisementBoardDict[key].Show()
x, y = chr.GetProjectPosition(player.GetMainCharacterIndex(), 220)
g_privateShopAdvertisementBoardDict[key].SetPosition(x - self.GetWidth()/2, y - self.GetHeight()/2)
else:
g_privateShopAdvertisementBoardDict[key].Hide()
class PrivateShopBuilder(ui.ScriptWindow):

View File

@@ -41,9 +41,6 @@ class OptionDialog(ui.ScriptWindow):
self.cameraModeButtonList = []
self.fogModeButtonList = []
self.tilingModeButtonList = []
self.renderFPSButtonList = []
self.renderFPSValues = [60, 120, 144, 240, 0]
self.performanceHUDButtonList = []
self.ctrlShadowQuality = 0
def Destroy(self):
@@ -75,13 +72,6 @@ class OptionDialog(ui.ScriptWindow):
self.fogModeButtonList.append(GetObject("fog_level2"))
self.tilingModeButtonList.append(GetObject("tiling_cpu"))
self.tilingModeButtonList.append(GetObject("tiling_gpu"))
self.renderFPSButtonList.append(GetObject("render_fps_60"))
self.renderFPSButtonList.append(GetObject("render_fps_120"))
self.renderFPSButtonList.append(GetObject("render_fps_144"))
self.renderFPSButtonList.append(GetObject("render_fps_240"))
self.renderFPSButtonList.append(GetObject("render_fps_max"))
self.performanceHUDButtonList.append(GetObject("performance_hud_off"))
self.performanceHUDButtonList.append(GetObject("performance_hud_on"))
self.tilingApplyButton=GetObject("tiling_apply")
#self.ctrlShadowQuality = GetObject("shadow_bar")
except:
@@ -116,13 +106,6 @@ class OptionDialog(ui.ScriptWindow):
self.tilingModeButtonList[0].SAFE_SetEvent(self.__OnClickTilingModeCPUButton)
self.tilingModeButtonList[1].SAFE_SetEvent(self.__OnClickTilingModeGPUButton)
self.renderFPSButtonList[0].SAFE_SetEvent(self.__OnClickRenderFPS60Button)
self.renderFPSButtonList[1].SAFE_SetEvent(self.__OnClickRenderFPS120Button)
self.renderFPSButtonList[2].SAFE_SetEvent(self.__OnClickRenderFPS144Button)
self.renderFPSButtonList[3].SAFE_SetEvent(self.__OnClickRenderFPS240Button)
self.renderFPSButtonList[4].SAFE_SetEvent(self.__OnClickRenderFPSMaxButton)
self.performanceHUDButtonList[0].SAFE_SetEvent(self.__OnClickPerformanceHUDOffButton)
self.performanceHUDButtonList[1].SAFE_SetEvent(self.__OnClickPerformanceHUDOnButton)
self.tilingApplyButton.SAFE_SetEvent(self.__OnClickTilingApplyButton)
@@ -132,7 +115,6 @@ class OptionDialog(ui.ScriptWindow):
self.__ClickRadioButton(self.fogModeButtonList, systemSetting.GetFogLevel())
# MR-14: -- END OF -- Fog update by Alaric
self.__ClickRadioButton(self.cameraModeButtonList, constInfo.GET_CAMERA_MAX_DISTANCE_INDEX())
self.RefreshRenderSettings()
if musicInfo.fieldMusic==musicInfo.METIN2THEMA:
self.selectMusicFile.SetText(uiSelectMusic.DEFAULT_THEMA)
@@ -197,41 +179,6 @@ class OptionDialog(ui.ScriptWindow):
self.__ClickRadioButton(self.fogModeButtonList, index)
def __ApplyAndSaveConfig(self):
systemSetting.ApplyConfig()
systemSetting.SaveConfig()
def __GetRenderFPSButtonIndex(self):
renderFPS = systemSetting.GetRenderFPS()
if renderFPS <= 0:
return len(self.renderFPSValues) - 1
try:
return self.renderFPSValues.index(renderFPS)
except ValueError:
bestIndex = 0
bestDistance = abs(self.renderFPSValues[0] - renderFPS)
for index in xrange(len(self.renderFPSValues) - 1):
distance = abs(self.renderFPSValues[index] - renderFPS)
if distance < bestDistance:
bestIndex = index
bestDistance = distance
return bestIndex
def RefreshRenderSettings(self):
self.__ClickRadioButton(self.renderFPSButtonList, self.__GetRenderFPSButtonIndex())
self.__ClickRadioButton(self.performanceHUDButtonList, 1 if systemSetting.IsShowPerformanceHUD() else 0)
def __SetRenderFPS(self, fps):
systemSetting.SetRenderFPS(fps)
self.__ApplyAndSaveConfig()
self.RefreshRenderSettings()
def __SetPerformanceHUD(self, isVisible):
systemSetting.SetShowPerformanceHUDFlag(1 if isVisible else 0)
self.__ApplyAndSaveConfig()
self.RefreshRenderSettings()
def __OnClickCameraModeShortButton(self):
self.__SetCameraMode(0)
@@ -247,27 +194,6 @@ class OptionDialog(ui.ScriptWindow):
def __OnClickFogModeLevel2Button(self):
self.__SetFogLevel(2)
def __OnClickRenderFPS60Button(self):
self.__SetRenderFPS(60)
def __OnClickRenderFPS120Button(self):
self.__SetRenderFPS(120)
def __OnClickRenderFPS144Button(self):
self.__SetRenderFPS(144)
def __OnClickRenderFPS240Button(self):
self.__SetRenderFPS(240)
def __OnClickRenderFPSMaxButton(self):
self.__SetRenderFPS(0)
def __OnClickPerformanceHUDOffButton(self):
self.__SetPerformanceHUD(False)
def __OnClickPerformanceHUDOnButton(self):
self.__SetPerformanceHUD(True)
def __OnChangeMusic(self, fileName):
self.selectMusicFile.SetText(fileName[:MUSIC_FILENAME_MAX_LEN])
@@ -313,8 +239,6 @@ class OptionDialog(ui.ScriptWindow):
return True
def Show(self):
self.RefreshRenderSettings()
self.__SetCurTilingMode()
ui.ScriptWindow.Show(self)
def Close(self):

View File

@@ -20,14 +20,6 @@ import constInfo
WARP_SCROLLS = [22011, 22000, 22010]
TALISMAN_ELEMENT_NAMES = {
1: "Fire",
2: "Ice",
3: "Lightning",
4: "Wind",
5: "Earth",
}
# Tooltip max width when descriptions are long
DESC_MAX_WIDTH = 280
@@ -1029,14 +1021,12 @@ class ItemToolTip(ToolTip):
isCostumeItem = 0
isCostumeHair = 0
isCostumeBody = 0
isCostumeSash = 0
if app.ENABLE_COSTUME_SYSTEM:
if item.ITEM_TYPE_COSTUME == itemType:
isCostumeItem = 1
isCostumeHair = item.COSTUME_TYPE_HAIR == itemSubType
isCostumeBody = item.COSTUME_TYPE_BODY == itemSubType
isCostumeSash = item.COSTUME_TYPE_SASH == itemSubType
#dbg.TraceError("IS_COSTUME_ITEM! body(%d) hair(%d)" % (isCostumeBody, isCostumeHair))
@@ -1127,16 +1117,6 @@ class ItemToolTip(ToolTip):
self.__AppendAccessoryMetinSlotInfo(metinSlot, constInfo.GET_BELT_MATERIAL_VNUM(itemVnum))
### Talisman ###
elif 26 == itemType:
self.__AppendLimitInformation()
self.__AppendAffectInformation()
self.__AppendAttributeInformation(attrSlot)
self.AppendWearableInformation()
self.AppendSpace(5)
elementName = TALISMAN_ELEMENT_NAMES.get(item.GetValue(0), "Unknown")
self.AppendTextLine("Element: %s" % elementName, self.NORMAL_COLOR)
## Costume Item ##
elif 0 != isCostumeItem:
self.__AppendLimitInformation()
@@ -1144,15 +1124,6 @@ class ItemToolTip(ToolTip):
self.__AppendAttributeInformation(attrSlot)
self.AppendWearableInformation()
if isCostumeSash:
self.AppendSpace(5)
absorbPct = metinSlot[1] if metinSlot and metinSlot[1] else item.GetValue(0)
self.AppendTextLine("Absorb rate: %d%%" % absorbPct, self.NORMAL_COLOR)
if metinSlot and metinSlot[0]:
self.AppendTextLine("Absorbed item vnum: %d" % metinSlot[0], self.NORMAL_COLOR)
else:
self.AppendTextLine("Right-click the sash, then select an item to absorb.", self.CONDITION_COLOR)
bHasRealtimeFlag = 0
for i in range(item.LIMIT_MAX_NUM):

View File

@@ -1,5 +1,5 @@
ScriptType CharacterSoundInformation
SoundDataCount 2
SoundData00 0.231000 "sound/pc2/assassin/dualhand_sword/combo4.wav"
SoundData01 0.198000 "sound/common/swing/a_dh_c_4.wav"
SoundData00 0.066000 "sound/pc2/assassin/dualhand_sword/combo7.wav"
SoundData01 0.033000 "sound/common/swing/w_1h_c_2.wav"

View File

@@ -1,6 +1,8 @@
import uiScriptLocale
import item
COSTUME_START_INDEX = item.COSTUME_SLOT_START
window = {
"name" : "CostumeWindow",
@@ -69,9 +71,9 @@ window = {
"height" : 145,
"slot" : (
{"index":item.COSTUME_SLOT_BODY, "x":61, "y":45, "width":32, "height":64},
{"index":item.COSTUME_SLOT_HAIR, "x":61, "y": 8, "width":32, "height":32},
{"index":item.COSTUME_SLOT_SASH, "x":5, "y":145, "width":32, "height":32},
{"index":COSTUME_START_INDEX+0, "x":61, "y":45, "width":32, "height":64},
{"index":COSTUME_START_INDEX+1, "x":61, "y": 8, "width":32, "height":32},
{"index":COSTUME_START_INDEX+2, "x":5, "y":145, "width":32, "height":32},
),
},
),

View File

@@ -69,8 +69,6 @@ window = {
##{"index":22, "x":75, "y":106, "width":32, "height":32},
## 새 벨트
{"index":23, "x":39, "y":106, "width":32, "height":32},
{"index":24, "x":2, "y":106, "width":32, "height":32},
{"index":25, "x":75, "y":106, "width":32, "height":32},
),
},

View File

@@ -20,7 +20,7 @@ window = {
"y" : 0,
"width" : 300,
"height" : 25*12+8,
"height" : 25*11+8,
"children" :
(
@@ -32,7 +32,7 @@ window = {
"y" : 0,
"width" : 300,
"height" : 25*12+8,
"height" : 25*11+8,
"children" :
(
@@ -433,29 +433,7 @@ window = {
"default_image" : ROOT_PATH + "middle_button_01.sub",
"over_image" : ROOT_PATH + "middle_button_02.sub",
"down_image" : ROOT_PATH + "middle_button_03.sub",
},
{
"name" : "autopickup_label",
"type" : "text",
"x" : LINE_LABEL_X,
"y" : 265+2,
"text" : "Auto Pickup",
},
{
"name" : "autopickup_button",
"type" : "button",
"x" : LINE_DATA_X,
"y" : 265,
"text" : "Configure",
"default_image" : ROOT_PATH + "middle_button_01.sub",
"over_image" : ROOT_PATH + "middle_button_02.sub",
"down_image" : ROOT_PATH + "middle_button_03.sub",
},
},
),
},
),

View File

@@ -91,7 +91,6 @@ window = {
# {"index":item.EQUIPMENT_RING2, "x":75, "y":106, "width":32, "height":32},
## »ő ş§Ć®
{"index":item.EQUIPMENT_BELT, "x":39, "y":106, "width":32, "height":32},
{"index":EQUIPMENT_START_INDEX+25, "x":75, "y":106, "width":32, "height":32},
),
},
## Dragon Soul Button

View File

@@ -1,11 +1,6 @@
import uiScriptLocale
ROOT_PATH = "d:/ymir work/ui/public/"
OPTION_RENDER_FPS = getattr(uiScriptLocale, "OPTION_RENDER_FPS", "Render FPS")
OPTION_RENDER_FPS_MAX = getattr(uiScriptLocale, "OPTION_RENDER_FPS_MAX", "MAX")
OPTION_PERFORMANCE_HUD = getattr(uiScriptLocale, "OPTION_PERFORMANCE_HUD", "Perf HUD")
OPTION_ON = getattr(uiScriptLocale, "OPTION_ON", "On")
OPTION_OFF = getattr(uiScriptLocale, "OPTION_OFF", "Off")
TEMPORARY_X = +13
TEXT_TEMPORARY_X = -10
@@ -20,7 +15,7 @@ window = {
"y" : 0,
"width" : 305,
"height" : 295,
"height" : 255,
"children" :
(
@@ -32,7 +27,7 @@ window = {
"y" : 0,
"width" : 305,
"height" : 295,
"height" : 255,
"children" :
(
@@ -266,124 +261,6 @@ window = {
"down_image" : ROOT_PATH + "middle_Button_03.sub",
},
{
"name" : "render_fps_mode",
"type" : "text",
"x" : 18,
"y" : 210 + 2,
"text" : OPTION_RENDER_FPS,
},
{
"name" : "render_fps_60",
"type" : "radio_button",
"x" : 82,
"y" : 210,
"text" : "60",
"default_image" : ROOT_PATH + "small_Button_01.sub",
"over_image" : ROOT_PATH + "small_Button_02.sub",
"down_image" : ROOT_PATH + "small_Button_03.sub",
},
{
"name" : "render_fps_120",
"type" : "radio_button",
"x" : 124,
"y" : 210,
"text" : "120",
"default_image" : ROOT_PATH + "small_Button_01.sub",
"over_image" : ROOT_PATH + "small_Button_02.sub",
"down_image" : ROOT_PATH + "small_Button_03.sub",
},
{
"name" : "render_fps_144",
"type" : "radio_button",
"x" : 166,
"y" : 210,
"text" : "144",
"default_image" : ROOT_PATH + "small_Button_01.sub",
"over_image" : ROOT_PATH + "small_Button_02.sub",
"down_image" : ROOT_PATH + "small_Button_03.sub",
},
{
"name" : "render_fps_240",
"type" : "radio_button",
"x" : 208,
"y" : 210,
"text" : "240",
"default_image" : ROOT_PATH + "small_Button_01.sub",
"over_image" : ROOT_PATH + "small_Button_02.sub",
"down_image" : ROOT_PATH + "small_Button_03.sub",
},
{
"name" : "render_fps_max",
"type" : "radio_button",
"x" : 250,
"y" : 210,
"text" : OPTION_RENDER_FPS_MAX,
"default_image" : ROOT_PATH + "small_Button_01.sub",
"over_image" : ROOT_PATH + "small_Button_02.sub",
"down_image" : ROOT_PATH + "small_Button_03.sub",
},
{
"name" : "performance_hud_mode",
"type" : "text",
"x" : 18,
"y" : 235 + 2,
"text" : OPTION_PERFORMANCE_HUD,
},
{
"name" : "performance_hud_off",
"type" : "radio_button",
"x" : 110,
"y" : 235,
"text" : OPTION_OFF,
"default_image" : ROOT_PATH + "small_Button_01.sub",
"over_image" : ROOT_PATH + "small_Button_02.sub",
"down_image" : ROOT_PATH + "small_Button_03.sub",
},
{
"name" : "performance_hud_on",
"type" : "radio_button",
"x" : 160,
"y" : 235,
"text" : OPTION_ON,
"default_image" : ROOT_PATH + "small_Button_01.sub",
"over_image" : ROOT_PATH + "small_Button_02.sub",
"down_image" : ROOT_PATH + "small_Button_03.sub",
},
## 그림자
# {

View File

@@ -163,14 +163,23 @@ Files under `.updates/` are created by the launcher. The user shouldn't touch th
1. On a trusted machine (not random laptop), with the private signing key present:
```bash
./scripts/make-release.sh --version 2026.04.14-1 --source /path/to/fresh/client
./scripts/make-release.sh --source /path/to/fresh/client --version 2026.04.14-1 \
--previous 2026.04.13-3 --notes notes.md --dry-run
```
2. The script walks the client directory, computes sha256 for each file, writes a `manifest.json`, signs it, and produces a release directory `release/2026.04.14-1/` containing the manifest, its signature, and only the new blobs (ones not already present on the server).
[`scripts/make-release.sh`](../scripts/make-release.sh) is the single entry
point for the v1 manual flow. It drives `make-manifest.py` + `sign-manifest.py`,
builds the content-addressed blob tree under `files/<hash[0:2]>/<hash>` with
hardlink-based deduplication, archives the signed manifest into
`manifests/<version>.json`, and — unless `--dry-run` is passed — rsyncs the
blob tree first and the `manifest.json` + `manifest.json.sig` pair last so the
release becomes visible atomically. Flags: `--key` (default
`~/.config/metin/launcher-signing-key`, must be mode 600), `--out` (default
`/tmp/release-<version>`), `--force` to overwrite a non-empty out dir, `--yes`
to skip the interactive rsync confirmation, `--rsync-target <user@host:/path>`
to override the upload destination.
2. The script walks the client directory, computes sha256 for each file, writes a `manifest.json`, signs it, and produces a release directory containing the manifest, its signature, and the deduplicated blob tree.
3. Human review: diff the new manifest against the previous one, sanity-check size and file count.
4. `rsync` the release directory to the VPS:
```bash
rsync -av release/2026.04.14-1/ mt2.jakubkadlec.dev@mt2.jakubkadlec.dev:/var/www/updates.jakubkadlec.dev/
```
4. Re-run without `--dry-run` (same args) to rsync to the VPS. The script prints the target and waits for confirmation unless `--yes` is passed.
5. Verify from a second machine: `curl` the manifest, check signature, check a random blob.
6. Tag the release in git.

169
scripts/make-release.sh Executable file
View File

@@ -0,0 +1,169 @@
#!/usr/bin/env bash
# make-release.sh — assemble, sign, and (optionally) publish a client release.
#
# Drives scripts/make-manifest.py + scripts/sign-manifest.py, then builds the
# content-addressed blob tree the launcher pulls from. See docs/update-manager.md
# for the end-to-end design; this script is the v1 manual publishing flow.
#
# Usage:
# scripts/make-release.sh --source <client-dir> --version <version> \
# [--previous <version>] [--notes <file>] \
# [--key <path>] [--out <path>] [--force] \
# [--dry-run] [--yes] [--rsync-target <user@host:/path>]
set -euo pipefail
# -------- arg parsing --------
SOURCE=""
VERSION=""
PREVIOUS=""
NOTES_FILE=""
KEY="${HOME}/.config/metin/launcher-signing-key"
OUT=""
FORCE=0
DRY_RUN=0
YES=0
RSYNC_TARGET="mt2.jakubkadlec.dev@mt2.jakubkadlec.dev:/var/www/updates.jakubkadlec.dev/"
die() { echo "error: $*" >&2; exit 1; }
say() { echo "[make-release] $*"; }
while [[ $# -gt 0 ]]; do
case "$1" in
--source) SOURCE="$2"; shift 2 ;;
--version) VERSION="$2"; shift 2 ;;
--previous) PREVIOUS="$2"; shift 2 ;;
--notes) NOTES_FILE="$2"; shift 2 ;;
--key) KEY="$2"; shift 2 ;;
--out) OUT="$2"; shift 2 ;;
--force) FORCE=1; shift ;;
--dry-run) DRY_RUN=1; shift ;;
--yes) YES=1; shift ;;
--rsync-target) RSYNC_TARGET="$2"; shift 2 ;;
-h|--help) sed -n '1,15p' "$0"; exit 0 ;;
*) die "unknown arg: $1" ;;
esac
done
[[ -n "$SOURCE" ]] || die "--source is required"
[[ -n "$VERSION" ]] || die "--version is required"
[[ -d "$SOURCE" ]] || die "source dir does not exist: $SOURCE"
[[ -f "$SOURCE/Metin2.exe" ]] || die "source does not look like a client dir (missing Metin2.exe): $SOURCE"
[[ -f "$KEY" ]] || die "signing key not found: $KEY"
KEY_MODE=$(stat -c '%a' "$KEY")
[[ "$KEY_MODE" == "600" ]] || die "signing key $KEY must be mode 600, got $KEY_MODE"
[[ -n "$NOTES_FILE" && ! -f "$NOTES_FILE" ]] && die "notes file not found: $NOTES_FILE"
: "${OUT:=/tmp/release-${VERSION}}"
SOURCE=$(cd "$SOURCE" && pwd)
OUT_ABS=$(mkdir -p "$OUT" && cd "$OUT" && pwd)
if [[ -n "$(ls -A "$OUT_ABS" 2>/dev/null)" && "$FORCE" -ne 1 ]]; then
die "output directory $OUT_ABS is non-empty (use --force to overwrite)"
fi
SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd)
say "source: $SOURCE"
say "version: $VERSION"
say "out: $OUT_ABS"
say "key: $KEY"
# -------- [1/6] manifest --------
say "[1/6] building manifest"
mkdir -p "$OUT_ABS/manifests" "$OUT_ABS/files"
MANIFEST="$OUT_ABS/manifest.json"
mk_args=(--source "$SOURCE" --version "$VERSION" --out "$MANIFEST")
[[ -n "$PREVIOUS" ]] && mk_args+=(--previous "$PREVIOUS")
if [[ -n "$NOTES_FILE" ]]; then
notes_text=$(cat "$NOTES_FILE")
mk_args+=(--notes "$notes_text")
fi
python3 "$SCRIPT_DIR/make-manifest.py" "${mk_args[@]}"
# -------- [2/6] sign --------
say "[2/6] signing manifest"
python3 "$SCRIPT_DIR/sign-manifest.py" --manifest "$MANIFEST" --key "$KEY"
SIG="$MANIFEST.sig"
[[ -f "$SIG" ]] || die "signature not produced"
sig_len=$(stat -c '%s' "$SIG")
[[ "$sig_len" == "64" ]] || die "signature is $sig_len bytes, expected 64"
# -------- [3/6] archive historical manifest --------
say "[3/6] archiving historical manifest -> manifests/${VERSION}.json"
cp -f "$MANIFEST" "$OUT_ABS/manifests/${VERSION}.json"
cp -f "$SIG" "$OUT_ABS/manifests/${VERSION}.json.sig"
# -------- [4/6] blob tree --------
say "[4/6] building content-addressed blob tree"
# Extract (path, sha256) pairs for launcher + every file entry.
mapfile -t PAIRS < <(jq -r '
([.launcher] + .files)
| .[]
| "\(.sha256)\t\(.path)"
' "$MANIFEST")
total_entries=${#PAIRS[@]}
unique_count=0
dedup_count=0
bytes_written=0
declare -A SEEN
for pair in "${PAIRS[@]}"; do
hash="${pair%%$'\t'*}"
rel="${pair#*$'\t'}"
src="$SOURCE/$rel"
[[ -f "$src" ]] || die "file in manifest missing from source: $rel"
if [[ -n "${SEEN[$hash]:-}" ]]; then
dedup_count=$((dedup_count + 1))
continue
fi
SEEN[$hash]=1
unique_count=$((unique_count + 1))
prefix="${hash:0:2}"
dst_dir="$OUT_ABS/files/$prefix"
dst="$dst_dir/$hash"
mkdir -p "$dst_dir"
if [[ -f "$dst" ]]; then
continue
fi
# Try hardlink first, fall back to copy across filesystems.
if ! cp -l "$src" "$dst" 2>/dev/null; then
cp "$src" "$dst"
fi
sz=$(stat -c '%s' "$dst")
bytes_written=$((bytes_written + sz))
done
say " entries: $total_entries unique blobs: $unique_count deduped: $dedup_count"
say " bytes written: $bytes_written"
# -------- [5/6] layout summary --------
say "[5/6] final layout:"
(cd "$OUT_ABS" && find . -maxdepth 2 -mindepth 1 -printf ' %p\n' | sort | head -40)
# -------- [6/6] rsync --------
if [[ "$DRY_RUN" -eq 1 ]]; then
say "[6/6] --dry-run set, skipping rsync. target would be: $RSYNC_TARGET"
exit 0
fi
say "[6/6] rsync target: $RSYNC_TARGET"
if [[ "$YES" -ne 1 ]]; then
read -r -p "continue? [y/N] " ans
[[ "$ans" == "y" || "$ans" == "Y" ]] || die "aborted by user"
fi
# Stage 1: everything except manifest.json(.sig) — blobs and historical archive.
rsync -av --delay-updates --checksum --omit-dir-times --no-perms \
--exclude 'manifest.json' --exclude 'manifest.json.sig' \
"$OUT_ABS"/ "$RSYNC_TARGET"
# Stage 2: manifest + signature, so the new release becomes visible last.
rsync -av --checksum --omit-dir-times --no-perms \
"$MANIFEST" "$SIG" "$RSYNC_TARGET"
say "done."