This is an example to study and explore for learning purposes. In the code below, where is the code to stop the behavior if someone else is detected? 

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
@@@@@@