1 import configuration
, utility
, packets
, craw
, threading
, time
, nil
.thread
, random
, npc
3 class town_portal_entry
:
4 def __init__(self
, object_id
, x
, y
):
8 class town_portal_timer(threading
.Thread
):
9 def __init__(self
, tp_entry
):
10 threading
.Thread
.__init
__(self
)
11 self
.tp_entry
= tp_entry
15 #print 'Entering TP %08x' % self.tp_entry.id
16 x
, y
= self
.tp_entry
.location
17 craw
.send_packet('\x03' + utility
.pack_number(x
, 2) + utility
.pack_number(y
, 2))
18 time
.sleep(configuration
.follow_portal_move_delay
)
19 portal_id
= utility
.pack_number(self
.tp_entry
.id, 4)
20 craw
.send_packet('\x04\x02\x00\x00\x00' + portal_id
)
21 time
.sleep(configuration
.follow_portal_interact_delay
)
22 craw
.send_packet('\x13\x02\x00\x00\x00' + portal_id
)
23 #print 'Done entering the TP'
25 class follow_handler_class
:
26 def __init__(self
, town_portal_handler
):
27 self
.town_portal_handler
= town_portal_handler
31 self
.following
= False
32 self
.town_portal_map
= {}
33 self
.current_portal
= None
34 self
.attacking
= False
38 def command_match(self
, input, command
):
39 return input == command
or input == '%s, %s' % (self
.my_name
, command
)
41 def process_command(self
, name
, message
):
42 player
= utility
.get_player_by_name(name
)
44 if self
.my_name
== name
:
48 print 'Unable to retrieve player data of player %s' % player
51 #print 'Processing <%s> "%s"' % (name, message)
53 if self
.command_match(message
, configuration
.follow_command
):
55 self
.leader
= player
.name
56 self
.leader_id
= player
.id
57 print 'Following %s' % self
.leader
58 packets
.send_chat(configuration
.follow_confirmation
% self
.leader
)
60 elif self
.command_match(message
, configuration
.stop_command
):
62 self
.following
= False
63 print 'No longer following %s' % self
.leader
64 packets
.send_chat(configuration
.stop_confirmation
% self
.leader
)
66 print '%s asked us to stop following the leader but we are currently not following anybody' % name
67 packets
.send_chat(configuration
.stop_error
)
69 elif self
.command_match(message
, configuration
.enter_tp_command
) and not utility
.town_check():
72 elif self
.command_match(message
, configuration
.enter_town_tp_command
) and utility
.town_check():
75 elif self
.command_match(message
, configuration
.go_to_town_command
):
76 print 'Casting a town portal to go to town'
77 self
.town_portal_handler
.town_tp()
79 elif self
.command_match(message
, configuration
.attack_command
):
81 packets
.send_chat(configuration
.attack_error
)
83 packets
.send_chat(configuration
.attack_confirmation
)
85 nil
.thread
.create_thread(self
.attack_thread
)
87 elif self
.command_match(message
, configuration
.stop_attacking_command
):
89 packets
.send_chat(configuration
.stop_attacking_confirmation
)
90 self
.attacking
= False
92 packets
.send_chat(configuration
.stop_attacking_error
)
94 def enter_tp(self
, player
):
96 tp_entry
= self
.town_portal_map
[player
.id]
98 packets
.send_chat(configuration
.tp_error
% player
.name
)
101 packets
.send_chat(configuration
.enter_tp_confirmation
% player
.name
)
102 town_portal_timer(tp_entry
)
104 def process_bytes(self
, bytes
):
105 if packets
.entering_game(bytes
):
108 message
= packets
.parse_message(bytes
)
110 name
, message
= message
112 my_name
= utility
.get_my_name()
113 self
.my_name
= my_name
115 if name
not in configuration
.follow_leaders
:
118 if name
!= my_name
and self
.command_match(message
, configuration
.leave_command
):
119 print '%s ordered us to leave the game' % name
122 if my_name
!= None and my_name
in configuration
.followers
:
123 self
.process_command(name
, message
)
125 move
= packets
.parse_move(bytes
)
127 player_id
, x
, y
= move
128 if self
.following
and player_id
== self
.leader_id
:
129 #print 'Following %s to (%d, %d)' % (self.leader, x, y)
130 craw
.move_click(x
, y
)
132 assignment
= packets
.town_portal_assignment(bytes
)
133 if assignment
!= None:
134 object_id
, x
, y
= assignment
135 self
.current_portal
= town_portal_entry(object_id
, x
, y
)
136 #print 'Portal assignment: %08x (%d, %d)' % (object_id, x, y)
138 portal_ownership
= packets
.portal_ownership(bytes
)
139 if portal_ownership
!= None:
140 player_id
, portal_id
= portal_ownership
141 if self
.current_portal
== None:
142 print 'Received portal ownership information without a previous object assignment'
144 self
.town_portal_map
[player_id
] = self
.current_portal
145 x
, y
= self
.current_portal
.location
146 #print 'Portal ownership detected: Portal %08x (%d, %d) belongs to player %08x' % (portal_id, x, y, player_id)
148 object_removal
= packets
.object_removal(bytes
)
149 if object_removal
!= None:
150 type, id = object_removal
151 for player_id
in self
.town_portal_map
:
152 if self
.town_portal_map
[player_id
].id == id:
153 del self
.town_portal_map
[player_id
]
154 #print 'Removed portal %08x by player %08x' % (id, player_id)
159 del self
.monsters
[id]
160 #print 'Removed unit %08x' % id
164 add_unit
= packets
.parse_add_unit(bytes
)
166 unit_type
, unit_id
= add_unit
169 location
= self
.assignments
[unit_id
]
170 self
.monsters
[unit_id
] = location
171 #print 'Added unit %08x: %s' % (unit_id, repr(location))
173 #print 'Unit was added without prior assignment: %08x' % unit_id
176 npc_move
= packets
.parse_npc_move(bytes
)
178 unit_id
, running
, x
, y
= npc_move
179 if unit_id
in self
.monsters
:
181 if unit_id
in self
.monsters
:
182 self
.monsters
[unit_id
] = target
183 #print 'Monster %08x is moving to %s' % (unit_id, repr(target))
185 npc_assignment
= packets
.parse_npc_assignment(bytes
)
186 if npc_assignment
!= None:
187 unit_id
, unit_code
, x
, y
, life
= npc_assignment
188 if unit_code
in npc
.npcs
:
189 #print 'Detected an NPC: %08x' % unit_id
193 self
.assignments
[unit_id
] = location
194 #print 'Assignment: %08x -> %s' % (unit_id, repr(location))
196 def get_targets(self
):
199 for unit_id
in self
.monsters
:
200 monster
= craw
.get_unit(unit_id
, 1)
201 #missing town NPC check?
203 print 'Failed to receive monster data for ID %08x' % unit_id
207 x
, y
= self
.monsters
[unit_id
]
211 if monster
.mode
in [0, 12]:
213 self
.attack_print('Dead monster: %08x' % unit_id
)
216 distance
= utility
.get_d2_distance(self
.i
, monster
)
217 if distance
> configuration
.follower_attack_radius
:
218 self
.attack_print('Distance to %08x too great: %.1f (%s)' % (monster
.id, distance
, repr((monster
.x
, monster
.y
))))
220 monsters
.append((distance
, unit_id
))
222 monsters
.sort(cmp = utility
.sort_units
)
226 def attack_print(self
, text
):
227 #craw.print_text(text)
230 def attack_thread(self
):
231 self
.attack_print('Attack thread has been launched')
232 while self
.attacking
:
233 self
.i
= utility
.get_my_player()
237 my_unit
= utility
.get_my_unit()
239 self
.attack_print('Unable to retrieve unit')
242 standing_still
= my_unit
.mode
== 1
243 if standing_still
and not utility
.town_check():
244 targets
= self
.get_targets()
245 if len(targets
) == 0:
246 self
.attack_print('No targets in range')
249 self
.attack_print('Targets in range:')
250 for distance
, id in targets
:
251 self
.attack_print('%08x: %.1f' % (id, distance
))
253 if targets
[0][0] <= configuration
.follower_defence_radius
:
254 #a monster got too close, kill it, quickly!
255 target
= targets
[0][1]
256 self
.attack_print('Dangerous close proximity monster detected')
258 index
= random
.randint(0, min(len(targets
), configuration
.follower_attack_maximal_randomisation_position
) - 1)
259 target
= targets
[index
][1]
261 self
.attack_print('Attacking target %08x' % target
)
262 packets
.cast_left_skill_at_target(1, target
)
264 self
.attack_print('Not standing still (player mode is %d)' % my_unit
.mode
)
267 time
.sleep(configuration
.follower_attack_delay
)