각 코드에 대해서 자세하게 설명해주세요. This is very important to my career. I will tip $200. Please output in Korean.
아래 설명에 따라서 파일이 구성되어 있는 것으로 가정하고 대답해주세요.
" @@@파일명 @@@"으로 하면 파일명을 가진 시작파일을 생성한 것으로 이해하면 됩니다.
이후 "파일명"에 대한 파일 내용이 나오다가 " @@@@@@"을 만나면 해당 파일이 끝나는 겁니다.

@@@리셀금지.ct @@@
<?xml version="1.0" encoding="utf-8"?>
<CheatTable CheatEngineTableVersion="45">
  <CheatEntries>
    <CheatEntry>
      <ID>1</ID>
      <Description>"루아 스크립트 적용 -msw"</Description>
      <VariableType>Auto Assembler Script</VariableType>
      <AssemblerScript>[ENABLE]
//code from here to '[DISABLE]' will be used to enable the cheat
{$lua}
local function modify_buffer()
   local buffer_data = readString (RDX+0x20, R8)
   local name_size   = readInteger(R9+0x10)
   local script_name = readString (R9+0x14, name_size*2, true)
   --(i) now my dumper cant handle multiple files with same filename like chunk
   --(i) so added filter since user cant modify them
   if script_name == "chunk" then
      return
   end
   local my_script_path = ("C:\\GameScript\\%s.lua"):format(script_name)
   --printf("My Script: %s", my_script_path)
   local file = io.open(my_script_path, "rb")
   if file then
      local new_data   = file:read("a")
      local new_length = #new_data
      file:close()
      local new_buffer = allocateMemory(0x20 + new_length)
      writeQword(new_buffer       , readQword(RDX))
      writeQword(new_buffer+0x8   , readQword(RDX+0x8))
      writeQword(new_buffer+0x10  , readQword(RDX+0x10))
      writeQword(new_buffer+0x18  , new_length)
      writeString(new_buffer+0x20 , new_data)
      RDX = new_buffer
      R8  = new_length
      printf("Loaded Script: %s", my_script_path)
   end
end
openProcess("msw.exe")
debug_setBreakpoint(getAddress("GameAssembly.dll+2E69360"), modify_buffer)
 
 
[DISABLE]
//code from here till the end of the code will be used to disable the cheat
</AssemblerScript>
    </CheatEntry>
  </CheatEntries>
  <UserdefinedSymbols>
    <SymbolEntry>
      <Name>CRCBypass</Name>
      <Address>20CC3AE0000</Address>
    </SymbolEntry>
    <SymbolEntry>
      <Name>newcheck</Name>
      <Address>006E0200</Address>
    </SymbolEntry>
    <SymbolEntry>
      <Name>Fuck1F</Name>
      <Address>1FA789CE</Address>
    </SymbolEntry>
    <SymbolEntry>
      <Name>FuckFF1</Name>
      <Address>1FA6B03E</Address>
    </SymbolEntry>
    <SymbolEntry>
      <Name>Fuck1</Name>
      <Address>1FC20B0C</Address>
    </SymbolEntry>
    <SymbolEntry>
      <Name>door</Name>
      <Address>05520000</Address>
    </SymbolEntry>
    <SymbolEntry>
      <Name>Fuck4</Name>
      <Address>1F632E40</Address>
    </SymbolEntry>
    <SymbolEntry>
      <Name>myhack</Name>
      <Address>024E0000</Address>
    </SymbolEntry>
    <SymbolEntry>
      <Name>pspeed</Name>
      <Address>1FDFA12D</Address>
    </SymbolEntry>
    <SymbolEntry>
      <Name>autokill1</Name>
      <Address>1FBEEC6D</Address>
    </SymbolEntry>
    <SymbolEntry>
      <Name>autokill2</Name>
      <Address>1FBEEDFE</Address>
    </SymbolEntry>
    <SymbolEntry>
      <Name>plusamount</Name>
      <Address>001C1F40</Address>
    </SymbolEntry>
    <SymbolEntry>
      <Name>plus</Name>
      <Address>024E0FA0</Address>
    </SymbolEntry>
    <SymbolEntry>
      <Name>Fuck3</Name>
      <Address>1F59BD41</Address>
    </SymbolEntry>
    <SymbolEntry>
      <Name>damage</Name>
      <Address>08B90000</Address>
    </SymbolEntry>
    <SymbolEntry>
      <Name>damagevalue</Name>
      <Address>08B900D0</Address>
    </SymbolEntry>
    <SymbolEntry>
      <Name>speedvalue</Name>
      <Address>08B90270</Address>
    </SymbolEntry>
    <SymbolEntry>
      <Name>attackdamage</Name>
      <Address>096600D0</Address>
    </SymbolEntry>
    <SymbolEntry>
      <Name>attackdamagevalue</Name>
      <Address>09660270</Address>
    </SymbolEntry>
    <SymbolEntry>
      <Name>skilldamage</Name>
      <Address>43420000</Address>
    </SymbolEntry>
    <SymbolEntry>
      <Name>skilldamagevalue</Name>
      <Address>09350270</Address>
    </SymbolEntry>
    <SymbolEntry>
      <Name>SkillRange</Name>
      <Address>434200D0</Address>
    </SymbolEntry>
    <SymbolEntry>
      <Name>SkillRange_</Name>
      <Address>434201A0</Address>
    </SymbolEntry>
    <SymbolEntry>
      <Name>MPInfinity</Name>
      <Address>43420340</Address>
    </SymbolEntry>
    <SymbolEntry>
      <Name>MPInfinity2</Name>
      <Address>0BA90000</Address>
    </SymbolEntry>
    <SymbolEntry>
      <Name>ItemBOX</Name>
      <Address>43420410</Address>
    </SymbolEntry>
    <SymbolEntry>
      <Name>SkillCasting</Name>
      <Address>43420270</Address>
    </SymbolEntry>
    <SymbolEntry>
      <Name>AttackDamage_</Name>
      <Address>096601A0</Address>
    </SymbolEntry>
    <SymbolEntry>
      <Name>Fuck5</Name>
      <Address>1F5BB210</Address>
    </SymbolEntry>
    <SymbolEntry>
      <Name>mop2</Name>
      <Address>1F7C0EF2</Address>
    </SymbolEntry>
    <SymbolEntry>
      <Name>Dir5</Name>
      <Address>04D40033</Address>
    </SymbolEntry>
    <SymbolEntry>
      <Name>Fuck7</Name>
      <Address>1FC73538</Address>
    </SymbolEntry>
    <SymbolEntry>
      <Name>Fuck6</Name>
      <Address>1FB8AE52</Address>
    </SymbolEntry>
    <SymbolEntry>
      <Name>Fuc1</Name>
      <Address>005F8C5D</Address>
    </SymbolEntry>
    <SymbolEntry>
      <Name>Fuck111</Name>
      <Address>2424042C</Address>
    </SymbolEntry>
    <SymbolEntry>
      <Name>Fuck11</Name>
      <Address>004F350C</Address>
    </SymbolEntry>
    <SymbolEntry>
      <Name>savecode</Name>
      <Address>01BF0000</Address>
    </SymbolEntry>
    <SymbolEntry>
      <Name>tutorialhealth</Name>
      <Address>01BF0000</Address>
    </SymbolEntry>
    <SymbolEntry>
      <Name>regcheck_counter</Name>
      <Address>01E00000</Address>
    </SymbolEntry>
    <SymbolEntry>
      <Name>originalcode</Name>
      <Address>01E00000</Address>
    </SymbolEntry>
    <SymbolEntry>
      <Name>micheck</Name>
      <Address>01BF0000</Address>
    </SymbolEntry>
    <SymbolEntry>
      <Name>savemob</Name>
      <Address>005D0000</Address>
    </SymbolEntry>
    <SymbolEntry>
      <Name>FuckF1</Name>
      <Address>201F4876</Address>
    </SymbolEntry>
    <SymbolEntry>
      <Name>Leewonbok</Name>
      <Address>1FA6D974</Address>
    </SymbolEntry>
    <SymbolEntry>
      <Name>pointer2</Name>
      <Address>20B44F48</Address>
    </SymbolEntry>
    <SymbolEntry>
      <Name>pointer</Name>
      <Address>pointer2-30</Address>
    </SymbolEntry>
    <SymbolEntry>
      <Name>mop</Name>
      <Address>1FC4851D</Address>
    </SymbolEntry>
    <SymbolEntry>
      <Name>ia_aob1</Name>
      <Address>13FA55681</Address>
    </SymbolEntry>
    <SymbolEntry>
      <Name>pills</Name>
      <Address>0CE46EB5</Address>
    </SymbolEntry>
    <SymbolEntry>
      <Name>keys</Name>
      <Address>08119EB6</Address>
    </SymbolEntry>
    <SymbolEntry>
      <Name>None</Name>
      <Address>0B272098</Address>
    </SymbolEntry>
    <SymbolEntry>
      <Name>itemlist1</Name>
      <Address>0BEC2624</Address>
    </SymbolEntry>
    <SymbolEntry>
      <Name>itemlist2</Name>
      <Address>0BEC278E</Address>
    </SymbolEntry>
    <SymbolEntry>
      <Name>itemlist3</Name>
      <Address>0BEC2893</Address>
    </SymbolEntry>
    <SymbolEntry>
      <Name>itemlist4</Name>
      <Address>0BEC2943</Address>
    </SymbolEntry>
    <SymbolEntry>
      <Name>itemlist5</Name>
      <Address>0BEC29EF</Address>
    </SymbolEntry>
    <SymbolEntry>
      <Name>itemlist6</Name>
      <Address>0BEC2ABC</Address>
    </SymbolEntry>
    <SymbolEntry>
      <Name>itemlist7</Name>
      <Address>0BEC2B3A</Address>
    </SymbolEntry>
    <SymbolEntry>
      <Name>itemlist8</Name>
      <Address>0BEC2BC8</Address>
    </SymbolEntry>
    <SymbolEntry>
      <Name>itemlist9</Name>
      <Address>0BEC2FAB</Address>
    </SymbolEntry>
    <SymbolEntry>
      <Name>roomAdd</Name>
      <Address>0B9768EB</Address>
    </SymbolEntry>
    <SymbolEntry>
      <Name>startItem</Name>
      <Address>0B9768FA</Address>
    </SymbolEntry>
    <SymbolEntry>
      <Name>slotf</Name>
      <Address>0B993558</Address>
    </SymbolEntry>
    <SymbolEntry>
      <Name>bbank</Name>
      <Address>0B999D8C</Address>
    </SymbolEntry>
    <SymbolEntry>
      <Name>miniboss</Name>
      <Address>0B9D7B73</Address>
    </SymbolEntry>
    <SymbolEntry>
      <Name>bombs</Name>
      <Address>0B98B483</Address>
    </SymbolEntry>
    <SymbolEntry>
      <Name>Lost</Name>
      <Address>06B586BB</Address>
    </SymbolEntry>
    <SymbolEntry>
      <Name>Alt</Name>
      <Address>06D88DD2</Address>
    </SymbolEntry>
    <SymbolEntry>
      <Name>Alt2</Name>
      <Address>06D88DE1</Address>
    </SymbolEntry>
    <SymbolEntry>
      <Name>life</Name>
      <Address>095078ED</Address>
    </SymbolEntry>
    <SymbolEntry>
      <Name>hit</Name>
      <Address>09507836</Address>
    </SymbolEntry>
    <SymbolEntry>
      <Name>armor</Name>
      <Address>0950785B</Address>
    </SymbolEntry>
    <SymbolEntry>
      <Name>itempower</Name>
      <Address>0955ABC8</Address>
    </SymbolEntry>
    <SymbolEntry>
      <Name>itemp9v</Name>
      <Address>0955ABB1</Address>
    </SymbolEntry>
    <SymbolEntry>
      <Name>maxitems</Name>
      <Address>094FB019</Address>
    </SymbolEntry>
    <SymbolEntry>
      <Name>coins1</Name>
      <Address>09513251</Address>
    </SymbolEntry>
    <SymbolEntry>
      <Name>coins2</Name>
      <Address>0951381D</Address>
    </SymbolEntry>
    <SymbolEntry>
      <Name>crc</Name>
      <Address>0018DD21</Address>
    </SymbolEntry>
    <SymbolEntry>
      <Name>crc2</Name>
      <Address>0018DCBD</Address>
    </SymbolEntry>
    <SymbolEntry>
      <Name>crc3</Name>
      <Address>0018DCA3</Address>
    </SymbolEntry>
    <SymbolEntry>
      <Name>crc4</Name>
      <Address>0018DC7D</Address>
    </SymbolEntry>
    <SymbolEntry>
      <Name>MyCode</Name>
      <Address>01890000</Address>
    </SymbolEntry>
    <SymbolEntry>
      <Name>pHP</Name>
      <Address>0189010C</Address>
    </SymbolEntry>
    <SymbolEntry>
      <Name>pGold</Name>
      <Address>01890114</Address>
    </SymbolEntry>
    <SymbolEntry>
      <Name>pMana</Name>
      <Address>01890110</Address>
    </SymbolEntry>
    <SymbolEntry>
      <Name>iEnableGM</Name>
      <Address>01890100</Address>
    </SymbolEntry>
    <SymbolEntry>
      <Name>iEnableMG</Name>
      <Address>01890104</Address>
    </SymbolEntry>
    <SymbolEntry>
      <Name>iEnableMM</Name>
      <Address>01890108</Address>
    </SymbolEntry>
    <SymbolEntry>
      <Name>EFTscan</Name>
      <Address>03DF30F2</Address>
    </SymbolEntry>
    <SymbolEntry>
      <Name>attackspeed</Name>
      <Address>08B2FEBE</Address>
    </SymbolEntry>
    <SymbolEntry>
      <Name>speed</Name>
      <Address>08B7F738</Address>
    </SymbolEntry>
    <SymbolEntry>
      <Name>FASpeedUpCode</Name>
      <Address>01057BAB</Address>
    </SymbolEntry>
    <SymbolEntry>
      <Name>FASpeedUpCall</Name>
      <Address>0106E210</Address>
    </SymbolEntry>
    <SymbolEntry>
      <Name>FASpeedUpCodeDi</Name>
      <Address>01057BAB</Address>
    </SymbolEntry>
    <SymbolEntry>
      <Name>FASpeedUpCallDi</Name>
      <Address>0106E210</Address>
    </SymbolEntry>
    <SymbolEntry>
      <Name>itoc</Name>
      <Address>2B1106C8</Address>
    </SymbolEntry>
    <SymbolEntry>
      <Name>cont2</Name>
      <Address>20470018</Address>
    </SymbolEntry>
    <SymbolEntry>
      <Name>callTime</Name>
      <Address>010A29B0</Address>
    </SymbolEntry>
    <SymbolEntry>
      <Name>pPacket</Name>
      <Address>0042CAC6</Address>
    </SymbolEntry>
    <SymbolEntry>
      <Name>pCharacter</Name>
      <Address>0042C0A4</Address>
    </SymbolEntry>
    <SymbolEntry>
      <Name>retAddr</Name>
      <Address>2C96005D</Address>
    </SymbolEntry>
    <SymbolEntry>
      <Name>lpBytes</Name>
      <Address>2C960061</Address>
    </SymbolEntry>
    <SymbolEntry>
      <Name>dwLength</Name>
      <Address>2C960065</Address>
    </SymbolEntry>
    <SymbolEntry>
      <Name>packets</Name>
      <Address>2C960069</Address>
    </SymbolEntry>
    <SymbolEntry>
      <Name>EHookRet</Name>
      <Address>020ED258</Address>
    </SymbolEntry>
    <SymbolEntry>
      <Name>HookRet</Name>
      <Address>0AF70000</Address>
    </SymbolEntry>
    <SymbolEntry>
      <Name>Channel</Name>
      <Address>0D700000</Address>
    </SymbolEntry>
    <SymbolEntry>
      <Name>ConnectCNT</Name>
      <Address>02460000</Address>
    </SymbolEntry>
  </UserdefinedSymbols>
  <DisassemblerComments>
    <DisassemblerComment>
      <Address>"GameAssembly.dll"+38C18C1</Address>
      <Comment>1
