/*******************************************************************************

  cstrike.inc v1.0.3

  Author: KRoT@L

  This file is the conversion of amxmodx's cstrike module.
  VexdUM has to be enabled to make these stocks work.
  Credit for the offsets and the initial code goes to AMX Mod X Development Team.
  
  Stocks:
  
    cs_get_user_money(id)
    cs_set_user_money(id, money, flash=1)
    cs_get_user_deaths(id)
    cs_set_user_deaths(id, newdeaths)
    cs_get_weapon_id(weapon_id)
    cs_get_weapon_silenced(weapon_id)
    cs_set_weapon_silenced(weapon_id, silence=1)
    cs_get_weapon_burstmode(weapon_id)
    cs_set_weapon_burstmode(weapon_id, burstmode=1)
    cs_get_user_armor(id, &CsArmorType:armortype)
    cs_set_user_armor(id, armorvalue, CsArmorType:armortype)
    cs_get_user_vip(id)
    cs_set_user_vip(id, vip=1, model=1, scoreboard=1)
    CsTeams:cs_get_user_team(id)
    cs_set_user_team(id, CsTeams:team, CsInternalModel:model=CS_DONTCHANGE)
    cs_get_user_inside_buyzone(id)
    cs_get_user_plant(id)
    cs_set_user_plant(id, plant=1, showbombicon=1)
    cs_get_user_defusekitkit(id)
    cs_set_user_defusekit(id, defusekit=1, r=0, g=160, b=0, icon[]="defuser", flash=0)
    cs_get_user_backpackammo(id, weapon)
    cs_set_user_backpackammo(id, weapon, amount)
    cs_get_user_nvg(id)
    cs_set_user_nvg(id, nvgoggles=1)
    cs_get_user_model(id, model[], len)
    cs_set_user_model(id, const model[])
    cs_reset_user_model(id)
    cs_get_hostage_id(hostage_id)
    cs_get_hostage_follow(hostage_id)
    cs_set_hostage_follow(hostage_id, followedid=0)
    cs_get_weapon_ammo(weapon_id)
    cs_set_weapon_ammo(weapon_id, newammo)
    cs_get_user_hasprimary(id)
    cs_get_user_tked(id)
    cs_set_user_tked(id, tk=1, subtract=1)
    cs_get_user_driving(id)
    cs_get_user_stationary(id)
    cs_get_user_shield(id)
    cs_user_spawn(id)
    cs_get_armoury_type(ent_id)
    cs_set_armoury_type(ent_id, type)
    cs_get_user_mapzones(id)

*******************************************************************************/

// Comment if you use AMX 099.
#define AMX_2006

/*******************************************************************************
*  This program is free software; you can redistribute it and/or modify it
*  under the terms of the GNU General Public License as published by the
*  Free Software Foundation; either version 2 of the License, or (at
*  your option) any later version.
*
*  This program is distributed in the hope that it will be useful, but
*  WITHOUT ANY WARRANTY; without even the implied warranty of
*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
*  General Public License for more details.
*
*  You should have received a copy of the GNU General Public License
*  along with this program; if not, write to the Free Software Foundation,
*  Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*  In addition, as a special exception, the author gives permission to
*  link the code of this program with the Half-Life Game Engine ("HL
*  Engine") and Modified Game Libraries ("MODs") developed by Valve,
*  L.L.C ("Valve"). You must obey the GNU General Public License in all
*  respects for all of the code used other than the HL Engine and MODs
*  from Valve. If you modify this file, you may extend this exception
*  to your version of the file, but you are not obligated to do so. If
*  you do not wish to do so, delete this exception statement from your
*  version.
*******************************************************************************/


#if defined _cstrike_included
  #endinput
#endif
#define _cstrike_included

#include <amxmod>
#include <Vexd_Utilities>
#include <VexdUM_stock>

#define HAS_SHIELD (1<<24)

#define OFFSET_ARMORTYPE 112
#define OFFSET_TEAM 114
#define OFFSET_CSMONEY 115
#define OFFSET_PRIMARYWEAPON 116
#define OFFSET_NVGOGGLES 129
#define OFFSET_DEFUSE_PLANT 193
#define OFFSET_VIP 209
#define OFFSET_TK  216
#define OFFSET_MAPZONE 235
#define OFFSET_ISDRIVING 350
#define OFFSET_STATIONARY 362

#define OFFSET_AWM_AMMO  377
#define OFFSET_SCOUT_AMMO 378
#define OFFSET_PARA_AMMO 379
#define OFFSET_FAMAS_AMMO 380
#define OFFSET_M3_AMMO 381
#define OFFSET_USP_AMMO  382
#define OFFSET_FIVESEVEN_AMMO 383
#define OFFSET_DEAGLE_AMMO 384
#define OFFSET_P228_AMMO 385
#define OFFSET_GLOCK_AMMO 386
#define OFFSET_FLASH_AMMO 387
#define OFFSET_HE_AMMO 388
#define OFFSET_SMOKE_AMMO 389
#define OFFSET_C4_AMMO 390

#define OFFSET_CSDEATHS  444
#define OFFSET_SHIELD 510

#define OFFSET_WEAPONTYPE 43
#define OFFSET_CLIPAMMO 51
#define OFFSET_SILENCER_FIREMODE 74

#define OFFSET_HOSTAGEFOLLOW 86
#define OFFSET_HOSTAGEID 487

#define OFFSET_ARMOURY_TYPE 34

#define M4A1_SILENCED (1<<2)
#define M4A1_ATTACHSILENCEANIM 6
#define M4A1_DETACHSILENCEANIM 13
#define USP_SILENCED (1<<0)
#define USP_ATTACHSILENCEANIM 7
#define USP_DETACHSILENCEANIM  15

#define GLOCK_SEMIAUTOMATIC 0
#define GLOCK_BURSTMODE  2
#define FAMAS_AUTOMATIC  0
#define FAMAS_BURSTMODE  16

