#include "main.h"

/* the actual menu rendering is done in proxyIDirect3DDssdgkasdg.......cpp */

/* XXX check NULL alloc, free memory, etc */

#define ID_MENU_MAIN                0
#define ID_MENU_CHEATS              1
#define ID_MENU_CHEATS_INVULN       1
#define ID_MENU_CHEATS_MONEY        2
#define ID_MENU_CHEATS_SPOOF_WEAPON 3
#define ID_MENU_CHEATS_FAKE_KILL    4
#define ID_MENU_CHEATS_WEATHER      5
#define ID_MENU_CHEATS_TIME         6
#define ID_MENU_PATCHES             7
#define ID_MENU_WEAPONS             8
#define ID_MENU_VEHICLES            9
#define ID_MENU_VEHICLES_SUB        10
#define ID_MENU_PLAYERS             11
#define ID_MENU_TELEPORTS           12
#define ID_MENU_MISCELLANEOUS       13
#define ID_MENU_DEBUG               14

#define ID_NONE                  -1

#define ID_CHEAT_INVULN          0
#define ID_CHEAT_WEAPONS         1
#define ID_CHEAT_MONEY           2
#define ID_CHEAT_PROT            3
#define ID_CHEAT_WARP_NEAR       4
#define ID_CHEAT_SPOOF_WEAPON    5
#define ID_CHEAT_FAKE_KILL       6
#define ID_CHEAT_JETPACK         8
#define ID_CHEAT_UNLOCK          9
#define ID_CHEAT_WEATHER         10
#define ID_CHEAT_TIME            11
#define ID_CHEAT_HAVEN           12
#define ID_CHEAT_HP              13
#define ID_CHEAT_ARMOR           14
#define ID_CHEAT_GRAVITY         15
#define ID_CHEAT_MAP             16
#define ID_CHEAT_GAME_SPEED      17

#define ID_CHEAT_INVULN_ACTOR    0
#define ID_CHEAT_INVULN_VEHICLE  1
#define ID_CHEAT_INVULN_TIRES    5
#define ID_CHEAT_INVULN_MIN_HP   2
#define ID_CHEAT_INVULN_DMGRED   3
#define ID_CHEAT_INVULN_REGEN    4

#define ID_CHEAT_MONEY_GIVE_500     0
#define ID_CHEAT_MONEY_GIVE_1000    1
#define ID_CHEAT_MONEY_GIVE_5000    2
#define ID_CHEAT_MONEY_GIVE_10000   3
#define ID_CHEAT_MONEY_GIVE_20000   4
#define ID_CHEAT_MONEY_MAX          5
#define ID_CHEAT_MONEY_RAND_MIN     6
#define ID_CHEAT_MONEY_RAND_MAX     7
#define ID_CHEAT_MONEY_IVAL_MIN     8
#define ID_CHEAT_MONEY_IVAL_MAX     9

#define ID_WEAPON_ENABLE         0
#define ID_WEAPON_ITEM           1

#define ID_VEHICLES_IWARP        0
#define ID_VEHICLES_FREEZE       1

#define ID_MISC_COORDS           0
#define ID_MISC_RELOAD           1
#define ID_MISC_HUDTEXT          2
#define ID_MISC_BAD_WEAPONS      3
#define ID_MISC_CHAT_TEXT        4
#define ID_MISC_KILL_TEXT        5
#define ID_MISC_NAME_TAG         6
#define ID_MISC_TRAILERS         7

#define ID_DEBUG_ENABLE             0
#define ID_DEBUG_SELF_ACTOR         1
#define ID_DEBUG_SELF_VEHICLE       2
#define ID_DEBUG_CAMERA             3
#define ID_DEBUG_VEHICLE_CONTACT    4
#define ID_DEBUG_BUILDING_CONTACT   5
#define ID_DEBUG_NEAREST_ACTOR      6
#define ID_DEBUG_NEAREST_VEHICLE    7
#define ID_DEBUG_VEHICLE_POOL       8
#define ID_DEBUG_SAMP_PLAYER_LIST   9
#define ID_DEBUG_GRAVITY            10
#define ID_DEBUG_TIMER_SPEED        11
#define ID_DEBUG_GAME_SPEED         12
#define ID_DEBUG_PARACHUTE          13

struct menu *menu_active = NULL;
static int menu_init;


static struct menu *menu_new(struct menu *parent, int id, menu_callback callback)
{
   struct menu *menu = (struct menu *)calloc(1, sizeof(struct menu));

   if(menu == NULL)
      return NULL;

   menu->id = id;
   menu->parent = parent;
   menu->callback = callback;

   return menu;
}


static struct menu_item *menu_item_add(struct menu *menu, struct menu *submenu, const char *name, int id, D3DCOLOR color, void *data)
{
   struct menu_item *item;
   int i;

   i = menu->count++;
   menu->item = (struct menu_item *)realloc(menu->item, sizeof(struct menu_item) * menu->count);
   item = &menu->item[i];

   memset(item, 0, sizeof(menu_item));
   item->submenu = submenu;
   item->menu = menu;
   item->name = _strdup(name);
   item->id = id;
   item->color = color;
   item->data = data;

   return item;
}


static void menu_item_name_set(struct menu_item *item, const char *fmt, ...)
{
   const char *name_new;
   char name[64];
   va_list ap;

   va_start(ap, fmt);
   vsnprintf(name, sizeof(name), fmt, ap);
   va_end(ap);

   if((name_new = _strdup(name)) != NULL)
   {
      free((void *)item->name);
      item->name = name_new;
   }
}


static void menu_cheats_spoof_kill_populate(struct menu *menu)
{
   struct samp_player_list *spl = samp_player_list_get();
   char text[64];
   int i;

   menu_items_free(menu);

   if(spl == NULL)
      return;

   for(i=0; i<SAMP_PLAYER_MAX; i++)
   {
      D3DCOLOR color = MENU_COLOR_DEFAULT;

      if(spl->alloc_map[i] != 1)
         continue;

      if(spl->player_info[i] != NULL)
         color = samp_color_get(spl->player_info[i]->color_id);

      snprintf(text, sizeof(text), "%d. Fake killed by %s", i, spl->name[i]);

      menu_item_add(menu, NULL, text, ID_CHEAT_FAKE_KILL, color, (void *)(UINT_PTR)i);
   }
}