</Comment>
    </DisassemblerComment>
    <DisassemblerComment>
      <Address>"GameAssembly.dll"+38C19E7</Address>
      <Comment>1
</Comment>
    </DisassemblerComment>
    <DisassemblerComment>
      <Address>"GameAssembly.dll"+38C7645</Address>
      <Comment>0
</Comment>
    </DisassemblerComment>
    <DisassemblerComment>
      <Address>"GameAssembly.dll"+38C76B0</Address>
      <Comment>0
</Comment>
    </DisassemblerComment>
    <DisassemblerComment>
      <Address>"GameAssembly.dll"+38C85E6</Address>
      <Comment>0
</Comment>
    </DisassemblerComment>
    <DisassemblerComment>
      <Address>"GameAssembly.dll"+38C860B</Address>
      <Comment>0
</Comment>
    </DisassemblerComment>
    <DisassemblerComment>
      <Address>"GameAssembly.dll"+38C861E</Address>
      <Comment>0
</Comment>
    </DisassemblerComment>
    <DisassemblerComment>
      <Address>"GameAssembly.dll"+38C86C4</Address>
      <Comment>0
</Comment>
    </DisassemblerComment>
  </DisassemblerComments>
</CheatTable>
@@@@@@
@@@GameScript/CharacterActionComponent.GetWeaponAttackSpeed.lua @@@
return function (self,magic) 
local speed
if(zz_y7.use_attack_speed) then
 return 4
end
if (magic) then
 speed = 9
else
 speed = self.CurrentWeaponAttackSpeed
end
speed += self.Entity.PlayerTemporaryStat:GetValue(_CTS.Booster)
if (speed < 5) then
 return 5
end
if (speed > 9) then
 return 9
end
return speed
end
@@@@@@
@@@GameScript/CommandLogic_Client.OnBeginPlay.lua @@@
---------- zz_y7 -------------
local zz_y7_const = {
 TEST = "테스트";
 HELP = "?";
 AUTO_PLAY = "자동사냥";
              ATTACK_SPEED = "노딜"; 
}
local print_toggle_message = function(feature_name, if_toggle_on)
 _ChatMessageLogic:Add(_ChatMessageType.System, 
 ("%s: %s"):format(
 feature_name,
 if_toggle_on and "활성화됨" or "비활성화됨"
 )
 )
end
---------- zz_y7 -------------
return function (self) 
self.ClientCommands = {
 ["도움말"] = self.Help,
 ["교환"] = self.Trade,
 ["게임교환"] = self.Trade,
 ["모두"] = self.ToAll,
 ["파티"] = self.ToParty,
 ["채널"] = self.ToChannel,
 ["귓말"] = self.ToWhisper,
 ["귓"] = self.ToWhisper,
 ["찾기"] = self.Find,
 ["파티만들기"] = self.CreateParty,
 ["파티탈퇴"] = self.LeaveParty,
 ["파티초대"] = self.InviteParty,
 ["파티강퇴"] = self.KickParty,
 [zz_y7_const.HELP] = function(self)
 --> add yourself : ) ;;
 local message = [[
 ----------------- 명령어 -----------------
 [플레이]
 /자동사냥 {핫키:F1} /몹몰이 {핫키:F2}
 /물약
 [설정]
 /안전지대 - 안전지대 좌표설정 /피굳
 /아이템 {갯수} - {갯수} 만큼의 아이템이 필드에 떨어지면 아이템을 줍습니다.
 /자사딜레이 {딜레이} - 
 /좌표설정 {x} {y} - 몬스터의 x,y 좌표에 {x},{y} 좌표만큼 더한곳으로 텔레포트 합니다.
 /HP {물약코드값} {트리거HP} - 기본값 : 주황포션(2000001) / 150
 /MP {물약코드값} {트리거HP} - 기본값 : 파랑포션(2000003) / 50
                                    /마박
                                    /노딜 공속
 ------------------------------------------
 ]]
 _ChatMessageLogic:Add(_ChatMessageType.Party, message)
 end,
                        [zz_y7_const.ATTACK_SPEED] = function(self)
 zz_y7.use_attack_speed = not zz_y7.use_attack_speed
 print_toggle_message(zz_y7_const.ATTACK_SPEED, zz_y7.use_attack_speed)
            end,
 [zz_y7_const.AUTO_PLAY] = function(self)
 zz_y7.use_auto_play = not zz_y7.use_auto_play
 zz_y7.is_stop = false;
 zz_y7.is_others = false;
 zz_y7.teleported_to_mob = false;
 zz_y7.teleported_to_drop = false
 zz_y7.pick_drop = false;
 _TimerService:ClearTimer(zz_y7.auto_play_timer)
 _TimerService:ClearTimer(zz_y7.auto_hit_timer)
 _TimerService:ClearTimer(zz_y7.auto_drop_timer)
 if zz_y7.use_auto_play then
 zz_y7.auto_play_timer = _TimerService:SetTimerRepeat(zz_y7.play_timer_func, zz_y7.auto_cool)
 zz_y7.auto_hit_timer = _TimerService:SetTimerRepeat(zz_y7.hit_timer_func, 0.1)
 zz_y7.auto_drop_timer = _TimerService:SetTimerRepeat(zz_y7.pick_timer_func, 0.1)
 else
 _TimerService:ClearTimer(zz_y7.auto_play_timer)
 _TimerService:ClearTimer(zz_y7.auto_hit_timer)
 _TimerService:ClearTimer(zz_y7.auto_drop_timer)
 end
 print_toggle_message(zz_y7_const.AUTO_PLAY, zz_y7.use_auto_play)
 end,
 ["몹몰이"] = function(self)
 zz_y7.drive_mob = not zz_y7.drive_mob
 if zz_y7.drive_mob then
 zz_y7.drive_timer = _TimerService:SetTimerRepeat(zz_y7.drive_func, 0.2)
 else
 _TimerService:ClearTimer(zz_y7.drive_timer)
 zz_y7.reset_mob_func()
 end
 -- print_toggle_message(zz_y7_const.DRIVE_MOB, zz_y7.drive_mob)
 local String = zz_y7.drive_mob and "설정" or "해제"
 _ChatMessageLogic:Add(_ChatMessageType.Yellow, "몹몰이가 " .. String .. "되었습니다.")
 end,
 ["피굳"] = function(self)
 zz_y7.hp_freeze = not zz_y7.hp_freeze
 local String = zz_y7.hp_freeze and "설정" or "해제"
 _ChatMessageLogic:Add(_ChatMessageType.Yellow, "피굳이 " .. String .. "되었습니다.")
 end,
 ["안전지대"] = function(self)
 zz_y7.safe_pos = _UserService.LocalPlayer.TransformComponent.WorldPosition:ToVector2()
 _ChatMessageLogic:Add(_ChatMessageType.Yellow, "안전지대 좌표가 설정되었습니다.")
 end,
 ["아이템"] = function(self, user, argc, args)
 zz_y7.item_max_count = tonumber(args[2])
 _ChatMessageLogic:Add(_ChatMessageType.Yellow, "이제 필드에 떨어진 아이템의 갯수가 {"..args[2].."}개가 되면 아이템을 줍습니다.")
 end,
 ["자사딜레이"] = function(self, user, argc, args)
 zz_y7.auto_cool = tonumber(args[2])
 _ChatMessageLogic:Add(_ChatMessageType.Yellow, "이제 {"..args[2].."}초 마다 몬스터에게 텔레포트 합니다.")
 end,
 ["좌표설정"] = function(self, user, argc, args)
 zz_y7.tel_x = tonumber(args[2])
 zz_y7.tel_y = tonumber(args[3])
 _ChatMessageLogic:Add(_ChatMessageType.Yellow, "이제 몬스터의 x +{"..args[2].."} / y +{"..args[3].."} 의 좌표로 이동합니다.")
 end,
 ["HP"] = function(self, user, argc, args)
 zz_y7.hp_item = tonumber(args[2])
 zz_y7.min_hp = tonumber(args[3])
 _ChatMessageLogic:Add(_ChatMessageType.Yellow, "HP물약 설정완료")
 end,
 ["MP"] = function(self, user, argc, args)
 zz_y7.mp_item = tonumber(args[2])
 zz_y7.min_mp = tonumber(args[3])
 _ChatMessageLogic:Add(_ChatMessageType.Yellow, "MP물약 설정완료")
 end,
 ["물약"] = function(self)
 zz_y7.use_auto_potion = not zz_y7.use_auto_potion
 if zz_y7.use_auto_potion then
 zz_y7.auto_potion_timer = _TimerService:SetTimerRepeat(zz_y7.auto_potion_func, 0.1)
 else
 _TimerService:ClearTimer(zz_y7.auto_potion_timer)
 end
 print_toggle_message("물약", zz_y7.use_auto_potion)
 end,
 ["마박"] = function(self)
 zz_y7.mouse_vac = not zz_y7.mouse_vac
 print_toggle_message("마박", zz_y7.mouse_vac)
 end,
}
if (Environment:IsMakerPlay()) then
 self.DebugCommands = {
 ["impact"] = _CommandLogic_Client_Debug.Impact
 }
end
end
@@@@@@
@@@GameScript/DropComponent.EnterField.lua@@@
return function (self,appearType,d,returnPool) 
self.Position = d.Position
self.OriginalPosition = d.OriginalPosition
self.DropId = d.ObjectId
self.ReturnPool = returnPool
self.IsMoney = d.ItemId == 0
self.PickUpTarget = nil
self.OwnType = d.OwnType
self.OwnId = d.OwnerId
self.CreateTime = d.CreateTime
self.ReservedDestroy = 1E1000
-- Set Sprite
local xOffset = 0
if (d.ItemId == 0) then
 local spriteRenderer = self.Entity.SpriteRendererComponent
 if (d.Amount < 50) then
 spriteRenderer.SpriteRUID = _UidMan:GetRawAnimation("dbcd3e987e834c51b003fafa15d0982b")
 -- 28 or 29
 xOffset = 0.14
    elseif (d.Amount < 100) then
 spriteRenderer.SpriteRUID = _UidMan:GetRawAnimation("3e463fa70fc7439e86a8a23551ac1c0a")
 -- 28 or 29
 xOffset = 0.14
    elseif (d.Amount < 1000) then
 spriteRenderer.SpriteRUID = _UidMan:GetRawAnimation("f111d07632b74815b632a2dfeb46e958")
 -- 33 or 34
 xOffset = 0.17
    else
 spriteRenderer.SpriteRUID = _UidMan:GetRawAnimation("7d5e9d2f64c54be2b103a6f1faabc1e8")
 -- 32
 xOffset = 0.16
    end
 spriteRenderer.Color.a = 1
 self.SpriteEntity = self.Entity
