CRAW now runs on Windows 7 too - the problem was that Windows 7 has moved some functi...
[craw.git] / script / follow.py
blob233cbbca66e09814c91d89c774cd62d0922df673
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):
5 self.id = object_id
6 self.location = (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
12 self.start()
14 def run(self):
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
28 self.initialise()
30 def initialise(self):
31 self.following = False
32 self.town_portal_map = {}
33 self.current_portal = None
34 self.attacking = False
35 self.monsters = {}
36 self.assignments = {}
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:
45 return
47 if player == None:
48 print 'Unable to retrieve player data of player %s' % player
49 return
51 #print 'Processing <%s> "%s"' % (name, message)
53 if self.command_match(message, configuration.follow_command):
54 self.following = True
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):
61 if self.following:
62 self.following = False
63 print 'No longer following %s' % self.leader
64 packets.send_chat(configuration.stop_confirmation % self.leader)
65 else:
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():
70 self.enter_tp(player)
72 elif self.command_match(message, configuration.enter_town_tp_command) and utility.town_check():
73 self.enter_tp(player)
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):
80 if self.attacking:
81 packets.send_chat(configuration.attack_error)
82 else:
83 packets.send_chat(configuration.attack_confirmation)
84 self.attacking = True
85 nil.thread.create_thread(self.attack_thread)
87 elif self.command_match(message, configuration.stop_attacking_command):
88 if self.attacking:
89 packets.send_chat(configuration.stop_attacking_confirmation)
90 self.attacking = False
91 else:
92 packets.send_chat(configuration.stop_attacking_error)
94 def enter_tp(self, player):
95 try:
96 tp_entry = self.town_portal_map[player.id]
97 except KeyError:
98 packets.send_chat(configuration.tp_error % player.name)
99 return
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):
106 self.initialise()
108 message = packets.parse_message(bytes)
109 if message != None:
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:
116 return
118 if name != my_name and self.command_match(message, configuration.leave_command):
119 print '%s ordered us to leave the game' % name
120 craw.leave_game()
122 if my_name != None and my_name in configuration.followers:
123 self.process_command(name, message)
125 move = packets.parse_move(bytes)
126 if move != None:
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'
143 else:
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)
155 break
157 try:
158 if type == 1:
159 del self.monsters[id]
160 #print 'Removed unit %08x' % id
161 except KeyError:
162 pass
164 add_unit = packets.parse_add_unit(bytes)
165 if add_unit != None:
166 unit_type, unit_id = add_unit
167 if unit_type == 1:
168 try:
169 location = self.assignments[unit_id]
170 self.monsters[unit_id] = location
171 #print 'Added unit %08x: %s' % (unit_id, repr(location))
172 except KeyError:
173 #print 'Unit was added without prior assignment: %08x' % unit_id
174 pass
176 npc_move = packets.parse_npc_move(bytes)
177 if npc_move != None:
178 unit_id, running, x, y = npc_move
179 if unit_id in self.monsters:
180 target = (x, y)
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
190 pass
191 else:
192 location = (x, y)
193 self.assignments[unit_id] = location
194 #print 'Assignment: %08x -> %s' % (unit_id, repr(location))
196 def get_targets(self):
197 monsters = []
199 for unit_id in self.monsters:
200 monster = craw.get_unit(unit_id, 1)
201 #missing town NPC check?
202 if monster == None:
203 print 'Failed to receive monster data for ID %08x' % unit_id
204 continue
206 #location hack
207 x, y = self.monsters[unit_id]
208 monster.x = x
209 monster.y = y
211 if monster.mode in [0, 12]:
212 #monster is dead
213 self.attack_print('Dead monster: %08x' % unit_id)
214 continue
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))))
219 continue
220 monsters.append((distance, unit_id))
222 monsters.sort(cmp = utility.sort_units)
224 return monsters
226 def attack_print(self, text):
227 #craw.print_text(text)
228 pass
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()
234 if self.i == None:
235 return
237 my_unit = utility.get_my_unit()
238 if my_unit == None:
239 self.attack_print('Unable to retrieve unit')
240 return
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')
247 pass
248 else:
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')
257 else:
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)
263 else:
264 self.attack_print('Not standing still (player mode is %d)' % my_unit.mode)
265 pass
267 time.sleep(configuration.follower_attack_delay)