static void menu_vehicles_populate(struct menu *menu, int class_id)
{
   const struct actor_info *actor_self = actor_info_get(ACTOR_SELF, 0);
   char name[64];
   int i;

   menu_items_free(menu);

   if(pool_vehicle != NULL)
   {
      for(i=0; i<pool_vehicle->size; i++)
      {
         static const struct vehicle_entry unknown = { 0, VEHICLE_CLASS_CAR, "[Unknown vehicle]" };
         const struct vehicle_info *info = vehicle_info_get(i, VEHICLE_ALIVE);
         const struct vehicle_entry *vehicle;
         float dist[3] = { 0.0f, 0.0f, 0.0f };

         if(info == NULL)
            continue;

         vehicle = gta_vehicle_get_by_id(info->base.model_alt_id);
         if(vehicle == NULL)
            vehicle = &unknown;

         if(vehicle->class_id != class_id)
            continue;

         if(actor_self != NULL)
            vect3_vect3_sub(&info->base.matrix[4*3], &actor_self->base.matrix[4*3], dist);

         snprintf(name, sizeof(name), "%s (%.2fm%s)",
               vehicle->name,
               vect3_length(dist),
               (info->passengers[0] == NULL) ? "" : ", Occupied"); /* XXX doesn't work :p */

         /* XXX maybe use color of car ... */
         menu_item_add(menu, NULL, name, i, MENU_COLOR_DEFAULT, NULL);
      }
   }
}


static void menu_players_populate(struct menu *menu)
{
   struct samp_player_list *spl = samp_player_list_get();
   char text[64];
   int i;

   menu_items_free(menu);

   if(spl == NULL)
      return;

   for(i=0; i<SAMP_PLAYER_MAX; i++)
   {
      D3DCOLOR color = MENU_COLOR_DEFAULT;

      if(spl->alloc_map[i] != 1)
         continue;

      if(spl->player_info[i] != NULL)
         color = samp_color_get(spl->player_info[i]->color_id);

      snprintf(text, sizeof(text), "%d. %s", i, spl->name[i]);

      menu_item_add(menu, NULL, text, i, color, NULL);
   }
}


/* called when a menu is going to be displayed
   (eg. when you hit f9, or go into a submenu) */
static void menu_event_activate(struct menu *menu)
{
   if(menu == NULL)
      return;

   switch(menu->id)
   {
   case ID_MENU_VEHICLES_SUB:
      menu_vehicles_populate(menu, (int)(UINT_PTR)menu->parent->item[menu->parent->pos].data);
      break;
   case ID_MENU_PLAYERS:
      menu_players_populate(menu);
      break;
   case ID_MENU_CHEATS_FAKE_KILL:
      menu_cheats_spoof_kill_populate(menu);
      break;
   }
}


/* run every frame */
void menu_run(void)
{
   struct menu_item *item;
   static uint32_t key_time;


   if(menu_active == NULL)
      return;

   if(KEY_PRESSED(set.key_menu))
   {
      cheat_state->generic.menu ^= 1;
      if(cheat_state->generic.menu)
         menu_event_activate(menu_active);
   }

   if(cheat_state->generic.menu)
   {
      if(KEY_PRESSED(set.key_menu_up) || KEY_PRESSED(set.key_menu_down))
      {
         menu_active->pos += KEY_PRESSED(set.key_menu_up) ? -1 : 1;
         key_time = time_get() + MSEC_TO_TIME(MENU_SCROLL_DELAY);
      }

      if(KEY_DOWN(set.key_menu_up) || KEY_DOWN(set.key_menu_down))
      {
         if(time_get() > key_time)
         {
            int diff = time_get() - key_time;
            int overruns = diff / MENU_SCROLL_TIME;
            int dir = KEY_DOWN(set.key_menu_up) ? -1 : 1;

            menu_active->pos += dir * (overruns + 1);

            key_time = time_get() + MENU_SCROLL_TIME - (diff % MENU_SCROLL_TIME);
         }
      }

      if(menu_active->pos < 0)
         menu_active->pos = menu_active->count - 1;
      if(menu_active->pos >= menu_active->count)
         menu_active->pos = 0;

      if(menu_active->count <= 0)
         item = NULL;
      else
         item = &menu_active->item[ menu_active->pos ];

      if(KEY_PRESSED(set.key_menu_left))
      {
         /* pressing left in the main menu, exits the menu */
         if(menu_active->parent == NULL)
         {
            cheat_state->generic.menu ^= 1;
            return;
         }
         menu_active = menu_active->parent;
         menu_event_activate(menu_active);
      }

      if(KEY_PRESSED(set.key_menu_right) || KEY_PRESSED(set.key_menu_select))
      {
         int was_selected = 0;

         if(KEY_PRESSED(set.key_menu_select) && menu_active->callback != NULL && item != NULL)
         {
            if(menu_active->callback(MENU_OP_SELECT, item))
               was_selected = 1;
         }
         if(!was_selected && item != NULL && item->submenu != NULL)
         {
            menu_active = item->submenu;
            menu_event_activate(menu_active);
         }
      }

      if(KEY_PRESSED(set.key_menu_dec) || KEY_PRESSED(set.key_menu_inc))
      {
         int op = KEY_PRESSED(set.key_menu_dec) ? MENU_OP_DEC : MENU_OP_INC;

         if(item != NULL)
            menu_active->callback(op, item);
      }

      KEY_CONSUME(set.key_menu_up);
      KEY_CONSUME(set.key_menu_right);
      KEY_CONSUME(set.key_menu_down);
      KEY_CONSUME(set.key_menu_left);
      KEY_CONSUME(set.key_menu_select);
   }
}


static int menu_callback_main(int op, struct menu_item *)
{
   return 0;
}