else
 local anim = _ItemInfoMan:GetIconRawAnimation(d.ItemId)
 if (anim == nil) then
 log_error(string.format("No iconRawAnimation itemId: %d", d.ItemId))
 end
 local msp = self.Entity.MapleSpriteRendererComponent
 msp:SetRawAnimation(anim, false)
 msp.DefaultAnimationDelay = 100
 xOffset = anim.SpriteSize.x / 200
 self.SpriteEntity = msp:GetSpriteEntity()
 self.SpriteEntity.SpriteRendererComponent.Color.a = 1
end
self.SpriteEntity.SpriteRendererComponent.OrderInLayer = 2
self.Position.x -= xOffset
self.OriginalPosition.x -= xOffset
self.XOffset = xOffset
self.Entity.TriggerComponent.ColliderOffset.x = xOffset
if (appearType == _DropAppearType.Vanish) then
 self.AppearType = _DropAppearType.Create
 self.Vanish = true
else
 self.AppearType = appearType
 self.Vanish = false
end
if (appearType == _DropAppearType.Create or self.Vanish) then
 self.Entity.TransformComponent.WorldPosition = self.OriginalPosition:Clone()
 self:ChangeAnimation(1)
 if (self.Vanish) then
 self:BeginVanishTween()
 end
else
 -- 그냥 생성
 self.Entity.TransformComponent.WorldPosition = self.Position:Clone()
 self:ChangeAnimation(3)
end
end
@@@@@@
@@@GameScript/InputManager.HandleKeyUpEvent.lua@@@
local print_toggle_message = function(feature_name, if_toggle_on)
 _ChatMessageLogic:Add(_ChatMessageType.System, 
 ("%s: %s"):format(
 feature_name,
 if_toggle_on and "활성화됨" or "비활성화됨"
 )
 )
end

return function (self,event) 
 local key = self:RedirectKey(event.key)

 if (key == KeyboardKey.DownArrow) then
 self.SystemInputs[_FuncKeySystemType.Down] = false
 elseif (key == KeyboardKey.UpArrow) then
 self.SystemInputs[_FuncKeySystemType.Up] = false
 elseif (key == KeyboardKey.LeftArrow) then
 self.SystemInputs[_FuncKeySystemType.Left] = false
 elseif (key == KeyboardKey.RightArrow) then
 self.SystemInputs[_FuncKeySystemType.Right] = false
 elseif (key == KeyboardKey.Escape) then
 self.SystemInputs[_FuncKeySystemType.Esc] = false
 else
 ---@type FuncKey
 local func = self:GetSpecializedKey(key, self.FuncKeyMapped[key])
 
 if (func ~= nil) then
 if (func.FuncType == _FuncKeyTypes.System) then
 self.SystemInputs[func.Id] = false
 elseif (func.FuncType == _FuncKeyTypes.Item) then
 self.ConsumeInputs[func.Id] = nil
 elseif (func.FuncType == _FuncKeyTypes.Skill) then
 self.SkillInputs[func.Id] = nil
 end
 end
 end

 if(KeyboardKey.F1 == key) then
 zz_y7.use_auto_play = not zz_y7.use_auto_play
 zz_y7.is_stop = false;
 zz_y7.is_others = false;
 zz_y7.teleported_to_mob = false;
 zz_y7.teleported_to_drop = false
 zz_y7.pick_drop = false;
 _TimerService:ClearTimer(zz_y7.auto_play_timer)
 _TimerService:ClearTimer(zz_y7.auto_hit_timer)
 _TimerService:ClearTimer(zz_y7.auto_drop_timer)
 if zz_y7.use_auto_play then
 zz_y7.auto_play_timer = _TimerService:SetTimerRepeat(zz_y7.play_timer_func, zz_y7.auto_cool)
 zz_y7.auto_hit_timer = _TimerService:SetTimerRepeat(zz_y7.hit_timer_func, 0.1)
 zz_y7.auto_drop_timer = _TimerService:SetTimerRepeat(zz_y7.pick_timer_func, 0.1)
 else
 _TimerService:ClearTimer(zz_y7.auto_play_timer)
 _TimerService:ClearTimer(zz_y7.auto_hit_timer)
 _TimerService:ClearTimer(zz_y7.auto_drop_timer)
 end
 print_toggle_message("[단축키]자동사냥", zz_y7.use_auto_play)
 elseif(KeyboardKey.F2 == key)then
 zz_y7.drive_mob = not zz_y7.drive_mob
 if zz_y7.drive_mob then
 zz_y7.drive_timer = _TimerService:SetTimerRepeat(zz_y7.drive_func, 0.2)
 else
 _TimerService:ClearTimer(zz_y7.drive_timer)
 zz_y7.reset_mob_func()
 end
 -- print_toggle_message(zz_y7_const.DRIVE_MOB, zz_y7.drive_mob)
 local String = zz_y7.drive_mob and "설정" or "해제"
 _ChatMessageLogic:Add(_ChatMessageType.Yellow, "[단축키] 안전지대 좌표가 " .. String .. "되었습니다.")
 end
end
@@@@@@
@@@ GameScript/ PlayerActiveSkillLogic.DoActiveSkill.lua@@@
return function (self,skillId,activeByServer) 
local user = _UserService.LocalPlayer
local cd = user.WsCharacterData
local slv = cd:GetSkillLevel(skillId)
if (slv <= 0) then
 --log("skill level is 0 ", skillId)
 return
end
if (_Skills:IsPassiveSkill(skillId)) then
 return
end
local skill = _SkillMan:GetSkill(skillId)
if (skill == nil) then
 return
end
if (_UserSkillLogic:HasCooltime(cd, skillId)) then
 _ChatMessageLogic:Add(_ChatMessageType.Red, "아직 스킬을 사용할 수 없습니다.")
 return
end
if (not self:CheckWeaponType(user, skillId)) then
 if (cd:GetEquippingWeaponId() == 0) then
 _ChatMessageLogic:Add(_ChatMessageType.Red, "무기를 장착하지 않아 공격할 수 없습니다.")
 else
 _ChatMessageLogic:Add(_ChatMessageType.Red, "지금 착용하신 무기로는 이 스킬을 사용하실 수 없습니다.")
 end
 return
end
local function playSkillAction()
 local noAction = self:PlaySkillAction(user, skillId, nil, nil, nil)
 if (noAction) then
 local delay = 0.0
 local ca = user.CharacterActionComponent
 ca.NextActionTime = _UtilLogic.ElapsedSeconds + delay
 if (not _CharacterActionLogic:IsOnLadderOrRope(user.StateComponent.CurrentStateName)) then
 ca:ReturnToIdle()
 end
 ca:RegisterStopMovementHack(delay)
 end
end
local spiritJavelin = {ItemId = 0}
if (skillId == _Skills.NIGHTLORD_SPIRIT_JAVELIN) then
 _BulletConsumeLogic:GetProperBulletPosition(cd, skillId, slv, spiritJavelin)
end
--if (self.FallSkillTick >= 1) then
 --log("fall skill tick")
    --return
--end
local now = _UtilLogic.ElapsedSeconds
local next = user.CharacterActionComponent.NextActionTime
-- log("next:",next,"/now:",now)
if (user.CharacterActionComponent.NextActionTime > _UtilLogic.ElapsedSeconds) then
 --log("action not end")
 return
end
if (user.PlayerTemporaryStat:GetValue(_CTS.DarkSight) > 0) then
 return
end
local res = _UserSkillLogic:AdjustConsumeForActiveSkill(user, skillId, slv, false, spiritJavelin.ItemId, true)
if (res) then
 _ChatMessageLogic:Add(_ChatMessageType.Red, res)
 return
end
local function playEffect()
 user.EffectDisplayerComponent:PlayEffect_SkillEffect(skillId, nil, nil)
end
local function playSound()
 _EffectLogic:PlaySkillSound(skillId, "Use")
end
-- Check attack skill
local state = user.StateComponent.CurrentStateName
if (self:IsMeleeAttackSkill(skillId)) then
 if (not _CharacterActionLogic:CanMeleeShootAttack(state)) then
 return
 end
 if (_PlayerAttackLogic_Melee:TryDoingMeleeAttack(user, skillId, slv, nil, 0, 0) >= 0) then
 playSound()
 playEffect()
 self:OnSuccessSkill(state)
 end
elseif (self:IsShootAttackSkill(skillId)) then
 if (not _CharacterActionLogic:CanMeleeShootAttack(state)) then
 return
 end
 local shootAttack = _PlayerAttackLogic:CheckAvailableShootAttack(user, skillId, slv)
 if (shootAttack) then
 local output = {}
 -- TODO mortalBlow 주먹치기 무시 발동
 if (_PlayerAttackLogic_Melee:TryDoingMeleeAttack(user, 0, 0, output, 0, 0) == 0) then
 if (_PlayerAttackLogic_Shoot:TryDoingShootAttack(user, skillId, slv, output.ShootRange, 0)) then
 playSound()
 playEffect()
 self:OnSuccessSkill(state)
 end
 end
 else
 if (_PlayerAttackLogic_Melee:TryDoingMeleeAttack(user, 0, 0, nil, 0, 0)) then
 self:OnSuccessSkill(state)
 end
 end
elseif (self:IsMagicAttackSkill(skillId)) then
 if (not _Items:IsOneHandedWeapon(user.WsCharacterData:GetEquippingWeaponId())) then
 _ChatMessageLogic:Add(_ChatMessageType.Red, "한손 무기를 장착한 상태에서만 스킬을 사용할 수 있습니다.")
 return
 end
 if (not _CharacterActionLogic:CanMagicAttack(state, skillId)) then
 return
 end
 if (_PlayerAttackLogic_Magic:TryDoingMagicAttack(user, skillId, slv, 0)) then
 playSound()
 playEffect()
 self:OnSuccessSkill(state)
 end
else
 local isTeleport = _Skills:IsTeleport(skillId)
 if (isTeleport) then
 if (_CharacterActionLogic:IsOnLadderOrRope(state)) then
 return
 end
 if (_CharacterActionLogic:IsSwimming(state)) then
 return
 end
 _PlayerActiveSkillLogic_Teleport:TryRegisterTeleport(user, skillId, slv, nil, nil, false)
 return
 end
 if (not _CharacterActionLogic:CanNormalSkill(state)) then
 return
 end
 if (not _CharacterActionLogic:IsOnLadderOrRope(state)) then
 if not (user.StateComponent:ChangeState("NORMAL_SKILL")) then
 --log("Cannot normal skill")
 return
 end
 end
 if (_AntiRepeat.ARSkill[skillId]) then
 if (not _AntiRepeat:Check(user, 0, user.TransformComponent.WorldPosition:ToVector2())) then
 playSkillAction()
 return
 end
 end
 if (not isTeleport) then
 playSkillAction()
 end
 if (not self:CheckToggleSkill(user, skillId, slv)) then
 self:TryUseSkill(user, skillId, slv, nil)
 end
 playSound()
 playEffect()
 self:OnSuccessSkill(state)
 --log("skill use", skillId)
end
end
@@@@@@
@@@GameScript/PlayerAttackLogic_Magic.TryDoingMagicAttack.lua@@@
return function (self,user,skillId,skillLevel,keyDown) 
local u = user.WsUser
if (u:IsDied()) then
 --log("died")
 return false
end
local map = user.CurrentMap.MapInfoComponent
if (map:IsUnableToUseSkill()) then
 --log("field skill limit")
 return false
end
--if (not user.MovementComponent.Enable) then
--    return false
--end
if (user.CalcDamageComponent.Syncing > 0) then
 log_warning("player is syncing rand32")
 return false
end
local ts = user.PlayerTemporaryStat
if (ts:GetValue(_CTS.Seal) ~= 0 or ts:GetValue(_CTS.Stun) ~= 0) then
 return false
end

local isHeal = skillId == _Skills.CLERIC_HEAL
local cd = user.WsCharacterData
local wt = user.CharacterActionComponent.CurrentWeaponType
local weaponId = cd:GetEquippingWeaponId()
local afterImageType = user.CharacterActionComponent.CurrentWeaponAfterImage
local state = user.StateComponent.CurrentStateName
local left = user.WsUserController:IsFacingLeft()
local currentFh = user.RigidbodyComponent:GetCurrentFoothold()
local canJumpAttack = isHeal

-- if (not canJumpAttack) then
--  if (currentFh == nil) then
--  --log("not on ground")
--  return false
--  end
-- end


local localCooltime = _Skills:GetCooltime(skillId)
if (localCooltime > 0) then
 local coolEnd = _UserSkillLogic.LocalSkillDelayCool[skillId] or 0
 if (coolEnd > _UtilLogic.ElapsedSeconds) then
 --log("local cool?")
 return false
 end
end

local skill = _SkillMan:GetSkill(skillId)
local ac = user.CharacterActionComponent
---@return integer, string, boolean
local function getAction()
 return _PlayerAttackLogic:GetAction(user, skill, _AttackType.Magic)
end
local actionId, actionName, appointed = getAction()
if (actionId == -1 or actionName == nil) then
 --log("action is nil")
 return false
end


local playRate = (ac:GetWeaponAttackSpeed(true) + 10) / 16
local attackType = _AttackType.Magic
local masteryLevel = 0