#define PLAYER_IS_VIP (1<<8)

#define TEAM_UNASSIGNED  0
#define TEAM_T 1
#define TEAM_CT 2
#define TEAM_SPECTATOR 3

#define CAN_PLANT_BOMB (1<<8)
#define HAS_DEFUSE_KIT (1<<16)

#define DEFUSER_COLOUR_R 0
#define DEFUSER_COLOUR_G 160
#define DEFUSER_COLOUR_B 0

#define HAS_NVGOGGLES (1<<0)

#define SCOREATTRIB_NOTHING 0
#define SCOREATTRIB_DEAD 1
#define SCOREATTRIB_BOMB 2
#define SCOREATTRIB_VIP  4

#define CSW_VEST 31
#define CSW_VESTHELM 32

#define CSA_MP5NAVY 0
#define CSA_TMP 1
#define CSA_P90 2
#define CSA_MAC10 3
#define CSA_AK47 4
#define CSA_SG552 5
#define CSA_M4A1 6
#define CSA_AUG 7
#define CSA_SCOUT 8
#define CSA_G3SG1 9
#define CSA_AWP 10
#define CSA_M3 11
#define CSA_XM1014 12
#define CSA_M249 13
#define CSA_FLASHBANG 14
#define CSA_HEGRENADE 15
#define CSA_VEST 16
#define CSA_VESTHELM 17
#define CSA_SMOKEGRENADE 18

#define PLAYER_IN_BUYZONE (1<<0)
#define PLAYER_IN_BOMB_TARGET (1<<1)
#define PLAYER_IN_HOSTAGE_RESCUE (1<<2)
#define PLAYER_IN_ESCAPEZONE (1<<3)
#define PLAYER_IN_VIP_SAFETYZONE (1<<4)


#if defined AMX_2006
  #define reportError report_error
#else
  #define reportError server_print
#endif


stock bool:is_player(id)
{
  new classname[32]
  entity_get_string(id, EV_SZ_classname, classname, 31)
  if(equal(classname, "player")) return true
  return false
}

stock bool:cs_check_player(id, const msg[])
{
  if((id < 1) || (id > get_maxplayers())) {
    reportError("%s: Player %d invalid", msg, id)
    return true
  }
  if(!is_user_connected(id)) {
    reportError("%s: Player %d not in-game", msg, id)
    return true
  }
  return false
}

stock bool:cs_check_entity(ent, const msg[])
{
  if((ent < 1) || (ent > get_maxentities())) {
    reportError("%s: Entity %d out of range", msg, ent)
    return true
  }
  if(!is_entity(ent)) {
    reportError("%s: Entity %d invalid", msg, ent)
    return true
  }
  return false
}

// Gets user money in cs.
// params[1] = user index
stock cs_get_user_money(id)
{
  if(cs_check_player(id, "cs_get_user_money")) return 0

  return get_offset_int(id, OFFSET_CSMONEY)
}

// Give money to user
// params[1] = user index
// params[2] = money
// params[3] = flash money?
stock cs_set_user_money(id, money, flash=1)
{
  if(cs_check_player(id, "cs_set_user_money")) return 0

  set_offset_int(id, OFFSET_CSMONEY, money)
  message_begin(MSG_ONE, get_user_msgid("Money"), {0,0,0}, id)
  write_long(money)
  write_byte(flash ? 1 : 0)
  message_end()
  return 1
}

// Gets user deaths in cs.
// params[1] = user index
stock cs_get_user_deaths(id)
{
  if(cs_check_player(id, "cs_get_user_deaths")) return 0

  return get_offset_int(id, OFFSET_CSDEATHS)
}

// Sets user deaths in cs.
// params[1] = user index
// params[2] = new deaths
stock cs_set_user_deaths(id, newdeaths)
{
  if(cs_check_player(id, "cs_set_user_deaths")) return 0

  set_offset_int(id, OFFSET_CSDEATHS, newdeaths)
  message_begin(MSG_ALL, get_user_msgid("ScoreInfo"))
  write_byte(id)
  write_short(get_user_frags(id))
  write_short(newdeaths)
  write_short(0)
  write_short(get_user_team(id))
  message_end()
  return 1
}

// Gets weapon type. Corresponds to CSW_*
// params[1] = weapon index
stock cs_get_weapon_id(weapon_id)
{
  if(cs_check_entity(weapon_id, "cs_get_weapon_id")) return 0

  return get_offset_int(weapon_id, OFFSET_WEAPONTYPE, 4)
}

// Is weapon silenced? Does only work on M4A1 and USP.
// params[1] = weapon index
stock cs_get_weapon_silenced(weapon_id)
{
  if(cs_check_entity(weapon_id, "cs_get_weapon_silenced")) return 0

  new weapontype = get_offset_int(weapon_id, OFFSET_WEAPONTYPE, 4)
  new silencemode = get_offset_int(weapon_id, OFFSET_SILENCER_FIREMODE, 4)
  switch(weapontype)
  {
    case CSW_M4A1:
      if(silencemode & M4A1_SILENCED)
        return 1
    case CSW_USP:
      if(silencemode & USP_SILENCED)
        return 1
  }
  return 0
}