static int menu_callback_cheats(int op, struct menu_item *item)
{
   struct actor_info *info;
   int mod;

   switch(op)
   {
   case MENU_OP_ENABLED:
      switch(item->id)
      {
      case ID_CHEAT_INVULN:        return cheat_state->generic.hp_cheat;
      case ID_CHEAT_WEAPONS:       return 0;
      case ID_CHEAT_MONEY:         return cheat_state->generic.money;
      case ID_CHEAT_PROT:          return cheat_state->vehicle.protection;
      case ID_CHEAT_WARP_NEAR:     return 0;
      case ID_CHEAT_SPOOF_WEAPON:  return (cheat_state->generic.spoof_weapon == (int)(UINT_PTR)item->data);
      case ID_CHEAT_FAKE_KILL:     return 0;
      case ID_CHEAT_JETPACK:       return 0;
      case ID_CHEAT_UNLOCK:        return cheat_state->generic.vehicles_unlock;
      case ID_CHEAT_WEATHER:       return (set.force_weather == (int)(UINT_PTR)item->data);
      case ID_CHEAT_TIME:          return (set.force_hour    == (int)(UINT_PTR)item->data);
      case ID_CHEAT_HAVEN:         return cheat_state->generic.hp_safe_haven;
      case ID_CHEAT_HP:            return 0;
      case ID_CHEAT_ARMOR:         return 0;
      case ID_CHEAT_GRAVITY:       return 0;
      case ID_CHEAT_MAP:           return cheat_state->generic.map;
      case ID_CHEAT_GAME_SPEED:    return 0;
      }
      break;

   case MENU_OP_SELECT:
      switch(item->id)
      {
      case ID_CHEAT_INVULN:
         cheat_state->generic.hp_cheat ^= 1;
         break;

      case ID_CHEAT_MONEY:
         cheat_state->generic.money ^= 1;
         break;

      case ID_CHEAT_PROT:
         cheat_state->vehicle.protection ^= 1;
         break;

      case ID_CHEAT_WARP_NEAR:
         cheat_teleport_nearest_car();
         break;

      case ID_CHEAT_SPOOF_WEAPON:
         if(cheat_state->generic.spoof_weapon == (int)(UINT_PTR)item->data)
            cheat_state->generic.spoof_weapon = -1;
         else
            cheat_state->generic.spoof_weapon = (int)(UINT_PTR)item->data;
         break;

      case ID_CHEAT_FAKE_KILL:
         {
            struct actor_info *info = samp_player_actor_info_get((int)(UINT_PTR)item->data);
            struct actor_info *self = actor_info_get(ACTOR_SELF, ACTOR_ALIVE);
            const char *errmsg = NULL;

            if(info == NULL)
               errmsg = "Player does not exists.";
            else if(ACTOR_IS_DEAD(info))
               errmsg = "The player is dead.";
            if(self == NULL)
               errmsg = "You are dead.";

            if(errmsg == NULL)
            {
               self->hitpoints = 0.0f;
               self->weapon_hit_by = info;
               if(cheat_state->generic.spoof_weapon == -1)
               {
                  self->weapon_hit_type = info->weapon[info->weapon_slot].id;
               }
            }
            else
            {
               cheat_state_text(errmsg);
            }
         }
         break;

      case ID_CHEAT_JETPACK:
         gta_jetpack_give();
         break;

      case ID_CHEAT_UNLOCK:
         cheat_state->generic.vehicles_unlock ^= 1;
         break;

      case ID_CHEAT_WEATHER:
         if(set.force_weather == (int)(UINT_PTR)item->data)
            set.force_weather = -1;
         else
            set.force_weather = (int)(UINT_PTR)item->data;
         break;

      case ID_CHEAT_TIME:
         if(set.force_hour == (int)(UINT_PTR)item->data)
            set.force_hour = -1;
         else
            set.force_hour = (int)(UINT_PTR)item->data;
         break;

      case ID_CHEAT_HAVEN:
         cheat_state->generic.hp_safe_haven ^= 1;
         break;

      case ID_CHEAT_MAP:
         cheat_state->generic.map ^= 1;
         break;

      case ID_CHEAT_HP:
      case ID_CHEAT_ARMOR:
         if((info = actor_info_get(ACTOR_SELF, ACTOR_ALIVE)) != NULL)
         {
            if(item->id == ID_CHEAT_HP)
               info->hitpoints = 100.0f;
            else
               info->armor = 100.0f;
         }
         break;
      default:
         return 0;
      }
      return 1;

   case MENU_OP_DEC:
   case MENU_OP_INC:
      mod = (op == MENU_OP_DEC) ? - 1: 1;
      switch(item->id)
      {
      case ID_CHEAT_GRAVITY:
         /* XXX floating point precision */
         if(mod > 0 && gta_gravity_get() < 0.1f)
            gta_gravity_set(gta_gravity_get() + 0.001f);
         if(mod < 0 && gta_gravity_get() > -0.1f)
            gta_gravity_set(gta_gravity_get() - 0.001f);
         menu_item_name_set(item, "Gravity: %.04f", gta_gravity_get());
         return 1;

      case ID_CHEAT_GAME_SPEED:
         /* XXX floating point precision */
         cheat_state->game_speed += (float)mod * 0.05f;
         menu_item_name_set(item, "Game speed: %d%%", (int)roundf(cheat_state->game_speed * 100.0f));
         return 1;
      }
   }

   return 0;
}


static int menu_callback_cheats_invuln(int op, struct menu_item *item)
{
   int mod;

   switch(op)
   {
   case MENU_OP_ENABLED:
      switch(item->id)
      {
      case ID_CHEAT_INVULN_ACTOR:   return cheat_state->actor.invulnerable;
      case ID_CHEAT_INVULN_VEHICLE: return cheat_state->vehicle.invulnerable;
      case ID_CHEAT_INVULN_TIRES:   return cheat_state->vehicle.hp_tire_support;
      case ID_CHEAT_INVULN_MIN_HP:  return cheat_state->vehicle.hp_minimum_on;
      case ID_CHEAT_INVULN_DMGRED:  return cheat_state->vehicle.hp_damage_reduce_on;
      case ID_CHEAT_INVULN_REGEN:   return cheat_state->vehicle.hp_regen_on;
      }
      break;

   case MENU_OP_SELECT:
      switch(item->id)
      {
      case ID_CHEAT_INVULN_ACTOR:   cheat_state->actor.invulnerable ^= 1;          break;
      case ID_CHEAT_INVULN_VEHICLE: cheat_state->vehicle.invulnerable ^= 1;        break;
      case ID_CHEAT_INVULN_TIRES:   cheat_state->vehicle.hp_tire_support ^= 1;     break;
      case ID_CHEAT_INVULN_MIN_HP:  cheat_state->vehicle.hp_minimum_on ^= 1;       break;
      case ID_CHEAT_INVULN_DMGRED:  cheat_state->vehicle.hp_damage_reduce_on ^= 1; break;
      case ID_CHEAT_INVULN_REGEN:   cheat_state->vehicle.hp_regen_on ^= 1;         break;
      default:
         return 0;
      }
      return 1;
      break;

   case MENU_OP_DEC:
   case MENU_OP_INC:
      mod = (op == MENU_OP_DEC) ? -1 : 1;
      switch(item->id)
      {
      case ID_CHEAT_INVULN_MIN_HP:
         if(set.hp_minimum > 1000.0f)
         {
            set.hp_minimum = (mod == 1) ? (set.hp_minimum * 2.0f) : (set.hp_minimum / 2.0f);
            if(set.hp_minimum < 1000.0f)
               set.hp_minimum = 1000.0f;
         }
         else if(set.hp_minimum > 250.0f)
         {
            set.hp_minimum += (float)mod * 50.0f;
         }
         else
         {
            set.hp_minimum += (float)mod * 10.0f;
         }

         if(set.hp_minimum <= 0.0f)
            set.hp_minimum = 0.0f;
         else if(set.hp_minimum > 1000000.0f)
            set.hp_minimum = 1000000.0f;
         else
            set.hp_minimum = roundf(set.hp_minimum);

         menu_item_name_set(item, "Vehicle minimum HP: %d", (int)set.hp_minimum);
         break;

      case ID_CHEAT_INVULN_DMGRED:
         set.hp_damage_reduce = roundf(set.hp_damage_reduce + mod * 5.0f);
         menu_item_name_set(item, "Vehicle damage reduction: %d%%", (int)set.hp_damage_reduce);
         break;

      case ID_CHEAT_INVULN_REGEN:
         set.hp_regen += (float)mod; 
         menu_item_name_set(item, "Vehicle HP regeneration: %dhp/sec", (int)set.hp_regen);
         break;
      }
      break;
   }

   return 0;
}