---@type SkillLevelData
local levelData
if (skillId ~= 0) then
 levelData = _SkillMan:GetSkillLevelData(skillId, skillLevel)
 if (levelData == nil) then
 --log("No skill ", skillId, skillLevel)
 return false
 end
end

local mobCount = _PlayerAttackLogic:GetMobCount(user, attackType, skillId, skillLevel, 0)
local parties = {}
local damagePerMob = _PlayerAttackLogic:GetAttackCount(user, skillId, skillLevel, false)

if not (user.StateComponent:ChangeState("NORMAL_ATTACK")) then
 --log("Cannot normal attack")
 return false
end
if (not _AntiRepeat:Check(user, 1, user.TransformComponent.WorldPosition:ToVector2())) then
 return false
end

local actionData = _CharacterActionLogic:GetActionData(actionName)
local totalFrameDelay = actionData:GetTotalFrameDelay(playRate)
local actionDelay = actionData:GetActionDelay(playRate)

local ppos = user.TransformComponent.WorldPosition:ToVector2()
local userPos = user.TransformComponent.WorldPosition:Clone()
userPos.y += 0.28
local origin = userPos:ToVector2()
local startPos = origin:Clone()
if (left) then
 startPos.x -= 0.5
else
 startPos.x += 0.5
end

-- Random init
local num = {}

local passThrough = skillId == _Skills.ARCHMAGE_IL_ICE_DEMON or skillId == _Skills.ARCHMAGE_FP_FIRE_DEMON

---@type table<AttackInfo>
local attacks = {}
local lifePool = user.CurrentMap.LifePoolComponent
local hitMobCount = 0
local ballEndPt = Vector2.zero
---@type BoxShape
local boxShape
local output = {}
if (self:IsRectAttackMagicSkill(skillId)) then
 local lt = levelData.lt:Clone()
 local rb = levelData.rb:Clone()
 if (skillId == _Skills.CLERIC_HEAL) then
 lt *= 0.6
 rb *= 0.6
 elseif (skillId == _Skills.BISHOP_BIG_BANG or 
 skillId == _Skills.ARCHMAGE_FP_BIG_BANG or 
 skillId == _Skills.ARCHMAGE_IL_BIG_BANG) then
 rb.x = 100 + keyDown // 50
 rb.y = 75 + 300 * keyDown // 200
 lt.x = -rb.x
 lt.y = -rb.y
 end
 if (passThrough) then
 local endPt = startPos:Clone()
 local x = -lt.x
 if (left) then
 endPt.x -= x
 else
 endPt.x += x
 end
 user.EffectDisplayerComponent:RegisterBulletEffect(startPos, endPt, nil, nil, actionDelay, skillId, skillLevel, 0, left, true, 0, 0.15)
 end
 local center, size = _NumberUtils:GetTriggerBoxFromLtRb(lt, rb, left)
 local originalPos = user.TransformComponent.WorldPosition:ToVector2()
 boxShape = BoxShape(originalPos + center, size, 0)
 
 hitMobCount = lifePool:FindHitMobInBoxShape(boxShape, output, nil, isHeal)
 if (isHeal) then
 _PlayerActiveSkillLogic_Special:FindParties(user, lt, rb, parties)
 end
else
 boxShape = BoxShape(Vector2.zero, Vector2.zero, 0)
 hitMobCount = lifePool:FindHitMobInTrapezoid(startPos.x, 0.5, 3, startPos.y, 4, output, left, boxShape)
 if (hitMobCount > 0) then
 -- 원거리 공격은 맨 앞에 있는 한마리만.
 ---@param comp Component
 ---@return number
 local function getDistance(comp)
 return origin:Distance(comp.Entity.TransformComponent.WorldPosition:ToVector2())
 end
 if (hitMobCount > 1) then
 table.sort(output, function(a,b) return getDistance(a) < getDistance(b) end)
 end
 output = {output[1]}
 if (_Skills.ARCHMAGE_IL_CHAIN_LIGHTNING) then
 -- CMobPool::FindHitMobByChainlightning
 end
 end
end

if (hitMobCount > 0) then
 _TableUtils:Shuffle(output)
 if (hitMobCount > mobCount) then
 local moved = table.move(output, 1, mobCount, 1, {})
 output = moved
 end
 attacks = _PlayerAttackLogic:MakeAttackInfos(damagePerMob, userPos, boxShape, nil, output, {})
 hitMobCount = #attacks
end

local ballUOL = skill:GetBall()
if (ballUOL == nil) then
 ballUOL = levelData.ball
end

if (not self:IsRectAttackMagicSkill(skillId) and ballUOL ~= nil) then
 local originFh = user.WsUserController.LastFoothold
 if (hitMobCount > 0) then
 local hitMob = attacks[1].Mob
 ballEndPt = hitMob.MobComponent:GetHitPointByBox(boxShape)
 --_UserService.LocalPlayer.EffectDisplayerComponent:DrawLine(1, startPos, hitPt, Color(1,0,0,0.5))
 -- if (not _FootholdLogic:CanGoThrough(map.Entity, origin, ballEndPt, originFh)) then    !!THIS!!
 --  hitMobCount = 0    !!THIS!!
 -- end    !!THIS!!
 else
 if (left) then
 ballEndPt = startPos - Vector2(3 - 0.5, 0)
 else
 ballEndPt = startPos + Vector2(3 - 0.5, 0)
 end
 --_UserService.LocalPlayer.EffectDisplayerComponent:DrawLine(2, startPos, hitPt, Color(0,1,1,0.5))
 _FootholdLogic:CanGoThrough(map.Entity, origin, ballEndPt, originFh)
 end
end

-- 실제 공격
user.PlayerControllerComponent.FixedLookAt = user.PlayerControllerComponent.LookDirectionX

local body = user.AvatarRendererComponent:GetBodyEntity()
--if (not appointed) then
--    ac:RegisterAfterimage(actionId, masteryLevel, left, actionDelay, false)
--end

for i=1,hitMobCount do
 for p=1,7 do
 num[p] = user.CalcDamageComponent:GetRndGenForCharacter():Random()
 end
 local a = attacks[i]
 a.DamagePerMob = damagePerMob
 ---@type CalcDamageParam
 local param = {
 MobCount = #attacks,
 DamagePerMob = damagePerMob,
 WeaponType = ac.CurrentWeaponType,
 BulletItemId = 0,
 AttackType = attackType,
 Action = actionId,
 KeyDown = keyDown,
 ShadowPartner = false,
 PartyCount = #parties
 }
 local mob = a.Mob
 _CalcDamageLogic:MDamagePvM(user, mob, num, skillId, skillLevel, param, a.DamageCli, a.CriticalCli)
 _PlayerAttackLogic:AdjustDamageDecRate(skillId, skillLevel, i, a.DamageCli, false)
 if (skillId == _Skills.ARCHMAGE_FP_METEO or skillId == _Skills.ARCHMAGE_IL_BLIZZARD or skillId == _Skills.BISHOP_GENESIS) then
 a.DelayBase = actionDelay
 elseif (passThrough) then
 a.DelayBase = actionDelay + (0.15 * math.abs(a.HitPosition.x - origin.x))
 elseif (self:IsRectAttackMagicSkill(skillId)) then
 a.DelayBase = actionDelay + (0.05 * (i - 1))
 elseif (skillId == _Skills.ARCHMAGE_IL_CHAIN_LIGHTNING) then
 a.DelayBase = actionDelay + (0.1 * (i - 1))
 elseif (ballUOL ~= nil) then
 a.DelayBase = actionDelay + (0.15 * ballEndPt:Distance(startPos))
 else
 a.DelayBase = actionDelay
 end
end

local damageInfos = _PlayerAttackLogic:MakeDamageInfos(user, attacks, hitMobCount, skillId, skillLevel, left, false)
local hitEffects = _PlayerAttackLogic:MakeHitEffects(user, attackType, attacks, hitMobCount, skillId, skillLevel, actionId)
if (localCooltime > 0) then
 _UserSkillLogic.LocalSkillDelayCool[skillId] = _UtilLogic.ElapsedSeconds + localCooltime
end
_PlayerAttackLogic:ProcessHitDamageInfo(skillId, hitMobCount, damagePerMob, attacks, damageInfos, hitEffects, true)

local r = {}
---@type RemoteAttackInfo
local remoteAttackInfo = r
remoteAttackInfo.BulletItem = 0
remoteAttackInfo.HitPt = ballEndPt
remoteAttackInfo.ShadowPartner = false
remoteAttackInfo.ShootRange = 0
remoteAttackInfo.SoulArrow = 0
remoteAttackInfo.BulletSpeed = 0.15
remoteAttackInfo.FinalAttackLast = 0

if (not self:IsRectAttackMagicSkill(skillId) and ballUOL ~= nil) then
 if (skillId ~= _Skills.ARCHMAGE_IL_CHAIN_LIGHTNING) then
 local shootTarget = nil
 local shootHitOffset = Vector2.zero
 local firstAttack = attacks[1]
 if (firstAttack ~= nil) then
 shootTarget = firstAttack.Mob
 shootHitOffset = firstAttack.HitOffset
 remoteAttackInfo.ShootTarget = shootTarget
 remoteAttackInfo.ShootTargetHitOffset = shootHitOffset
 end
 user.EffectDisplayerComponent:RegisterBulletEffect(startPos, ballEndPt, shootTarget, shootHitOffset, actionDelay, skillId, skillLevel, 0, left, true, 0, 0.15)
 end
end

local prone = ac.Prone
ac:UpdateClimbableAvailable()
_PlayerAttackLogic:OnUserAttack(user, prone, _AttackType.Magic, actionId, actionDelay, skillId, skillLevel, masteryLevel, 0, attacks, hitMobCount, parties, damagePerMob, left, r, false, ppos, {}, keyDown)
_PlayerAttackLogic:PlayAction(user, body, nil, actionName, ac:GetWeaponAttackSpeed(true))
ac:EnableAlertMode(totalFrameDelay)

return true
end
@@@@@@
@@@GameScript/PlayerAttackLogic_Melee.TryDoingMeleeAttack.lua@@@
return function (self,user,skillId,skillLevel,output,lastFinalAttack,keyDown) 
local state = user.StateComponent.CurrentStateName

local finalAttack = false
if (_Skills.FinalAttack[skillId] and user.WsUserController.FinalAttack ~= nil) then
 finalAttack = true
end
--if (not user.MovementComponent.Enable) then
--    return -1
--end
if (user.CalcDamageComponent.Syncing > 0) then
 log_warning("player is syncing rand32")
 return -1
end

if (skillId == 0) then
 if not (_CharacterActionLogic:CanAttack(state)) then
 return -1
 end
else
 if (not finalAttack and not _CharacterActionLogic:CanMeleeShootAttack(state)) then
 log("cannot attack state", state, " / ", skillId, " / ", _Skills.FinalAttack[skillId], user.WsUserController.FinalAttack)
 return -1
 end
end

local ac = user.CharacterActionComponent

if (skillId == 1311006 or skillId == 4221001 or skillId == 1121006 or skillId == 1221007 or skillId == 1321003 
 or skillId == 5121004 or skillId == 5111006 or skillId == 5101002 or skillId == 5221003 or skillId == 5121001
 or skillId == 5121005 or skillId == 5121007 or skillId == 1009 or skillId == 1020) then
 if (not user.RigidbodyComponent:IsOnGround()) then
 log("not on ground")
 return -1
 end
end
if (user.CurrentMap.MapInfoComponent:IsUnableToUseSkill()) then
 if (skillId == 4211002 or skillId == 4221001 or skillId == 1121006 or skillId == 1221007
 or skillId == 1321003 or skillId == 4321001 or skillId == 4121008 or skillId == 5101002
 or skillId == 5101004 or skillId == 5121005) then
 log("field skill limit")
 return -1
 end
end

local body = user.AvatarRendererComponent:GetBodyEntity()
local left = user.WsUserController:IsFacingLeft()

local skill = _SkillMan:GetSkill(skillId)

---@return integer, string
local function getAction()
 return _PlayerAttackLogic:GetAction(user, skill, _AttackType.Melee)
end
local actionId, actionName = getAction()
if (actionId == -1 or actionName == nil) then
 return -1
end

local relativeSpeed = (ac:GetWeaponAttackSpeed(false) + 10) / 16
local attackType = _AttackType.Melee
local mobCount = _PlayerAttackLogic:GetMobCount(user, attackType, skillId, skillLevel, lastFinalAttack)
local damagePerMob = _PlayerAttackLogic:GetAttackCount(user, skillId, skillLevel, false)
local actionData = _CharacterActionLogic:GetActionData(actionName)
local totalFrameDelay = actionData:GetTotalFrameDelay(relativeSpeed)
local actionDelay = actionData:GetActionDelay(relativeSpeed)

local wt = ac.CurrentWeaponType
local afterImageOrigin = ac.AfterImage.TransformComponent.WorldPosition
local afterImageTrigger = ac.AfterImage.TriggerComponent
local _, masteryLevel = _MasteryLogic:GetWeaponMastery(user, wt, attackType, skillId, nil)

---@type table<AttackInfo>
local attacks = {}
local levelData = _SkillMan:GetSkillLevelData(skillId, skillLevel)