// Silence/unsilence gun. Does only work on M4A1 and USP.
// params[1] = weapon index
// params[2] = 1 and we silence the gun, 0 and we unsilence the gun.
stock cs_set_weapon_silenced(weapon_id, silence=1)
{
  if(cs_check_entity(weapon_id, "cs_set_weapon_silenced")) return 0

  new weapontype = get_offset_int(weapon_id, OFFSET_WEAPONTYPE, 4)
  new silencemode = get_offset_int(weapon_id, OFFSET_SILENCER_FIREMODE, 4)

  switch(weapontype)
  {
    case CSW_M4A1: {
      if(silence == 1) {
        if(!(silencemode & M4A1_SILENCED)) {
          silencemode |= M4A1_SILENCED
          set_offset_int(weapon_id, OFFSET_SILENCER_FIREMODE, silencemode, 4)
          new owner = entity_get_edict(weapon_id, EV_ENT_owner)
          if(is_player(owner))
            entity_set_int(owner, EV_INT_weaponanim, M4A1_ATTACHSILENCEANIM)
        }
      }
      else if(silencemode & M4A1_SILENCED) {
        silencemode &= ~M4A1_SILENCED
        set_offset_int(weapon_id, OFFSET_SILENCER_FIREMODE, silencemode, 4)
        new owner = entity_get_edict(weapon_id, EV_ENT_owner)
        if(is_player(owner))
          entity_set_int(owner, EV_INT_weaponanim, M4A1_DETACHSILENCEANIM)
      }
    }
    case CSW_USP: {
      if(silence == 1) {
        if(!(silencemode & USP_SILENCED)) {
          silencemode |= USP_SILENCED
          set_offset_int(weapon_id, OFFSET_SILENCER_FIREMODE, silencemode, 4)
          new owner = entity_get_edict(weapon_id, EV_ENT_owner)
          if(is_player(owner))
            entity_set_int(owner, EV_INT_weaponanim, USP_ATTACHSILENCEANIM)
        }
      }
      else if(silencemode & USP_SILENCED) {
        silencemode &= ~USP_SILENCED
        set_offset_int(weapon_id, OFFSET_SILENCER_FIREMODE, silencemode, 4)
        new owner = entity_get_edict(weapon_id, EV_ENT_owner)
        if(is_player(owner))
          entity_set_int(owner, EV_INT_weaponanim, USP_DETACHSILENCEANIM)
      }
    }
    default:
      return 0
  }
  return 1
}

// Is weapon in burst mode? Does only work with FAMAS and GLOCK.
// params[1] = weapon index
stock cs_get_weapon_burstmode(weapon_id)
{
  if(cs_check_entity(weapon_id, "cs_get_weapon_burstmode")) return 0

  new weapontype = get_offset_int(weapon_id, OFFSET_WEAPONTYPE, 4)
  new firemode = get_offset_int(weapon_id, OFFSET_SILENCER_FIREMODE, 4)

  switch(weapontype)
  {
    case CSW_GLOCK18: {
      if (firemode == GLOCK_BURSTMODE)
        return 1
    }
    case CSW_FAMAS: {
      if (firemode == FAMAS_BURSTMODE)
        return 1
    }
  }
  return 0
}

// Set/unset burstmode. Does only work with FAMAS and GLOCK.
// params[1] = weapon index
// params[2] = 1 and we set burstmode, 0 and we unset it.
stock cs_set_weapon_burstmode(weapon_id, burstmode=1)
{
  if(cs_check_entity(weapon_id, "cs_set_weapon_burstmode")) return 0

  new weapontype = get_offset_int(weapon_id, OFFSET_WEAPONTYPE, 4)
  new firemode = get_offset_int(weapon_id, OFFSET_SILENCER_FIREMODE, 4)
  new previousMode = firemode

  switch (weapontype)
  {
    case CSW_GLOCK18: {
      if(burstmode) {
        if (previousMode != GLOCK_BURSTMODE) {
          firemode = GLOCK_BURSTMODE
          set_offset_int(weapon_id, OFFSET_SILENCER_FIREMODE, firemode, 4)

          new owner = entity_get_edict(weapon_id, EV_ENT_owner)
          if(is_player(owner)) {
            message_begin(MSG_ONE, get_user_msgid("TextMsg"), {0,0,0}, owner)
            write_byte(4)
            write_string("#Switch_To_BurstFire")
            message_end()
          }
        }
      }
      else if (previousMode != GLOCK_SEMIAUTOMATIC) {
        firemode = GLOCK_SEMIAUTOMATIC
        set_offset_int(weapon_id, OFFSET_SILENCER_FIREMODE, firemode, 4)

        new owner = entity_get_edict(weapon_id, EV_ENT_owner)
        if(is_player(owner)) {
          message_begin(MSG_ONE, get_user_msgid("TextMsg"), {0,0,0}, owner)
          write_byte(4)
          write_string("#Switch_To_SemiAuto")
          message_end()
        }
      }
    }
    case CSW_FAMAS: {
      if (burstmode) {
        if (previousMode != FAMAS_BURSTMODE) {
          firemode = FAMAS_BURSTMODE
          set_offset_int(weapon_id, OFFSET_SILENCER_FIREMODE, firemode, 4)

          new owner = entity_get_edict(weapon_id, EV_ENT_owner)
          if(is_player(owner)) {
            message_begin(MSG_ONE, get_user_msgid("TextMsg"), {0,0,0}, owner)
            write_byte(4)
            write_string("#Switch_To_BurstFire")
            message_end()
          }
        }
      }
      else if (previousMode != FAMAS_AUTOMATIC) {
        firemode = FAMAS_AUTOMATIC
        set_offset_int(weapon_id, OFFSET_SILENCER_FIREMODE, firemode, 4)

        new owner = entity_get_edict(weapon_id, EV_ENT_owner)
        if(is_player(owner)) {
          message_begin(MSG_ONE, get_user_msgid("TextMsg"), {0,0,0}, owner)
          write_byte(4)
          write_string("#Switch_To_FullAuto")
          message_end()
        }
      }
    }
    default:
      return 0
  }
  return 1
}