static int menu_callback_cheats_money(int op, struct menu_item *item)
{
   uint32_t money;
   int mod;

   switch(op)
   {
   case MENU_OP_SELECT:
      money = gta_money_get();
      switch(item->id)
      {
      case ID_CHEAT_MONEY_GIVE_500:    money += 500;   break;
      case ID_CHEAT_MONEY_GIVE_1000:   money += 1000;  break;
      case ID_CHEAT_MONEY_GIVE_5000:   money += 5000;  break;
      case ID_CHEAT_MONEY_GIVE_10000:  money += 10000; break;
      case ID_CHEAT_MONEY_GIVE_20000:  money += 20000; break;
      }
      gta_money_set(money);
      break;

   case MENU_OP_DEC:
   case MENU_OP_INC:
      mod = (op == MENU_OP_DEC) ? -1 : 1;
      switch(item->id)
      {
      case ID_CHEAT_MONEY_MAX:      set.money_amount_max += 50000 * mod; break;
      case ID_CHEAT_MONEY_RAND_MIN: set.money_amount_rand_min += 500 * mod; break;
      case ID_CHEAT_MONEY_RAND_MAX: set.money_amount_rand_max += 500 * mod; break;
      case ID_CHEAT_MONEY_IVAL_MIN: set.money_interval_rand_min += 1 * mod; break;
      case ID_CHEAT_MONEY_IVAL_MAX: set.money_interval_rand_max += 1 * mod; break;
      }
      switch(item->id)
      {
      case ID_CHEAT_MONEY_MAX:      menu_item_name_set(item, "Max money: %d", (int)set.money_amount_max);              break;
      case ID_CHEAT_MONEY_RAND_MIN: menu_item_name_set(item, "Random min: %d", (int)set.money_amount_rand_min);        break;
      case ID_CHEAT_MONEY_RAND_MAX: menu_item_name_set(item, "Random max: %d", (int)set.money_amount_rand_max);        break;
      case ID_CHEAT_MONEY_IVAL_MIN: menu_item_name_set(item, "Interval min: %dsec", (int)set.money_interval_rand_min); break;
      case ID_CHEAT_MONEY_IVAL_MAX: menu_item_name_set(item, "Interval max: %dsec", (int)set.money_interval_rand_max); break;
      default:
         return 0;
      }
   }

   return 0;
}


static int menu_callback_weapons(int op, struct menu_item *item)
{
   struct weapon_entry *weapon = (struct weapon_entry *)item->data;

   if(op == MENU_OP_ENABLED)
   {
      switch(item->id)
      {
      case ID_NONE:          return 0;
      case ID_WEAPON_ENABLE: return cheat_state->generic.weapon;
      }

      if(weapon == NULL)
         return 0;

      if(set.weapon[weapon->slot] == weapon)
         return 1;
   }
   else if(op == MENU_OP_SELECT)
   {
      if(item->id == ID_WEAPON_ENABLE)
      {
         cheat_state->generic.weapon ^= 1;
         return 1;
      }

      if(weapon == NULL)
         return 0;

      if(set.weapon[weapon->slot] == weapon)
         set.weapon[weapon->slot] = NULL;
      else
         set.weapon[weapon->slot] = weapon;

      return 1;
   }
   return 0;
}


static int menu_callback_patches(int op, struct menu_item *item)
{
   struct patch_set *patch = &set.patch[item->id];

   if(op == MENU_OP_ENABLED)
   {
      return patch->installed;
   }
   else if(op == MENU_OP_SELECT)
   {
      if(patch->installed || patch->failed)
         patcher_remove(patch);
      else
         patcher_install(patch);

      return 1;
   }

   return 0;
}


static int menu_callback_vehicles(int op, struct menu_item *item)
{
   if(op == MENU_OP_ENABLED)
   {
      switch(item->id)
      {
      case ID_VEHICLES_IWARP:  return cheat_state->generic.vehicles_warp_invert;
      case ID_VEHICLES_FREEZE: return cheat_state->generic.vehicles_freeze;
      }
   }
   else if(op == MENU_OP_SELECT)
   {
      switch(item->id)
      {
      case ID_VEHICLES_IWARP:  cheat_state->generic.vehicles_warp_invert ^= 1; break;
      case ID_VEHICLES_FREEZE: cheat_state->generic.vehicles_freeze ^= 1;      break;
      default:
         return 0;
      }
      return 1;
   }

   return 0;
}


static int menu_callback_vehicles_sub(int op, struct menu_item *item)
{
   if(op == MENU_OP_SELECT && item->id != ID_NONE)
   {
      struct actor_info *self = actor_info_get(ACTOR_SELF, ACTOR_ALIVE);
      struct vehicle_info *info = vehicle_info_get(item->id,
         VEHICLE_ALIVE | ((cheat_state->generic.vehicles_freeze || !cheat_state->generic.vehicles_warp_invert) ? 0 : VEHICLE_EMPTY));
      float pos[3];

      if(info != NULL && self != NULL)
      {
         if(cheat_state->generic.vehicles_warp_invert)
         {
            vect3_copy(&self->base.matrix[4*3], pos);
            pos[0] += sinf(-self->z_angle) * 5.0f;
            pos[1] += cosf(-self->z_angle) * 5.0f;
            pos[2] += 0.5f;
            cheat_vehicle_teleport(info, pos, gta_interior_id_get());

            matrix_identity(info->base.matrix);
            vect3_copy(pos, &info->base.matrix[4*3]);
            vect3_mult(info->speed, 0.0f, info->speed);
            info->speed[2] = 0.05f;
            vect3_mult(info->spin, 0.0f, info->spin);
         }
         else
         {
            vect3_copy(&info->base.matrix[4*3], pos);
            pos[2] += 1.5f;
            cheat_teleport(pos, info->base.interior_id);
         }
      }
      else
      {
         if(self == NULL)
         {
            cheat_state_text("You are dead.");
         }
         else
         {
            if(vehicle_info_get(item->id, 0) == NULL)
               cheat_state_text("Vehicle not found.");
            else if(vehicle_info_get(item->id, VEHICLE_EMPTY) == NULL)
               cheat_state_text("Vehicle is not empty.");
            else if(vehicle_info_get(item->id, VEHICLE_ALIVE) == NULL)
               cheat_state_text("Vehicle is destroyed.");
         }
      }

      return 1;
   }

   return 0;
}


