fixed chicken messages
[snoogans.git] / maphack.c
blob99792f6d9264604aaa53cc639b0ef4ba386fd08b
1 /*
2 * Copyright (C) 2010 gonzoj
4 * Please check the CREDITS file for further information.
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 #include <pthread.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <unistd.h>
26 #include "d2pointers.h"
27 #include "d2structs.h"
28 #include "kernel32.h"
29 #include "misc.h"
30 #include "patch.h"
31 #include "types.h"
32 #include "user32.h"
34 #include "debug.h"
36 #define CLEAR(a, n, t) memset(a, 0, n * sizeof(t))
38 typedef struct level_info
40 int act_no;
41 DWORD level_id;
42 int x;
43 int y;
44 } level_info;
46 #define PRINT(info) printf("level_info.act_no: %i\n", info.act_no); printf("level_info.level_id: %i\n", info.level_id); printf("level_info.x: %i\n", info.x); printf("level_info.y: %i\n", info.y);
48 int n_info_level = 0;
49 level_info *info_level = NULL;
51 #define INIT(info) info = NULL; n_##info = 0;
52 #define ADD(type, info, i) info = realloc(info, ++n_##info * sizeof(type)); memcpy(info + n_##info - 1, &i, sizeof(type));
53 #define REMOVE_ALL(info) if (info) free(info); info = NULL; n_##info = 0;
54 #define SIZE(info) n_##info
55 #define ELEMENT(info, i) info[i]
57 pthread_t mh_thread;
59 int area_local_id[] =
60 { 0, 5055, 5054, 5053, 5052, 5051, 5050, 5049, 3714, 5047, 5046, 5045, 5044,
61 5043, 5042, 5041, 5040, 5039, 5038, 5037, 5036, 5035, 5034, 5033, 5032,
62 5031, 5030, 5029, 5028, 5027, 5026, 5025, 5024, 5023, 5022, 5021, 5020,
63 5019, 5018, 788, 852, 851, 850, 849, 848, 847, 846, 845, 844, 843, 842,
64 841, 840, 839, 838, 837, 836, 835, 834, 833, 832, 831, 830, 829, 828,
65 827, 826, 826, 826, 826, 826, 826, 826, 825, 824, 820, 819, 818, 817,
66 816, 815, 814, 813, 812, 810, 811, 809, 808, 806, 805, 807, 804, 845,
67 844, 803, 802, 801, 800, 799, 798, 797, 796, 795, 790, 792, 793, 794,
68 791, 789, 22646, 22647, 22648, 22649, 22650, 22651, 22652, 22653, 22654,
69 22655, 22656, 22657, 22660, 21865, 21866, 21867, 22658, 22659, 22662,
70 22663, 22664, 22665, 22667, 22666, 11155, 11156, 11157, 5018 };
72 int town_levels[] =
73 { 1, 40, 75, 103, 109, 137 };
75 int revealed_act[5];
77 int loaded = 0;
79 int reset = 1;
81 void
82 draw_presets(room2 *room2)
84 preset_unit *unit;
85 for (unit = room2->preset; unit != NULL; unit = unit->next)
87 int cell_no = -1;
88 switch (unit->type)
90 case 1:
92 switch (unit->txt_file_no)
94 case 256:
96 cell_no = 300;
97 break;
99 case 745:
101 cell_no = 745;
102 break;
106 case 2:
108 switch (unit->txt_file_no)
110 case 580:
112 if (room2->level->level_no == 79)
114 cell_no = 318;
116 break;
118 case 371:
120 if (room2->level->level_no == 25)
122 cell_no = 301;
124 break;
126 case 152:
128 cell_no = 300;
129 break;
131 case 460:
133 cell_no = 1468;
134 break;
136 case 402:
138 if (room2->level->level_no == 46)
140 cell_no = 0;
142 break;
144 case 267:
146 if (room2->level->level_no != 75 && room2->level->level_no != 103)
148 cell_no = 0;
150 break;
152 case 367:
154 if (room2->level->level_no == 107)
156 cell_no = 376;
158 break;
161 if (cell_no == -1)
163 if (unit->txt_file_no > 572)
165 cell_no = 0;
167 else
169 object_txt *obj = D2COMMON_get_object_txt(unit->txt_file_no);
170 if (obj->automap == 310)
172 cell_no = obj->subclass & 1 ? 310 : 0;
174 else
176 cell_no = obj->automap;
180 break;
182 case 5:
184 level_info info =
185 { 0 };
186 room_tile *tile;
187 if (room2->room_tiles == NULL)
189 break;
191 for (tile = room2->room_tiles; tile != NULL; tile = tile->next)
193 if (*(tile->num) == unit->txt_file_no)
195 info.level_id = tile->room2->level->level_no;
196 break;
199 if (info.level_id == 0)
201 break;
203 unit_any *hero = D2CLIENT_get_player_unit();
204 info.act_no = hero->act_no;
205 info.x = unit->x + (room2->x * 5) - (8 << 1);
206 info.y = unit->y + (room2->y * 5) - 10;
207 DEBUG_DO(printf("add level info #%i\n", n_info_level);)
208 DEBUG_DO(PRINT(info);)
209 ADD(level_info, info_level, info)
210 break;
213 if (cell_no > 0)
215 automap_cell *cell = D2CLIENT_new_automap_cell();
216 cell->cell_no = cell_no;
217 int x = unit->x + room2->x * 5;
218 int y = unit->y + room2->y * 5;
219 cell->pixel_x = ((x - y) * 16) / 10 + 1;
220 cell->pixel_y = ((x + y) * 8) / 10 - 3;
221 D2CLIENT_add_automap_cell(cell,
222 &((*p_D2CLIENT_automap_layer)->objects));
228 reveal_level(level *level)
230 DEBUG_DO(printf("init automap layer...\n");)
231 if (!init_automap_layer(level->level_no))
233 printf("err: could not init automap layer for level %i\n",
234 level->level_no);
235 return 0;
237 DEBUG_DO(printf("automap_layer: 0x%08X\n", (vaddr) *p_D2CLIENT_automap_layer);)
238 DEBUG_DO(printf("get player struct...\n");)
239 unit_any *unit = D2CLIENT_get_player_unit();
240 if (unit == NULL)
242 printf("err: could not get player unit\n");
243 return 0;
245 DEBUG_DO(printf("unit: 0x%08X\n", (vaddr) unit);)
246 room2 *room2;
247 for (room2 = level->room2_first; room2 != NULL; room2 = room2->next)
249 DEBUG_DO(printf("room2: 0x%08X\n", (vaddr) room2);)
250 int remove = 0;
251 if (room2->room1 == NULL)
253 DEBUG_DO(printf("add room data...\n");)
254 D2COMMON_add_room_data(level->misc->act, level->level_no, room2->x,
255 room2->y, unit->path->room1);
256 if (room2->room1 == NULL)
258 printf("err: could not add room data\n");
259 continue;
261 remove = 1;
263 DEBUG_DO(printf("room1: 0x%08X\n", (vaddr) room2->room1);)
264 DEBUG_DO(printf("reveal room...\n");)
265 D2CLIENT_reveal_automap_room(room2->room1, 1, *p_D2CLIENT_automap_layer);
266 DEBUG_DO(printf("draw presets...\n");)
267 draw_presets(room2);
268 if (remove)
270 DEBUG_DO(printf("remove room data...\n");)
271 D2COMMON_remove_room_data(level->misc->act, level->level_no,
272 room2->x, room2->y, unit->path->room1);
275 DEBUG_DO(printf("init automap layer back...\n");)
276 init_automap_layer(unit->path->room1->room2->level->level_no);
277 return 1;
281 reveal_act()
283 DEBUG_DO(printf("get player unit...\n");)
284 unit_any *unit = D2CLIENT_get_player_unit();
285 DEBUG_DO(printf("unit: 0x%08X\n", (vaddr) unit);)
286 if (unit == NULL)
288 return 0;
289 printf("err: could not get player unit\n");
291 int n_level;
292 for (n_level = town_levels[unit->act_no] + 1; n_level
293 < town_levels[unit->act_no + 1]; n_level++)
295 if (n_level == 132)
297 continue;
299 DEBUG_DO(printf("get level struct...\n");)
300 level *level = get_level(unit->act->misc, n_level);
301 DEBUG_DO(printf("level: 0x%08X\n", (vaddr) level);)
302 if (level == NULL)
304 printf("err: could not get a valid level struct\n");
305 continue;
307 if (level->room2_first == NULL)
309 DEBUG_DO(printf("try to init level...\n");)
310 D2COMMON_init_level(level);
311 if (level->room2_first == NULL)
313 printf("err: could not init level %i\n", n_level);
314 continue;
317 DEBUG_DO(printf("reveal level %i...\n", n_level);)
318 if (!reveal_level(level))
320 printf("err: could not reveal level %i\n", n_level);
323 DEBUG_DO(printf("revealed all levels\n");)
324 DEBUG_DO(printf("revealed act %i\n", unit->act_no + 1);)
325 return 1;
328 void
329 reveal_automap()
331 unit_any *hero = *p_D2CLIENT_player_unit;
332 if (!hero)
334 return;
336 if (!revealed_act[hero->act_no])
338 manage_threads(SuspendThread);
339 if (reveal_act())
341 revealed_act[hero->act_no] = 1;
342 print_ingame(D2FONT_GOLD, "[snoogans] %s%s %i", FONT_COLOR(0),
343 "revealed act", hero->act_no + 1);
345 else
347 print_ingame(D2FONT_GOLD, "[snoogans] %s%s %i", FONT_COLOR(1),
348 "failed to reveal act", hero->act_no + 1);
350 manage_threads(ResumeThread);
354 void
355 draw_monsters()
357 unit_any *player = D2CLIENT_get_player_unit();
358 if (player == NULL)
360 printf("err: could not get player struct\n");
361 return;
363 int level_no = player->path->room1->room2->level->level_no;
364 if (level_no == 1 || level_no == 40 || level_no == 75 || level_no == 103
365 || level_no == 109)
367 return;
369 room1 *room1;
370 for (room1 = player->act->room1; room1 != NULL; room1 = room1->next)
372 unit_any *unit;
373 for (unit = room1->unit_first; unit != NULL; unit = unit->list_next)
375 if (!valid_monster(unit))
377 continue;
379 if (unit->type == 1)
381 if (unit->monster_data->boss & 1 && !(unit->monster_data->champ
382 & 1))
384 draw_cross(unit->path->x, unit->path->y, D2GFX_WHITE, 1);
385 if (unit->monster_data->boss & 1
386 && !(unit->monster_data->champ & 1))
388 ms_wchar_t *wname = D2CLIENT_get_unit_name(unit);
389 if (wname)
391 char name[512];
392 ms_wchar_to_char(wname, name);
393 draw_text(unit->path->x - 6, unit->path->y - 6,
394 D2FONT_GOLD, 1, D2FONT_SMALL, 1, "%s", name);
398 /* check immunities */
400 /* physical */
401 else if (D2COMMON_get_unit_stat(unit, 36, 0) >= 100)
403 draw_cross(unit->path->x, unit->path->y, D2GFX_GOLD, 1);
405 /* cold */
406 else if (D2COMMON_get_unit_stat(unit, 43, 0) >= 100)
408 draw_cross(unit->path->x, unit->path->y, D2GFX_BLUE, 1);
410 /* fire */
411 else if (D2COMMON_get_unit_stat(unit, 39, 0) >= 100)
413 draw_cross(unit->path->x, unit->path->y, D2GFX_RED, 1);
415 /* light */
416 else if (D2COMMON_get_unit_stat(unit, 41, 0) >= 100)
418 draw_cross(unit->path->x, unit->path->y, D2GFX_YELLOW, 1);
420 /* poison */
421 else if (D2COMMON_get_unit_stat(unit, 45, 0) >= 100)
423 draw_cross(unit->path->x, unit->path->y, D2GFX_GREEN, 1);
425 /* magic */
426 else if (D2COMMON_get_unit_stat(unit, 37, 0) >= 100)
428 draw_cross(unit->path->x, unit->path->y, D2GFX_ORANGE, 1);
430 else
432 draw_cross(unit->path->x, unit->path->y, D2GFX_WHITE, 1);
435 else if (unit->type == 3)
437 int missile_color = (player->id == unit->owner_id) ? D2GFX_GREEN
438 : D2GFX_RED;
439 draw_box(unit->path->x - 3, unit->path->y - 3, 3, 3,
440 missile_color, 1, 5);
446 void
447 draw_automap_text()
449 unit_any *player = D2CLIENT_get_player_unit();
450 int i;
451 for (i = 0; i < SIZE(info_level); i++)
453 level_info info = ELEMENT(info_level, i);
454 if (info.act_no != player->act_no)
456 continue;
458 ms_wchar_t *local_text = D2LANG_get_local_text(
459 area_local_id[info.level_id]);
460 if (local_text)
462 int font = D2FONT_WHITE;
463 char level_name[512];
464 ms_wchar_to_char(local_text, level_name);
465 if (info.level_id == player->act->misc->staff_tomb_level)
467 font = D2FONT_GREEN;
469 draw_text(info.x, info.y, font, 1, D2FONT_SMALL, 1, "%s", level_name);
474 void chicken()
476 static int hp_warning = 1;
477 static int ping_warning = 1;
479 if (!intown())
481 if (get_hp_percent() < 25) /* hardcoded :( */
483 print_ingame(D2FONT_GOLD, "[snoogans] %schicken exit (%i%)", FONT_COLOR(1), 25);
484 D2CLIENT_exit_game();
485 return;
487 else if (get_hp_percent() < 33 && hp_warning) /* hardcoded :( */
489 print_ingame(D2FONT_GOLD, "[snoogans] %scritical health points (%i%)", FONT_COLOR(1), 33);
490 hp_warning = 0;
492 else if (get_hp_percent() >= 33 && !hp_warning)
494 hp_warning = 1;
496 if (*p_D2CLIENT_ping > 1000) /* hardcoded :( */
498 print_ingame(D2FONT_GOLD, "[snoogans] %schicken exit (%i ms)", FONT_COLOR(1), 1000);
499 D2CLIENT_exit_game();
500 return;
502 else if (*p_D2CLIENT_ping > 300 && ping_warning) /* hardcoded :( */
504 print_ingame(D2FONT_GOLD, "[snoogans] %scritical ping (%i ms)", FONT_COLOR(1), 300);
505 ping_warning = 0;
507 else if (*p_D2CLIENT_ping <= 300 && !ping_warning)
509 ping_warning = 1;
514 void *
515 maphack(void *arg)
517 loaded = 1;
518 if (ingame())
520 print_ingame(D2FONT_GOLD, "[snoogans] %s%s", FONT_COLOR(0),
521 "loaded snoogans.so v 0.4");
523 CLEAR(revealed_act, 5, int);
524 while (loaded)
526 usleep(100000);
527 if (!ingame() && *p_D2WIN_first_control)
529 CLEAR(revealed_act, 5, int);
530 REMOVE_ALL(info_level)
531 reset = 1;
533 else if (ingame())
535 chicken(); /* chicken feature, maybe better included in the ingame hook */
536 if (!D2CLIENT_get_ui_var(0x01))
538 viewing_unit = NULL;
540 if (viewing_unit && viewing_unit->id)
542 if (!viewing_unit->inventory)
544 viewing_unit = NULL;
545 D2CLIENT_set_ui_var(0x01, 1, 0);
547 else if (!D2CLIENT_find_server_side_unit(viewing_unit->id,
548 viewing_unit->type))
550 viewing_unit = NULL;
551 D2CLIENT_set_ui_var(0x01, 1, 0);
553 else if (viewing_unit->mode == 0 || viewing_unit->mode == 17)
555 viewing_unit = NULL;
556 D2CLIENT_set_ui_var(0x01, 1, 0);
559 unit_any *unit = D2CLIENT_get_selected_unit();
560 if (unit && unit->inventory && unit->mode != 0 && unit->mode != 17
561 && unit->type == 0)
563 if (!D2CLIENT_get_ui_var(0x01))
565 viewing_unit = unit;
568 game_info *info = D2CLIENT_get_game_info();
569 if (info && reset)
571 strcpy(last_game_name, info->game_name);
572 reset = 0;
576 if (ingame())
578 print_ingame(D2FONT_GOLD, "[snoogans] %s%s", FONT_COLOR(0), "unloaded");
580 REMOVE_ALL(info_level)
581 return NULL;
584 void
585 stop_maphack()
587 if (!loaded)
589 return;
591 loaded = 0;
592 pthread_join(mh_thread, NULL);