/*
The ratter object
The obj_Ratter object is a bot module designed to be used with
EVEBOT. The ratter bot will warp from belt to belt and wtfbbqpwn
any NPC ships it finds.
-- GliderPro
*/
objectdef obj_Ratter
{
variable string SVN_REVISION = "$Rev: 1207 $"
variable int Version
variable string CurrentState
variable time NextPulse
variable int PulseIntervalInSeconds = 5
variable float RatWaitCounter
/* Cache for NPCs */
variable obj_EntityCache RatCache
variable index:int DoNotKillList
/* Used for calculating battleship chain values */
variable obj_Targets_Rats RatCalculator
variable bool bPlayerCheck
/* Todo: This, IsOrbiting, and Orbit should all probably be moved to a movement object. -- stealthy */
variable int iEntityOrbiting
variable int iEntityKeepingAtRange
method Initialize()
{
Event[OnFrame]:AttachAtom[This:Pulse]
BotModules:Insert["Ratter"]
RatCache:SetUpdateFrequency[1]
RatCache:UpdateSearchParams["Unused","CategoryID,CATEGORYID_ENTITY"]
; Startup in fight mode, so that it checks current belt for rats, if we happen to be in one.
This.CurrentState:Set["FIGHT"]
UI:UpdateConsole["obj_Ratter: Initialized", LOG_MINOR]
}
method Pulse()
{
if !${Config.Common.BotMode.Equal[Ratter]}
{
return
}
if ${Time.Timestamp} >= ${This.NextPulse.Timestamp}
{
if !${EVEBot.Paused}
{
This:SetState[]
}
bPlayerCheck:Set[${Targets.PC}]
This.NextPulse:Set[${Time.Timestamp}]
This.NextPulse.Second:Inc[${This.PulseIntervalInSeconds}]
This.NextPulse:Update
}
}
method Shutdown()
{
Event[OnFrame]:DetachAtom[This:Pulse]
}
/* NOTE: The order of these if statements is important!! */
;; STATE MACHINE: * -> IDLE -> MOVE -> PCCHECK -> FIGHT -> *
method SetState()
{
UI:UpdateConsole["obj_Ratter: Hiding: ${Defense.Hiding}, Hide Reason: ${Defense.HideReason}",LOG_DEBUG]
if ${Defense.Hiding}
{
This.CurrentState:Set["IDLE"]
return
}
if ${Me.InStation} == TRUE
{
This.CurrentState:Set["INSTATION"]
return
}
UI:UpdateConsole["obj_Ratter: NPC Check: ${This.NPCCheck}",LOG_DEBUG]
if ${Me.GetTargets} > 0 || (${This.NPCCheck} && !${Targets.PC})
{
This.CurrentState:Set["FIGHT"]
}
else
{
This.CurrentState:Set["STATE_CHANGE_BELT"]
}
}
function ProcessState()
{
if !${Config.Common.BotMode.Equal[Ratter]}
{
return
}
UI:UpdateConsole["obj_Ratter: Processing State: ${This.CurrentState}",LOG_DEBUG]
;UI:UpdateConsole["DEBUG: ${This.CurrentState}"]
switch ${This.CurrentState}
{
case STATE_WAIT_WARP
break
case STATE_IDLE
break
case STATE_ABORT
Call Station.Dock
break
case INSTATION
case STATE_DOCKED
if !${Defense.Hiding}
{
; TODO - this could cause dock/redock loops if armor or hull are below minimums, or if drones are still in shortage state -- CyberTech
call Station.Undock
}
break
case IDLE
; if we're idle (defense was hiding) but defense is no longer hiding; set state to change belt so that we don't freeze up.
if !${Defense.Hiding}
{
UI:UpdateConsole["obj_Ratter: We were hiding but no longer are; setting state to change belt.",LOG_CRITICAL]
This.CurrentState:Set[STATE_CHANGE_BELT]
}
break
case STATE_CHANGE_BELT
Offense:Disable
; TODO - check for use of belt bookmark object instead - CyberTech
;if ${Config.Miner.UseFieldBookmarks}
;{
; call BeltBookmarks.WarpToNext
;}
;else
;{
call Belts.WarpToNext
;}
Ship:Activate_SensorBoost
This.CurrentState:Set["WAITING_FOR_RATS_1"]
break
case WAITING_FOR_RATS_1
This.RatWaitCounter:Inc[0.5]
if ${Targets.PC}
{
This.CurrentState:Set["STATE_CHANGE_BELT"]
}
elseif ${This.NPCCheck}
{
This.CurrentState:Set["WAITING_FOR_RATS_2"]
}
elseif ${This.RatWaitCounter} > 30
{
UI:UpdateConsole["Rats didn't show up, moving"]
This.CurrentState:Set["STATE_CHANGE_BELT"]
}
else
{
wait 0.5
}
break
case WAITING_FOR_RATS_2
if ${This.RatWaitCounter} > 1
{
; If we had to wait for rats, Wait another second to try to let them get into range/out of warp
wait 10
}
This.RatWaitCounter:Set[0]
This.CurrentState:Set["FIGHT"]
break
case FIGHT
variable bool bEntityFollowingExists = FALSE
if ${Me.ToEntity.Following(exists)}
{
bEntityFollowingExists:Set[TRUE]
}
if !${Offense.Running}
{
Offense:Enable
}
if !${Targeting.Running}
{
Targeting:Enable
}
This:QueueTargets
/* Don't worry about orbiting or keeping at range if we're a missile boat */
/* Todo: remove this, "It's dangerous in belts" */
UI:UpdateConsole["obj_Ratter: Me.ActiveTarget(exists): ${Me.ActiveTarget(exists)}, Me.ToEntity.FollowRange: ${Me.ToEntity.FollowRange}, bEntityFollowingExists: ${bEntityFollowingExists}",LOG_DEBUG]
if !${Config.Combat.ShouldUseMissiles} && ${Me.ActiveTarget(exists)} && \
((${Me.ToEntity.FollowRange} == 0) || !${bEntityFollowingExists})
{
Me.ActiveTarget:KeepAtRange[${Ship.GetMinimumTurretRange[1]}]
}
break
case STATE_ERROR
UI:UpdateConsole["CurrentState is ERROR"]
break
default
UI:UpdateConsole["Error: CurrentState is unknown value ${This.CurrentState}"]
break
}
}
function Move()
{
call Belts.WarpToNext
; TODO - CyberTech - Make this a proper solution instead of this half-ass piss
; Wait for the rats to warp into the belt. Reports are between 10 and 20 seconds.
/* I plan to at the very least align to the next belt before waiting so that we'll
be aligned and ready to go in case nothing shows up after our 30-second waitfest or
social suddenly becomes unsafe. */
variable int Count
for (Count:Set[0] ; ${Count}<=30 ; Count:Inc)
{
if ${Targets.PC} || ${This.NPCCheck}
{
break
}
wait 10
}
if (${Count} > 1)
{
; If we had to wait for rats, Wait another second to try to let them get into range/out of warp
wait 10
}
}
/* QueueTargets(): Handle any queueing, prioritizing, chaining, etc. of rats.
This will do pretty much nothing but queue targets. */
method QueueTargets()
{
/* Activate any sensor boosters */
Ship:Activate_SensorBoost
/* Get total battleship value for chaining purposes */
variable int iTotalBSValue = ${RatCalculator.CalcTotalBattleShipValue}
variable bool bHavePriorityTarget = FALSE
variable bool bHaveSpecialTarget = FALSE
variable bool bHaveMultipleTypes = FALSE
variable bool iTempTypeID
RatCache.Entities:GetIterator[RatCache.EntityIterator]
/* Start iterating through */
if ${RatCache.EntityIterator:First(exists)}
{
do
{
/* Check for multiple types (if we only have one type of rat it's likely an in-progress chain) */
if ${iTempTypeID} == 0
{
iTempTypeID:Set[${RatCache.EntityIterator.Value.TypeID}]
}
if ${iTempTypeID} != ${RatCache.EntityIterator.Value.TypeID}
{
bHaveMultipleTypes:Set[TRUE]
}
/* If this target is already queue in any way, continue on. */
if ${Targeting.IsQueued[${RatCache.EntityIterator.Value.ID}]} || ${Targeting.IsMandatoryQueued[${RatCache.EntityIterator.Value.ID}]} || \
${Offense.IsConcordTarget[${RatCache.EntityIterator.Value.GroupID}]} || ${This.IsDoNotKill[${RatCache.EntityIterator.Value.ID}]}
{
if ${Targeting.IsMandatoryQueued[${RatCache.EntityIterator.Value.ID}]}
{
bHavePriorityTarget:Set[TRUE]
}
UI:UpdateConsole["obj_Ratter: ${RatCache.EntityIterator.Value.ID} is queued, concord, or DNK, skipping in priority target iteration.", LOG_DEBUG]
continue
}
/* Is our target a "priority" target? If so, we won't continue with the rest of the targeting/chaining until
all priority targets are dead. Gotta kill those scrambling bastards... */
if ${Targets.IsPriorityTarget[${RatCache.EntityIterator.Value.Name}]}
{
UI:UpdateConsole["obj_Ratter: We have a priority target: ${RatCache.EntityIterator.Value.Name}."]
bHavePriorityTarget:Set[TRUE]
/* Queue[ID, Priority, TypeID, Mandatory] */
/* Queue it mandatory so we make sure it dies. */
UI:UpdateConsole["obj_Ratter: Queueing priority target: ${RatCache.EntityIterator.Value.Name}"]
Targeting:Queue[${RatCache.EntityIterator.Value.ID},0,${RatCache.EntityIterator.Value.TypeID},TRUE,TRUE]
}
}
while ${RatCache.EntityIterator:Next(exists)}
}
; If I have a priority target, just return. Targeting will lock both mandatory and non-mandatory at the same time, which is bad(tm)
if ${bHavePriorityTarget}
{
return
}
/* Now for the fun task of figuring out chaining. */
/* If I'm chaining spawns and either I'm chaining solo or I'm not chaining solo but there are others here... */
/* I also only want to actually chain it if the calculated BS value is above our threshold */
if (${Config.Combat.ChainSpawns} || (${Config.Combat.ChainSolo} && ${EVE.LocalsCount} == 1)) && \
${iTotalBSValue} >= ${Config.Combat.MinChainBounty}
{
/* Start iterating... */
if ${RatCache.EntityIterator:First(exists)}
{
do
{
; If the target is already queued, just continue.
if ${Targeting.IsQueued[${RatCache.EntityIterator.Value.ID}]} || ${Offense.IsConcordTarget[${RatCache.EntityIterator.Value.GroupID}]} || \
${This.IsDoNotKill[${RatCache.EntityIterator.Value.ID}]}
{
UI:UpdateConsole["obj_Ratter: ${RatCache.EntityIterator.Value.ID} already queued, concord, or DNK, skipping", LOG_DEBUG]
continue
}
; Since we're chaining, if it isn't a special spawn or a battleship, and we've already queued any priority targets, we don't want it.
; Add it to do not kill.
UI:UpdateConsole["obj_Ratter: Find Battleship? Name: ${RatCache.EntityIterator.Value.Name}, Group: ${RatCache.EntityIterator.Value.Group}, Exists? ${RatCache.EntityIterator.Value.Group.Find["Battleship"](exists)}, Mutli? ${bHaveMultipleTypes}", LOG_DEBUG]
if !${RatCache.EntityIterator.Value.Group.Find["Battleship"](exists)} && !${Targets.IsSpecialTarget[${RatCache.EntityIterator.Value.Name}]}
{
UI:UpdateConsole["obj_Ratter: ${RatCache.EntityIterator.Value.Name} isn't a battleship or special target -- adding to DNK list.", LOG_DEBUG]
DoNotKillList:Insert[${RatCache.EntityIterator.Value.ID}]
continue
}
; If we have a special target, queue it higher priority.
; Currently it does nothing because combat doesn't detect "higher priority" targets
if ${Targets.IsSpecialTarget[${RatCache.EntityIterator.Value.Name}]}
{
UI:UpdateConsole["obj_Ratter: Queueing special target ${RatCache.EntityIterator.Value.Name}, ${RatCache.EntityIterator.Value.ID}.",LOG_CRITICAL]
if ${Config.Common.UseSound}
{
Sound:PlayDetectSound
}
Targeting:Queue[${RatCache.EntityIterator.Value.ID},1,${RatCache.EntityIterator.Value.TypeID},FALSE]
continue
}
; Basically, the only way we get this far is our entity is a battleship we haven't queued.
; So queue it!
UI:UpdateConsole["obj_Ratter: Queueing chainable battleship ${RatCache.EntityIterator.Value.Name}",LOG_DEBUG]
Targeting:Queue[${RatCache.EntityIterator.Value.ID},2,${RatCache.EntityIterator.Value.TypeID},FALSE]
}
while ${RatCache.EntityIterator:Next(exists)}
}
}
else
{
if ${RatCache.EntityIterator:First(exists)}
{
do
{
; If the target is already queued, just continue.
if ${Targeting.IsQueued[${RatCache.EntityIterator.Value.ID}]} || ${Offense.IsConcordTarget[${RatCache.EntityIterator.Value.GroupID}]} || \
${This.IsDoNotKill[${RatCache.EntityIterator.Value.ID}]}
{
UI:UpdateConsole["obj_Ratter: ${RatCache.EntityIterator.Value.ID} already queued, concord, or DNK, skipping", LOG_DEBUG]
continue
}
; If we have a special target, queue it higher priority.
; Currently it does nothing because combat doesn't detect "higher priority" targets
if ${Targets.IsSpecialTarget[${RatCache.EntityIterator.Value.Name}]}
{
UI:UpdateConsole["obj_Ratter: Not chaining, queueing special target ${RatCache.EntityIterator.Value.Name}, ${RatCache.EntityIterator.Value.ID}.",LOG_CRITICAL]
if ${Config.Common.UseSound}
{
Sound:PlayDetectSound
}
Targeting:Queue[${RatCache.EntityIterator.Value.ID},1,${RatCache.EntityIterator.Value.TypeID},FALSE]
continue
}
UI:UpdateConsole["obj_Ratter: Not chaining, queueing target ${RatCache.EntityIterator.Value.Name}.",LOG_DEBUG]
Targeting:Queue[${RatCache.EntityIterator.Value.ID},2,${RatCache.EntityIterator.Value.TypeID},FALSE]
}
while ${RatCache.EntityIterator:Next(exists)}
}
}
}
/* bool NPCCheck():
return true if we have non-donotkill and non-concord npcs nearby and non-singletype */
member:bool NPCCheck()
{
RatCache.Entities:GetIterator[RatCache.EntityIterator]
variable bool HaveMultipleTypes = FALSE
variable int TempTypeID
if ${RatCache.EntityIterator:First(exists)}
{
do
{
UI:UpdateConsole["obj_Ratter: ${RatCache.EntityIterator.Value.Type} ${RatCache.EntityIterator.Value.TypeID} ${RatCache.EntityIterator.Value.Group} ${RatCache.EntityIterator.Value.GroupID}",LOG_DEBUG]
if ${TempTypeID} == 0
{
TempTypeID:Set[${RatCache.EntityIterator.Value.TypeID}]
}
if ${TempTypeID} != ${RatCache.EntityIterator.Value.TypeID}
{
HaveMultipleTypes:Set[TRUE]
break
}
}
while ${RatCache.EntityIterator:Next(exists)}
}
UI:UpdateConsole["obj_Ratter: HaveMultipleTypes? ${HaveMultipleTypes}",LOG_DEBUG]
if ${RatCache.EntityIterator:First(exists)}
{
do
{
if !${Offense.IsConcordTarget[${RatCache.EntityIterator.Value.GroupID}]} && \
(${Targets.IsPriorityTarget[${RatCache.EntityIterator.Value.Name}]} || \
${Targets.IsSpecialTarget[${RatCache.EntityIterator.Value.Name}]} || \
(!${This.IsDoNotKill[${RatCache.EntityIterator.Value.ID}]} && ${HaveMultipleTypes}))
{
return TRUE
}
}
while ${RatCache.EntityIterator:Next(exists)}
}
return FALSE
}
/* bool IsDoNotKill(int entityID):
return true if given entity ID is on our do not kill list, otherwise return false */
member:bool IsDoNotKill(int entityID)
{
variable iterator DoNotKillIterator
DoNotKillList:GetIterator[DoNotKillIterator]
if ${DoNotKillIterator:First(exists)}
{
do
{
if ${DoNotKillIterator.Value} == ${entityID}
{
return TRUE
}
}
while ${DoNotKillIterator:Next(exists)}
}
return FALSE
}
}