static int menu_callback_players(int op, struct menu_item *item)
{
   if(op == MENU_OP_SELECT)
   {
      struct samp_player_list *spl = samp_player_list_get();
      struct actor_info *actor = NULL;
      int id = item->id;
      float pos[3];

      if(spl == NULL)
         return 0;

      if(spl->alloc_map[id] != 1)
      {
         cheat_state_text("Player does not exist.");
         return 0;
      }

      if(!samp_player_position_get(id, pos))
      {
         cheat_state_text("Bad player info.");
         return 0;
      }

      if(spl->player_info[id]->actor != NULL)
         actor = *spl->player_info[id]->actor;

      if(actor != NULL && ACTOR_IS_DEAD(actor))
      {
         cheat_state_text("Player is dead.");
         return 0;
      }

      pos[2] += 1.0f;
      cheat_teleport(pos, 0);  /* XXX interior ID */

      return 1;
   }

   return 0;
}


static int menu_callback_teleports(int op, struct menu_item *item)
{
   if(op == MENU_OP_SELECT)
   {
      cheat_teleport_history_save();
      cheat_state_text("Teleported to: %s.",
         set.static_teleport_name[item->id]);
      cheat_teleport(
         set.static_teleport[item->id].pos,
         set.static_teleport[item->id].interior_id);
      return 1;
   }

   return 0;
}


static int menu_callback_misc(int op, struct menu_item *item)
{
   if(op == MENU_OP_ENABLED)
   {
      switch(item->id)
      {
      case ID_MISC_HUDTEXT:     return set.d3dtext_hud;
      case ID_MISC_BAD_WEAPONS: return cheat_state->generic.weapon_disable;
      case ID_MISC_CHAT_TEXT:   return (samp_chat_info_get() == NULL) ? 0 : !samp_chat_info_get()->enabled;
      case ID_MISC_KILL_TEXT:   return (samp_kill_info_get() == NULL) ? 0 : !samp_kill_info_get()->enabled;
      case ID_MISC_NAME_TAG:    return set.d3dtext_tags;
      case ID_MISC_TRAILERS:    return set.trailer_support;
      }
   }
   else if(op == MENU_OP_SELECT)
   {
      switch(item->id)
      {
      case ID_MISC_COORDS:
         {
            float *pos = (cheat_state->state == CHEAT_STATE_VEHICLE) ?
                  cheat_state->vehicle.coords
                  : cheat_state->actor.coords;
            log_debug("static_teleport_name[] = \"\"");
            log_debug("static_teleport_pos[] = %.2f %.2f %.2f   %d",
               pos[0], pos[1], pos[2], gta_interior_id_get());
            cheat_state_text("Current coordinates written to log file.");
         }
         break;
      case ID_MISC_RELOAD:
         ini_reload();
         break;
      case ID_MISC_HUDTEXT:
         set.d3dtext_hud ^= 1;
         break;
      case ID_MISC_BAD_WEAPONS:
         cheat_state->generic.weapon_disable ^= 1;
         break;
      case ID_MISC_CHAT_TEXT:
         if(samp_chat_info_get() != NULL)
            samp_chat_info_get()->enabled ^= 1;
         break;
      case ID_MISC_KILL_TEXT:
         if(samp_kill_info_get() != NULL)
            samp_kill_info_get()->enabled ^= 1;
         break;
      case ID_MISC_NAME_TAG:
         set.d3dtext_tags ^= 1;
         break;
      case ID_MISC_TRAILERS:
         set.trailer_support ^= 1;
         break;
      default:
         return 0;
      }
      return 1;
   }

   return 0;
}


static int menu_callback_debug(int op, struct menu_item *item)
{
   if(op == MENU_OP_ENABLED)
   {
      if(item->id == ID_DEBUG_ENABLE)
         return cheat_state->debug_enabled;
      return 0;
   }

   if(op == MENU_OP_SELECT)
   {
      switch(item->id)
      {
      case ID_DEBUG_ENABLE:
         cheat_state->debug_enabled ^= 1;
         break;
      case ID_DEBUG_SELF_ACTOR:       debug_ptr_set((void *)actor_info_get(ACTOR_SELF, 0)); break;
      case ID_DEBUG_SELF_VEHICLE:     debug_ptr_set((void *)vehicle_info_get(VEHICLE_SELF, 0)); break;
      case ID_DEBUG_CAMERA:           debug_ptr_set((void *)cam_matrix); break;
      case ID_DEBUG_VEHICLE_CONTACT:  debug_ptr_set((void *)actor_info_get(ACTOR_SELF, 0)->vehicle_contact); break;
      case ID_DEBUG_BUILDING_CONTACT: debug_ptr_set((void *)actor_info_get(ACTOR_SELF, 0)->building_contact); break;
      case ID_DEBUG_NEAREST_ACTOR:    debug_ptr_set((void *)actor_info_get(actor_find_nearest(0), 0)); break;
      case ID_DEBUG_NEAREST_VEHICLE:  debug_ptr_set((void *)vehicle_info_get(vehicle_find_nearest(0), 0)); break;
      case ID_DEBUG_VEHICLE_POOL:     debug_ptr_set((void *)pool_vehicle); break;
      case ID_DEBUG_SAMP_PLAYER_LIST: debug_ptr_set((void *)samp_player_list_get()); break;
      case ID_DEBUG_GRAVITY:          debug_ptr_set((void *)0x00863984); break;
      case ID_DEBUG_TIMER_SPEED:      debug_ptr_set((void *)0x00B7015C); break;
      case ID_DEBUG_GAME_SPEED:       debug_ptr_set((void *)0x00B7CB64); break;
      case ID_DEBUG_PARACHUTE:        debug_ptr_set((void *)0x06DAEF40); break;
      default:
         return 0;
      }
      return 1;
   }

   return 0;
}