// Returns how much armor and sets reference of what type.
// params[1] = user index
// params[2] = byref, gets armor type (0:no armor/1:vest/2:vest+helmet)
enum CsArmorType {
	CS_ARMOR_NONE = 0, // no armor
	CS_ARMOR_KEVLAR = 1, // armor
	CS_ARMOR_VESTHELM = 2 // armor and helmet
};
stock cs_get_user_armor(id, &CsArmorType:armortype)
{
  if(cs_check_player(id, "cs_get_user_armor")) return 0

  switch(get_offset_int(id, OFFSET_ARMORTYPE))
  {
    case 0: armortype = CS_ARMOR_NONE
    case 1: armortype = CS_ARMOR_KEVLAR
    case 2: armortype = CS_ARMOR_VESTHELM
  }
  return floatround(entity_get_float(id, EV_FL_armorvalue))
}

// Sets armor and sets what type and send a message to client.
// params[1] = user index
// params[2] = armor value
// params[3] = armor type (0:no armor/1:vest/2:vest+helmet)
stock cs_set_user_armor(id, armorvalue, CsArmorType:armortype)
{
  if(cs_check_player(id, "cs_set_user_armor")) return 0

  switch(armortype) {
    case CS_ARMOR_NONE: set_offset_int(id, OFFSET_ARMORTYPE, 0)
    case CS_ARMOR_KEVLAR: set_offset_int(id, OFFSET_ARMORTYPE, 1)
    case CS_ARMOR_VESTHELM: set_offset_int(id, OFFSET_ARMORTYPE, 2)
  }
  entity_set_float(id, EV_FL_armorvalue, float(armorvalue))
  if(armortype == CS_ARMOR_KEVLAR || armortype == CS_ARMOR_VESTHELM)
  {
    message_begin(MSG_ONE, get_user_msgid("ArmorType"), {0,0,0}, id)
    write_byte((armortype == CS_ARMOR_VESTHELM) ? 1 : 0)
    message_end()
  }
  return 1
}

// Is user vip?
// params[1] = user index
stock cs_get_user_vip(id)
{
  if(cs_check_player(id, "cs_get_user_vip")) return 0

  if(get_offset_int(id, OFFSET_VIP) & PLAYER_IS_VIP) return 1
  return 0
}

// Sets user vip.
// params[1] = user index
// params[2] = if 1, activate vip, else deactivate vip
// params[3] = if 1, update model
// params[4] = if 1, update scoreboard with vip information
stock cs_set_user_vip(id, vip=1, model=1, scoreboard=1)
{
  if(cs_check_player(id, "cs_set_user_vip")) return 0
	
  if(vip == 1)
  {
    set_offset_int(id, OFFSET_VIP, get_offset_int(id, OFFSET_VIP) | PLAYER_IS_VIP)
    if(model == 1)
      set_user_model(id, "vip")
    if(scoreboard == 1)
    {
      message_begin(MSG_ALL, get_user_msgid("ScoreAttrib"))
      write_byte(id)
      write_byte(SCOREATTRIB_VIP)
      message_end()
    }
  }
  else
  {
    set_offset_int(id, OFFSET_VIP, get_offset_int(id, OFFSET_VIP) & ~PLAYER_IS_VIP)

    if(model == 1)
    {
      new CTmodels[4][] = {"urban", "gsg9", "sas", "gign"}
      set_user_model(id, CTmodels[random_num(0,3)])
    }
    
    if(scoreboard == 1)
    {
      new scoreattrib
      if(entity_get_int(id, EV_INT_deadflag) == DEAD_NO && get_user_health(id) > 0)
        scoreattrib = SCOREATTRIB_NOTHING
      else
        scoreattrib = SCOREATTRIB_DEAD
      message_begin(MSG_ALL, get_user_msgid("ScoreAttrib"))
      write_byte(id)
      write_byte(scoreattrib)
      message_end()
    }
  }
  return 1
}

// Gets user team.
// params[1] = user index
enum CsTeams {
  CS_TEAM_UNASSIGNED = 0,
	CS_TEAM_T = 1,
	CS_TEAM_CT = 2,
	CS_TEAM_SPECTATOR = 3
};
stock CsTeams:cs_get_user_team(id)
{
  if(cs_check_player(id, "cs_get_user_team")) return CS_TEAM_UNASSIGNED
  switch(get_offset_int(id, OFFSET_TEAM))
  {
    case 1: return CS_TEAM_T
    case 2: return CS_TEAM_CT
    case 3: return CS_TEAM_SPECTATOR
  }
  return CS_TEAM_UNASSIGNED
}

// Sets user team.
// params[1] = user index
// params[2] = team
// params[3] = model = 0
enum CsInternalModel {
	CS_DONTCHANGE = 0,
	CS_CT_URBAN = 1,
	CS_T_TERROR = 2,
	CS_T_LEET = 3,
	CS_T_ARCTIC = 4,
	CS_CT_GSG9 = 5,
	CS_CT_GIGN = 6,
	CS_CT_SAS = 7,
	CS_T_GUERILLA = 8,
	CS_CT_VIP = 9
};
stock cs_set_user_team(id, {CsTeams,_}:team, {CsInternalModel,_}:model = CS_DONTCHANGE)
{
  if(cs_check_player(id, "cs_set_user_team")) return 0

  switch(team)
  {
    case CS_TEAM_T: set_offset_int(id, OFFSET_TEAM, 1)
    case CS_TEAM_CT: set_offset_int(id, OFFSET_TEAM, 2)
    case CS_TEAM_SPECTATOR: set_offset_int(id, OFFSET_TEAM, 3)
  }
  switch(model)
  {
    case CS_CT_URBAN: {
      set_user_model(id, "urban")
    }
    case CS_T_TERROR: {
      set_user_model(id, "terror")
    }
    case CS_T_LEET: {
      set_user_model(id, "leet")
    }
    case CS_T_ARCTIC: {
      set_user_model(id, "arctic")
    }
    case CS_CT_GSG9: {
      set_user_model(id, "gsg9")
    }
    case CS_CT_GIGN: {
      set_user_model(id, "gign")
    }
    case CS_CT_SAS: {
      set_user_model(id, "sas")
    }
    case CS_T_GUERILLA: {
      set_user_model(id, "guerilla")
    }
    case CS_CT_VIP: {
      set_user_model(id, "vip")
    }
  }

  new teaminfo[32]
  switch(team)
  {
    case CS_TEAM_UNASSIGNED: {
      copy(teaminfo, 31, "UNASSIGNED")
    }
    case CS_TEAM_T: {
      copy(teaminfo, 31, "TERRORIST")
    }
    case CS_TEAM_CT: {
      copy(teaminfo, 31, "CT")
    }
    case CS_TEAM_SPECTATOR: {
      copy(teaminfo, 31, "SPECTATOR")
    }
  }
  message_begin(MSG_ALL, get_user_msgid("TeamInfo"))
  write_byte(id)
  write_string(teaminfo)
  message_end()
  return 1
}