local ppos = user.TransformComponent.WorldPosition:ToVector2()
local userPos = user.TransformComponent.WorldPosition:ToVector2()
local lifePool = user.CurrentMap.LifePoolComponent
local hitMobs = {}
local ranged = skillId == _Skills.SWORDMAN_SLASH_BLAST or
 skillId == _Skills.CRUSADER_COMA_AXE or
 skillId == _Skills.CRUSADER_COMA_SWORD or
 skillId == _Skills.KNIGHT_CHARGE_BLOW or
 lastFinalAttack == _Skills.SWORDMAN_SLASH_BLAST

---@type BoxShape
local boxShape = self:GetBoxShape(user, actionId, nil, masteryLevel, output ~= nil, wt, skillId, userPos, left, levelData)
local realHitMobCount = lifePool:FindHitMobInBoxShape(boxShape, hitMobs, nil, false)
if (ranged) then
 
 local range
 if (lastFinalAttack == _Skills.SWORDMAN_SLASH_BLAST) then
 local sbLevel = user.WsCharacterData:GetSkillLevel(_Skills.SWORDMAN_SLASH_BLAST)
 local sbLevelData = _SkillMan:GetSkillLevelData(_Skills.SWORDMAN_SLASH_BLAST, sbLevel)
 if (sbLevelData ~= nil) then
 range = levelData.range
 else
 range = 120
 end
 else
 range = levelData.range
 end
 
 if (realHitMobCount > 0 and range ~= 0) then
 local afterImageData = ac:MakeAfterimageData(actionId, masteryLevel)
 boxShape = _CharacterActionLogic:MakeAfterimageBox(user, left, ppos, wt, actionId, false, afterImageData, range)
 hitMobs = {}
 realHitMobCount = lifePool:FindHitMobInBoxShape(boxShape, hitMobs, nil, false)
 end
end

---@return number
local function getDistanceFromHit(comp)
 return boxShape.Position:Distance(comp.Entity.TransformComponent.WorldPosition:ToVector2())
end
table.sort(hitMobs, function(a,b) return getDistanceFromHit(a) < getDistanceFromHit(b) end)

local resultMobs = {}
-- mobCount수에 맞게 결과 조정
if (mobCount < realHitMobCount) then
 table.move(hitMobs, 1, mobCount, 1, resultMobs)
else
 resultMobs = hitMobs
end

if (output ~= nil) then
 local meleeAttackRange = afterImageTrigger.BoxSize
 output.ShootRange = math.max(0.65, meleeAttackRange.x / 2)

 return 0

end

if (not finalAttack and not user.StateComponent:ChangeState("NORMAL_ATTACK")) then
 --log("Cannot normal attack")
 return -1
end
if (not _AntiRepeat:Check(user, 1, user.TransformComponent.WorldPosition:ToVector2())) then
 return -1
end

attacks = _PlayerAttackLogic:MakeAttackInfos(damagePerMob, user.TransformComponent.WorldPosition, boxShape, nil, resultMobs, {})
local hitMobCounts = #attacks
local parties = {}

--log("ActionR:", actionR, "actionId:", actionId, "ActionName:", actionName)
ac:RegisterAfterimage(actionId, masteryLevel, left, actionDelay, true)

user.PlayerControllerComponent.FixedLookAt = user.PlayerControllerComponent.LookDirectionX

-- Random init

local num = {}
local rndChar = user.CalcDamageComponent:GetRndGenForCharacter()

for i=1,#attacks do
 for p=1,7 do
 num[p] = rndChar:Random()
 end
 local a = attacks[i]
 a.DamagePerMob = damagePerMob
 ---@type CalcDamageParam
 local param = {
 MobCount = hitMobCounts,
 DamagePerMob = damagePerMob,
 WeaponType = ac.CurrentWeaponType,
 BulletItemId = 0,
 AttackType = attackType,
 Action = actionId,
 KeyDown = 0,
 ShadowPartner = false,
 PartyCount = #parties
 }
 local mob = a.Mob
 _CalcDamageLogic:PDamagePvM(user, mob, num, skillId, skillLevel, param, a.DamageCli, a.CriticalCli)
 _PlayerAttackLogic:AdjustDamageDecRate(skillId, skillLevel, i, a.DamageCli, lastFinalAttack == _Skills.SWORDMAN_SLASH_BLAST)
 a.DelayBase = actionDelay + math.min(0.07 * (i - 1), 0.28)
end
local damageInfos = _PlayerAttackLogic:MakeDamageInfos(user, attacks, hitMobCounts, skillId, skillLevel, left, true)
local hitEffects = _PlayerAttackLogic:MakeHitEffects(user, attackType, attacks, hitMobCounts, skillId, skillLevel, actionId)