void menu_maybe_init(void)
{
   struct menu *menu_main,
                  *menu_cheats,
                     *menu_cheats_inv,
                     *menu_cheats_money,
                     *menu_cheats_spoof_weapon,
                     *menu_cheats_fake_kill,
                     *menu_cheats_weather,
                     *menu_cheats_time,
                  *menu_patches,
                  *menu_weapons,
                  *menu_vehicles,
                  *menu_players,
                  *menu_teleports,
                  *menu_misc,
                     *menu_debug;
   char name[128];
   int i, slot;

   if(menu_init)
      return;

   menu_init = 1;

   menu_main                = menu_new(NULL,        ID_MENU_MAIN,                menu_callback_main);
   menu_cheats              = menu_new(menu_main,   ID_MENU_CHEATS,              menu_callback_cheats);
   menu_cheats_inv          = menu_new(menu_cheats, ID_MENU_CHEATS_INVULN,       menu_callback_cheats_invuln);
   menu_cheats_money        = menu_new(menu_cheats, ID_MENU_CHEATS_MONEY,        menu_callback_cheats_money);
   menu_cheats_spoof_weapon = menu_new(menu_cheats, ID_MENU_CHEATS_SPOOF_WEAPON, menu_callback_cheats);
   menu_cheats_fake_kill    = menu_new(menu_cheats, ID_MENU_CHEATS_FAKE_KILL,    menu_callback_cheats);
   menu_cheats_weather      = menu_new(menu_cheats, ID_MENU_CHEATS_WEATHER,      menu_callback_cheats);
   menu_cheats_time         = menu_new(menu_cheats, ID_MENU_CHEATS_TIME,         menu_callback_cheats);
   menu_patches             = menu_new(menu_main,   ID_MENU_PATCHES,             menu_callback_patches);
   menu_weapons             = menu_new(menu_main,   ID_MENU_WEAPONS,             menu_callback_weapons);
   menu_vehicles            = menu_new(menu_main,   ID_MENU_VEHICLES,            menu_callback_vehicles);
   menu_players             = menu_new(menu_main,   ID_MENU_PLAYERS,             menu_callback_players);
   menu_teleports           = menu_new(menu_main,   ID_MENU_TELEPORTS,           menu_callback_teleports);
   menu_misc                = menu_new(menu_main,   ID_MENU_MISCELLANEOUS,       menu_callback_misc);
   menu_debug               = menu_new(menu_misc,   ID_MENU_DEBUG,               menu_callback_debug);

   /* main menu */
   menu_item_add(menu_main,   menu_cheats,         "Cheats",             ID_NONE, MENU_COLOR_DEFAULT, NULL);
   menu_item_add(menu_main,   menu_patches,        "Patches",            ID_NONE, MENU_COLOR_DEFAULT, NULL);
   menu_item_add(menu_main,   menu_weapons,        "Weapons",            ID_NONE, MENU_COLOR_DEFAULT, NULL);
   menu_item_add(menu_main,   menu_vehicles,       "Vehicles",           ID_NONE, MENU_COLOR_DEFAULT, NULL);
   menu_item_add(menu_main,   menu_players,        "Players",            ID_NONE, MENU_COLOR_DEFAULT, NULL);
   menu_item_add(menu_main,   menu_teleports,      "Teleports",          ID_NONE, MENU_COLOR_DEFAULT, NULL);
   menu_item_add(menu_main,   menu_misc,           "Miscellaneous",      ID_NONE, MENU_COLOR_DEFAULT, NULL);

   /* main menu -> cheats */
   menu_item_add(menu_cheats, menu_cheats_inv,           "Invulnerable",            ID_CHEAT_INVULN,     MENU_COLOR_DEFAULT, NULL);
   menu_item_add(menu_cheats, NULL,                      "Restore HP (detected)",   ID_CHEAT_HP,         MENU_COLOR_DEFAULT, NULL);
   menu_item_add(menu_cheats, NULL,                      "Restore armor",           ID_CHEAT_ARMOR,      MENU_COLOR_DEFAULT, NULL);
   menu_item_add(menu_cheats, menu_cheats_money,         "Money",                   ID_CHEAT_MONEY,      MENU_COLOR_DEFAULT, NULL);
   menu_item_add(menu_cheats, NULL,                      "Map",                     ID_CHEAT_MAP,        MENU_COLOR_DEFAULT, NULL);
   menu_item_add(menu_cheats, NULL,                      "Misc. protections",       ID_CHEAT_PROT,       MENU_COLOR_DEFAULT, NULL);
   menu_item_add(menu_cheats, NULL,                      "Go to nearest empty car", ID_CHEAT_WARP_NEAR,  MENU_COLOR_DEFAULT, NULL);
   menu_item_add(menu_cheats, NULL,                      "Give Jetpack",            ID_CHEAT_JETPACK,    MENU_COLOR_DEFAULT, NULL);
   menu_item_add(menu_cheats, menu_cheats_spoof_weapon,  "Spoof weapon",            ID_NONE,             MENU_COLOR_DEFAULT, NULL);
   menu_item_add(menu_cheats, menu_cheats_fake_kill,     "Fake kill",               ID_NONE,             MENU_COLOR_DEFAULT, NULL);
   menu_item_add(menu_cheats, NULL,                      "Unlock vehicles",         ID_CHEAT_UNLOCK,     MENU_COLOR_DEFAULT, NULL);
   menu_item_add(menu_cheats, menu_cheats_weather,       "Freeze weather",          ID_NONE,             MENU_COLOR_DEFAULT, NULL);
   menu_item_add(menu_cheats, menu_cheats_time,          "Freeze time",             ID_NONE,             MENU_COLOR_DEFAULT, NULL);
   menu_item_add(menu_cheats, NULL,                      "Gravity: 0.0080",         ID_CHEAT_GRAVITY,    MENU_COLOR_DEFAULT, NULL);
   menu_item_add(menu_cheats, NULL,                      "Game speed: 100%",        ID_CHEAT_GAME_SPEED, MENU_COLOR_DEFAULT, NULL);
   menu_item_add(menu_cheats, NULL,                      "Safe haven (crap)",       ID_CHEAT_HAVEN,      MENU_COLOR_DEFAULT, NULL);

   /* main menu -> cheats -> invulnerable */
   menu_item_add(menu_cheats_inv, NULL, "Actor invulnerability",    ID_CHEAT_INVULN_ACTOR,   MENU_COLOR_DEFAULT, NULL);
   menu_item_add(menu_cheats_inv, NULL, "Vehicle invulnerability",  ID_CHEAT_INVULN_VEHICLE, MENU_COLOR_DEFAULT, NULL);
   menu_item_add(menu_cheats_inv, NULL, "Vehicle tire protections", ID_CHEAT_INVULN_TIRES,   MENU_COLOR_DEFAULT, NULL);
   /*menu_item_add(menu_cheats_inv, NULL, "\t", ID_NONE, MENU_COLOR_SEPARATOR, NULL);*/
   snprintf(name, sizeof(name), "Vehicle minimum HP: %d", (int)set.hp_minimum);
   menu_item_add(menu_cheats_inv, NULL, name, ID_CHEAT_INVULN_MIN_HP, MENU_COLOR_DEFAULT, NULL);
   snprintf(name, sizeof(name), "Vehicle damage reduction: %d%%", (int)set.hp_damage_reduce);
   menu_item_add(menu_cheats_inv, NULL, name, ID_CHEAT_INVULN_DMGRED, MENU_COLOR_DEFAULT, NULL);
   snprintf(name, sizeof(name), "Vehicle HP regeneration: %dhp/sec", (int)set.hp_regen);
   menu_item_add(menu_cheats_inv, NULL, name, ID_CHEAT_INVULN_REGEN,  MENU_COLOR_DEFAULT, NULL);

   /* main menu -> cheats -> money */
   menu_item_add(menu_cheats_money, NULL, "Give $500",   ID_CHEAT_MONEY_GIVE_500,   MENU_COLOR_DEFAULT, NULL);
   menu_item_add(menu_cheats_money, NULL, "Give $1000",  ID_CHEAT_MONEY_GIVE_1000,  MENU_COLOR_DEFAULT, NULL);
   menu_item_add(menu_cheats_money, NULL, "Give $5000",  ID_CHEAT_MONEY_GIVE_5000,  MENU_COLOR_DEFAULT, NULL);
   menu_item_add(menu_cheats_money, NULL, "Give $10000", ID_CHEAT_MONEY_GIVE_10000, MENU_COLOR_DEFAULT, NULL);
   menu_item_add(menu_cheats_money, NULL, "Give $20000", ID_CHEAT_MONEY_GIVE_20000, MENU_COLOR_DEFAULT, NULL);
   menu_item_add(menu_cheats_money, NULL, "\tSettings",  ID_NONE,                   MENU_COLOR_SEPARATOR, NULL);
   snprintf(name, sizeof(name), "Max money: %d", (int)set.money_amount_max);
   menu_item_add(menu_cheats_money, NULL, name, ID_CHEAT_MONEY_MAX, MENU_COLOR_DEFAULT, NULL);
   snprintf(name, sizeof(name), "Random min: %d", (int)set.money_amount_rand_min);
   menu_item_add(menu_cheats_money, NULL, name, ID_CHEAT_MONEY_RAND_MIN, MENU_COLOR_DEFAULT, NULL);
   snprintf(name, sizeof(name), "Random max: %d", (int)set.money_amount_rand_max);
   menu_item_add(menu_cheats_money, NULL, name, ID_CHEAT_MONEY_RAND_MAX, MENU_COLOR_DEFAULT, NULL);
   snprintf(name, sizeof(name), "Interval min: %dsec", (int)set.money_interval_rand_min);
   menu_item_add(menu_cheats_money, NULL, name, ID_CHEAT_MONEY_IVAL_MIN, MENU_COLOR_DEFAULT, NULL);
   snprintf(name, sizeof(name), "Interval max: %dsec", (int)set.money_interval_rand_max);
   menu_item_add(menu_cheats_money, NULL, name, ID_CHEAT_MONEY_IVAL_MAX, MENU_COLOR_DEFAULT, NULL);

   /* main menu -> cheats -> weather */
   for(i=0; ; i++)
   {
      static const struct { int id; char *name; } weather_map[] =
      {
          0, "Blue sky",
          8, "Stormy",
          9, "Cloudy and foggy",
         10, "Clear blue sky",
         11, "Scorching hot (heat waves)",
         12, "Very dull, colorless, hazy",
         16, "Dull, cloudy, rainy",
         17, "Scorching hot",
         19, "Sandstorm",
         20, "Greenish fog",
         21, "Very dark, gradiented skyline, purple",
         22, "Very dark, gradiented skyline, green",
         23, "Variations of pale orange",
         27, "Variations of fresh blue",
         30, "Variations of dark, cloudy, teal",
         33, "Dark, cloudy, brown",
         34, "Blue, purple, regular",
         35, "Dull brown",
         36, "Extremely bright",
         40, "Blue, purple, cloudy",
         43, "Dark toxic clouds",
         44, "Black, white sky",
         45, "Black, purple sky",
         -1, NULL
      };

      if(weather_map[i].name == NULL)
         break;

      menu_item_add(menu_cheats_weather, NULL, weather_map[i].name, ID_CHEAT_WEATHER, MENU_COLOR_DEFAULT, (void *)(UINT_PTR)weather_map[i].id);
   }

   /* main menu -> cheats -> time */
   for(i=0; i<24; i++)
   {
      snprintf(name, sizeof(name), "%02d:00", i);
      menu_item_add(menu_cheats_time, NULL, name, ID_CHEAT_TIME, MENU_COLOR_DEFAULT, (void *)(UINT_PTR)i);
   }


   /* main menu -> cheats -> fake weapon */
   for(i=0; weapon_list[i].name!=NULL; i++)
   {
      const struct weapon_entry *weapon = &weapon_list[i];

      if(strcmp(weapon->name, "Camera")     == 0 ||
         strcmp(weapon->name, "NV Goggles") == 0 ||
         strcmp(weapon->name, "IR Goggles") == 0 ||
         strcmp(weapon->name, "Parachute")  == 0 ||
         strcmp(weapon->name, "Detonator")  == 0)
         continue;

      snprintf(name, sizeof(name), "Fake killed by %s", weapon->name);
      menu_item_add(menu_cheats_spoof_weapon, NULL, name, ID_CHEAT_SPOOF_WEAPON, MENU_COLOR_DEFAULT, (void *)(UINT_PTR)weapon->id);
   }

   menu_item_add(menu_cheats_spoof_weapon, NULL, "Fake death by fall", ID_CHEAT_SPOOF_WEAPON, MENU_COLOR_DEFAULT, (void *)(UINT_PTR)20);
   menu_item_add(menu_cheats_spoof_weapon, NULL, "Fake begin run over", ID_CHEAT_SPOOF_WEAPON, MENU_COLOR_DEFAULT, (void *)(UINT_PTR)21);


   /* main menu -> weapons */
   menu_item_add(menu_weapons, NULL, "Enable weapon cheat", ID_WEAPON_ENABLE, MENU_COLOR_DEFAULT, NULL);
   for(slot=0; slot<13; slot++)
   {
      const struct weapon_entry *weapon = weapon_list;

      snprintf(name, sizeof(name), "\tSlot %d", slot);
      menu_item_add(menu_weapons, NULL, name, ID_NONE, MENU_COLOR_SEPARATOR, NULL);

      while(weapon->name != NULL)
      {
         if(weapon->slot == slot)
            menu_item_add(menu_weapons, NULL, weapon->name, ID_WEAPON_ITEM, MENU_COLOR_DEFAULT, (void *)weapon);
         weapon++;
      }
   }

   /* main menu -> vehicles */
   for(i=0; i<VEHICLE_CLASS_COUNT; i++)
   {
      struct menu *menu = menu_new(menu_vehicles, ID_MENU_VEHICLES_SUB, menu_callback_vehicles_sub);

      snprintf(name, sizeof(name), "%s", gta_vehicle_class_name(i));
      menu_item_add(menu_vehicles, menu, name, ID_NONE, MENU_COLOR_DEFAULT, (void *)(UINT_PTR)i);
   }
   menu_item_add(menu_vehicles, NULL, "Freeze nearby vehicles", ID_VEHICLES_FREEZE, MENU_COLOR_DEFAULT, NULL);
   menu_item_add(menu_vehicles, NULL, "Warp vehicles to you",   ID_VEHICLES_IWARP,  MENU_COLOR_DEFAULT, NULL);


   /* main menu -> patches */
   for(i=0; i<INI_PATCHES_MAX; i++)
   {
      if(set.patch[i].name == NULL)
         continue;

      menu_item_add(menu_patches, NULL, set.patch[i].name, i, MENU_COLOR_DEFAULT, NULL);
   }


   /* teleports */
   for(i=0; i<STATIC_TELEPORT_MAX; i++)
   {
      if(strlen(set.static_teleport_name[i]) == 0)
         continue;

      if(vect3_near_zero(set.static_teleport[i].pos))
         continue;

      menu_item_add(menu_teleports, NULL, set.static_teleport_name[i], i, MENU_COLOR_DEFAULT, NULL);
   }


   /* misc */
   menu_item_add(menu_misc, NULL,       "Write coordinates to log file", ID_MISC_COORDS,      MENU_COLOR_DEFAULT, NULL);
   menu_item_add(menu_misc, NULL,       "Reload settings",               ID_MISC_RELOAD,      MENU_COLOR_DEFAULT, NULL);
   menu_item_add(menu_misc, NULL,       "Enable HUD text",               ID_MISC_HUDTEXT,     MENU_COLOR_DEFAULT, NULL);
   menu_item_add(menu_misc, NULL,       "Replace chat text rendering",   ID_MISC_CHAT_TEXT,   MENU_COLOR_DEFAULT, NULL);
   menu_item_add(menu_misc, NULL,       "Replace kill text rendering",   ID_MISC_KILL_TEXT,   MENU_COLOR_DEFAULT, NULL);
   menu_item_add(menu_misc, NULL,       "Replace name tag rendering",    ID_MISC_NAME_TAG,    MENU_COLOR_DEFAULT, NULL);
   menu_item_add(menu_misc, NULL,       "Disable bad weapons",           ID_MISC_BAD_WEAPONS, MENU_COLOR_DEFAULT, NULL);
   menu_item_add(menu_misc, NULL,       "Trailer support",               ID_MISC_TRAILERS,    MENU_COLOR_DEFAULT, NULL);
   menu_item_add(menu_misc, menu_debug, "Debug",                         ID_NONE,             MENU_COLOR_DEFAULT, NULL);

   /* misc -> debug */
   menu_item_add(menu_debug, NULL, "Enable",             ID_DEBUG_ENABLE,           MENU_COLOR_DEFAULT, NULL);
   menu_item_add(menu_debug, NULL, "Self actor",         ID_DEBUG_SELF_ACTOR,       MENU_COLOR_DEFAULT, NULL);
   menu_item_add(menu_debug, NULL, "Self vehicle",       ID_DEBUG_SELF_VEHICLE,     MENU_COLOR_DEFAULT, NULL);
   menu_item_add(menu_debug, NULL, "Camera",             ID_DEBUG_CAMERA,           MENU_COLOR_DEFAULT, NULL);
   menu_item_add(menu_debug, NULL, "Vehicle contact",    ID_DEBUG_VEHICLE_CONTACT,  MENU_COLOR_DEFAULT, NULL);
   menu_item_add(menu_debug, NULL, "Building contact",   ID_DEBUG_BUILDING_CONTACT, MENU_COLOR_DEFAULT, NULL);
   menu_item_add(menu_debug, NULL, "Nearest actor",      ID_DEBUG_NEAREST_ACTOR,    MENU_COLOR_DEFAULT, NULL);
   menu_item_add(menu_debug, NULL, "Nearest vehicle",    ID_DEBUG_NEAREST_VEHICLE,  MENU_COLOR_DEFAULT, NULL);
   menu_item_add(menu_debug, NULL, "Vehicle pool",       ID_DEBUG_VEHICLE_POOL,     MENU_COLOR_DEFAULT, NULL);
   menu_item_add(menu_debug, NULL, "SA:MP Player List",  ID_DEBUG_SAMP_PLAYER_LIST, MENU_COLOR_DEFAULT, NULL);
   menu_item_add(menu_debug, NULL, "Gravity",            ID_DEBUG_GRAVITY,          MENU_COLOR_DEFAULT, NULL);
   menu_item_add(menu_debug, NULL, "Timer speed",        ID_DEBUG_TIMER_SPEED,      MENU_COLOR_DEFAULT, NULL);
   menu_item_add(menu_debug, NULL, "Game speed",         ID_DEBUG_GAME_SPEED,       MENU_COLOR_DEFAULT, NULL);
   menu_item_add(menu_debug, NULL, "Parachute class",    ID_DEBUG_PARACHUTE,        MENU_COLOR_DEFAULT, NULL);

   menu_active = menu_main;
}