// Is user inside buy zone?
// params[1] = user index
stock cs_get_user_inside_buyzone(id)
{
  if(cs_check_player(id, "cs_get_user_inside_buyzone")) return 0

  return (get_offset_int(id, OFFSET_MAPZONE) & PLAYER_IN_BUYZONE) ? 1 : 0
}

// Can user plant a bomb if he has one?
// params[1] = user index
stock cs_get_user_plant(id)
{
  if(cs_check_player(id, "cs_get_user_plant")) return 0

  if(get_offset_int(id, OFFSET_DEFUSE_PLANT) & CAN_PLANT_BOMB) return 1
  return 0
}

// Sets user plant "skill".
// params[1] = user index
// params[2] = 1 = able to
// params[3] = show bomb icon?
stock cs_set_user_plant(id, plant=1, showbombicon=1)
{
  if(cs_check_player(id, "cs_set_user_plant")) return 0

  new plantskill = get_offset_int(id, OFFSET_DEFUSE_PLANT)

  if(plant)
  {
    plantskill |= CAN_PLANT_BOMB
    set_offset_int(id, OFFSET_DEFUSE_PLANT, plantskill)
    if(showbombicon)
    {
      message_begin(MSG_ONE, get_user_msgid("StatusIcon"), {0,0,0}, id)
      write_byte(1)
      write_string("c4")
      write_byte(DEFUSER_COLOUR_R)
      write_byte(DEFUSER_COLOUR_G)
      write_byte(DEFUSER_COLOUR_B)
      message_end()
    }
  }
  else
  {
    plantskill &= ~CAN_PLANT_BOMB
    set_offset_int(id, OFFSET_DEFUSE_PLANT, plantskill)
    message_begin(MSG_ONE, get_user_msgid("StatusIcon"), {0,0,0}, id)
    write_byte(0)
    write_string("c4")
    message_end()
  }
  return 1
}

// Does user have defusekit?
// params[1] = user index
stock cs_get_user_defusekitkit(id)
{
  if(cs_check_player(id, "cs_get_user_defusekitkit")) return 0

  if(get_offset_int(id, OFFSET_DEFUSE_PLANT) & HAS_DEFUSE_KIT) return 1
  return 0
}

// Gives/removes defusekit.
// params[1] = user index
// params[2] = 1 = give
// params[3] = r
// params[4] = g
// params[5] = b
// params[6] = icon[]
// params[7] = flash = 0
stock cs_set_user_defusekit(id, defusekit=1, r=0, g=160, b=0, icon[]="defuser", flash=0)
{
  if(cs_check_player(id, "cs_set_user_defusekit")) return 0

  new defuse = get_offset_int(id, OFFSET_DEFUSE_PLANT)

  if(defusekit)
  {
    new colour[3] = {DEFUSER_COLOUR_R, DEFUSER_COLOUR_G, DEFUSER_COLOUR_B}
    if(r != -1) colour[0] = r
    if(g != -1) colour[1] = g
    if(b != -1) colour[2] = b

    defuse |= HAS_DEFUSE_KIT
    set_offset_int(id, OFFSET_DEFUSE_PLANT, defuse)
    message_begin(MSG_ONE, get_user_msgid("StatusIcon"), {0,0,0}, id)
    write_byte((flash == 1) ? 2 : 1)
    write_string(icon[0] ? icon : "defuser")
    write_byte(colour[0])
    write_byte(colour[1])
    write_byte(colour[2])
    message_end()
  }
  else
  {
    defuse &= ~HAS_DEFUSE_KIT
    set_offset_int(id, OFFSET_DEFUSE_PLANT, defuse)
    message_begin(MSG_ONE, get_user_msgid("StatusIcon"), {0,0,0}, id)
    write_byte(0)
    write_string("defuser")
    message_end()
  }
  return 1
}