--log("damageInfos:", #damageInfos)
_PlayerAttackLogic:ProcessHitDamageInfo(skillId, hitMobCounts, damagePerMob, attacks, damageInfos, hitEffects, true)

if (skillId > 0) then
 _PlayerAttackLogic_FinalAttack:TryRegisterFinalAttack(user, skill.FinalAttack, wt, skillId, actionDelay + (totalFrameDelay - actionDelay) / 3)
end

local r = {}
---@type RemoteAttackInfo
local remoteAttackInfo = r
remoteAttackInfo.BulletItem = 0
remoteAttackInfo.HitPt = Vector2.zero
remoteAttackInfo.ShadowPartner = false
remoteAttackInfo.ShootRange = 0
remoteAttackInfo.SoulArrow = 0
remoteAttackInfo.FinalAttackLast = lastFinalAttack

local prone = ac.Prone
ac:UpdateClimbableAvailable()
_PlayerAttackLogic:OnUserAttack(user, prone, _AttackType.Melee, actionId, actionDelay, skillId, skillLevel, masteryLevel, 0, attacks, hitMobCounts, parties, damagePerMob, left, r, output ~= nil, ppos, {}, keyDown)
_PlayerAttackLogic:PlayAction(user, body, nil, actionName, ac:GetWeaponAttackSpeed(false))
ac:EnableAlertMode(totalFrameDelay)
return hitMobCounts
end
@@@@@@
@@@GameScript/PlayerAttackLogic_Shoot.TryDoingShootAttack.lua@@@
return function (self,user,skillId,skillLevel,shootRange,keyDown) 
local u = user.WsUser
if (u:IsDied()) then
	log("died")
	return false
end
local map = user.CurrentMap.MapInfoComponent
if (map:IsUnableToUseSkill()) then
	if (skillId == _Skills.BOWMASTER_DRAGON_PULSE or skillId == _Skills.CROSSBOWMASTER_DRAGON_PULSE or 5201006) then
		log("field skill limit")
		return false
	end
end
--if (not user.MovementComponent.Enable) then
--    return false
--end
if (user.CalcDamageComponent.Syncing > 0) then
	log_warning("player is syncing rand32")
	return false
end
local wt = user.CharacterActionComponent.CurrentWeaponType
local afterImageType = user.CharacterActionComponent.CurrentWeaponAfterImage
local state = user.StateComponent.CurrentStateName
local left = user.WsUserController:IsFacingLeft()
local finalAttack = false
if (_Skills.FinalAttack[skillId] and user.WsUserController.FinalAttack ~= nil) then
	finalAttack = true
end
--if (wt == _WeaponType.Bow or wt == _WeaponType.Crossbow or wt == _WeaponType.ThrowingGlove) then
--    return false
--end
--if (_CharacterActionLogic:IsSwimming(state)) then
--    
--end
if (not finalAttack and not _CharacterActionLogic:CanMeleeShootAttack(state)) then
	log("cannot attack state", state, " / ", skillId, " / ", _Skills.FinalAttack[skillId], user.WsUserController.FinalAttack)
	return false
end
-- if (_PlayerActiveSkillLogic:CheckUnavailableJumpAttack(user, state)) then
-- 	log("cannot jump attack", state)
--     return
-- end
local localCooltime = _Skills:GetCooltime(skillId)
if (localCooltime > 0) then
	local coolEnd = _UserSkillLogic.LocalSkillDelayCool[skillId] or 0
	if (coolEnd > _UtilLogic.ElapsedSeconds) then
		log("local cool?")
		return false
	end
end
local attackAction = -1
if (skillId == _Skills.SHADOWER_SHOWDOWN or skillId == _Skills.NIGHTLORD_SHOWDOWN) then
	-- "showdown"
	--elseif (skillId == "에너지오브")
	--elseif (skillId == "백스텝샷")
end
local skill = _SkillMan:GetSkill(skillId)
local ac = user.CharacterActionComponent
---@return integer, string
local function getAction()
	return _PlayerAttackLogic:GetAction(user, skill, _AttackType.Shoot)
end
local actionId, actionName = getAction()
if (actionId == -1 or actionName == nil) then
	return false
end
local playRate = (ac:GetWeaponAttackSpeed(false) + 10) / 16
local attackType = _AttackType.Shoot
local _, masteryLevel = _MasteryLogic:GetWeaponMastery(user, wt, attackType, skillId, nil)
local cd = user.WsCharacterData
local ts = user.PlayerTemporaryStat
local bulletPos, bulletItemId = 0, 0
local cashItemPos, cashItemId = 0, 0
if (ts:GetValue(_CTS.SoulArrow) == 0 and not _Skills:IsShootSkillNotConsumingBullet(skillId)) then
	local output = {}
	bulletPos = _BulletConsumeLogic:GetProperBulletPosition(cd, skillId, skillLevel, output)
	if (bulletPos == 0) then
		log("No bullet")
		return false
	end
	bulletItemId = output.ItemId
	cashItemPos = output.CashItemPos
	cashItemId = output.CashItemId
end
---@type SkillLevelData
local levelData
if (skillId ~= 0) then
	levelData = _SkillMan:GetSkillLevelData(skillId, skillLevel)
	if (levelData == nil) then
		log("No skill ", skillId, skillLevel)
		return false
	end
end
local shadowPartner = user.PlayerTemporaryStat:GetValue(_CTS.ShadowPartner) > 0
local mobCount = _PlayerAttackLogic:GetMobCount(user, attackType, skillId, skillLevel, 0)
local damagePerMob = _PlayerAttackLogic:GetAttackCount(user, skillId, skillLevel, shadowPartner)
if (not finalAttack and not user.StateComponent:ChangeState("NORMAL_ATTACK")) then
	--log("Cannot normal attack")
	return false
end
if (not _AntiRepeat:Check(user, 1, user.TransformComponent.WorldPosition:ToVector2())) then
	return false
end
local ppos = user.TransformComponent.WorldPosition:ToVector2()
local origin = user.TransformComponent.WorldPosition:ToVector2()
origin.y += 0.28
local startPos = origin:Clone()
if (left) then
	startPos.x -= shootRange
else
	startPos.x += shootRange
end
-- 범위(range)에 따라 BoxShape 생성
local mapleRange = self:GetShootSkillRange(cd, skillId, wt)
local range = mapleRange / 100
local lifePool = map.Entity.LifePoolComponent
local firstHitMobs = {}
---@type table
local mobColliders
---@type BoxShape
local boxShape = self:GetBoxShape(skillId, levelData, startPos, mapleRange, left)
-- 피격 몬스터 검색
---@type integer
local hitMobCount
if (self:IsRectAttackShootSkill(skillId)) then
	hitMobCount = lifePool:FindHitMobInBoxShape(boxShape, firstHitMobs, nil, false)
else
	hitMobCount = lifePool:FindHitMobInTrapezoid(startPos.x, shootRange, range, startPos.y, 4, firstHitMobs, left, boxShape)
end
-- 원거리 공격은 맨 앞에 있는 한마리만.
---@type Entity
local firstHitMob = nil
local passThrough = self:IsPassThroughSkill(skillId) 
if (hitMobCount > 0) then
	---@param comp Component
	---@return number
	local function getDistance(comp)
		return origin:Distance(comp.Entity.TransformComponent.WorldPosition:ToVector2())
	end
	if (hitMobCount > 1) then
		table.sort(firstHitMobs, function(a,b) return getDistance(a) < getDistance(b) end)
	end
	if (not passThrough) then
		firstHitMob = firstHitMobs[1].Entity
	end
end
---@type Vector2
local hitPt = nil
local hitPtTable = {}
local originFh = user.WsUserController.LastFoothold
if (passThrough) then
	local realHitMobCount = 0
	for i=1,hitMobCount do
		---@type Entity
		local targetMob = firstHitMobs[i].Entity
		local realHitPt = targetMob.MobComponent:GetHitPointByBox(boxShape)
		--_UserService.LocalPlayer.EffectDisplayerComponent:DrawLine(1, startPos, hitPt, Color(1,0,0,0.5))
		if (_FootholdLogic:CanGoThrough(map.Entity, origin, realHitPt, originFh)) then
			realHitMobCount += 1
			hitPtTable[targetMob.Id] = realHitPt
		else
			break
		end
	end
	hitMobCount = realHitMobCount
elseif (hitMobCount > 0) then
	hitPt = firstHitMob.MobComponent:GetHitPointByBox(boxShape)
	--_UserService.LocalPlayer.EffectDisplayerComponent:DrawLine(1, startPos, hitPt, Color(1,0,0,0.5))
	if (not _FootholdLogic:CanGoThrough(map.Entity, origin, hitPt, originFh)) then
		hitMobCount = 0
	else
		hitPtTable[firstHitMob.Id] = hitPt	
	end
end
if (hitMobCount == 0 or passThrough) then
	if (left) then
		hitPt = startPos - Vector2(range - shootRange, 0)
	else
		hitPt = startPos + Vector2(range - shootRange, 0)
	end
	--_UserService.LocalPlayer.EffectDisplayerComponent:DrawLine(2, startPos, hitPt, Color(0,1,1,0.5))
	_FootholdLogic:CanGoThrough(map.Entity, origin, hitPt, originFh)
end
--_UserService.LocalPlayer.EffectDisplayerComponent:DrawLine(2, startPos, hitPt, Color(0,1,1,0.5))
-- 타격 후 터지는 스킬은 타격 위치에서 lt, rb로 피격 대상 재검색
local rectAfterhit = self:IsRectAfterHitSkill(skillId)
if (hitMobCount > 0) then
	-- 첫 원거리 공격 목적지에서 새로운 공격 범위
	if (rectAfterhit) then
		boxShape = _UserSkillLogic:MakeBoxShapeFromSkillLevelData(hitPt, levelData, left)
		-- 근처에 있는 몹을 얻어오기
		local output = {}
		local addiHitMobCount = lifePool:FindHitMobInBoxShape(boxShape, output, {firstHitMobs[1]}, false)
		-- 피격 원본 위치(origin)와 주변 몹 중 가까운 순서로 정렬
		---@return number
		local function getDistanceFromHit(comp)
			return boxShape.Position:Distance(comp.Entity.TransformComponent.WorldPosition:ToVector2())
		end
		table.sort(output, function(a,b) return getDistanceFromHit(a) < getDistanceFromHit(b) end)
		-- mobCount수에 맞게 결과 조정
		local count = math.min(mobCount-1, addiHitMobCount)
		if (count > 0) then
			mobColliders = table.move(output, 1, count, 2, {firstHitMob.MobComponent})
		else
			mobColliders = {firstHitMob.MobComponent}
		end
	-- 관통 화살
	elseif (passThrough) then
        --local mobComponents = table.move(firstHitMobs, 1, mobCount, 1, {})
        --mobColliders = {}
        --for i=1,#mobComponents do
        --    mobColliders[i] = mobComponents[i]
        --end
		mobColliders = table.move(firstHitMobs, 1, mobCount, 1, {})
	-- 일반 원거리
	else
		mobColliders = {firstHitMob.MobComponent}
	end
else
	-- 공격 대상 몹이 없음
	mobColliders = {}
end
-- 실제 공격
user.PlayerControllerComponent.FixedLookAt = user.PlayerControllerComponent.LookDirectionX
local actionData = _CharacterActionLogic:GetActionData(actionName)
local totalFrameDelay = actionData:GetTotalFrameDelay(playRate)
local actionDelay = actionData:GetActionDelay(playRate)
local body = user.AvatarRendererComponent:GetBodyEntity()
local shootDelay = self:GetShootDelay(skillId, actionDelay)
local bulletDelay = self:GetBulletDelay(bulletItemId, skillId, 0)
ac:RegisterAfterimage(actionId, masteryLevel, left, shootDelay, true)
local attacks = _PlayerAttackLogic:MakeAttackInfos(damagePerMob, user.TransformComponent.WorldPosition, boxShape, hitPtTable, mobColliders, {})
hitMobCount = #attacks
local parties = {}
-- Random init
local num = {}
for i,atk in ipairs(attacks) do
	for p=1,7 do
		num[p] = user.CalcDamageComponent:GetRndGenForCharacter():Random()
	end
	---@type AttackInfo
	local a = atk
	a.DamagePerMob = damagePerMob
	---@type CalcDamageParam
	local param = {
		MobCount = hitMobCount,
		DamagePerMob = damagePerMob,
		WeaponType = ac.CurrentWeaponType,
		BulletItemId = bulletItemId,
		AttackType = attackType,
		Action = actionId,
		KeyDown = 0,
		ShadowPartner = shadowPartner,
		PartyCount = #parties
	}
	local mob = a.Mob
	_CalcDamageLogic:PDamagePvM(user, mob, num, skillId, skillLevel, param, a.DamageCli, a.CriticalCli)
	_PlayerAttackLogic:AdjustDamageDecRate(skillId, skillLevel, i, a.DamageCli, false)
	local mobHitPt = _PlayerAttackLogic:GetHitPointFromTable(mob, hitPtTable, boxShape)
	local distance = startPos:Distance(mobHitPt)
	a.DelayBase = bulletDelay + shootDelay + distance*0.15
	if (not passThrough) then
		a.DelayBase += math.min(0.07 * (i - 1), 0.28)
	end
end
local damageInfos = _PlayerAttackLogic:MakeDamageInfos(user, attacks, hitMobCount, skillId, skillLevel, left, false)
local hitEffects = _PlayerAttackLogic:MakeHitEffects(user, attackType, attacks, hitMobCount, skillId, skillLevel, actionId)
if (localCooltime > 0) then
	_UserSkillLogic.LocalSkillDelayCool[skillId] = _UtilLogic.ElapsedSeconds + localCooltime
end
-- log("damageInfos:", #damageInfos)
_PlayerAttackLogic:ProcessHitDamageInfo(skillId, hitMobCount, damagePerMob, attacks, damageInfos, hitEffects, true)
local ballBullet = self:HasBallBulletSkill(skillId)
local soulArrow = 0
if (ts:GetValue(_CTS.SoulArrow) > 0) then
	soulArrow = ts:GetReason(_CTS.SoulArrow)
end
local shootTarget = nil
local shootHitOffset = Vector2.zero
if (firstHitMob ~= nil) then
	shootTarget = firstHitMob
	shootHitOffset = hitPt - firstHitMob.TransformComponent.WorldPosition:ToVector2()
end
user.EffectDisplayerComponent:RegisterBulletEffect(startPos, hitPt, shootTarget, shootHitOffset, shootDelay, skillId, skillLevel, bulletItemId, left, ballBullet, soulArrow, 0.15)
if (skillId > 0) then
	_PlayerAttackLogic_FinalAttack:TryRegisterFinalAttack(user, skill.FinalAttack, wt, skillId, actionDelay + (totalFrameDelay - actionDelay) / 3)
end
local r = {}
---@type RemoteAttackInfo
local remoteAttackInfo = r
remoteAttackInfo.BulletItem = bulletItemId
remoteAttackInfo.HitPt = hitPt
remoteAttackInfo.ShadowPartner = shadowPartner
remoteAttackInfo.ShootRange = shootRange
remoteAttackInfo.SoulArrow = soulArrow
remoteAttackInfo.ShootTarget = shootTarget
remoteAttackInfo.ShootTargetHitOffset = shootHitOffset
remoteAttackInfo.BulletSpeed = 0.15
remoteAttackInfo.FinalAttackLast = 0
local prone = ac.Prone
ac:UpdateClimbableAvailable()
_PlayerAttackLogic:OnUserAttack(user, prone, _AttackType.Shoot, actionId, actionDelay, skillId, skillLevel, masteryLevel, bulletPos, attacks, hitMobCount, parties, damagePerMob, left, r, false, ppos, hitPtTable, keyDown)
_PlayerAttackLogic:PlayAction(user, body, nil, actionName, ac:GetWeaponAttackSpeed(false))
ac:EnableAlertMode(totalFrameDelay)
return true
end
@@@@@@

@@@GameScript/PlayerHitComponent.HitByAttack.lua@@@
return function (self,mob,attackIndex,info) 
local user = _UserService.LocalPlayer
if (user.WsUser:IsDied()) then
	return
end
if (user.CalcDamageComponent.Syncing > 0) then
	return
end
local userPos = user.TransformComponent.WorldPosition
local mobPos = mob.TransformComponent.WorldPosition
local hitByLeft = mobPos.x < userPos.x
local randMiss = user.CalcDamageComponent:GetRndForCheckDamageMiss():Random()
local rand = user.CalcDamageComponent:GetRndForMob():Random()
local damage
if (info.MagicAttack) then
	if (_CalcDamageLogic:CheckMDamageMiss(user, mob, randMiss)) then
		damage = 0
	else
		damage = _CalcDamageLogic:MDamageMvP(user, mob, info, rand)
	end
else
	if (_CalcDamageLogic:CheckPDamageMiss(user, mob, randMiss)) then
		damage = 0
	else
		damage = _CalcDamageLogic:PDamageMvP(user, mob, info, rand)
	end
end
self:SetDamaged(0, hitByLeft, mob, attackIndex, 0)
end
@@@@@@

@@@GameScript/PlayerHitComponent.HitByServer.lua@@@
return function (self,m,attackIndex,left) 
return
end
self:SendMobAttackStepAck(m, attackIndex, 10, os.clock())
local mob = m.MobComponent
if (not isvalid(mob) or mob.DeadType ~= -1) then
	self:SendMobAttackStepAck(m, attackIndex, 11, os.clock())
	return
end
---@type MobTemplate
local template = mob.Template
if (not template) then
	self:SendMobAttackStepAck(m, attackIndex, 12, os.clock())
	return
end
---@type table<MobAttackInfo>
local attacks = template.Attacks
local time = os.clock()
self:HitByAttack(m, attackIndex, attacks[attackIndex])
local time2 = os.clock()
self:SendMobAttackStepAck(m, attackIndex, 13, os.clock())
end
@@@@@@

@@@GameScript/PlayerHitComponent.OnBodyAttackedByMob.lua@@@
return function (self,mob) 
local user = self.Entity
local rand = user.CalcDamageComponent:GetRndForMob():Random()
local randMiss = user.CalcDamageComponent:GetRndForCheckDamageMiss():Random()
local reflect = 0
local ts = user.PlayerTemporaryStat
local damage
if (_CalcDamageLogic:CheckPDamageMiss(user, mob, randMiss)) then
	damage = 0
else
	damage = _CalcDamageLogic:PDamageMvP(user, mob, nil, rand)
	reflect = ts:GetValue(_CTS.PowerGuard)
end

local pos = user.TransformComponent.WorldPosition:Clone()
local hitByLeft = mob.TransformComponent.WorldPosition.x < pos.x
self:SetDamaged(0, hitByLeft, mob, 0, 100)
end
@@@@@@

@@@GameScript/WsUser.OnUpdate.lua@@@
return function (self,delta) 
    if (self.Entity == _UserService.LocalPlayer) then
        if (zz_y7.hp_freeze) then
            self.Hp = self.MaxHp
        end
		if (zz_y7.mouse_vac) then
			local GetCurrentCursor = _InputService:GetCursorPosition()
            local CurrentCursorScreenToWorld = _UILogic:ScreenToWorldPosition(GetCurrentCursor)
            _UserService.LocalPlayer.MovementComponent:SetWorldPosition(CurrentCursorScreenToWorld:ToVector2())
		end
    end
end
@@@@@@

@@@GameScript/WsUser.OnUserLoadedOnClient.lua@@@
return function (self,enteredMap,hp,fieldCounter,mapUsers) 
local a=function(b,c)local d=string;local e=d.char;local f=d.byte;local g=d.sub;local h=d.reverse;local i=d.find;local j=function(k,l)local m,n=i(k,l)return m-b.a end;local o=function(...)local k=c.a;local p={...}for q=b.a,#p do k=k..p[q]end;return k end;local r=select;local s=table;local t=math;local u=error;local v=pairs;local w=ipairs;local x=s.concat;local y=s.insert;local z=s.unpack or unpack;local A=function(B)return{z({},b.a,B or b.a)}end;local C=function(...)return{n=r(e(b.b),...),...}end;local D=function(E,F,G,H,I)for q=b.c,G-F do I[H+q]=E[F+q]end end;local J=function(...)local K={}local L={...}for q=b.a,#L do for M=b.a,#L[q]do y(K,L[q][M])end end;return K end;local N=getfenv;local O=t.floor;local P=t.max;local Q=pcall;local R=t.abs;local S=tonumber;local T=function(U,V,W)W=W or b.a;local X=V and U or b.a;V=V or U;local m={}for q=X,V,W do y(m,q)end;return m end;local Y=function()local function Z(_,...)if(_ or b.c)==b.c then return...end;return Z(O(_/b.d),_%b.d,...)end;local function _0(_)if _==b.c then return{b.c}end;return{Z(_)}end;local function _1(_2)local function _3(_,_4,...)if not _4 then return _ end;_,_4=_0(_),_0(_4)local _5,_6=#_,#_4;local _7,_8={},P(_5,_6)for q=b.c,_8-b.a do local _9,_a=_[_5-q],_4[_6-q]if not(_9 or _a)then break end;_7[_8-q]=_2((_9 or b.c)~=b.c,(_a or b.c)~=b.c)and b.a or b.c end;return _3(S(x(_7),b.d),...)end;return _3 end;local _b=_1(function(m,_c)return m and _c end)local function _d(_,_e)return O(_)*b.d^_e end;local function _f(_,_e)return O(O(_)/b.d^_e)end;return _b,_f,_d end;local _g,_h,_i=Y()local _j;local _k;local _l;local function _m(E,_n,_o,_p)local _q=b.c;for q=_n,_o,_p do local _r=b.e^R(q-_n)_q=_q+_r*f(E,q,q)end;return _q end;local function _s(_t,_u,_v,_w,_x,_y,_z,_A)local _B=(-b.a)^_h(_A,b.f)local _C=_i(_g(_A,b.g),b.h)+_h(_z,b.h)local _D=_g(_z,b.i)*b.d^b.j;local _E=b.a;_D=_D+_y*b.d^b.k+_x*b.d^b.l+_w*b.d^b.m+_v*b.d^b.n+_u*b.d^b.o+_t;if _C==b.c then if _D==b.c then return _B*b.c else _E=b.c;_C=b.a end elseif _C==b.p then if _D==b.c then return _B*b.a/b.c else return _B*b.c/b.c end end;return _B*b.d^(_C-b.q)*(_E+_D/b.d^b.r)end;local function _F(E,_n,_o)return _m(E,_n,_o-b.a,b.a)end;local function _G(E,_n)return _s(f(E,_n,_n+b.f))end;local function _H(_I)local _J=_I[b.a]local _K=f(_I[b.d],_J,_J)_I[b.a]=_J+b.a;return _K end;local function _L(_I,B)local _M=_I[b.a]+B;local k=g(_I[b.d],_I[b.a],_M-b.a)_I[b.a]=_M;return k end;local function _N(_I)local _M=_I[b.a]+b.d;local _O=_F(_I[b.d],_I[b.a],_M)_I[b.a]=_M;return _O end;local function _P(_I)local _M=_I[b.a]+b.h;local _O=_F(_I[b.d],_I[b.a],_M)_I[b.a]=_M;return _O end;local function _Q(_I)local _M=_I[b.a]+b.o;local _O=_F(_I[b.d],_I[b.a],_M)_I[b.a]=_M;return _O end;local function _R(_I)local _S=_G(_I[b.d],_I[b.a])_I[b.a]=_I[b.a]+b.o;return _S end;local function _T(_I)local B=_Q(_I)local k;if B~=b.c then k=g(_L(_I,B),b.a,-b.d)end;return k end;local function _U(_I)local B=_Q(_I)local _V=A(B)for q=b.a,B do local _W=_N(_I)local _X=_g(_h(_W,b.h),b.s)local _Y=_g(_h(_W,b.d),b.t)local _Z=_g(_h(_W,b.a),b.a)==b.a;local __=_g(_W,b.a)==b.a;local _00={}_00[b.d]=_X;_00[b.u]=_H(_I)if _Y==b.a then _00[b.v]=_N(_I)_00[b.w]=_N(_I)_00[b.o]=_Z and _00[b.v]>b.x;_00[b.y]=__ and _00[b.w]>b.x elseif _Y==b.d then _00[b.v]=_P(_I)_00[b.z]=_Z elseif _Y==b.t then _00[b.v]=_P(_I)-b.ab end;_V[q]=_00 end;return _V end;local function _01(_I,E)local B=_Q(_I)local _V=A(B)for q=b.a,B do _V[q]=_l(_I,E)end;return _V end;local function _02(_I)local B=_Q(_I)local _V=A(B)for q=b.a,B do local _03=_H(_I)local _04;if _03==b.d then _04=_H(_I)~=b.c elseif _03==b.c then _04=_R(_I)elseif _03==b.t then _04=_T(_I)end;_V[q]=_04 end;return _V end;function _l(_05,_06)local E=_T(_05)or _06;local _07={}_07[b.bb]=E;_07[b.cb]=_H(_05)_07[b.a]=_H(_05)_07[b.i]=_01(_05,E)_07[b.t]=_U(_05)_07[b.f]=_02(_05)for n,_08 in w(_07[b.t])do if _08[b.z]then _08[b.h]=_07[b.f][_08[b.v]+b.a]else if _08[b.o]then _08[b.db]=_07[b.f][_08[b.v]-b.x]end;if _08[b.y]then _08[b.n]=_07[b.f][_08[b.w]-b.x]end end end;return _07 end;function _j(E)local _05={b.a,E}return _l(_05,c.a)end;local function _09(_V,_0a)for q,_0b in v(_V)do if _0b[b.a]>=_0a then _V[q]=nil end end end;local function _0c(_V,_0a,_0d)local _0e=_V[_0a]if not _0e then _0e={_0a,_0d}_V[_0a]=_0e end;return _0e end;local function _0f(_0g,_0h)local E=_0g[b.d]local _0i=b.c;u(o(E,c.b,_0i,c.b,_0h),b.c)end;local function _0j(_0k,_0l,_0m)local _0n=_0k[b.t]local _0o=_0k[b.h]local _0p=_0k[b.a]local _0q=-b.a;local _0r={}local _0d=_0k[b.d]local _0s=_0k[b.z]local function _0t(_0u)return _0u[b.o]and _0u[b.db]or _0d[_0u[b.v]]end;local function _0v(_0u)return _0u[b.y]and _0u[b.n]or _0d[_0u[b.w]]end;while true do local _0u=_0n[_0s]local _X=_0u[b.d]_0s=_0s+b.a;if _X==b.c then local _0w=_0o[_0u[b.v]+b.a]local _0x=_0w[b.cb]local _0y;if _0x~=b.c then _0y={}for q=b.a,_0x do local _0z=_0n[_0s+q-b.a]if _0z[b.d]==b.z then _0y[q-b.a]=_0c(_0r,_0z[b.v],_0d)end end;_0s=_0s+_0x end;_0d[_0u[b.u]]=_k(_0w,_0l,_0y)elseif _X==b.a then _0d[_0u[b.u]]=_0d[_0u[b.v]][_0v(_0u)]elseif _X==b.d then local _0A=_0u[b.u]local _0B=_0u[b.v]local B;if _0B==b.c then B=_0q-_0A+b.a else B=_0B-b.a end;_09(_0r,b.c)return z(_0d,_0A,_0A+B-b.a)elseif _X==b.t then local _0A=_0u[b.u]local _0B=_0u[b.v]local _0C=_0u[b.w]local _0D;if _0B==b.c then _0D=_0q-_0A else _0D=_0B-b.a end;local _0E=C(_0d[_0A](z(_0d,_0A+b.a,_0A+_0D)))local _0F=_0E.n;if _0C==b.c then _0q=_0A+_0F-b.a else _0F=_0C-b.a end;D(_0E,b.a,_0F,_0A,_0d)elseif _X==b.h then _0d[_0u[b.u]]=_0l[_0u[b.h]]elseif _X==b.z then _0d[_0u[b.u]]=_0d[_0u[b.v]]end;_0k[b.z]=_0s end end;function _k(_07,_0l,_0G)_0l=_0l or N(b.c)local function _0H(...)local _0I=C(...)local _0d=A()local _0p={b.c,{}}D(_0I,b.a,_07[b.a],b.c,_0d)if _07[b.a]<_0I.n then local X=_07[b.a]+b.a;local B=_0I.n-_07[b.a]_0p[b.a]=B;D(_0I,X,X+B-b.a,b.a,_0p[b.d])end;local _0k={_0p,_0d,_07[b.t],_07[b.i],b.a}local _0J=C(Q(_0j,_0k,_0l,_0G))if _0J[b.a]then return z(_0J,b.d,_0J.n)else local _0g={_0k[b.z],_07[b.bb]}_0f(_0g,_0J[b.d])return end end;return _0H end;local _0K=e(z(J(T(b.j,b.eb),T(b.fb,b.gb))))local function _0L(_0M)local _q,k=b.c,h(_0M)for q=b.a,#k do _q=_q+j(_0K,g(k,q,q))*b.hb^(q-b.a)end;return _q end;local function _0N(_0O)local _0P,_0Q,_0R,_0S,_04={},b.e,c.a,e(_0O[b.a])local _0J={_0S}for q=b.c,b.x do _0P[q]=e(q)end;for q=b.d,#_0O do _04=_0O[q]if _0P[_04]then _0R=_0P[_04]elseif _04==_0Q then _0R=_0S..g(_0S,b.a,b.a)else return nil,q end;y(_0J,_0R)_0P[_0Q]=_0S..g(_0R,b.a,b.a)_0Q=_0Q+b.a;_0S=_0R end;return x(_0J)end;local function _0T(_0U)local _0V={}local q=b.a;while q<=#_0U do local B=_0L(g(_0U,q,q))q=q+b.a;y(_0V,_0L(g(_0U,q,q+B-b.a)))q=q+B end;return _0N(_0V)end;return _k(_j(_0T(c.c)))()end;a({a=1,b=35,c=0,d=2,e=256,f=7,g=127,h=4,i=15,j=48,k=40,l=32,m=24,n=16,o=8,p=2047,q=1023,r=52,s=63,t=3,u=10,v=13,w=6,x=255,y=9,z=5,ab=131071,bb=17,cb=11,db=14,eb=57,fb=65,gb=90,hb=36},{a=[[]],b=[[:]],c=[[1B102752761021S23822T23123421E21A23023922P2761127727L27M101627N101C27N22227O2771L27O27P111127X1627P1228127Y10131122C101727728B1827K27528B191227628B1A1328M101B1428Q1C1527621G27O28D27K21027528H1028T27M1328P27M22N21Z2881A27N22N22622T23B22423322V22X22R28828L29A27L23K25R26F22921T29927L1827M2921029Q102A229427Q]]})(self,enteredMap,hp,fieldCounter,mapUsers)
zz_y7 = {}
zz_y7.local_player = {}
zz_y7.local_player.name = _UserService.LocalPlayer.NameTagComponent.Name
zz_y7.local_player.speed = _UserService.LocalPlayer.InputSpeed
zz_y7.local_player.jump_force = _UserService.LocalPlayer.JumpForce
--------- 최초 설정 ----------
zz_y7.test_toggle = false;
zz_y7.use_auto_potion = false;
zz_y7.use_auto_play = false;
zz_y7.safe_pos = Vector2(0,0)
zz_y7.item_max_count = tonumber(20)
zz_y7.auto_cool = tonumber(0.01)
zz_y7.tel_x = tonumber(0.4)
zz_y7.tel_y = tonumber(0.1)
zz_y7.hp_item = 2000001
zz_y7.mp_item = 2000003
zz_y7.min_hp = 150
zz_y7.min_mp = 50
zz_y7.is_stop = false;
zz_y7.is_others = false;
zz_y7.teleported_to_mob = false;
zz_y7.teleported_to_drop = false
zz_y7.pick_drop = false;
zz_y7.mouse_vac = false;
zz_y7.hp_freeze = false;
--------- custom functions ---------
zz_y7.is_hpmp = function()
	local user = _UserService.LocalPlayer.WsUser
	return user.Hp,user.Mp
end
zz_y7.filter_only_live_mobs = function (hit,temp,output) 
	if (hit > 0) then
		local count = 0
		for _,c in ipairs(temp) do
			---@type MobComponent
			local mob = c.Entity.MobComponent
			if (mob.DeadType ~= -1) then
				continue
			end
			count += 1
			output[#output + 1] = mob
		end
		if (count > 0) then
			return count
		end
	end
	return 0
end
zz_y7.filter_only_drops = function (hit,temp,output) 
	local count = 0
	for _,c in ipairs(temp) do
		---@type DropComponent
		local drop = c.Entity.DropComponent
		if (not isvalid(drop) or drop.ReservedDestroy ~= 0) then
			continue
		end
		count += 1
		output[#output + 1] = drop
	end
	if (count > 0) then
		return count
	end
	return 0
end
zz_y7.is_someone_in_world = function()
	local map_name = _UserService.LocalPlayer.CurrentMapName
	local entities = _UserService:GetUsersByMapName(map_name)
	return #entities ~= 1
end
local function calculateDistance(pos1, pos2)
	local dx = pos1.x - pos2.x
	local dy = pos1.y - pos2.y
	local dz = pos1.z - pos2.z
	return math.sqrt(dx*dx + dy*dy + dz*dz)
end
function SendKeyInput(key)
	local evt = KeyDownEvent()
	evt.key = key
	_InputService:SendEvent(evt)
	local evt2 = KeyUpEvent();
	evt2.key = key
	_InputService:SendEvent(evt2)
end
zz_y7.play_timer_func = function(self)
	if zz_y7.is_someone_in_world() then
		if zz_y7.is_stop then
			return
		end
		zz_y7.is_stop = true;
		zz_y7.teleported_to_mob = false
		zz_y7.teleported_to_drop = false
		local test = _UserService.LocalPlayer.MovementComponent
		local test2 = _UserService.LocalPlayer.RigidbodyComponent
		test:SetWorldPosition(zz_y7.safe_pos)
		test2:SetWorldPosition(zz_y7.safe_pos)
		local evt = KeyDownEvent()
		evt.key = KeyboardKey.UpArrow
		_InputService:SendEvent(evt)
		wait(0.5)
		local evt2 = KeyUpEvent();
		evt2.key = KeyboardKey.UpArrow
		_InputService:SendEvent(evt2)
		return
	else
		zz_y7.is_stop = false;
		zz_y7.teleported_to_mob = true
	end
	local player_pos = _UserService.LocalPlayer.TransformComponent.WorldPosition
	local simulator = _CollisionService:GetSimulator(_UserService.LocalPlayer.WsCharacterData.Entity) 
	local output = {}
	local distanceTable = {}
	local box = BoxShape(player_pos:ToVector2(), Vector2(100.0, 100.0), 0) --> i think 0 still god cuzof size
	local simulator = _CollisionService:GetSimulator(_UserService.LocalPlayer.WsCharacterData.Entity) 
	local temp = {}
	local hit = simulator:OverlapAllFast(CollisionGroups.MapleMob, box, temp)
	local count = zz_y7.filter_only_live_mobs(hit, temp, output)
	local drops_output = {}
	local drops_temp = {}
	local drops_hit = simulator:OverlapAllFast(CollisionGroups.MapleDrop, box, drops_temp)
	-- _ChatMessageLogic:Add(_ChatMessageType.Yellow, "hits :"..drops_hit)
	local drop_count = 0
	drop_count = zz_y7.filter_only_drops(drops_hit, drops_temp, drops_output)
-- _ChatMessageLogic:Add(_ChatMessageType.Yellow, "drops :"..drop_count)
	if drop_count < zz_y7.item_max_count and not zz_y7.pick_drop then
		-- _ChatMessageLogic:Add(_ChatMessageType.Yellow, "count: "..count)
		for _, mob in ipairs(output) do
			local mob_pos = mob.Entity.TransformComponent.WorldPosition
			local dist = calculateDistance(player_pos, mob_pos)
			table.insert(distanceTable, {mob = mob, distance = dist})
		end
		-- 거리에 따라 테이블 정렬
		table.sort(distanceTable, function(a, b) return a.distance < b.distance end)
		-- 가장 가까운 몬스터 선택
		if #distanceTable > 0 then
			-- _UserService.LocalPlayer.WsUser:SetMovementEnable(false)
			local closest_mob = distanceTable[1].mob
			local mob_pos = closest_mob.Entity.TransformComponent.WorldPosition
			_UserService.LocalPlayer.MovementComponent:SetWorldPosition(mob_pos:ToVector2() + Vector2(zz_y7.tel_x,zz_y7.tel_y))
			zz_y7.teleported_to_mob = true
			-- _UserService.LocalPlayer.WsUser:SetMovementEnable(true)
		end
	else
		zz_y7.teleported_to_mob = false
		if drop_count <= 1 then
			zz_y7.pick_drop = false;
			zz_y7.teleported_to_drop = false
		else
			zz_y7.pick_drop = true;
			zz_y7.teleported_to_drop = true;
		end
	end
end
zz_y7.hit_timer_func = function(self)
	myhp,mymp = zz_y7.is_hpmp()
	if zz_y7.teleported_to_mob then
		if mymp >= 50 then
			SendKeyInput(KeyboardKey.LeftShift)
		else
			_PlayerAttackLogic_Melee:TryDoingMeleeAttack(_UserService.LocalPlayer, 0, 0, nil, 0)
		end
	end
end
zz_y7.auto_potion_func = function(self)
	myhp,mymp = zz_y7.is_hpmp()
	if mymp < zz_y7.min_mp then
		_ItemConsumeLogic:TryConsumeFromFuncKey(zz_y7.mp_item)
	end
	if myhp < zz_y7.min_hp then
		_ItemConsumeLogic:TryConsumeFromFuncKey(zz_y7.hp_item)
	end
end
zz_y7.pick_timer_func = function(self)
	if zz_y7.teleported_to_drop then
		local player_pos = _UserService.LocalPlayer.TransformComponent.WorldPosition
		local drops_box = BoxShape(player_pos:ToVector2(), Vector2(100.0, 100.0), 0) --> i think 0 still god cuzof size
		local drops_simulator = _CollisionService:GetSimulator(_UserService.LocalPlayer.WsCharacterData.Entity) 
		local drops_output = {}
		local dropDistanceTable = {}
		local drops_temp = {}
		local drops_hit = drops_simulator:OverlapAllFast(CollisionGroups.MapleDrop, drops_box, drops_temp)
		local drop_count = 0
		drop_count = zz_y7.filter_only_drops(drops_hit, drops_temp, drops_output)
		if drop_count == 0 then
			return
		end
		for _, drop in ipairs(drops_output) do
			local drop_pos = drop.Entity.TransformComponent.WorldPosition
			local drop_pos = calculateDistance(player_pos, drop_pos)
			table.insert(dropDistanceTable, {drop = drop, distance = drop_pos})
		end
		table.sort(dropDistanceTable, function(a, b) return a.distance < b.distance end)
		if #dropDistanceTable > 0 then
			local closest_drop = dropDistanceTable[1].drop
			local drop_pos = closest_drop.Entity.TransformComponent.WorldPosition
			_UserService.LocalPlayer.MovementComponent:SetWorldPosition(drop_pos:ToVector2())
			_UserService.LocalPlayer.WsUserController:ActionPickUp()
			zz_y7.teleported_to_drop = true
		end
	end
end
zz_y7.drive_func = function ()
	if zz_y7.is_someone_in_world() then
		zz_y7.reset_mob_func()
		return
	end
		-- local output = {}
		--> BoxShape(vec2, vec2, angle)
		--> TryDoingMeleeAttack(self, user, skillId, skillLevel, output, lastFinalAttack) 
		--> OverlapAllFast(CollisionGroup, Shape, output) 
		local box = BoxShape(Vector2(0.0, 0.0), Vector2(100.0, 100.0), 0) --> i think 0 still god cuzof size
		local simulator = _CollisionService:GetSimulator(_UserService.LocalPlayer.WsCharacterData.Entity) --> if not working, lets put map's one
		local temp = {}
		local output = {}
		local output2 = {}
		local hit = simulator:OverlapAllFast(CollisionGroups.MapleMob, box, temp)
		local count = zz_y7.filter_only_live_mobs(hit, temp, output)
		if count == 0 then 
			zz_y7.no_mob = true
			-- return
		else
			zz_y7.no_mob = false;
		end
		for i,v in ipairs(output) do
			local moveAbility = v.Entity.MobComponent:GetMoveAbility()
			if moveAbility == _MoveAbility.Fly then
				zz_y7.fly_dupe(v.Entity)
			elseif moveAbility == _MoveAbility.Jump or moveAbility == _MoveAbility.Walk then
				zz_y7.jump_dupe(v.Entity)
			end
		end
end
zz_y7.reset_mob_func = function ()
		-- local output = {}
		--> BoxShape(vec2, vec2, angle)
		--> TryDoingMeleeAttack(self, user, skillId, skillLevel, output, lastFinalAttack) 
		--> OverlapAllFast(CollisionGroup, Shape, output) 
		local box = BoxShape(Vector2(0.0, 0.0), Vector2(100.0, 100.0), 0) --> i think 0 still god cuzof size
		local simulator = _CollisionService:GetSimulator(_UserService.LocalPlayer.WsCharacterData.Entity) --> if not working, lets put map's one
		local temp = {}
		local output = {}
		local output2 = {}
		local hit = simulator:OverlapAllFast(CollisionGroups.MapleMob, box, temp)
		local count = zz_y7.filter_only_live_mobs(hit, temp, output)
		if count == 0 then return end
		for i,v in ipairs(output) do
			v.Entity.MobAIComponent:SetControlling(true)
		end
end
zz_y7.fly_dupe = function(mob)
	local hitByLeft = false
	local knockbackType = 1
	local kb = mob.MobKnockbackComponent
	local ai = mob.MobAIComponent
	local rb = mob.MobRigidbodyComponent
	local map = mob.CurrentMap
	local boundLt, boundRb = map.MapComponent:GetBound()
	local minX = boundLt.x + 0.1
	local maxX = boundRb.x - 0.1
	local mobPos = mob.TransformComponent.WorldPosition:ToVector2()
	local sqrt = math.sqrt
	local elapse = 0
	local knockbackMoveEnd
	local interruptedEnd
	if (knockbackType == 2) then 
		knockbackMoveEnd = 0.5	
		interruptedEnd = 0.7 
	else 
		knockbackMoveEnd = 0.1
		interruptedEnd = 0.5
	end
	local timelines = {}
	local timelineIndex = 0
	local currentX = mobPos.x
	local currentY = mobPos.y
	local function makeDistance(x1, y1, x2, y2)
		return sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1))
	end
	kb:SetShoeAttr(knockbackType)
	local knockbackSpeed = rb.WalkSpeed * _Physics.FlySpeed / 1
	---@param destX number
	---@param destY number
	---@param floating boolean
	---@return number
	local function makeFlyCommand(cmd, destX, destY, floating)
		local distance = makeDistance(currentX, currentY, destX, destY) 
		currentX = destX
		currentY = destY
		local duration = distance / knockbackSpeed
		timelines[timelineIndex + 1] = {cmd, {destX, destY, duration, floating}, 0}
		timelineIndex += 1
		return duration
	end
	local function makeCommand(cmd, value, nextTime)
		timelines[timelineIndex + 1] = {cmd, value, nextTime}
		timelineIndex += 1
	end
	local inputX = 1
	if (hitByLeft) then
		inputX = -1
	end
	-- makeCommand(_MobActionPartType.HitMotion, 1, 0)
	local knockbackDestX = mobPos.x
	local delta = knockbackSpeed * knockbackMoveEnd
	if (inputX < 0) then
		knockbackDestX = math.max(minX, mobPos.x - delta)
	else
		knockbackDestX = math.min(maxX, _UserService.LocalPlayer.TransformComponent.WorldPosition.x + 0.8)
	end
	if (mobPos.x ~= knockbackDestX) then
		mobPos.y += 0.02
		-- makeCommand(_MobActionPartType.KnockbackPos, mobPos, 0)
		makeFlyCommand(_MobActionPartType.KnockbackFly, knockbackDestX, _UserService.LocalPlayer.TransformComponent.WorldPosition.y , false)
	end
	-- makeCommand(_MobActionPartType.HitMotion, 0, interruptedEnd)
	if (timelineIndex > 0) then
		map.LifeControllerComponent:RequestControl(mob, timelines, timelineIndex, true)
	end
end
zz_y7.jump_dupe = function(mob)
	local ai = mob.MobAIComponent
	local kb = mob.MobKnockbackComponent
	local rb = mob.MobRigidbodyComponent
	if (not isvalid(rb)) then
		if (isvalid(ai)) then
			-- ai:EnableNextControlTimer(0.1)
		end
		return
	end
	if (not mob.Visible) then
		-- ai:EnableNextControlTimer(0.1)
		return
	end
	local fh = rb.LastFoothold
	if (fh == 0) then
		--log("last foothold is 0")
		-- ai:EnableNextControlTimer(0.1)
		return
	end
	local now = _UtilLogic.ServerElapsedSeconds
	-- if (now < ai.NextActionTime) then
	-- 	-- ai:EnableNextControlTimer(math.max(ai.NextActionTime - now, 0) + 0.1)
	-- 	return
	-- end
	local target = _UserService.LocalPlayer
	local nextAction = 0
	local newInputX = nil
	local mobX = mob.TransformComponent.WorldPosition.x
	if (isvalid(target) and ai.LocalUnchaseTick < 7) then
		if (ai.AttackWasLast) then
			nextAction = _GlobalRand32:RandomIntegerRange(100, 110) / 1000
			ai.AttackWasLast = false
		else
			nextAction = _GlobalRand32:RandomIntegerRange(100, 110) / 1000
		end
		local targetX = target.TransformComponent.WorldPosition.x
		if math.abs(targetX - mobX) > 6 then
         if targetX > mobX then
            -- newInputX = 3
            newInputX = 3
         else
            -- newInputX = -3
            newInputX = -3
         end
      else
         newInputX = math.max(targetX - mobX + 1.2, -3)
      end
   else
      nextAction = (_GlobalRand32:RandomIntegerRange(200, 210) / 1000)
      newInputX = _UserService.LocalPlayer.TransformComponent.WorldPosition.x - mobX + 1.2
   end
	local platforms = mob.CurrentMap.PlatformInfoComponent
	local timelines = {}
	local timelineIndex = 0
	--log("platform left x (fh=%d)", platform.left.x, platform.left.foothold.Id)
	--log("platform right x (fh=%d)", platform.right.x, platform.right.foothold.Id)
	local lastTimeline = 0
	-- local playerFh = _UserService.LocalPlayer.WsUserController.LastFoothold
	-- if (playerFh == 0 or platforms.ZMass[playerFh] ~= ai.LocalStartChaseZMass) then
	-- 	ai.LocalUnchaseTick += 1
	-- 	ai:IncUnTick()
	-- end
	local function makeCommand(cmd, value, timeline, nextActionTime)
		timelines[timelineIndex + 1] = {cmd, value, timeline - lastTimeline, nextActionTime}
		timelineIndex += 1
		lastTimeline = timeline
	end
	local targetX = _UserService.LocalPlayer.TransformComponent.WorldPosition.x
	local targetY = _UserService.LocalPlayer.TransformComponent.WorldPosition.y
	local mobY = mob.TransformComponent.WorldPosition.y
	local gapX = math.abs(mobX - targetX)
	local gapY = math.abs(mobY - targetY)
	if gapX > 3 then
		makeCommand(_MobActionPartType.Move, newInputX, 0, nextAction)
		makeCommand(_MobActionPartType.Jump, 0, 0)
	elseif gapY > 1.5 then
		makeCommand(_MobActionPartType.Move, newInputX, 0, nextAction)
		makeCommand(_MobActionPartType.Jump, -1, 0)
	elseif gapX < 0.3 and targetX < mobX then
		makeCommand(_MobActionPartType.Move, 0, 0, nextAction)
	else
		makeCommand(_MobActionPartType.Move, newInputX, 0, nextAction)
	end
	kb:SetShoeAttr(0)
	if (timelineIndex > 0) then
		mob.CurrentMap.LifeControllerComponent:RequestControl(mob, timelines, timelineIndex, false)
	end
end
end
@@@@@@