static void menu_free(struct menu *menu)
{
   int i;

   for(i=0; i<menu->count; i++)
   {
      if(menu->item[i].submenu != NULL)
      {
         menu_free(menu->item[i].submenu);
         if(menu->item[i].name != NULL)
            free((void *)menu->item[i].name);
      }
   }
   /*if(menu->parent != NULL)
   {
      struct menu *parent = menu->parent;
      for(i=0; i<parent->count; i++)
      {
         if(menu->item[i].submenu == menu)
            menu->item[i].submenu = NULL;
      }
   }*/
   free(menu->item);
   free(menu);
}


void menu_items_free(struct menu *menu)
{
   int i;

   if(menu == NULL)
      return;

   for(i=0; i<menu->count; i++)
   {
      if(menu->item[i].submenu != NULL)
      {
         menu_free(menu->item[i].submenu);
         if(menu->item[i].name != NULL)
            free((void *)menu->item[i].name);
      }
   }
   free(menu->item);
   menu->item = NULL;
   menu->count = 0;
   menu->pos = 0;
   menu->top_pos = 0;
}


void menu_free_all(void)
{
   struct menu *menu;

   if(menu_active == NULL)
      return;

   /* find root menu */
   for(menu=menu_active; menu->parent!=NULL; menu=menu->parent);

   menu_free(menu);
   menu_active = NULL;
   menu_init = 0;
}
