-- LUA Script - precede every function and global member with lowercase name of script + '_main' --DESCRIPTION: smallg's fish following waypoint script --DESCRIPTION: [SWIMSPEED=100] how fast the fish moves --DESCRIPTION: [ALERTRANGE=500] how far the fish will detect the player from --DESCRIPTION: if attackrange = 0 then fish will flee from player when detected --DESCRIPTION: [ATTACKRANGE=80] fish must be within this range to start attacking the player --DESCRIPTION: [DAMAGE=1] --DESCRIPTION: [WATERHEIGHT=500] player is in water when below this height (default = 500) --DESCRIPTION: [ANIMATED!=0] --DESCRIPTION: [SWIMANIM=1] swimming animation number --DESCRIPTION: [ATTACKANIM=2] attacking animation number fishstate = {} local angle = {} local maxplayerdistance = {} local swim_speed_max = {} local followheight = {} local pathindex, pointindex, pathdirection = {}, {}, {} isfish = {} g_fish = {} function fish_properties(e, swimspeed, alertrange, attackrange, damage, waterheight, animated, swimanim, attackanim) local f = g_fish[e] f.swimspeed = swimspeed f.alertrange = alertrange f.attackrange = attackrange f.damage = damage f.waterheight = waterheight f.animated = animated f.swimanim = swimanim f.attackanim = attackanim swim_speed_max[e] = f.swimspeed end function fish_init(e) g_fish[e] = {} local f = g_fish[e] f.swimspeed = 100 f.alertrange = 500 f.attackrange = 80 f.damage = 1 f.waterheight = 500 f.animated = 0 f.swimanim = 1 f.attackanim = 2 if math.random(1,10) < 10 then f.attackrange = 0 end angle[e] = 0 fishstate[e] = "get path" swim_speed_max[e] = f.swimspeed isfish[e] = e end function fish_main(e) local function AngleToPoint(e,x,z) if g_Entity[e] ~= nil and x > 0 and z > 0 then local destx = x - g_Entity[e]['x'] local destz = z - g_Entity[e]['z'] local angle = math.atan2(destx,destz) angle = angle * (180.0 / math.pi) --angle = angle + math.random(-1,1) if angle < 0 then angle = 360 + angle elseif angle > 360 then angle = angle - 360 end return angle end end local function GetPlayerFlatDistance(e) if g_Entity[e] ~= nil then local disx = g_Entity[e]['x'] - g_PlayerPosX local disz = g_Entity[e]['z'] - g_PlayerPosZ return math.sqrt(disx^2 + disz^2) end end local function GetPlayerFlatDistanceToPoint(x,z) local disx = g_PlayerPosX - x local disz = g_PlayerPosZ - z return math.sqrt(disx^2 + disz^2) end local function GetFlatDistance(e,x,z) if g_Entity[e] ~= nil then local disx = g_Entity[e]['x'] - x local disz = g_Entity[e]['z'] - z return math.sqrt(disx^2 + disz^2) end end --PromptLocal(e,fishstate[e]) --TextCenterOnX(50,80,3,g_PlayerPosY) local f = g_fish[e] --Prompt(g_PlayerPosY) CollisionOff(e) if fishstate[e] == "get path" then local PathIndex = -1 local PathPointIndex = -1 local pClosest = 999999 for pa = 1, AIGetTotalPaths(), 1 do for po = 1 , AIGetPathCountPoints(pa), 1 do local pDX = g_Entity[e]['x'] - AIPathGetPointX(pa,po) local pDZ = g_Entity[e]['z'] - AIPathGetPointZ(pa,po) local pDist = (pDX*pDX)+(pDZ*pDZ) if pDist < pClosest and pDist < 12500*12500 then pClosest = pDist PathIndex = pa PathPointIndex = po end end -- po end -- pa if PathIndex > -1 then pathindex[e] = PathIndex pointindex[e] = PathPointIndex pathdirection[e] = 1 fishstate[e] = "swim" followheight[e] = math.random(100,150) angle[e] = 0 else fishstate[e] = "idle" end elseif fishstate[e] == "idle" then if g_PlayerPosY < f.waterheight then if GetPlayerFlatDistance(e) < f.alertrange then StartTimer(e) fishstate[e] = "alerted" end end elseif fishstate[e] == "swim" then if f.animated == 1 then if g_Entity[e]['animating'] ~= f.swimanim then SetAnimation(f.swimanim) LoopAnimation(e) ModulateSpeed(e,1) end end local destx = AIPathGetPointX(pathindex[e],pointindex[e]) local destz = AIPathGetPointZ(pathindex[e],pointindex[e]) angle[e] = AngleToPoint(e,destx, destz) SetRotation(e,0,angle[e],0) --AIEntityGoToPosition(g_Entity[e]['obj'],ai_patrol_x[e],ai_patrol_z[e]) MoveForward(e,f.swimspeed) local ty = GetTerrainHeight(g_Entity[e]['x'], g_Entity[e]['z']) --Prompt(ty) ty = ty + followheight[e] local heightdif = 0 if g_Entity[e]['y'] < ty then heightdif = ty - g_Entity[e]['y'] heightdif = heightdif * 1 elseif g_Entity[e]['y'] > ty then heightdif = g_Entity[e]['y'] - ty heightdif = heightdif * -1 end MoveUp(e,heightdif) local tDistX = g_Entity[e]['x'] - destx local tDistZ = g_Entity[e]['z'] - destz local DistFromPath = (tDistX*tDistX)+(tDistZ*tDistZ) if DistFromPath < 100000 then followheight[e] = math.random(100,150) angle[e] = 0 f.swimspeed = swim_speed_max[e] * math.random(0.75,1.25) if pathdirection[e] == 1 then pointindex[e] = pointindex[e] + 1 local pathnodes = AIGetPathCountPoints(pathindex[e]) if pointindex[e] > pathnodes then pointindex[e] = pathnodes - 1 pathdirection[e] = -1 end else pointindex[e] = pointindex[e] - 1 if pointindex[e] < 1 then pointindex[e] = 2 pathdirection[e] = 1 end end end if g_PlayerPosY < f.waterheight then local pfdist = GetPlayerFlatDistance(e) if pfdist < f.alertrange then fishstate[e] = "alerted" end end elseif fishstate[e] == "alerted" then maxplayerdistance[e] = GetPlayerFlatDistance(e) if f.attackrange > 0 then fishstate[e] = "alert" else fishstate[e] = "flee" end elseif fishstate[e] == "flee" then if pathindex[e] ~= nil then local tdist = 0 local pathnodes = AIGetPathCountPoints(pathindex[e]) for i = 1, pathnodes do local tdist2 = GetPlayerFlatDistanceToPoint(AIPathGetPointX(pathindex[e], i), AIPathGetPointZ(pathindex[e], i)) if tdist2 > tdist then tdist = tdist2 pointindex[e] = i end end angle[e] = AngleToPoint(e, AIPathGetPointX(pathindex[e], pointindex[e]), AIPathGetPointZ(pathindex[e], pointindex[e])) else angle[e] = AngleToPoint(e, g_PlayerPosX, g_PlayerPosZ)+math.random(90,270) end fishstate[e] = "fleeing" elseif fishstate[e] == "fleeing" then SetRotation(e,0,angle[e],0) if f.animated == 1 then if g_Entity[e]['animating'] ~= f.swimanim then SetAnimation(f.swimanim) LoopAnimation(e) end end local ty = GetTerrainHeight(g_Entity[e]['x'], g_Entity[e]['z']) --Prompt(ty) ty = ty + followheight[e] local heightdif = 0 if g_Entity[e]['y'] < ty then heightdif = ty - g_Entity[e]['y'] heightdif = heightdif * 1 elseif g_Entity[e]['y'] > ty then heightdif = g_Entity[e]['y'] - ty heightdif = heightdif * -1 end MoveUp(e,heightdif) ModulateSpeed(e,8) MoveForward(e,f.swimspeed * 8) if GetFlatDistance(e, AIPathGetPointX(pathindex[e], pointindex[e]), AIPathGetPointZ(pathindex[e], pointindex[e])) < 100000 then if pathindex[e] ~= nil then fishstate[e] = "swim" else fishstate[e] = "get path" end end elseif fishstate[e] == "alert" then ModulateSpeed(e,1.5) if f.animated == 1 then if g_Entity[e]['animating'] ~= f.swimanim then SetAnimation(f.swimanim) LoopAnimation(e) end end local pfdist = GetPlayerFlatDistance(e) local turnspeed = 30 * (1-(pfdist / maxplayerdistance[e])) RotateToPlayerSlowly(e,turnspeed) local heightdif = 0 if g_PlayerPosY < g_Entity[e]['y'] then heightdif = (g_Entity[e]['y'] - g_PlayerPosY) * -0.5 elseif g_PlayerPosY > g_Entity[e]['y'] then heightdif = (g_PlayerPosY - g_Entity[e]['y']) * 0.5 end MoveUp(e,heightdif) if pfdist < f.attackrange then fishstate[e] = "attack" elseif pfdist > f.alertrange * 1.3 or g_PlayerPosY > f.waterheight then fishstate[e] = "get path" else MoveForward(e,f.swimspeed * 1.5) end elseif fishstate[e] == "attack" then if f.animated == 1 then if g_Entity[e]['animating'] ~= f.attackanim then SetAnimation(f.attackanim) PlayAnimation(e) end else StartTimer(e) end PlaySound(e,1) HurtPlayer(e,f.damage) ModulateSpeed(e,1) fishstate[e] = "attacking" elseif fishstate[e] == "attacking" then local heightdif = 0 if g_PlayerPosY < g_Entity[e]['y'] then heightdif = (g_Entity[e]['y'] - g_PlayerPosY) * -0.5 elseif g_PlayerPosY > g_Entity[e]['y'] then heightdif = (g_PlayerPosY - g_Entity[e]['y']) * 0.5 end MoveUp(e,heightdif) RotateToPlayer(e) local pfdist = GetPlayerFlatDistance(e) if pfdist > f.attackrange then MoveForward(e,swim_speed_max[e]*0.25) elseif pfdist < f.attackrange then MoveForward(e,swim_speed_max[e]*-0.25) end if (f.animated == 1 and g_Entity[e]['animating'] == 0) or (f.animated == 0 and GetTimer(e) > 250) then if pathindex[e] ~= nil then fishstate[e] = "swim" else fishstate[e] = "get path" end end elseif fishstate[e] == "got away" then if pathindex[e] ~= nil then fishstate[e] = "swim" else fishstate[e] = "get path" end else StopAnimation(e) g_Entity[e]['animating'] = -1 end --fishstate --PromptLocal(e,fishstate[e]) --CollisionOn(e) end --main function fish_exit(e) end