// Gets amount of ammo in a user's backpack for a specific weapon type.
// params[1] = user index
// params[2] = weapon, as in CSW_*
stock cs_get_user_backpackammo(id, weapon)
{
  if(cs_check_player(id, "cs_get_user_backpackammo")) return 0

  new offset

  switch(weapon)
  {
    case CSW_AWP: {
      offset = OFFSET_AWM_AMMO
    }
    case CSW_SCOUT: {
      offset = OFFSET_SCOUT_AMMO
    }
    case CSW_AK47: {
      offset = OFFSET_SCOUT_AMMO
    }
    case CSW_G3SG1: {
      offset = OFFSET_SCOUT_AMMO
    }
    case CSW_M249: {
      offset = OFFSET_PARA_AMMO
    }
    case CSW_FAMAS: {
      offset = OFFSET_FAMAS_AMMO
    }
    case CSW_M4A1: {
      offset = OFFSET_FAMAS_AMMO
    }
    case CSW_AUG: {
      offset = OFFSET_FAMAS_AMMO
    }
    case CSW_SG550: {
      offset = OFFSET_FAMAS_AMMO
    }
    case CSW_GALIL: {
      offset = OFFSET_FAMAS_AMMO
    }
    case CSW_SG552: {
      offset = OFFSET_FAMAS_AMMO
    }
    case CSW_M3: {
      offset = OFFSET_M3_AMMO
    }
    case CSW_XM1014: {
      offset = OFFSET_M3_AMMO
    }
    case CSW_USP: {
      offset = OFFSET_USP_AMMO
    }
    case CSW_UMP45: {
      offset = OFFSET_USP_AMMO
    }
    case CSW_MAC10: {
      offset = OFFSET_USP_AMMO
    }
    case CSW_FIVESEVEN: {
      offset = OFFSET_FIVESEVEN_AMMO
    }
    case CSW_P90: {
      offset = OFFSET_FIVESEVEN_AMMO
    }
    case CSW_DEAGLE: {
      offset = OFFSET_DEAGLE_AMMO
    }
    case CSW_P228: {
      offset = OFFSET_P228_AMMO
    }
    case CSW_GLOCK18: {
      offset = OFFSET_GLOCK_AMMO
    }
    case CSW_MP5NAVY: {
      offset = OFFSET_GLOCK_AMMO
    }
    case CSW_TMP: {
      offset = OFFSET_GLOCK_AMMO
    }
    case CSW_ELITE: {
      offset = OFFSET_GLOCK_AMMO
    }
    case CSW_FLASHBANG: {
      offset = OFFSET_FLASH_AMMO
    }
    case CSW_HEGRENADE: {
      offset = OFFSET_HE_AMMO
    }
    case CSW_SMOKEGRENADE: {
      offset = OFFSET_SMOKE_AMMO
    }
    case CSW_C4: {
      offset = OFFSET_C4_AMMO
    }
    default: {
      reportError("cs_get_user_backpackammo: Invalid weapon id %d", weapon)
      return 0
    }
  }
  return get_offset_int(id, offset)
}

// Sets amount of ammo in a user's backpack for a specific weapon type.
// params[1] = user index
// params[2] = weapon, as in CSW_*
// params[3] = new amount
stock cs_set_user_backpackammo(id, weapon, amount)
{
  if(cs_check_player(id, "cs_set_user_backpackammo")) return 0

  new offset

  switch(weapon)
  {
    case CSW_AWP: {
      offset = OFFSET_AWM_AMMO
    }
    case CSW_SCOUT: {
      offset = OFFSET_SCOUT_AMMO
    }
    case CSW_AK47: {
      offset = OFFSET_SCOUT_AMMO
    }
    case CSW_G3SG1: {
      offset = OFFSET_SCOUT_AMMO
    }
    case CSW_M249: {
      offset = OFFSET_PARA_AMMO
    }
    case CSW_FAMAS: {
      offset = OFFSET_FAMAS_AMMO
    }
    case CSW_M4A1: {
      offset = OFFSET_FAMAS_AMMO
    }
    case CSW_AUG: {
      offset = OFFSET_FAMAS_AMMO
    }
    case CSW_SG550: {
      offset = OFFSET_FAMAS_AMMO
    }
    case CSW_GALIL: {
      offset = OFFSET_FAMAS_AMMO
    }
    case CSW_SG552: {
      offset = OFFSET_FAMAS_AMMO
    }
    case CSW_M3: {
      offset = OFFSET_M3_AMMO
    }
    case CSW_XM1014: {
      offset = OFFSET_M3_AMMO
    }
    case CSW_USP: {
      offset = OFFSET_USP_AMMO
    }
    case CSW_UMP45: {
      offset = OFFSET_USP_AMMO
    }
    case CSW_MAC10: {
      offset = OFFSET_USP_AMMO
    }
    case CSW_FIVESEVEN: {
      offset = OFFSET_FIVESEVEN_AMMO
    }
    case CSW_P90: {
      offset = OFFSET_FIVESEVEN_AMMO
    }
    case CSW_DEAGLE: {
      offset = OFFSET_DEAGLE_AMMO
    }
    case CSW_P228: {
      offset = OFFSET_P228_AMMO
    }
    case CSW_GLOCK18: {
      offset = OFFSET_GLOCK_AMMO
    }
    case CSW_MP5NAVY: {
      offset = OFFSET_GLOCK_AMMO
    }
    case CSW_TMP: {
      offset = OFFSET_GLOCK_AMMO
    }
    case CSW_ELITE: {
      offset = OFFSET_GLOCK_AMMO
    }
    case CSW_FLASHBANG: {
      offset = OFFSET_FLASH_AMMO
    }
    case CSW_HEGRENADE: {
      offset = OFFSET_HE_AMMO
    }
    case CSW_SMOKEGRENADE: {
      offset = OFFSET_SMOKE_AMMO
    }
    case CSW_C4: {
      offset = OFFSET_C4_AMMO
    }
    default: {
      reportError("cs_set_user_backpackammo: Invalid weapon id %d", weapon)
      return 0
    }
  }
  set_offset_int(id, offset, amount)
  return 1
}

// Does user have night vision goggles?
// params[1] = user index
stock cs_get_user_nvg(id)
{
  if(cs_check_player(id, "cs_get_user_nvg")) return 0

  if(get_offset_int(id, OFFSET_NVGOGGLES) & HAS_NVGOGGLES) return 1
  return 0
}

// Gives/removes nvgoggles.
// params[1] = user index
// params[2] = 1 = give, 0 = remove
stock cs_set_user_nvg(id, nvgoggles=1)
{
  if(cs_check_player(id, "cs_set_user_nvg")) return 0

  new defusekit = get_offset_int(id, OFFSET_NVGOGGLES)

  if(nvgoggles)
  {
    if (defusekit & HAS_NVGOGGLES)
    {
      message_begin(MSG_ONE, get_user_msgid("TextMsg"), {0,0,0}, id)
      write_byte(4)
      write_string("#Already_Have_One")
      message_end()
    }
    else
    {
      defusekit |= HAS_NVGOGGLES
      set_offset_int(id, OFFSET_NVGOGGLES, defusekit)
    }
  }
  else
  {
    defusekit &= ~HAS_NVGOGGLES
    set_offset_int(id, OFFSET_NVGOGGLES, defusekit)
  }
  return 1
}

