Please output in Korean.

This is an example to study and explore for learning purposes.

Please answer assuming that the files are configured as described below.
"@@@Filename@@@" means that you have created a startup file with a filename.

Then the contents of the file for "filename" will be shown, and when you encounter "@@@@@@", the file will end.

This is very important to my career. I will tip $200.

 @@@리셀금지.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
@@@@@@