// Gets model a player has.
// params[1] = user index
// params[2] = model
// params[3] = max length to set
stock cs_get_user_model(id, model[], len)
{
  if(cs_check_player(id, "cs_get_user_model")) return 0

  return entity_get_string(id, EV_SZ_model, model, len)
}

// Sets model on player.
// params[1] = user index
// params[2] = model
stock cs_set_user_model(id, const model[])
{
  if(cs_check_player(id, "cs_set_user_model")) return 0

  set_user_model(id, model)
  return 1
}

// Resets model on player.
// params[1] = user index
stock cs_reset_user_model(id)
{
  if(cs_check_player(id, "cs_reset_user_model")) return 0

  set_user_model(id, "")
  return 1
}

// Gets unique id of a CS hostage.
// params[1] = hostage entity index
stock cs_get_hostage_id(hostage_id)
{
  if(cs_check_entity(hostage_id, "cs_get_hostage_id")) return 0

  new classname[32]
  entity_get_string(hostage_id, EV_SZ_classname, classname, 31)
  if(!equal(classname, "hostage_entity"))
  {
    reportError("cs_get_hostage_id: Entity %d (^"%s^") is not a hostage", hostage_id, classname)
    return 0
  }
  return get_offset_int(hostage_id, OFFSET_HOSTAGEID)
}

// What index is the hostage following? (this doesn't have to be a player)
// params[1] = hostage index
stock cs_get_hostage_follow(hostage_id)
{
  if(cs_check_entity(hostage_id, "cs_get_hostage_follow")) return 0

  new classname[32]
  entity_get_string(hostage_id, EV_SZ_classname, classname, 31)
  if(!equal(classname, "hostage_entity"))
  {
    reportError("cs_get_hostage_follow: Entity %d (^"%s^") is not a hostage", hostage_id, classname)
    return 0
  }

  new following = get_offset_int(hostage_id, OFFSET_HOSTAGEFOLLOW)
  if(following == 0) return following

  return pointer_to_index(following)
}

// What index should the hostage be following? (this doesn't have to be a player)
// params[1] = hostage index
// params[2] = index to follow, if 0 then set hostage to not follow anything
stock cs_set_hostage_follow(hostage_id, followedid=0)
{
  if(cs_check_entity(hostage_id, "cs_set_hostage_follow")) return 0

  new classname[32]
  entity_get_string(hostage_id, EV_SZ_classname, classname, 31)
  if(!equal(classname, "hostage_entity"))
  {
    reportError("cs_set_hostage_follow: Entity %d (^"%s^") is not a hostage", hostage_id, classname)
    return 0
  }

  if(followedid == 0)
  {
    set_offset_int(hostage_id, OFFSET_HOSTAGEFOLLOW, 0)
    return 1
  }

  if(cs_check_entity(followedid, "cs_set_hostage_follow")) return 0

  set_offset_int(hostage_id, OFFSET_HOSTAGEFOLLOW, index_to_pointer(followedid))
  return 1
}

// Gets amount of ammo in weapon's clip.
// params[1] = weapon index
stock cs_get_weapon_ammo(weapon_id)
{
  if(cs_check_entity(weapon_id, "cs_get_weapon_ammo")) return 0

  return get_offset_int(weapon_id, OFFSET_CLIPAMMO, 4)
}

// Sets amount of ammo in weapon's clip.
// params[1] = weapon index
// params[2] = newammo
stock cs_set_weapon_ammo(weapon_id, newammo)
{
  if(cs_check_entity(weapon_id, "cs_set_weapon_ammo")) return 0

  set_offset_int(weapon_id, OFFSET_CLIPAMMO, newammo, 4)
  return 1
}

// Returns 1 if user has a primary or shield (actually just returns the value at the offset).
// params[1] = user index
stock cs_get_user_hasprimary(id)
{
  if(cs_check_player(id, "cs_get_user_hasprimary")) return 0

  return get_offset_int(id, OFFSET_PRIMARYWEAPON)
}

// Returns 1 if user has committed a team killing.
// params[1] = user index
stock cs_get_user_tked(id)
{
  if(cs_check_player(id, "cs_get_user_tked")) return 0

  return get_offset_int(id, OFFSET_TK)
}

// Sets whether or not player has committed a TK.
// params[1] = user index
// params[2] = 1: player has TKed, 0: player hasn't TKed
// params[3] = number of frags to subtract
stock cs_set_user_tked(id, tk=1, subtract=1)
{
  if(cs_check_player(id, "cs_set_user_tked")) return 0

  if(tk) set_offset_int(id, OFFSET_TK, 1)
  else set_offset_int(id, OFFSET_TK, 0)

  if(subtract)
  {
    new frags = get_user_frags(id) - subtract
    entity_set_float(id, EV_FL_frags, float(frags))

    message_begin(MSG_ALL, get_user_msgid("ScoreInfo"))
    write_byte(id)
    write_short(frags)
    write_short(get_user_deaths(id))
    write_short(0)
    write_short(get_user_team(id))
    message_end()
  }
  return 1
}

// Returns different values depending on if user is driving a value - and if so at what speed.
// 0: no driving
// 1: driving, but standing still
// 2-4: different positive speeds
// 5: negative speed (backing)
// params[1] = user index
stock cs_get_user_driving(id)
{
  if(cs_check_player(id, "cs_get_user_driving")) return 0

  return get_offset_int(id, OFFSET_ISDRIVING)
}

// Returns 1 if client is using a stationary gun (maybe also other stuff)
// params[1] = user index
stock cs_get_user_stationary(id)
{
  if(cs_check_player(id, "cs_get_user_stationary")) return 0

  return get_offset_int(id, OFFSET_STATIONARY)
}

// Returns 1 if user has a shield.
// params[1] = user index
stock cs_get_user_shield(id)
{
  if(cs_check_player(id, "cs_get_user_shield")) return 0

  if (get_offset_int(id, OFFSET_SHIELD) & HAS_SHIELD) return 1
  return 0
}

// Spawns a Counter-Strike player.
// params[1] = user index
stock cs_user_spawn(id)
{
  if(cs_check_player(id, "cs_user_spawn")) return 0

  entity_set_int(id, EV_INT_spawnflags, DEAD_RESPAWNABLE)
  DispatchSpawn(id)
  entity_set_int(id, EV_INT_iuser1, 0)
  return 1
}

// Return CSW_* constants of specified armoury_entity.
// params[1] = entity
stock cs_get_armoury_type(ent_id)
{
  if(cs_check_entity(ent_id, "cs_get_armoury_type")) return 0

  new classname[32]
  entity_get_string(ent_id, EV_SZ_classname, classname, 31)
  if(!equal(classname, "armoury_entity")) {
    reportError("cs_get_armoury_type: Entity %d (^"%s^") is not an armoury_entity", ent_id, classname)
    return 0
  }

  new weapontype = get_offset_int(ent_id, OFFSET_ARMOURY_TYPE, 4)

  new weapontype_out
  switch(weapontype)
  {
    case CSA_MP5NAVY:       weapontype_out = CSW_MP5NAVY
    case CSA_TMP:           weapontype_out = CSW_TMP
    case CSA_P90:           weapontype_out = CSW_P90
    case CSA_MAC10:         weapontype_out = CSW_MAC10
    case CSA_AK47:          weapontype_out = CSW_AK47
    case CSA_SG552:         weapontype_out = CSW_SG552
    case CSA_M4A1:          weapontype_out = CSW_M4A1
    case CSA_AUG:           weapontype_out = CSW_AUG
    case CSA_SCOUT:         weapontype_out = CSW_SCOUT
    case CSA_G3SG1:         weapontype_out = CSW_G3SG1
    case CSA_AWP:           weapontype_out = CSW_AWP
    case CSA_M3:            weapontype_out = CSW_M3
    case CSA_XM1014:        weapontype_out = CSW_XM1014
    case CSA_M249:          weapontype_out = CSW_M249
    case CSA_FLASHBANG:     weapontype_out = CSW_FLASHBANG
    case CSA_HEGRENADE:     weapontype_out = CSW_HEGRENADE
    case CSA_VEST:          weapontype_out = CSW_VEST
    case CSA_VESTHELM:      weapontype_out = CSW_VESTHELM
    case CSA_SMOKEGRENADE:  weapontype_out = CSW_SMOKEGRENADE
    default: {
      reportError("cs_get_armoury_type: Unexpected weapon type of %d", ent_id)
      return 0
    }
  }

  return weapontype_out
}

// Set an armoury_entity to be of specified type.
// You will have to set the appropriate model.
// The second argument, type, should be a CSW_* constant. Not all weapons are supported by Counter-strike.
// Supported weapons/items: CSW_MP5NAVY, CSW_TMP, CSW_P90, CSW_MAC10, CSW_AK47,
// CSW_SG552, CSW_M4A1, CSW_AUG, CSW_SCOUT, CSW_G3SG1, CSW_AWP, CSW_M3, CSW_XM1014,
// CSW_M249, CSW_FLASHBANG, CSW_HEGRENADE, CSW_VEST, CSW_VESTHELM, CSW_SMOKEGRENADE
// params[1] = entity
// params[2] = CSW_* constant
stock cs_set_armoury_type(ent_id, type)
{
  if(cs_check_entity(ent_id, "cs_set_armoury_type")) return 0

  new classname[32]
  entity_get_string(ent_id, EV_SZ_classname, classname, 31)
  if(!equal(classname, "armoury_entity")) {
    reportError("cs_set_armoury_type: Entity %d (^"%s^") is not an armoury_entity", ent_id, classname)
    return 0
  }

  new weapontype
  switch(type)
  {
    case CSW_MP5NAVY:       weapontype = CSA_MP5NAVY
    case CSW_TMP:           weapontype = CSA_TMP
    case CSW_P90:           weapontype = CSA_P90
    case CSW_MAC10:         weapontype = CSA_MAC10
    case CSW_AK47:          weapontype = CSA_AK47
    case CSW_SG552:         weapontype = CSA_SG552
    case CSW_M4A1:          weapontype = CSA_M4A1
    case CSW_AUG:           weapontype = CSA_AUG
    case CSW_SCOUT:         weapontype = CSA_SCOUT
    case CSW_G3SG1:         weapontype = CSA_G3SG1
    case CSW_AWP:           weapontype = CSA_AWP
    case CSW_M3:            weapontype = CSA_M3
    case CSW_XM1014:        weapontype = CSA_XM1014
    case CSW_M249:          weapontype = CSA_M249
    case CSW_FLASHBANG:     weapontype = CSA_FLASHBANG
    case CSW_HEGRENADE:     weapontype = CSA_HEGRENADE
    case CSW_VEST:          weapontype = CSA_VEST
    case CSW_VESTHELM:      weapontype = CSA_VESTHELM
    case CSW_SMOKEGRENADE:  weapontype = CSA_SMOKEGRENADE
    default: {
      reportError("cs_set_armoury_type: Unsupported weapon type: %d", type)
      return 0
    }
  }

  set_offset_int(ent_id, OFFSET_ARMOURY_TYPE, weapontype, 4)

  return 1
}

// Is user inside a special zone?
// params[1] = user index
stock cs_get_user_mapzones(id) {
  if(cs_check_player(id, "cs_get_user_mapzones")) return 0
  
  return get_offset_int(id, OFFSET_MAPZONE)
}
