[
  {
    "path": ".editorconfig",
    "content": "\nroot = true\n\n[*]\nend_of_line = crlf\ninsert_final_newline = true\ncharset = utf-8\nindent_style = tab\nindent_size = 4\n\n[.travis.yml]\nindent_style = space\nindent_size = 2\n"
  },
  {
    "path": ".github/workflows/ci.yml",
    "content": "\nname: CI\non: [push, pull_request]\n\njobs:\n  linter:\n    runs-on: ubuntu-latest\n\n    steps:\n    - uses: actions/checkout@v2\n      with:\n        path: helix\n\n    - uses: leafo/gh-actions-lua@v8.0.0\n      with:\n        luaVersion: \"5.2\"\n\n    - uses: leafo/gh-actions-luarocks@v4.0.0\n\n    - name: Pull gluacheck\n      uses: actions/checkout@v2\n      with:\n        repository: impulsh/gluacheck\n        path: luacheck\n\n    - name: Build gluacheck\n      working-directory: luacheck\n      run: luarocks make\n\n    - name: Lint\n      working-directory: helix\n      run: luacheck .\n\n  docs:\n    runs-on: ubuntu-latest\n\n    steps:\n      - uses: actions/checkout@v2\n        with:\n          path: helix\n\n      - uses: leafo/gh-actions-lua@v8.0.0\n        with:\n          luaVersion: \"5.2\"\n\n      - uses: leafo/gh-actions-luarocks@v4.0.0\n\n      - name: Pull LDoc\n        uses: actions/checkout@v2\n        with:\n          repository: impulsh/LDoc\n          path: ldoc\n\n      - name: Build LDoc\n        working-directory: ldoc\n        run: luarocks make\n\n      - name: Build docs\n        working-directory: helix\n        run: ldoc . --fatalwarnings\n\n      - name: Copy assets\n        working-directory: helix\n        run: |\n          cp -v docs/css/* docs/html\n          cp -v docs/js/* docs/html\n\n      - name: Deploy\n        if: github.event_name == 'push' && github.ref == 'refs/heads/master' && github.repository == 'NebulousCloud/helix' && success()\n        uses: peaceiris/actions-gh-pages@v3\n        with:\n          github_token: ${{ secrets.GITHUB_TOKEN }}\n          publish_dir: helix/docs/html\n          cname: docs.gethelix.co\n"
  },
  {
    "path": ".gitignore",
    "content": "\n*.sublime-*\ngamemode/config/sv_database.lua\ndocs/html\n.vscode/*\n.DS_Store\nhelix.yml\n"
  },
  {
    "path": ".luacheckrc",
    "content": "\nmax_line_length = 128\nstd = \"luajit+gmod+helix\"\nignore = {\n\t\"212\", -- unused argument\n}\n\n-- helix\nstds.helix = {}\nstds.helix.globals = {\n\t\"ix\",\n\t\"Schema\",\n\n\t\"ITEM\",\n\t\"PLUGIN\",\n\t\"ATTRIBUTE\",\n\t\"NAME\",\n\t\"LANGUAGE\",\n\t\"FACTION\",\n\t\"CLASS\",\n\t\"CHAT_RECOGNIZED\",\n\t\"ALWAYS_RAISED\",\n\t\"ICON_RENDER_QUEUE\",\n\t\"USABLE_FUNCS\",\n}\nstds.helix.read_globals = {\n\t\"L\",\n\t\"L2\",\n\t\"IX_RELOADED\",\n\t\"CHAT_CLASS\",\n\t\"HOOKS_CACHE\",\n\t\"BAR_HEIGHT\",\n\t\"ACCESS_LABELS\",\n\t\"CAMI\",\n\n\t\"netstream\",\n\t\"mysql\",\n\t\"pon\",\n\t\"ikon\",\n\t\"BaseClass\",\n\n\t\"SetNetVar\",\n\t\"GetNetVar\",\n\t\"ixSoundDuration\",\n\n\t\"HOLDTYPE_TRANSLATOR\",\n\t\"PLAYER_HOLDTYPE_TRANSLATOR\",\n\n\t\"ACT_VM_FISTS_DRAW\",\n\t\"ACT_VM_FISTS_HOLSTER\",\n\n\t\"TOOLTIP_GENERIC\",\n\t\"TOOLTIP_ITEM\",\n\n\t\"FLAG_NORMAL\",\n\t\"FLAG_SUCCESS\",\n\t\"FLAG_WARNING\",\n\t\"FLAG_DANGER\",\n\t\"FLAG_SERVER\",\n\t\"FLAG_DEV\",\n\n\t\"DOOR_OWNER\",\n\t\"DOOR_TENANT\",\n\t\"DOOR_GUEST\",\n\t\"DOOR_NONE\",\n\n\t\"VENDOR_BUY\",\n\t\"VENDOR_SELL\",\n\t\"VENDOR_BOTH\",\n\t\"VENDOR_WELCOME\",\n\t\"VENDOR_LEAVE\",\n\t\"VENDOR_NOTRADE\",\n\t\"VENDOR_PRICE\",\n\t\"VENDOR_STOCK\",\n\t\"VENDOR_MODE\",\n\t\"VENDOR_MAXSTOCK\",\n\t\"VENDOR_SELLANDBUY\",\n\t\"VENDOR_SELLONLY\",\n\t\"VENDOR_BUYONLY\",\n\t\"VENDOR_TEXT\",\n\n\t\"FCAP_IMPULSE_USE\",\n\t\"FCAP_CONTINUOUS_USE\",\n\t\"FCAP_ONOFF_USE\",\n\t\"FCAP_DIRECTIONAL_USE\",\n\t\"FCAP_USE_ONGROUND\",\n\t\"FCAP_USE_IN_RADIUS\",\n}\n\nfiles = {\n\t-- some phrases are unavoidably long, so we'll ignore the max line length for language files\n\t[\"gamemode/languages/**/*.lua\"] = {\n\t\tignore = {\n\t\t\t\"631\"\n\t\t}\n\t},\n\n\t[\"plugins/**/languages/*.lua\"] = {\n\t\tignore = {\n\t\t\t\"631\"\n\t\t}\n\t}\n}\n\n-- ignore third party files\nexclude_files = {\n\t\"gamemode/core/libs/thirdparty/**/*.lua\"\n}\n\n-- gmod\nstds.gmod = {}\nstds.gmod.globals = {\n\t\"GM\",\n\t\"ENT\",\n\t\"TOOL\",\n\t\"SWEP\"\n}\nstds.gmod.read_globals = {\n\t\"VERSION\",\n\t\"CLIENT\",\n\t\"SERVER\",\n\t\"GAMEMODE\",\n\t\"NULL\",\n\n\t\"vector_origin\",\n\t\"vector_up\",\n\t\"angle_zero\",\n\t\"color_white\",\n\t\"color_black\",\n\t\"color_transparent\",\n\n\t\"PLAYERANIMEVENT_CANCEL_RELOAD\",\n\t\"ACT_COMBINE_THROW_GRENADE\",\n\n\t-- Generated on Wed Jan 17 02:56:57 2018\n\t\"ACT_MP_GESTURE_VC_NODYES\",\n\t\"ACT_MELEE_ATTACK_SWING_GESTURE\",\n\t\"SCHED_TAKE_COVER_FROM_ORIGIN\",\n\t\"ACT_BUSY_SIT_GROUND_ENTRY\",\n\t\"ACT_DOD_CROUCH_IDLE_C96\",\n\t\"ACT_IDLE_STEALTH\",\n\t\"ACT_DOD_PRIMARYATTACK_DEPLOYED\",\n\t\"ACT_MP_STAND_PRIMARY\",\n\t\"ACT_DOD_CROUCHWALK_AIM\",\n\t\"ACT_DOD_RELOAD_PRONE_DEPLOYED_BAR\",\n\t\"ACT_GLOCK_SHOOT_RELOAD\",\n\t\"TEXT_ALIGN_CENTER\",\n\t\"DMG_CRUSH\",\n\t\"ScrH\",\n\t\"ACT_DOD_RELOAD_CROUCH_BAR\",\n\t\"ACT_HL2MP_JUMP_PASSIVE\",\n\t\"ACT_MP_GESTURE_VC_HANDMOUTH_SECONDARY\",\n\t\"ACT_MP_JUMP_START_MELEE\",\n\t\"ACT_GET_UP_CROUCH\",\n\t\"ACT_DOD_CROUCHWALK_IDLE_GREASE\",\n\t\"ACT_DOD_CROUCHWALK_IDLE_PISTOL\",\n\t\"ACT_HL2MP_WALK_CROUCH_SUITCASE\",\n\t\"ACT_HL2MP_GESTURE_RELOAD_SLAM\",\n\t\"SIMPLE_USE\",\n\t\"ACT_DOD_WALK_AIM_GREN_STICK\",\n\t\"ACT_DOD_PRONEWALK_IDLE_MG\",\n\t\"FL_FLY\",\n\t\"ACT_HL2MP_IDLE_CROUCH_REVOLVER\",\n\t\"presets\",\n\t\"ACT_MP_RUN_MELEE\",\n\t\"halo\",\n\t\"HULL_WIDE_SHORT\",\n\t\"video\",\n\t\"SND_SPAWNING\",\n\t\"ACT_MP_GRENADE2_DRAW\",\n\t\"ACT_OVERLAY_PRIMARYATTACK\",\n\t\"COND_SEE_NEMESIS\",\n\t\"KEY_O\",\n\t\"ACT_OVERLAY_GRENADEREADY\",\n\t\"ACT_COVER_MED\",\n\t\"AddConsoleCommand\",\n\t\"ACT_VM_IIDLE_M203\",\n\t\"ACT_HL2MP_JUMP_RPG\",\n\t\"ACT_DOD_STAND_AIM_MG\",\n\t\"MAT_VENT\",\n\t\"ACT_VM_HITRIGHT2\",\n\t\"ACT_VM_RELOAD_EMPTY\",\n\t\"DSprite\",\n\t\"BONE_CALCULATE_MASK\",\n\t\"GetGlobalVector\",\n\t\"ACT_HL2MP_IDLE_PISTOL\",\n\t\"SetGlobalVector\",\n\t\"HTTP\",\n\t\"WorldToLocal\",\n\t\"ACT_MP_CROUCH_IDLE\",\n\t\"COLLISION_GROUP_VEHICLE\",\n\t\"KEY_XBUTTON_B\",\n\t\"ACT_PICKUP_RACK\",\n\t\"CAP_MOVE_CRAWL\",\n\t\"ACT_DOD_CROUCHWALK_AIM_BOLT\",\n\t\"ACT_MP_MELEE_GRENADE2_DRAW\",\n\t\"ACT_DIE_BARNACLE_SWALLOW\",\n\t\"ACT_WALK_STEALTH_PISTOL\",\n\t\"ACT_DOD_PRONE_AIM_MP44\",\n\t\"ACT_PRONE_FORWARD\",\n\t\"COND_ENEMY_OCCLUDED\",\n\t\"ACT_DOD_RUN_IDLE_BOLT\",\n\t\"GetConVarNumber\",\n\t\"ACT_DOD_CROUCHWALK_AIM_RIFLE\",\n\t\"ACT_DOD_RELOAD_RIFLEGRENADE\",\n\t\"ACT_VM_IIN_M203\",\n\t\"ACT_MP_ATTACK_AIRWALK_BUILDING\",\n\t\"FVPHYSICS_DMG_DISSOLVE\",\n\t\"ACT_INVALID\",\n\t\"ACT_DOD_RELOAD_PRONE_MP44\",\n\t\"ACT_SLAM_TRIPMINE_DRAW\",\n\t\"ACT_MP_GESTURE_VC_NODNO\",\n\t\"SetGlobalAngle\",\n\t\"ACT_DOD_SPRINT_IDLE_BOLT\",\n\t\"ACT_MP_PRIMARY_GRENADE1_IDLE\",\n\t\"ACT_VM_PRIMARYATTACK_DEPLOYED_1\",\n\t\"ClientsideScene\",\n\t\"MASK_SHOT\",\n\t\"DTree_Node_Button\",\n\t\"GetGlobalAngle\",\n\t\"MOVETYPE_ISOMETRIC\",\n\t\"ACT_GESTURE_RANGE_ATTACK_SMG1_LOW\",\n\t\"NAV_MESH_STOP\",\n\t\"COND_NONE\",\n\t\"ACT_SHIELD_ATTACK\",\n\t\"ACT_GESTURE_RANGE_ATTACK_SMG2\",\n\t\"CNavArea\",\n\t\"ACT_HL2MP_WALK\",\n\t\"include\",\n\t\"ACT_WALK_ANGRY\",\n\t\"ACT_DOD_PRIMARYATTACK_PRONE_PISTOL\",\n\t\"ACT_MP_GESTURE_VC_FINGERPOINT_BUILDING\",\n\t\"DDrawer\",\n\t\"ACT_VM_IRECOIL1\",\n\t\"BOX_TOP\",\n\t\"ACT_MP_RUN_PDA\",\n\t\"ACT_DOD_RELOAD_BOLT\",\n\t\"SaveLastMap\",\n\t\"ACT_DOD_RUN_ZOOM_BOLT\",\n\t\"ACT_MP_RELOAD_AIRWALK_LOOP\",\n\t\"ACT_DEEPIDLE1\",\n\t\"COND_SEE_ENEMY\",\n\t\"DColorPalette\",\n\t\"DMG_DIRECT\",\n\t\"Derma_Message\",\n\t\"DPanelSelect\",\n\t\"ACT_HL2MP_ZOMBIE_SLUMP_RISE\",\n\t\"FVPHYSICS_NO_IMPACT_DMG\",\n\t\"CLASS_CONSCRIPT\",\n\t\"CONTINUOUS_USE\",\n\t\"STUDIO_TRANSPARENCY\",\n\t\"NOTIFY_UNDO\",\n\t\"ACT_MP_ATTACK_CROUCH_GRENADE_PRIMARY\",\n\t\"ACT_SMG2_FIRE2\",\n\t\"ACT_RANGE_ATTACK_AR1\",\n\t\"ACT_MP_DEPLOYED_IDLE\",\n\t\"ACT_MP_ATTACK_SWIM_PRIMARYFIRE\",\n\t\"kRenderFxEnvSnow\",\n\t\"COND_WAY_CLEAR\",\n\t\"MATERIAL_LINE_STRIP\",\n\t\"SetGlobalString\",\n\t\"ACT_IDLE_RELAXED\",\n\t\"ACT_VM_PRIMARYATTACK\",\n\t\"ACT_HL2MP_GESTURE_RANGE_ATTACK_DUEL\",\n\t\"ACT_VM_DEPLOY_EMPTY\",\n\t\"ACT_VM_IDLE_EMPTY\",\n\t\"Matrix\",\n\t\"ACT_DOD_SECONDARYATTACK_PRONE_TOMMY\",\n\t\"COND_WEAPON_SIGHT_OCCLUDED\",\n\t\"ACT_DOD_PRIMARYATTACK_KNIFE\",\n\t\"ACT_VM_RELOAD_END_EMPTY\",\n\t\"menu\",\n\t\"CLASS_SCANNER\",\n\t\"ACT_MP_CROUCHWALK_SECONDARY\",\n\t\"ACT_DOD_SECONDARYATTACK_BOLT\",\n\t\"SF_NPC_ALWAYSTHINK\",\n\t\"ACT_DOD_WALK_IDLE_TNT\",\n\t\"ACT_HL2MP_WALK_ZOMBIE\",\n\t\"ACT_IDLE_SMG1_RELAXED\",\n\t\"TextEntry\",\n\t\"ACT_DOD_STAND_ZOOM_RIFLE\",\n\t\"COND_HEAR_BUGBAIT\",\n\t\"COND_WEAPON_PLAYER_NEAR_TARGET\",\n\t\"LoadPresets\",\n\t\"IN_ALT2\",\n\t\"ACT_MP_SWIM_DEPLOYED\",\n\t\"SetGlobalInt\",\n\t\"DrawSunbeams\",\n\t\"ACT_MP_ATTACK_SWIM_SECONDARY\",\n\t\"KEY_4\",\n\t\"ACT_MP_GESTURE_VC_FISTPUMP_MELEE\",\n\t\"ACT_DOD_RUN_IDLE_PSCHRECK\",\n\t\"ACT_PICKUP_GROUND\",\n\t\"ACT_WALK_RPG\",\n\t\"ACT_BUSY_SIT_CHAIR\",\n\t\"ACT_DOD_CROUCHWALK_AIM_KNIFE\",\n\t\"TYPE_COLOR\",\n\t\"ACT_DOD_CROUCH_IDLE_30CAL\",\n\t\"ACT_DOD_PRONEWALK_AIM_SPADE\",\n\t\"properties\",\n\t\"ACT_HL2MP_IDLE_RPG\",\n\t\"achievements\",\n\t\"CAP_WEAPON_MELEE_ATTACK2\",\n\t\"ACT_VM_UNLOAD\",\n\t\"ACT_VM_DRAW_EMPTY\",\n\t\"LocalPlayer\",\n\t\"ACT_ZOMBIE_CLIMB_UP\",\n\t\"SURF_WARP\",\n\t\"MAT_SLOSH\",\n\t\"ACT_DOD_WALK_ZOOM_PSCHRECK\",\n\t\"FSOLID_TRIGGER_TOUCH_DEBRIS\",\n\t\"ACT_MP_JUMP_LAND_MELEE\",\n\t\"PLAYERANIMEVENT_SWIM\",\n\t\"ACT_DOD_RELOAD_PSCHRECK\",\n\t\"ACT_DOD_RELOAD_CROUCH\",\n\t\"PLAYER\",\n\t\"ACT_DOD_RELOAD_CROUCH_MP44\",\n\t\"table\",\n\t\"ACT_HL2MP_FIST_BLOCK\",\n\t\"ACT_VM_PRIMARYATTACK_EMPTY\",\n\t\"ACT_RANGE_AIM_PISTOL_LOW\",\n\t\"Panel\",\n\t\"BONE_USED_BY_VERTEX_MASK\",\n\t\"ACT_MP_ATTACK_CROUCH_PRIMARY\",\n\t\"KEY_PAD_8\",\n\t\"ACT_CROUCHING_SHIELD_UP\",\n\t\"ipairs\",\n\t\"KEY_LALT\",\n\t\"ACT_DIEVIOLENT\",\n\t\"ACT_DOD_PRIMARYATTACK_MP40\",\n\t\"KEY_PAD_PLUS\",\n\t\"KEY_LBRACKET\",\n\t\"MsgAll\",\n\t\"ACT_DOD_CROUCHWALK_IDLE_MP44\",\n\t\"GetGlobalInt\",\n\t\"ACT_CROUCHING_SHIELD_DOWN\",\n\t\"NUM_BEAMS\",\n\t\"COND_CAN_MELEE_ATTACK1\",\n\t\"COND_LOST_ENEMY\",\n\t\"ACT_RUN_AIM_RIFLE\",\n\t\"ACT_VM_PULLBACK_HIGH\",\n\t\"DrawBackground\",\n\t\"TEXT_ALIGN_BOTTOM\",\n\t\"ACT_HL2MP_WALK_AR2\",\n\t\"ACT_RUN_PROTECTED\",\n\t\"CreateParticleSystem\",\n\t\"ACT_HL2MP_JUMP_DUEL\",\n\t\"ACT_DOD_CROUCHWALK_AIM_BAR\",\n\t\"ACT_VM_HOLSTERFULL_M203\",\n\t\"ACT_DOD_PRONEWALK_IDLE_PSCHRECK\",\n\t\"ACT_DOD_PRIMARYATTACK_PRONE_MP44\",\n\t\"ACT_STARTDYING\",\n\t\"TYPE_BOOL\",\n\t\"MOVETYPE_NOCLIP\",\n\t\"FVPHYSICS_PENETRATING\",\n\t\"NPC_STATE_INVALID\",\n\t\"STUDIO_TWOPASS\",\n\t\"ACT_VM_MISSRIGHT2\",\n\t\"KEY_E\",\n\t\"FORCE_STRING\",\n\t\"HITGROUP_LEFTLEG\",\n\t\"ACT_GET_DOWN_STAND\",\n\t\"ACT_HL2MP_SWIM_REVOLVER\",\n\t\"ACT_DOD_PRONE_ZOOM_FORWARD_RIFLE\",\n\t\"ACT_DOD_PRONEWALK_IDLE_MP40\",\n\t\"ACT_MP_GESTURE_VC_NODNO_SECONDARY\",\n\t\"BLEND_ZERO\",\n\t\"spawnmenu\",\n\t\"D_NU\",\n\t\"KEY_PAD_0\",\n\t\"ACT_MP_RUN_SECONDARY\",\n\t\"ACT_DIE_BACKSHOT\",\n\t\"ACT_WALK_CROUCH_RIFLE\",\n\t\"ACT_MP_ATTACK_STAND_GRENADE_PRIMARY\",\n\t\"ACT_HL2MP_GESTURE_RELOAD_REVOLVER\",\n\t\"ACT_CROSSBOW_IDLE_UNLOADED\",\n\t\"IMAGE_FORMAT_RGBA16161616\",\n\t\"ACT_DOD_CROUCHWALK_ZOOM_BOLT\",\n\t\"STENCILCOMPARISONFUNCTION_LESS\",\n\t\"ACT_DOD_SPRINT_AIM_KNIFE\",\n\t\"ACT_BARNACLE_PULL\",\n\t\"CONTENTS_TESTFOGVOLUME\",\n\t\"SNDLVL_45dB\",\n\t\"ACT_DIE_LEFTSIDE\",\n\t\"kRenderFxEnvRain\",\n\t\"ACT_GMOD_GESTURE_DISAGREE\",\n\t\"ACT_MP_JUMP_LAND_PRIMARY\",\n\t\"SQLStr\",\n\t\"ACT_OVERLAY_SHIELD_ATTACK\",\n\t\"SCHED_ALERT_WALK\",\n\t\"ACT_SIGNAL_ADVANCE\",\n\t\"DImageButton\",\n\t\"search\",\n\t\"ACT_DOD_RELOAD_CROUCH_RIFLEGRENADE\",\n\t\"KEY_XSTICK1_DOWN\",\n\t\"FVPHYSICS_NO_NPC_IMPACT_DMG\",\n\t\"RENDERGROUP_TRANSLUCENT\",\n\t\"DrawTexturize\",\n\t\"ACT_HANDGRENADE_THROW3\",\n\t\"ACT_OVERLAY_SHIELD_UP\",\n\t\"HITGROUP_CHEST\",\n\t\"BONE_ALWAYS_PROCEDURAL\",\n\t\"ACT_DOD_PRIMARYATTACK_PRONE_SPADE\",\n\t\"ACT_HL2MP_JUMP_SMG1\",\n\t\"ACT_DOD_STAND_AIM_MP40\",\n\t\"CreateMaterial\",\n\t\"COND_TARGET_OCCLUDED\",\n\t\"kRenderFxHologram\",\n\t\"KEY_APOSTROPHE\",\n\t\"RENDERGROUP_STATIC\",\n\t\"ENT_BRUSH\",\n\t\"ACT_VM_DETACH_SILENCER\",\n\t\"KEY_ENTER\",\n\t\"ACT_VM_DEPLOYED_LIFTED_IDLE\",\n\t\"SCHED_MOVE_TO_WEAPON_RANGE\",\n\t\"ACT_MP_RELOAD_AIRWALK_SECONDARY\",\n\t\"ACT_GMOD_GESTURE_RANGE_ZOMBIE_SPECIAL\",\n\t\"KEY_F7\",\n\t\"SetClipboardText\",\n\t\"ACT_HL2MP_GESTURE_RANGE_ATTACK_MELEE2\",\n\t\"ACT_MP_WALK_MELEE\",\n\t\"DMG_ACID\",\n\t\"ACT_MP_RELOAD_AIRWALK_PRIMARY_LOOP\",\n\t\"DProperty_Generic\",\n\t\"PANEL\",\n\t\"ProjectedTexture\",\n\t\"ACT_MP_RELOAD_SWIM_SECONDARY_END\",\n\t\"ACT_VM_IDLE_DEPLOYED_4\",\n\t\"ACT_IDLE_MANNEDGUN\",\n\t\"ACT_DOD_RELOAD_K43\",\n\t\"ACT_DOD_HS_CROUCH_PSCHRECK\",\n\t\"Add_NPC_Class\",\n\t\"TEXTUREFLAGS_UNUSED_00080000\",\n\t\"COND_HEAR_WORLD\",\n\t\"ACT_VM_PRIMARYATTACK_8\",\n\t\"MOVECOLLIDE_DEFAULT\",\n\t\"ACT_DOD_WALK_IDLE_GREASE\",\n\t\"ACT_WALK_SUITCASE\",\n\t\"FCVAR_GAMEDLL\",\n\t\"ACT_PLAYER_CROUCH_FIRE\",\n\t\"ACT_MP_ATTACK_AIRWALK_GRENADE_BUILDING\",\n\t\"ACT_HL2MP_GESTURE_RELOAD_PISTOL\",\n\t\"ACT_RANGE_ATTACK_SMG1_LOW\",\n\t\"ACT_RELOAD_SMG1_LOW\",\n\t\"ACT_FLINCH_RIGHTLEG\",\n\t\"ACT_MP_PRIMARY_GRENADE1_ATTACK\",\n\t\"ACT_COVER_SMG1_LOW\",\n\t\"ONOFF_USE\",\n\t\"ACT_HL2MP_JUMP_SLAM\",\n\t\"CheckButton\",\n\t\"kRenderFxStrobeSlow\",\n\t\"TYPE_EFFECTDATA\",\n\t\"FSOLID_VOLUME_CONTENTS\",\n\t\"TimedCos\",\n\t\"ACT_DEEPIDLE3\",\n\t\"ACT_HL2MP_WALK_CROUCH_PASSIVE\",\n\t\"ACT_DUCK_DODGE\",\n\t\"ACT_HL2MP_RUN_ZOMBIE_FAST\",\n\t\"PrintMessage\",\n\t\"usermessage\",\n\t\"ACT_DOD_RUN_AIM\",\n\t\"JoinServer\",\n\t\"ACT_MP_GESTURE_VC_THUMBSUP_PRIMARY\",\n\t\"TYPE_FILE\",\n\t\"RunGameUICommand\",\n\t\"ACT_HL2MP_JUMP_KNIFE\",\n\t\"ACT_HL2MP_WALK_FIST\",\n\t\"MOVECOLLIDE_FLY_BOUNCE\",\n\t\"EFL_NO_WATER_VELOCITY_CHANGE\",\n\t\"SURF_HITBOX\",\n\t\"STENCILOPERATION_REPLACE\",\n\t\"ACT_MP_ATTACK_AIRWALK_MELEE\",\n\t\"ColorToHSV\",\n\t\"ACT_MP_WALK_PDA\",\n\t\"IMAGE_FORMAT_ABGR8888\",\n\t\"BuildNetworkedVarsTable\",\n\t\"KEY_XBUTTON_A\",\n\t\"hook\",\n\t\"SF_CITIZEN_RANDOM_HEAD\",\n\t\"CreateSprite\",\n\t\"DListView_ColumnPlain\",\n\t\"IsTableOfEntitiesValid\",\n\t\"ACT_HL2MP_IDLE_CROUCH_MELEE\",\n\t\"ACT_TURN_RIGHT\",\n\t\"CompileFile\",\n\t\"ACT_DOD_SPRINT_IDLE_RIFLE\",\n\t\"DKillIcon\",\n\t\"SNDLVL_180dB\",\n\t\"ACT_DOD_WALK_IDLE_PSCHRECK\",\n\t\"DMG_DROWN\",\n\t\"DMG_BURN\",\n\t\"ACT_MP_SWIM_BUILDING\",\n\t\"CONTENTS_SOLID\",\n\t\"ACT_MP_STAND_IDLE\",\n\t\"SCHED_SCENE_GENERIC\",\n\t\"PLAYERANIMEVENT_FLINCH_RIGHTLEG\",\n\t\"CreateClientConVar\",\n\t\"DListLayout\",\n\t\"ACT_MP_RELOAD_AIRWALK_SECONDARY_END\",\n\t\"ACT_CROUCH\",\n\t\"ACT_VM_PULLBACK_LOW\",\n\t\"NPC_STATE_IDLE\",\n\t\"ACT_MP_RELOAD_SWIM_END\",\n\t\"ACT_MP_ATTACK_CROUCH_GRENADE_SECONDARY\",\n\t\"COND_HEAR_BULLET_IMPACT\",\n\t\"ACT_OPEN_DOOR\",\n\t\"ACT_DOD_PRIMARYATTACK_PRONE_DEPLOYED_MG\",\n\t\"ACT_IDLE_ANGRY_PISTOL\",\n\t\"ACT_DOD_STAND_ZOOM_PSCHRECK\",\n\t\"COND_CAN_MELEE_ATTACK2\",\n\t\"ACT_READINESS_PISTOL_RELAXED_TO_STIMULATED\",\n\t\"engine\",\n\t\"SF_PHYSBOX_NEVER_PICK_UP\",\n\t\"ACT_GESTURE_RANGE_ATTACK_ML\",\n\t\"ACT_DOD_DEPLOY_30CAL\",\n\t\"CLASS_ZOMBIE\",\n\t\"ACT_RPG_IDLE_UNLOADED\",\n\t\"DProperty_Int\",\n\t\"ACT_IDLE_SHOTGUN_RELAXED\",\n\t\"RunStringEx\",\n\t\"ACT_MP_JUMP\",\n\t\"MOVETYPE_FLYGRAVITY\",\n\t\"matproxy\",\n\t\"ACT_VM_MISSRIGHT\",\n\t\"physenv\",\n\t\"DColorMixer\",\n\t\"RenderStereoscopy\",\n\t\"Schedule\",\n\t\"ACT_MP_GESTURE_VC_FISTPUMP\",\n\t\"ACT_RELOAD_PISTOL_LOW\",\n\t\"SF_NPC_NO_WEAPON_DROP\",\n\t\"ACT_SLAM_STICKWALL_DRAW\",\n\t\"ACT_HL2MP_WALK_CROUCH_GRENADE\",\n\t\"MsgN\",\n\t\"ACT_DOD_CROUCHWALK_IDLE_PSCHRECK\",\n\t\"CAP_SQUAD\",\n\t\"ACT_MP_AIRWALK_SECONDARY\",\n\t\"ALL_VISIBLE_CONTENTS\",\n\t\"ColorAlpha\",\n\t\"ACT_DOD_SPRINT_IDLE_GREASE\",\n\t\"SNDLVL_STATIC\",\n\t\"killicon\",\n\t\"ACT_SIGNAL2\",\n\t\"DProperty_VectorColor\",\n\t\"DCollapsibleCategory\",\n\t\"FCVAR_ARCHIVE_XBOX\",\n\t\"KEY_PAD_MULTIPLY\",\n\t\"fingerposer\",\n\t\"RENDERGROUP_OPAQUE\",\n\t\"require\",\n\t\"GetGlobalBool\",\n\t\"SCHED_COMBAT_WALK\",\n\t\"ACT_DEEPIDLE2\",\n\t\"ACT_HL2MP_SWIM_IDLE_PASSIVE\",\n\t\"SNDLVL_50dB\",\n\t\"NAV_MESH_FUNC_COST\",\n\t\"rawequal\",\n\t\"ACT_MP_GESTURE_VC_THUMBSUP\",\n\t\"ACT_DOD_RUN_IDLE_30CAL\",\n\t\"ACT_DRIVE_AIRBOAT\",\n\t\"ACT_GMOD_SIT_ROLLERCOASTER\",\n\t\"PrecacheScene\",\n\t\"SetGlobalBool\",\n\t\"IsInGame\",\n\t\"HUD_PRINTNOTIFY\",\n\t\"ACT_DOD_SPRINT_IDLE_MP40\",\n\t\"TYPE_SAVE\",\n\t\"ParticleEffect\",\n\t\"ACT_RUN_AIM_STEALTH\",\n\t\"ACT_DOD_RELOAD_PRONE_C96\",\n\t\"KEY_LEFT\",\n\t\"EFL_DONTWALKON\",\n\t\"PATTACH_ABSORIGIN\",\n\t\"ACT_HL2MP_GESTURE_RELOAD_PHYSGUN\",\n\t\"ACT_HL2MP_SWIM\",\n\t\"CHAN_VOICE_BASE\",\n\t\"ACT_DOD_PRIMARYATTACK_PRONE_GREN_FRAG\",\n\t\"ACT_HL2MP_WALK_CROUCH_ANGRY\",\n\t\"ACT_DOD_RUN_ZOOM_PSCHRECK\",\n\t\"ACT_DOD_PRONEWALK_IDLE_TNT\",\n\t\"TextEntryLoseFocus\",\n\t\"ACT_HL2MP_GESTURE_RANGE_ATTACK_SMG1\",\n\t\"IsColor\",\n\t\"ACT_RANGE_ATTACK2\",\n\t\"ACT_DOD_WALK_AIM_GREN_FRAG\",\n\t\"BONE_USED_BY_VERTEX_LOD4\",\n\t\"CONTENTS_TEAM3\",\n\t\"ACT_DOD_WALK_AIM_SPADE\",\n\t\"ACT_DOD_PRIMARYATTACK_PRONE_GREASE\",\n\t\"PresetEditor\",\n\t\"FVPHYSICS_PLAYER_HELD\",\n\t\"SetGlobalEntity\",\n\t\"ACT_HL2MP_SWIM_KNIFE\",\n\t\"ACT_GMOD_GESTURE_MELEE_SHOVE_1HAND\",\n\t\"ACT_GESTURE_RANGE_ATTACK_SNIPER_RIFLE\",\n\t\"GetSaveFileDetails\",\n\t\"ACT_DOD_RELOAD_PRONE_DEPLOYED_MG\",\n\t\"ACT_RUN_STEALTH\",\n\t\"coroutine\",\n\t\"GetGlobalEntity\",\n\t\"ACT_VM_IDLE_DEPLOYED_EMPTY\",\n\t\"HITGROUP_LEFTARM\",\n\t\"ACT_DOD_RELOAD_PRONE_DEPLOYED_MG34\",\n\t\"TEXFILTER\",\n\t\"SF_PHYSPROP_PREVENT_PICKUP\",\n\t\"ACT_HL2MP_WALK_CROUCH\",\n\t\"DrawToyTown\",\n\t\"DBubbleContainer\",\n\t\"EndTooltip\",\n\t\"MsgC\",\n\t\"SCHED_FLEE_FROM_BEST_SOUND\",\n\t\"ACT_MP_CROUCH_SECONDARY\",\n\t\"ACT_OBJ_UPGRADING\",\n\t\"ACT_DOD_CROUCH_AIM\",\n\t\"SF_NPC_ALTCOLLISION\",\n\t\"ACT_MP_AIRWALK_PRIMARY\",\n\t\"ACT_DOD_STAND_IDLE_TNT\",\n\t\"ACT_MP_GRENADE1_DRAW\",\n\t\"ACT_DOD_RELOAD_PRONE_DEPLOYED_FG42\",\n\t\"ACT_VM_PULLBACK_HIGH_BAKE\",\n\t\"ACT_RANGE_ATTACK_AR2\",\n\t\"SF_NPC_GAG\",\n\t\"UpdateLoadPanel\",\n\t\"CHAN_REPLACE\",\n\t\"ACT_VM_ISHOOT_M203\",\n\t\"ACT_DOD_RELOAD_PRONE_RIFLEGRENADE\",\n\t\"FCVAR_SPONLY\",\n\t\"TRACER_NONE\",\n\t\"DTooltip\",\n\t\"KEY_HOME\",\n\t\"ACT_IDLE_STEALTH_PISTOL\",\n\t\"CAP_MOVE_SHOOT\",\n\t\"Task\",\n\t\"ACT_VM_DRAW_DEPLOYED\",\n\t\"TYPE_CONVAR\",\n\t\"ACT_GMOD_TAUNT_DANCE\",\n\t\"ACT_FLINCH_CHEST\",\n\t\"ACT_DOD_HS_CROUCH_KNIFE\",\n\t\"ACT_DOD_PRIMARYATTACK_DEPLOYED_MG\",\n\t\"JOYSTICK_FIRST\",\n\t\"ACT_MP_SECONDARY_GRENADE2_IDLE\",\n\t\"SCHED_SCRIPTED_FACE\",\n\t\"ACT_DOD_RUN_IDLE_RIFLE\",\n\t\"ACT_MP_ATTACK_STAND_GRENADE_SECONDARY\",\n\t\"ACT_IDLE_RIFLE\",\n\t\"ACT_HL2MP_SWIM_AR2\",\n\t\"KEY_M\",\n\t\"ACT_SLAM_TRIPMINE_IDLE\",\n\t\"ContentIcon\",\n\t\"ACT_VM_PRIMARYATTACK_DEPLOYED\",\n\t\"DrawBloom\",\n\t\"ACT_DOD_CROUCHWALK_ZOOM_RIFLE\",\n\t\"DAlphaBar\",\n\t\"MAT_SNOW\",\n\t\"ACT_VM_IDLE\",\n\t\"ACT_MP_RELOAD_AIRWALK\",\n\t\"ACT_DOD_WALK_ZOOM_RIFLE\",\n\t\"IMAGE_FORMAT_RGB565\",\n\t\"ACT_DOD_PRONE_ZOOMED\",\n\t\"ACT_VM_DRYFIRE\",\n\t\"TEXTUREFLAGS_SINGLECOPY\",\n\t\"ACT_HL2MP_IDLE_CROUCH_MAGIC\",\n\t\"ACT_MP_RELOAD_CROUCH_PRIMARY\",\n\t\"STUDIO_STATIC_LIGHTING\",\n\t\"ACT_DOD_CROUCH_ZOOM_BOLT\",\n\t\"ACT_LOOKBACK_LEFT\",\n\t\"SNDLVL_130dB\",\n\t\"EFL_KEEP_ON_RECREATE_ENTITIES\",\n\t\"ACT_MP_ATTACK_CROUCH_GRENADE\",\n\t\"IN_MOVERIGHT\",\n\t\"EFL_DIRTY_ABSANGVELOCITY\",\n\t\"TRACER_RAIL\",\n\t\"COND_PROVOKED\",\n\t\"PLAYER_WALK\",\n\t\"ACT_MP_ATTACK_STAND_GRENADE_MELEE\",\n\t\"ACT_DOD_HS_CROUCH_BAZOOKA\",\n\t\"ACT_HL2MP_IDLE_CROUCH_FIST\",\n\t\"ACT_DOD_STAND_IDLE\",\n\t\"ACT_DOD_PRIMARYATTACK_GREASE\",\n\t\"ACT_DOD_PRIMARYATTACK_PRONE_DEPLOYED_RIFLE\",\n\t\"ACT_DIEFORWARD\",\n\t\"FFT_2048\",\n\t\"ACT_DOD_CROUCHWALK_IDLE_BAZOOKA\",\n\t\"SNDLVL_90dB\",\n\t\"DrawMotionBlur\",\n\t\"DPanel\",\n\t\"MAT_METAL\",\n\t\"EF_NOSHADOW\",\n\t\"ACT_MP_SECONDARY_GRENADE1_IDLE\",\n\t\"ACT_HL2MP_JUMP_ZOMBIE\",\n\t\"ACT_VM_UNDEPLOY_8\",\n\t\"COLLISION_GROUP_NONE\",\n\t\"ACT_VM_DEPLOYED_IRON_IN\",\n\t\"ACT_MP_ATTACK_STAND_GRENADE\",\n\t\"RENDERMODE_TRANSALPHADD\",\n\t\"ACT_MP_JUMP_MELEE\",\n\t\"ACT_MP_JUMP_SECONDARY\",\n\t\"Either\",\n\t\"ACT_HL2MP_GESTURE_RANGE_ATTACK_CAMERA\",\n\t\"KEY_W\",\n\t\"SCHED_COMBAT_PATROL\",\n\t\"ACT_FLINCH_HEAD\",\n\t\"ACT_DROP_WEAPON\",\n\t\"NOTIFY_CLEANUP\",\n\t\"DCategoryList\",\n\t\"ACT_SPECIAL_ATTACK1\",\n\t\"ACT_DOD_PRONEWALK_IDLE_BAZOOKA\",\n\t\"ACT_HL2MP_WALK_CROUCH_ZOMBIE_01\",\n\t\"ACT_GAUSS_SPINCYCLE\",\n\t\"D_HT\",\n\t\"FL_CONVEYOR\",\n\t\"ACT_GMOD_TAUNT_MUSCLE\",\n\t\"TYPE_TEXTURE\",\n\t\"DLabelURL\",\n\t\"list\",\n\t\"DMG_NEVERGIB\",\n\t\"ACT_HL2MP_GESTURE_RANGE_ATTACK_CROSSBOW\",\n\t\"ACT_DOD_STAND_ZOOM_BAZOOKA\",\n\t\"ACT_DOD_PRONE_DEPLOY_TOMMY\",\n\t\"ACT_DOD_RUN_AIM_30CAL\",\n\t\"ACT_DOD_PRONEWALK_IDLE_BOLT\",\n\t\"ACT_DOD_RELOAD_PRONE_TOMMY\",\n\t\"FL_UNBLOCKABLE_BY_PLAYER\",\n\t\"ACT_READINESS_PISTOL_STIMULATED_TO_RELAXED\",\n\t\"ACT_SLAM_STICKWALL_ATTACH2\",\n\t\"ACT_HL2MP_WALK_ZOMBIE_05\",\n\t\"ACT_DOD_CROUCH_IDLE\",\n\t\"ACT_MP_MELEE_GRENADE2_IDLE\",\n\t\"isfunction\",\n\t\"SetPhysConstraintSystem\",\n\t\"STENCIL_DECR\",\n\t\"ACT_VM_DEPLOY\",\n\t\"KEY_XSTICK2_RIGHT\",\n\t\"ACT_DOD_RUN_IDLE_MP44\",\n\t\"CREATERENDERTARGETFLAGS_HDR\",\n\t\"ACT_BUSY_LEAN_BACK_ENTRY\",\n\t\"MATERIAL_LINES\",\n\t\"Particle\",\n\t\"ACT_DOD_STAND_IDLE_RIFLE\",\n\t\"SCHED_RUN_FROM_ENEMY\",\n\t\"TranslateDownloadableName\",\n\t\"ACT_DOD_RELOAD_DEPLOYED_30CAL\",\n\t\"ACT_DOD_DEFUSE_TNT\",\n\t\"kRenderFxPulseFastWider\",\n\t\"FindMetaTable\",\n\t\"RENDERGROUP_BOTH\",\n\t\"ACT_DOD_WALK_AIM_PSCHRECK\",\n\t\"RandomPairs\",\n\t\"SNDLVL_NORM\",\n\t\"ACT_VM_ISHOOT\",\n\t\"BONE_SCREEN_ALIGN_CYLINDER\",\n\t\"PLAYERANIMEVENT_FLINCH_LEFTARM\",\n\t\"player_manager\",\n\t\"ACT_MP_ATTACK_CROUCH_GRENADE_MELEE\",\n\t\"ACT_VM_IDLE_4\",\n\t\"SF_NPC_FADE_CORPSE\",\n\t\"ACT_DO_NOT_DISTURB\",\n\t\"ACT_VM_DEPLOYED_IRON_OUT\",\n\t\"ACT_RANGE_ATTACK_PISTOL_LOW\",\n\t\"ACT_DOD_HS_IDLE_BAZOOKA\",\n\t\"NPC\",\n\t\"ACT_DOD_STAND_IDLE_MG\",\n\t\"ACT_WALK_CROUCH\",\n\t\"Weapon\",\n\t\"FL_OBJECT\",\n\t\"ACT_MP_AIRWALK_PDA\",\n\t\"ACT_IDLE_AIM_RELAXED\",\n\t\"DHorizontalScroller\",\n\t\"isvector\",\n\t\"ACT_DOD_WALK_AIM_BAZOOKA\",\n\t\"KEY_SLASH\",\n\t\"ACT_RANGE_ATTACK_SMG2\",\n\t\"COND_REPEATED_DAMAGE\",\n\t\"ACT_TRANSITION\",\n\t\"DTab\",\n\t\"MATERIAL_TRIANGLES\",\n\t\"ACT_DOD_CROUCH_IDLE_TNT\",\n\t\"FVPHYSICS_WAS_THROWN\",\n\t\"DNotify\",\n\t\"ACT_MP_ATTACK_STAND_PRIMARY\",\n\t\"ACT_MP_MELEE_GRENADE1_DRAW\",\n\t\"SCHED_ALERT_FACE\",\n\t\"ACT_HL2MP_WALK_CROUCH_SLAM\",\n\t\"KEY_XBUTTON_RIGHT\",\n\t\"ACT_VM_HITCENTER2\",\n\t\"ACT_DOD_CROUCHWALK_IDLE_C96\",\n\t\"ACT_MP_RELOAD_SWIM_LOOP\",\n\t\"KEY_XBUTTON_X\",\n\t\"SNDLVL_75dB\",\n\t\"ACT_DOD_PRIMARYATTACK_PRONE_BOLT\",\n\t\"ACT_MP_RELOAD_STAND_SECONDARY_LOOP\",\n\t\"COND_NO_CUSTOM_INTERRUPTS\",\n\t\"ACT_DOD_IDLE_ZOOMED\",\n\t\"CUserCmd\",\n\t\"BOX_BACK\",\n\t\"ACT_DOD_RUN_IDLE_PISTOL\",\n\t\"ACT_DOD_PRONE_DEPLOYED\",\n\t\"FCVAR_NOTIFY\",\n\t\"KEY_COMMA\",\n\t\"ACT_DOD_WALK_ZOOMED\",\n\t\"ACT_RELOAD_PISTOL\",\n\t\"TYPE_DLIGHT\",\n\t\"CLASS_MILITARY\",\n\t\"ACT_MP_DEPLOYED\",\n\t\"ACT_VM_DEPLOYED_IN\",\n\t\"OBS_MODE_DEATHCAM\",\n\t\"Format\",\n\t\"ACT_DOD_SPRINT_AIM_GREN_STICK\",\n\t\"CAP_WEAPON_RANGE_ATTACK1\",\n\t\"ACT_MP_JUMP_LAND_SECONDARY\",\n\t\"CONTENTS_WATER\",\n\t\"ACT_OBJ_DISMANTLING\",\n\t\"EyePos\",\n\t\"FingerVar\",\n\t\"PathFollower\",\n\t\"ACT_DOD_RELOAD_C96\",\n\t\"IN_BULLRUSH\",\n\t\"DLabelEditable\",\n\t\"ACT_HL2MP_GESTURE_RELOAD_SUITCASE\",\n\t\"CONTENTS_MONSTERCLIP\",\n\t\"MAT_WARPSHIELD\",\n\t\"ACT_HL2MP_GESTURE_RANGE_ATTACK_ANGRY\",\n\t\"ACT_DOD_HS_IDLE_K98\",\n\t\"KEY_PERIOD\",\n\t\"ACT_DOD_SPRINT_IDLE_BAR\",\n\t\"ACT_DOD_CROUCH_AIM_MP44\",\n\t\"ACT_STEP_RIGHT\",\n\t\"ACT_RUNTOIDLE\",\n\t\"TYPE_STRING\",\n\t\"SCHED_BACK_AWAY_FROM_SAVE_POSITION\",\n\t\"ACT_VM_MISSLEFT\",\n\t\"FFT_512\",\n\t\"ACT_DYINGLOOP\",\n\t\"ACT_DOD_CROUCH_AIM_BAR\",\n\t\"frame_blend\",\n\t\"IMAGE_FORMAT_RGBA16161616F\",\n\t\"ValidPanel\",\n\t\"NAV_MESH_WALK\",\n\t\"RememberCursorPosition\",\n\t\"ACT_HL2MP_JUMP_AR2\",\n\t\"MAT_SAND\",\n\t\"KEY_PAD_7\",\n\t\"ACT_DOD_CROUCH_IDLE_MP40\",\n\t\"ACT_IDLE_ON_FIRE\",\n\t\"KEY_J\",\n\t\"ACT_DOD_RUN_AIM_RIFLE\",\n\t\"CLASS_PROTOSNIPER\",\n\t\"RenderSuperDoF\",\n\t\"kRenderFxRagdoll\",\n\t\"CAP_USE_SHOT_REGULATOR\",\n\t\"ACT_SLAM_TRIPMINE_TO_STICKWALL_ND\",\n\t\"ACT_HL2MP_GESTURE_RELOAD_MELEE\",\n\t\"ACT_PLAYER_WALK_FIRE\",\n\t\"SortedPairsByValue\",\n\t\"ACT_GESTURE_MELEE_ATTACK1\",\n\t\"SCHED_VICTORY_DANCE\",\n\t\"ACT_MP_SWIM_SECONDARY\",\n\t\"DScrollBarGrip\",\n\t\"ClearBackgroundImages\",\n\t\"ACT_GESTURE_TURN_RIGHT90\",\n\t\"KEY_SEMICOLON\",\n\t\"CAP_SIMPLE_RADIUS_DAMAGE\",\n\t\"ACT_DOD_STAND_IDLE_C96\",\n\t\"ACT_SHIELD_KNOCKBACK\",\n\t\"ACT_HL2MP_SWIM_IDLE\",\n\t\"ACT_RUN_RPG_RELAXED\",\n\t\"ACT_HL2MP_SWIM_IDLE_MELEE\",\n\t\"ACT_HL2MP_GESTURE_RANGE_ATTACK_SLAM\",\n\t\"ACT_GMOD_GESTURE_POINT\",\n\t\"ACT_DOD_PRONE_DEPLOY_MG\",\n\t\"ACT_RELOAD_SHOTGUN_LOW\",\n\t\"ACT_IDLE_SHOTGUN_AGITATED\",\n\t\"DModelSelectMulti\",\n\t\"ACT_VM_IOUT\",\n\t\"STENCILCOMPARISONFUNCTION_GREATEREQUAL\",\n\t\"ACT_DOD_RELOAD_GARAND\",\n\t\"DMenu\",\n\t\"ACT_WALK_HURT\",\n\t\"ACT_DI_ALYX_ANTLION\",\n\t\"ACT_VM_PRIMARYATTACK_DEPLOYED_3\",\n\t\"SCHED_TAKE_COVER_FROM_ENEMY\",\n\t\"ACT_MP_ATTACK_SWIM_PREFIRE\",\n\t\"ACT_DOD_RUN_AIM_GREN_STICK\",\n\t\"ACT_VM_SPRINT_IDLE\",\n\t\"ACT_DOD_CROUCHWALK_AIM_GREN_STICK\",\n\t\"ACT_MP_RELOAD_AIRWALK_SECONDARY_LOOP\",\n\t\"saverestore\",\n\t\"MASK_OPAQUE_AND_NPCS\",\n\t\"ACT_VM_DEPLOY_5\",\n\t\"ai\",\n\t\"surface\",\n\t\"ACT_GESTURE_FLINCH_RIGHTARM\",\n\t\"ACT_SCRIPT_CUSTOM_MOVE\",\n\t\"ACT_DOD_PRONE_ZOOM_PSCHRECK\",\n\t\"ACT_VM_PULLPIN\",\n\t\"ACT_DOD_RELOAD_PRONE_K43\",\n\t\"HULL_HUMAN\",\n\t\"ACT_IDLE_ANGRY_SMG1\",\n\t\"CAP_NO_HIT_PLAYER\",\n\t\"ACT_DOD_PRONE_AIM_BAZOOKA\",\n\t\"ACT_HL2MP_RUN_SCARED\",\n\t\"ACT_GET_DOWN_CROUCH\",\n\t\"ACT_RUN_PISTOL\",\n\t\"ACT_MP_GESTURE_VC_HANDMOUTH_PRIMARY\",\n\t\"DProperties\",\n\t\"ACT_DOD_WALK_AIM_MG\",\n\t\"ACT_SLAM_THROW_THROW_ND\",\n\t\"ACT_STRAFE_RIGHT\",\n\t\"COLLISION_GROUP_WORLD\",\n\t\"ACT_DOD_PRIMARYATTACK_PISTOL\",\n\t\"NAV_MESH_HAS_ELEVATOR\",\n\t\"DGrid\",\n\t\"ACT_HL2MP_GESTURE_RANGE_ATTACK_MELEE\",\n\t\"ACT_DOD_CROUCH_ZOOMED\",\n\t\"DMG_FALL\",\n\t\"DImage\",\n\t\"COND_FLOATING_OFF_GROUND\",\n\t\"OBS_MODE_FREEZECAM\",\n\t\"ChangeBackground\",\n\t\"cookie\",\n\t\"GetOverlayPanel\",\n\t\"MAT_GRASS\",\n\t\"ACT_HL2MP_GESTURE_RELOAD_GRENADE\",\n\t\"ACT_MP_JUMP_START_PRIMARY\",\n\t\"constraint\",\n\t\"DListView_Line\",\n\t\"ACT_IDLE_PISTOL\",\n\t\"ACT_DOD_PRIMARYATTACK_PSCHRECK\",\n\t\"MOUSE_LEFT\",\n\t\"kRenderFxPulseSlowWide\",\n\t\"ACT_HL2MP_WALK_CROUCH_REVOLVER\",\n\t\"ACT_ZOMBIE_LEAP_START\",\n\t\"ACT_VM_IDLE_TO_LOWERED\",\n\t\"KEY_B\",\n\t\"MOVETYPE_WALK\",\n\t\"ACT_RIDE_MANNED_GUN\",\n\t\"ACT_MP_JUMP_PRIMARY\",\n\t\"ACT_HL2MP_GESTURE_RANGE_ATTACK_SHOTGUN\",\n\t\"ACT_DOD_STAND_AIM_SPADE\",\n\t\"SortedPairs\",\n\t\"ACT_CROUCHIDLE_AGITATED\",\n\t\"ACT_SMG2_RELOAD2\",\n\t\"KEY_XBUTTON_LEFT\",\n\t\"NAV_MESH_NO_MERGE\",\n\t\"NORTH_EAST\",\n\t\"ACT_CROUCHING_GRENADEIDLE\",\n\t\"ACT_VM_PRIMARYATTACK_3\",\n\t\"ACT_HANDGRENADE_THROW2\",\n\t\"ACT_DOD_RELOAD_PRONE_M1CARBINE\",\n\t\"ACT_GESTURE_RANGE_ATTACK_AR2\",\n\t\"ACT_MP_ATTACK_STAND_GRENADE_BUILDING\",\n\t\"COND_SEE_HATE\",\n\t\"ACT_MP_ATTACK_SWIM_BUILDING\",\n\t\"ACT_DOD_STAND_AIM_30CAL\",\n\t\"BLEND_ONE_MINUS_SRC_COLOR\",\n\t\"ACT_IDLE_AIM_RIFLE_STIMULATED\",\n\t\"COND_CAN_RANGE_ATTACK2\",\n\t\"ACT_DOD_RELOAD_CROUCH_BOLT\",\n\t\"DeriveGamemode\",\n\t\"ACT_HL2MP_JUMP_MELEE2\",\n\t\"ACT_MP_GESTURE_VC_FISTPUMP_PDA\",\n\t\"FVPHYSICS_DMG_SLICE\",\n\t\"ACT_SIGNAL3\",\n\t\"ACT_SHIELD_UP\",\n\t\"ACT_GESTURE_RANGE_ATTACK_AR2_GRENADE\",\n\t\"ACT_HL2MP_SWIM_IDLE_PHYSGUN\",\n\t\"ACT_DOD_STAND_IDLE_GREASE\",\n\t\"ACT_HL2MP_GESTURE_RANGE_ATTACK_GRENADE\",\n\t\"TYPE_LIGHTUSERDATA\",\n\t\"ACT_DOD_CROUCH_IDLE_MP44\",\n\t\"ACT_VM_DFIREMODE\",\n\t\"setmetatable\",\n\t\"SNDLVL_35dB\",\n\t\"ACT_ZOMBIE_LEAPING\",\n\t\"ACT_DOD_RELOAD_PRONE\",\n\t\"ACT_SHIELD_UP_IDLE\",\n\t\"ACT_MP_ATTACK_CROUCH_PRIMARY_DEPLOYED\",\n\t\"KEY_I\",\n\t\"ACT_HL2MP_SIT_RPG\",\n\t\"ACT_DOD_RELOAD_TOMMY\",\n\t\"getmetatable\",\n\t\"KEY_N\",\n\t\"ACT_SLAM_THROW_ND_DRAW\",\n\t\"ACT_HL2MP_WALK_CROUCH_ZOMBIE\",\n\t\"ACT_HL2MP_IDLE_ZOMBIE\",\n\t\"rawset\",\n\t\"CNewParticleEffect\",\n\t\"ACT_MP_GESTURE_VC_FINGERPOINT_MELEE\",\n\t\"ACT_HL2MP_RUN_FAST\",\n\t\"EFL_NO_GAME_PHYSICS_SIMULATION\",\n\t\"ACT_SHIELD_DOWN\",\n\t\"JOYSTICK_LAST_POV_BUTTON\",\n\t\"ACT_DOD_RUN_AIM_MP44\",\n\t\"SNDLVL_85dB\",\n\t\"SURF_NOLIGHT\",\n\t\"GetDefaultLoadingHTML\",\n\t\"ACT_VM_SPRINT_LEAVE\",\n\t\"ACT_DOD_PRIMARYATTACK_PRONE_BAR\",\n\t\"SCHED_DISARM_WEAPON\",\n\t\"KEY_XBUTTON_STICK2\",\n\t\"os\",\n\t\"ACT_DOD_PRONE_AIM_GREN_STICK\",\n\t\"ACT_VM_RELEASE\",\n\t\"ACT_HL2MP_WALK_SLAM\",\n\t\"Derma_Anim\",\n\t\"ACT_HL2MP_SWIM_IDLE_GRENADE\",\n\t\"ACT_VM_IDLE_3\",\n\t\"ENT_ANIM\",\n\t\"ACT_MP_RELOAD_CROUCH\",\n\t\"construct\",\n\t\"ACT_DOD_RUN_IDLE_MG\",\n\t\"KEY_V\",\n\t\"ACT_DOD_SPRINT_IDLE_TNT\",\n\t\"EFL_KILLME\",\n\t\"ACT_VM_LOWERED_TO_IDLE\",\n\t\"COND_HEAR_DANGER\",\n\t\"ACT_MP_GESTURE_VC_THUMBSUP_PDA\",\n\t\"ACT_MP_STAND_MELEE\",\n\t\"COND_NPC_UNFREEZE\",\n\t\"CLuaParticle\",\n\t\"ACT_MP_GESTURE_VC_FISTPUMP_PRIMARY\",\n\t\"ACT_HL2MP_IDLE_CROUCH_SUITCASE\",\n\t\"ACT_SMALL_FLINCH\",\n\t\"TEXTUREFLAGS_ALL_MIPS\",\n\t\"EyeVector\",\n\t\"ACT_ROLL_RIGHT\",\n\t\"ACT_PHYSCANNON_UPGRADE\",\n\t\"ACT_MP_MELEE_GRENADE1_ATTACK\",\n\t\"ACT_DI_ALYX_ZOMBIE_MELEE\",\n\t\"SendUserMessage\",\n\t\"ACT_SHOTGUN_PUMP\",\n\t\"FL_SWIM\",\n\t\"Tool\",\n\t\"PrecacheSentenceFile\",\n\t\"ACT_DOD_CROUCHWALK_IDLE_TOMMY\",\n\t\"ACT_DIE_GUTSHOT\",\n\t\"ACT_RPG_HOLSTER_UNLOADED\",\n\t\"ACT_RUN_AIM_SHOTGUN\",\n\t\"ACT_RANGE_ATTACK2_LOW\",\n\t\"DListBoxItem\",\n\t\"ACT_DOD_STAND_AIM\",\n\t\"ACT_DOD_RUN_ZOOM_BAZOOKA\",\n\t\"CNavLadder\",\n\t\"ACT_READINESS_AGITATED_TO_STIMULATED\",\n\t\"ACT_READINESS_STIMULATED_TO_RELAXED\",\n\t\"ACT_DOD_HS_CROUCH\",\n\t\"ACT_BUSY_SIT_GROUND_EXIT\",\n\t\"ACT_HL2MP_WALK_CROUCH_MELEE2\",\n\t\"ACT_VM_UNDEPLOY_5\",\n\t\"EFL_NO_DAMAGE_FORCES\",\n\t\"ACT_MP_RELOAD_STAND_PRIMARY\",\n\t\"KEY_PAD_3\",\n\t\"ACT_DOD_WALK_IDLE_TOMMY\",\n\t\"COLLISION_GROUP_NPC\",\n\t\"ispanel\",\n\t\"ACT_RELOAD_LOW\",\n\t\"KEY_PAD_DECIMAL\",\n\t\"ACT_DOD_RELOAD_CROUCH_BAZOOKA\",\n\t\"DTree_Node\",\n\t\"ACT_RPG_FIDGET_UNLOADED\",\n\t\"AchievementIcon\",\n\t\"ACT_BARNACLE_HIT\",\n\t\"ACT_DOD_RUN_AIM_MP40\",\n\t\"ACT_HL2MP_GESTURE_RELOAD_CAMERA\",\n\t\"gmod\",\n\t\"IconEditor\",\n\t\"MatSelect\",\n\t\"ACT_VM_IDLE_DEPLOYED_2\",\n\t\"SNDLVL_105dB\",\n\t\"ACT_DOD_CROUCH_AIM_MP40\",\n\t\"ACT_HL2MP_GESTURE_RANGE_ATTACK_PHYSGUN\",\n\t\"DLabel\",\n\t\"ACT_HL2MP_IDLE_CROUCH_SMG1\",\n\t\"ACT_RUN\",\n\t\"ACT_VM_PULLBACK\",\n\t\"FCVAR_PRINTABLEONLY\",\n\t\"ACT_SIGNAL1\",\n\t\"KEY_DELETE\",\n\t\"EmitSound\",\n\t\"CHAN_VOICE\",\n\t\"DListView\",\n\t\"STUDIO_GENERATE_STATS\",\n\t\"ACT_SWIM\",\n\t\"ACT_DOD_RELOAD_PISTOL\",\n\t\"COND_ENEMY_FACING_ME\",\n\t\"TYPE_FUNCTION\",\n\t\"ACT_DOD_PRIMARYATTACK_DEPLOYED_30CAL\",\n\t\"SCHED_PATROL_RUN\",\n\t\"ACT_MP_ATTACK_STAND_SECONDARY\",\n\t\"ACT_VM_UNDEPLOY_6\",\n\t\"ACT_VM_UNDEPLOY_3\",\n\t\"COND_HAVE_TARGET_LOS\",\n\t\"EFL_TOUCHING_FLUID\",\n\t\"ACT_DOD_RUN_AIM_C96\",\n\t\"ACT_GESTURE_RANGE_ATTACK_TRIPWIRE\",\n\t\"ACT_HL2MP_SWIM_IDLE_REVOLVER\",\n\t\"ACT_HL2MP_IDLE_CROSSBOW\",\n\t\"ACT_DOD_PRONE_DEPLOY_30CAL\",\n\t\"ACT_DOD_PRONEWALK_IDLE_PISTOL\",\n\t\"ACT_OVERLAY_SHIELD_KNOCKBACK\",\n\t\"util\",\n\t\"ACT_DOD_CROUCH_IDLE_BAR\",\n\t\"ACT_HL2MP_SWIM_SUITCASE\",\n\t\"ACT_PHYSCANNON_ANIMATE_PRE\",\n\t\"IncludeCS\",\n\t\"package\",\n\t\"ACT_COVER_LOW\",\n\t\"NumDownloadables\",\n\t\"BOX_RIGHT\",\n\t\"ACT_MP_ATTACK_SWIM_GRENADE_PRIMARY\",\n\t\"ACT_MP_RELOAD_CROUCH_PRIMARY_END\",\n\t\"ACT_OBJ_STARTUP\",\n\t\"FVPHYSICS_HEAVY_OBJECT\",\n\t\"ACT_HL2MP_JUMP_PHYSGUN\",\n\t\"CurTime\",\n\t\"WEAPON_PROFICIENCY_VERY_GOOD\",\n\t\"COND_HEALTH_ITEM_AVAILABLE\",\n\t\"ACT_IDLE_AGITATED\",\n\t\"OBS_MODE_NONE\",\n\t\"GetRenderTargetEx\",\n\t\"RENDERGROUP_OPAQUE_HUGE\",\n\t\"WEAPON_PROFICIENCY_AVERAGE\",\n\t\"ACT_HL2MP_WALK_ANGRY\",\n\t\"COLLISION_GROUP_DEBRIS_TRIGGER\",\n\t\"USE_TOGGLE\",\n\t\"TEXT_ALIGN_RIGHT\",\n\t\"USE_SET\",\n\t\"ACT_MP_GRENADE1_IDLE\",\n\t\"TYPE_MATERIAL\",\n\t\"DOF_Start\",\n\t\"OBS_MODE_CHASE\",\n\t\"CLASS_VORTIGAUNT\",\n\t\"TYPE_VIDEO\",\n\t\"TYPE_VECTOR\",\n\t\"TYPE_USERMSG\",\n\t\"FCVAR_ARCHIVE\",\n\t\"ACT_VM_RECOIL3\",\n\t\"KEY_H\",\n\t\"TYPE_USERCMD\",\n\t\"TYPE_THREAD\",\n\t\"ChangeTooltip\",\n\t\"ACT_DOD_SPRINT_IDLE_MP44\",\n\t\"ACT_HL2MP_WALK_MELEE\",\n\t\"SlideBar\",\n\t\"ACT_VM_MISSCENTER\",\n\t\"NextBot\",\n\t\"ACT_MP_DOUBLEJUMP\",\n\t\"DTextEntry\",\n\t\"JS_Utility\",\n\t\"TYPE_SOUNDHANDLE\",\n\t\"DScrollPanel\",\n\t\"ACT_VM_DEPLOY_3\",\n\t\"ACT_MP_JUMP_START_SECONDARY\",\n\t\"TEXTUREFLAGS_UNUSED_80000000\",\n\t\"ACT_GESTURE_RELOAD\",\n\t\"ACT_MP_GESTURE_VC_NODNO_PDA\",\n\t\"MOVECOLLIDE_FLY_CUSTOM\",\n\t\"ACT_HL2MP_IDLE_AR2\",\n\t\"TYPE_SOUND\",\n\t\"TYPE_SCRIPTEDVEHICLE\",\n\t\"TYPE_RESTORE\",\n\t\"ACT_MP_GESTURE_VC_HANDMOUTH\",\n\t\"TYPE_RECIPIENTFILTER\",\n\t\"TYPE_PROJECTEDTEXTURE\",\n\t\"ACT_MP_JUMP_LAND\",\n\t\"ACT_MELEE_ATTACK2\",\n\t\"ACT_VM_DRYFIRE_LEFT\",\n\t\"IRestore\",\n\t\"ACT_DOD_CROUCHWALK_ZOOMED\",\n\t\"ACT_VM_IDLE_7\",\n\t\"CONTENTS_DETAIL\",\n\t\"TYPE_PIXELVISHANDLE\",\n\t\"TYPE_PHYSOBJ\",\n\t\"SCHED_COMBAT_STAND\",\n\t\"IMesh\",\n\t\"ACT_DOD_CROUCH_AIM_MG\",\n\t\"MAT_TILE\",\n\t\"ACT_STEP_FORE\",\n\t\"JOYSTICK_LAST_BUTTON\",\n\t\"TYPE_PATH\",\n\t\"ACT_DOD_CROUCH_AIM_GREN_STICK\",\n\t\"TYPE_PARTICLEEMITTER\",\n\t\"ACT_FIRE_LOOP\",\n\t\"ACT_GET_UP_STAND\",\n\t\"TYPE_PARTICLE\",\n\t\"TYPE_PANEL\",\n\t\"TYPE_NUMBER\",\n\t\"TYPE_NIL\",\n\t\"EFL_SERVER_ONLY\",\n\t\"gameevent\",\n\t\"ACT_SIGNAL_RIGHT\",\n\t\"ACT_SLAM_THROW_TO_STICKWALL_ND\",\n\t\"ACT_DOD_RELOAD_CROUCH_C96\",\n\t\"ACT_DOD_RELOAD_BAZOOKA\",\n\t\"TYPE_NAVAREA\",\n\t\"ACT_FLINCH_LEFTLEG\",\n\t\"TYPE_MOVEDATA\",\n\t\"ACT_RUN_CROUCH\",\n\t\"ACT_SWIM_IDLE\",\n\t\"TYPE_MATRIX\",\n\t\"USE_ON\",\n\t\"ACT_MP_PRIMARY_GRENADE1_DRAW\",\n\t\"ACT_VM_IDLE_SILENCED\",\n\t\"ACT_RPG_DRAW_UNLOADED\",\n\t\"ACT_VM_DEPLOY_2\",\n\t\"ACT_CLIMB_DOWN\",\n\t\"TYPE_LOCOMOTION\",\n\t\"ModelImage\",\n\t\"FSOLID_FORCE_WORLD_ALIGNED\",\n\t\"IN_WALK\",\n\t\"ACT_DOD_CROUCHWALK_AIM_GREN_FRAG\",\n\t\"ACT_BUSY_SIT_GROUND\",\n\t\"TYPE_IMESH\",\n\t\"ACT_GESTURE_FLINCH_STOMACH\",\n\t\"ACT_CROUCHIDLE_AIM_STIMULATED\",\n\t\"ACT_DOD_RELOAD_PRONE_PSCHRECK\",\n\t\"TYPE_ENTITY\",\n\t\"Model\",\n\t\"TEXTUREFLAGS_POINTSAMPLE\",\n\t\"IMAGE_FORMAT_BGRA8888\",\n\t\"TYPE_DAMAGEINFO\",\n\t\"ACT_HL2MP_SWIM_IDLE_KNIFE\",\n\t\"ACT_DOD_CROUCH_AIM_SPADE\",\n\t\"ACT_DOD_STAND_AIM_GREN_FRAG\",\n\t\"ClientsideModel\",\n\t\"TYPE_COUNT\",\n\t\"RemoveTooltip\",\n\t\"COND_BEHIND_ENEMY\",\n\t\"TYPE_ANGLE\",\n\t\"NAV_MESH_RUN\",\n\t\"KEY_F9\",\n\t\"CEffectData\",\n\t\"TRANSMIT_NEVER\",\n\t\"TRANSMIT_ALWAYS\",\n\t\"ACT_MP_ATTACK_SWIM_GRENADE_BUILDING\",\n\t\"SCHED_AMBUSH\",\n\t\"MATERIAL_TRIANGLE_STRIP\",\n\t\"ai_task\",\n\t\"STUDIO_WIREFRAME_VCOLLIDE\",\n\t\"EF_NORECEIVESHADOW\",\n\t\"ACT_HL2MP_RUN_MELEE\",\n\t\"TRACER_LINE\",\n\t\"net\",\n\t\"AngleRand\",\n\t\"COND_TOO_FAR_TO_ATTACK\",\n\t\"ACT_GMOD_TAUNT_ROBOT\",\n\t\"ACT_SLAM_DETONATOR_THROW_DRAW\",\n\t\"ACT_RANGE_ATTACK_SHOTGUN\",\n\t\"ACT_SLAM_DETONATOR_DRAW\",\n\t\"ACT_MP_ATTACK_SWIM_GRENADE_SECONDARY\",\n\t\"TEXTUREFLAGS_UNUSED_10000000\",\n\t\"TEXTUREFLAGS_SSBUMP\",\n\t\"TEXTUREFLAGS_VERTEXTEXTURE\",\n\t\"ACT_READINESS_RELAXED_TO_STIMULATED_WALK\",\n\t\"TEXTUREFLAGS_CLAMPU\",\n\t\"TEXTUREFLAGS_UNUSED_01000000\",\n\t\"TEXTUREFLAGS_NODEPTHBUFFER\",\n\t\"CONTENTS_CURRENT_0\",\n\t\"ACT_HL2MP_WALK_CROUCH_RPG\",\n\t\"DSlider\",\n\t\"util.worldpicker\",\n\t\"ACT_HL2MP_SWIM_IDLE_SMG1\",\n\t\"ACT_HL2MP_WALK_GRENADE\",\n\t\"ContextBase\",\n\t\"ACT_MP_RELOAD_STAND_SECONDARY\",\n\t\"ACT_IDLETORUN\",\n\t\"NORTH\",\n\t\"IN_ATTACK\",\n\t\"ACT_OVERLAY_GRENADEIDLE\",\n\t\"COND_BETTER_WEAPON_AVAILABLE\",\n\t\"TEXTUREFLAGS_IMMEDIATE_CLEANUP\",\n\t\"TEXTUREFLAGS_NODEBUGOVERRIDE\",\n\t\"KEY_F11\",\n\t\"TEXTUREFLAGS_RENDERTARGET\",\n\t\"ACT_IDLE_ANGRY_MELEE\",\n\t\"TEXTUREFLAGS_EIGHTBITALPHA\",\n\t\"TEXTUREFLAGS_ONEBITALPHA\",\n\t\"SuppressHostEvents\",\n\t\"IN_USE\",\n\t\"ACT_DOD_RELOAD_BAR\",\n\t\"NAV_MESH_NO_JUMP\",\n\t\"ACT_HL2MP_IDLE_CROUCH_MELEE2\",\n\t\"BLEND_DST_COLOR\",\n\t\"CONTENTS_MOVEABLE\",\n\t\"TEXTUREFLAGS_NOMIP\",\n\t\"ACT_VM_RELOAD_INSERT_PULL\",\n\t\"render\",\n\t\"ACT_MP_ATTACK_CROUCH_MELEE\",\n\t\"ACT_HL2MP_SWIM_IDLE_FIST\",\n\t\"ACT_DOD_PRONEWALK_IDLE_30CAL\",\n\t\"TEXTUREFLAGS_PWL_CORRECTED\",\n\t\"TEXTUREFLAGS_HINT_DXT5\",\n\t\"ACT_DOD_CROUCHWALK_AIM_PISTOL\",\n\t\"TEXTUREFLAGS_ANISOTROPIC\",\n\t\"TEXTUREFLAGS_CLAMPT\",\n\t\"TEXTUREFLAGS_CLAMPS\",\n\t\"navmesh\",\n\t\"TEXT_ALIGN_TOP\",\n\t\"TEXTUREFLAGS_TRILINEAR\",\n\t\"TEXT_ALIGN_LEFT\",\n\t\"ACT_GESTURE_RELOAD_PISTOL\",\n\t\"SCHED_RUN_FROM_ENEMY_MOB\",\n\t\"ACT_90_LEFT\",\n\t\"TEAM_SPECTATOR\",\n\t\"ENT_AI\",\n\t\"TEAM_UNASSIGNED\",\n\t\"TEAM_CONNECTING\",\n\t\"IsUselessModel\",\n\t\"BLEND_SRC_ALPHA\",\n\t\"COND_ENEMY_DEAD\",\n\t\"ACT_MP_RELOAD_CROUCH_SECONDARY_LOOP\",\n\t\"MATERIAL_CULLMODE_CCW\",\n\t\"COND_NO_WEAPON\",\n\t\"SURF_NOSHADOWS\",\n\t\"COND_HAVE_ENEMY_LOS\",\n\t\"SURF_BUMPLIGHT\",\n\t\"FCVAR_NONE\",\n\t\"SURF_SKIP\",\n\t\"SURF_HINT\",\n\t\"HULL_TINY\",\n\t\"ACT_SLAM_TRIPMINE_ATTACH\",\n\t\"ControlPanel\",\n\t\"ACT_STAND\",\n\t\"SURF_TRIGGER\",\n\t\"LocalToWorld\",\n\t\"drive\",\n\t\"ACT_DOD_STAND_AIM_BAR\",\n\t\"GetMapList\",\n\t\"ACT_MP_ATTACK_STAND_STARTFIRE\",\n\t\"SURF_NOPORTAL\",\n\t\"ACT_MP_SWIM\",\n\t\"ACT_MP_GESTURE_VC_FINGERPOINT_PDA\",\n\t\"SURF_TRANS\",\n\t\"ACT_GESTURE_RANGE_ATTACK_HMG1\",\n\t\"GESTURE_SLOT_ATTACK_AND_RELOAD\",\n\t\"SURF_SKY\",\n\t\"ACT_ARM\",\n\t\"SURF_LIGHT\",\n\t\"DMG_VEHICLE\",\n\t\"ACT_DOD_SPRINT_IDLE_C96\",\n\t\"FL_WATERJUMP\",\n\t\"STUDIO_SSAODEPTHTEXTURE\",\n\t\"ACT_MP_SPRINT\",\n\t\"STUDIO_NOSHADOWS\",\n\t\"ACT_RELOAD_FINISH\",\n\t\"STUDIO_ITEM_BLINK\",\n\t\"ACT_DOD_RUN_IDLE_TNT\",\n\t\"ACT_VM_RECOIL2\",\n\t\"CLASS_PLAYER\",\n\t\"SCHED_WAKE_ANGRY\",\n\t\"STUDIO_VIEWXFORMATTACHMENTS\",\n\t\"ACT_CROSSBOW_DRAW_UNLOADED\",\n\t\"ACT_MP_GESTURE_VC_FISTPUMP_BUILDING\",\n\t\"STUDIO_RENDER\",\n\t\"ACT_MP_CROUCHWALK\",\n\t\"bit\",\n\t\"ACT_LOOKBACK_RIGHT\",\n\t\"STEPSOUNDTIME_WATER_FOOT\",\n\t\"CAP_INNATE_MELEE_ATTACK1\",\n\t\"KEY_F\",\n\t\"STEPSOUNDTIME_WATER_KNEE\",\n\t\"KEY_1\",\n\t\"ACT_MP_GESTURE_VC_NODYES_PDA\",\n\t\"STEPSOUNDTIME_ON_LADDER\",\n\t\"STEPSOUNDTIME_NORMAL\",\n\t\"STENCILOPERATION_DECR\",\n\t\"STENCILOPERATION_INCR\",\n\t\"STENCILOPERATION_INVERT\",\n\t\"STENCILOPERATION_DECRSAT\",\n\t\"ACT_SLAM_DETONATOR_DETONATE\",\n\t\"ACT_MP_ATTACK_AIRWALK_PRIMARYFIRE\",\n\t\"STENCILOPERATION_INCRSAT\",\n\t\"MOVETYPE_OBSERVER\",\n\t\"ACT_WALK_AIM_RIFLE\",\n\t\"ACT_DOD_SECONDARYATTACK_PRONE_RIFLE\",\n\t\"STENCILOPERATION_ZERO\",\n\t\"ACT_MP_ATTACK_SWIM_PRIMARY\",\n\t\"STENCILOPERATION_KEEP\",\n\t\"ACT_DOD_STAND_IDLE_PSCHRECK\",\n\t\"jit\",\n\t\"FSOLID_NOT_STANDABLE\",\n\t\"ACT_MP_RELOAD_SWIM_PRIMARY_LOOP\",\n\t\"CLASS_PLAYER_ALLY\",\n\t\"ACT_VM_IDLE_EMPTY_LEFT\",\n\t\"STENCILCOMPARISONFUNCTION_NOTEQUAL\",\n\t\"STENCILCOMPARISONFUNCTION_GREATER\",\n\t\"STENCILCOMPARISONFUNCTION_LESSEQUAL\",\n\t\"ACT_DOD_CROUCH_AIM_BAZOOKA\",\n\t\"Derma_Query\",\n\t\"STENCILCOMPARISONFUNCTION_EQUAL\",\n\t\"ACT_DOD_CROUCHWALK_IDLE_BOLT\",\n\t\"EF_DIMLIGHT\",\n\t\"STENCILCOMPARISONFUNCTION_NEVER\",\n\t\"STENCIL_INCR\",\n\t\"STENCIL_INVERT\",\n\t\"ACT_HL2MP_SWIM_IDLE_ANGRY\",\n\t\"NODOCK\",\n\t\"STENCIL_DECRSAT\",\n\t\"ControlPresets\",\n\t\"STENCIL_INCRSAT\",\n\t\"STENCIL_REPLACE\",\n\t\"KEY_XSTICK2_DOWN\",\n\t\"PLAYERANIMEVENT_ATTACK_SECONDARY\",\n\t\"STENCIL_ZERO\",\n\t\"ACT_GESTURE_RANGE_ATTACK1\",\n\t\"ACT_GESTURE_FLINCH_CHEST\",\n\t\"STENCIL_ALWAYS\",\n\t\"STENCIL_GREATEREQUAL\",\n\t\"STENCIL_NOTEQUAL\",\n\t\"GetConVar_Internal\",\n\t\"DColorButton\",\n\t\"ACT_IDLE_CARRY\",\n\t\"FCVAR_CLIENTCMD_CAN_EXECUTE\",\n\t\"MAT_GRATE\",\n\t\"ACT_MP_WALK_PRIMARY\",\n\t\"STENCIL_LESSEQUAL\",\n\t\"STENCIL_EQUAL\",\n\t\"ACT_MP_JUMP_BUILDING\",\n\t\"HULL_WIDE_HUMAN\",\n\t\"STENCIL_NEVER\",\n\t\"ISave\",\n\t\"CLASS_COMBINE\",\n\t\"SOLID_CUSTOM\",\n\t\"ACT_DOD_RUN_IDLE\",\n\t\"ACT_GESTURE_RELOAD_SHOTGUN\",\n\t\"ACT_SLAM_STICKWALL_ND_DRAW\",\n\t\"ACT_SHOTGUN_RELOAD_START\",\n\t\"ACT_HL2MP_IDLE_CROUCH_GRENADE\",\n\t\"Mesh\",\n\t\"ACT_MP_RELOAD_SWIM_SECONDARY\",\n\t\"ACT_MP_JUMP_LAND_PDA\",\n\t\"ACT_DOD_SECONDARYATTACK_MP40\",\n\t\"SOLID_BSP\",\n\t\"ACT_DOD_SPRINT_IDLE_BAZOOKA\",\n\t\"ACT_DOD_PRONEWALK_IDLE_RIFLE\",\n\t\"SF_NPC_LONG_RANGE\",\n\t\"ACT_VM_UNDEPLOY_7\",\n\t\"ACT_VM_RELOAD_INSERT\",\n\t\"PLAYER_JUMP\",\n\t\"SNDLVL_150dB\",\n\t\"NAV_MESH_STAIRS\",\n\t\"SNDLVL_GUNFIRE\",\n\t\"SCHED_WAIT_FOR_SCRIPT\",\n\t\"ACT_BUSY_QUEUE\",\n\t\"ACT_RANGE_ATTACK1_LOW\",\n\t\"HSVToColor\",\n\t\"EF_ITEM_BLINK\",\n\t\"ACT_VM_PRIMARYATTACK_DEPLOYED_7\",\n\t\"SNDLVL_120dB\",\n\t\"PrintTable\",\n\t\"ACT_HL2MP_GESTURE_RELOAD_KNIFE\",\n\t\"ACT_HL2MP_SWIM_IDLE_AR2\",\n\t\"DNumberScratch\",\n\t\"SNDLVL_110dB\",\n\t\"ACT_MP_STAND_SECONDARY\",\n\t\"SNDLVL_100dB\",\n\t\"DMG_SONIC\",\n\t\"ACT_SLAM_THROW_DRAW\",\n\t\"ACT_MP_SWIM_MELEE\",\n\t\"ACT_DOD_ZOOMLOAD_PSCHRECK\",\n\t\"mesh\",\n\t\"SCHED_RANGE_ATTACK2\",\n\t\"ACT_MP_ATTACK_AIRWALK_GRENADE_SECONDARY\",\n\t\"SNDLVL_TALKING\",\n\t\"KEY_D\",\n\t\"ACT_OBJ_IDLE\",\n\t\"SNDLVL_80dB\",\n\t\"EFL_DORMANT\",\n\t\"ACT_DOD_SECONDARYATTACK_PRONE_BOLT\",\n\t\"CLASS_BULLSEYE\",\n\t\"DMG_RADIATION\",\n\t\"SNDLVL_65dB\",\n\t\"ACT_MP_RELOAD_CROUCH_SECONDARY\",\n\t\"Entity\",\n\t\"ACT_MP_PRIMARY_GRENADE2_DRAW\",\n\t\"TEXTUREFLAGS_NOLOD\",\n\t\"SNDLVL_55dB\",\n\t\"ACT_BUSY_SIT_CHAIR_EXIT\",\n\t\"KEY_RBRACKET\",\n\t\"SNDLVL_40dB\",\n\t\"MASK_BLOCKLOS_AND_NPCS\",\n\t\"SNDLVL_30dB\",\n\t\"ACT_HL2MP_WALK_CROUCH_SCARED\",\n\t\"ACT_DOD_RUN_IDLE_BAR\",\n\t\"ACT_DOD_PRONE_AIM_BOLT\",\n\t\"DShape\",\n\t\"FCVAR_USERINFO\",\n\t\"ACT_MP_RELOAD_AIRWALK_END\",\n\t\"CONTENTS_AREAPORTAL\",\n\t\"SNDLVL_25dB\",\n\t\"team\",\n\t\"ACT_DOD_HS_CROUCH_K98\",\n\t\"ACT_GMOD_TAUNT_PERSISTENCE\",\n\t\"DExpandButton\",\n\t\"COND_LOW_PRIMARY_AMMO\",\n\t\"RealTime\",\n\t\"ACT_VM_IDLE_M203\",\n\t\"ACT_RELOAD_SHOTGUN\",\n\t\"SND_DO_NOT_OVERWRITE_EXISTING_ON_CHANNEL\",\n\t\"ACT_DOD_WALK_AIM_TOMMY\",\n\t\"ACT_HL2MP_WALK_CAMERA\",\n\t\"MOVETYPE_FLY\",\n\t\"SND_SHOULDPAUSE\",\n\t\"ACT_SHOTGUN_RELOAD_FINISH\",\n\t\"ACT_MP_ATTACK_STAND_PRIMARYFIRE_DEPLOYED\",\n\t\"ACT_SHIPLADDER_DOWN\",\n\t\"ACT_MP_GESTURE_FLINCH_PRIMARY\",\n\t\"SND_DELAY\",\n\t\"SOUTH\",\n\t\"ACT_DOD_CROUCH_AIM_GREASE\",\n\t\"SND_STOP\",\n\t\"ACT_CROSSBOW_HOLSTER_UNLOADED\",\n\t\"ACT_VM_IDLE_DEPLOYED_7\",\n\t\"ACT_DOD_SPRINT_AIM_GREN_FRAG\",\n\t\"ACT_DOD_HS_IDLE_TOMMY\",\n\t\"SND_CHANGE_PITCH\",\n\t\"ACT_GESTURE_RANGE_ATTACK_PISTOL\",\n\t\"SND_CHANGE_VOL\",\n\t\"SND_NOFLAGS\",\n\t\"SIM_GLOBAL_FORCE\",\n\t\"CAP_INNATE_RANGE_ATTACK2\",\n\t\"ACT_DOD_PRIMARYATTACK_PRONE_30CAL\",\n\t\"ACT_HL2MP_WALK_PHYSGUN\",\n\t\"ACT_HL2MP_WALK_CROUCH_PHYSGUN\",\n\t\"ACT_DOD_STAND_ZOOM_BOLT\",\n\t\"ACT_MP_GESTURE_VC_NODNO_PRIMARY\",\n\t\"ACT_DOD_RUN_AIM_BOLT\",\n\t\"SIM_LOCAL_ACCELERATION\",\n\t\"SIM_NOTHING\",\n\t\"ACT_DOD_SECONDARYATTACK_CROUCH_MP40\",\n\t\"ACT_DOD_PRONE_ZOOM_RIFLE\",\n\t\"ACT_HL2MP_GESTURE_RANGE_ATTACK_PISTOL\",\n\t\"KEY_XBUTTON_LEFT_SHOULDER\",\n\t\"effects\",\n\t\"UnPredictedCurTime\",\n\t\"ACT_HL2MP_IDLE_CROUCH_ZOMBIE\",\n\t\"SCHED_FLINCH_PHYSICS\",\n\t\"SF_PHYSBOX_MOTIONDISABLED\",\n\t\"ACT_DOD_WALK_AIM_MP44\",\n\t\"PLAYERANIMEVENT_FLINCH_RIGHTARM\",\n\t\"EyeAngles\",\n\t\"SF_NPC_WAIT_TILL_SEEN\",\n\t\"ACT_GESTURE_FLINCH_BLAST_DAMAGED_SHOTGUN\",\n\t\"SF_NPC_WAIT_FOR_SCRIPT\",\n\t\"SF_NPC_TEMPLATE\",\n\t\"SF_NPC_START_EFFICIENT\",\n\t\"BLOOD_COLOR_MECH\",\n\t\"SOLID_NONE\",\n\t\"gcinfo\",\n\t\"SF_NPC_FALL_TO_GROUND\",\n\t\"concommand\",\n\t\"SF_NPC_DROP_HEALTHKIT\",\n\t\"BLOOD_COLOR_ZOMBIE\",\n\t\"PLAYER_IN_VEHICLE\",\n\t\"SF_FLOOR_TURRET_CITIZEN\",\n\t\"Derma_Hook\",\n\t\"SF_CITIZEN_USE_RENDER_BOUNDS\",\n\t\"SF_CITIZEN_RANDOM_HEAD_MALE\",\n\t\"DNumPad\",\n\t\"ACT_RUN_AIM_RIFLE_STIMULATED\",\n\t\"ACT_DOD_PRONE_AIM_PSCHRECK\",\n\t\"FL_DONTTOUCH\",\n\t\"HULL_MEDIUM_TALL\",\n\t\"DMG_PARALYZE\",\n\t\"ACT_MP_RELOAD_STAND_END\",\n\t\"system\",\n\t\"SF_CITIZEN_IGNORE_SEMAPHORE\",\n\t\"SF_CITIZEN_FOLLOW\",\n\t\"KEY_SPACE\",\n\t\"SF_CITIZEN_AMMORESUPPLIER\",\n\t\"FL_INRAIN\",\n\t\"KEY_XBUTTON_UP\",\n\t\"URLLabel\",\n\t\"ACT_WALK_PISTOL\",\n\t\"KEY_PAGEDOWN\",\n\t\"STUDIO_DRAWTRANSLUCENTSUBMODELS\",\n\t\"SCHED_WAIT_FOR_SPEAK_FINISH\",\n\t\"ACT_IDLE_RPG\",\n\t\"SNDLVL_140dB\",\n\t\"SCHED_TARGET_FACE\",\n\t\"MAT_ANTLION\",\n\t\"IMAGE_FORMAT_RGBA8888\",\n\t\"ACT_VM_IDLE_2\",\n\t\"ACT_VM_THROW\",\n\t\"SCHED_TARGET_CHASE\",\n\t\"SCHED_TAKE_COVER_FROM_BEST_SOUND\",\n\t\"ACT_DOD_SPRINT_IDLE_PISTOL\",\n\t\"ACT_SHIPLADDER_UP\",\n\t\"SCHED_SWITCH_TO_PENDING_WEAPON\",\n\t\"SCHED_STANDOFF\",\n\t\"SCHED_SPECIAL_ATTACK2\",\n\t\"SCHED_SPECIAL_ATTACK1\",\n\t\"SCHED_SMALL_FLINCH\",\n\t\"SCHED_SLEEP\",\n\t\"SCHED_SHOOT_ENEMY_COVER\",\n\t\"ACT_DOD_PRIMARYATTACK_BAZOOKA\",\n\t\"SCHED_SCRIPTED_WALK\",\n\t\"SCHED_SCRIPTED_WAIT\",\n\t\"SCHED_SCRIPTED_RUN\",\n\t\"SCHED_SCRIPTED_CUSTOM_MOVE\",\n\t\"COND_NO_HEAR_DANGER\",\n\t\"SCHED_RUN_RANDOM\",\n\t\"SCHED_RUN_FROM_ENEMY_FALLBACK\",\n\t\"ACT_DOD_PRONE_AIM_C96\",\n\t\"KEY_LCONTROL\",\n\t\"MATERIAL_FOG_LINEAR_BELOW_FOG_Z\",\n\t\"ACT_DOD_PRIMARYATTACK_CROUCH_KNIFE\",\n\t\"SCHED_RANGE_ATTACK1\",\n\t\"GESTURE_SLOT_CUSTOM\",\n\t\"MAT_FOLIAGE\",\n\t\"ACT_DOD_RELOAD_PRONE_RIFLE\",\n\t\"DListView_DraggerBar\",\n\t\"SURF_NODECALS\",\n\t\"SCHED_NONE\",\n\t\"kRenderFxSpotlight\",\n\t\"ACT_DOD_RELOAD_MP40\",\n\t\"RENDERMODE_TRANSTEXTURE\",\n\t\"ACT_VM_UNUSABLE\",\n\t\"ACT_WALK_RPG_RELAXED\",\n\t\"SCHED_NEW_WEAPON\",\n\t\"ACT_GESTURE_TURN_LEFT45\",\n\t\"ACT_SMG2_IDLE2\",\n\t\"SCHED_MOVE_AWAY_FROM_ENEMY\",\n\t\"SCHED_MOVE_AWAY_FAIL\",\n\t\"ACT_DEPLOY\",\n\t\"next\",\n\t\"ACT_OBJ_DETERIORATING\",\n\t\"SCHED_MOVE_AWAY_END\",\n\t\"IN_WEAPON1\",\n\t\"ACT_DOD_CROUCHWALK_AIM_BAZOOKA\",\n\t\"ACT_SMG2_DRAW2\",\n\t\"VectorRand\",\n\t\"cvars\",\n\t\"SCHED_MOVE_AWAY\",\n\t\"DMenuOptionCVar\",\n\t\"SCHED_MELEE_ATTACK2\",\n\t\"KEY_T\",\n\t\"COND_WEAPON_PLAYER_IN_SPREAD\",\n\t\"SCHED_INVESTIGATE_SOUND\",\n\t\"ACT_MP_RELOAD_STAND\",\n\t\"utf8\",\n\t\"CONTENTS_GRATE\",\n\t\"MATERIAL_QUADS\",\n\t\"SCHED_INTERACTION_MOVE_TO_PARTNER\",\n\t\"IMaterial\",\n\t\"ACT_DOD_CROUCHWALK_AIM_PSCHRECK\",\n\t\"ACT_SLAM_STICKWALL_TO_THROW_ND\",\n\t\"SCHED_IDLE_WANDER\",\n\t\"SCHED_IDLE_WALK\",\n\t\"SCHED_IDLE_STAND\",\n\t\"ACT_MP_JUMP_FLOAT_PRIMARY\",\n\t\"ACT_DOD_CROUCH_AIM_C96\",\n\t\"ACT_GESTURE_RANGE_ATTACK_PISTOL_LOW\",\n\t\"IsFriendEntityName\",\n\t\"EFFECT\",\n\t\"DProperty_Combo\",\n\t\"KEY_CAPSLOCKTOGGLE\",\n\t\"SCHED_FORCED_GO_RUN\",\n\t\"select\",\n\t\"SCHED_FORCED_GO\",\n\t\"ACT_DOD_SECONDARYATTACK_TOMMY\",\n\t\"RestoreCursorPosition\",\n\t\"SF_PHYSPROP_MOTIONDISABLED\",\n\t\"SCHED_FEAR_FACE\",\n\t\"ACT_SLAM_DETONATOR_HOLSTER\",\n\t\"ACT_DOD_RELOAD_RIFLE\",\n\t\"FVPHYSICS_CONSTRAINT_STATIC\",\n\t\"SCHED_FALL_TO_GROUND\",\n\t\"ACT_MP_ATTACK_CROUCH_MELEE_SECONDARY\",\n\t\"DMG_NERVEGAS\",\n\t\"SCHED_FAIL_NOSTOP\",\n\t\"SCHED_FAIL_ESTABLISH_LINE_OF_FIRE\",\n\t\"SCHED_FAIL\",\n\t\"SCHED_ESTABLISH_LINE_OF_FIRE_FALLBACK\",\n\t\"ACT_HL2MP_WALK_CROUCH_SHOTGUN\",\n\t\"ACT_VM_IOUT_M203\",\n\t\"ACT_RUN_RIFLE\",\n\t\"ACT_HL2MP_RUN_PROTECTED\",\n\t\"ACT_PLAYER_CROUCH_WALK_FIRE\",\n\t\"SCHED_DROPSHIP_DUSTOFF\",\n\t\"SCHED_DIE_RAGDOLL\",\n\t\"IN_RIGHT\",\n\t\"SCHED_DIE\",\n\t\"ACT_MP_ATTACK_CROUCH_SECONDARY\",\n\t\"SCHED_COWER\",\n\t\"MASK_PLAYERSOLID\",\n\t\"ACT_SLAM_THROW_TO_STICKWALL\",\n\t\"SCHED_COMBAT_FACE\",\n\t\"COLLISION_GROUP_PUSHAWAY\",\n\t\"DEntityProperties\",\n\t\"SCHED_CHASE_ENEMY\",\n\t\"ToggleFavourite\",\n\t\"SCHED_BACK_AWAY_FROM_ENEMY\",\n\t\"HUD_PRINTCENTER\",\n\t\"ACT_MP_STAND_PDA\",\n\t\"ACT_DOD_RELOAD_DEPLOYED_FG42\",\n\t\"SCHED_ARM_WEAPON\",\n\t\"DMenuBar\",\n\t\"ACT_HL2MP_IDLE_SHOTGUN\",\n\t\"SF_CITIZEN_RANDOM_HEAD_FEMALE\",\n\t\"ACT_MP_GESTURE_VC_THUMBSUP_BUILDING\",\n\t\"FSOLID_ROOT_PARENT_ALIGNED\",\n\t\"SCHED_ALERT_SCAN\",\n\t\"SCHED_ALERT_REACT_TO_COMBAT_SOUND\",\n\t\"SCHED_ALERT_FACE_BESTSOUND\",\n\t\"SCHED_AISCRIPT\",\n\t\"ACT_HL2MP_WALK_SUITCASE\",\n\t\"ACT_VM_UNDEPLOY_1\",\n\t\"ACT_VM_ISHOOTDRY\",\n\t\"ACT_DOD_PRONE_ZOOM_FORWARD_BOLT\",\n\t\"ACT_RANGE_ATTACK1\",\n\t\"ACT_VM_UNUSABLE_TO_USABLE\",\n\t\"LAST_SHARED_SCHEDULE\",\n\t\"IMAGE_FORMAT_RGB888\",\n\t\"RT_SIZE_OFFSCREEN\",\n\t\"FrameTime\",\n\t\"RT_SIZE_FULL_FRAME_BUFFER\",\n\t\"COND_HEAR_COMBAT\",\n\t\"RT_SIZE_HDR\",\n\t\"ACT_DOD_CROUCHWALK_AIM_C96\",\n\t\"RT_SIZE_PICMIP\",\n\t\"ACT_MP_GESTURE_FLINCH_RIGHTLEG\",\n\t\"RT_SIZE_DEFAULT\",\n\t\"PLAYER_RELOAD\",\n\t\"DIconLayout\",\n\t\"RT_SIZE_NO_CHANGE\",\n\t\"COND_HEAR_PLAYER\",\n\t\"ACT_VM_CRAWL_M203\",\n\t\"RENDERMODE_WORLDGLOW\",\n\t\"RENDERMODE_TRANSADDFRAMEBLEND\",\n\t\"MATERIAL_RT_DEPTH_SHARED\",\n\t\"COND_HEAR_THUMPER\",\n\t\"CT_DEFAULT\",\n\t\"RENDERMODE_TRANSADD\",\n\t\"ACT_RANGE_AIM_AR2_LOW\",\n\t\"ACT_VM_PRIMARYATTACK_DEPLOYED_4\",\n\t\"RENDERMODE_TRANSALPHA\",\n\t\"RENDERMODE_GLOW\",\n\t\"ACT_HL2MP_IDLE_CROUCH_PASSIVE\",\n\t\"SCHED_NEW_WEAPON_CHEAT\",\n\t\"CAP_MOVE_SWIM\",\n\t\"DListViewHeaderLabel\",\n\t\"KEY_UP\",\n\t\"ACT_CLIMB_UP\",\n\t\"RENDERMODE_NORMAL\",\n\t\"ACT_HL2MP_RUN_ANGRY\",\n\t\"RENDERGROUP_OTHER\",\n\t\"COND_NEW_ENEMY\",\n\t\"RENDERGROUP_OPAQUE_BRUSH\",\n\t\"ACT_DOD_SPRINT_AIM_SPADE\",\n\t\"ACT_DOD_CROUCH_ZOOM_BAZOOKA\",\n\t\"KEY_BACKQUOTE\",\n\t\"RENDERGROUP_VIEWMODEL_TRANSLUCENT\",\n\t\"ACT_WALK_SCARED\",\n\t\"ACT_VM_MISSCENTER2\",\n\t\"ACT_MELEE_ATTACK1\",\n\t\"RENDERGROUP_STATIC_HUGE\",\n\t\"KEY_L\",\n\t\"PLAYERANIMEVENT_SPAWN\",\n\t\"PLAYERANIMEVENT_SNAP_YAW\",\n\t\"ACT_DOD_RELOAD_CROUCH_RIFLE\",\n\t\"PLAYERANIMEVENT_RELOAD_LOOP\",\n\t\"PLAYERANIMEVENT_RELOAD_END\",\n\t\"ACT_IDLE_HURT\",\n\t\"PLAYERANIMEVENT_RELOAD\",\n\t\"ACT_HL2MP_GESTURE_RELOAD_SMG1\",\n\t\"KEY_U\",\n\t\"ACT_DOD_CROUCH_IDLE_TOMMY\",\n\t\"PLAYERANIMEVENT_FLINCH_LEFTLEG\",\n\t\"PLAYERANIMEVENT_FLINCH_HEAD\",\n\t\"GESTURE_SLOT_SWIM\",\n\t\"PLAYERANIMEVENT_FLINCH_CHEST\",\n\t\"CHAN_VOICE2\",\n\t\"ACT_MP_ATTACK_STAND_MELEE\",\n\t\"PLAYERANIMEVENT_DOUBLEJUMP\",\n\t\"PLAYERANIMEVENT_DIE\",\n\t\"PLAYERANIMEVENT_CUSTOM_SEQUENCE\",\n\t\"ACT_VM_DRAWFULL_M203\",\n\t\"ACT_DOD_PRIMARYATTACK_GREN_FRAG\",\n\t\"IN_WEAPON2\",\n\t\"PLAYERANIMEVENT_CUSTOM_GESTURE_SEQUENCE\",\n\t\"ACT_HL2MP_SWIM_IDLE_SCARED\",\n\t\"PLAYERANIMEVENT_CUSTOM_GESTURE\",\n\t\"PLAYERANIMEVENT_CUSTOM\",\n\t\"ServerLog\",\n\t\"PLAYERANIMEVENT_CANCEL\",\n\t\"ACT_HL2MP_WALK_CROUCH_SMG1\",\n\t\"DOF_OFFSET\",\n\t\"PLAYERANIMEVENT_ATTACK_PRIMARY\",\n\t\"ParticleEffectAttach\",\n\t\"PLAYERANIMEVENT_ATTACK_GRENADE\",\n\t\"SafeRemoveEntity\",\n\t\"ACT_DOD_CROUCH_AIM_PISTOL\",\n\t\"RadioButton\",\n\t\"ACT_DOD_CROUCHWALK_IDLE_BAR\",\n\t\"CLuaEmitter\",\n\t\"ACT_MP_RELOAD_CROUCH_SECONDARY_END\",\n\t\"FL_STATICPROP\",\n\t\"ACT_RANGE_AIM_LOW\",\n\t\"FL_GRENADE\",\n\t\"EF_BONEMERGE_FASTCULL\",\n\t\"ACT_HL2MP_RUN_PHYSGUN\",\n\t\"ACT_DOD_PRIMARYATTACK_BAR\",\n\t\"PLAYER_ATTACK1\",\n\t\"ACT_MP_DEPLOYED_PRIMARY\",\n\t\"PLAYER_DIE\",\n\t\"PLAYER_SUPERJUMP\",\n\t\"setfenv\",\n\t\"COND_ENEMY_UNREACHABLE\",\n\t\"PATTACH_POINT_FOLLOW\",\n\t\"PATTACH_POINT\",\n\t\"CAP_FRIENDLY_DMG_IMMUNE\",\n\t\"ACT_MP_CROUCHWALK_PDA\",\n\t\"ACT_DOD_CROUCHWALK_AIM_GREASE\",\n\t\"ACT_VM_SECONDARYATTACK\",\n\t\"PATTACH_ABSORIGIN_FOLLOW\",\n\t\"ACT_DOD_PRIMARYATTACK_CROUCH_GREN_FRAG\",\n\t\"ACT_GESTURE_RELOAD_SMG1\",\n\t\"ACT_IDLE_ANGRY_RPG\",\n\t\"ACT_HL2MP_SWIM_PASSIVE\",\n\t\"CHAN_USER_BASE\",\n\t\"ACT_HL2MP_WALK_KNIFE\",\n\t\"ACT_RANGE_ATTACK_AR2_GRENADE\",\n\t\"ACT_DROP_WEAPON_SHOTGUN\",\n\t\"DMG_DROWNRECOVER\",\n\t\"OBS_MODE_IN_EYE\",\n\t\"ACT_DOD_PRIMARYATTACK_PRONE_MG\",\n\t\"EF_NODRAW\",\n\t\"ACT_DOD_WALK_IDLE_MP44\",\n\t\"MOUSE_FIRST\",\n\t\"OBS_MODE_FIXED\",\n\t\"ACT_HL2MP_WALK_ZOMBIE_01\",\n\t\"ACT_VM_PRIMARYATTACK_2\",\n\t\"WEAPON_PROFICIENCY_GOOD\",\n\t\"NUM_SPRITES\",\n\t\"scripted_ents\",\n\t\"KEY_BACKSPACE\",\n\t\"NUM_HULLS\",\n\t\"KEY_PAD_ENTER\",\n\t\"ACT_HL2MP_WALK_ZOMBIE_04\",\n\t\"serverlist\",\n\t\"NUM_AI_CLASSES\",\n\t\"ACT_TRIPMINE_WORLD\",\n\t\"DDragBase\",\n\t\"NPC_STATE_DEAD\",\n\t\"DCategoryHeader\",\n\t\"NPC_STATE_PRONE\",\n\t\"NPC_STATE_PLAYDEAD\",\n\t\"KEY_CAPSLOCK\",\n\t\"NPC_STATE_SCRIPT\",\n\t\"NPC_STATE_COMBAT\",\n\t\"ACT_HL2MP_WALK_RPG\",\n\t\"ACT_HL2MP_WALK_ZOMBIE_06\",\n\t\"NPC_STATE_ALERT\",\n\t\"ACT_IDLE_ANGRY_SHOTGUN\",\n\t\"NPC_STATE_NONE\",\n\t\"DMG_BLAST_SURFACE\",\n\t\"NOTIFY_HINT\",\n\t\"DPanelOverlay\",\n\t\"ACT_DYINGTODEAD\",\n\t\"NOTIFY_ERROR\",\n\t\"ACT_HL2MP_WALK_CROUCH_PISTOL\",\n\t\"NOTIFY_GENERIC\",\n\t\"ACT_HL2MP_IDLE_CROUCH_ZOMBIE_02\",\n\t\"GO_ELEVATOR_DOWN\",\n\t\"ACT_MP_CROUCH_BUILDING\",\n\t\"ACT_DOD_PRONEWALK_AIM_KNIFE\",\n\t\"ACT_HL2MP_JUMP_SCARED\",\n\t\"ACT_DOD_STAND_IDLE_MP40\",\n\t\"GO_ELEVATOR_UP\",\n\t\"PLAYERANIMEVENT_JUMP\",\n\t\"GO_LADDER_DOWN\",\n\t\"ACT_DOD_DEPLOY_RIFLE\",\n\t\"unpack\",\n\t\"ACT_VM_RELOAD_DEPLOYED\",\n\t\"GO_SOUTH\",\n\t\"ACT_COVER_LOW_RPG\",\n\t\"GO_EAST\",\n\t\"MATERIAL_LIGHT_POINT\",\n\t\"WEST\",\n\t\"ACT_GMOD_TAUNT_LAUGH\",\n\t\"EAST\",\n\t\"NUM_CORNERS\",\n\t\"SOUTH_WEST\",\n\t\"SOUTH_EAST\",\n\t\"MATERIAL_FOG_LINEAR\",\n\t\"BONE_SCREEN_ALIGN_SPHERE\",\n\t\"IN_ATTACK2\",\n\t\"NAV_MESH_AVOID\",\n\t\"NORTH_WEST\",\n\t\"ACT_DOD_RELOAD_PRONE_BOLT\",\n\t\"ACT_MP_WALK\",\n\t\"CAP_INNATE_RANGE_ATTACK1\",\n\t\"ACT_DOD_RELOAD_PRONE_DEPLOYED_30CAL\",\n\t\"NAV_MESH_NAV_BLOCKER\",\n\t\"ACT_DOD_PRIMARYATTACK_CROUCH\",\n\t\"NAV_MESH_CLIFF\",\n\t\"NAV_MESH_OBSTACLE_TOP\",\n\t\"NAV_MESH_NO_HOSTAGES\",\n\t\"NAV_MESH_STAND\",\n\t\"NAV_MESH_DONT_HIDE\",\n\t\"ACT_DOD_CROUCHWALK_IDLE\",\n\t\"DListView_Column\",\n\t\"NAV_MESH_TRANSIENT\",\n\t\"TRANSMIT_PVS\",\n\t\"TEXTUREFLAGS_PROCEDURAL\",\n\t\"NAV_MESH_PRECISE\",\n\t\"KEY_PAD_9\",\n\t\"ACT_SLAM_STICKWALL_ND_ATTACH2\",\n\t\"ACT_HOP\",\n\t\"NAV_MESH_CROUCH\",\n\t\"NAV_MESH_INVALID\",\n\t\"EF_BRIGHTLIGHT\",\n\t\"PLAYER_IDLE\",\n\t\"ACT_DOD_CROUCHWALK_AIM_MG\",\n\t\"ACT_HL2MP_IDLE_CROUCH_KNIFE\",\n\t\"rawget\",\n\t\"ACT_HL2MP_GESTURE_RELOAD_CROSSBOW\",\n\t\"ACT_SLAM_THROW_ND_IDLE\",\n\t\"MOVETYPE_LADDER\",\n\t\"CTakeDamageInfo\",\n\t\"input\",\n\t\"DForm\",\n\t\"ENTITY\",\n\t\"ACT_DOD_CROUCH_AIM_PSCHRECK\",\n\t\"Derma_StringRequest\",\n\t\"ACT_HL2MP_SWIM_CAMERA\",\n\t\"ACT_GMOD_GESTURE_TAUNT_ZOMBIE\",\n\t\"MOVETYPE_VPHYSICS\",\n\t\"SND_IGNORE_PHONEMES\",\n\t\"ACT_GESTURE_RANGE_ATTACK_SHOTGUN\",\n\t\"ACT_MP_RUN\",\n\t\"ACT_DOD_CROUCH_IDLE_BAZOOKA\",\n\t\"ACT_MP_GESTURE_VC_HANDMOUTH_BUILDING\",\n\t\"GMOD_CHANNEL_PLAYING\",\n\t\"DRGBPicker\",\n\t\"MOVETYPE_NONE\",\n\t\"CreateSound\",\n\t\"ACT_MP_CROUCHWALK_PRIMARY\",\n\t\"ACT_DOD_RUN_AIM_BAR\",\n\t\"MOVECOLLIDE_COUNT\",\n\t\"COLLISION_GROUP_IN_VEHICLE\",\n\t\"collectgarbage\",\n\t\"ACT_DI_ALYX_ZOMBIE_SHOTGUN64\",\n\t\"DBinder\",\n\t\"MATERIAL_RT_DEPTH_NONE\",\n\t\"ACT_GRENADE_TOSS\",\n\t\"ACT_VM_IDLE_DEPLOYED\",\n\t\"ACT_VM_DOWN\",\n\t\"ACT_SHOTGUN_IDLE4\",\n\t\"DOF_SPACING\",\n\t\"ACT_DOD_STAND_IDLE_30CAL\",\n\t\"steamworks\",\n\t\"MATERIAL_RT_DEPTH_SEPARATE\",\n\t\"MATERIAL_LIGHT_SPOT\",\n\t\"EFL_CHECK_UNTOUCH\",\n\t\"GO_NORTH\",\n\t\"DProperty_Float\",\n\t\"IN_ALT1\",\n\t\"MATERIAL_LIGHT_DISABLE\",\n\t\"SCHED_RELOAD\",\n\t\"isangle\",\n\t\"ACT_DI_ALYX_ZOMBIE_TORSO_MELEE\",\n\t\"ACT_HL2MP_SIT_SMG1\",\n\t\"MATERIAL_FOG_NONE\",\n\t\"HULL_SMALL_CENTERED\",\n\t\"ACT_DOD_DEPLOY_TOMMY\",\n\t\"MATERIAL_CULLMODE_CW\",\n\t\"SURF_NOCHOP\",\n\t\"TRACER_LINE_AND_WHIZ\",\n\t\"SCHED_INTERACTION_WAIT_FOR_PARTNER\",\n\t\"sql\",\n\t\"MATERIAL_POLYGON\",\n\t\"MATERIAL_POINTS\",\n\t\"IsValid\",\n\t\"ACT_VM_HAULBACK\",\n\t\"ACT_MP_WALK_SECONDARY\",\n\t\"ACT_WALK_AIM_SHOTGUN\",\n\t\"FCVAR_SERVER_CAN_EXECUTE\",\n\t\"MAT_DEFAULT\",\n\t\"MAT_WOOD\",\n\t\"ACT_MP_PRIMARY_GRENADE2_IDLE\",\n\t\"ACT_SIGNAL_HALT\",\n\t\"ACT_HL2MP_SWIM_IDLE_MAGIC\",\n\t\"ACT_MP_ATTACK_SWIM_SECONDARYFIRE\",\n\t\"ACT_MP_GESTURE_VC_NODNO_BUILDING\",\n\t\"ACT_COVER\",\n\t\"SCHED_PATROL_WALK\",\n\t\"ACT_HL2MP_SWIM_IDLE_SLAM\",\n\t\"MAT_FLESH\",\n\t\"MAT_EGGSHELL\",\n\t\"KEY_NUMLOCK\",\n\t\"CLuaLocomotion\",\n\t\"ACT_DOD_PRIMARYATTACK_TOMMY\",\n\t\"ACT_DOD_CROUCH_AIM_RIFLE\",\n\t\"MAT_CONCRETE\",\n\t\"ACT_VM_DOWN_EMPTY\",\n\t\"ACT_WALK_AIM\",\n\t\"ACT_DOD_WALK_AIM_MP40\",\n\t\"MAT_CLIP\",\n\t\"CT_REFUGEE\",\n\t\"MAT_ALIENFLESH\",\n\t\"MASK_WATER\",\n\t\"MASK_VISIBLE_AND_NPCS\",\n\t\"MASK_VISIBLE\",\n\t\"ACT_HL2MP_WALK_CROUCH_ZOMBIE_03\",\n\t\"MASK_SPLITAREAPORTAL\",\n\t\"ACT_IDLE_SUITCASE\",\n\t\"MASK_SOLID_BRUSHONLY\",\n\t\"MASK_SOLID\",\n\t\"ACT_DOD_ZOOMLOAD_PRONE_BAZOOKA\",\n\t\"GMOD_CHANNEL_STALLED\",\n\t\"ACT_DOD_PRONE_FORWARD_ZOOMED\",\n\t\"MASK_SHOT_PORTAL\",\n\t\"ACT_DOD_DEPLOYED\",\n\t\"MASK_SHOT_HULL\",\n\t\"MASK_PLAYERSOLID_BRUSHONLY\",\n\t\"ACT_HL2MP_IDLE_CROUCH_RPG\",\n\t\"game\",\n\t\"SCHED_COMBAT_SWEEP\",\n\t\"EffectData\",\n\t\"MASK_OPAQUE\",\n\t\"ACT_LAND\",\n\t\"KEY_NUMLOCKTOGGLE\",\n\t\"COND_HEAR_MOVE_AWAY\",\n\t\"MASK_NPCSOLID\",\n\t\"ACT_COMBAT_IDLE\",\n\t\"MASK_DEADSOLID\",\n\t\"MASK_CURRENT\",\n\t\"OrderVectors\",\n\t\"DMG_BULLET\",\n\t\"COND_SEE_PLAYER\",\n\t\"ACT_HL2MP_SIT_AR2\",\n\t\"ACT_HL2MP_IDLE_MELEE_ANGRY\",\n\t\"KEY_P\",\n\t\"MASK_ALL\",\n\t\"ACT_GESTURE_TURN_LEFT90\",\n\t\"kRenderFxGlowShell\",\n\t\"kRenderFxExplode\",\n\t\"kRenderFxDistort\",\n\t\"kRenderFxNoDissipation\",\n\t\"ACT_VM_DRAW_SILENCED\",\n\t\"kRenderFxFlickerFast\",\n\t\"kRenderFxFlickerSlow\",\n\t\"EFL_BOT_FROZEN\",\n\t\"COND_PLAYER_ADDED_TO_SQUAD\",\n\t\"DColorCombo\",\n\t\"kRenderFxStrobeFast\",\n\t\"kRenderFxSolidFast\",\n\t\"kRenderFxSolidSlow\",\n\t\"kRenderFxFadeFast\",\n\t\"ACT_VM_PRIMARYATTACK_DEPLOYED_2\",\n\t\"kRenderFxFadeSlow\",\n\t\"OnModelLoaded\",\n\t\"kRenderFxPulseFastWide\",\n\t\"kRenderFxPulseFast\",\n\t\"KEY_TAB\",\n\t\"kRenderFxNone\",\n\t\"ACT_MP_ATTACK_STAND_MELEE_SECONDARY\",\n\t\"ACT_MP_ATTACK_SWIM_MELEE\",\n\t\"IN_GRENADE1\",\n\t\"ACT_MP_GESTURE_VC_NODNO_MELEE\",\n\t\"ACT_GESTURE_FLINCH_HEAD\",\n\t\"ACT_DOD_CROUCHWALK_IDLE_TNT\",\n\t\"ClientsideRagdoll\",\n\t\"debugoverlay\",\n\t\"IN_SPEED\",\n\t\"IN_SCORE\",\n\t\"CHAN_BODY\",\n\t\"ACT_DOD_SPRINT_IDLE_MG\",\n\t\"IN_RELOAD\",\n\t\"ACT_MP_ATTACK_CROUCH_SECONDARYFIRE\",\n\t\"ACT_DOD_RUN_AIM_BAZOOKA\",\n\t\"IN_RUN\",\n\t\"IN_MOVELEFT\",\n\t\"IsEnemyEntityName\",\n\t\"IN_LEFT\",\n\t\"ACT_WALK_RELAXED\",\n\t\"ACT_DOD_RELOAD_FG42\",\n\t\"IN_CANCEL\",\n\t\"ACT_HL2MP_SWIM_SLAM\",\n\t\"ACT_RUN_ON_FIRE\",\n\t\"ACT_SMG2_TOAUTO\",\n\t\"COND_NOT_FACING_ATTACK\",\n\t\"numpad\",\n\t\"ACT_SLAM_STICKWALL_ND_ATTACH\",\n\t\"ACT_MP_RUN_PRIMARY\",\n\t\"ismatrix\",\n\t\"IN_FORWARD\",\n\t\"IN_DUCK\",\n\t\"ACT_DOD_SPRINT_IDLE_PSCHRECK\",\n\t\"DamageInfo\",\n\t\"ACT_WALK_CROUCH_RPG\",\n\t\"JOYSTICK_FIRST_AXIS_BUTTON\",\n\t\"DListViewLine\",\n\t\"ACT_VM_RELOAD_M203\",\n\t\"ACT_DOD_PRIMARYATTACK_SPADE\",\n\t\"ACT_TURNRIGHT45\",\n\t\"FL_STEPMOVEMENT\",\n\t\"IMAGE_FORMAT_BGR888\",\n\t\"RT_SIZE_FULL_FRAME_BUFFER_ROUNDED_UP\",\n\t\"IMAGE_FORMAT_DEFAULT\",\n\t\"SF_CITIZEN_MEDIC\",\n\t\"HULL_LARGE_CENTERED\",\n\t\"ACT_DOD_ZOOMLOAD_PRONE_PSCHRECK\",\n\t\"HULL_LARGE\",\n\t\"HULL_TINY_CENTERED\",\n\t\"HULL_MEDIUM\",\n\t\"STENCIL_LESS\",\n\t\"ACT_HL2MP_GESTURE_RANGE_ATTACK_ZOMBIE\",\n\t\"ACT_MP_GESTURE_VC_NODYES_MELEE\",\n\t\"ACT_MP_ATTACK_AIRWALK_GRENADE\",\n\t\"HUD_PRINTTALK\",\n\t\"ACT_IDLE_SMG1_STIMULATED\",\n\t\"HUD_PRINTCONSOLE\",\n\t\"CONTENTS_MONSTER\",\n\t\"ACT_VM_UNDEPLOY\",\n\t\"ACT_MP_JUMP_START_BUILDING\",\n\t\"FORCE_NUMBER\",\n\t\"ACT_MP_RELOAD_SWIM_PRIMARY\",\n\t\"ACT_VM_DRAW\",\n\t\"ACT_VM_IIN\",\n\t\"ACT_VM_HITLEFT2\",\n\t\"ACT_HL2MP_IDLE_CROUCH\",\n\t\"HITGROUP_RIGHTARM\",\n\t\"ACT_WALK_RIFLE_RELAXED\",\n\t\"DMG_DISSOLVE\",\n\t\"HITGROUP_GENERIC\",\n\t\"SF_NPC_NO_PLAYER_PUSHAWAY\",\n\t\"ACT_RANGE_ATTACK_HMG1\",\n\t\"GMOD_CHANNEL_STOPPED\",\n\t\"GLOBAL_DEAD\",\n\t\"DrawSobel\",\n\t\"GLOBAL_ON\",\n\t\"PLAYER_LEAVE_AIMING\",\n\t\"KEY_Z\",\n\t\"DCheckBox\",\n\t\"SCHED_PRE_FAIL_ESTABLISH_LINE_OF_FIRE\",\n\t\"GESTURE_SLOT_VCD\",\n\t\"ACT_HL2MP_RUN_PASSIVE\",\n\t\"GESTURE_SLOT_FLINCH\",\n\t\"ACT_HL2MP_WALK_CROSSBOW\",\n\t\"ACT_DOD_RELOAD_DEPLOYED_BAR\",\n\t\"MOUSE_RIGHT\",\n\t\"ACT_MP_STAND_BUILDING\",\n\t\"timer\",\n\t\"ACT_RAPPEL_LOOP\",\n\t\"newproxy\",\n\t\"GESTURE_SLOT_GRENADE\",\n\t\"ACT_DOD_CROUCH_AIM_BOLT\",\n\t\"FVPHYSICS_PART_OF_RAGDOLL\",\n\t\"ACT_HL2MP_IDLE_CROUCH_SLAM\",\n\t\"ACT_MP_ATTACK_CROUCH_PRIMARYFIRE_DEPLOYED\",\n\t\"ACT_GESTURE_TURN_LEFT45_FLAT\",\n\t\"ACT_DOD_CROUCH_AIM_KNIFE\",\n\t\"COND_SCHEDULE_DONE\",\n\t\"ACT_BUSY_LEAN_LEFT_EXIT\",\n\t\"FVPHYSICS_NO_SELF_COLLISIONS\",\n\t\"FVPHYSICS_NO_PLAYER_PICKUP\",\n\t\"FVPHYSICS_MULTIOBJECT_ENTITY\",\n\t\"FSOLID_MAX_BITS\",\n\t\"istable\",\n\t\"ACT_VM_RELOAD_IDLE\",\n\t\"COLLISION_GROUP_INTERACTIVE_DEBRIS\",\n\t\"STENCILCOMPARISONFUNCTION_ALWAYS\",\n\t\"ACT_RUN_CROUCH_AIM\",\n\t\"FSOLID_TRIGGER\",\n\t\"FSOLID_NOT_SOLID\",\n\t\"DOF_Kill\",\n\t\"ACT_VM_ATTACH_SILENCER\",\n\t\"EFL_NO_THINK_FUNCTION\",\n\t\"ACT_180_LEFT\",\n\t\"FSOLID_CUSTOMBOXTEST\",\n\t\"FSOLID_CUSTOMRAYTEST\",\n\t\"FORCE_BOOL\",\n\t\"FCVAR_DONTRECORD\",\n\t\"ACT_MP_GESTURE_FLINCH_LEFTLEG\",\n\t\"FL_TRANSRAGDOLL\",\n\t\"ACT_RUN_RELAXED\",\n\t\"kRenderFxClampMinScale\",\n\t\"FL_FROZEN\",\n\t\"FL_KILLME\",\n\t\"FL_WORLDBRUSH\",\n\t\"ACT_CROUCHING_SHIELD_ATTACK\",\n\t\"PrecacheParticleSystem\",\n\t\"ACT_RUN_STIMULATED\",\n\t\"FL_BASEVELOCITY\",\n\t\"FL_GRAPHED\",\n\t\"ACT_GESTURE_BIG_FLINCH\",\n\t\"FL_PARTIALGROUND\",\n\t\"FL_AIMTARGET\",\n\t\"ACT_DOD_CROUCHWALK_ZOOM_BAZOOKA\",\n\t\"FL_NOTARGET\",\n\t\"ACT_DOD_HS_IDLE_STICKGRENADE\",\n\t\"ACT_RANGE_ATTACK_SNIPER_RIFLE\",\n\t\"ACT_HL2MP_JUMP\",\n\t\"ACT_DOD_WALK_IDLE_PISTOL\",\n\t\"Localize\",\n\t\"BOTTOM\",\n\t\"pairs\",\n\t\"FL_INWATER\",\n\t\"FL_FAKECLIENT\",\n\t\"FL_CLIENT\",\n\t\"ACT_VM_DEPLOY_6\",\n\t\"ACT_HL2MP_WALK_PISTOL\",\n\t\"ACT_DIE_CHESTSHOT\",\n\t\"FL_ATCONTROLS\",\n\t\"FL_ONFIRE\",\n\t\"ACT_DOD_RELOAD_MP44\",\n\t\"ACT_MP_ATTACK_AIRWALK_GRENADE_MELEE\",\n\t\"ACT_GESTURE_FLINCH_RIGHTLEG\",\n\t\"FL_ONTRAIN\",\n\t\"http\",\n\t\"ACT_HL2MP_SWIM_DUEL\",\n\t\"FL_ANIMDUCKING\",\n\t\"FL_DUCKING\",\n\t\"MarkupObject\",\n\t\"FFT_32768\",\n\t\"CompileString\",\n\t\"COLLISION_GROUP_PROJECTILE\",\n\t\"MOUSE_WHEEL_DOWN\",\n\t\"ENT_NEXTBOT\",\n\t\"COLLISION_GROUP_PLAYER\",\n\t\"FFT_16384\",\n\t\"FFT_8192\",\n\t\"KEY_PAD_6\",\n\t\"FFT_4096\",\n\t\"ACT_DOD_RELOAD_PRONE_PISTOL\",\n\t\"SENSORBONE\",\n\t\"ACT_DOD_WALK_AIM_GREASE\",\n\t\"FFT_256\",\n\t\"KEY_FIRST\",\n\t\"FCVAR_UNREGISTERED\",\n\t\"ACT_DOD_PRONEWALK_IDLE_BAR\",\n\t\"ACT_DOD_WALK_AIM_PISTOL\",\n\t\"ACT_SMG2_TOBURST\",\n\t\"FCVAR_UNLOGGED\",\n\t\"FCVAR_SERVER_CANNOT_QUERY\",\n\t\"FCVAR_REPLICATED\",\n\t\"FCVAR_PROTECTED\",\n\t\"COND_NO_PRIMARY_AMMO\",\n\t\"FCVAR_NOT_CONNECTED\",\n\t\"ACT_HANDGRENADE_THROW1\",\n\t\"ACT_SLAM_STICKWALL_ND_IDLE\",\n\t\"ACT_MP_ATTACK_STAND_PREFIRE\",\n\t\"FCVAR_NEVER_AS_STRING\",\n\t\"string\",\n\t\"ACT_WALK_RIFLE_STIMULATED\",\n\t\"FCVAR_LUA_SERVER\",\n\t\"FCVAR_LUA_CLIENT\",\n\t\"FCVAR_DEMO\",\n\t\"FCVAR_CLIENTDLL\",\n\t\"STENCIL_GREATER\",\n\t\"FCVAR_CHEAT\",\n\t\"TYPE_USERDATA\",\n\t\"Vector\",\n\t\"EFL_USE_PARTITION_WHEN_NOT_SOLID\",\n\t\"EFL_SETTING_UP_BONES\",\n\t\"ACT_BUSY_LEAN_BACK\",\n\t\"sound\",\n\t\"ACT_DOD_PRONE_DEPLOY_RIFLE\",\n\t\"DListViewLabel\",\n\t\"KEY_XBUTTON_DOWN\",\n\t\"ACT_HL2MP_IDLE_ANGRY\",\n\t\"ACT_IDLE_MELEE\",\n\t\"CONTENTS_CURRENT_180\",\n\t\"CLASS_COMBINE_HUNTER\",\n\t\"ACT_DOD_PRIMARYATTACK_PRONE_RIFLE\",\n\t\"DrawMaterialOverlay\",\n\t\"ACT_DOD_PRIMARYATTACK_RIFLE\",\n\t\"EFL_NO_PHYSCANNON_INTERACTION\",\n\t\"EFL_NO_MEGAPHYSCANNON_RAGDOLL\",\n\t\"EFL_NO_DISSOLVE\",\n\t\"EFL_NO_AUTO_EDICT_ATTACH\",\n\t\"COND_SMELL\",\n\t\"EFL_NOTIFY\",\n\t\"KEY_K\",\n\t\"EFL_IS_BEING_LIFTED_BY_BARNACLE\",\n\t\"duplicator\",\n\t\"EFL_IN_SKYBOX\",\n\t\"motionsensor\",\n\t\"KEY_PAD_MINUS\",\n\t\"ACT_HL2MP_SWIM_IDLE_RPG\",\n\t\"EFL_DONTBLOCKLOS\",\n\t\"ACT_VM_PICKUP\",\n\t\"TypeID\",\n\t\"EFL_DIRTY_SURROUNDING_COLLISION_BOUNDS\",\n\t\"EFL_DIRTY_SPATIAL_PARTITION\",\n\t\"EFL_DIRTY_SHADOWUPDATE\",\n\t\"ACT_HL2MP_JUMP_SUITCASE\",\n\t\"EFL_DIRTY_ABSTRANSFORM\",\n\t\"Sound\",\n\t\"ACT_MP_ATTACK_AIRWALK_SECONDARY\",\n\t\"AddWorldTip\",\n\t\"MATERIAL_LIGHT_DIRECTIONAL\",\n\t\"DynamicLight\",\n\t\"kRenderFxStrobeFaster\",\n\t\"EF_FOLLOWBONE\",\n\t\"VisualizeLayout\",\n\t\"ACT_HL2MP_SWIM_IDLE_DUEL\",\n\t\"ACT_DOD_PRONE_AIM_KNIFE\",\n\t\"MOVETYPE_PUSH\",\n\t\"ACT_DOD_RELOAD_CROUCH_M1CARBINE\",\n\t\"WEAPON_PROFICIENCY_POOR\",\n\t\"BONE_USED_BY_HITBOX\",\n\t\"PLAYER_START_AIMING\",\n\t\"ACT_TURN\",\n\t\"ACT_MP_ATTACK_SWIM_POSTFIRE\",\n\t\"Slider\",\n\t\"KEY_0\",\n\t\"ACT_HL2MP_WALK_CROUCH_CAMERA\",\n\t\"FL_NPC\",\n\t\"TOP\",\n\t\"ACT_GMOD_GESTURE_AGREE\",\n\t\"RIGHT\",\n\t\"DFrame\",\n\t\"LEFT\",\n\t\"FILL\",\n\t\"DMG_SLOWBURN\",\n\t\"BLOOD_COLOR_ANTLION\",\n\t\"KEY_F3\",\n\t\"ACT_DOD_RUN_IDLE_TOMMY\",\n\t\"SNDLVL_70dB\",\n\t\"PATTACH_WORLDORIGIN\",\n\t\"ACT_STEP_BACK\",\n\t\"DMG_PLASMA\",\n\t\"ACT_GMOD_GESTURE_RANGE_ZOMBIE\",\n\t\"DMG_PHYSGUN\",\n\t\"ACT_RUN_AIM_AGITATED\",\n\t\"OBS_MODE_ROAMING\",\n\t\"AddCSLuaFile\",\n\t\"HITGROUP_HEAD\",\n\t\"DMG_BUCKSHOT\",\n\t\"DMG_AIRBOAT\",\n\t\"ACT_VM_RECOIL1\",\n\t\"SCHED_FAIL_TAKE_COVER\",\n\t\"ACT_DOD_CROUCH_IDLE_PSCHRECK\",\n\t\"DMG_ALWAYSGIB\",\n\t\"Frame\",\n\t\"DMG_ENERGYBEAM\",\n\t\"ACT_HL2MP_GESTURE_RELOAD_DUEL\",\n\t\"SNDLVL_95dB\",\n\t\"SetGlobalFloat\",\n\t\"ACT_WALK_CARRY\",\n\t\"ACT_GESTURE_MELEE_ATTACK2\",\n\t\"JS_Language\",\n\t\"ACT_VM_PRIMARYATTACK_DEPLOYED_5\",\n\t\"ACT_DOD_PRONE_AIM_GREN_FRAG\",\n\t\"SCHED_ESTABLISH_LINE_OF_FIRE\",\n\t\"ACT_DOD_WALK_AIM_BAR\",\n\t\"ACT_SLAM_TRIPMINE_TO_THROW_ND\",\n\t\"STUDIO_SHADOWDEPTHTEXTURE\",\n\t\"ACT_HL2MP_GESTURE_RELOAD_FIST\",\n\t\"DMG_SLASH\",\n\t\"ACT_DOD_HS_IDLE\",\n\t\"ACT_MP_ATTACK_STAND_POSTFIRE\",\n\t\"DMG_GENERIC\",\n\t\"ACT_HL2MP_SWIM_SHOTGUN\",\n\t\"ACT_VM_DEPLOY_1\",\n\t\"D_LI\",\n\t\"CAP_DUCK\",\n\t\"ACT_GESTURE_TURN_LEFT90_FLAT\",\n\t\"FindTooltip\",\n\t\"D_FR\",\n\t\"ACT_DOD_RELOAD_PRONE_FG42\",\n\t\"KEY_EQUAL\",\n\t\"RealFrameTime\",\n\t\"WorkshopFileBase\",\n\t\"D_ER\",\n\t\"ACT_DOD_WALK_AIM_30CAL\",\n\t\"ACT_HL2MP_SWIM_IDLE_ZOMBIE\",\n\t\"ACT_DOD_PRIMARYATTACK_CROUCH_GREN_STICK\",\n\t\"CT_REBEL\",\n\t\"MAT_BLOODYFLESH\",\n\t\"ACT_HL2MP_IDLE_REVOLVER\",\n\t\"DCheckBoxLabel\",\n\t\"KEY_9\",\n\t\"CREATERENDERTARGETFLAGS_UNFILTERABLE_OK\",\n\t\"ACT_MP_SECONDARY_GRENADE1_ATTACK\",\n\t\"EFL_NOCLIP_ACTIVE\",\n\t\"ACT_HL2MP_RUN_CAMERA\",\n\t\"FL_GODMODE\",\n\t\"EmitSentence\",\n\t\"CONTENTS_TRANSLUCENT\",\n\t\"ACT_DOD_HS_CROUCH_30CAL\",\n\t\"ACT_DOD_PRONE_AIM_RIFLE\",\n\t\"ACT_GMOD_GESTURE_ITEM_GIVE\",\n\t\"CONTENTS_ORIGIN\",\n\t\"HITGROUP_GEAR\",\n\t\"CONTENTS_LADDER\",\n\t\"ACT_DOD_WALK_AIM_RIFLE\",\n\t\"CONTENTS_HITBOX\",\n\t\"CONTENTS_DEBRIS\",\n\t\"ACT_DOD_PRONE_AIM_MG\",\n\t\"CONTENTS_CURRENT_UP\",\n\t\"ACT_PHYSCANNON_ANIMATE_POST\",\n\t\"ACT_MP_ATTACK_AIRWALK_SECONDARYFIRE\",\n\t\"ACT_DISARM\",\n\t\"CONTENTS_CURRENT_90\",\n\t\"ACT_MP_RELOAD_STAND_PRIMARY_LOOP\",\n\t\"CONTENTS_CURRENT_270\",\n\t\"TEXTUREFLAGS_UNUSED_00400000\",\n\t\"CONTENTS_PLAYERCLIP\",\n\t\"Error\",\n\t\"ACT_MP_SECONDARY_GRENADE1_DRAW\",\n\t\"SNDLVL_60dB\",\n\t\"ACT_VM_RELOAD\",\n\t\"CAP_AUTO_DOORS\",\n\t\"ACT_DI_ALYX_ZOMBIE_SHOTGUN26\",\n\t\"CONTENTS_TEAM2\",\n\t\"CONTENTS_TEAM1\",\n\t\"COLLISION_GROUP_NPC_SCRIPTED\",\n\t\"CONTENTS_OPAQUE\",\n\t\"CONTENTS_BLOCKLOS\",\n\t\"ACT_VM_DIFIREMODE\",\n\t\"ACT_DOD_CROUCH_AIM_GREN_FRAG\",\n\t\"ACT_HL2MP_WALK_CROUCH_FIST\",\n\t\"CONTENTS_AUX\",\n\t\"ACT_DOD_DEPLOY_MG\",\n\t\"ACT_MP_CROUCH_DEPLOYED\",\n\t\"ACT_DOD_WALK_IDLE_30CAL\",\n\t\"CONTENTS_EMPTY\",\n\t\"SCHED_MELEE_ATTACK1\",\n\t\"ACT_90_RIGHT\",\n\t\"ACT_GESTURE_RANGE_ATTACK_SMG1\",\n\t\"COND_WEAPON_BLOCKED_BY_FRIEND\",\n\t\"TEXTUREFLAGS_UNUSED_40000000\",\n\t\"ACT_HL2MP_SWIM_SCARED\",\n\t\"ACT_DOD_PRONE_ZOOM_FORWARD_BAZOOKA\",\n\t\"COND_TOO_CLOSE_TO_ATTACK\",\n\t\"COND_TASK_FAILED\",\n\t\"ACT_POLICE_HARASS1\",\n\t\"ACT_STRAFE_LEFT\",\n\t\"DPanelList\",\n\t\"VGUIFrameTime\",\n\t\"error\",\n\t\"COND_TALKER_RESPOND_TO_QUESTION\",\n\t\"ACT_DOD_RELOAD_DEPLOYED\",\n\t\"ACT_DOD_SECONDARYATTACK_PRONE_MP40\",\n\t\"MASK_BLOCKLOS\",\n\t\"ACT_MP_SECONDARY_GRENADE2_DRAW\",\n\t\"ACT_GESTURE_TURN_RIGHT45_FLAT\",\n\t\"ACT_CROUCHING_GRENADEREADY\",\n\t\"COND_SEE_DISLIKE\",\n\t\"COND_RECEIVED_ORDERS\",\n\t\"ITexture\",\n\t\"COND_PLAYER_REMOVED_FROM_SQUAD\",\n\t\"COND_PLAYER_PUSHING\",\n\t\"ACT_DOD_PRIMARYATTACK_DEPLOYED_RIFLE\",\n\t\"ACT_VM_DEPLOYED_IDLE\",\n\t\"COND_NPC_FREEZE\",\n\t\"SCHED_NPC_FREEZE\",\n\t\"COND_NO_SECONDARY_AMMO\",\n\t\"COND_MOBBED_BY_ENEMIES\",\n\t\"COND_LOST_PLAYER\",\n\t\"COLLISION_GROUP_INTERACTIVE\",\n\t\"EF_BONEMERGE\",\n\t\"ACT_MP_SWIM_PDA\",\n\t\"DAdjustableModelPanel\",\n\t\"ACT_VM_FIRE_TO_EMPTY\",\n\t\"tonumber\",\n\t\"ACT_UNDEPLOY\",\n\t\"ACT_VM_SWINGMISS\",\n\t\"COND_IN_PVS\",\n\t\"BLEND_ONE_MINUS_DST_COLOR\",\n\t\"COND_IDLE_INTERRUPT\",\n\t\"JOYSTICK_FIRST_BUTTON\",\n\t\"COND_HEAVY_DAMAGE\",\n\t\"RENDERMODE_ENVIROMENTAL\",\n\t\"RunString\",\n\t\"COND_HEAR_SPOOKY\",\n\t\"ACT_MP_ATTACK_SWIM_GRENADE_MELEE\",\n\t\"CSoundPatch\",\n\t\"COND_HEAR_PHYSICS_DANGER\",\n\t\"ACT_WALK_STIMULATED\",\n\t\"COLLISION_GROUP_BREAKABLE_GLASS\",\n\t\"MASK_NPCSOLID_BRUSHONLY\",\n\t\"COND_GIVE_WAY\",\n\t\"ACT_GMOD_IN_CHAT\",\n\t\"BONE_USED_BY_BONE_MERGE\",\n\t\"RunConsoleCommand\",\n\t\"ACT_HL2MP_IDLE_CROUCH_PHYSGUN\",\n\t\"COND_ENEMY_WENT_NULL\",\n\t\"DMG_PREVENT_PHYSICS_FORCE\",\n\t\"ACT_DEEPIDLE4\",\n\t\"COND_ENEMY_TOO_FAR\",\n\t\"COND_CAN_RANGE_ATTACK1\",\n\t\"KEY_RSHIFT\",\n\t\"CONTENTS_TEAM4\",\n\t\"ACT_DOD_HS_IDLE_KNIFE\",\n\t\"EditablePanel\",\n\t\"ACT_HL2MP_SWIM_MAGIC\",\n\t\"GetHUDPanel\",\n\t\"ACT_COVER_PISTOL_LOW\",\n\t\"IsEntity\",\n\t\"COLLISION_GROUP_NPC_ACTOR\",\n\t\"SCHED_CHASE_ENEMY_FAILED\",\n\t\"COLLISION_GROUP_DISSOLVING\",\n\t\"ACT_HL2MP_GESTURE_RANGE_ATTACK_SUITCASE\",\n\t\"ACT_FLY\",\n\t\"COLLISION_GROUP_DOOR_BLOCKER\",\n\t\"ACT_DOD_PRIMARYATTACK_PRONE_DEPLOYED\",\n\t\"ACT_HL2MP_RUN_SUITCASE\",\n\t\"ACT_HL2MP_GESTURE_RELOAD_ZOMBIE\",\n\t\"CLASS_BARNACLE\",\n\t\"DSizeToContents\",\n\t\"ACT_DOD_HS_IDLE_MG42\",\n\t\"ACT_VM_RELOAD2\",\n\t\"ACT_HL2MP_RUN_ZOMBIE\",\n\t\"ACT_DOD_CROUCHWALK_IDLE_30CAL\",\n\t\"ACT_DOD_PRONE_AIM_BAR\",\n\t\"COLLISION_GROUP_PLAYER_MOVEMENT\",\n\t\"FSOLID_USE_TRIGGER_BOUNDS\",\n\t\"ACT_CROUCHING_SHIELD_KNOCKBACK\",\n\t\"EF_NOINTERP\",\n\t\"COLLISION_GROUP_DEBRIS\",\n\t\"ACT_VM_SWINGHIT\",\n\t\"ACT_DOD_WALK_IDLE_MG\",\n\t\"ColorRand\",\n\t\"ACT_DOD_RELOAD_CROUCH_PSCHRECK\",\n\t\"CLASS_EARTH_FAUNA\",\n\t\"CLASS_FLARE\",\n\t\"CLASS_MISSILE\",\n\t\"CLASS_STALKER\",\n\t\"ACT_DOD_RUN_AIM_SPADE\",\n\t\"ACT_VM_HOLSTER_EMPTY\",\n\t\"ACT_BIG_FLINCH\",\n\t\"CLASS_MANHACK\",\n\t\"CLASS_HEADCRAB\",\n\t\"ACT_RANGE_AIM_SMG1_LOW\",\n\t\"CLASS_COMBINE_GUNSHIP\",\n\t\"SOLID_VPHYSICS\",\n\t\"CLASS_CITIZEN_REBEL\",\n\t\"CLASS_CITIZEN_PASSIVE\",\n\t\"ACT_DOD_PRIMARYATTACK_PRONE_KNIFE\",\n\t\"CAP_USE_WEAPONS\",\n\t\"MOVECOLLIDE_FLY_SLIDE\",\n\t\"DermaMenu\",\n\t\"STUDIO_WIREFRAME\",\n\t\"MOUSE_5\",\n\t\"ACT_VM_FIDGET\",\n\t\"draw\",\n\t\"CHAN_STATIC\",\n\t\"ACT_DOD_STAND_IDLE_BAR\",\n\t\"CHAN_STREAM\",\n\t\"ACT_DOD_PRIMARYATTACK_30CAL\",\n\t\"CHAN_ITEM\",\n\t\"KEY_XBUTTON_LTRIGGER\",\n\t\"GetLoadPanel\",\n\t\"ACT_MP_RELOAD_SWIM_SECONDARY_LOOP\",\n\t\"ACT_DOD_HS_IDLE_PSCHRECK\",\n\t\"ACT_HL2MP_IDLE_CROUCH_PISTOL\",\n\t\"ACT_RELOAD_SMG1\",\n\t\"BUTTON_CODE_COUNT\",\n\t\"ACT_TURN_LEFT\",\n\t\"ACT_DOD_RELOAD_M1CARBINE\",\n\t\"CAP_NO_HIT_SQUADMATES\",\n\t\"CAP_AIM_GUN\",\n\t\"PATTACH_CUSTOMORIGIN\",\n\t\"CAP_ANIMATEDFACE\",\n\t\"CAP_INNATE_MELEE_ATTACK2\",\n\t\"STENCIL_KEEP\",\n\t\"ACT_DOD_STAND_AIM_KNIFE\",\n\t\"CancelLoading\",\n\t\"ACT_VM_SPRINT_ENTER\",\n\t\"ACT_HL2MP_IDLE_CAMERA\",\n\t\"Path\",\n\t\"CAP_WEAPON_RANGE_ATTACK2\",\n\t\"ACT_HL2MP_GESTURE_RELOAD\",\n\t\"CAP_TURN_HEAD\",\n\t\"ACT_MP_JUMP_START\",\n\t\"CONTENTS_IGNORE_NODRAW_OPAQUE\",\n\t\"ACT_RANGE_ATTACK_SHOTGUN_LOW\",\n\t\"CAP_SKIP_NAV_GROUND_CHECK\",\n\t\"ACT_MP_GESTURE_VC_NODYES_SECONDARY\",\n\t\"RENDERMODE_TRANSCOLOR\",\n\t\"CAP_MOVE_CLIMB\",\n\t\"CAP_MOVE_FLY\",\n\t\"CAP_MOVE_JUMP\",\n\t\"IVideoWriter\",\n\t\"JOYSTICK_LAST\",\n\t\"KEY_XBUTTON_RTRIGGER\",\n\t\"cleanup\",\n\t\"IN_JUMP\",\n\t\"LanguageChanged\",\n\t\"ACT_DOD_HS_CROUCH_PISTOL\",\n\t\"DTree\",\n\t\"RegisterDermaMenuForClose\",\n\t\"JOYSTICK_FIRST_POV_BUTTON\",\n\t\"MOUSE_COUNT\",\n\t\"MOUSE_LAST\",\n\t\"MOUSE_WHEEL_UP\",\n\t\"MOUSE_4\",\n\t\"MOUSE_MIDDLE\",\n\t\"KEY_XSTICK2_UP\",\n\t\"KEY_XSTICK2_LEFT\",\n\t\"JOYSTICK_LAST_AXIS_BUTTON\",\n\t\"module\",\n\t\"ACT_OBJ_PLACING\",\n\t\"ACT_MP_JUMP_PDA\",\n\t\"KEY_XSTICK1_UP\",\n\t\"Color\",\n\t\"ACT_DIE_RIGHTSIDE\",\n\t\"ACT_MP_AIRWALK\",\n\t\"KEY_XSTICK1_LEFT\",\n\t\"CloseDermaMenus\",\n\t\"KEY_XSTICK1_RIGHT\",\n\t\"ACT_HL2MP_WALK_SMG1\",\n\t\"FFT_1024\",\n\t\"ACT_DOD_SECONDARYATTACK_PRONE\",\n\t\"ACT_VM_HOLSTER_M203\",\n\t\"KEY_XBUTTON_STICK1\",\n\t\"KEY_XBUTTON_START\",\n\t\"KEY_XBUTTON_BACK\",\n\t\"ACT_DOD_STAND_AIM_C96\",\n\t\"KEY_XBUTTON_RIGHT_SHOULDER\",\n\t\"KEY_7\",\n\t\"KEY_COUNT\",\n\t\"ACT_GMOD_GESTURE_BOW\",\n\t\"ACT_DOD_PRIMARYATTACK_PRONE_DEPLOYED_30CAL\",\n\t\"DVScrollBar\",\n\t\"GetDownloadables\",\n\t\"KEY_LAST\",\n\t\"ACT_HL2MP_SWIM_IDLE_MELEE2\",\n\t\"MASK_NPCWORLDSTATIC\",\n\t\"ACT_DOD_RELOAD_GREASEGUN\",\n\t\"TEXTUREFLAGS_DEPTHRENDERTARGET\",\n\t\"KEY_F10\",\n\t\"ACT_DOD_WALK_IDLE\",\n\t\"KEY_F8\",\n\t\"KEY_F6\",\n\t\"xpcall\",\n\t\"ACT_MP_AIRWALK_MELEE\",\n\t\"KEY_F5\",\n\t\"KEY_F4\",\n\t\"KEY_APP\",\n\t\"ACT_RANGE_ATTACK_SMG1\",\n\t\"BONE_USED_BY_VERTEX_LOD2\",\n\t\"DMG_REMOVENORAGDOLL\",\n\t\"ACT_MP_MELEE_GRENADE1_IDLE\",\n\t\"ACT_DOD_STAND_AIM_PSCHRECK\",\n\t\"KEY_F1\",\n\t\"KEY_RIGHT\",\n\t\"KEY_DOWN\",\n\t\"KEY_RWIN\",\n\t\"ACT_VM_DRYFIRE_SILENCED\",\n\t\"RichText\",\n\t\"ACT_HL2MP_SWIM_PHYSGUN\",\n\t\"ACT_CROUCHING_PRIMARYATTACK\",\n\t\"ACT_DOD_PRONE_AIM_MP40\",\n\t\"BONE_PHYSICS_PROCEDURAL\",\n\t\"ProtectedCall\",\n\t\"KEY_RCONTROL\",\n\t\"ACT_VM_IDLE_DEPLOYED_3\",\n\t\"KEY_RALT\",\n\t\"LAST_SHARED_COLLISION_GROUP\",\n\t\"KEY_LSHIFT\",\n\t\"ACT_HL2MP_SWIM_MELEE2\",\n\t\"KEY_PAGEUP\",\n\t\"ACT_DOD_CROUCH_AIM_TOMMY\",\n\t\"KEY_INSERT\",\n\t\"KEY_SCROLLLOCK\",\n\t\"KEY_ESCAPE\",\n\t\"ACT_HL2MP_IDLE_CROUCH_ANGRY\",\n\t\"kRenderFxPulseSlow\",\n\t\"KEY_MINUS\",\n\t\"KEY_BACKSLASH\",\n\t\"ACT_LEAP\",\n\t\"ACT_RUN_AIM\",\n\t\"DrawColorModify\",\n\t\"UTIL_IsUselessModel\",\n\t\"EFL_HAS_PLAYER_CHILD\",\n\t\"GetRenderTarget\",\n\t\"KEY_PAD_DIVIDE\",\n\t\"ACT_RUN_RIFLE_STIMULATED\",\n\t\"ACT_BUSY_STAND\",\n\t\"KEY_PAD_5\",\n\t\"KEY_PAD_4\",\n\t\"TauntCamera\",\n\t\"SND_STOP_LOOPING\",\n\t\"ACT_SMG2_DRYFIRE2\",\n\t\"ACT_HL2MP_RUN_GRENADE\",\n\t\"debug\",\n\t\"KEY_X\",\n\t\"GO_JUMP\",\n\t\"KEY_S\",\n\t\"KEY_R\",\n\t\"ACT_SIGNAL_GROUP\",\n\t\"CREATERENDERTARGETFLAGS_AUTOMIPMAP\",\n\t\"KEY_G\",\n\t\"CAP_WEAPON_MELEE_ATTACK1\",\n\t\"KEY_A\",\n\t\"ACT_DRIVE_JEEP\",\n\t\"ACT_DOD_PRIMARYATTACK_PRONE_TOMMY\",\n\t\"KEY_8\",\n\t\"KEY_XBUTTON_Y\",\n\t\"BONE_USED_MASK\",\n\t\"KEY_6\",\n\t\"KEY_5\",\n\t\"KEY_3\",\n\t\"COND_LIGHT_DAMAGE\",\n\t\"ACT_HL2MP_IDLE_SCARED\",\n\t\"BUTTON_CODE_LAST\",\n\t\"BUTTON_CODE_NONE\",\n\t\"BUTTON_CODE_INVALID\",\n\t\"BOX_BOTTOM\",\n\t\"ACT_MP_SWIM_DEPLOYED_PRIMARY\",\n\t\"ACT_DOD_PRIMARYATTACK_C96\",\n\t\"LerpVector\",\n\t\"ACT_DOD_RELOAD_CROUCH_TOMMY\",\n\t\"BOX_FRONT\",\n\t\"BONE_USED_BY_ANYTHING\",\n\t\"BONE_USED_BY_VERTEX_LOD7\",\n\t\"ACT_VM_DEPLOY_4\",\n\t\"assert\",\n\t\"ACT_DOD_PRIMARYATTACK_PRONE_BAZOOKA\",\n\t\"ACT_DOD_PRIMARYATTACK_MP44\",\n\t\"BONE_USED_BY_VERTEX_LOD6\",\n\t\"BONE_USED_BY_VERTEX_LOD5\",\n\t\"BONE_USED_BY_VERTEX_LOD3\",\n\t\"BONE_USED_BY_VERTEX_LOD1\",\n\t\"ACT_DIESIMPLE\",\n\t\"BONE_USED_BY_VERTEX_LOD0\",\n\t\"ACT_MP_RELOAD_CROUCH_END\",\n\t\"BONE_USED_BY_ATTACHMENT\",\n\t\"SysTime\",\n\t\"KEY_LWIN\",\n\t\"ACT_CROUCHIDLE_STIMULATED\",\n\t\"ACT_MP_CROUCHWALK_MELEE\",\n\t\"menubar\",\n\t\"BLOOD_COLOR_ANTLION_WORKER\",\n\t\"GMOD_CHANNEL_PAUSED\",\n\t\"ACT_HL2MP_IDLE_CROUCH_SCARED\",\n\t\"BLOOD_COLOR_YELLOW\",\n\t\"DONT_BLEED\",\n\t\"ents\",\n\t\"ACT_DOD_CROUCH_ZOOM_PSCHRECK\",\n\t\"ACT_DOD_WALK_AIM_BOLT\",\n\t\"BLOOD_COLOR_RED\",\n\t\"GetConVar\",\n\t\"BLEND_SRC_COLOR\",\n\t\"BLEND_SRC_ALPHA_SATURATE\",\n\t\"BLEND_ONE_MINUS_DST_ALPHA\",\n\t\"BLEND_DST_ALPHA\",\n\t\"ACT_VM_IDLE_5\",\n\t\"ACT_FLINCH_RIGHTARM\",\n\t\"ACT_FIRE_END\",\n\t\"BLEND_ONE\",\n\t\"LAST_SHARED_ACTIVITY\",\n\t\"ACT_GMOD_GESTURE_MELEE_SHOVE_2HAND\",\n\t\"ACT_MP_GESTURE_FLINCH\",\n\t\"DNumberWang\",\n\t\"ACT_HL2MP_RUN_CROSSBOW\",\n\t\"ACT_GMOD_GESTURE_ITEM_DROP\",\n\t\"ACT_HL2MP_ZOMBIE_SLUMP_IDLE\",\n\t\"BLEND_ONE_MINUS_SRC_ALPHA\",\n\t\"MATERIAL_RT_DEPTH_ONLY\",\n\t\"ACT_DOD_PRIMARYATTACK_CROUCH_SPADE\",\n\t\"ACT_HL2MP_SIT_MELEE\",\n\t\"ACT_SLAM_THROW_IDLE\",\n\t\"ACT_HL2MP_WALK_MAGIC\",\n\t\"GetLoadStatus\",\n\t\"ACT_DOD_CROUCHWALK_AIM_MP44\",\n\t\"ACT_HL2MP_SIT_GRENADE\",\n\t\"ACT_HL2MP_RUN_REVOLVER\",\n\t\"ACT_DOD_STAND_AIM_GREN_STICK\",\n\t\"RenderDoF\",\n\t\"ACT_HL2MP_SIT_SHOTGUN\",\n\t\"ACT_HL2MP_SIT_PISTOL\",\n\t\"KEY_BREAK\",\n\t\"ACT_HL2MP_GESTURE_RELOAD_MELEE2\",\n\t\"ACT_FIRE_START\",\n\t\"ACT_HL2MP_WALK_MELEE2\",\n\t\"ACT_HL2MP_IDLE_MELEE2\",\n\t\"DHTMLControls\",\n\t\"ACT_HL2MP_GESTURE_RELOAD_PASSIVE\",\n\t\"ACT_HL2MP_GESTURE_RANGE_ATTACK_PASSIVE\",\n\t\"COLLISION_GROUP_PASSABLE_DOOR\",\n\t\"PanelList\",\n\t\"ACT_HL2MP_GESTURE_RANGE_ATTACK_KNIFE\",\n\t\"ACT_DOD_PRIMARYATTACK_MG\",\n\t\"ACT_HL2MP_RUN_KNIFE\",\n\t\"ACT_HL2MP_IDLE_KNIFE\",\n\t\"ACT_HL2MP_WALK_DUEL\",\n\t\"ACT_DOD_PRONE_AIM_30CAL\",\n\t\"ACT_HL2MP_SIT\",\n\t\"ACT_HL2MP_SWIM_FIST\",\n\t\"gui\",\n\t\"Lerp\",\n\t\"ACT_SLAM_STICKWALL_TO_TRIPMINE_ND\",\n\t\"ACT_HL2MP_JUMP_FIST\",\n\t\"ACT_DIERAGDOLL\",\n\t\"umsg\",\n\t\"DropEntityIfHeld\",\n\t\"ACT_DOD_STAND_IDLE_BAZOOKA\",\n\t\"ACT_HL2MP_JUMP_GRENADE\",\n\t\"ACT_GESTURE_TURN_RIGHT\",\n\t\"ACT_MP_GESTURE_FLINCH_RIGHTARM\",\n\t\"notification\",\n\t\"ACT_HL2MP_RUN\",\n\t\"ACT_HL2MP_GESTURE_RANGE_ATTACK_FIST\",\n\t\"CONTENTS_SLIME\",\n\t\"STNDRD\",\n\t\"ACT_HL2MP_RUN_FIST\",\n\t\"DColumnSheet\",\n\t\"ACT_DOD_CROUCH_IDLE_RIFLE\",\n\t\"ACT_HL2MP_WALK_PASSIVE\",\n\t\"ACT_DOD_STAND_AIM_BAZOOKA\",\n\t\"SCHED_BIG_FLINCH\",\n\t\"ACT_VM_HITCENTER\",\n\t\"ACT_DOD_STAND_IDLE_BOLT\",\n\t\"ACT_DOD_WALK_IDLE_C96\",\n\t\"ACT_DOD_WALK_IDLE_BOLT\",\n\t\"DIconBrowser\",\n\t\"ACT_HL2MP_RUN_AR2\",\n\t\"ACT_MP_VCD\",\n\t\"ACT_HL2MP_IDLE_FIST\",\n\t\"ACT_DOD_WALK_AIM_C96\",\n\t\"ACT_VM_MISSLEFT2\",\n\t\"ACT_GMOD_NOCLIP_LAYER\",\n\t\"Angle\",\n\t\"GO_LADDER_UP\",\n\t\"ACT_HL2MP_IDLE_CROUCH_CROSSBOW\",\n\t\"ACT_READINESS_RELAXED_TO_STIMULATED\",\n\t\"ACT_MP_RELOAD_STAND_PRIMARY_END\",\n\t\"ACT_180_RIGHT\",\n\t\"ACT_DOD_CROUCH_ZOOM_RIFLE\",\n\t\"ACT_IDLE_AIM_STIMULATED\",\n\t\"ACT_VM_RELOAD_INSERT_EMPTY\",\n\t\"ACT_DOD_RUN_AIM_PSCHRECK\",\n\t\"Msg\",\n\t\"TEXTUREFLAGS_NORMAL\",\n\t\"ACT_PRONE_IDLE\",\n\t\"ACT_HL2MP_SIT_PHYSGUN\",\n\t\"ACT_RUN_AGITATED\",\n\t\"DModelPanel\",\n\t\"OpenFolder\",\n\t\"RENDERMODE_NONE\",\n\t\"ACT_VM_IFIREMODE\",\n\t\"ACT_POLICE_HARASS2\",\n\t\"gamemode\",\n\t\"ACT_WALK_AIM_RELAXED\",\n\t\"CLASS_ANTLION\",\n\t\"ACT_DOD_RELOAD_PRONE_DEPLOYED\",\n\t\"ACT_MP_JUMP_FLOAT_MELEE\",\n\t\"ACT_MP_GESTURE_FLINCH_MELEE\",\n\t\"SNDLVL_20dB\",\n\t\"ACT_VM_READY_M203\",\n\t\"ACT_HL2MP_SWIM_RPG\",\n\t\"ACT_GMOD_GESTURE_ITEM_PLACE\",\n\t\"weapons\",\n\t\"ACT_IDLE_AIM_AGITATED\",\n\t\"TEXTUREFLAGS_ENVMAP\",\n\t\"ACT_VM_SHOOTLAST\",\n\t\"ACT_DOD_CROUCH_IDLE_GREASE\",\n\t\"GLOBAL_OFF\",\n\t\"ParticleEmitter\",\n\t\"VGUIRect\",\n\t\"ACT_OVERLAY_SHIELD_DOWN\",\n\t\"ACT_VM_ISHOOT_LAST\",\n\t\"DComboBox\",\n\t\"ACT_VM_FIREMODE\",\n\t\"ACT_VM_IRECOIL2\",\n\t\"ACT_VM_IIDLE_EMPTY\",\n\t\"ACT_DOD_STAND_AIM_TOMMY\",\n\t\"KEY_Y\",\n\t\"ACT_VM_IDLE_LOWERED\",\n\t\"ACT_HL2MP_SWIM_IDLE_CAMERA\",\n\t\"DMenuOption\",\n\t\"ACT_VM_DEPLOYED_LIFTED_OUT\",\n\t\"ACT_VM_DEPLOYED_LIFTED_IN\",\n\t\"ACT_DOD_CROUCHWALK_IDLE_MP40\",\n\t\"ACT_DOD_STAND_AIM_PISTOL\",\n\t\"ACT_PLAYER_IDLE_FIRE\",\n\t\"ACT_VM_DEPLOYED_IRON_DRYFIRE\",\n\t\"ACT_VM_DEPLOYED_IRON_FIRE\",\n\t\"ACT_VM_DEPLOYED_IRON_IDLE\",\n\t\"PhysObj\",\n\t\"isnumber\",\n\t\"ACT_VM_DEPLOYED_RELOAD_EMPTY\",\n\t\"ACT_RUN_AIM_STEALTH_PISTOL\",\n\t\"ACT_HL2MP_RUN_PANICKED\",\n\t\"ACT_VM_DEPLOYED_RELOAD\",\n\t\"ACT_VM_DEPLOYED_DRYFIRE\",\n\t\"ACT_VM_DEPLOYED_FIRE\",\n\t\"ACT_VM_HOLSTER\",\n\t\"COND_PHYSICS_DAMAGE\",\n\t\"ACT_TURNLEFT45\",\n\t\"ACT_DOD_HS_CROUCH_STICKGRENADE\",\n\t\"ACT_VM_HITKILL\",\n\t\"TGAImage\",\n\t\"RenderAngles\",\n\t\"ACT_RUN_HURT\",\n\t\"ACT_RESET\",\n\t\"ACT_SLAM_THROW_THROW_ND2\",\n\t\"ACT_MP_RUN_BUILDING\",\n\t\"ACT_FLINCH_STOMACH\",\n\t\"ACT_VM_SWINGHARD\",\n\t\"ACT_VM_PRIMARYATTACK_4\",\n\t\"ACT_MP_ATTACK_AIRWALK_GRENADE_PRIMARY\",\n\t\"ACT_DOD_PRIMARYATTACK_GREN_STICK\",\n\t\"ACT_DOD_HS_CROUCH_MP44\",\n\t\"ACT_VM_IIDLE\",\n\t\"ACT_HL2MP_JUMP_SHOTGUN\",\n\t\"ACT_VM_IIN_EMPTY\",\n\t\"Stack\",\n\t\"HITGROUP_RIGHTLEG\",\n\t\"ACT_VM_PRIMARYATTACK_SILENCED\",\n\t\"dragndrop\",\n\t\"ACT_HL2MP_IDLE_CROUCH_ZOMBIE_01\",\n\t\"ACT_SIGNAL_LEFT\",\n\t\"ACT_MP_GESTURE_VC_FINGERPOINT\",\n\t\"SavePresets\",\n\t\"PropSelect\",\n\t\"ACT_WALK_ON_FIRE\",\n\t\"ACT_VM_READY\",\n\t\"ENT_FILTER\",\n\t\"ACT_SLAM_DETONATOR_IDLE\",\n\t\"ACT_HOVER\",\n\t\"SNDLVL_NONE\",\n\t\"ACT_MP_RELOAD_STAND_LOOP\",\n\t\"ACT_VM_CRAWL_EMPTY\",\n\t\"ACT_DOD_PRONEWALK_IDLE_TOMMY\",\n\t\"cam\",\n\t\"ACT_VM_CRAWL\",\n\t\"ACT_HL2MP_GESTURE_RANGE_ATTACK_MAGIC\",\n\t\"ACT_SPECIAL_ATTACK2\",\n\t\"JS_Workshop\",\n\t\"ACT_DOD_PRONEWALK_IDLE_MP44\",\n\t\"COLLISION_GROUP_VEHICLE_CLIP\",\n\t\"ACT_HL2MP_GESTURE_RANGE_ATTACK_SCARED\",\n\t\"COND_WEAPON_HAS_LOS\",\n\t\"DNumSlider\",\n\t\"ACT_MP_JUMP_FLOAT\",\n\t\"ACT_HL2MP_RUN_SLAM\",\n\t\"KEY_END\",\n\t\"ConVarExists\",\n\t\"ACT_DOD_RUN_IDLE_GREASE\",\n\t\"ACT_IDLE_PACKAGE\",\n\t\"GetGlobalString\",\n\t\"DHTML\",\n\t\"ACT_SIGNAL_FORWARD\",\n\t\"ACT_DOD_WALK_AIM\",\n\t\"ACT_GESTURE_FLINCH_LEFTLEG\",\n\t\"ACT_DOD_CROUCHWALK_ZOOM_PSCHRECK\",\n\t\"ACT_GLOCK_SHOOTEMPTY\",\n\t\"ACT_DOD_WALK_IDLE_BAR\",\n\t\"ACT_DOD_SECONDARYATTACK_CROUCH_TOMMY\",\n\t\"SafeRemoveEntityDelayed\",\n\t\"ACT_HL2MP_IDLE_CROUCH_SHOTGUN\",\n\t\"ACT_GAUSS_SPINUP\",\n\t\"ACT_IDLE_AIM_STEALTH\",\n\t\"CRecipientFilter\",\n\t\"ACT_READINESS_PISTOL_RELAXED_TO_STIMULATED_WALK\",\n\t\"ACT_HL2MP_JUMP_MELEE\",\n\t\"ACT_DOD_CROUCHWALK_AIM_MP40\",\n\t\"tostring\",\n\t\"ACT_DOD_PRIMARYATTACK_PRONE_GREN_STICK\",\n\t\"ACT_DOD_PRONE_ZOOM_BAZOOKA\",\n\t\"ACT_CLIMB_DISMOUNT\",\n\t\"SCHED_HIDE_AND_RELOAD\",\n\t\"TRACER_BEAM\",\n\t\"TYPE_TABLE\",\n\t\"ACT_DOD_PRIMARYATTACK_PRONE_MP40\",\n\t\"ACT_HL2MP_IDLE_PASSIVE\",\n\t\"ACT_HL2MP_IDLE_MELEE\",\n\t\"ACT_HL2MP_SWIM_CROSSBOW\",\n\t\"ACT_HL2MP_SWIM_IDLE_CROSSBOW\",\n\t\"ACT_DOD_PRONE_ZOOM_BOLT\",\n\t\"ACT_HL2MP_JUMP_CROSSBOW\",\n\t\"ACT_BUSY_LEAN_BACK_EXIT\",\n\t\"ACT_RANGE_ATTACK_ML\",\n\t\"player\",\n\t\"SF_CITIZEN_NOT_COMMANDABLE\",\n\t\"ACT_GESTURE_MELEE_ATTACK_SWING\",\n\t\"ACT_GESTURE_RANGE_ATTACK_SLAM\",\n\t\"ACT_MP_RELOAD_CROUCH_PRIMARY_LOOP\",\n\t\"DOFModeHack\",\n\t\"ACT_HL2MP_WALK_CROUCH_CROSSBOW\",\n\t\"WEAPON_PROFICIENCY_PERFECT\",\n\t\"ACT_VM_IDLE_8\",\n\t\"ACT_HL2MP_SWIM_ANGRY\",\n\t\"ACT_VM_PRIMARYATTACK_7\",\n\t\"ACT_GESTURE_SMALL_FLINCH\",\n\t\"ACT_RUN_SCARED\",\n\t\"ACT_VM_DEPLOY_7\",\n\t\"ACT_HL2MP_RUN_RPG\",\n\t\"ACT_WALK_AIM_STIMULATED\",\n\t\"ACT_HL2MP_IDLE_PHYSGUN\",\n\t\"LerpAngle\",\n\t\"ACT_MP_MELEE_GRENADE2_ATTACK\",\n\t\"ACT_MP_GRENADE2_IDLE\",\n\t\"isentity\",\n\t\"BroadcastLua\",\n\t\"ACT_DOD_CROUCHWALK_IDLE_MG\",\n\t\"ACT_HL2MP_WALK_CROUCH_DUEL\",\n\t\"ACT_HL2MP_IDLE_CROUCH_DUEL\",\n\t\"ACT_RUN_AIM_PISTOL\",\n\t\"ACT_CROSSBOW_FIDGET_UNLOADED\",\n\t\"ai_schedule\",\n\t\"CreateConVar\",\n\t\"ACT_WALK_AGITATED\",\n\t\"ACT_MP_RELOAD_AIRWALK_PRIMARY_END\",\n\t\"derma\",\n\t\"ACT_HL2MP_IDLE_DUEL\",\n\t\"SCHED_DUCK_DODGE\",\n\t\"ACT_IDLE_ANGRY\",\n\t\"ACT_VM_PRIMARYATTACK_6\",\n\t\"ACT_DOD_CROUCH_IDLE_BOLT\",\n\t\"ACT_DOD_STAND_AIM_MP44\",\n\t\"TYPE_PARTICLESYSTEM\",\n\t\"SURF_NODRAW\",\n\t\"ACT_VM_DEPLOYED_OUT\",\n\t\"KEY_PAD_1\",\n\t\"ACT_DIE_BACKSIDE\",\n\t\"ACT_DOD_RUN_AIM_KNIFE\",\n\t\"ACT_DOD_WALK_IDLE_RIFLE\",\n\t\"GWEN\",\n\t\"print\",\n\t\"ACT_HL2MP_IDLE_GRENADE\",\n\t\"ACT_DOD_SPRINT_IDLE_TOMMY\",\n\t\"EFL_FORCE_CHECK_TRANSMIT\",\n\t\"KEY_C\",\n\t\"ACT_RELOAD_START\",\n\t\"ACT_HL2MP_GESTURE_RELOAD_RPG\",\n\t\"CONTENTS_WINDOW\",\n\t\"language\",\n\t\"ACT_HL2MP_GESTURE_RANGE_ATTACK_RPG\",\n\t\"ACT_HL2MP_GESTURE_RELOAD_SHOTGUN\",\n\t\"ACT_STEP_LEFT\",\n\t\"ACT_SIGNAL_TAKECOVER\",\n\t\"CLASS_METROPOLICE\",\n\t\"DMG_SHOCK\",\n\t\"baseclass\",\n\t\"ACT_HL2MP_WALK_CROUCH_MELEE\",\n\t\"ACT_MP_ATTACK_SWIM_PDA\",\n\t\"ACT_DOD_RUN_ZOOM_RIFLE\",\n\t\"ACT_MP_GESTURE_FLINCH_CHEST\",\n\t\"ACT_VM_PRIMARYATTACK_1\",\n\t\"ACT_VM_HITRIGHT\",\n\t\"ACT_DIEBACKWARD\",\n\t\"ACT_HL2MP_SWIM_IDLE_SHOTGUN\",\n\t\"ACT_VM_IDLE_6\",\n\t\"ConsoleAutoComplete\",\n\t\"ACT_MP_ATTACK_STAND_SECONDARYFIRE\",\n\t\"ACT_GESTURE_RANGE_ATTACK2_LOW\",\n\t\"ACT_RANGE_ATTACK_THROW\",\n\t\"ACT_DOD_CROUCHWALK_AIM_SPADE\",\n\t\"DMG_BLAST\",\n\t\"ACT_ROLL_LEFT\",\n\t\"ACT_HL2MP_RUN_SHOTGUN\",\n\t\"ACT_DOD_PRIMARYATTACK_BOLT\",\n\t\"ACT_HL2MP_WALK_SHOTGUN\",\n\t\"TimedSin\",\n\t\"ACT_HL2MP_GESTURE_RELOAD_AR2\",\n\t\"Button\",\n\t\"ACT_SLAM_THROW_THROW\",\n\t\"ACT_HL2MP_GESTURE_RANGE_ATTACK_AR2\",\n\t\"ACT_READINESS_PISTOL_AGITATED_TO_STIMULATED\",\n\t\"ACT_DOD_STAND_AIM_GREASE\",\n\t\"ACT_HL2MP_WALK_CROUCH_AR2\",\n\t\"ACT_HL2MP_IDLE_CROUCH_AR2\",\n\t\"ACT_WALK_AIM_RIFLE_STIMULATED\",\n\t\"ACT_GRENADE_ROLL\",\n\t\"ACT_BUSY_LEAN_LEFT_ENTRY\",\n\t\"ACT_HL2MP_SWIM_SMG1\",\n\t\"TYPE_INVALID\",\n\t\"TEXTUREFLAGS_UNUSED_00200000\",\n\t\"ACT_IDLE_SMG1\",\n\t\"RENDERGROUP_VIEWMODEL\",\n\t\"ACT_DOD_RELOAD_PRONE_GREASEGUN\",\n\t\"AddBackgroundImage\",\n\t\"SoundDuration\",\n\t\"ACT_MP_GESTURE_VC_THUMBSUP_MELEE\",\n\t\"ACT_VM_UNDEPLOY_2\",\n\t\"ACT_HL2MP_RUN_SMG1\",\n\t\"ACT_DOD_HS_CROUCH_MG42\",\n\t\"ACT_WALK_RIFLE\",\n\t\"ACT_HL2MP_IDLE_SMG1\",\n\t\"ACT_HL2MP_SWIM_PISTOL\",\n\t\"DModelSelect\",\n\t\"ACT_HL2MP_SWIM_IDLE_PISTOL\",\n\t\"ACT_HL2MP_JUMP_PISTOL\",\n\t\"ACT_RANGE_ATTACK_AR2_LOW\",\n\t\"ACT_DOD_PRONEWALK_AIM_GREN_FRAG\",\n\t\"ACT_DOD_PRONE_AIM_TOMMY\",\n\t\"ACT_VM_IDLE_1\",\n\t\"ACT_SLAM_THROW_DETONATOR_HOLSTER\",\n\t\"ACT_PLAYER_RUN_FIRE\",\n\t\"ACT_USE\",\n\t\"ACT_VM_UNDEPLOY_EMPTY\",\n\t\"ACT_MELEE_ATTACK_SWING\",\n\t\"ACT_HL2MP_RUN_PISTOL\",\n\t\"ACT_BARNACLE_CHEW\",\n\t\"ACT_MP_GESTURE_VC_NODYES_BUILDING\",\n\t\"ACT_HL2MP_GESTURE_RANGE_ATTACK\",\n\t\"ACT_DOD_RUN_AIM_MG\",\n\t\"ACT_HL2MP_IDLE\",\n\t\"chat\",\n\t\"ACT_GLIDE\",\n\t\"GetViewEntity\",\n\t\"ACT_WALK_CROUCH_AIM\",\n\t\"vgui\",\n\t\"ACT_SLAM_TRIPMINE_ATTACH2\",\n\t\"ACT_MP_GESTURE_FLINCH_STOMACH\",\n\t\"ACT_HL2MP_SWIM_IDLE_SUITCASE\",\n\t\"HTML\",\n\t\"ACT_RUN_STEALTH_PISTOL\",\n\t\"ACT_VM_PRIMARYATTACK_5\",\n\t\"RecipientFilter\",\n\t\"EFL_DIRTY_ABSVELOCITY\",\n\t\"IN_BACK\",\n\t\"CHAN_AUTO\",\n\t\"ACT_RUN_AIM_RELAXED\",\n\t\"PositionSpawnIcon\",\n\t\"ACT_DOD_RUN_IDLE_MP40\",\n\t\"ACT_MP_CROUCH_PRIMARY\",\n\t\"ACT_VM_RELOADEMPTY\",\n\t\"ACT_HL2MP_SWIM_ZOMBIE\",\n\t\"CT_UNIQUE\",\n\t\"ACT_MP_ATTACK_CROUCH_PREFIRE\",\n\t\"GetHostName\",\n\t\"ACT_HL2MP_SIT_FIST\",\n\t\"ACT_DOD_STAND_IDLE_MP44\",\n\t\"ACT_WALK_AIM_AGITATED\",\n\t\"AvatarImage\",\n\t\"COLLISION_GROUP_WEAPON\",\n\t\"DProgress\",\n\t\"KEY_F12\",\n\t\"ACT_RANGE_ATTACK_SLAM\",\n\t\"ACT_DOD_PRONEWALK_AIM_GREN_STICK\",\n\t\"ACT_VM_DRAW_M203\",\n\t\"ACT_HL2MP_GESTURE_RELOAD_SCARED\",\n\t\"ACT_HL2MP_IDLE_SUITCASE\",\n\t\"BLOOD_COLOR_GREEN\",\n\t\"ACT_HL2MP_WALK_SCARED\",\n\t\"undo\",\n\t\"ACT_VM_PRIMARYATTACK_DEPLOYED_8\",\n\t\"KEY_NONE\",\n\t\"ACT_HL2MP_JUMP_ANGRY\",\n\t\"IGModAudioChannel\",\n\t\"ACT_HL2MP_GESTURE_RELOAD_ANGRY\",\n\t\"hammer\",\n\t\"Label\",\n\t\"IsMounted\",\n\t\"ACT_MP_GESTURE_FLINCH_SECONDARY\",\n\t\"ENT_POINT\",\n\t\"ACT_HL2MP_SWIM_GRENADE\",\n\t\"ACT_WALK_AIM_STEALTH\",\n\t\"KEY_2\",\n\t\"pcall\",\n\t\"ACT_HL2MP_IDLE_CROUCH_CAMERA\",\n\t\"SND_IGNORE_NAME\",\n\t\"isbool\",\n\t\"ACT_DI_ALYX_HEADCRAB_MELEE\",\n\t\"ACT_MP_SWIM_IDLE\",\n\t\"AddonMaterial\",\n\t\"ACT_DOD_RUN_AIM_GREASE\",\n\t\"ACT_HL2MP_JUMP_REVOLVER\",\n\t\"ACT_HL2MP_GESTURE_RANGE_ATTACK_REVOLVER\",\n\t\"Derma_DrawBackgroundBlur\",\n\t\"ACT_VICTORY_DANCE\",\n\t\"markup\",\n\t\"NAV_MESH_JUMP\",\n\t\"ACT_HL2MP_WALK_REVOLVER\",\n\t\"CT_DOWNTRODDEN\",\n\t\"getfenv\",\n\t\"PrecacheSentenceGroup\",\n\t\"MAT_GLASS\",\n\t\"file\",\n\t\"ACT_WALK_PACKAGE\",\n\t\"ConVar\",\n\t\"DisableClipping\",\n\t\"ACT_GESTURE_TURN_RIGHT45\",\n\t\"ACT_HL2MP_JUMP_MAGIC\",\n\t\"ACT_FLINCH_PHYSICS\",\n\t\"ACT_BARNACLE_CHOMP\",\n\t\"Derma_Install_Convar_Functions\",\n\t\"AddOriginToPVS\",\n\t\"ACT_RANGE_ATTACK_PISTOL\",\n\t\"ACT_HL2MP_WALK_CROUCH_MAGIC\",\n\t\"ACT_HL2MP_RUN_MAGIC\",\n\t\"ACT_HL2MP_IDLE_MAGIC\",\n\t\"ACT_HL2MP_SIT_SLAM\",\n\t\"ACT_ZOMBIE_CLIMB_START\",\n\t\"ACT_DOD_PRONEWALK_IDLE_C96\",\n\t\"ACT_BUSY_SIT_CHAIR_ENTRY\",\n\t\"ACT_DOD_ZOOMLOAD_BAZOOKA\",\n\t\"ACT_DOD_RUN_IDLE_BAZOOKA\",\n\t\"ACT_RUN_CROUCH_AIM_RIFLE\",\n\t\"ACT_OBJ_ASSEMBLING\",\n\t\"ACT_GMOD_GESTURE_RANGE_FRENZY\",\n\t\"TEXTUREFLAGS_BORDER\",\n\t\"ACT_MP_PRIMARY_GRENADE2_ATTACK\",\n\t\"ACT_HL2MP_WALK_CROUCH_ZOMBIE_05\",\n\t\"widgets\",\n\t\"ACT_DOD_PRONE_AIM_GREASE\",\n\t\"Player\",\n\t\"ACT_HL2MP_WALK_CROUCH_ZOMBIE_04\",\n\t\"ACT_HL2MP_WALK_CROUCH_ZOMBIE_02\",\n\t\"SNDLVL_IDLE\",\n\t\"File\",\n\t\"ACT_RUN_AIM_STIMULATED\",\n\t\"ACT_HL2MP_WALK_ZOMBIE_03\",\n\t\"ACT_HL2MP_WALK_ZOMBIE_02\",\n\t\"ACT_HL2MP_RUN_CHARGING\",\n\t\"DColorCube\",\n\t\"LAST_VISIBLE_CONTENTS\",\n\t\"ACT_DOD_STAND_IDLE_TOMMY\",\n\t\"ACT_GMOD_TAUNT_CHEER\",\n\t\"ACT_DOD_PRIMARYATTACK_PRONE_PSCHRECK\",\n\t\"ACT_OVERLAY_SHIELD_UP_IDLE\",\n\t\"ACT_GESTURE_RANGE_ATTACK1_LOW\",\n\t\"ACT_CROUCHING_SHIELD_UP_IDLE\",\n\t\"ACT_VM_IOUT_EMPTY\",\n\t\"ACT_GMOD_GESTURE_WAVE\",\n\t\"ACT_GMOD_TAUNT_SALUTE\",\n\t\"CAP_MOVE_GROUND\",\n\t\"ACT_RUN_RPG\",\n\t\"NamedColor\",\n\t\"ACT_GMOD_GESTURE_BECON\",\n\t\"MOVETYPE_CUSTOM\",\n\t\"ACT_DOD_RELOAD_PRONE_BAZOOKA\",\n\t\"COND_SEE_FEAR\",\n\t\"ACT_GESTURE_FLINCH_BLAST\",\n\t\"ScrW\",\n\t\"ACT_RANGE_ATTACK_TRIPWIRE\",\n\t\"ACT_MP_GESTURE_VC_HANDMOUTH_PDA\",\n\t\"ACT_GESTURE_FLINCH_LEFTARM\",\n\t\"CAP_USE\",\n\t\"ACT_TRIPMINE_GROUND\",\n\t\"ACT_IDLE_STIMULATED\",\n\t\"ACT_IDLE\",\n\t\"ACT_VM_IDLE_DEPLOYED_6\",\n\t\"SCHED_ALERT_STAND\",\n\t\"ACT_RUN_CROUCH_RIFLE\",\n\t\"IN_ZOOM\",\n\t\"FL_ONGROUND\",\n\t\"ACT_MP_GESTURE_VC_HANDMOUTH_MELEE\",\n\t\"ACT_VM_RELOAD_END\",\n\t\"ACT_DOD_HS_IDLE_PISTOL\",\n\t\"ACT_MP_GESTURE_VC_FISTPUMP_SECONDARY\",\n\t\"MAT_DIRT\",\n\t\"SIM_LOCAL_FORCE\",\n\t\"ACT_GESTURE_RANGE_ATTACK2\",\n\t\"ACT_MP_GESTURE_VC_NODYES_PRIMARY\",\n\t\"ACT_MP_GESTURE_VC_FINGERPOINT_PRIMARY\",\n\t\"resource\",\n\t\"ACT_MP_ATTACK_STAND_PDA\",\n\t\"Material\",\n\t\"ACT_MP_JUMP_FLOAT_PDA\",\n\t\"ACT_MP_JUMP_START_PDA\",\n\t\"ACT_SLAM_STICKWALL_DETONATOR_HOLSTER\",\n\t\"ACT_GESTURE_FLINCH_BLAST_DAMAGED\",\n\t\"DIRECTIONAL_USE\",\n\t\"HITGROUP_STOMACH\",\n\t\"CMoveData\",\n\t\"DTileLayout\",\n\t\"ACT_MP_CROUCH_PDA\",\n\t\"ACT_ZOMBIE_CLIMB_END\",\n\t\"KEY_Q\",\n\t\"ACT_MP_ATTACK_CROUCH_GRENADE_BUILDING\",\n\t\"GetDemoFileDetails\",\n\t\"ACT_DIE_FRONTSIDE\",\n\t\"ACT_MP_SWIM_PRIMARY\",\n\t\"ACT_MP_ATTACK_CROUCH_BUILDING\",\n\t\"DEFINE_BASECLASS\",\n\t\"ACT_SLAM_STICKWALL_DETONATE\",\n\t\"ACT_MP_ATTACK_STAND_BUILDING\",\n\t\"DHorizontalDivider\",\n\t\"ACT_HL2MP_GESTURE_RELOAD_MAGIC\",\n\t\"ACT_WALK_CROUCH_AIM_RIFLE\",\n\t\"Awesomium\",\n\t\"ACT_MP_CROUCHWALK_BUILDING\",\n\t\"ACT_MP_AIRWALK_BUILDING\",\n\t\"ACT_MP_WALK_BUILDING\",\n\t\"ACT_DOD_PRONEWALK_IDLE_GREASE\",\n\t\"MATERIAL_LINE_LOOP\",\n\t\"KEY_F2\",\n\t\"ACT_MP_SECONDARY_GRENADE2_ATTACK\",\n\t\"ACT_MP_ATTACK_CROUCH_POSTFIRE\",\n\t\"ACT_MP_GESTURE_VC_FINGERPOINT_SECONDARY\",\n\t\"DPropertySheet\",\n\t\"ACT_DOD_SPRINT_IDLE_30CAL\",\n\t\"GameDetails\",\n\t\"USE_OFF\",\n\t\"MAT_PLASTIC\",\n\t\"ACT_VM_IDLE_DEPLOYED_8\",\n\t\"ACT_MP_GRENADE2_ATTACK\",\n\t\"ACT_MP_GRENADE1_ATTACK\",\n\t\"ACT_DOD_CROUCHWALK_AIM_TOMMY\",\n\t\"ACT_MP_RELOAD_STAND_SECONDARY_END\",\n\t\"ACT_VM_FIZZLE\",\n\t\"GetGlobalFloat\",\n\t\"ACT_SPRINT\",\n\t\"ACT_WALK\",\n\t\"SCREENFADE\",\n\t\"tobool\",\n\t\"ACT_DOD_PRONE_AIM_SPADE\",\n\t\"ACT_DOD_RUN_AIM_GREN_FRAG\",\n\t\"KEY_PAD_2\",\n\t\"DMG_CLUB\",\n\t\"ACT_WALK_AIM_STEALTH_PISTOL\",\n\t\"GO_WEST\",\n\t\"IN_GRENADE2\",\n\t\"SOLID_OBB\",\n\t\"EF_PARENT_ANIMATES\",\n\t\"ACT_CROUCHIDLE\",\n\t\"BONE_PHYSICALLY_SIMULATED\",\n\t\"ACT_HL2MP_SIT_CROSSBOW\",\n\t\"ACT_MP_CROUCH_MELEE\",\n\t\"DFileBrowser\",\n\t\"SOLID_BBOX\",\n\t\"CHAN_WEAPON\",\n\t\"ErrorNoHalt\",\n\t\"ErrorNoHaltWithStack\",\n\t\"ACT_HL2MP_RUN_DUEL\",\n\t\"ACT_DOD_WALK_ZOOM_BAZOOKA\",\n\t\"type\",\n\t\"MOVETYPE_STEP\",\n\t\"ACT_MP_GESTURE_FLINCH_HEAD\",\n\t\"ACT_SLAM_THROW_TO_TRIPMINE_ND\",\n\t\"ACT_DOD_HS_IDLE_MP44\",\n\t\"ACT_DOD_STAND_IDLE_PISTOL\",\n\t\"ACT_GESTURE_RANGE_ATTACK_AR1\",\n\t\"ACT_DOD_RUN_AIM_PISTOL\",\n\t\"ACT_DIE_HEADSHOT\",\n\t\"ACT_MP_JUMP_FLOAT_SECONDARY\",\n\t\"ACT_FLINCH_LEFTARM\",\n\t\"NumModelSkins\",\n\t\"ACT_MP_RELOAD_SWIM_PRIMARY_END\",\n\t\"DrawSharpen\",\n\t\"ACT_VM_USABLE_TO_UNUSABLE\",\n\t\"ACT_DOD_WALK_IDLE_BAZOOKA\",\n\t\"ACT_VM_PRIMARYATTACK_DEPLOYED_EMPTY\",\n\t\"ACT_MP_ATTACK_AIRWALK_PRIMARY\",\n\t\"ACT_MP_ATTACK_STAND_PRIMARY_DEPLOYED\",\n\t\"ACT_RUN_CROUCH_RPG\",\n\t\"TYPE_NAVLADDER\",\n\t\"DebugInfo\",\n\t\"ACT_HL2MP_RUN_MELEE2\",\n\t\"ACT_SLAM_THROW_THROW2\",\n\t\"BOX_LEFT\",\n\t\"ACT_DOD_CROUCHWALK_AIM_30CAL\",\n\t\"ACT_MP_RELOAD_SWIM\",\n\t\"ACT_GESTURE_BARNACLE_STRANGLE\",\n\t\"ACT_MP_RELOAD_CROUCH_LOOP\",\n\t\"CONTENTS_CURRENT_DOWN\",\n\t\"DProperty_Boolean\",\n\t\"ACT_MP_ATTACK_SWIM_GRENADE\",\n\t\"math\",\n\t\"ImageCheckBox\",\n\t\"ACT_MP_ATTACK_CROUCH_PRIMARYFIRE\",\n\t\"ACT_DOD_PRIMARYATTACK_PRONE\",\n\t\"ACT_MP_ATTACK_STAND_PRIMARYFIRE\",\n\t\"ACT_SHOTGUN_IDLE_DEEP\",\n\t\"CAP_OPEN_DOORS\",\n\t\"RecordDemoFrame\",\n\t\"MAT_COMPUTER\",\n\t\"Vehicle\",\n\t\"ACT_HL2MP_IDLE_SLAM\",\n\t\"ACT_MP_CROUCH_DEPLOYED_IDLE\",\n\t\"FrameNumber\",\n\t\"ACT_DOD_PLANT_TNT\",\n\t\"ACT_DOD_HS_CROUCH_TOMMY\",\n\t\"CLASS_NONE\",\n\t\"ACT_DOD_HS_IDLE_30CAL\",\n\t\"ACT_MP_GESTURE_FLINCH_LEFTARM\",\n\t\"ACT_DOD_PRIMARYATTACK_PRONE_C96\",\n\t\"ACT_DOD_SECONDARYATTACK_CROUCH\",\n\t\"ACT_DOD_PRONE_ZOOM_FORWARD_PSCHRECK\",\n\t\"ACT_DOD_CROUCH_IDLE_PISTOL\",\n\t\"ACT_GESTURE_RANGE_ATTACK_THROW\",\n\t\"ACT_DEPLOY_IDLE\",\n\t\"ACT_DOD_RELOAD_PRONE_BAR\",\n\t\"ACT_DOD_RELOAD_PRONE_MP40\",\n\t\"ACT_COWER\",\n\t\"GESTURE_SLOT_JUMP\",\n\t\"ACT_DOD_RELOAD_DEPLOYED_MG34\",\n\t\"ACT_DOD_RELOAD_DEPLOYED_MG\",\n\t\"ACT_IDLE_RPG_RELAXED\",\n\t\"AccessorFunc\",\n\t\"ACT_DOD_RELOAD_CROUCH_PISTOL\",\n\t\"GetConVarString\",\n\t\"ScreenScale\",\n\t\"ACT_DOD_RELOAD_CROUCH_MP40\",\n\t\"ACT_SLAM_THROW_DETONATE\",\n\t\"ACT_GESTURE_FLINCH_BLAST_SHOTGUN\",\n\t\"ACT_DOD_WALK_IDLE_MP40\",\n\t\"CtrlListBox\",\n\t\"SIM_GLOBAL_ACCELERATION\",\n\t\"ACT_HL2MP_WALK_CROUCH_KNIFE\",\n\t\"ACT_DOD_RUN_AIM_TOMMY\",\n\t\"SANDBOX\",\n\t\"ACT_PHYSCANNON_ANIMATE\",\n\t\"ACT_SLAM_DETONATOR_STICKWALL_DRAW\",\n\t\"ACT_SLAM_STICKWALL_ATTACH\",\n\t\"ACT_GESTURE_TURN_LEFT\",\n\t\"ACT_VM_IDLE_DEPLOYED_1\",\n\t\"ACT_BUSY_LEAN_LEFT\",\n\t\"EFL_NO_ROTORWASH_PUSH\",\n\t\"ACT_DOD_SECONDARYATTACK_RIFLE\",\n\t\"IMAGE_FORMAT_ARGB8888\",\n\t\"DMG_POISON\",\n\t\"SortedPairsByMemberValue\",\n\t\"ACT_DOD_WALK_ZOOM_BOLT\",\n\t\"SF_ROLLERMINE_FRIENDLY\",\n\t\"ACT_MP_JUMP_FLOAT_BUILDING\",\n\t\"LoadLastMap\",\n\t\"ACT_HL2MP_SWIM_MELEE\",\n\t\"CLASS_PLAYER_ALLY_VITAL\",\n\t\"ACT_DOD_PRONE_AIM_PISTOL\",\n\t\"ACT_DOD_WALK_AIM_KNIFE\",\n\t\"ACT_VM_DEPLOY_8\",\n\t\"ACT_GMOD_GESTURE_ITEM_THROW\",\n\t\"SpawnIcon\",\n\t\"ACT_DOD_CROUCH_IDLE_MG\",\n\t\"gmsave\",\n\t\"CSEnt\",\n\t\"SOLID_OBB_YAW\",\n\t\"ACT_GESTURE_TURN_RIGHT90_FLAT\",\n\t\"bf_read\",\n\t\"ACT_DOD_STAND_AIM_BOLT\",\n\t\"ACT_DOD_STAND_AIM_RIFLE\",\n\t\"ACT_DOD_RUN_IDLE_C96\",\n\t\"ACT_SLAM_STICKWALL_TO_THROW\",\n\t\"ACT_HL2MP_JUMP_CAMERA\",\n\t\"ACT_SLAM_STICKWALL_IDLE\",\n\t\"ACT_WALK_AIM_PISTOL\",\n\t\"ACT_MP_GESTURE_VC_THUMBSUP_SECONDARY\",\n\t\"FL_DISSOLVING\",\n\t\"ACT_RANGE_ATTACK_RPG\",\n\t\"ACT_DOD_CROUCH_AIM_30CAL\",\n\t\"ACT_VM_UNDEPLOY_4\",\n\t\"ACT_DOD_CROUCHWALK_IDLE_RIFLE\",\n\t\"ACT_VM_IDLE_DEPLOYED_5\",\n\t\"KEY_SCROLLLOCKTOGGLE\",\n\t\"isstring\",\n\t\"ACT_IDLE_SHOTGUN_STIMULATED\",\n\t\"VMatrix\",\n\t\"ACT_DOD_RELOAD_PRONE_GARAND\",\n\t\"controlpanel\",\n\t\"ACT_MP_RELOAD_AIRWALK_PRIMARY\",\n\t\"DVerticalDivider\",\n\t\"ACT_VM_RELOAD_SILENCED\",\n\t\"SScale\",\n\t\"CLASS_HACKED_ROLLERMINE\",\n\t\"ACT_VM_DOWN_M203\",\n\t\"DListBox\",\n\t\"ACT_RUN_RIFLE_RELAXED\",\n\t\"ACT_OBJ_RUNNING\",\n\t\"ACT_WALK_STEALTH\",\n\t\"IsFirstTimePredicted\",\n\t\"ACT_VM_PRIMARYATTACK_DEPLOYED_6\",\n\t\"ACT_MP_JUMP_LAND_BUILDING\",\n\t\"ACT_PHYSCANNON_DETACH\",\n\t\"DButton\",\n\t\"ACT_VM_HITLEFT\",\n\t\"ACT_JUMP\",\n\t\"SCHED_GET_HEALTHKIT\",\n\t\"ACT_RELOAD\"\n\t-- End generated code\n}\n"
  },
  {
    "path": "LICENSE.txt",
    "content": "\nThe MIT License (MIT)\n\nCopyright (c) 2015 Brian Hang, Kyu Yeon Lee\nCopyright (c) 2018-2021 Alexander Grist-Hucker, Igor Radovanovic\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "<p align=\"center\">\n\t<img src=\"https://raw.githubusercontent.com/NebulousCloud/helix/master/docs/banner.gif\" alt=\"Helix\" />\n</p>\n\n<p align=\"center\">\n\t<a href=\"https://discord.gg/2AutUcF\">\n\t\t<img src=\"https://img.shields.io/discord/505957257125691423.svg\" alt=\"Discord\" />\n\t</a>\n\t<a href=\"https://github.com/NebulousCloud/helix/actions\">\n\t\t<img src=\"https://img.shields.io/github/workflow/status/NebulousCloud/helix/CI\" alt=\"Build Status\" />\n\t</a>\n</p>\n\nHelix is a framework for roleplay gamemodes in [Garry's Mod](https://gmod.facepunch.com/), based off of [NutScript 1.1](https://github.com/rebel1324/NutScript). Helix provides a stable, feature-filled, open-source, and DRM-free base so you can focus more on the things you want: making gameplay.\n\n## Getting Started\nVisit the getting started guide in the [documentation](https://docs.gethelix.co/manual/getting-started/) for an in-depth guide.\n\nIf you know what you're doing, a quick start for bootstrapping your own schema is forking/copying the skeleton schema at https://github.com/nebulouscloud/helix-skeleton. The skeleton contains all the important elements you need to have a functioning schema so you can get to coding right away.\n\nYou can also use our HL2 RP schema at https://github.com/nebulouscloud/helix-hl2rp as a base to work off of if you need something more fleshed out.\n\n## Plugins\nIf you'd like to enhance your gamemode, you can use any of the freely provided plugins available at the [Helix Plugin Center](https://plugins.gethelix.co). It is also encouraged to submit your own plugins for others to find and use at https://github.com/nebulouscloud/helix-plugins\n\n## Documentation\nUp-to-date documentation can be found at https://docs.gethelix.co. This is automatically updated when commits are pushed to the master branch.\n\nIf you'd like to ask some questions or integrate with the community, you can always join our [Discord](https://discord.gg/2AutUcF) server. We highly encourage you to search through the documentation before posting a question - the docs contain a good deal of information about how the various systems in Helix work, and it might explain what you're looking for.\n\n### Building documentation\nIf you're planning on contributing to the documentation, you'll probably want to preview your changes before you commit. The documentation can be built using [LDoc](https://github.com/impulsh/ldoc) - note that we use a forked version to add some functionality. You'll need [LuaRocks](https://luarocks.org/) installed in order to get started.\n\n```shell\n# installing ldoc\ngit clone https://github.com/impulsh/ldoc\ncd ldoc\nluarocks make\n\n# navigate to the helix repo folder and run\nldoc .\n```\n\nYou may not see the syntax highlighting work on your local copy - you'll need to copy the files in `docs/js` and `docs/css` over into the `docs/html` folder after it's done building.\n\n## Contributing\nFeel free to submit a pull request with any fixes/changes that you might find beneficial. Currently, there are no solid contributing guidelines other than keeping your code consistent with the rest of the framework.\n\n## Acknowledgements\nHelix is a fork of NutScript 1.1 by [Chessnut](https://github.com/brianhang) and [rebel1324](https://github.com/rebel1324).\n"
  },
  {
    "path": "config.ld",
    "content": "\nfile = {\n\t\"gamemode\",\n\t\"plugins\",\n\t\"docs/hooks\",\n\texclude = {\"gamemode/core/libs/thirdparty\"}\n}\n\nmodule_file = {\n\tCharacter = \"gamemode/core/meta/sh_character.lua\",\n\tEntity = \"gamemode/core/meta/sh_entity.lua\",\n\tInventory = \"gamemode/core/meta/sh_inventory.lua\",\n\tItem = \"gamemode/core/meta/sh_item.lua\",\n\tPlayer = \"gamemode/core/meta/sh_player.lua\"\n}\n\ndir = \"docs/html\"\nproject = \"Helix\"\ntitle = \"Helix Documentation\"\n\nno_space_before_args = true\nstyle = \"docs/css\"\ntemplate = \"docs/templates\"\nformat = \"markdown\"\nignore = true\ntopics = \"docs/manual\"\nuse_markdown_titles = true\nkind_names = {module = \"Libraries\", topic = \"Manual\"}\nmerge = true\nsort = true\nsort_modules = true\n\nsimple_args_string = true -- we show optionals/defaults outside of the display name\nstrip_metamethod_prefix = true -- remove the name of the table when displaying metamethod names\nno_viewed_topic_at_top = true -- don't put the currently viewed topic at the top\nuse_new_templates = true -- new templating system\npretty_urls = true -- avoid showing .html in urls\npretty_topic_names = true -- strips extension from manual filenames, this does not check filename collisions\n\ncustom_tags = {\n\t{\"realm\", hidden = true},\n\t{\"internal\", hidden = true}\n}\n\ncustom_display_name_handler = function(item, default_handler)\n\tif (item.type == \"function\" and item.module) then\n\t\tif (item.module.type == \"classmod\" or item.module.type == \"panel\") then\n\t\t\treturn item.module.mod_name .. \":\" .. default_handler(item)\n\t\telseif (item.module.type == \"hooks\") then\n\t\t\treturn item.module.mod_name:upper() .. \":\" .. default_handler(item)\n\t\tend\n\tend\n\n\treturn default_handler(item)\nend\n\nnew_type(\"hooks\", \"Hooks\", true)\nnew_type(\"panel\", \"Panels\", true)\n\n-- helix types\ntparam_alias(\"char\", \"Character\")\ntparam_alias(\"inventory\", \"Inventory\")\ntparam_alias(\"item\", \"Item\")\ntparam_alias(\"ixtype\", \"ix.type\")\ntparam_alias(\"date\", \"date\")\n\n-- standard types\ntparam_alias(\"string\", \"string\")\ntparam_alias(\"bool\", \"boolean\")\ntparam_alias(\"func\", \"function\")\ntparam_alias(\"player\", \"Player\")\ntparam_alias(\"entity\", \"Entity\")\ntparam_alias(\"color\", \"color\")\ntparam_alias(\"tab\", \"table\")\ntparam_alias(\"material\", \"material\")\ntparam_alias(\"vector\", \"vector\")\ntparam_alias(\"angle\", \"angle\")\n"
  },
  {
    "path": "docs/css/highlight.css",
    "content": "/*\n\ngithub.com style (c) Vasily Polovnyov <vast@whiteants.net>\n\n*/\n\n.hljs {\n  display: block;\n  color: #333;\n}\n\n.hljs-comment,\n.hljs-quote {\n  color: #535346;\n  font-style: italic;\n}\n\n.hljs-keyword,\n.hljs-selector-tag,\n.hljs-subst {\n  color: #333;\n  font-weight: bold;\n}\n\n.hljs-number,\n.hljs-literal,\n.hljs-variable,\n.hljs-template-variable,\n.hljs-tag .hljs-attr {\n  color: #008080;\n}\n\n.hljs-string,\n.hljs-doctag {\n  color: #d14;\n}\n\n.hljs-title,\n.hljs-section,\n.hljs-selector-id {\n  color: #900;\n  font-weight: bold;\n}\n\n.hljs-subst {\n  font-weight: normal;\n}\n\n.hljs-type,\n.hljs-class .hljs-title {\n  color: #458;\n  font-weight: bold;\n}\n\n.hljs-tag,\n.hljs-name,\n.hljs-attribute {\n  color: #000080;\n  font-weight: normal;\n}\n\n.hljs-regexp,\n.hljs-link {\n  color: #009926;\n}\n\n.hljs-symbol,\n.hljs-bullet {\n  color: #990073;\n}\n\n.hljs-built_in,\n.hljs-builtin-name {\n  color: #0086b3;\n}\n\n.hljs-meta {\n  color: #999;\n  font-weight: bold;\n}\n\n.hljs-deletion {\n  background: #fdd;\n}\n\n.hljs-addition {\n  background: #dfd;\n}\n\n.hljs-emphasis {\n  font-style: italic;\n}\n\n.hljs-strong {\n  font-weight: bold;\n}\n"
  },
  {
    "path": "docs/css/ldoc.css",
    "content": "\n:root {\n\t--content-width: 1200px;\n\t--sidebar-width: 330px;\n\n\t--padding-big: 48px;\n\t--padding-normal: 24px;\n\t--padding-small: 16px;\n\t--padding-tiny: 10px;\n\n\t--font-massive: 32px;\n\t--font-huge: 24px;\n\t--font-big: 18px;\n\t--font-normal: 16px;\n\t--font-tiny: 12px;\n\n\t--font-style-normal: Segoe UI, Helvetica, Arial, sans-serif;\n\t--font-style-code: Consolas, monospace;\n\n\t--color-accent: rgb(115, 53, 142);\n\t--color-accent-dark: rgb(85, 39, 105);\n\t--color-white: rgb(255, 255, 255);\n\t--color-offwhite: rgb(200, 200, 200);\n\t--color-white-accent: rgb(203, 190, 209);\n\t--color-black: rgb(0, 0, 0);\n\t--color-lightgrey: rgb(160, 160, 160);\n\t--color-background-light: rgb(240, 240, 240);\n\t--color-background-dark: rgb(33, 33, 33);\n}\n\n* {\n\tpadding: 0;\n\tmargin: 0;\n\tbox-sizing: border-box;\n}\n\nbody {\n\tbackground-color: var(--color-background-light);\n\tfont-family: var(--font-style-normal);\n\n\tdisplay: flex;\n\tflex-direction: column;\n}\n\na {\n\tcolor: inherit;\n\ttext-decoration: inherit;\n}\n\nh1, h2, h3, h4 {\n\tfont-weight: 400;\n}\n\nul li {\n\tmargin-left: var(--padding-small);\n}\n\n/* landing */\n.landing {\n\tbackground-color: var(--color-accent);\n\tcolor: var(--color-white);\n\n\tpadding: 128px 0 128px 0;\n}\n\n.landing h1 {\n\tmargin: 0;\n\tpadding: 0;\n\tborder: none;\n\n\tfont-weight: 100;\n\tfont-size: var(--font-massive);\n\ttext-align: center;\n}\n\n.wrapper {\n\tpadding: var(--padding-small);\n}\n\ndetails {\n\tuser-select: none;\n}\n\ndetails summary {\n\toutline: none;\n}\n\ncode {\n\tfont-family: \"Source Code Pro\", monospace;\n\tfont-size: 85%;\n\twhite-space: pre;\n\ttab-size: 4;\n\t-moz-tab-size: 4;\n\tpadding: 2px 4px;\n\tbackground-color: rgb(33, 33, 33, 0.1);\n}\n\npre {\n\tbackground-color: rgb(33, 33, 33, 0.1);\n\tmargin-top: var(--padding-small);\n\tpadding: var(--padding-tiny);\n\toverflow: auto;\n}\n\npre code {\n\tbackground-color: transparent;\n}\n\nspan.realm {\n\twidth: 14px;\n\theight: 14px;\n\tborder-radius: 3px;\n\tdisplay: inline-block;\n\tmargin-right: 6px;\n}\n\nspan.realm.shared {\n\tbackground: linear-gradient(45deg, #f80 0%, #f80 50%, #08f 51%, #08f 100%);\n}\n\nspan.realm.client {\n\tbackground-color: #f80;\n}\n\nspan.realm.server {\n\tbackground-color: #08f;\n}\n\n/* wrapper element for sidebar/content */\nmain {\n\tdisplay: flex;\n\tflex-direction: row;\n\tjustify-content: space-between;\n\talign-items: flex-start;\n\n\twidth: var(--content-width);\n\tmargin: auto;\n}\n\n/* sidebar */\nnav {\n\tcolor: var(--color-offwhite);\n\tbackground-color: var(--color-background-dark);\n\n\tposition: fixed;\n\tdisplay: flex;\n\tflex-direction: column;\n\n\twidth: var(--sidebar-width);\n\theight: 100%;\n}\n\n/* sidebar header */\nnav header {\n\tcolor: var(--color-white);\n\tbackground-color: var(--color-accent);\n\n\tpadding: var(--padding-small);\n}\n\nnav header h1 {\n\tfont-size: var(--font-huge);\n\tfont-weight: 100;\n\ttext-align: center;\n\n\tmargin-bottom: var(--padding-small);\n}\n\n#search {\n\tbackground-color: var(--color-accent-dark);\n\tcolor: var(--color-white);\n\n\tborder: none;\n\tfont-size: var(--font-normal);\n\toutline: none;\n\n\twidth: 100%;\n\tpadding: 6px;\n}\n\n#search::placeholder {\n\tcolor: var(--color-white-accent);\n}\n\n#search::-webkit-search-cancel-button {\n\tdisplay: none;\n}\n\n/* sidebar contents */\nnav section {\n\tpadding: var(--padding-small);\n\toverflow: auto;\n}\n\nnav section ul {\n\tlist-style-type: none;\n}\n\nnav section::-webkit-scrollbar,\npre::-webkit-scrollbar {\n\twidth: 8px;\n\theight: 8px;\n}\n\nnav section::-webkit-scrollbar-track,\npre::-webkit-scrollbar-track {\n\tbackground: transparent;\n}\n\nnav section::-webkit-scrollbar-thumb {\n\tbackground-color: var(--color-lightgrey);\n}\n\npre::-webkit-scrollbar-thumb {\n\tbackground-color: var(--color-lightgrey);\n}\n\n/* sidebar contents category */\nnav section details.category {\n\tpadding-top: var(--padding-tiny);\n}\n\nnav section details.category > ul > li {\n\tmargin: 0;\n\tline-height: 1.5;\n}\n\nnav section details.category > ul > li a {\n\tdisplay: inline-block;\n\twidth: 90%;\n}\n\nnav section details.category:first-of-type {\n\tpadding-top: calc(var(--padding-tiny) * -1);\n}\n\nnav section details.category summary::-webkit-details-marker {\n\topacity: 0.5;\n\tcursor: pointer;\n}\n\nnav section details.category summary h2 {\n\tcolor: var(--color-accent);\n\n\tfont-size: var(--font-big);\n\tletter-spacing: 2px;\n\ttext-transform: uppercase;\n\tcursor: pointer;\n\n\tpadding-bottom: var(--padding-tiny);\n}\n\n/* content */\narticle {\n\tbackground-color: rgb(255, 255, 255);\n\n\twidth: calc(100% - var(--sidebar-width));\n\tmin-height: 100vh;\n\tmargin-left: var(--sidebar-width);\n}\n\narticle .wrapper > *:first-child {\n\tmargin-top: 0;\n}\n\n/* header */\narticle header {\n\tcolor: rgb(255, 255, 255);\n\tbackground-color: rgb(115, 53, 142);\n\tpadding: var(--padding-tiny);\n}\n\narticle header h1 {\n\tborder-bottom: 1px solid rgba(255, 255, 255, 0.25);\n\tpadding-bottom: 8px;\n\tfont-family: var(--font-style-code);\n\tmargin: 0;\n}\n\narticle header h2 {\n\tpadding-top: var(--padding-tiny);\n\tmargin: 0;\n\tfont-size: var(--font-normal);\n\tfont-weight: normal;\n}\n\narticle header.module a {\n\tcolor: white !important;\n\ttext-decoration: underline;\n}\n\ndetails.category > summary {\n\tlist-style: none;\n}\n\ndetails.category > summary::-webkit-details-marker {\n\tdisplay: none;\n}\n\narticle h1 {\n\tfont-size: 28px;\n\tfont-weight: 600;\n\tborder-bottom: 1px solid rgba(0, 0, 0, 0.25);\n\tmargin-top: 24px;\n\tmargin-bottom: 16px;\n\tpadding-bottom: 8px;\n}\n\narticle h2 {\n\tfont-size: 20px;\n\tfont-weight: 600;\n\tmargin-top: 12px;\n}\n\narticle h3 {\n\tcolor: rgb(115, 53, 142);\n\tmargin-top: var(--padding-tiny);\n\ttext-transform: uppercase;\n}\n\narticle p {\n\tmargin-top: var(--padding-small);\n}\n\narticle p a,\narticle ul li a,\narticle h1 a,\narticle h2 a {\n\tcolor: rgb(115, 53, 142);\n\tfont-weight: 600;\n}\n\narticle h1.title {\n\tcolor: rgb(255, 255, 255);\n\tbackground-color: rgb(115, 53, 142);\n\tmargin-top: var(--padding-small);\n\tmargin-bottom: 0;\n\tpadding: var(--padding-tiny);\n\n\tfont-size: var(--font-big);\n\tfont-weight: 100;\n\tletter-spacing: 2px;\n\ttext-transform: uppercase;\n}\n\na.reference {\n\tcolor: rgb(115, 53, 142);\n\tfloat: right;\n\tmargin-top: 8px;\n\tpadding-left: 8px;\n\tfont-size: 14px;\n\tfont-weight: 600;\n}\n\n.notice {\n\t--color-notice-background: var(--color-accent);\n\t--color-notice-text: var(--color-notice-background);\n\n\tmargin-top: var(--padding-tiny);\n\tborder: 2px solid var(--color-notice-background);\n}\n\n.notice.error {\n\t--color-notice-background: rgb(194, 52, 130);\n}\n\n.notice.warning {\n\t--color-notice-background: rgb(224, 169, 112);\n\t--color-notice-text: rgb(167, 104, 37);\n}\n\n.notice .title {\n\tcolor: var(--color-white);\n\tbackground-color: var(--color-notice-background);\n\n\tpadding: var(--padding-tiny);\n\tfont-size: var(--font-normal);\n\n\ttext-transform: uppercase;\n\tletter-spacing: 2px;\n}\n\n.notice p {\n\tcolor: var(--color-notice-text);\n\n\tmargin: 0 !important;\n\tpadding: var(--padding-tiny);\n}\n\n/* function/table */\n.method {\n\tdisplay: flex;\n\tflex-flow: column;\n\tbackground-color: rgb(230, 230, 230);\n\tpadding: var(--padding-tiny);\n\tmargin-top: var(--padding-small);\n}\n\n.method header {\n\tcolor: rgb(0, 0, 0);\n\tbackground-color: inherit;\n\tpadding: 0;\n\torder: -1;\n}\n\n.method header .anchor {\n\tcolor: inherit;\n\ttext-decoration: inherit;\n}\n\n.method header .anchor:target h1 {\n\tbackground-color: rgba(115, 53, 142, 0.2);\n\tbackground-clip: content-box;\n}\n\n.method header h1 {\n\tfont-family: \"Source Code Pro\", monospace;\n\tpadding-bottom: var(--padding-tiny);\n\tborder-bottom: 1px solid rgba(0, 0, 0, 0.25);\n\tfont-size: 20px;\n}\n\n.method header p:first-of-type {\n\tmargin-top: var(--padding-tiny);\n}\n\n.method h3 {\n\tcolor: rgb(115, 53, 142);\n\tfont-size: var(--font-normal);\n\tletter-spacing: 2px;\n\ttext-transform: uppercase;\n}\n\n.method pre {\n\tmargin-top: var(--padding-tiny);\n}\n\n@media only screen and (max-width: 1100px) {\n\tmain nav {\n\t\tposition: inherit;\n\t}\n\n\tmain article {\n\t\tmargin-left: 0;\n\t}\n}\n\n.method ul {\n\tmargin-top: var(--padding-tiny);\n\tbackground-color: inherit;\n}\n\n.method ul li {\n\tlist-style: none;\n\tmargin: 4px 0 0 var(--padding-normal);\n}\n\n.method ul li:first-of-type {\n\tmargin-top: 0;\n}\n\n.method ul li p {\n\tmargin: 4px 0 0 var(--padding-normal);\n}\n\n.method ul li pre {\n\tmargin: 4px 0 0 var(--padding-normal);\n}\n\n.method ul li a {\n\tcolor: rgb(115, 53, 142);\n\tfont-weight: 600;\n}\n\n/* we have to manually specify these instead of making a shared class since you cannot customize the parameter class in ldoc */\n.parameter, .type, .default {\n\tdisplay: inline-block;\n\tcolor: rgb(255, 255, 255) !important;\n\n\tpadding: 4px;\n\tfont-size: 14px;\n\tfont-family: \"Source Code Pro\", monospace;\n}\n\n.parameter {\n\tbackground-color: rgb(115, 53, 142);\n}\n\n.type {\n\tbackground-color: rgb(31, 141, 155);\n}\n\na.type {\n\tfont-weight: 300 !important;\n\ttext-decoration: underline;\n}\n\n.default {\n\tbackground-color: rgb(193, 114, 11);\n}\n\n.type a {\n\tpadding: 0;\n}\n\n.or {\n\tcolor: rgba(115, 53, 142, 0.5);\n\tbackground-color: inherit;\n\n\twidth: calc(100% - 32px);\n\theight: 8px;\n\tmargin: 0 0 8px 32px;\n\n\ttext-align: center;\n\tfont-weight: 600;\n\tborder-bottom: 1px solid rgba(115, 53, 142, 0.5);\n}\n\n.or span {\n\tbackground-color: inherit;\n\tpadding: 0 8px 0 8px;\n}\n"
  },
  {
    "path": "docs/hooks/class.lua",
    "content": "\n-- luacheck: ignore 111\n\n--[[--\nClass setup hooks.\n\nAs with `Faction`s, `Class`es get their own hooks for when players leave/join a class, etc. These hooks are only\nvalid in class tables that are created in `schema/classes/sh_classname.lua`, and cannot be used like regular gamemode hooks.\n]]\n-- @hooks Class\n\n--- Whether or not a player can switch to this class.\n-- @realm shared\n-- @player client Client that wants to switch to this class\n-- @treturn bool True if the player is allowed to switch to this class\n-- @usage function CLASS:CanSwitchTo(client)\n-- \treturn client:IsAdmin() -- only admins allowed in this class!\n-- end\nfunction CanSwitchTo(client)\nend\n\n--- Called when a character has left this class and has joined a different one. You can get the class the character has\n-- has joined by calling `character:GetClass()`.\n-- @realm server\n-- @player client Player who left this class\nfunction OnLeave(client)\nend\n\n--- Called when a character has joined this class.\n-- @realm server\n-- @player client Player who has joined this class\n-- @usage function CLASS:OnSet(client)\n-- \tclient:SetModel(\"models/police.mdl\")\n-- end\nfunction OnSet(client)\nend\n\n--- Called when a character in this class has spawned in the world.\n-- @realm server\n-- @player client Player that has just spawned\nfunction OnSpawn(client)\nend\n"
  },
  {
    "path": "docs/hooks/faction.lua",
    "content": "\n-- luacheck: ignore 111\n\n--[[--\nFaction setup hooks.\n\nFactions get their own hooks that are called for various reasons, but the most common one is to set up a character\nonce it's created and assigned to a certain faction. For example, giving a police faction character a weapon on creation.\nThese hooks are used in faction tables that are created in `schema/factions/sh_factionname.lua` and cannot be used like\nregular gamemode hooks.\n]]\n-- @hooks Faction\n\n--- Called when the default name for a character needs to be retrieved (i.e upon initial creation).\n-- @realm shared\n-- @player client Client to get the default name for\n-- @treturn string Default name for the newly created character\n-- @usage function FACTION:GetDefaultName(client)\n-- \treturn \"MPF-RCT.\" .. tostring(math.random(1, 99999))\n-- end\nfunction GetDefaultName(client)\nend\n\n--- Called when a character has been initally created and assigned to this faction.\n-- @realm server\n-- @player client Client that owns the character\n-- @char character Character that has been created\n-- @usage function FACTION:OnCharacterCreated(client, character)\n-- \tlocal inventory = character:GetInventory()\n-- \tinventory:Add(\"pistol\")\n-- end\nfunction OnCharacterCreated(client, character)\nend\n\n--- Called when a character in this faction has spawned in the world.\n-- @realm server\n-- @player client Player that has just spawned\nfunction OnSpawn(client)\nend\n\n--- Called when a player's character has been transferred to this faction.\n-- @realm server\n-- @char character Character that was transferred\n-- @usage function FACTION:OnTransferred(character)\n-- \tcharacter:SetModel(self.models[1])\n-- end\nfunction OnTransferred(character)\nend\n"
  },
  {
    "path": "docs/hooks/plugin.lua",
    "content": "\n-- luacheck: ignore 111\n\n--[[--\nGlobal hooks for general use.\n\nPlugin hooks are regular hooks that can be used in your schema with `Schema:HookName(args)`, in your plugin with\n`PLUGIN:HookName(args)`, or in your addon with `hook.Add(\"HookName\", function(args) end)`.\n]]\n-- @hooks Plugin\n\n--- Adjusts the data used just before creating a new character.\n-- @realm server\n-- @player client Player that is creating the character\n-- @tab payload Table of data to be used for character creation\n-- @tab newPayload Table of data be merged with the current payload\n-- @usage function PLUGIN:AdjustCreationPayload(client, payload, newPayload)\n-- \tnewPayload.money = payload.attributes[\"stm\"] -- Sets the characters initial money to the stamina attribute value.\n-- end\nfunction AdjustCreationPayload(client, payload, newPayload)\nend\n\n--- Adjusts a player's current stamina offset amount. This is called when the player's stamina is about to be changed; every\n-- `0.25` seconds on the server, and every frame on the client.\n-- @realm shared\n-- @player client Player whose stamina is changing\n-- @number baseOffset Amount the stamina is changing by. This can be a positive or negative number depending if they are\n-- exhausting or regaining stamina\n-- @treturn number New offset to use\n-- @usage function PLUGIN:AdjustStaminaOffset(client, baseOffset)\n-- \treturn baseOffset * 2 -- Drain/Regain stamina twice as fast.\n-- end\nfunction AdjustStaminaOffset(client, baseOffset)\nend\n\n--- Creates the business panel in the tab menu.\n-- @realm client\n-- @treturn bool Whether or not to create the business menu\n-- @usage function PLUGIN:BuildBusinessMenu()\n-- \treturn LocalPlayer():IsAdmin() -- Only builds the business menu for admins.\n-- end\nfunction BuildBusinessMenu()\nend\n\n--- Whether or not a message can be auto formatted with punctuation and capitalization.\n-- @realm server\n-- @player speaker Player that sent the message\n-- @string chatType Chat type of the message. This will be something registered with `ix.chat.Register` - like `ic`, `ooc`, etc.\n-- @string text Unformatted text of the message\n-- @treturn bool Whether or not to allow auto formatting on the message\n-- @usage function PLUGIN:CanAutoFormatMessage(speaker, chatType, text)\n-- \treturn false -- Disable auto formatting outright.\n-- end\nfunction CanAutoFormatMessage(speaker, chatType, text)\nend\n\n--- Whether or not certain information can be displayed in the character info panel in the tab menu.\n-- @realm client\n-- @tab suppress Information to **NOT** display in the UI - modify this to change the behaviour. This is a table of the names of\n-- some panels to avoid displaying. Valid names include:\n--\n-- - `time` - current in-game time\n-- - `name` - name of the character\n-- - `description` - description of the character\n-- - `characterInfo` - entire panel showing a list of additional character info\n-- - `faction` - faction name of the character\n-- - `class` - name of the character's class if they're in one\n-- - `money` - current money the character has\n-- - `attributes` - attributes list for the character\n--\n-- Note that schemas/plugins can add additional character info panels.\n-- @usage function PLUGIN:CanCreateCharacterInfo(suppress)\n-- \tsuppress.attributes = true -- Hides the attributes panel from the character info tab\n-- end\nfunction CanCreateCharacterInfo(suppress)\nend\n\n--- Whether or not the ammo HUD should be drawn.\n-- @realm client\n-- @entity weapon Weapon the player currently is holding\n-- @treturn bool Whether or not to draw the ammo hud\n-- @usage function PLUGIN:CanDrawAmmoHUD(weapon)\n-- \tif (weapon:GetClass() == \"weapon_frag\") then -- Hides the ammo hud when holding grenades.\n-- \t\treturn false\n-- \tend\n-- end\nfunction CanDrawAmmoHUD(weapon)\nend\n\n--- Called when a player tries to use abilities on the door, such as locking.\n-- @realm shared\n-- @player client The client trying something on the door.\n-- @entity door The door entity itself.\n-- @number access The access level used when called.\n-- @treturn bool Whether or not to allow the client access.\n-- @usage function PLUGIN:CanPlayerAccessDoor(client, door, access)\n-- \treturn true -- Always allow access.\n-- end\nfunction CanPlayerAccessDoor(client, door, access)\nend\n\n--- Whether or not a player is allowed to combine an item `other` into the given `item`.\n-- @realm server\n-- @player client Player attempting to combine an item into another\n-- @number item instance ID of the item being dropped onto\n-- @number other instance ID of the item being combined into the first item, this can be invalid due to it being from clientside\n-- @treturn bool Whether or not to allow the player to combine the items\n-- @usage function PLUGIN:CanPlayerCombineItem(client, item, other)\n--\t\tlocal otherItem = ix.item.instances[other]\n--\n--\t\tif (otherItem and otherItem.uniqueID == \"soda\") then\n--\t\t\treturn false -- disallow combining any item that has a uniqueID equal to `soda`\n--\t\tend\n--\tend\nfunction CanPlayerCombineItem(client, item, other)\nend\n\n--- Whether or not a player is allowed to create a new character with the given payload.\n-- @realm server\n-- @player client Player attempting to create a new character\n-- @tab payload Data that is going to be used for creating the character\n-- @treturn bool Whether or not the player is allowed to create the character. This function defaults to `true`, so you\n-- should only ever return `false` if you're disallowing creation. Otherwise, don't return anything as you'll prevent any other\n-- calls to this hook from running.\n-- @treturn string Language phrase to use for the error message\n-- @treturn ... Arguments to use for the language phrase\n-- @usage function PLUGIN:CanPlayerCreateCharacter(client, payload)\n-- \tif (!client:IsAdmin()) then\n-- \t\treturn false, \"notNow\" -- only allow admins to create a character\n-- \tend\n-- end\n-- -- non-admins will see the message \"You are not allowed to do this right now!\"\nfunction CanPlayerCreateCharacter(client, payload)\nend\n\n--- Whether or not a player is allowed to drop the given `item`.\n-- @realm server\n-- @player client Player attempting to drop an item\n-- @number item instance ID of the item being dropped\n-- @treturn bool Whether or not to allow the player to drop the item\n-- @usage function PLUGIN:CanPlayerDropItem(client, item)\n-- \treturn false -- Never allow dropping items.\n-- end\nfunction CanPlayerDropItem(client, item)\nend\n\n--- Whether or not a player can earn money at regular intervals. This hook runs only if the player's character faction has\n-- a salary set - i.e `FACTION.pay` is set to something other than `0` for their faction.\n-- @realm server\n-- @player client Player to give money to\n-- @tab faction Faction of the player's character\n-- @treturn bool Whether or not to allow the player to earn salary\n-- @usage function PLUGIN:CanPlayerEarnSalary(client, faction)\n-- \treturn client:IsAdmin() -- Restricts earning salary to admins only.\n-- end\nfunction CanPlayerEarnSalary(client, faction)\nend\n\n--- Whether or not the player is allowed to enter observer mode. This is allowed only for admins by default and can be\n-- customized by server owners if the server is using a CAMI-compliant admin mod.\n-- @realm server\n-- @player client Player attempting to enter observer\n-- @treturn bool Whether or not to allow the player to enter observer\n-- @usage function PLUGIN:CanPlayerEnterObserver(client)\n-- \treturn true -- Always allow observer.\n-- end\nfunction CanPlayerEnterObserver(client)\nend\n\n--- Whether or not a player can equip the given `item`. This is called for items with `outfit`, `pacoutfit`, or `weapons` as\n-- their base. Schemas/plugins can utilize this hook for their items.\n-- @realm server\n-- @player client Player attempting to equip the item\n-- @tab item Item being equipped\n-- @treturn bool Whether or not to allow the player to equip the item\n-- @see CanPlayerUnequipItem\n-- @usage function PLUGIN:CanPlayerEquipItem(client, item)\n-- \treturn client:IsAdmin() -- Restrict equipping items to admins only.\n-- end\nfunction CanPlayerEquipItem(client, item)\nend\n\n--- Whether or not a player is allowed to hold an entity with the hands SWEP.\n-- @realm server\n-- @player client Player attempting to hold an entity\n-- @entity entity Entity being held\n-- @treturn bool Whether or not to allow the player to hold the entity\n-- @usage function PLUGIN:CanPlayerHoldObject(client, entity)\n-- \treturn !(client:GetMoveType() == MOVETYPE_NOCLIP and !client:InVehicle()) -- Disallow players in observer holding objects.\n-- end\nfunction CanPlayerHoldObject(client, entity)\nend\n\n--- Whether or not a player is allowed to interact with an entity's interaction menu if it has one.\n-- @realm server\n-- @player client Player attempting interaction\n-- @entity entity Entity being interacted with\n-- @string option Option selected by the player\n-- @param data Any data passed with the interaction option\n-- @treturn bool Whether or not to allow the player to interact with the entity\n-- @usage function PLUGIN:CanPlayerInteractEntity(client, entity, option, data)\n--  if (entity:GetClass() == \"my_big_entity\" and entity:GetPos():Distance(client:GetPos()) < 192) then\n--    return true -- Force allow interacting if within larger than default interact range of large entity\n--  end\n--\n-- \tif (client:GetNetVar(\"drunk\")) then\n--    return false -- Disallow interacting with an entity while drunk\n-- \tend\n-- end\nfunction CanPlayerInteractEntity(client, entity, option, data)\nend\n\n--- Whether or not a player is allowed to interact with an item via an inventory action (e.g picking up, dropping, transferring\n-- inventories, etc). Note that this is for an item *table*, not an item *entity*. This is called after `CanPlayerDropItem`\n-- and `CanPlayerTakeItem`.\n-- @realm server\n-- @player client Player attempting interaction\n-- @string action The action being performed\n-- @param item Item's instance ID or item table\n-- @param data Any data passed with the action\n-- @treturn bool Whether or not to allow the player to interact with the item\n-- @usage function PLUGIN:CanPlayerInteractItem(client, action, item, data)\n-- \treturn false -- Disallow interacting with any item.\n-- end\nfunction CanPlayerInteractItem(client, action, item, data)\nend\n\n--- Whether or not a plyer is allowed to join a class.\n-- @realm shared\n-- @player client Player attempting to join\n-- @number class ID of the class\n-- @tab info The class table\n-- @treturn bool Whether or not to allow the player to join the class\n-- @usage function PLUGIN:CanPlayerJoinClass(client, class, info)\n-- \treturn client:IsAdmin() -- Restrict joining classes to admins only.\n-- end\nfunction CanPlayerJoinClass(client, class, info)\nend\n\n--- Whether or not a player can knock on the door with the hands SWEP.\n-- @realm server\n-- @player client Player attempting to knock\n-- @entity entity Door being knocked on\n-- @treturn bool Whether or not to allow the player to knock on the door\n-- @usage function PLUGIN:CanPlayerKnock(client, entity)\n-- \treturn false -- Disable knocking on doors outright.\n-- end\nfunction CanPlayerKnock(client, entity)\nend\n\n--- Whether or not a player can open a shipment spawned from the business menu.\n-- @realm server\n-- @player client Player attempting to open the shipment\n-- @entity entity Shipment entity\n-- @treturn bool Whether or not to allow the player to open the shipment\n-- @usage function PLUGIN:CanPlayerOpenShipment(client, entity)\n-- \treturn client:Team() == FACTION_BMD -- Restricts opening shipments to FACTION_BMD.\n-- end\nfunction CanPlayerOpenShipment(client, entity)\nend\n\n--- Whether or not a player is allowed to spawn a container entity.\n-- @realm server\n-- @player client Player attempting to spawn a container\n-- @string model Model of the container being spawned\n-- @entity entity Container entity\n-- @treturn bool Whether or not to allow the player to spawn the container\n-- @usage function PLUGIN:CanPlayerSpawnContainer(client, model, entity)\n-- \treturn client:IsAdmin() -- Restrict spawning containers to admins.\n-- end\nfunction CanPlayerSpawnContainer(client, model, entity)\nend\n\n--- Whether or not a player is allowed to take an item and put it in their inventory.\n-- @realm server\n-- @player client Player attempting to take the item\n-- @entity item Entity corresponding to the item\n-- @treturn bool Whether or not to allow the player to take the item\n-- @usage function PLUGIN:CanPlayerTakeItem(client, item)\n-- \treturn !(client:GetMoveType() == MOVETYPE_NOCLIP and !client:InVehicle()) -- Disallow players in observer taking items.\n-- end\nfunction CanPlayerTakeItem(client, item)\nend\n\n--- Whether or not the player is allowed to punch with the hands SWEP.\n-- @realm shared\n-- @player client Player attempting throw a punch\n-- @treturn bool Whether or not to allow the player to punch\n-- @usage function PLUGIN:CanPlayerThrowPunch(client)\n-- \treturn client:GetCharacter():GetAttribute(\"str\", 0) > 0 -- Only allow players with strength to punch.\n-- end\nfunction CanPlayerThrowPunch(client)\nend\n\n--- Whether or not a player can trade with a vendor.\n-- @realm server\n-- @player client Player attempting to trade\n-- @entity entity Vendor entity\n-- @string uniqueID The uniqueID of the item being traded.\n-- @bool isSellingToVendor If the client is selling to the vendor\n-- @treturn bool Whether or not to allow the client to trade with the vendor\n-- @usage function PLUGIN:CanPlayerTradeWithVendor(client, entity, uniqueID, isSellingToVendor)\n-- \treturn false -- Disallow trading with vendors outright.\n-- end\nfunction CanPlayerTradeWithVendor(client, entity, uniqueID, isSellingToVendor)\nend\n\n--- Whether or not a player can unequip an item.\n-- @realm server\n-- @player client Player attempting to unequip an item\n-- @tab item Item being unequipped\n-- @treturn bool Whether or not to allow the player to unequip the item\n-- @see CanPlayerEquipItem\n-- @usage function PLUGIN:CanPlayerUnequipItem(client, item)\n-- \treturn false -- Disallow unequipping items.\n-- end\nfunction CanPlayerUnequipItem(client, item)\nend\n\n--- Whether or not a player can buy an item from the business menu.\n-- @realm shared\n-- @player client Player that uses a business menu\n-- @string uniqueID The uniqueID of the business menu item\n-- @treturn bool Whether or not to allow the player to buy an item from the business menu\n-- @usage function PLUGIN:CanPlayerUseBusiness(client, uniqueID)\n--  return false -- Disallow buying from the business menu.\n-- end\nfunction CanPlayerUseBusiness(client, uniqueID)\nend\n\n--- Whether or not a player can use a character.\n-- @realm shared\n-- @player client Player that wants to use a character\n-- @char character Character that a player wants to use\n-- @treturn bool Whether or not to allow the player to load a character\n-- @usage function PLUGIN:CanPlayerUseCharacter(client, character)\n-- \treturn false -- Disallow using any character.\n-- end\nfunction CanPlayerUseCharacter(client, character)\nend\n\n--- Whether or not a player can use a door.\n-- @realm server\n-- @player client Player that wants to use a door\n-- @entity entity Door that a player wants to use\n-- @treturn bool Whether or not to allow the player to use a door\n-- @usage function PLUGIN:CanPlayerUseDoor(client, character)\n-- \treturn false -- Disallow using any door.\n-- end\nfunction CanPlayerUseDoor(client, entity)\nend\n\n--- Determines whether a player can use a vendor.\n-- @realm server\n-- @player activator The player attempting to use the vendor\n-- @entity vendor The vendor entity being used\n-- @treturn bool Returns false if the player can't use the vendor\nfunction CanPlayerUseVendor(activator, vendor)\nend\n\n--- Whether or not a player can view his inventory.\n-- @realm client\n-- @treturn bool Whether or not to allow the player to view his inventory\n-- @usage function PLUGIN:CanPlayerViewInventory()\n-- \treturn false -- Prevent player from viewing his inventory.\n-- end\nfunction CanPlayerViewInventory()\nend\n\n--- Whether or not to save a container.\n-- @realm server\n-- @entity entity Container entity to save\n-- @tab inventory Container inventory\n-- @treturn bool Whether or not to save a container\n-- @usage function PLUGIN:CanSaveContainer(entity, inventory)\n--  return false -- Disallow saving any container.\n-- end\nfunction CanSaveContainer(entity, inventory)\nend\n\n--- @realm shared\nfunction CanTransferItem(item, currentInv, oldInv)\nend\n\n--- @realm shared\nfunction CharacterAttributeBoosted(client, character, attribID, boostID, boostAmount)\nend\n\n--- @realm shared\nfunction CharacterAttributeUpdated(client, self, key, value)\nend\n\n--- @realm shared\nfunction CharacterDeleted(client, id, isCurrentChar)\nend\n\n--- @realm shared\nfunction CharacterHasFlags(self, flags)\nend\n\n--- @realm shared\nfunction CharacterLoaded(character)\nend\n\n--- Called when a character was saved.\n-- @realm server\n-- @char character that was saved\nfunction CharacterPostSave(character)\nend\n\n--- @realm shared\nfunction CharacterPreSave(character)\nend\n\n--- @realm shared\nfunction CharacterRecognized()\nend\n\n--- Called when a character was restored.\n-- @realm server\n-- @char character that was restored\nfunction CharacterRestored(character)\nend\n\n--- @realm shared\nfunction CharacterVarChanged(character, key, oldVar, value)\nend\n\n--- @realm shared\nfunction CharacterVendorTraded(client, entity, uniqueID, isSellingToVendor)\nend\n\n--- @realm client\nfunction ChatboxCreated()\nend\n\n--- @realm client\nfunction ChatboxPositionChanged(x, y, width, height)\nend\n\n--- @realm client\nfunction ColorSchemeChanged(color)\nend\n\n--- Called when a container was removed.\n-- @realm server\n-- @entity container Container that was removed\n-- @tab inventory Container inventory\nfunction ContainerRemoved(container, inventory)\nend\n\n--- @realm client\nfunction CreateCharacterInfo(panel)\nend\n\n--- @realm client\nfunction CreateCharacterInfoCategory(panel)\nend\n\n--- @realm client\nfunction CreateItemInteractionMenu(icon, menu, itemTable)\nend\n\n--- @realm client\nfunction CreateMenuButtons(tabs)\nend\n\n--- Called when a shipment was created.\n-- @realm server\n-- @player client Player that ordered the shipment\n-- @entity entity Shipment entity\nfunction CreateShipment(client, entity)\nend\n\n--- Called when a server has connected to the database.\n-- @realm server\nfunction DatabaseConnected()\nend\n\n--- Called when a server failed to connect to the database.\n-- @realm server\n-- @string error Error that prevented server from connecting to the database\nfunction DatabaseConnectionFailed(error)\nend\n\n--- @realm shared\nfunction DoPluginIncludes(path, pluginTable)\nend\n\n--- @realm client\nfunction DrawCharacterOverview()\nend\n\n--- @realm client\nfunction DrawHelixModelView(panel, entity)\nend\n\n--- @realm client\nfunction DrawPlayerRagdoll(entity)\nend\n\n--- @realm client\nfunction GetCharacterDescription(client)\nend\n\n--- @realm shared\nfunction GetCharacterName(speaker, chatType)\nend\n\n--- @realm shared\nfunction GetChatPrefixInfo(text)\nend\n\n--- @realm client\nfunction GetCrosshairAlpha(curAlpha)\nend\n\n--- @realm shared\nfunction GetDefaultAttributePoints(client, count)\nend\n\n--- @realm shared\nfunction GetDefaultCharacterName(client, faction)\nend\n\n--- @realm shared\nfunction GetMaxPlayerCharacter(client)\nend\n\n--- Returns the sound to emit from the player upon death. If nothing is returned then it will use the default male/female death\n-- sounds.\n-- @realm server\n-- @player client Player that died\n-- @treturn[1] string Sound to play\n-- @treturn[2] bool `false` if a sound shouldn't be played at all\n-- @usage function PLUGIN:GetPlayerDeathSound(client)\n-- \t-- play impact sound every time someone dies\n-- \treturn \"physics/body/body_medium_impact_hard1.wav\"\n-- end\n-- @usage function PLUGIN:GetPlayerDeathSound(client)\n-- \t-- don't play a sound at all\n-- \treturn false\n-- end\nfunction GetPlayerDeathSound(client)\nend\n\n--- @realm client\nfunction GetPlayerEntityMenu(client, options)\nend\n\n--- @realm client\nfunction GetPlayerIcon(speaker)\nend\n\n--- Returns the sound to emit from the player upon getting damage.\n-- @realm server\n-- @player client Client that received damage\n-- @treturn string Sound to emit\n-- @usage function PLUGIN:GetPlayerPainSound(client)\n-- \treturn \"NPC_MetroPolice.Pain\" -- Make players emit MetroPolice pain sound.\n-- end\nfunction GetPlayerPainSound(client)\nend\n\n--- @realm shared\nfunction GetPlayerPunchDamage(client, damage, context)\nend\n\n--- Returns the salary that character should get instead of his faction salary.\n-- @realm server\n-- @player client Client that is receiving salary\n-- @tab faction Faction of the player's character\n-- @treturn number Character salary\n-- @see CanPlayerEarnSalary\n-- @usage function PLUGIN:GetSalaryAmount(client, faction)\n--  return 0 -- Everyone get no salary.\n-- end\nfunction GetSalaryAmount(client, faction)\nend\n\n--- @realm client\nfunction GetTypingIndicator(character, text)\nend\n\n--- Registers chat classes after the core framework chat classes have been registered. You should usually create your chat\n-- classes in this hook - especially if you want to reference the properties of a framework chat class.\n-- @realm shared\n-- @usage function PLUGIN:InitializedChatClasses()\n-- \t-- let's say you wanted to reference an existing chat class's color\n-- \tix.chat.Register(\"myclass\", {\n-- \t\tformat = \"%s says \\\"%s\\\"\",\n-- \t\tGetColor = function(self, speaker, text)\n-- \t\t\t-- make the chat class slightly brighter than the \"ic\" chat class\n-- \t\t\tlocal color = ix.chat.classes.ic:GetColor(speaker, text)\n--\n-- \t\t\treturn Color(color.r + 35, color.g + 35, color.b + 35)\n-- \t\tend,\n-- \t\t-- etc.\n-- \t})\n-- end\n-- @see ix.chat.Register\n-- @see ix.chat.classes\nfunction InitializedChatClasses()\nend\n\n--- @realm shared\nfunction InitializedConfig()\nend\n\n--- @realm shared\nfunction InitializedPlugins()\nend\n\n--- @realm shared\nfunction InitializedSchema()\nend\n\n--- Called when an item was added to the inventory.\n-- @realm server\n-- @tab oldInv Previous item inventory\n-- @tab inventory New item inventory\n-- @tab item Item that was added to the inventory\nfunction InventoryItemAdded(oldInv, inventory, item)\nend\n\n--- Called when an item was removed from the inventory.\n-- @realm server\n-- @tab inventory Inventory from which item was removed\n-- @tab item Item that was removed from the inventory\nfunction InventoryItemRemoved(inventory, item)\nend\n\n--- @realm shared\nfunction IsCharacterRecognized(character, id)\nend\n\n--- @realm client\nfunction IsPlayerRecognized(client)\nend\n\n--- @realm client\nfunction IsRecognizedChatType(chatType)\nend\n\n--- Called when server is loading data.\n-- @realm server\nfunction LoadData()\nend\n\n--- @realm client\nfunction LoadFonts(font, genericFont)\nend\n\n--- @realm client\nfunction LoadIntro()\nend\n\n--- @realm client\nfunction MenuSubpanelCreated(subpanelName, panel)\nend\n\n--- @realm client\nfunction MessageReceived(client, info)\nend\n\n--- @realm client\nfunction OnAreaChanged(oldID, newID)\nend\n\n--- @realm shared\nfunction OnCharacterCreated(client, character)\nend\n\n--- Called when a player who uses a character has disconnected.\n-- @realm server\n-- @player client The player that has disconnected\n-- @char character The character that the player was using\nfunction OnCharacterDisconnect(client, character)\nend\n\n--- Called when a character was ragdolled or unragdolled.\n-- @realm server\n-- @player client Player that was ragdolled or unradolled\n-- @entity entity Ragdoll that represents the player\n-- @bool bFallenOver Whether or not the character was ragdolled or unragdolled\nfunction OnCharacterFallover(client, entity, bFallenOver)\nend\n\n--- Called when a character has gotten up from the ground.\n-- @realm server\n-- @player client Player that has gotten up\n-- @entity ragdoll Ragdoll used to represent the player\nfunction OnCharacterGetup(client, ragdoll)\nend\n\n--- @realm client\nfunction OnCharacterMenuCreated(panel)\nend\n\n--- Called whenever an item entity has spawned in the world. You can access the entity's item table with\n-- `entity:GetItemTable()`.\n-- @realm server\n-- @entity entity Spawned item entity\n-- @usage function PLUGIN:OnItemSpawned(entity)\n-- \tlocal item = entity:GetItemTable()\n-- \t-- do something with the item here\n-- end\nfunction OnItemSpawned(entity)\nend\n\n--- @realm shared\nfunction OnItemTransferred(item, curInv, inventory)\nend\n\n--- @realm client\nfunction OnLocalVarSet(key, var)\nend\n\n--- @realm client\nfunction OnPAC3PartTransferred(part)\nend\n\n--- Called when a player has picked up the money from the ground.\n-- @realm server\n-- @player client Player that picked up the money\n-- @entity self Money entity\n-- @treturn bool Whether or not to allow the player to pick up the money\n-- @usage function PLUGIN:OnPickupMoney(client, self)\n-- \treturn false -- Disallow picking up money.\n-- end\nfunction OnPickupMoney(client, self)\nend\n\n--- @realm shared\nfunction OnPlayerAreaChanged(client, oldID, newID)\nend\n\n--- Called when a player has entered or exited the observer mode.\n-- @realm server\n-- @player client Player that entered or exited the observer mode\n-- @bool state Previous observer state\nfunction OnPlayerObserve(client, state)\nend\n\n--- Called when a player has selected the entity interaction menu option while interacting with a player.\n-- @realm server\n-- @player client Player that other player has interacted with\n-- @player callingClient Player that has interacted with with other player\n-- @string option Option that was selected\nfunction OnPlayerOptionSelected(client, callingClient, option)\nend\n\n--- Called when a player has purchased or sold a door.\n-- @realm server\n-- @player client Player that has purchased or sold a door\n-- @entity entity Door that was purchased or sold\n-- @bool bBuying Whether or not the player is bying a door\n-- @func bCallOnDoorChild Function to call something on the door child\nfunction OnPlayerPurchaseDoor(client, entity, bBuying, bCallOnDoorChild)\nend\n\n--- Called when a player was restricted.\n-- @realm server\n-- @player client Player that was restricted\nfunction OnPlayerRestricted(client)\nend\n\n--- Called when a player was unrestricted.\n-- @realm server\n-- @player client Player that was unrestricted\nfunction OnPlayerUnRestricted(client)\nend\n\n--- Called when a saved items were loaded.\n-- @realm server\n-- @tab loadedItems Table of items that were loaded\nfunction OnSavedItemLoaded(loadedItems)\nend\n\n--- Called when server database are being wiped.\n-- @realm server\nfunction OnWipeTables()\nend\n\n--- @realm shared\nfunction PlayerEnterSequence(client, sequence, callback, time, bNoFreeze)\nend\n\n--- Called when a player has interacted with an entity through the entity's interaction menu.\n-- @realm server\n-- @player client Player that performed interaction\n-- @entity entity Entity being interacted with\n-- @string option Option selected by the player\n-- @param data Any data passed with the interaction option\nfunction PlayerInteractEntity(client, entity, option, data)\nend\n\n--- Called when a player has interacted with an item.\n-- @realm server\n-- @player client Player that interacted with an item\n-- @string action Action selected by the player\n-- @tab item Item being interacted with\nfunction PlayerInteractItem(client, action, item)\nend\n\n--- Called when a player has joined a class.\n-- @realm server\n-- @player client Player that has joined a class\n-- @number class Index of the class player has joined to\n-- @number oldClass Index of the player's previous class\nfunction PlayerJoinedClass(client, class, oldClass)\nend\n\n--- @realm shared\nfunction PlayerLeaveSequence(entity)\nend\n\n--- Called when a player has loaded a character.\n-- @realm server\n-- @player client Player that has loaded a character\n-- @char character Character that was loaded\n-- @char currentChar Character that player was using\nfunction PlayerLoadedCharacter(client, character, currentChar)\nend\n\n--- Called when a player has locked a door.\n-- @realm server\n-- @player client Player that has locked a door\n-- @entity door Door that was locked\n-- @entity partner Door partner\nfunction PlayerLockedDoor(client, door, partner)\nend\n\n--- Called when a player has locked a vehicle.\n-- @realm server\n-- @player client Player that has locked a vehicle\n-- @entity vehicle Vehicle that was locked\nfunction PlayerLockedVehicle(client, vehicle)\nend\n\n--- Called when player has said something in the text chat.\n-- @realm server\n-- @player speaker Player that has said something in the text chat\n-- @string chatType Type of the chat that player used\n-- @string text Chat message that player send\n-- @bool anonymous Whether or not message was anonymous\n-- @tab receivers Players who will hear that message\n-- @string rawText Chat message without any formatting\n-- @treturn string You can return text that will be shown instead\n-- @usage function PLUGIN:PlayerMessageSend(speaker, chatType, text, anonymous, receivers, rawText)\n--  return \"Text\" -- When a player writes something into chat, he will say \"Text\" instead.\n-- end\nfunction PlayerMessageSend(speaker, chatType, text, anonymous, receivers, rawText)\nend\n\n--- Called when a player model was changed.\n-- @realm server\n-- @player client Player whose model was changed\n-- @string oldModel Old player model\nfunction PlayerModelChanged(client, oldModel)\nend\n\n--- Called when a player has got stamina.\n-- @realm server\n-- @player client Player who has got stamina\nfunction PlayerStaminaGained(client)\nend\n\n--- Called when a player has lost stamina.\n-- @realm server\n-- @player client Player who has lost stamina\nfunction PlayerStaminaLost(client)\nend\n\n--- @realm shared\nfunction PlayerThrowPunch(client, trace)\nend\n\n--- Called when a player has unlocked a door.\n-- @realm server\n-- @player client Player that has unlocked a door\n-- @entity door Door that was unlocked\n-- @entity partner Door partner\nfunction PlayerUnlockedDoor(client, door, partner)\nend\n\n--- Called when a player has unlocked a vehicle.\n-- @realm server\n-- @player client Player that has unlocked a vehicle\n-- @entity vehicle Vehicle that was unlocked\nfunction PlayerUnlockedVehicle(client, vehicle)\nend\n\n--- Called when a player has used an entity.\n-- @realm server\n-- @player client Player who has used an entity\n-- @entity entity Entity that was used by the player\nfunction PlayerUse(client, entity)\nend\n\n--- Called when a player has used a door.\n-- @realm server\n-- @player client Player who has used a door\n-- @entity entity Door that was used by the player\nfunction PlayerUseDoor(client, entity)\nend\n\n--- @realm shared\nfunction PlayerWeaponChanged(client, weapon)\nend\n\n--- @realm shared\nfunction PluginLoaded(uniqueID, pluginTable)\nend\n\n--- @realm shared\nfunction PluginShouldLoad(uniqueID)\nend\n\n--- @realm shared\nfunction PluginUnloaded(uniqueID)\nend\n\n--- @realm client\nfunction PopulateCharacterInfo(client, character, tooltip)\nend\n\n--- @realm client\nfunction PopulateEntityInfo(entity, tooltip)\nend\n\n--- @realm client\nfunction PopulateHelpMenu(categories)\nend\n\n--- @realm client\nfunction PopulateImportantCharacterInfo(entity, character, tooltip)\nend\n\n--- @realm client\nfunction PopulateItemTooltip(tooltip, item)\nend\n\n--- @realm client\nfunction PopulatePlayerTooltip(client, tooltip)\nend\n\n--- @realm client\nfunction PopulateScoreboardPlayerMenu(client, menu)\nend\n\n--- @realm client\nfunction PostChatboxDraw(width, height, alpha)\nend\n\n--- @realm client\nfunction PostDrawHelixModelView(panel, entity)\nend\n\n--- @realm client\nfunction PostDrawInventory(panel)\nend\n\n--- Called when server data was loaded.\n-- @realm server\nfunction PostLoadData()\nend\n\n--- Called after player loadout.\n-- @realm server\n-- @player client\nfunction PostPlayerLoadout(client)\nend\n\n--- Called after player has said something in the text chat.\n-- @realm server\n-- @player client Player that has said something in the text chat\n-- @string chatType Type of the chat that player used\n-- @string message Chat message that player send\n-- @bool anonymous Whether or not message was anonymous\nfunction PostPlayerSay(client, chatType, message, anonymous)\nend\n\n--- @realm shared\nfunction PostSetupActs()\nend\n\n--- Called before character deletion.\n-- @realm server\n-- @player client Character owner\n-- @char character Chraracter that will be deleted\nfunction PreCharacterDeleted(client, character)\nend\n\n--- Called before character loading.\n-- @realm server\n-- @player client Player that loading a character\n-- @char character Character that will be loaded\n-- @char currentChar Character that player is using\nfunction PrePlayerLoadedCharacter(client, character, currentChar)\nend\n\n--- Called before a message sent by a player is processed to be sent to other players - i.e this is ran as early as possible\n-- and before things like the auto chat formatting. Can be used to prevent the message from being sent at all.\n-- @realm server\n-- @player client Player sending the message\n-- @string chatType Chat class of the message\n-- @string message Contents of the message\n-- @bool bAnonymous Whether or not the player is sending the message anonymously\n-- @treturn bool Whether or not to prevent the message from being sent\n-- @usage function PLUGIN:PrePlayerMessageSend(client, chatType, message, bAnonymous)\n-- \tif (!client:IsAdmin()) then\n-- \t\treturn false -- only allow admins to talk in chat\n-- \tend\n-- end\nfunction PrePlayerMessageSend(client, chatType, message, bAnonymous)\nend\n\n--- Called when server is saving data.\n-- @realm server\nfunction SaveData()\nend\n\n--- @realm client\nfunction ScreenResolutionChanged(width, height)\nend\n\n--- @realm shared\nfunction SetupActs()\nend\n\n--- @realm shared\nfunction SetupAreaProperties()\nend\n\n--- Called when a player has taken a shipment item.\n-- @realm server\n-- @player client Player that has taken a shipment item\n-- @string uniqueID UniqueID of the shipment item that was taken\n-- @number amount Amount of the items that were taken\nfunction ShipmentItemTaken(client, uniqueID, amount)\nend\n\n--- @realm client\nfunction ShouldBarDraw(bar)\nend\n\n--- Whether or not the server should delete saved items.\n-- @realm server\n-- @treturn bool Whether or not the server should delete saved items\n-- @usage function PLUGIN:ShouldDeleteSavedItems()\n--  return true -- Delete all saved items.\n-- end\nfunction ShouldDeleteSavedItems()\nend\n\n--- @realm client\nfunction ShouldDisplayArea(newID)\nend\n\n--- @realm client\nfunction ShouldDrawCrosshair(client, weapon)\nend\n\n--- @realm client\nfunction ShouldDrawItemSize(item)\nend\n\n--- @realm client\nfunction ShouldHideBars()\nend\n\n--- Whether or not a character should be permakilled upon death. This is only called if the `permakill` server config is\n-- enabled.\n-- @realm server\n-- @player client Player to permakill\n-- @char character Player's current character\n-- @entity inflictor Entity that inflicted the killing blow\n-- @entity attacker Other player or entity that killed the player\n-- @treturn bool `false` if the player should not be permakilled\n-- @usage function PLUGIN:ShouldPermakillCharacter(client, character, inflictor, attacker)\n-- \t\tif (client:IsAdmin()) then\n-- \t\t\treturn false -- all non-admin players will have their character permakilled\n-- \t\tend\n-- \tend\nfunction ShouldPermakillCharacter(client, character, inflictor, attacker)\nend\n\n--- Whether or not player should drown.\n-- @realm server\n-- @player client Player that is underwater\n-- @treturn bool Whether or not player should drown\n-- @usage function PLUGIN:ShouldPlayerDrowned(client)\n--  return false -- Players will not drown.\n-- end\nfunction ShouldPlayerDrowned(client)\nend\n\n--- Whether or not remove player ragdoll on death.\n-- @realm server\n-- @player client Player that died\n-- @treturn bool Whether or not remove player ragdoll on death\n-- @usage function PLUGIN:ShouldRemoveRagdollOnDeath(client)\n--  return false -- Player ragdolls will not be removed.\n-- end\nfunction ShouldRemoveRagdollOnDeath(client)\nend\n\n--- Whether or not to restore character inventory.\n-- @realm server\n-- @number characterID ID of the character\n-- @number inventoryID ID of the inventory\n-- @string inventoryType Type of the inventory\n-- @treturn bool Whether or not to restore character inventory\n-- @usage function PLUGIN:ShouldRestoreInventory(characterID, inventoryID, inventoryType)\n--  return false -- Character inventories will not be restored.\n-- end\nfunction ShouldRestoreInventory(characterID, inventoryID, inventoryType)\nend\n\n--- @realm client\nfunction ShouldShowPlayerOnScoreboard(client)\nend\n\n--- Whether or not spawn player ragdoll on death.\n-- @realm server\n-- @player client Player that died\n-- @treturn bool Whether or not spawn player ragdoll on death\n-- @usage function PLUGIN:ShouldSpawnClientRagdoll(client)\n--  return false -- Player ragdolls will not be spawned.\n-- end\nfunction ShouldSpawnClientRagdoll(client)\nend\n\n--- @realm client\nfunction ShowEntityMenu(entity)\nend\n\n--- @realm client\nfunction ThirdPersonToggled(oldValue, value)\nend\n\n--- @realm client\nfunction UpdateCharacterInfo(panel, character)\nend\n\n--- @realm client\nfunction UpdateCharacterInfoCategory(panel, character)\nend\n\n--- Called when the distance on which the voice can be heard was changed.\n-- @realm server\n-- @number newValue New voice distance\nfunction VoiceDistanceChanged(newValue)\nend\n\n--- @realm client\nfunction WeaponCycleSound()\nend\n\n--- @realm client\nfunction WeaponSelectSound(weapon)\nend\n"
  },
  {
    "path": "docs/js/app.js",
    "content": "\nconst skippedCategories = [\"manual\"];\n\nclass Node\n{\n\tconstructor(name, element, expandable, noAutoCollapse, children = [])\n\t{\n\t\tthis.name = name;\n\t\tthis.element = element;\n\t\tthis.expandable = expandable;\n\t\tthis.noAutoCollapse = noAutoCollapse;\n\t\tthis.children = children;\n\t}\n\n\tAddChild(name, element, expandable, noAutoCollapse, children)\n\t{\n\t\tlet newNode = new Node(name, element, expandable, noAutoCollapse, children);\n\t\tthis.children.push(newNode);\n\n\t\treturn newNode;\n\t}\n}\n\nclass SearchManager\n{\n\tconstructor(input, contents)\n\t{\n\t\tthis.input = input;\n\t\tthis.input.addEventListener(\"input\", event =>\n\t\t{\n\t\t\tthis.OnInputUpdated(this.input.value.toLowerCase().replace(/:/g, \".\"));\n\t\t});\n\n\t\t// setup search tree\n\t\tthis.tree = new Node(\"\", document.createElement(\"null\"), true, true);\n\t\tthis.entries = {};\n\n\t\tconst categoryElements = contents.querySelectorAll(\".category\");\n\n\t\t// iterate each kind (hooks/libraries/classes/etc)\n\t\tfor (const category of categoryElements)\n\t\t{\n\t\t\tconst nameElement = category.querySelector(\":scope > summary > h2\");\n\n\t\t\tif (!nameElement)\n\t\t\t{\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst categoryName = nameElement.textContent.trim().toLowerCase();\n\n\t\t\tif (skippedCategories.includes(categoryName))\n\t\t\t{\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tlet categoryNode = this.tree.AddChild(categoryName, category, true, true);\n\t\t\tconst sectionElements = category.querySelectorAll(\":scope > ul > li\");\n\n\t\t\tfor (const section of sectionElements)\n\t\t\t{\n\t\t\t\tconst entryElements = section.querySelectorAll(\":scope > details > ul > li > a\");\n\t\t\t\tconst sectionName = section.querySelector(\":scope > details > summary > a\")\n\t\t\t\t\t.textContent\n\t\t\t\t\t.trim()\n\t\t\t\t\t.toLowerCase();\n\n\t\t\t\tlet sectionNode = categoryNode.AddChild(sectionName, section.querySelector(\":scope > details\"), true);\n\n\t\t\t\tfor (let i = 0; i < entryElements.length; i++)\n\t\t\t\t{\n\t\t\t\t\tconst entryElement = entryElements[i];\n\t\t\t\t\tconst entryName = entryElement.textContent.trim().toLowerCase();\n\n\t\t\t\t\tsectionNode.AddChild(sectionName + \".\" + entryName, entryElement.parentElement);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tResetVisibility(current)\n\t{\n\t\tcurrent.element.style.display = \"\";\n\n\t\tif (current.noAutoCollapse)\n\t\t{\n\t\t\tcurrent.element.open = true;\n\t\t}\n\t\telse if (current.expandable)\n\t\t{\n\t\t\tcurrent.element.open = false;\n\t\t}\n\n\t\tfor (let node of current.children)\n\t\t{\n\t\t\tthis.ResetVisibility(node);\n\t\t}\n\t}\n\n\tSearch(input, current)\n\t{\n\t\tlet matched = false;\n\n\t\tif (current.name.indexOf(input) != -1)\n\t\t{\n\t\t\tmatched = true;\n\t\t}\n\n\t\tfor (let node of current.children)\n\t\t{\n\t\t\tlet childMatched = this.Search(input, node);\n\t\t\tmatched = matched || childMatched;\n\t\t}\n\n\t\tif (matched)\n\t\t{\n\t\t\tcurrent.element.style.display = \"\";\n\n\t\t\tif (current.expandable)\n\t\t\t{\n\t\t\t\tcurrent.element.open = true;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tcurrent.element.style.display = \"none\";\n\n\t\t\tif (current.expandable)\n\t\t\t{\n\t\t\t\tcurrent.element.open = false;\n\t\t\t}\n\t\t}\n\n\t\treturn matched;\n\t}\n\n\tOnInputUpdated(input)\n\t{\n\t\tif (input.length <= 1)\n\t\t{\n\t\t\tthis.ResetVisibility(this.tree);\n\t\t\treturn;\n\t\t}\n\n\t\tthis.Search(input, this.tree);\n\t}\n}\n\nwindow.onload = function()\n{\n\tconst openDetails = document.querySelector(\".category > ul > li > details[open]\");\n\n\tif (openDetails)\n\t{\n\t\topenDetails.scrollIntoView();\n\t}\n}\n\ndocument.addEventListener(\"DOMContentLoaded\", function()\n{\n\tconst searchInput = document.getElementById(\"search\");\n\tconst contents = document.querySelector(\"body > main > nav > section\");\n\n\tif (searchInput && contents)\n\t{\n\t\tnew SearchManager(searchInput, contents);\n\t}\n});\n"
  },
  {
    "path": "docs/manual/converting-from-clockwork.md",
    "content": "# Clockwork to Helix Migration\n\nIf you are here, you probably want to be converting your code from another framework to Helix. Doing so should not be a difficult task. Most of the previous functions are probably within Helix in one form or another! This means all you need to do is match *x* function found in the old framework to *y* function in Helix. Some headings will contain a link - this will bring you to the documentation for Helix's equivalent library or class.\n\nThis tutorial assumes basic to intermediate knowledge and experience with Garry's Mod Lua.\n\n**Before you start!** You will notice that Helix uses client for the variable that represents a player. Clockwork uses player for the variable instead, but this will conflict with the player library. So if you see `_player` being used in Clockwork, it means the Garry's Mod player library. This is just a preference and does not affect anything besides appear. So keep in mind throughout the tutorial, you may see player being used for Clockwork code and client being used for Helix code. They represent the same thing, just with a different name.\n\nIf you are converting Clockwork code to Helix, keep in mind that `_player` is not defined so you will need to either define `_player` yourself or switch it to player instead and change the variable name to client for player objects.\n\n# Basics of Conversion\n\n## Folders\nClockwork code and file structure is not too different from Helix. In the schema, the plugins folder and schema folder stay in the same place. There are some minor differences in naming however:\n\n- The `schema/entities` folder should be moved outside out of the schema folder.\n- The `libraries` folder needs to be renamed to `libs` to load.\n- The `commands` tab will not load as each command is now defined in a single shared file, does not matter which one.\n\n## Deriving from Helix\nThis is pretty important. If you want to use Helix as the base, you need to set it as the base. So, go to your Clockwork schema's `gamemode` folder. Inside should be two files: `init.lua `and `cl_init.lua`. Open both, and you should see something along the lines of `DeriveGamemode(\"Clockwork\")`. Change this to `DeriveGamemode(\"helix\")`.\n\n# The Schema\n\n## Introduction\nInside of the `schema` folder of the actual schema, you should see a file named `sh_schema.lua`. This is the main schema file in both Clockwork and Helix. Most of your changes may actually be within this file.\n\n## Including Files\nBoth frameworks come with a utility function to include a file without worrying about sending them to the client and stuff. In Clockwork, this function is `Clockwork.kernel:IncludePrefixed(\"sh_myfile.lua\")`. Change this to `ix.util.Include(\"sh_myfile.lua\") `and save.\n\n# The Plugin\n\n## Introduction\nPlugins serve as a means to add on to a schema or framework without directly modifying either. This allows for easier modifications that can be added/removed with ease. It is recommended that you keep all custom modifications left to plugins rather than editing the framework or the schema if possible.\n\n## Structure\nAll plugins in Clockwork and Helix go into the `plugins` folder. However, there are many differences with the CW plugin structure. First of all, there are two things you see when you open a plugin folder: `plugin` again and `plugin.ini`.\n\nHelix only has one file needed: `sh_plugin.lua` which acts like `sh_schema.lua` but for plugins.\n\n## Conversion\nThe first step is to move all of the contents from the `plugin` folder to the main folder of the plugin folder. The `sh_plugin.lua` file needs to be changed to provide basic information about the plugin.You need to define three things in `sh_plugin.lua` which can be found within the `plugin.ini` file:\n\n- `PLUGIN.name = \"Plugin Name\"`\n- `PLUGIN.author = \"Plugin Author\"`\n- `PLUGIN.description = \"Plugin Description\"`\n\nIf the plugin uses a special variable (e.g. `cwPluginName`) for the plugin, change it to `PLUGIN`.\n\n- Note that the `PLUGIN` table is removed after the plugin is loaded. So if you want to use `PLUGIN` after the plugin has loaded (such as in console commands, in entities, etc.), add `local PLUGIN = PLUGIN` at the top.\n- You can see if a global variable is defined for it by looking for `PLUGIN:SetGlobalAlias(\"cwMyPlugin\")`. So, one would change `cwMyPlugin` to `PLUGIN`.\n\n# The `Character` Object\nOne main thing that is very notable is how the character is referenced using `client:GetCharacter()` which returns a character object. The way the object works is just like an entity you spawn. It has its own properties like the model, color, etc. that makes it unique. You can access all the characters in a table which stores loaded characters with `ix.char.loaded`.\n\nThe character object comes with many predefined methods. You can look at how they are defined [by clicking here](https://github.com/NebulousCloud/helix/blob/master/gamemode/core/meta/sh_character.lua). The character object makes it very simple to manager character information.\n\nYou will notice throughout the framework, the character object is used a lot. The use of the character object makes a large barrier between what belongs to the character and what belongs to the player. For example: flags, models, factions, data, and other things are stored on the character and can be accessed by the character object.\n\nIn Clockwork, there is no use of an object. Instead, the character information is intertwined with the player object. For example:\n\n```\n-- in Clockwork\nplayer:SetCharacterData(\"foo\", \"bar\")\n\n-- in Helix\nclient:GetCharacter():SetData(\"foo\", \"bar\")\n```\n\nThe use of the character object allows you to access other characters a player might own without needing to have them be the active character, or even access them when the player is not on the server. Overall, the use of the character object may seem like a complex concept, but will simplify a lot of things once you get the hang of the idea.\n\n# The Libraries\n\n## Animations (`ix.anim`)\nClockwork features many functions to set up animations for a specific model. Helix too has this functionality. Helix has one function instead that pairs a model to a specific \"animation class\" (grouping of animation types). So, all one needs to do is find the appropriate animation class to match the model with. Looking at the Clockwork function name should tell you.\n\n```\n-- before\nClockwork.animation:AddCivilProtectionModel(\"models/mymodel.mdl\")\n\n-- after\nix.anim.SetModelClass(\"models/mymodel.mdl\", \"metrocop\")\n```\n\n## Attributes (`ix.attributes`)\nAttributes allow the player to boost certain abilities over time. Both frameworks require one to register attributes, but they are done differently. In Clockwork, the `ATTRIBUTE` table needs to be defined and registered manually. In Helix, the `ATTRIBUTE` table is automatically defined and registered for you. All you need to do is have `ATTRIBUTE.value = \"value\"`. The basic parts of the attribute needed is `ATTRIBUTE.name` and `ATTRIBUTE.description`.\n\nOne extra feature for attributes in Helix is `ATTRIBUTE:OnSetup(client, value)` which is a function that gets called on spawn to apply any effects. For example, the stamina attribute changes the player's run speed by adding the amount of stamina points the player has.\n\nYou can find an example at [https://github.com/NebulousCloud/helix/blob/master/plugins/stamina/attributes/sh_stm.lua](https://github.com/NebulousCloud/helix/blob/master/plugins/stamina/attributes/sh_stm.lua)\n\n## Classes (`ix.class`)\nClasses are a part of the factions. They basically are a more specific form of a faction. Factions in Helix and Clockwork work similarly. For instance, all classes are placed in the `classes` folder under the schema folder and use `CLASS` as the main variable inside the file.\n\nHowever:\n\n- You do not need to use `local CLASS = Clockwork.class:New(\"My Class\")`. Instead, `CLASS` is already defined for you and you set the name using `CLASS.name = \"My Class\"`\n- `CLASS.factions` is *not* a table, so `CLASS.factions = {FACTION_MYFACTION}` becomes `CLASS.faction = FACTION_MYFACTION`\n- You do not need to use `CLASS:Register()` as classes are registered for you after the file is done processing.\n- Classes are *optional* for factions rather than being required.\n\n## Commands (`ix.command`)\nCommands no longer need to be in separate files. Instead, they are just placed into one large file. However, if you really wanted you can register multiple commands across multiple files or however you want. One thing you may notice is Clockwork uses a _COMMAND_ table while Helix does not always. It is simply a design preference. You can find examples at [https://github.com/NebulousCloud/helix/blob/master/gamemode/core/sh_commands.lua](https://github.com/NebulousCloud/helix/blob/master/gamemode/core/sh_commands.lua)\n\nIt should be noted that:\n\n- `COMMAND.tip` is not used.\n- `COMMAND.text` is not used.\n- `COMMAND.flags` is not used.\n- `COMMAND.arguments` does not need to be defined if no arguments are needed but is defined as a table of argument types when needed `arguments = {ix.type.character, ix.type.number}`. See `ix.command.CommandArgumentsStructure` for details.\n- `COMMAND.access` for checking whether or not a person is a (super)admin can be replaced with `adminOnly = true` or `superAdminOnly = true` in the command table.\n\n## Configurations (`ix.config`)\nIn Helix, the method of adding configurations that can be changed by server owners is heavily simplified. [See an example here](https://github.com/NebulousCloud/helix/blob/master/gamemode/config/sh_config.lua).\n\nAdding a configuration is as follows:\n\n```\n-- before\nClockwork.config:Add(\"run_speed\", 225)\n\n-- after\nix.config.Add(\"runSpeed\", 235, ...)\n```\nYou'll notice that ellipses (...) were added at the end. This is because there are more arguments since adding configuration information has been placed into one function. Additionally:\n\n- `Clockwork.config:ShareKey()` is not needed.\n- The 3rd argument for `Clockwork.config:AddToSystem(name, key, description, min, max)` is also the 3rd argument for `ix.config.Add`\n- The 4th argument for `ix.config.Add` is an optional function that is called when the configuration is changed.\n- The 5th argument for `ix.config.Add` is a table. You can specify the category for the configuration to group it with other configurations. There is also a data table inside which can be used to determine the minimum value and maximum value for numbers. Check out [an example here](https://github.com/NebulousCloud/helix/blob/master/gamemode/config/sh_config.lua). See also `ix.config`.\n\n## Currency (`ix.currency`)\nUpdating your currency code is simple:\n\n```\n-- before\nClockwork.config:SetKey(\"name_cash\", \"Tokens\")\nClockwork.config:SetKey(\"name_cash\", \"Dollars\") -- another example\n\n-- after\nix.currency.Set(\"\", \"token\", \"tokens\")\nix.currency.Set(\"$\", \"dollar\", \"dollars\")\n```\n\nNote that you need to provide a symbol for that currency (€ for Euro, £ for Pound, ¥ for Yen, etc.) or just leave it as an empty string (`\"\"`) and then provide the singular form of the name for the currency, then the plural form.\n\n## Datastream\nHelix uses the [net library](http://wiki.garrysmod.com/page/Net_Library_Usage) whereas Clockwork uses datastream ([netstream](https://github.com/alexgrist/NetStream/blob/master/netstream2.lua)).\n\nIf you're unfamiliar with the net library, you can include the netstream library to your schema by downloading [netstream](https://github.com/alexgrist/NetStream/blob/master/netstream2.lua) to `schema/libs/thirdparty/sh_netstream2.lua` and adding `ix.util.Include(\"libs/thirdparty/sh_netstream2.lua\")` to your `sh_schema.lua` file.\n\nStarting a datastream:\n\n```\n-- before\nClockwork.datastream:Start(receiver, \"MessageName\", {1, 2, 3});\n\n-- after\nnetstream.Start(receiver, \"MessageName\", 1, 2, 3)\n```\n\nReceiving a datastream:\n\n```\n-- before\nClockwork.datastream:Hook(\"MessageName\", function(player, data)\n\tlocal a = data[1];\n\tlocal b = data[2];\n\tlocal c = data[3];\n\n\tprint(a, b, c);\nend);\n\n-- after\nnetstream.Hook(\"MessageName\", function(client, a, b, c)\n\tprint(a, b, c)\nend)\n```\n\n## Factions (`ix.faction`)\nFactions, like classes, are pretty similar too. They share pretty much the same differences as classes in Clockwork and Helix do.\n\nFor instance:\n\n- You do not need to use `local FACTION = Clockwork.faction:New(\"Name Here\")`, instead `FACTION` is already defined for you and you set the name using `FACTION.name = \"Name Here\"`\n- `FACTION.whitelist = true` is changed to `FACTION.isDefault = false`\n- `FACTION.models` does not need a male and female part. Instead, all the models are combined into one big list.\n- `function FACTION:GetName(name)` becomes `function FACTION:GetDefaultName(name)`\n- `FACTION.description = \"Describe me\"` is added to the faction.\n- `FACTION_MYFACTION = FACTION:Register()` becomes `FACTION_MYFACTION = FACTION.index`\n\n## Flags (`ix.flag`)\nFlags are functionally equivalent in Helix. To add a new flag:\n\n```\n-- before\nClockwork.flag:Add(\"x\", \"Name\", \"Description\")\n\n-- after\nix.flag.Add(\"x\", \"Description\")\n```\n\nTo check or manipulate a character's flag(s):\n\n```\n-- before\nClockwork.player:GiveFlags(player, flags)\nClockwork.player:TakeFlags(player, flags)\nClockwork.player:HasFlags(player, flags)\n\n-- after\nclient:GetCharacter():GiveFlags(flags)\nclient:GetCharacter():TakeFlags(flags)\nclient:GetCharacter():HasFlags(flags)\n```\n\n## Inventories (`Inventory`)\nInventories have also had a change in the way they work that may seem very different than Clockwork. Similar to how characters are their own objects, inventories become their own objects as well. These inventory objects belong to character objects, which belongs to players. So, this creates a chain of objects which is neat. The use of inventories as objects makes it very simple to attach inventories to anything.\n\nTo access a player's inventory, you need to use `client:GetCharacter():GetInventory()` which returns the main inventory object for the player's character. You can also access all loaded inventories with `ix.item.inventories` but that is not important right now.\n\n## Items (`Item`)\nAs discussed above, inventories contain items. Items are still used in inventories and world entities, use default class data, have callback functions, and can contain unique item data per instance.\n\n### Setting up items\nEvery time needs to be registered, or have information about it (such as the name, model, what it does, etc.) defined. In Clockwork, you have your items defined in schemas/plugins under the items folder.\n\nSo let's start with the differences in structure in the item file.\n\n- `local ITEM = Clockwork.item:New();` is removed\n- `ITEM.uniqueID` is *completely* optional\n- Replace `ITEM.cost` with `ITEM.price`\n- `ITEM:Register()` is removed\n\n### Item Sizes\nHelix's inventory uses a grid and utilizes width and height instead of weight as a means of inventory capacity. This means you will have to change your item's weight (`ITEM.weight`) to something that might be analagous to the item's size using `ITEM.width` and `ITEM.height`. The item's size must be at least one by one grid cell. It's up to you to balance the sizes of items in your use case - taking into account how many items a character might have at once, the default inventory size set in the config, etc.\n\n### Item Functions\nItem functions are defined very differently than they are in Clockwork. For example:\n\n```\n-- before\nfunction ITEM:OnUse(player, entity)\n\tprint(\"My name is: \" .. player:Name(), entity)\nend\n\n-- after\nITEM.functions.Use = {\n\tOnRun = function(item)\n\t\tprint(\"My name is: \" .. item.player, item.entity)\n\tend\n}\n```\n\nAll item functions are defined in the `ITEM.functions` table. This allows the drop-down menus when using the item a lot easier and cleaner to generate dynamically. There is also more control of the icons used for the options, whether or not the function should be displayed, etc.\n\nYou can see an example of a water item here: [https://github.com/NebulousCloud/helix-hl2rp/blob/master/schema/items/sh_water.lua](https://github.com/NebulousCloud/helix-hl2rp/blob/master/schema/items/sh_water.lua)\n\nHere, we can define what happens when the function is run, what the icon is, and what sound it plays when used. It is basically put into one area rather than being scattered among hooks and stuff.\n\n### Giving/Taking Items\nSo before we can give/take items, we need to understand what the *item instance* is. Using the analogy earlier about how the inventory system is like a forum, and inside the forum are posts (the items in this case), we can think of instancing an item as making a new post on a forum. So when we talk about an *item instance*, it is an item that has been created in the past. The reason we use an item instance (which is its own object too, neat!) is to make each item ever created unique. Each item instance can have its own data unique to itself.\n\nClockwork also uses an item instance system where you have to instance an item. So, to instance an item in Clockwork you would use:\n\n```\nitem = Clockwork.item:CreateInstance(\"item\")\n```\n\nAnd this would create a new instance of an item. Helix's instancing system is slightly different. Instead of having the function return the instance like it does in Clockwork, Helix relies on a callback to pass the instance. The reason for this is the item must be inserted into the database to get a unique number to represent that item. This is not done instantly, otherwise servers would freeze when new items are made. Clockwork uses the time and adds a number to get the numeric ID for an item, which allows the item to be returned which \"solves\" the issue, but I digress.\n\nThe Helix equivalent would be:\n\n```\nix.item.Instance(0, \"item\", data, x, y, function(item) end)\n```\n\nLet's break down the differences:\n\n- For Helix's item instance, the 1st argument (`0`) is the inventory that the item belongs to. You can specify 0 so it does not belong to any inventory.\n- The data argument is *optional* and is just a table for the item data.\n- *x* and *y* are the position of the items in inventory. You can find an available *x* and *y* with `inventory:FindEmptySlot()`.\n- The function is an *optional* argument that passes the item instance. This is where you can directly access the new item.\n\nKeep in mind that Helix will simplify the item system for you when it can. Normally, you would not need to instance an item yourself unless you were doing something advanced.\n\nSo you might be wondering, how do I spawn an item in the map, and how do I give a player an item? In Clockwork, you would do the following:\n\n```\n-- spawning an item in the map\nClockwork.entity:CreateItem(player, Clockwork.item:CreateInstance(\"item\"), Vector(1, 2, 3));\n\n-- giving a player an item\nplayer:GiveItem(Clockwork.item:CreateInstance(\"item\"));\n```\n\nThe equivalent in Helix would be:\n\n```\n-- spawning an item in the map\nix.item.Spawn(\"item\", Vector(1, 2, 3))\n\n-- giving a player an item\nclient:GetCharacter():GetInventory():Add(\"test\")\n```\n\nSo in these two examples, the whole deal of instancing items is done for you in Helix!\n\n# Hooks\nYou will need to modify the function name and arguments for your schema or plugin hooks.\n\n```\n-- before\nfunction Schema:PlayerPlayPainSound(player, gender, damageInfo, hitGroup)\n\t-- ...\nend\n\n-- after\nfunction Schema:GetPlayerPainSound(client)\n\t-- ...\nend\n```\n\nYou can see the documented hooks for the schema and plugins in the `Plugin` section.\n\n# Conclusion\nOverall, most of the conversion from Clockwork to Helix is simply renaming a certain function and/or switching the order of arguments around. Both are frameworks so they function similarly.\n\nYou may want to use our HL2 RP schema example for reference which can be found at [https://github.com/NebulousCloud/helix-hl2rp](https://github.com/NebulousCloud/helix-hl2rp)\n"
  },
  {
    "path": "docs/manual/getting-started.md",
    "content": "# Getting Started\nIt's pretty easy to get started with creating your own schema with Helix. It requires a bit of bootstrapping if you're starting from scratch, but you should quickly be on your way to developing your schema after following one of the below sections in this guide.\n\n# Installing the framework\nBefore you start working on your schema, you'll need to install Helix onto your server. The exact instructions will vary based on your server provider, or if you're hosting the server yourself.\n\nYou'll need to download the framework from [GitHub](https://github.com/NebulousCloud/helix) into a folder called `helix`. This folder goes into your server's `gamemodes` folder at `garrysmod/gamemodes/helix`. That's it! The framework is now installed onto your server. Of course, you'll need to restart your server after installing the framework and a schema.\n\n# MySQL usage\nBy default, Helix will use SQLite (which is built into Garry's Mod) to store player/character data. This requires no configuration and will work \"out of the box\" after installing and will be fine for most server owners. However, you might want to connect your database to your website or use multiple servers with one database - this will require the usage of an external database accessible elsewhere. This will require the use of a MySQL server. Some server providers will provide you with a MySQL database for free to use with your server.\n\n## Installing\nHelix uses the [MySQLOO](https://github.com/FredyH/MySQLOO) library to connect to MySQL databases. You'll need to follow the instructions for installing that library onto your server before continuing. In a nutshell, you need to make sure `gmsv_mysqloo_win32.dll` or `gmsv_mysqloo_linux.dll` (depending on your server's operating system) is in the `garrysmod/lua/bin` folder. \n\nIn older versions of MySQLOO, you previously required a .dll called `libmysql.dll` to place in your `root` server folder, where `srcds`/`srcds_linux` was stored. Newer versions of MySQLOO no longer require this.\n\n## Configuring\nNow that you've installed MySQLOO, you need to tell Helix that you want to connect to an external MySQL database instead of using SQLite. This requires creating a `helix.yml` configuration file in the `garrysmod/gamemodes/helix` folder. There is an example one already made for you called `helix.example.yml` that you can copy and rename to `helix.yml`.\n\nThe first thing you'll need to change is the `adapter` entry so that it says `mysqloo`. Next is to change the other entries to match your database's connection information. Here is an example of what your `helix.yml` should look like:\n\n```\ndatabase:\n  adapter: \"mysqloo\"\n  hostname: \"myexampledatabase.com\"\n  username: \"myusername\"\n  password: \"mypassword\"\n  database: \"helix\"\n  port: 3306\n```\n\nThe `hostname` field can either be a domain name (like `myexampledatabase.com`) or an IP address (`123.123.123.123`). If you don't know what the `port` field should be, simply leave it as the default `3306`; this is the default port for MySQL database connections. The `database` field is the name of the database that you've created for Helix. Note that it does not need to be `helix`, it can be whatever you'd like.\n\nAnother important thing to note about this configuration file is that you **must** indent with **two spaces only**. `database` should not have any spacing before it, and all other entries must have two spaces before them. Failing to ensure this will make the configuration file fail to load.\n\n# Starting with the HL2 RP schema (Basic)\nThis section is for using the existing HL2 RP schema as a base for your own schema. It contains a good amount of example code if you need a stronger foundation than just a skeleton.\n\nFirst, you'll need to download the schema from [GitHub](https://github.com/NebulousCloud/helix-hl2rp). Make sure that you download the contents of the repository into a folder called `ixhl2rp` and place it into your `garrysmod/gamemodes` folder. That's all you'll need to do to get the schema installed, other than setting your gamemode to `ixhl2rp` in the server's command line.\n\n# Starting with the skeleton (Basic)\nIf you don't want excess code you might not use, or prefer to build from an almost-empty foundation that covers the basic bootstrapping, then the skeleton schema is for you. The skeleton schema contains a lot of comments explaining why code is laid out in a certain way, and some other helpful tips/explanations. Make sure you give it a read!\n\nYou'll need to download the schema from [GitHub](https://github.com/NebulousCloud/helix-skeleton) into the folder name of your choice - just make sure it's all lowercase with no spaces. Our example for the sake of brevity will be `myschema`. Place the folder into `garrysmod/gamemodes`.\n\nNext up is to modify the gamemode info so that Garry's Mod will properly recognize it. Rename `skeleton.txt` in your schema folder to your folder's name. In our example we would rename `skeleton.txt` to `myschema.txt`. Next, you'll need to modify the contents of `myschema.txt` and replace the existing information with your own - making sure to replace the `\"skeleton\"` at the top of the file to your folder's name. In our case we would replace it with `\"myschema\"`. Once you've renamed the file, you're all good to go!\n\n# Converting from Clockwork (Intermediate)\nIf you are looking to switch to Helix from Clockwork, you can follow the @{converting-from-clockwork|conversion guide}.\n\n# Starting from scratch (Intermediate)\nYou can always create the gamemode files yourself if you'd like (although we suggest the skeleton schema in general). In general, a schema is a gamemode that is derived from `helix` and automatically loads `schema/sh_schema.lua`. You shouldn't have your schema files outside of the `schema` folder. The files you'll need are as follows:\n\n`gamemode/init.lua`\n```\nAddCSLuaFile(\"cl_init.lua\")\nDeriveGamemode(\"helix\")\n```\n\n`gamemode/cl_init.lua`\n```\nDeriveGamemode(\"helix\")\n```\n\n`schema/sh_schema.lua`\n```\nSchema.name = \"My Schema\"\nSchema.author = \"me!\"\nSchema.description = \"My awesome schema.\"\n\n-- include your other schema files\nix.util.Include(\"cl_schema.lua\")\nix.util.Include(\"sv_schema.lua\")\n-- etc.\n```\n"
  },
  {
    "path": "docs/templates/landing.ltp",
    "content": "\n<div class=\"landing\">\n\t<h1>Helix Documentation</h1>\n</div>\n\n<div class=\"wrapper\">\n\t<p style=\"text-align: center;\">Welcome to the documentation for Helix - the better gamemode framework.</p>\n\t<h2>Developers</h2>\n\t<p>The sidebar shows the entire contents of the documentation. Libraries, functions, etc are all searchable with the search box at the top of the sidebar. Migrating from Clockwork? Check out the <a href=\"{* ldoc.url('manual/converting-from-clockwork') *}\">conversion guide</a> in the manual.</p>\n\t<h2>Server owners</h2>\n\t<p>If you're looking to get your Helix server up and running as soon as possible, check out the <a href=\"{* ldoc.url('manual/getting-started') *}\">Getting Started</a> guide in the manual.</p>\n\t<h2>Community</h2>\n\t<p>Questions? Want to show off your work? Maybe drop a new plugin release? Come join our community <a href=\"https://discord.gg/2AutUcF\" target=\"_blank\">Discord server</a>.</p>\n\t<h2>Contributing</h2>\n\t<p>Helix is a large project and there are still a few things missing here and there. Contributions to the documentation - from function references, to simple typo fixes - are welcomed! Check out the <code>ix.storage</code> library's <a href=\"https://github.com/NebulousCloud/helix/blob/master/gamemode/core/libs/sh_storage.lua\" target=\"_blank\">source code</a> for a good example on how to write documentation. You'll need a basic understanding of <a href=\"https://guides.github.com/features/mastering-markdown/\" target=\"_blank\">Markdown</a>, since it's used extensively to generate the markup.</p>\n\t<p>If you'd like to contribute code, you can visit the <a href=\"https://github.com/NebulousCloud/helix/\" target=\"_blank\">GitHub repository</a> and make a pull request.</p>\n\t<h2>Learning</h2>\n\t<p>Getting started on developing with the Helix framework requires an intermediate level of Garry's Mod Lua knowledge. You'll want to learn the basics before you get starting making a schema. The <a href=\"https://wiki.facepunch.com/gmod/\" target=\"_blank\">Garry's Mod Wiki</a> is a good place to start.</p>\n</div>\n"
  },
  {
    "path": "docs/templates/ldoc.ltp",
    "content": "\n{%\nlocal baseUrl = ldoc.css:gsub(\"ldoc.css\", \"\")\nlocal repo = \"https://github.com/nebulouscloud/helix/\"\nlocal pageTitle = mod and (ldoc.display_name(mod) .. \" - \" .. ldoc.title) or ldoc.title\n\nlocal oldmarkup = ldoc.markup\nfunction ldoc.markup(text, item)\n\treturn oldmarkup(text, item, ldoc.plain)\nend\n\nfunction ldoc.url(path)\n\treturn baseUrl .. path\nend\n\nfunction ldoc.realm_icon(realm)\n\treturn \"<span class=\\\"realm \" .. (realm or \"\") .. \"\\\"></span>\";\nend\n\nfunction ldoc.is_kind_classmethod(kind)\n\treturn kind ~= \"libraries\"\nend\n\nfunction ldoc.repo_reference(item)\n\treturn repo .. \"tree/master\" .. item.file.filename:gsub(item.file.base, \"/gamemode\") .. \"#L\" .. item.lineno\nend\n\nlocal function moduleDescription(mod)\n\tif (mod.type == \"topic\") then\n\t\treturn mod.body:gsub(mod.display_name, \"\"):gsub(\"#\", \"\"):sub(1, 256) .. \"...\"\n\tend\n\n\treturn mod.summary\nend\n%}\n\n<html>\n\t<head>\n\t\t<title>{{pageTitle}}</title>\n\n\t\t<meta property=\"og:type\" content=\"website\" />\n\t\t<meta property=\"og:title\" content=\"{{pageTitle}}\" />\n\t\t<meta property=\"og:site_name\" content=\"Helix Documentation\" />\n\n\t\t{% if (mod) then %}\n\t\t\t<meta property=\"og:description\" content=\"{{moduleDescription(mod)}}\" />\n\t\t{% else %}\n\t\t\t<meta property=\"og:description\" content=\"Documentation and function reference for the Helix framework.\" />\n\t\t{% end %}\n\n\t\t<link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/css?family=Source+Code+Pro\" />\n\t\t<link rel=\"stylesheet\" href=\"{* ldoc.css *}\" />\n\t\t<link rel=\"stylesheet\" href=\"{* ldoc.url('highlight.css') *}\" />\n\t</head>\n\n\t<body>\n\t\t<main>\n\t\t\t{(docs/templates/sidebar.ltp)}\n\n\t\t\t<article>\n\t\t\t\t{% if (ldoc.root) then -- we're rendering the landing page (index.html) %}\n\t\t\t\t\t{(docs/templates/landing.ltp)}\n\t\t\t\t{% elseif (ldoc.body) then -- we're rendering non-code elements %}\n\t\t\t\t\t<div class=\"wrapper\">\n\t\t\t\t\t\t{* ldoc.body *}\n\t\t\t\t\t</div>\n\t\t\t\t{% elseif (module) then -- we're rendering libary contents %}\n\t\t\t\t\t<div class=\"wrapper\">\n\t\t\t\t\t\t{(docs/templates/module.ltp)}\n\t\t\t\t\t</div>\n\t\t\t\t{% end %}\n\t\t\t</article>\n\t\t</main>\n\n\t\t<script type=\"text/javascript\" src=\"{* ldoc.url('app.js') *}\"></script>\n\t\t<script type=\"text/javascript\" src=\"{* ldoc.url('highlight.min.js') *}\"></script>\n\t\t<script type=\"text/javascript\">\n\t\t\tvar elements = document.querySelectorAll(\"pre code\")\n\n\t\t\thljs.configure({\n\t\t\t\tlanguages: [\"lua\"]\n\t\t\t});\n\n\t\t\tfor (var i = 0; i < elements.length; i++)\n\t\t\t{\n\t\t\t\thljs.highlightBlock(elements[i]);\n\t\t\t}\n\t\t</script>\n\t</body>\n</html>\n"
  },
  {
    "path": "docs/templates/module.ltp",
    "content": "\n<header class=\"module\">\n\t<h1>{{mod.name}}</h1>\n\t<h2>{* ldoc.markup(mod.summary) *}</h2>\n</header>\n\n<p>{* ldoc.markup(mod.description) *}</p>\n\n{% for kind, items in mod.kinds() do %}\n\t<h1 class=\"title\">{{kind}}</h1>\n\n\t{% for item in items() do %}\n\t\t<section class=\"method\">\n\t\t\t<header>\n\t\t\t\t<a class=\"anchor\" id=\"{{item.name}}\">\n\t\t\t\t\t<h1>{* ldoc.realm_icon(item.tags.realm[1]) *}</span>{{ldoc.display_name(item)}}</h1>\n\t\t\t\t</a>\n\n\t\t\t\t{% if (item.tags.internal) then %}\n\t\t\t\t\t<div class=\"notice error\">\n\t\t\t\t\t\t<div class=\"title\">Internal</div>\n\t\t\t\t\t\t<p>This is an internal function! You are able to use it, but you risk unintended side effects if used incorrectly.</p>\n\t\t\t\t\t</div>\n\t\t\t\t{% end %}\n\n\t\t\t\t{% if (item.module and item.module.type ~= \"hooks\") then %}\n\t\t\t\t\t<a class=\"reference\" href=\"{* ldoc.repo_reference(item) *}\">View source &raquo;</a>\n\t\t\t\t{% end %}\n\n\t\t\t\t{% if (ldoc.descript(item):len() == 0) then %}\n\t\t\t\t\t<div class=\"notice warning\">\n\t\t\t\t\t\t<div class=\"title\">Incomplete</div>\n\t\t\t\t\t\t<p>Documentation for this section is incomplete and needs expanding.</p>\n\t\t\t\t\t</div>\n\t\t\t\t{% else %}\n\t\t\t\t\t<p>{* ldoc.markup(ldoc.descript(item)) *}</p>\n\t\t\t\t{% end %}\n\t\t\t</header>\n\n\t\t\t{# function arguments #}\n\t\t\t{% if (item.params and #item.params > 0) then %}\n\t\t\t\t{% local subnames = mod.kinds:type_of(item).subnames %}\n\n\t\t\t\t{% if (subnames) then %}\n\t\t\t\t\t<h3>{{subnames}}</h3>\n\t\t\t\t{% end %}\n\n\t\t\t\t{% for argument in ldoc.modules.iter(item.params) do %}\n\t\t\t\t\t{% local argument, sublist = item:subparam(argument) %}\n\n\t\t\t\t\t<ul>\n\t\t\t\t\t\t{% for argumentName in ldoc.modules.iter(argument) do %}\n\t\t\t\t\t\t\t{% local displayName = item:display_name_of(argumentName) %}\n\t\t\t\t\t\t\t{% local type = ldoc.typename(item:type_of_param(argumentName)) %}\n\t\t\t\t\t\t\t{% local default = item:default_of_param(argumentName) %}\n\n\t\t\t\t\t\t\t<li>\n\t\t\t\t\t\t\t\t<span class=\"tag parameter\">{{displayName}}</span>\n\n\t\t\t\t\t\t\t\t{% if (type ~= \"\") then %}\n\t\t\t\t\t\t\t\t\t<span class=\"tag\">{* type *}</span>\n\t\t\t\t\t\t\t\t{% end %}\n\n\t\t\t\t\t\t\t\t{% if (default and default ~= true) then %}\n\t\t\t\t\t\t\t\t\t<span class=\"tag default\">default: {{default}}</span>\n\t\t\t\t\t\t\t\t{% elseif (default) then %}\n\t\t\t\t\t\t\t\t\t<span class=\"tag default\">optional</span>\n\t\t\t\t\t\t\t\t{% end %}\n\n\t\t\t\t\t\t\t\t<p>{* ldoc.markup(item.params.map[argumentName]) *}</p>\n\t\t\t\t\t\t\t</li>\n\t\t\t\t\t\t{% end %}\n\t\t\t\t\t</ul>\n\t\t\t\t{% end %}\n\t\t\t{% end %}\n\n\t\t\t{# function returns #}\n\t\t\t{% if ((not ldoc.no_return_or_parms) and item.retgroups) then %}\n\t\t\t\t{% local groups = item.retgroups %}\n\n\t\t\t\t<h3>Returns</h3>\n\t\t\t\t<ul>\n\t\t\t\t\t{% for i, group in ldoc.ipairs(groups) do %}\n\t\t\t\t\t\t{% for returnValue in group:iter() do %}\n\t\t\t\t\t\t\t{% local type, ctypes = item:return_type(returnValue) %}\n\t\t\t\t\t\t\t{% type = ldoc.typename(type) %}\n\n\t\t\t\t\t\t\t<li>\n\t\t\t\t\t\t\t\t{% if (type ~= \"\") then %}\n\t\t\t\t\t\t\t\t\t{* type *}\n\t\t\t\t\t\t\t\t{% else -- we'll assume that it will return a variable type if none is set %}\n\t\t\t\t\t\t\t\t\t<span class=\"tag type\">any</span>\n\t\t\t\t\t\t\t\t{% end %}\n\n\t\t\t\t\t\t\t\t<p>{* ldoc.markup(returnValue.text) *}</p>\n\t\t\t\t\t\t\t</li>\n\t\t\t\t\t\t{% end %}\n\n\t\t\t\t\t\t{% if (i ~= #groups) then %}\n\t\t\t\t\t\t\t<div class=\"or\"><span>OR</span></div>\n\t\t\t\t\t\t{% end %}\n\t\t\t\t\t{% end %}\n\t\t\t\t</ul>\n\t\t\t{% end %}\n\n\t\t\t{% if (item.usage) then -- function usage %}\n\t\t\t\t<h3>Example Usage</h3>\n\t\t\t\t{% for usage in ldoc.modules.iter(item.usage) do %}\n\t\t\t\t\t<pre><code>{* usage *}</code></pre>\n\t\t\t\t{% end %}\n\t\t\t{% end %}\n\n\t\t\t{% if (item.see) then %}\n\t\t\t\t<h3>See Also</h3>\n\t\t\t\t<ul>\n\t\t\t\t\t{% for see in ldoc.modules.iter(item.see) do %}\n\t\t\t\t\t\t<li><a href=\"{* ldoc.href(see) *}\">{{see.label}}</a></li>\n\t\t\t\t\t{% end %}\n\t\t\t\t</ul>\n\t\t\t{% end %}\n\t\t</section>\n\t{% end %}\n{% end %}\n"
  },
  {
    "path": "docs/templates/sidebar.ltp",
    "content": "\n{%\nlocal function isKindExpandable(kind)\n\treturn kind ~= \"Manual\"\nend\n%}\n\n<nav>\n\t<header>\n\t\t{% if (not ldoc.root) then %}\n\t\t\t<h1><a href=\"{* ldoc.url('') *}\">Helix Documentation</a></h1>\n\t\t{% end %}\n\t\t<input id=\"search\" type=\"search\" autocomplete=\"off\" placeholder=\"Search...\" />\n\t</header>\n\n\t<section>\n\t\t{% for kind, mods, type in ldoc.kinds() do %}\n\t\t\t{% if (ldoc.allowed_in_contents(type, mod)) then %}\n\t\t\t\t<details class=\"category\" open>\n\t\t\t\t\t<summary>\n\t\t\t\t\t\t<h2>{{kind}}</h2>\n\t\t\t\t\t</summary>\n\n\t\t\t\t\t<ul>\n\t\t\t\t\t\t{% for currentMod in mods() do %}\n\t\t\t\t\t\t\t{% local name = ldoc.display_name(currentMod) %}\n\t\t\t\t\t\t\t<li>\n\t\t\t\t\t\t\t\t{% if (isKindExpandable(kind)) then %}\n\t\t\t\t\t\t\t\t\t<details {{currentMod.name == (mod or {}).name and \"open\" or \"\"}}>\n\t\t\t\t\t\t\t\t\t<summary><a href=\"{* ldoc.ref_to_module(currentMod) *}\">{{name}}</a></summary>\n\n\t\t\t\t\t\t\t\t\t<ul>\n\t\t\t\t\t\t\t\t{% else %}\n\t\t\t\t\t\t\t\t\t<a href=\"{* ldoc.ref_to_module(currentMod) *}\">{{name}}</a>\n\t\t\t\t\t\t\t\t{% end %}\n\n\t\t\t\t\t\t\t\t{% if (isKindExpandable(kind)) then\n\t\t\t\t\t\t\t\t\tcurrentMod.items:sort(function(a, b)\n\t\t\t\t\t\t\t\t\t\treturn a.name < b.name\n\t\t\t\t\t\t\t\t\tend)\n\t\t\t\t\t\t\t\tend %}\n\n\t\t\t\t\t\t\t\t{% for k, v in pairs(currentMod.items) do %}\n\t\t\t\t\t\t\t\t\t{% if (v.kind == \"functions\") then %}\n\t\t\t\t\t\t\t\t\t\t<li>\n\t\t\t\t\t\t\t\t\t\t\t{* ldoc.realm_icon(v.tags.realm[1]) *}\n\t\t\t\t\t\t\t\t\t\t\t<a href=\"{* ldoc.ref_to_module(currentMod) *}#{{v.name}}\">\n\t\t\t\t\t\t\t\t\t\t\t\t{% if (ldoc.is_kind_classmethod(currentMod.kind)) then\n\t\t\t\t\t\t\t\t\t\t\t\t\techo((v.name:gsub(\".+:\", \"\")))\n\t\t\t\t\t\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t\t\t\t\t\techo((v.name:gsub(currentMod.name .. \".\", \"\")))\n\t\t\t\t\t\t\t\t\t\t\t\tend %}\n\t\t\t\t\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t\t\t\t\t</li>\n\t\t\t\t\t\t\t\t\t{% end %}\n\t\t\t\t\t\t\t\t{% end %}\n\n\t\t\t\t\t\t\t\t{% if (isKindExpandable(kind)) then %}\n\t\t\t\t\t\t\t\t\t</ul>\n\t\t\t\t\t\t\t\t\t</details>\n\t\t\t\t\t\t\t\t{% end %}\n\t\t\t\t\t\t\t</li>\n\t\t\t\t\t\t{% end %}\n\t\t\t\t\t</ul>\n\t\t\t\t</details>\n\t\t\t{% end %}\n\t\t{% end %}\n\t</section>\n</nav>\n"
  },
  {
    "path": "entities/entities/ix_item.lua",
    "content": "\nAddCSLuaFile()\n\nENT.Base = \"base_entity\"\nENT.Type = \"anim\"\nENT.PrintName = \"Item\"\nENT.Category = \"Helix\"\nENT.Spawnable = false\nENT.ShowPlayerInteraction = true\nENT.RenderGroup = RENDERGROUP_BOTH\nENT.bNoPersist = true\n\nfunction ENT:SetupDataTables()\n\tself:NetworkVar(\"String\", 0, \"ItemID\")\nend\n\nif (SERVER) then\n\tlocal invalidBoundsMin = Vector(-8, -8, -8)\n\tlocal invalidBoundsMax = Vector(8, 8, 8)\n\n\tutil.AddNetworkString(\"ixItemEntityAction\")\n\n\tfunction ENT:Initialize()\n\t\tself:SetModel(\"models/props_junk/watermelon01.mdl\")\n\t\tself:SetSolid(SOLID_VPHYSICS)\n\t\tself:PhysicsInit(SOLID_VPHYSICS)\n\t\tself:SetUseType(SIMPLE_USE)\n\t\tself.health = 50\n\n\t\tlocal physObj = self:GetPhysicsObject()\n\n\t\tif (IsValid(physObj)) then\n\t\t\tphysObj:EnableMotion(true)\n\t\t\tphysObj:Wake()\n\t\tend\n\tend\n\n\tfunction ENT:Use(activator, caller)\n\t\tlocal itemTable = self:GetItemTable()\n\n\t\tif (IsValid(caller) and caller:IsPlayer() and caller:GetCharacter() and itemTable) then\n\t\t\titemTable.player = caller\n\t\t\titemTable.entity = self\n\n\t\t\tif (itemTable.functions.take.OnCanRun(itemTable)) then\n\t\t\t\tcaller:PerformInteraction(ix.config.Get(\"itemPickupTime\", 0.5), self, function(client)\n\t\t\t\t\tif (!ix.item.PerformInventoryAction(client, \"take\", self)) then\n\t\t\t\t\t\treturn false -- do not mark dirty if interaction fails\n\t\t\t\t\tend\n\t\t\t\tend)\n\t\t\tend\n\n\t\t\titemTable.player = nil\n\t\t\titemTable.entity = nil\n\t\tend\n\tend\n\n\tfunction ENT:SetItem(itemID)\n\t\tlocal itemTable = ix.item.instances[itemID]\n\n\t\tif (itemTable) then\n\t\t\tlocal material = itemTable:GetMaterial(self)\n\n\t\t\tself:SetSkin(itemTable:GetSkin())\n\t\t\tself:SetModel(itemTable:GetModel())\n\n\t\t\tif (material) then\n\t\t\t\tself:SetMaterial(material)\n\t\t\tend\n\n\t\t\tself:PhysicsInit(SOLID_VPHYSICS)\n\t\t\tself:SetSolid(SOLID_VPHYSICS)\n\t\t\tself:SetItemID(itemTable.uniqueID)\n\t\t\tself.ixItemID = itemID\n\n\t\t\tif (!table.IsEmpty(itemTable.data)) then\n\t\t\t\tself:SetNetVar(\"data\", itemTable.data)\n\t\t\tend\n\n\t\t\tlocal physObj = self:GetPhysicsObject()\n\n\t\t\tif (!IsValid(physObj)) then\n\t\t\t\tself:PhysicsInitBox(invalidBoundsMin, invalidBoundsMax)\n\t\t\t\tself:SetCollisionBounds(invalidBoundsMin, invalidBoundsMax)\n\t\t\tend\n\n\t\t\tif (IsValid(physObj)) then\n\t\t\t\tphysObj:EnableMotion(true)\n\t\t\t\tphysObj:Wake()\n\t\t\tend\n\n\t\t\tif (itemTable.OnEntityCreated) then\n\t\t\t\titemTable:OnEntityCreated(self)\n\t\t\tend\n\t\tend\n\tend\n\n\tfunction ENT:OnDuplicated(entTable)\n\t\tlocal itemID = entTable.ixItemID\n\t\tlocal itemTable = ix.item.instances[itemID]\n\n\t\tix.item.Instance(0, itemTable.uniqueID, itemTable.data, 1, 1, function(item)\n\t\t\tself:SetItem(item:GetID())\n\t\tend)\n\tend\n\n\tfunction ENT:OnTakeDamage(damageInfo)\n\t\tlocal itemTable = ix.item.instances[self.ixItemID]\n\n\t\tif (itemTable.OnEntityTakeDamage\n\t\tand itemTable:OnEntityTakeDamage(self, damageInfo) == false) then\n\t\t\treturn\n\t\tend\n\n\t\tlocal damage = damageInfo:GetDamage()\n\t\tself:SetHealth(self:Health() - damage)\n\n\t\tif (self:Health() <= 0 and !self.ixIsDestroying) then\n\t\t\tself.ixIsDestroying = true\n\t\t\tself.ixDamageInfo = {damageInfo:GetAttacker(), damage, damageInfo:GetInflictor()}\n\t\t\tself:Remove()\n\t\tend\n\tend\n\n\tfunction ENT:OnRemove()\n\t\tif (!ix.shuttingDown and !self.ixIsSafe and self.ixItemID) then\n\t\t\tlocal itemTable = ix.item.instances[self.ixItemID]\n\n\t\t\tif (itemTable) then\n\t\t\t\tif (self.ixIsDestroying) then\n\t\t\t\t\tself:EmitSound(\"physics/cardboard/cardboard_box_break\"..math.random(1, 3)..\".wav\")\n\t\t\t\t\tlocal position = self:LocalToWorld(self:OBBCenter())\n\n\t\t\t\t\tlocal effect = EffectData()\n\t\t\t\t\t\teffect:SetStart(position)\n\t\t\t\t\t\teffect:SetOrigin(position)\n\t\t\t\t\t\teffect:SetScale(3)\n\t\t\t\t\tutil.Effect(\"GlassImpact\", effect)\n\n\t\t\t\t\tif (itemTable.OnDestroyed) then\n\t\t\t\t\t\titemTable:OnDestroyed(self)\n\t\t\t\t\tend\n\n\t\t\t\t\tix.log.Add(self.ixDamageInfo[1], \"itemDestroy\", itemTable:GetName(), itemTable:GetID())\n\t\t\t\tend\n\n\t\t\t\tif (itemTable.OnRemoved) then\n\t\t\t\t\titemTable:OnRemoved()\n\t\t\t\tend\n\n\t\t\t\tlocal query = mysql:Delete(\"ix_items\")\n\t\t\t\t\tquery:Where(\"item_id\", self.ixItemID)\n\t\t\t\tquery:Execute()\n\t\t\tend\n\t\tend\n\tend\n\n\tfunction ENT:Think()\n\t\tlocal itemTable = self:GetItemTable()\n\n\t\tif (!itemTable) then\n\t\t\tself:Remove()\n\t\tend\n\n\t\tif (itemTable.Think) then\n\t\t\titemTable:Think(self)\n\t\tend\n\n\t\treturn true\n\tend\n\n\tfunction ENT:UpdateTransmitState()\n\t\treturn TRANSMIT_PVS\n\tend\n\n\tnet.Receive(\"ixItemEntityAction\", function(length, client)\n\t\tix.item.PerformInventoryAction(client, net.ReadString(), net.ReadEntity())\n\tend)\nelse\n\tENT.PopulateEntityInfo = true\n\n\tlocal shadeColor = Color(0, 0, 0, 200)\n\tlocal blockSize = 4\n\tlocal blockSpacing = 2\n\n\tfunction ENT:OnPopulateEntityInfo(tooltip)\n\t\tlocal item = self:GetItemTable()\n\n\t\tif (!item) then\n\t\t\treturn\n\t\tend\n\n\t\tlocal oldData = item.data\n\n\t\titem.data = self:GetNetVar(\"data\", {})\n\t\titem.entity = self\n\n\t\tix.hud.PopulateItemTooltip(tooltip, item)\n\n\t\tlocal name = tooltip:GetRow(\"name\")\n\t\tlocal color = name and name:GetBackgroundColor() or ix.config.Get(\"color\")\n\n\t\t-- set the arrow to be the same colour as the title/name row\n\t\ttooltip:SetArrowColor(color)\n\n\t\tif ((item.width > 1 or item.height > 1) and\n\t\t\thook.Run(\"ShouldDrawItemSize\", item) != false) then\n\n\t\t\tlocal sizeHeight = item.height * blockSize + item.height * blockSpacing\n\t\t\tlocal size = tooltip:Add(\"Panel\")\n\t\t\tsize:SetWide(tooltip:GetWide())\n\n\t\t\tif (tooltip:IsMinimal()) then\n\t\t\t\tsize:SetTall(sizeHeight)\n\t\t\t\tsize:Dock(TOP)\n\t\t\t\tsize:SetZPos(-999)\n\t\t\telse\n\t\t\t\tsize:SetTall(sizeHeight + 8)\n\t\t\t\tsize:Dock(BOTTOM)\n\t\t\tend\n\n\t\t\tsize.Paint = function(sizePanel, width, height)\n\t\t\t\tif (!tooltip:IsMinimal()) then\n\t\t\t\t\tsurface.SetDrawColor(ColorAlpha(shadeColor, 60))\n\t\t\t\t\tsurface.DrawRect(0, 0, width, height)\n\t\t\t\tend\n\n\t\t\t\tlocal x, y = width * 0.5 - 1, height * 0.5 - 1\n\t\t\t\tlocal itemWidth = item.width - 1\n\t\t\t\tlocal itemHeight = item.height - 1\n\t\t\t\tlocal heightDifference = ((itemHeight + 1) * blockSize + blockSpacing * itemHeight)\n\n\t\t\t\tx = x - (itemWidth * blockSize + blockSpacing * itemWidth) * 0.5\n\t\t\t\ty = y - heightDifference * 0.5\n\n\t\t\t\tfor i = 0, itemHeight do\n\t\t\t\t\tfor j = 0, itemWidth do\n\t\t\t\t\t\tlocal blockX, blockY = x + j * blockSize + j * blockSpacing, y + i * blockSize + i * blockSpacing\n\n\t\t\t\t\t\tsurface.SetDrawColor(shadeColor)\n\t\t\t\t\t\tsurface.DrawRect(blockX + 1, blockY + 1, blockSize, blockSize)\n\n\t\t\t\t\t\tsurface.SetDrawColor(color)\n\t\t\t\t\t\tsurface.DrawRect(blockX, blockY, blockSize, blockSize)\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\n\t\t\ttooltip:SizeToContents()\n\t\tend\n\n\t\titem.entity = nil\n\t\titem.data = oldData\n\tend\n\n\tfunction ENT:DrawTranslucent()\n\t\tlocal itemTable = self:GetItemTable()\n\n\t\tif (itemTable and itemTable.DrawEntity) then\n\t\t\titemTable:DrawEntity(self)\n\t\tend\n\tend\n\n\tfunction ENT:Draw()\n\t\tself:DrawModel()\n\tend\nend\n\nfunction ENT:GetEntityMenu(client)\n\tlocal itemTable = self:GetItemTable()\n\tlocal options = {}\n\n\tif (!itemTable) then\n\t\treturn false\n\tend\n\n\titemTable.player = client\n\titemTable.entity = self\n\n\tfor k, v in SortedPairs(itemTable.functions) do\n\t\tif (k == \"take\" or k == \"combine\") then\n\t\t\tcontinue\n\t\tend\n\n\t\tif (v.OnCanRun and v.OnCanRun(itemTable) == false) then\n\t\t\tcontinue\n\t\tend\n\n\t\t-- we keep the localized phrase since we aren't using the callbacks - the name won't matter in this case\n\t\toptions[L(v.name or k)] = function()\n\t\t\tlocal send = true\n\n\t\t\tif (v.OnClick) then\n\t\t\t\tsend = v.OnClick(itemTable)\n\t\t\tend\n\n\t\t\tif (v.sound) then\n\t\t\t\tsurface.PlaySound(v.sound)\n\t\t\tend\n\n\t\t\tif (send != false) then\n\t\t\t\tnet.Start(\"ixItemEntityAction\")\n\t\t\t\t\tnet.WriteString(k)\n\t\t\t\t\tnet.WriteEntity(self)\n\t\t\t\tnet.SendToServer()\n\t\t\tend\n\n\t\t\t-- don't run callbacks since we're handling it manually\n\t\t\treturn false\n\t\tend\n\tend\n\n\titemTable.player = nil\n\titemTable.entity = nil\n\n\treturn options\nend\n\nfunction ENT:GetItemTable()\n\treturn ix.item.list[self:GetItemID()]\nend\n\nfunction ENT:GetData(key, default)\n\tlocal data = self:GetNetVar(\"data\", {})\n\n\treturn data[key] or default\nend\n"
  },
  {
    "path": "entities/entities/ix_money.lua",
    "content": "AddCSLuaFile()\n\nENT.Type = \"anim\"\nENT.PrintName = \"Money\"\nENT.Category = \"Helix\"\nENT.Spawnable = false\nENT.ShowPlayerInteraction = true\nENT.bNoPersist = true\n\nfunction ENT:SetupDataTables()\n\tself:NetworkVar(\"Int\", 0, \"Amount\")\nend\n\nif (SERVER) then\n\tlocal invalidBoundsMin = Vector(-8, -8, -8)\n\tlocal invalidBoundsMax = Vector(8, 8, 8)\n\n\tfunction ENT:Initialize()\n\t\tself:SetModel(ix.currency.model)\n\t\tself:SetSolid(SOLID_VPHYSICS)\n\t\tself:PhysicsInit(SOLID_VPHYSICS)\n\t\tself:SetUseType(SIMPLE_USE)\n\n\t\tlocal physObj = self:GetPhysicsObject()\n\n\t\tif (IsValid(physObj)) then\n\t\t\tphysObj:EnableMotion(true)\n\t\t\tphysObj:Wake()\n\t\telse\n\t\t\tself:PhysicsInitBox(invalidBoundsMin, invalidBoundsMax)\n\t\t\tself:SetCollisionBounds(invalidBoundsMin, invalidBoundsMax)\n\t\tend\n\tend\n\n\tfunction ENT:Use(activator)\n\t\tif (self.ixSteamID and self.ixCharID) then\n\t\t\tlocal char = activator:GetCharacter()\n\n\t\t\tif (char and self.ixCharID != char:GetID() and self.ixSteamID == activator:SteamID()) then\n\t\t\t\tactivator:NotifyLocalized(\"itemOwned\")\n\t\t\t\treturn false\n\t\t\tend\n\t\tend\n\n\t\tactivator:PerformInteraction(ix.config.Get(\"itemPickupTime\", 0.5), self, function(client)\n\t\t\tif (hook.Run(\"OnPickupMoney\", client, self) != false) then\n\t\t\t\tself:Remove()\n\t\t\tend\n\t\tend)\n\tend\n\n\tfunction ENT:UpdateTransmitState()\n\t\treturn TRANSMIT_PVS\n\tend\nelse\n\tENT.PopulateEntityInfo = true\n\n\tfunction ENT:OnPopulateEntityInfo(container)\n\t\tlocal text = container:AddRow(\"name\")\n\t\ttext:SetImportant()\n\t\ttext:SetText(ix.currency.Get(self:GetAmount()))\n\t\ttext:SizeToContents()\n\tend\nend\n"
  },
  {
    "path": "entities/entities/ix_shipment.lua",
    "content": "\nAddCSLuaFile()\n\nENT.Type = \"anim\"\nENT.PrintName = \"Shipment\"\nENT.Category = \"Helix\"\nENT.Spawnable = false\nENT.ShowPlayerInteraction = true\nENT.bNoPersist = true\n\nfunction ENT:SetupDataTables()\n\tself:NetworkVar(\"Int\", 0, \"DeliveryTime\")\nend\n\nif (SERVER) then\n\tfunction ENT:Initialize()\n\t\tself:SetModel(\"models/Items/item_item_crate.mdl\")\n\t\tself:SetSolid(SOLID_VPHYSICS)\n\t\tself:PhysicsInit(SOLID_VPHYSICS)\n\t\tself:SetUseType(SIMPLE_USE)\n\t\tself:PrecacheGibs()\n\n\t\tlocal physObj = self:GetPhysicsObject()\n\n\t\tif (IsValid(physObj)) then\n\t\t\tphysObj:EnableMotion(true)\n\t\t\tphysObj:Wake()\n\t\tend\n\n\t\tself:SetDeliveryTime(CurTime() + 120)\n\n\t\ttimer.Simple(120, function()\n\t\t\tif (IsValid(self)) then\n\t\t\t\tself:Remove()\n\t\t\tend\n\t\tend)\n\tend\n\n\tfunction ENT:Use(activator)\n\t\tactivator:PerformInteraction(ix.config.Get(\"itemPickupTime\", 0.5), self, function(client)\n\t\t\tif (client:GetCharacter() and client:GetCharacter():GetID() == self:GetNetVar(\"owner\", 0)\n\t\t\tand hook.Run(\"CanPlayerOpenShipment\", client, self) != false) then\n\t\t\t\tclient.ixShipment = self\n\n\t\t\t\tnet.Start(\"ixShipmentOpen\")\n\t\t\t\t\tnet.WriteEntity(self)\n\t\t\t\t\tnet.WriteTable(self.items)\n\t\t\t\tnet.Send(client)\n\t\t\tend\n\n\t\t\t-- don't mark dirty since the player could come back and use this shipment again later\n\t\t\treturn false\n\t\tend)\n\tend\n\n\tfunction ENT:SetItems(items)\n\t\tself.items = items\n\tend\n\n\tfunction ENT:GetItemCount()\n\t\tlocal count = 0\n\n\t\tfor _, v in pairs(self.items) do\n\t\t\tcount = count + math.max(v, 0)\n\t\tend\n\n\t\treturn count\n\tend\n\n\tfunction ENT:OnRemove()\n\t\tself:EmitSound(\"physics/cardboard/cardboard_box_break\"..math.random(1, 3)..\".wav\")\n\n\t\tlocal position = self:LocalToWorld(self:OBBCenter())\n\n\t\tlocal effect = EffectData()\n\t\t\teffect:SetStart(position)\n\t\t\teffect:SetOrigin(position)\n\t\t\teffect:SetScale(3)\n\t\tutil.Effect(\"GlassImpact\", effect)\n\tend\n\n\tfunction ENT:UpdateTransmitState()\n\t\treturn TRANSMIT_PVS\n\tend\nelse\n\tENT.PopulateEntityInfo = true\n\n\tlocal size = 150\n\tlocal tempMat = Material(\"particle/warp1_warp\", \"alphatest\")\n\n\tfunction ENT:Draw()\n\t\tlocal pos, ang = self:GetPos(), self:GetAngles()\n\n\t\tself:DrawModel()\n\n\t\tpos = pos + self:GetUp() * 25\n\t\tpos = pos + self:GetForward() * 1\n\t\tpos = pos + self:GetRight() * 3\n\n\t\tlocal delTime = math.max(math.ceil(self:GetDeliveryTime() - CurTime()), 0)\n\n\t\tlocal func = function()\n\t\t\tsurface.SetMaterial(tempMat)\n\t\t\tsurface.SetDrawColor(0, 0, 0, 200)\n\t\t\tsurface.DrawTexturedRect(-size / 2, -size / 2 - 10, size, size)\n\n\t\t\tix.util.DrawText(\"k\", 0, 0, color_white, 1, 4, \"ixIconsBig\")\n\t\t\tix.util.DrawText(delTime, 0, -10, color_white, 1, 5, \"ixBigFont\")\n\t\tend\n\n\t\tcam.Start3D2D(pos, ang, .15)\n\t\t\tfunc()\n\t\tcam.End3D2D()\n\n\t\tang:RotateAroundAxis(ang:Right(), 180)\n\t\tpos = pos - self:GetUp() * 26\n\n\t\tcam.Start3D2D(pos, ang, .15)\n\t\t\tfunc()\n\t\tcam.End3D2D()\n\tend\n\n\tfunction ENT:OnPopulateEntityInfo(container)\n\t\tlocal owner = ix.char.loaded[self:GetNetVar(\"owner\", 0)]\n\n\t\tlocal name = container:AddRow(\"name\")\n\t\tname:SetImportant()\n\t\tname:SetText(L(\"shipment\"))\n\t\tname:SizeToContents()\n\n\t\tif (owner) then\n\t\t\tlocal description = container:AddRow(\"description\")\n\t\t\tdescription:SetText(L(\"shipmentDesc\", owner:GetName()))\n\t\t\tdescription:SizeToContents()\n\t\tend\n\tend\nend\n"
  },
  {
    "path": "entities/weapons/ix_hands.lua",
    "content": "\nAddCSLuaFile()\n\nif (CLIENT) then\n\tSWEP.PrintName = \"Hands\"\n\tSWEP.Slot = 0\n\tSWEP.SlotPos = 1\n\tSWEP.DrawAmmo = false\n\tSWEP.DrawCrosshair = true\nend\n\nSWEP.Author = \"Chessnut\"\nSWEP.Instructions = [[Primary Fire: Throw/Punch\nSecondary Fire: Knock/Pickup\nSecondary Fire + Mouse: Rotate Object\nReload: Drop]]\nSWEP.Purpose = \"Hitting things and knocking on doors.\"\nSWEP.Drop = false\n\nSWEP.ViewModelFOV = 45\nSWEP.ViewModelFlip = false\nSWEP.AnimPrefix\t = \"rpg\"\n\nSWEP.ViewTranslation = 4\nif CLIENT then\n\tSWEP.NextAllowedPlayRateChange = 0\nend\n\nSWEP.Primary.ClipSize = -1\nSWEP.Primary.DefaultClip = -1\nSWEP.Primary.Automatic = false\nSWEP.Primary.Ammo = \"\"\nSWEP.Primary.Damage = 5\nSWEP.Primary.Delay = 0.75\n\nSWEP.Secondary.ClipSize = -1\nSWEP.Secondary.DefaultClip = 0\nSWEP.Secondary.Automatic = false\nSWEP.Secondary.Ammo = \"\"\nSWEP.Secondary.Delay = 0.5\n\nSWEP.ViewModel = Model(\"models/weapons/c_arms.mdl\")\nSWEP.WorldModel = \"\"\n\nSWEP.UseHands = true\nSWEP.LowerAngles = Angle(0, 5, -14)\nSWEP.LowerAngles2 = Angle(0, 5, -19)\nSWEP.KnockViewPunchAngle = Angle(-1.3, 1.8, 0)\n\nSWEP.FireWhenLowered = true\nSWEP.HoldType = \"fist\"\n\nSWEP.holdDistance = 64\nSWEP.maxHoldDistance = 96 -- how far away the held object is allowed to travel before forcefully dropping\nSWEP.maxHoldStress = 4000 -- how much stress the held object can undergo before forcefully dropping\n\n-- luacheck: globals ACT_VM_FISTS_DRAW ACT_VM_FISTS_HOLSTER\nACT_VM_FISTS_DRAW = 2\nACT_VM_FISTS_HOLSTER = 1\n\nfunction SWEP:Initialize()\n\tself:SetHoldType(self.HoldType)\n\n\tself.lastHand = 0\n\tself.maxHoldDistanceSquared = self.maxHoldDistance ^ 2\n\tself.heldObjectAngle = Angle(angle_zero)\nend\n\nif (CLIENT) then\n\tfunction SWEP:DoDrawCrosshair(x, y)\n\t\tsurface.SetDrawColor(255, 255, 255, 66)\n\t\tsurface.DrawRect(x - 2, y - 2, 4, 4)\n\tend\n\n\thook.Add(\"CreateMove\", \"ixHandsCreateMove\", function(cmd)\n\t\tif (LocalPlayer():GetLocalVar(\"bIsHoldingObject\", false) and cmd:KeyDown(IN_ATTACK2)) then\n\t\t\tcmd:ClearMovement()\n\t\t\tlocal angle = RenderAngles()\n\t\t\tangle.z = 0\n\t\t\tcmd:SetViewAngles(angle)\n\t\tend\n\tend)\nend\n\nfunction SWEP:Deploy()\n\tif (!IsValid(self:GetOwner())) then\n\t\treturn\n\tend\n\n\tlocal viewModel = self:GetOwner():GetViewModel()\n\n\tif (IsValid(viewModel)) then\n\t\tviewModel:SetPlaybackRate(1)\n\t\tviewModel:ResetSequence(ACT_VM_FISTS_DRAW)\n\t\tif CLIENT then\n\t\t\tself.NextAllowedPlayRateChange = CurTime() + viewModel:SequenceDuration()\n\t\tend\n\tend\n\n\tself:DropObject()\n\treturn true\nend\n\nfunction SWEP:Precache()\n\tutil.PrecacheSound(\"npc/vort/claw_swing1.wav\")\n\tutil.PrecacheSound(\"npc/vort/claw_swing2.wav\")\n\tutil.PrecacheSound(\"physics/plastic/plastic_box_impact_hard1.wav\")\n\tutil.PrecacheSound(\"physics/plastic/plastic_box_impact_hard2.wav\")\n\tutil.PrecacheSound(\"physics/plastic/plastic_box_impact_hard3.wav\")\n\tutil.PrecacheSound(\"physics/plastic/plastic_box_impact_hard4.wav\")\n\tutil.PrecacheSound(\"physics/wood/wood_crate_impact_hard2.wav\")\n\tutil.PrecacheSound(\"physics/wood/wood_crate_impact_hard3.wav\")\nend\n\nfunction SWEP:OnReloaded()\n\tself.maxHoldDistanceSquared = self.maxHoldDistance ^ 2\n\tself:DropObject()\nend\n\nfunction SWEP:Holster()\n\tif (!IsValid(self:GetOwner())) then\n\t\treturn\n\tend\n\n\tlocal viewModel = self:GetOwner():GetViewModel()\n\n\tif (IsValid(viewModel)) then\n\t\tviewModel:SetPlaybackRate(1)\n\t\tviewModel:ResetSequence(ACT_VM_FISTS_HOLSTER)\n\t\tif CLIENT then\n\t\t\tself.NextAllowedPlayRateChange = CurTime() + viewModel:SequenceDuration()\n\t\tend\n\tend\n\n\treturn true\nend\n\nfunction SWEP:Think()\n\tif (!IsValid(self:GetOwner())) then\n\t\treturn\n\tend\n\n\tif (CLIENT) then\n\t\tlocal viewModel = self:GetOwner():GetViewModel()\n\n\t\tif (IsValid(viewModel) and self.NextAllowedPlayRateChange < CurTime()) then\n\t\t\tviewModel:SetPlaybackRate(1)\n\t\tend\n\telse\n\t\tif (self:IsHoldingObject()) then\n\t\t\tlocal physics = self:GetHeldPhysicsObject()\n\t\t\tlocal bIsRagdoll = self.heldEntity:IsRagdoll()\n\t\t\tlocal holdDistance = bIsRagdoll and self.holdDistance * 0.5 or self.holdDistance\n\t\t\tlocal targetLocation = self:GetOwner():GetShootPos() + self:GetOwner():GetForward() * holdDistance\n\n\t\t\tif (bIsRagdoll) then\n\t\t\t\ttargetLocation.z = math.min(targetLocation.z, self:GetOwner():GetShootPos().z - 32)\n\t\t\tend\n\n\t\t\tif (!IsValid(physics)) then\n\t\t\t\tself:DropObject()\n\t\t\t\treturn\n\t\t\tend\n\n\t\t\tif (physics:GetPos():DistToSqr(targetLocation) > self.maxHoldDistanceSquared) then\n\t\t\t\tself:DropObject()\n\t\t\telse\n\t\t\t\tlocal physicsObject = self.holdEntity:GetPhysicsObject()\n\t\t\t\tlocal currentPlayerAngles = self:GetOwner():EyeAngles()\n\t\t\t\tlocal client = self:GetOwner()\n\n\t\t\t\tif (client:KeyDown(IN_ATTACK2)) then\n\t\t\t\t\tlocal cmd = client:GetCurrentCommand()\n\t\t\t\t\tself.heldObjectAngle:RotateAroundAxis(currentPlayerAngles:Forward(), cmd:GetMouseX() / 15)\n\t\t\t\t\tself.heldObjectAngle:RotateAroundAxis(currentPlayerAngles:Right(), cmd:GetMouseY() / 15)\n\t\t\t\tend\n\n\t\t\t\tself.lastPlayerAngles = self.lastPlayerAngles or currentPlayerAngles\n\t\t\t\tself.heldObjectAngle.y = self.heldObjectAngle.y - math.AngleDifference(self.lastPlayerAngles.y, currentPlayerAngles.y)\n\t\t\t\tself.lastPlayerAngles = currentPlayerAngles\n\n\t\t\t\tphysicsObject:Wake()\n\t\t\t\tphysicsObject:ComputeShadowControl({\n\t\t\t\t\tsecondstoarrive = 0.01,\n\t\t\t\t\tpos = targetLocation,\n\t\t\t\t\tangle = self.heldObjectAngle,\n\t\t\t\t\tmaxangular = 256,\n\t\t\t\t\tmaxangulardamp = 10000,\n\t\t\t\t\tmaxspeed = 256,\n\t\t\t\t\tmaxspeeddamp = 10000,\n\t\t\t\t\tdampfactor = 0.8,\n\t\t\t\t\tteleportdistance = self.maxHoldDistance * 0.75,\n\t\t\t\t\tdeltatime = FrameTime()\n\t\t\t\t})\n\n\t\t\t\tif (physics:GetStress() > self.maxHoldStress) then\n\t\t\t\t\tself:DropObject()\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\t\t-- Prevents the camera from getting stuck when the object that the client is holding gets deleted.\n\t\tif(!IsValid(self.heldEntity) and self:GetOwner():GetLocalVar(\"bIsHoldingObject\", true)) then\n\t\t\tself:GetOwner():SetLocalVar(\"bIsHoldingObject\", false)\n\t\tend\n\tend\nend\n\nfunction SWEP:GetHeldPhysicsObject()\n\treturn IsValid(self.heldEntity) and self.heldEntity:GetPhysicsObject() or nil\nend\n\nfunction SWEP:CanHoldObject(entity)\n\tlocal physics = entity:GetPhysicsObject()\n\n\treturn IsValid(physics) and\n\t\t(physics:GetMass() <= ix.config.Get(\"maxHoldWeight\", 100) and physics:IsMoveable()) and\n\t\t!self:IsHoldingObject() and\n\t\t!IsValid(entity.ixHeldOwner) and\n\t\thook.Run(\"CanPlayerHoldObject\", self:GetOwner(), entity)\nend\n\nfunction SWEP:IsHoldingObject()\n\treturn IsValid(self.heldEntity) and\n\t\tIsValid(self.heldEntity.ixHeldOwner) and\n\t\tself.heldEntity.ixHeldOwner == self:GetOwner()\nend\n\nfunction SWEP:PickupObject(entity)\n\tif (self:IsHoldingObject() or\n\t\t!IsValid(entity) or\n\t\t!IsValid(entity:GetPhysicsObject())) then\n\t\treturn\n\tend\n\n\tlocal physics = entity:GetPhysicsObject()\n\tphysics:EnableGravity(false)\n\tphysics:AddGameFlag(FVPHYSICS_PLAYER_HELD)\n\n\tentity.ixHeldOwner = self:GetOwner()\n\tentity.ixCollisionGroup = entity:GetCollisionGroup()\n\tentity:StartMotionController()\n\tentity:SetCollisionGroup(COLLISION_GROUP_WEAPON)\n\n\tself.heldObjectAngle = entity:GetAngles()\n\tself.heldEntity = entity\n\n\tself.holdEntity = ents.Create(\"prop_physics\")\n\tself.holdEntity:SetPos(self.heldEntity:LocalToWorld(self.heldEntity:OBBCenter()))\n\tself.holdEntity:SetAngles(self.heldEntity:GetAngles())\n\tself.holdEntity:SetModel(\"models/weapons/w_bugbait.mdl\")\n\tself.holdEntity:SetOwner(self:GetOwner())\n\n\tself.holdEntity:SetNoDraw(true)\n\tself.holdEntity:SetNotSolid(true)\n\tself.holdEntity:SetCollisionGroup(COLLISION_GROUP_DEBRIS)\n\tself.holdEntity:DrawShadow(false)\n\n\tself.holdEntity:Spawn()\n\n\tlocal trace = self:GetOwner():GetEyeTrace()\n\tlocal physicsObject = self.holdEntity:GetPhysicsObject()\n\n\tif (IsValid(physicsObject)) then\n\t\tphysicsObject:SetMass(2048)\n\t\tphysicsObject:SetDamping(0, 1000)\n\t\tphysicsObject:EnableGravity(false)\n\t\tphysicsObject:EnableCollisions(false)\n\t\tphysicsObject:EnableMotion(false)\n\tend\n\n\tif (trace.Entity:IsRagdoll()) then\n\t\tlocal tracedEnt = trace.Entity\n\t\tself.holdEntity:SetPos(tracedEnt:GetBonePosition(tracedEnt:TranslatePhysBoneToBone(trace.PhysicsBone)))\n\tend\n\n\tself.constraint = constraint.Weld(self.holdEntity, self.heldEntity, 0,\n\t\ttrace.Entity:IsRagdoll() and trace.PhysicsBone or 0, 0, true, true)\nend\n\nfunction SWEP:DropObject(bThrow)\n\tif (!IsValid(self.heldEntity) or self.heldEntity.ixHeldOwner != self:GetOwner()) then\n\t\treturn\n\tend\n\n\tself.lastPlayerAngles = nil\n\tself:GetOwner():SetLocalVar(\"bIsHoldingObject\", false)\n\n\tself.constraint:Remove()\n\tself.holdEntity:Remove()\n\n\tself.heldEntity:StopMotionController()\n\tself.heldEntity:SetCollisionGroup(self.heldEntity.ixCollisionGroup or COLLISION_GROUP_NONE)\n\n\tlocal physics = self:GetHeldPhysicsObject()\n\tphysics:EnableGravity(true)\n\tphysics:Wake()\n\tphysics:ClearGameFlag(FVPHYSICS_PLAYER_HELD)\n\n\tif (bThrow) then\n\t\ttimer.Simple(0, function()\n\t\t\tif (IsValid(physics) and IsValid(self:GetOwner())) then\n\t\t\t\tphysics:AddGameFlag(FVPHYSICS_WAS_THROWN)\n\t\t\t\tphysics:ApplyForceCenter(self:GetOwner():GetAimVector() * ix.config.Get(\"throwForce\", 732))\n\t\t\tend\n\t\tend)\n\tend\n\n\tself.heldEntity.ixHeldOwner = nil\n\tself.heldEntity.ixCollisionGroup = nil\n\tself.heldEntity = nil\nend\n\nfunction SWEP:PlayPickupSound(surfaceProperty)\n\tlocal result = \"Flesh.ImpactSoft\"\n\n\tif (surfaceProperty != nil) then\n\t\tlocal surfaceName = util.GetSurfacePropName(surfaceProperty)\n\t\tlocal soundName = surfaceName:gsub(\"^metal$\", \"SolidMetal\") .. \".ImpactSoft\"\n\n\t\tif (sound.GetProperties(soundName)) then\n\t\t\tresult = soundName\n\t\tend\n\tend\n\n\tself:GetOwner():EmitSound(result, 75, 100, 40)\nend\n\nfunction SWEP:Holster()\n\tif (!IsFirstTimePredicted() or CLIENT) then\n\t\treturn\n\tend\n\n\tself:DropObject()\n\treturn true\nend\n\nfunction SWEP:OnRemove()\n\tif (SERVER) then\n\t\tself:DropObject()\n\tend\nend\n\nfunction SWEP:OwnerChanged()\n\tif (SERVER) then\n\t\tself:DropObject()\n\tend\nend\n\nfunction SWEP:DoPunchAnimation()\n\tself.lastHand = math.abs(1 - self.lastHand)\n\n\tlocal sequence = 3 + self.lastHand\n\tlocal viewModel = self:GetOwner():GetViewModel()\n\n\tif (IsValid(viewModel)) then\n\t\tviewModel:SetPlaybackRate(0.5)\n\t\tviewModel:SetSequence(sequence)\n\t\tif CLIENT then\n\t\t\tself.NextAllowedPlayRateChange = CurTime() + viewModel:SequenceDuration() * 2\n\t\tend\n\tend\nend\n\nfunction SWEP:PrimaryAttack()\n\tif (!IsFirstTimePredicted()) then\n\t\treturn\n\tend\n\n\tif (SERVER and self:IsHoldingObject()) then\n\t\tself:DropObject(true)\n\t\treturn\n\tend\n\n\tself:SetNextPrimaryFire(CurTime() + self.Primary.Delay)\n\n\tif (hook.Run(\"CanPlayerThrowPunch\", self:GetOwner()) == false) then\n\t\treturn\n\tend\n\n\tif (ix.plugin.Get(\"stamina\")) then\n\t\tlocal staminaUse = ix.config.Get(\"punchStamina\")\n\n\t\tif (staminaUse > 0) then\n\t\t\tlocal value = self:GetOwner():GetLocalVar(\"stm\", 0) - staminaUse\n\n\t\t\tif (value < 0) then\n\t\t\t\treturn\n\t\t\telseif (SERVER) then\n\t\t\t\tself:GetOwner():ConsumeStamina(staminaUse)\n\t\t\tend\n\t\tend\n\tend\n\n\tif (SERVER) then\n\t\tself:GetOwner():EmitSound(\"npc/vort/claw_swing\"..math.random(1, 2)..\".wav\")\n\tend\n\n\tself:DoPunchAnimation()\n\n\tself:GetOwner():SetAnimation(PLAYER_ATTACK1)\n\tself:GetOwner():ViewPunch(Angle(self.lastHand + 2, self.lastHand + 5, 0.125))\n\n\ttimer.Simple(0.055, function()\n\t\tif (IsValid(self) and IsValid(self:GetOwner())) then\n\t\t\tlocal damage = self.Primary.Damage\n\t\t\tlocal context = {damage = damage}\n\t\t\tlocal result = hook.Run(\"GetPlayerPunchDamage\", self:GetOwner(), damage, context)\n\n\t\t\tif (result != nil) then\n\t\t\t\tdamage = result\n\t\t\telse\n\t\t\t\tdamage = context.damage\n\t\t\tend\n\n\t\t\tself:GetOwner():LagCompensation(true)\n\t\t\t\tlocal data = {}\n\t\t\t\t\tdata.start = self:GetOwner():GetShootPos()\n\t\t\t\t\tdata.endpos = data.start + self:GetOwner():GetAimVector() * 96\n\t\t\t\t\tdata.filter = self:GetOwner()\n\t\t\t\tlocal trace = util.TraceLine(data)\n\n\t\t\t\tif (SERVER and trace.Hit) then\n\t\t\t\t\tlocal entity = trace.Entity\n\n\t\t\t\t\tif (IsValid(entity)) then\n\t\t\t\t\t\tlocal damageInfo = DamageInfo()\n\t\t\t\t\t\t\tdamageInfo:SetAttacker(self:GetOwner())\n\t\t\t\t\t\t\tdamageInfo:SetInflictor(self)\n\t\t\t\t\t\t\tdamageInfo:SetDamage(damage)\n\t\t\t\t\t\t\tdamageInfo:SetDamageType(DMG_GENERIC)\n\t\t\t\t\t\t\tdamageInfo:SetDamagePosition(trace.HitPos)\n\t\t\t\t\t\t\tdamageInfo:SetDamageForce(self:GetOwner():GetAimVector() * 1024)\n\t\t\t\t\t\tentity:DispatchTraceAttack(damageInfo, data.start, data.endpos)\n\n\t\t\t\t\t\tself:GetOwner():EmitSound(\"physics/body/body_medium_impact_hard\"..math.random(1, 6)..\".wav\", 80)\n\t\t\t\t\tend\n\t\t\t\tend\n\n\t\t\t\thook.Run(\"PlayerThrowPunch\", self:GetOwner(), trace)\n\t\t\tself:GetOwner():LagCompensation(false)\n\t\tend\n\tend)\nend\n\nfunction SWEP:SecondaryAttack()\n\tif (!IsFirstTimePredicted()) then\n\t\treturn\n\tend\n\n\tlocal data = {}\n\t\tdata.start = self:GetOwner():GetShootPos()\n\t\tdata.endpos = data.start + self:GetOwner():GetAimVector() * 84\n\t\tdata.filter = {self, self:GetOwner()}\n\tlocal trace = util.TraceLine(data)\n\tlocal entity = trace.Entity\n\n\tif CLIENT then\n\t\tlocal viewModel = self:GetOwner():GetViewModel()\n\n\t\tif (IsValid(viewModel)) then\n\t\t\tviewModel:SetPlaybackRate(0.5)\n\t\t\tif CLIENT then\n\t\t\t\tself.NextAllowedPlayRateChange = CurTime() + viewModel:SequenceDuration() * 2\n\t\t\tend\n\t\tend\n\tend\n\n\tif (SERVER and IsValid(entity)) then\n\t\tif (entity:IsDoor()) then\n\t\t\tif (hook.Run(\"CanPlayerKnock\", self:GetOwner(), entity) == false) then\n\t\t\t\treturn\n\t\t\tend\n\n\t\t\tself:GetOwner():ViewPunch(self.KnockViewPunchAngle)\n\t\t\tself:GetOwner():EmitSound(\"physics/wood/wood_crate_impact_hard\"..math.random(2, 3)..\".wav\")\n\t\t\tself:GetOwner():SetAnimation(PLAYER_ATTACK1)\n\n\t\t\tself:DoPunchAnimation()\n\t\t\tself:SetNextSecondaryFire(CurTime() + 0.4)\n\t\t\tself:SetNextPrimaryFire(CurTime() + 1)\n\t\telseif (entity:IsPlayer() and ix.config.Get(\"allowPush\", true)) then\n\t\t\tlocal direction = self:GetOwner():GetAimVector() * (300 + (self:GetOwner():GetCharacter():GetAttribute(\"str\", 0) * 3))\n\t\t\t\tdirection.z = 0\n\t\t\tentity:SetVelocity(direction)\n\n\t\t\tself:GetOwner():EmitSound(\"Weapon_Crossbow.BoltHitBody\")\n\t\t\tself:SetNextSecondaryFire(CurTime() + 1.5)\n\t\t\tself:SetNextPrimaryFire(CurTime() + 1.5)\n\t\telseif (!entity:IsNPC() and self:CanHoldObject(entity)) then\n\t\t\tself:GetOwner():SetLocalVar(\"bIsHoldingObject\", true)\n\t\t\tself:PickupObject(entity)\n\t\t\tself:PlayPickupSound(trace.SurfaceProps)\n\t\t\tself:SetNextSecondaryFire(CurTime() + self.Secondary.Delay)\n\t\tend\n\tend\nend\n\nfunction SWEP:Reload()\n\tif (!IsFirstTimePredicted()) then\n\t\treturn\n\tend\n\n\tif (SERVER and IsValid(self.heldEntity)) then\n\t\tself:DropObject()\n\tend\nend\n"
  },
  {
    "path": "gamemode/cl_init.lua",
    "content": "\n-- unix systems are case-sensitive, are missing fonts, or use different naming conventions\nif (!system.IsWindows()) then\n\tlocal fontOverrides = {\n\t\t[\"Roboto\"] = \"Roboto Regular\",\n\t\t[\"Roboto Th\"] = \"Roboto Thin\",\n\t\t[\"Roboto Lt\"] = \"Roboto Light\",\n\t\t[\"Roboto Bk\"] = \"Roboto Black\",\n\t\t[\"coolvetica\"] = \"Coolvetica\",\n\t\t[\"tahoma\"] = \"Tahoma\",\n\t\t[\"Harmonia Sans Pro Cyr\"] = \"Roboto Regular\",\n\t\t[\"Harmonia Sans Pro Cyr Light\"] = \"Roboto Light\",\n\t\t[\"Century Gothic\"] = \"Roboto Regular\"\n\t}\n\n\tif (system.IsOSX()) then\n\t\tfontOverrides[\"Consolas\"] = \"Monaco\"\n\telse\n\t\tfontOverrides[\"Consolas\"] = \"Courier New\"\n\tend\n\n\tlocal ixCreateFont = surface.CreateFont\n\n\tfunction surface.CreateFont(name, info) -- luacheck: globals surface\n\t\tlocal font = info.font\n\n\t\tif (font and fontOverrides[font]) then\n\t\t\tinfo.font = fontOverrides[font]\n\t\tend\n\n\t\tixCreateFont(name, info)\n\tend\nend\n\nDeriveGamemode(\"sandbox\")\nix = ix or {util = {}, gui = {}, meta = {}}\n\n-- Include core files.\ninclude(\"core/sh_util.lua\")\ninclude(\"core/sh_data.lua\")\ninclude(\"shared.lua\")\n\n-- Sandbox stuff\nCreateConVar(\"cl_weaponcolor\", \"0.30 1.80 2.10\", {\n\tFCVAR_ARCHIVE, FCVAR_USERINFO, FCVAR_DONTRECORD\n}, \"The value is a Vector - so between 0-1 - not between 0-255\")\n\ntimer.Remove(\"HintSystem_OpeningMenu\")\ntimer.Remove(\"HintSystem_Annoy1\")\ntimer.Remove(\"HintSystem_Annoy2\")\n"
  },
  {
    "path": "gamemode/config/sh_config.lua",
    "content": "\r\n-- You can change the default language by setting this in your schema.\r\nix.config.language = \"english\"\r\n\r\n--[[\r\n\tDO NOT CHANGE ANYTHING BELOW THIS.\r\n\r\n\tThis is the Helix main configuration file.\r\n\tThis file DOES NOT set any configurations, instead it just prepares them.\r\n\tTo set the configuration, there is a \"Config\" tab in the F1 menu for super admins and above.\r\n\tUse the menu to change the variables, not this file.\r\n--]]\r\n\r\nix.config.Add(\"maxCharacters\", 5, \"The maximum number of characters a player can have.\", nil, {\r\n\tdata = {min = 1, max = 50},\r\n\tcategory = \"characters\"\r\n})\r\nix.config.Add(\"color\", Color(75, 119, 190, 255), \"The main color theme for the framework.\", function(oldValue, newValue)\r\n\tif (newValue.a != 255) then\r\n\t\tix.config.Set(\"color\", ColorAlpha(newValue, 255))\r\n\t\treturn\r\n\tend\r\n\r\n\tif (CLIENT) then\r\n\t\thook.Run(\"ColorSchemeChanged\", newValue)\r\n\tend\r\nend, {category = \"appearance\"})\r\nix.config.Add(\"font\", \"Roboto Th\", \"The font used to display titles.\", function(oldValue, newValue)\r\n\tif (CLIENT) then\r\n\t\thook.Run(\"LoadFonts\", newValue, ix.config.Get(\"genericFont\"))\r\n\tend\r\nend, {category = \"appearance\"})\r\n\r\nix.config.Add(\"genericFont\", \"Roboto\", \"The font used to display generic texts.\", function(oldValue, newValue)\r\n\tif (CLIENT) then\r\n\t\thook.Run(\"LoadFonts\", ix.config.Get(\"font\"), newValue)\r\n\tend\r\nend, {category = \"appearance\"})\r\n\r\nix.config.Add(\"maxAttributes\", 100, \"The maximum amount each attribute can be.\", nil, {\r\n\tdata = {min = 0, max = 100},\r\n\tcategory = \"characters\"\r\n})\r\nix.config.Add(\"chatAutoFormat\", true, \"Whether or not to automatically capitalize and punctuate in-character text.\", nil, {\r\n\tcategory = \"Chat\"\r\n})\r\nix.config.Add(\"chatRange\", 280, \"The maximum distance a person's IC chat message goes to.\", nil, {\r\n\tdata = {min = 10, max = 5000, decimals = 1},\r\n\tcategory = \"chat\"\r\n})\r\nix.config.Add(\"chatMax\", 256, \"The maximum amount of characters that can be sent in chat.\", nil, {\r\n\tdata = {min = 32, max = 1024},\r\n\tcategory = \"chat\"\r\n})\r\nix.config.Add(\"chatColor\", Color(255, 255, 150), \"The default color for IC chat.\", nil, {category = \"chat\"})\r\nix.config.Add(\"chatListenColor\", Color(175, 255, 150), \"The color for IC chat if you are looking at the speaker.\", nil, {\r\n\tcategory = \"chat\"\r\n})\r\nix.config.Add(\"oocDelay\", 10, \"The delay before a player can use OOC chat again in seconds.\", nil, {\r\n\tdata = {min = 0, max = 10000},\r\n\tcategory = \"chat\"\r\n})\r\nix.config.Add(\"allowGlobalOOC\", true, \"Whether or not Global OOC is enabled.\", nil, {\r\n\tcategory = \"chat\"\r\n})\r\nix.config.Add(\"loocDelay\", 0, \"The delay before a player can use LOOC chat again in seconds.\", nil, {\r\n\tdata = {min = 0, max = 10000},\r\n\tcategory = \"chat\"\r\n})\r\nix.config.Add(\"spawnTime\", 5, \"The time it takes to respawn.\", nil, {\r\n\tdata = {min = 0, max = 10000},\r\n\tcategory = \"characters\"\r\n})\r\nix.config.Add(\"inventoryWidth\", 6, \"How many slots in a row there is in a default inventory.\", nil, {\r\n\tdata = {min = 0, max = 20},\r\n\tcategory = \"characters\"\r\n})\r\nix.config.Add(\"inventoryHeight\", 4, \"How many slots in a column there is in a default inventory.\", nil, {\r\n\tdata = {min = 0, max = 20},\r\n\tcategory = \"characters\"\r\n})\r\nix.config.Add(\"minNameLength\", 4, \"The minimum number of characters in a name.\", nil, {\r\n\tdata = {min = 4, max = 64},\r\n\tcategory = \"characters\"\r\n})\r\nix.config.Add(\"maxNameLength\", 32, \"The maximum number of characters in a name.\", nil, {\r\n\tdata = {min = 16, max = 128},\r\n\tcategory = \"characters\"\r\n})\r\nix.config.Add(\"minDescriptionLength\", 16, \"The minimum number of characters in a description.\", nil, {\r\n\tdata = {min = 0, max = 300},\r\n\tcategory = \"characters\"\r\n})\r\nix.config.Add(\"saveInterval\", 300, \"How often characters save in seconds.\", nil, {\r\n\tdata = {min = 60, max = 3600},\r\n\tcategory = \"characters\"\r\n})\r\nix.config.Add(\"walkSpeed\", 130, \"How fast a player normally walks.\", function(oldValue, newValue)\r\n\tfor _, v in player.Iterator()\tdo\r\n\t\tv:SetWalkSpeed(newValue)\r\n\tend\r\nend, {\r\n\tdata = {min = 75, max = 500},\r\n\tcategory = \"characters\"\r\n})\r\nix.config.Add(\"runSpeed\", 235, \"How fast a player normally runs.\", function(oldValue, newValue)\r\n\tfor _, v in player.Iterator()\tdo\r\n\t\tv:SetRunSpeed(newValue)\r\n\tend\r\nend, {\r\n\tdata = {min = 75, max = 500},\r\n\tcategory = \"characters\"\r\n})\r\nix.config.Add(\"walkRatio\", 0.5, \"How fast one goes when holding ALT.\", nil, {\r\n\tdata = {min = 0, max = 1, decimals = 1},\r\n\tcategory = \"characters\"\r\n})\r\nix.config.Add(\"intro\", true, \"Whether or not the Helix intro is enabled for new players.\", nil, {\r\n\tcategory = \"appearance\"\r\n})\r\nix.config.Add(\"music\", \"music/hl2_song2.mp3\", \"The default music played in the character menu.\", nil, {\r\n\tcategory = \"appearance\"\r\n})\r\nix.config.Add(\"communityURL\", \"https://nebulous.cloud/\", \"The URL to navigate to when the community button is clicked.\", nil, {\r\n\tcategory = \"appearance\"\r\n})\r\nix.config.Add(\"communityText\", \"@community\",\r\n\t\"The text to display on the community button. You can use language phrases by prefixing with @\", nil, {\r\n\tcategory = \"appearance\"\r\n})\r\nix.config.Add(\"vignette\", true, \"Whether or not the vignette is shown.\", nil, {\r\n\tcategory = \"appearance\"\r\n})\r\nix.config.Add(\"scoreboardRecognition\", false, \"Whether or not recognition is used in the scoreboard.\", nil, {\r\n\tcategory = \"characters\"\r\n})\r\nix.config.Add(\"defaultMoney\", 0, \"The amount of money that players start with.\", nil, {\r\n\tcategory = \"characters\",\r\n\tdata = {min = 0, max = 1000}\r\n})\r\nix.config.Add(\"minMoneyDropAmount\", 1, \"The minimum amount of money that can be dropped.\", nil, {\r\n\tcategory = \"characters\",\r\n\tdata = {min = 1, max = 1000}\r\n})\r\nix.config.Add(\"allowVoice\", false, \"Whether or not voice chat is allowed.\", function(oldValue, newValue)\r\n\tif (SERVER) then\r\n\t\thook.Run(\"VoiceToggled\", newValue)\r\n\tend\r\nend, {\r\n\tcategory = \"server\"\r\n})\r\nix.config.Add(\"voiceDistance\", 600.0, \"How far can the voice be heard.\", function(oldValue, newValue)\r\n\tif (SERVER) then\r\n\t\thook.Run(\"VoiceDistanceChanged\", newValue)\r\n\tend\r\nend, {\r\n\tcategory = \"server\",\r\n\tdata = {min = 0, max = 5000, decimals = 1}\r\n})\r\nix.config.Add(\"weaponAlwaysRaised\", false, \"Whether or not weapons are always raised.\", nil, {\r\n\tcategory = \"server\"\r\n})\r\nix.config.Add(\"weaponRaiseTime\", 1, \"The time it takes for a weapon to raise.\", nil, {\r\n\tdata = {min = 0.1, max = 60, decimals = 1},\r\n\tcategory = \"server\"\r\n})\r\nix.config.Add(\"allowBusiness\", true, \"Whether or not business is enabled.\", nil, {\r\n\tcategory = \"server\"\r\n})\r\nix.config.Add(\"maxHoldWeight\", 100, \"The maximum weight that a player can carry in their hands.\", nil, {\r\n\tdata = {min = 1, max = 500},\r\n\tcategory = \"interaction\"\r\n})\r\nix.config.Add(\"throwForce\", 732, \"How hard a player can throw the item that they're holding.\", nil, {\r\n\tdata = {min = 0, max = 8192},\r\n\tcategory = \"interaction\"\r\n})\r\nix.config.Add(\"allowPush\", true, \"Whether or not pushing with hands is allowed.\", nil, {\r\n\tcategory = \"interaction\"\r\n})\r\nix.config.Add(\"itemPickupTime\", 0.5, \"How long it takes to pick up and put an item in your inventory.\", nil, {\r\n\tdata = {min = 0, max = 5, decimals = 1},\r\n\tcategory = \"interaction\"\r\n})\r\nix.config.Add(\"year\", 2015, \"The current in-game year.\", function(oldValue, newValue)\r\n\tif (SERVER and !ix.date.bSaving) then\r\n\t\tix.date.ResolveOffset()\r\n\t\tix.date.current:setyear(newValue)\r\n\t\tix.date.Send()\r\n\tend\r\nend, {\r\n\tdata = {min = 1, max = 9999},\r\n\tcategory = \"date\"\r\n})\r\nix.config.Add(\"month\", 1, \"The current in-game month.\", function(oldValue, newValue)\r\n\tif (SERVER and !ix.date.bSaving) then\r\n\t\tix.date.ResolveOffset()\r\n\t\tix.date.current:setmonth(newValue)\r\n\t\tix.date.Send()\r\n\tend\r\nend, {\r\n\tdata = {min = 1, max = 12},\r\n\tcategory = \"date\"\r\n})\r\nix.config.Add(\"day\", 1, \"The current in-game day.\", function(oldValue, newValue)\r\n\tif (SERVER and !ix.date.bSaving) then\r\n\t\tix.date.ResolveOffset()\r\n\t\tix.date.current:setday(newValue)\r\n\t\tix.date.Send()\r\n\tend\r\nend, {\r\n\tdata = {min = 1, max = 31},\r\n\tcategory = \"date\"\r\n})\r\nix.config.Add(\"secondsPerMinute\", 60, \"How many seconds it takes for a minute to pass in-game.\", function(oldValue, newValue)\r\n\tif (SERVER and !ix.date.bSaving) then\r\n\t\tix.date.UpdateTimescale(newValue)\r\n\t\tix.date.Send()\r\n\tend\r\nend, {\r\n\tdata = {min = 0.01, max = 120},\r\n\tcategory = \"date\"\r\n})"
  },
  {
    "path": "gamemode/config/sh_options.lua",
    "content": "\nif (CLIENT) then\n\tix.option.Add(\"animationScale\", ix.type.number, 1, {\n\t\tcategory = \"appearance\", min = 0.3, max = 2, decimals = 1\n\t})\n\n\tix.option.Add(\"24hourTime\", ix.type.bool, false, {\n\t\tcategory = \"appearance\"\n\t})\n\n\tix.option.Add(\"altLower\", ix.type.bool, true, {\n\t\tcategory = \"general\"\n\t})\n\n\tix.option.Add(\"alwaysShowBars\", ix.type.bool, false, {\n\t\tcategory = \"appearance\"\n\t})\n\n\tix.option.Add(\"minimalTooltips\", ix.type.bool, false, {\n\t\tcategory = \"appearance\"\n\t})\n\n\tix.option.Add(\"noticeDuration\", ix.type.number, 8, {\n\t\tcategory = \"appearance\", min = 0.1, max = 20, decimals = 1\n\t})\n\n\tix.option.Add(\"noticeMax\", ix.type.number, 4, {\n\t\tcategory = \"appearance\", min = 1, max = 20\n\t})\n\n\tix.option.Add(\"cheapBlur\", ix.type.bool, false, {\n\t\tcategory = \"performance\"\n\t})\n\n\tix.option.Add(\"disableAnimations\", ix.type.bool, false, {\n\t\tcategory = \"performance\"\n\t})\n\n\tix.option.Add(\"openBags\", ix.type.bool, true, {\n\t\tcategory = \"general\"\n\t})\n\n\tix.option.Add(\"showIntro\", ix.type.bool, true, {\n\t\tcategory = \"general\"\n\t})\n\n\tix.option.Add(\"escCloseMenu\", ix.type.bool, false, {\n\t\tcategory = \"general\"\n\t})\nend\n\nix.option.Add(\"language\", ix.type.array, ix.config.language or \"english\", {\n\tcategory = \"general\",\n\tbNetworked = true,\n\tpopulate = function()\n\t\tlocal entries = {}\n\n\t\tfor k, _ in SortedPairs(ix.lang.stored) do\n\t\t\tlocal name = ix.lang.names[k]\n\t\t\tlocal name2 = k:utf8sub(1, 1):utf8upper() .. k:utf8sub(2)\n\n\t\t\tif (name) then\n\t\t\t\tname = name .. \" (\" .. name2 .. \")\"\n\t\t\telse\n\t\t\t\tname = name2\n\t\t\tend\n\n\t\t\tentries[k] = name\n\t\tend\n\n\t\treturn entries\n\tend\n})\n"
  },
  {
    "path": "gamemode/core/cl_skin.lua",
    "content": "\nlocal gradient = surface.GetTextureID(\"vgui/gradient-d\")\nlocal gradientUp = surface.GetTextureID(\"vgui/gradient-u\")\nlocal gradientLeft = surface.GetTextureID(\"vgui/gradient-l\")\nlocal gradientRadial = Material(\"helix/gui/radial-gradient.png\")\nlocal defaultBackgroundColor = Color(30, 30, 30, 200)\n\nlocal SKIN = {}\nderma.DefineSkin(\"helix\", \"The base skin for the Helix framework.\", SKIN)\n\nSKIN.fontCategory = \"ixMediumLightFont\"\nSKIN.fontCategoryBlur = \"ixMediumLightBlurFont\"\nSKIN.fontSegmentedProgress = \"ixMediumLightFont\"\n\nSKIN.Colours = table.Copy(derma.SkinList.Default.Colours)\n\nSKIN.Colours.Info = Color(100, 185, 255)\nSKIN.Colours.Success = Color(64, 185, 85)\nSKIN.Colours.Error = Color(255, 100, 100)\nSKIN.Colours.Warning = Color(230, 180, 0)\nSKIN.Colours.MenuLabel = color_white\nSKIN.Colours.DarkerBackground = Color(0, 0, 0, 77)\n\nSKIN.Colours.SegmentedProgress = {}\nSKIN.Colours.SegmentedProgress.Bar = Color(64, 185, 85)\nSKIN.Colours.SegmentedProgress.Text = color_white\n\nSKIN.Colours.Area = {}\n\nSKIN.Colours.Window.TitleActive = Color(0, 0, 0)\nSKIN.Colours.Window.TitleInactive = color_white\n\nSKIN.Colours.Button.Normal = color_white\nSKIN.Colours.Button.Hover = color_white\nSKIN.Colours.Button.Down = Color(180, 180, 180)\nSKIN.Colours.Button.Disabled = Color(0, 0, 0, 100)\n\nSKIN.Colours.Label.Default = color_white\n\nfunction SKIN.tex.Menu_Strip(x, y, width, height, color)\n\tsurface.SetDrawColor(0, 0, 0, 200)\n\tsurface.DrawRect(x, y, width, height)\n\n\tsurface.SetDrawColor(ColorAlpha(color or ix.config.Get(\"color\"), 175))\n\tsurface.SetTexture(gradient)\n\tsurface.DrawTexturedRect(x, y, width, height)\n\n\tsurface.SetTextColor(color_white)\nend\n\nhook.Add(\"ColorSchemeChanged\", \"ixSkin\", function(color)\n\tSKIN.Colours.Area.Background = color\nend)\n\nfunction SKIN:DrawHelixCurved(x, y, radius, segments, barHeight, fraction, color, altColor)\n\tradius = radius or math.min(ScreenScale(72), 128) * 2\n\tsegments = segments or 76\n\tbarHeight = barHeight or 64\n\tcolor = color or ix.config.Get(\"color\")\n\taltColor = altColor or Color(color.r * 0.5, color.g * 0.5, color.b * 0.5, color.a)\n\tfraction = fraction or 1\n\n\tsurface.SetTexture(-1)\n\n\tfor i = 1, math.ceil(segments) do\n\t\tlocal angle = math.rad((i / segments) * -360)\n\t\tlocal barX = x + math.sin(angle + (fraction * math.pi * 2)) * radius\n\t\tlocal barY = y + math.cos(angle + (fraction * math.pi * 2)) * radius\n\t\tlocal barOffset = math.sin(SysTime() + i * 0.5)\n\n\t\tif (barOffset > 0) then\n\t\t\tsurface.SetDrawColor(color)\n\t\telse\n\t\t\tsurface.SetDrawColor(altColor)\n\t\tend\n\n\t\tsurface.DrawTexturedRectRotated(barX, barY, 4, barOffset * (barHeight * fraction), math.deg(angle))\n\tend\nend\n\nfunction SKIN:DrawHelix(x, y, width, height, segments, color, fraction, speed)\n\tsegments = segments or width * 0.05\n\tcolor = color or ix.config.Get(\"color\")\n\tfraction = fraction or 0.25\n\tspeed = speed or 1\n\n\tfor i = 1, math.ceil(segments) do\n\t\tlocal offset = math.sin((SysTime() + speed) + i * fraction)\n\t\tlocal barHeight = height * offset\n\n\t\tsurface.SetTexture(-1)\n\n\t\tif (offset > 0) then\n\t\t\tsurface.SetDrawColor(color)\n\t\telse\n\t\t\tsurface.SetDrawColor(color.r * 0.5, color.g * 0.5, color.b * 0.5, color.a)\n\t\tend\n\n\t\tsurface.DrawTexturedRectRotated(x + (i / segments) * width, y + height * 0.5, 4, barHeight, 0)\n\tend\nend\n\nfunction SKIN:PaintFrame(panel)\n\tif (!panel.bNoBackgroundBlur) then\n\t\tix.util.DrawBlur(panel, 10)\n\tend\n\n\tsurface.SetDrawColor(30, 30, 30, 150)\n\tsurface.DrawRect(0, 0, panel:GetWide(), panel:GetTall())\n\n\tif (panel:GetTitle() != \"\" or panel.btnClose:IsVisible()) then\n\t\tsurface.SetDrawColor(ix.config.Get(\"color\"))\n\t\tsurface.DrawRect(0, 0, panel:GetWide(), 24)\n\n\t\tif (panel.bHighlighted) then\n\t\t\tself:DrawImportantBackground(0, 0, panel:GetWide(), 24, ColorAlpha(color_white, 22))\n\t\tend\n\tend\n\n\tsurface.SetDrawColor(ix.config.Get(\"color\"))\n\tsurface.DrawOutlinedRect(0, 0, panel:GetWide(), panel:GetTall())\nend\n\nfunction SKIN:PaintBaseFrame(panel, width, height)\n\tif (!panel.bNoBackgroundBlur) then\n\t\tix.util.DrawBlur(panel, 10)\n\tend\n\n\tsurface.SetDrawColor(30, 30, 30, 150)\n\tsurface.DrawRect(0, 0, width, height)\n\n\tsurface.SetDrawColor(ix.config.Get(\"color\"))\n\tsurface.DrawOutlinedRect(0, 0, width, height)\nend\n\nfunction SKIN:DrawImportantBackground(x, y, width, height, color)\n\tcolor = color or defaultBackgroundColor\n\n\tsurface.SetTexture(gradientLeft)\n\tsurface.SetDrawColor(color)\n\tsurface.DrawTexturedRect(x, y, width, height)\nend\n\nfunction SKIN:DrawCharacterStatusBackground(panel, fraction)\n\tsurface.SetDrawColor(0, 0, 0, fraction * 100)\n\tsurface.DrawRect(0, 0, ScrW(), ScrH())\n\tix.util.DrawBlurAt(0, 0, ScrW(), ScrH(), 5, nil, fraction * 255)\nend\n\nfunction SKIN:PaintPanel(panel)\n\tif (panel.m_bBackground) then\n\t\tlocal width, height = panel:GetSize()\n\t\tif (panel.m_bgColor) then\n\t\t\tsurface.SetDrawColor(panel.m_bgColor)\n\t\telse\n\t\t\tsurface.SetDrawColor(30, 30, 30, 100)\n\t\tend\n\t\tsurface.DrawRect(0, 0, width, height)\n\t\tsurface.SetDrawColor(0, 0, 0, 150)\n\t\tsurface.DrawOutlinedRect(0, 0, width, height)\n\tend\nend\n\nfunction SKIN:PaintMenuBackground(panel, width, height, alphaFraction)\n\talphaFraction = alphaFraction or 1\n\n\tsurface.SetDrawColor(ColorAlpha(color_black, alphaFraction * 255))\n\tsurface.SetTexture(gradient)\n\tsurface.DrawTexturedRect(0, 0, width, height)\n\n\tix.util.DrawBlur(panel, alphaFraction * 15, nil, 200)\nend\n\nfunction SKIN:PaintPlaceholderPanel(panel, width, height, barWidth, padding)\n\tlocal size = math.max(width, height)\n\tbarWidth = barWidth or size * 0.05\n\n\tlocal segments = size / barWidth\n\n\tfor i = 1, segments do\n\t\tsurface.SetTexture(-1)\n\t\tsurface.SetDrawColor(0, 0, 0, 88)\n\t\tsurface.DrawTexturedRectRotated(i * barWidth, i * barWidth, barWidth, size * 2, -45)\n\tend\nend\n\nfunction SKIN:PaintCategoryPanel(panel, text, color)\n\ttext = text or \"\"\n\tcolor = color or ix.config.Get(\"color\")\n\n\tsurface.SetFont(self.fontCategoryBlur)\n\n\tlocal textHeight = select(2, surface.GetTextSize(text)) + 6\n\tlocal width, height = panel:GetSize()\n\n\tsurface.SetDrawColor(0, 0, 0, 100)\n\tsurface.DrawRect(0, textHeight, width, height - textHeight)\n\n\tself:DrawImportantBackground(0, 0, width, textHeight, color)\n\n\tsurface.SetTextColor(color_black)\n\tsurface.SetTextPos(4, 4)\n\tsurface.DrawText(text)\n\n\tsurface.SetFont(self.fontCategory)\n\tsurface.SetTextColor(color_white)\n\tsurface.SetTextPos(4, 4)\n\tsurface.DrawText(text)\n\n\tsurface.SetDrawColor(color)\n\tsurface.DrawOutlinedRect(0, 0, width, height)\n\n\treturn 1, textHeight, 1, 1\nend\n\nfunction SKIN:PaintButton(panel)\n\tif (panel.m_bBackground) then\n\t\tlocal w, h = panel:GetWide(), panel:GetTall()\n\t\tlocal alpha = 50\n\n\t\tif (panel:GetDisabled()) then\n\t\t\talpha = 10\n\t\telseif (panel.Depressed) then\n\t\t\talpha = 180\n\t\telseif (panel.Hovered) then\n\t\t\talpha = 75\n\t\tend\n\n\t\tif (panel:GetParent() and panel:GetParent():GetName() == \"DListView_Column\") then\n\t\t\tsurface.SetDrawColor(color_white)\n\t\t\tsurface.DrawRect(0, 0, w, h)\n\t\tend\n\n\t\tsurface.SetDrawColor(30, 30, 30, alpha)\n\t\tsurface.DrawRect(0, 0, w, h)\n\n\t\tsurface.SetDrawColor(0, 0, 0, 180)\n\t\tsurface.DrawOutlinedRect(0, 0, w, h)\n\n\t\tsurface.SetDrawColor(180, 180, 180, 2)\n\t\tsurface.DrawOutlinedRect(1, 1, w - 2, h - 2)\n\tend\nend\n\nfunction SKIN:PaintEntityInfoBackground(panel, width, height)\n\tix.util.DrawBlur(panel, 1)\n\n\tsurface.SetDrawColor(self.Colours.DarkerBackground)\n\tsurface.DrawRect(0, 0, width, height)\nend\n\nfunction SKIN:PaintTooltipBackground(panel, width, height)\n\tix.util.DrawBlur(panel, 1)\n\n\tsurface.SetDrawColor(self.Colours.DarkerBackground)\n\tsurface.DrawRect(0, 0, width, height)\nend\n\nfunction SKIN:PaintTooltipMinimalBackground(panel, width, height)\n\tsurface.SetDrawColor(0, 0, 0, 150 * panel.fraction)\n\tsurface.SetMaterial(gradientRadial)\n\tsurface.DrawTexturedRect(0, 0, width, height)\nend\n\nfunction SKIN:PaintSegmentedProgressBackground(panel, width, height)\nend\n\nfunction SKIN:PaintSegmentedProgress(panel, width, height)\n\tlocal font = panel:GetFont() or self.fontSegmentedProgress\n\tlocal textColor = panel:GetTextColor() or self.Colours.SegmentedProgress.Text\n\tlocal barColor = panel:GetBarColor() or self.Colours.SegmentedProgress.Bar\n\tlocal segments = panel:GetSegments()\n\tlocal segmentHalfWidth = width / #segments * 0.5\n\n\tsurface.SetDrawColor(barColor)\n\tsurface.DrawRect(0, 0, panel:GetFraction() * width, height)\n\n\tfor i = 1, #segments do\n\t\tlocal text = segments[i]\n\t\tlocal x = (i - 1) / #segments * width + segmentHalfWidth\n\t\tlocal y = height * 0.5\n\n\t\tdraw.SimpleText(text, font, x, y, textColor, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)\n\tend\nend\n\nfunction SKIN:PaintCharacterCreateBackground(panel, width, height)\n\tsurface.SetDrawColor(40, 40, 40, 255)\n\tsurface.SetTexture(gradient)\n\tsurface.DrawTexturedRect(0, 0, width, height)\nend\n\nfunction SKIN:PaintCharacterLoadBackground(panel, width, height)\n\tsurface.SetDrawColor(40, 40, 40, panel:GetBackgroundFraction() * 255)\n\tsurface.SetTexture(gradient)\n\tsurface.DrawTexturedRect(0, 0, width, height)\nend\n\nfunction SKIN:PaintCharacterTransitionOverlay(panel, x, y, width, height, color)\n\tcolor = color or ix.config.Get(\"color\")\n\n\tsurface.SetDrawColor(color)\n\tsurface.DrawRect(x, y, width, height)\nend\n\nfunction SKIN:PaintAreaEntry(panel, width, height)\n\tlocal color = ColorAlpha(panel:GetBackgroundColor() or self.Colours.Area.Background, panel:GetBackgroundAlpha())\n\n\tself:DrawImportantBackground(0, 0, width, height, color)\nend\n\nfunction SKIN:PaintListRow(panel, width, height)\n\tsurface.SetDrawColor(0, 0, 0, 150)\n\tsurface.DrawRect(0, 0, width, height)\nend\n\nfunction SKIN:PaintSettingsRowBackground(panel, width, height)\n\tlocal index = panel:GetBackgroundIndex()\n\tlocal bReset = panel:GetShowReset()\n\n\tif (index == 0) then\n\t\tsurface.SetDrawColor(30, 30, 30, 45)\n\t\tsurface.DrawRect(0, 0, width, height)\n\tend\n\n\tif (bReset) then\n\t\tsurface.SetDrawColor(self.Colours.Warning)\n\t\tsurface.DrawRect(0, 0, 2, height)\n\tend\nend\n\nfunction SKIN:PaintVScrollBar(panel, width, height)\nend\n\nfunction SKIN:PaintHScrollBar(panel, width, height)\nend\n\nfunction SKIN:PaintScrollBarGrip(panel, width, height)\n\tlocal parent = panel:GetParent()\n\n\tif (IsValid(parent.btnUp)) then\n\t\t-- Vertical scrollbar\n\t\tlocal upButtonHeight = parent.btnUp:GetTall()\n\t\tlocal downButtonHeight = parent.btnDown:GetTall()\n\n\t\tDisableClipping(true)\n\t\t\tsurface.SetDrawColor(30, 30, 30, 200)\n\t\t\tsurface.DrawRect(4, -upButtonHeight, width - 8, height + upButtonHeight + downButtonHeight)\n\t\tDisableClipping(false)\n\telse\n\t\t-- Horizontal scrollbar\n\t\tlocal leftButtonWidth = parent.btnLeft:GetWide()\n\t\tlocal rightButtonWidth = parent.btnRight:GetWide()\n\n\t\tDisableClipping(true)\n\t\t\tsurface.SetDrawColor(30, 30, 30, 200)\n\t\t\tsurface.DrawRect(-leftButtonWidth, 4, width + leftButtonWidth + rightButtonWidth, height - 8)\n\t\tDisableClipping(false)\n\tend\nend\n\nfunction SKIN:PaintButtonUp(panel, width, height)\nend\n\nfunction SKIN:PaintButtonDown(panel, width, height)\nend\n\nfunction SKIN:PaintButtonLeft(panel, width, height)\nend\n\nfunction SKIN:PaintButtonRight(panel, width, height)\nend\n\nfunction SKIN:PaintComboBox(panel, width, height)\nend\n\nfunction SKIN:PaintComboDownArrow(panel, width, height)\n\tsurface.SetFont(\"ixIconsSmall\")\n\n\tlocal textWidth, textHeight = surface.GetTextSize(\"r\")\n\tlocal alpha = (panel.ComboBox:IsMenuOpen() or panel.ComboBox.Hovered) and 200 or 100\n\n\tsurface.SetTextColor(ColorAlpha(ix.config.Get(\"color\"), alpha))\n\tsurface.SetTextPos(width * 0.5 - textWidth * 0.5, height * 0.5 - textHeight * 0.5)\n\tsurface.DrawText(\"r\")\nend\n\nfunction SKIN:PaintMenu(panel, width, height)\n\tix.util.DrawBlur(panel)\n\n\tsurface.SetDrawColor(30, 30, 30, 150)\n\tsurface.DrawRect(0, 0, width, height)\nend\n\nfunction SKIN:PaintMenuOption(panel, width, height)\n\tif (panel.m_bBackground and (panel.Hovered or panel.Highlight)) then\n\t\tself:DrawImportantBackground(0, 0, width, height, ix.config.Get(\"color\"))\n\tend\nend\n\nfunction SKIN:PaintHelixSlider(panel, width, height)\n\tsurface.SetDrawColor(self.Colours.DarkerBackground)\n\tsurface.DrawRect(0, 0, width, height)\n\n\tsurface.SetDrawColor(self.Colours.Success)\n\tsurface.DrawRect(0, 0, panel:GetVisualFraction() * width, height)\nend\n\nfunction SKIN:PaintChatboxTabButton(panel, width, height)\n\tif (panel:GetActive()) then\n\t\tsurface.SetDrawColor(ix.config.Get(\"color\", Color(75, 119, 190, 255)))\n\t\tsurface.DrawRect(0, 0, width, height)\n\telse\n\t\tsurface.SetDrawColor(0, 0, 0, 100)\n\t\tsurface.DrawRect(0, 0, width, height)\n\n\t\tif (panel:GetUnread()) then\n\t\t\tsurface.SetDrawColor(ColorAlpha(self.Colours.Warning, Lerp(panel.unreadAlpha, 0, 100)))\n\t\t\tsurface.SetTexture(gradient)\n\t\t\tsurface.DrawTexturedRect(0, 0, width, height - 1)\n\t\tend\n\tend\n\n\t-- border\n\tsurface.SetDrawColor(color_black)\n\tsurface.DrawRect(width - 1, 0, 1, height) -- right\nend\n\nfunction SKIN:PaintChatboxTabs(panel, width, height, alpha)\n\tsurface.SetDrawColor(0, 0, 0, 33)\n\tsurface.DrawRect(0, 0, width, height)\n\n\tsurface.SetDrawColor(0, 0, 0, 100)\n\tsurface.SetTexture(gradient)\n\tsurface.DrawTexturedRect(0, height * 0.5, width, height * 0.5)\n\n\tlocal tab = panel:GetActiveTab()\n\n\tif (tab) then\n\t\tlocal button = tab:GetButton()\n\t\tlocal x, _ = button:GetPos()\n\n\t\t-- outline\n\t\tsurface.SetDrawColor(0, 0, 0, 200)\n\t\tsurface.DrawRect(0, height - 1, x, 1) -- left\n\t\tsurface.DrawRect(x + button:GetWide(), height - 1, width - x - button:GetWide(), 1) -- right\n\tend\nend\n\nfunction SKIN:PaintChatboxBackground(panel, width, height)\n\tix.util.DrawBlur(panel, 10)\n\n\tif (panel:GetActive()) then\n\t\tsurface.SetDrawColor(ColorAlpha(ix.config.Get(\"color\"), 120))\n\t\tsurface.SetTexture(gradientUp)\n\t\tsurface.DrawTexturedRect(0, panel.tabs.buttons:GetTall(), width, height * 0.25)\n\tend\n\n\tsurface.SetDrawColor(color_black)\n\tsurface.DrawOutlinedRect(0, 0, width, height)\nend\n\nfunction SKIN:PaintChatboxEntry(panel, width, height)\n\tsurface.SetDrawColor(0, 0, 0, 66)\n\tsurface.DrawRect(0, 0, width, height)\n\n\tpanel:DrawTextEntryText(color_white, ix.config.Get(\"color\"), color_white)\n\n\tsurface.SetDrawColor(color_black)\n\tsurface.DrawOutlinedRect(0, 0, width, height)\nend\n\nfunction SKIN:DrawChatboxPreviewBox(x, y, text, color)\n\tcolor = color or ix.config.Get(\"color\")\n\n\tlocal textWidth, textHeight = surface.GetTextSize(text)\n\tlocal width, height = textWidth + 8, textHeight + 8\n\n\t-- background\n\tsurface.SetDrawColor(color)\n\tsurface.DrawRect(x, y, width, height)\n\n\t-- text\n\tsurface.SetTextColor(color_white)\n\tsurface.SetTextPos(x + width * 0.5 - textWidth * 0.5, y + height * 0.5 - textHeight * 0.5)\n\tsurface.DrawText(text)\n\n\t-- outline\n\tsurface.SetDrawColor(color.r * 0.5, color.g * 0.5, color.b * 0.5, 255)\n\tsurface.DrawOutlinedRect(x, y, width, height)\n\n\treturn width\nend\n\nfunction SKIN:DrawChatboxPrefixBox(panel, width, height)\n\tlocal color = panel:GetBackgroundColor()\n\n\t-- background\n\tsurface.SetDrawColor(color)\n\tsurface.DrawRect(0, 0, width, height)\n\n\t-- outline\n\tsurface.SetDrawColor(color.r * 0.5, color.g * 0.5, color.b * 0.5, 255)\n\tsurface.DrawOutlinedRect(0, 0, width, height)\nend\n\n\nfunction SKIN:PaintChatboxAutocompleteEntry(panel, width, height)\n\t-- selected background\n\tif (panel.highlightAlpha > 0) then\n\t\tself:DrawImportantBackground(0, 0, width, height, ColorAlpha(ix.config.Get(\"color\"), panel.highlightAlpha * 66))\n\tend\n\n\t-- lower border\n\tsurface.SetDrawColor(200, 200, 200, 33)\n\tsurface.DrawRect(0, height - 1, width, 1)\nend\n\nfunction SKIN:PaintWindowMinimizeButton(panel, width, height)\nend\n\nfunction SKIN:PaintWindowMaximizeButton(panel, width, height)\nend\n\nfunction SKIN:PaintInfoBar(panel, width, height, color)\n\t-- bar\n\tsurface.SetDrawColor(color.r, color.g, color.b, 250)\n\tsurface.DrawRect(0, 0, width, height)\n\n\t-- gradient overlay\n\tsurface.SetDrawColor(230, 230, 230, 8)\n\tsurface.SetTexture(gradientUp)\n\tsurface.DrawTexturedRect(0, 0, width, height)\nend\n\nfunction SKIN:PaintInfoBarBackground(panel, width, height)\n\tsurface.SetDrawColor(230, 230, 230, 15)\n\tsurface.DrawRect(0, 0, width, height)\n\tsurface.DrawOutlinedRect(0, 0, width, height)\n\n\tpanel.bar:PaintManual()\n\n\tDisableClipping(true)\n\t\tpanel.label:PaintManual()\n\tDisableClipping(false)\nend\n\nfunction SKIN:PaintInventorySlot(panel, width, height)\n\tsurface.SetDrawColor(35, 35, 35, 85)\n\tsurface.DrawRect(1, 1, width - 2, height - 2)\n\n\tsurface.SetDrawColor(0, 0, 0, 250)\n\tsurface.DrawOutlinedRect(1, 1, width - 2, height - 2)\nend\n\nfunction SKIN:PaintDeathScreenBackground(panel, width, height, progress)\n\tsurface.SetDrawColor(0, 0, 0, (progress / 0.3) * 255)\n\tsurface.DrawRect(0, 0, width, height)\nend\n\nfunction SKIN:PaintDeathScreen(panel, width, height, progress)\n\tix.bar.DrawAction()\nend\n\ndo\n\t-- check if sounds exist, otherwise fall back to default UI sounds\n\tlocal bWhoosh = file.Exists(\"sound/helix/ui/whoosh1.wav\", \"GAME\")\n\tlocal bRollover = file.Exists(\"sound/helix/ui/rollover.wav\", \"GAME\")\n\tlocal bPress = file.Exists(\"sound/helix/ui/press.wav\", \"GAME\")\n\tlocal bNotify = file.Exists(\"sound/helix/ui/REPLACEME.wav\", \"GAME\") -- @todo\n\n\tsound.Add({\n\t\tname = \"Helix.Whoosh\",\n\t\tchannel = CHAN_STATIC,\n\t\tvolume = 0.4,\n\t\tlevel = 80,\n\t\tpitch = bWhoosh and {90, 105} or 100,\n\t\tsound = bWhoosh and {\n\t\t\t\"helix/ui/whoosh1.wav\",\n\t\t\t\"helix/ui/whoosh2.wav\",\n\t\t\t\"helix/ui/whoosh3.wav\",\n\t\t\t\"helix/ui/whoosh4.wav\",\n\t\t\t\"helix/ui/whoosh5.wav\",\n\t\t\t\"helix/ui/whoosh6.wav\"\n\t\t} or \"\"\n\t})\n\n\tsound.Add({\n\t\tname = \"Helix.Rollover\",\n\t\tchannel = CHAN_STATIC,\n\t\tvolume = 0.5,\n\t\tlevel = 80,\n\t\tpitch = {95, 105},\n\t\tsound = bRollover and \"helix/ui/rollover.wav\" or \"ui/buttonrollover.wav\"\n\t})\n\n\tsound.Add({\n\t\tname = \"Helix.Press\",\n\t\tchannel = CHAN_STATIC,\n\t\tvolume = 0.5,\n\t\tlevel = 80,\n\t\tpitch = bPress and {95, 110} or 100,\n\t\tsound = bPress and \"helix/ui/press.wav\" or \"ui/buttonclickrelease.wav\"\n\t})\n\n\tsound.Add({\n\t\tname = \"Helix.Notify\",\n\t\tchannel = CHAN_STATIC,\n\t\tvolume = 0.35,\n\t\tlevel = 80,\n\t\tpitch = 140,\n\t\tsound = bNotify and \"helix/ui/REPLACEME.wav\" or \"weapons/grenade/tick1.wav\"\n\t})\nend\n\nderma.RefreshSkins()\n"
  },
  {
    "path": "gamemode/core/derma/cl_attribute.lua",
    "content": "\nlocal gradient = ix.util.GetMaterial(\"vgui/gradient-u\")\nlocal gradient2 = ix.util.GetMaterial(\"vgui/gradient-d\")\n\nlocal PANEL = {}\n\nAccessorFunc(PANEL, \"color\", \"Color\")\nAccessorFunc(PANEL, \"value\", \"Value\", FORCE_NUMBER)\nAccessorFunc(PANEL, \"boostValue\", \"Boost\", FORCE_NUMBER)\nAccessorFunc(PANEL, \"max\", \"Max\", FORCE_NUMBER)\n\nfunction PANEL:Init()\n\tself:SetTall(20)\n\n\tself.add = self:Add(\"DImageButton\")\n\tself.add:SetSize(16, 16)\n\tself.add:Dock(RIGHT)\n\tself.add:DockMargin(2, 2, 2, 2)\n\tself.add:SetImage(\"icon16/add.png\")\n\tself.add.OnMousePressed = function()\n\t\tself.pressing = 1\n\t\tself:DoChange()\n\t\tself.add:SetAlpha(150)\n\tend\n\tself.add.OnMouseReleased = function()\n\t\tif (self.pressing) then\n\t\t\tself.pressing = nil\n\t\t\tself.add:SetAlpha(255)\n\t\tend\n\tend\n\tself.add.OnCursorExited = self.add.OnMouseReleased\n\n\tself.sub = self:Add(\"DImageButton\")\n\tself.sub:SetSize(16, 16)\n\tself.sub:Dock(LEFT)\n\tself.sub:DockMargin(2, 2, 2, 2)\n\tself.sub:SetImage(\"icon16/delete.png\")\n\tself.sub.OnMousePressed = function()\n\t\tself.pressing = -1\n\t\tself:DoChange()\n\t\tself.sub:SetAlpha(150)\n\tend\n\tself.sub.OnMouseReleased = function()\n\t\tif (self.pressing) then\n\t\t\tself.pressing = nil\n\t\t\tself.sub:SetAlpha(255)\n\t\tend\n\tend\n\tself.sub.OnCursorExited = self.sub.OnMouseReleased\n\n\tself.value = 0\n\tself.deltaValue = self.value\n\tself.max = 10\n\tself.animateSpeed = 15\n\n\tself.bar = self:Add(\"DPanel\")\n\tself.bar:Dock(FILL)\n\tself.bar.Paint = function(this, w, h)\n\t\tsurface.SetDrawColor(35, 35, 35, 250)\n\t\tsurface.DrawRect(0, 0, w, h)\n\n\t\tw, h = w - 4, h - 4\n\n\t\tlocal value = self.deltaValue / self.max\n\n\t\tif (value > 0) then\n\t\t\tlocal color = self.color and self.color or ix.config.Get(\"color\")\n\t\t\tlocal boostedValue = self.boostValue or 0\n\t\t\tlocal add = 0\n\n\t\t\tif (self.deltaValue != self.value) then\n\t\t\t\tadd = 35\n\t\t\tend\n\n\t\t\t-- your stat\n\t\t\tdo\n\t\t\t\tif !(boostedValue < 0 and math.abs(boostedValue) > self.value) then\n\t\t\t\t\tsurface.SetDrawColor(color.r + add, color.g + add, color.b + add, 230)\n\t\t\t\t\tsurface.DrawRect(2, 2, w * value, h)\n\n\t\t\t\t\tsurface.SetDrawColor(255, 255, 255, 35)\n\t\t\t\t\tsurface.SetMaterial(gradient)\n\t\t\t\t\tsurface.DrawTexturedRect(2, 2, w * value, h)\n\t\t\t\tend\n\t\t\tend\n\n\t\t\t-- boosted stat\n\t\t\tdo\n\t\t\t\tlocal boostValue\n\n\t\t\t\tif (boostedValue != 0) then\n\t\t\t\t\tif (boostedValue < 0) then\n\t\t\t\t\t\tlocal please = math.min(self.value, math.abs(boostedValue))\n\t\t\t\t\t\tboostValue = ((please or 0) / self.max) * (self.deltaValue / self.value)\n\t\t\t\t\telse\n\t\t\t\t\t\tboostValue = ((boostedValue or 0) / self.max) * (self.deltaValue / self.value)\n\t\t\t\t\tend\n\n\t\t\t\t\tif (boostedValue < 0) then\n\t\t\t\t\t\tsurface.SetDrawColor(200, 40, 40, 230)\n\n\t\t\t\t\t\tlocal bWidth = math.abs(w * boostValue)\n\t\t\t\t\t\tsurface.DrawRect(2 + w * value - bWidth, 2, bWidth, h)\n\n\t\t\t\t\t\tsurface.SetDrawColor(255, 255, 255, 35)\n\t\t\t\t\t\tsurface.SetMaterial(gradient)\n\t\t\t\t\t\tsurface.DrawTexturedRect(2 + w * value - bWidth, 2, bWidth, h)\n\t\t\t\t\telse\n\t\t\t\t\t\tsurface.SetDrawColor(40, 200, 40, 230)\n\t\t\t\t\t\tsurface.DrawRect(2 + w * value, 2, w * boostValue, h)\n\n\t\t\t\t\t\tsurface.SetDrawColor(255, 255, 255, 35)\n\t\t\t\t\t\tsurface.SetMaterial(gradient)\n\t\t\t\t\t\tsurface.DrawTexturedRect(2 + w * value, 2, w * boostValue, h)\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\n\t\tsurface.SetDrawColor(255, 255, 255, 5)\n\t\tsurface.SetMaterial(gradient2)\n\t\tsurface.DrawTexturedRect(2, 2, w, h)\n\tend\n\n\tself.label = self.bar:Add(\"DLabel\")\n\tself.label:Dock(FILL)\n\tself.label:SetExpensiveShadow(1, Color(0, 0, 60))\n\tself.label:SetContentAlignment(5)\nend\n\nfunction PANEL:Think()\n\tif (self.pressing) then\n\t\tif ((self.nextPress or 0) < CurTime()) then\n\t\t\tself:DoChange()\n\t\tend\n\tend\n\n\tself.deltaValue = math.Approach(self.deltaValue, self.value, FrameTime() * self.animateSpeed)\nend\n\nfunction PANEL:DoChange()\n\tif ((self.value == 0 and self.pressing == -1) or (self.value == self.max and self.pressing == 1)) then\n\t\treturn\n\tend\n\n\tself.nextPress = CurTime() + 0.2\n\n\tif (self:OnChanged(self.pressing) != false) then\n\t\tself.value = math.Clamp(self.value + self.pressing, 0, self.max)\n\tend\nend\n\nfunction PANEL:OnChanged(difference)\nend\n\nfunction PANEL:SetText(text)\n\tself.label:SetText(text)\nend\n\nfunction PANEL:SetReadOnly()\n\tself.sub:Remove()\n\tself.add:Remove()\nend\n\nvgui.Register(\"ixAttributeBar\", PANEL, \"DPanel\")\n"
  },
  {
    "path": "gamemode/core/derma/cl_bar.lua",
    "content": "\n-- bar manager\n-- this manages positions for bar panels\nlocal PANEL = {}\n\nAccessorFunc(PANEL, \"padding\", \"Padding\", FORCE_NUMBER)\n\nfunction PANEL:Init()\n\tself:SetSize(ScrW() * 0.35, ScrH())\n\tself:SetPos(4, 4)\n\tself:ParentToHUD()\n\n\tself.bars = {}\n\tself.padding = 2\n\n\t-- add bars that were registered before manager creation\n\tfor _, v in ipairs(ix.bar.list) do\n\t\tv.panel = self:AddBar(v.index, v.color, v.priority)\n\tend\nend\n\nfunction PANEL:GetAll()\n\treturn self.bars\nend\n\nfunction PANEL:Clear()\n\tfor k, v in ipairs(self.bars) do\n\t\tv:Remove()\n\n\t\ttable.remove(self.bars, k)\n\tend\nend\n\nfunction PANEL:AddBar(index, color, priority)\n\tlocal panel = self:Add(\"ixInfoBar\")\n\tpanel:SetSize(self:GetWide(), BAR_HEIGHT)\n\tpanel:SetVisible(false)\n\tpanel:SetID(index)\n\tpanel:SetColor(color)\n\tpanel:SetPriority(priority)\n\n\tself.bars[#self.bars + 1] = panel\n\tself:Sort()\n\n\treturn panel\nend\n\nfunction PANEL:RemoveBar(panel)\n\tlocal toRemove\n\n\tfor k, v in ipairs(self.bars) do\n\t\tif (v == panel) then\n\t\t\ttoRemove = k\n\t\t\tbreak\n\t\tend\n\tend\n\n\tif (toRemove) then\n\t\ttable.remove(self.bars, toRemove)\n\n\t\t-- Decrease index value for the next bars\n\t\tfor i = toRemove, #self.bars do\n\t\t\tix.bar.list[i].index = i\n\t\t\tself.bars[i]:SetID(i)\n\t\tend\n\tend\n\n\tpanel:Remove()\n\tself:Sort()\nend\n\n-- sort bars by priority\nfunction PANEL:Sort()\n\ttable.sort(self.bars, function(a, b)\n\t\treturn a:GetPriority() < b:GetPriority()\n\tend)\nend\n\n-- update target Y positions\nfunction PANEL:Organize()\n\tlocal currentY = 0\n\n\tfor _, v in ipairs(self.bars) do\n\t\tif (!v:IsVisible()) then\n\t\t\tcontinue\n\t\tend\n\n\t\tv:SetPos(0, currentY)\n\n\t\tcurrentY = currentY + self.padding + v:GetTall()\n\tend\n\n\tself:SetSize(self:GetWide(), currentY)\nend\n\nfunction PANEL:Think()\n\tlocal menu = (IsValid(ix.gui.characterMenu) and !ix.gui.characterMenu:IsClosing()) and ix.gui.characterMenu\n\t\tor IsValid(ix.gui.menu) and ix.gui.menu\n\tlocal fraction = menu and 1 - menu.currentAlpha / 255 or 1\n\n\tself:SetAlpha(255 * fraction)\n\n\t-- don't update bars when not visible\n\tif (fraction == 0) then\n\t\treturn\n\tend\n\n\tlocal curTime = CurTime()\n\tlocal bShouldHide = hook.Run(\"ShouldHideBars\")\n\tlocal bAlwaysShow = ix.option.Get(\"alwaysShowBars\", false)\n\n\tfor _, v in ipairs(self.bars) do\n\t\tlocal info = ix.bar.list[v:GetID()]\n\t\tlocal realValue, barText = info.GetValue()\n\n\t\tif (bShouldHide or realValue == false) then\n\t\t\tv:SetVisible(false)\n\t\t\tcontinue\n\t\tend\n\n\t\tif (v:GetDelta() != realValue) then\n\t\t\tv:SetLifetime(curTime + 5)\n\t\tend\n\n\t\tif (v:GetLifetime() < curTime and !info.visible and !bAlwaysShow and !hook.Run(\"ShouldBarDraw\", info)) then\n\t\t\tv:SetVisible(false)\n\t\t\tcontinue\n\t\tend\n\n\t\tv:SetVisible(true)\n\t\tv:SetValue(realValue)\n\t\tv:SetText(isstring(barText) and barText or \"\")\n\tend\n\n\tself:Organize()\nend\n\nfunction PANEL:OnRemove()\n\tself:Clear()\nend\n\nvgui.Register(\"ixInfoBarManager\", PANEL, \"Panel\")\n\nPANEL = {}\n\nAccessorFunc(PANEL, \"index\", \"ID\", FORCE_NUMBER)\nAccessorFunc(PANEL, \"color\", \"Color\")\nAccessorFunc(PANEL, \"priority\", \"Priority\", FORCE_NUMBER)\nAccessorFunc(PANEL, \"value\", \"Value\", FORCE_NUMBER)\nAccessorFunc(PANEL, \"delta\", \"Delta\", FORCE_NUMBER)\nAccessorFunc(PANEL, \"lifetime\", \"Lifetime\", FORCE_NUMBER)\n\nfunction PANEL:Init()\n\tself.value = 0\n\tself.delta = 0\n\tself.lifetime = 0\n\n\tself.bar = self:Add(\"DPanel\")\n\tself.bar:SetPaintedManually(true)\n\tself.bar:Dock(FILL)\n\tself.bar:DockMargin(2, 2, 2, 2)\n\tself.bar.Paint = function(this, width, height)\n\t\twidth = width * math.min(self.delta, 1)\n\n\t\tderma.SkinFunc(\"PaintInfoBar\", self, width, height, self.color)\n\tend\n\n\tself.label = self:Add(\"DLabel\")\n\tself.label:SetFont(\"ixSmallFont\")\n\tself.label:SetContentAlignment(5)\n\tself.label:SetText(\"\")\n\tself.label:SetTextColor(Color(240, 240, 240))\n\tself.label:SetExpensiveShadow(2, Color(20, 20, 20))\n\tself.label:SetPaintedManually(true)\n\tself.label:SizeToContents()\n\tself.label:Dock(FILL)\nend\n\nfunction PANEL:SetText(text)\n\tself.label:SetText(text)\n\tself.label:SizeToContents()\nend\n\nfunction PANEL:Think()\n\tself.delta = math.Approach(self.delta, self.value, FrameTime())\nend\n\nfunction PANEL:Paint(width, height)\n\tderma.SkinFunc(\"PaintInfoBarBackground\", self, width, height)\nend\n\nvgui.Register(\"ixInfoBar\", PANEL, \"Panel\")\n\nif (IsValid(ix.gui.bars)) then\n\tix.gui.bars:Remove()\n\tix.gui.bars = vgui.Create(\"ixInfoBarManager\")\nend\n"
  },
  {
    "path": "gamemode/core/derma/cl_business.lua",
    "content": "\nlocal PANEL = {}\n\nfunction PANEL:Init()\n\t-- being relative.\n\tlocal size = 120\n\tself:SetSize(size, size * 1.4)\nend\n\nfunction PANEL:SetItem(itemTable)\n\tself.itemName = L(itemTable.name):lower()\n\n\tself.price = self:Add(\"DLabel\")\n\tself.price:Dock(BOTTOM)\n\tself.price:SetText(itemTable.price and ix.currency.Get(itemTable.price) or L\"free\":utf8upper())\n\tself.price:SetContentAlignment(5)\n\tself.price:SetTextColor(color_white)\n\tself.price:SetFont(\"ixSmallFont\")\n\tself.price:SetExpensiveShadow(1, Color(0, 0, 0, 200))\n\n\tself.name = self:Add(\"DLabel\")\n\tself.name:Dock(TOP)\n\tself.name:SetText(itemTable.GetName and itemTable:GetName() or L(itemTable.name))\n\tself.name:SetContentAlignment(5)\n\tself.name:SetTextColor(color_white)\n\tself.name:SetFont(\"ixSmallFont\")\n\tself.name:SetExpensiveShadow(1, Color(0, 0, 0, 200))\n\tself.name.Paint = function(this, w, h)\n\t\tsurface.SetDrawColor(0, 0, 0, 75)\n\t\tsurface.DrawRect(0, 0, w, h)\n\tend\n\n\tself.icon = self:Add(\"SpawnIcon\")\n\tself.icon:SetZPos(1)\n\tself.icon:SetSize(self:GetWide(), self:GetWide())\n\tself.icon:Dock(FILL)\n\tself.icon:DockMargin(5, 5, 5, 10)\n\tself.icon:InvalidateLayout(true)\n\tself.icon:SetModel(itemTable:GetModel(), itemTable:GetSkin())\n\tself.icon:SetHelixTooltip(function(tooltip)\n\t\tix.hud.PopulateItemTooltip(tooltip, itemTable)\n\tend)\n\tself.icon.itemTable = itemTable\n\tself.icon.DoClick = function(this)\n\t\tif (IsValid(ix.gui.checkout)) then\n\t\t\treturn\n\t\tend\n\n\t\tlocal parent = ix.gui.business\n\t\tlocal bAdded = parent:BuyItem(itemTable.uniqueID)\n\n\t\tif (bAdded) then\n\t\t\tsurface.PlaySound(\"buttons/button14.wav\")\n\t\tend\n\tend\n\tself.icon.PaintOver = function(this)\n\t\tif (itemTable and itemTable.PaintOver) then\n\t\t\tlocal w, h = this:GetSize()\n\n\t\t\titemTable.PaintOver(this, itemTable, w, h)\n\t\tend\n\tend\n\n\tif ((itemTable.iconCam and !ICON_RENDER_QUEUE[itemTable.uniqueID]) or itemTable.forceRender) then\n\t\tlocal iconCam = itemTable.iconCam\n\t\ticonCam = {\n\t\t\tcam_pos = iconCam.pos,\n\t\t\tcam_fov = iconCam.fov,\n\t\t\tcam_ang = iconCam.ang,\n\t\t}\n\t\tICON_RENDER_QUEUE[itemTable.uniqueID] = true\n\n\t\tself.icon:RebuildSpawnIconEx(\n\t\t\ticonCam\n\t\t)\n\tend\nend\n\nvgui.Register(\"ixBusinessItem\", PANEL, \"DPanel\")\n\nPANEL = {}\n\nfunction PANEL:Init()\n\tix.gui.business = self\n\n\tself:SetSize(self:GetParent():GetSize())\n\n\tself.categories = self:Add(\"DScrollPanel\")\n\tself.categories:Dock(LEFT)\n\tself.categories:SetWide(260)\n\tself.categories.Paint = function(this, w, h)\n\t\tsurface.SetDrawColor(0, 0, 0, 150)\n\t\tsurface.DrawRect(0, 0, w, h)\n\tend\n\tself.categories:DockPadding(5, 5, 5, 5)\n\tself.categories:DockMargin(0, 46, 0, 0)\n\tself.categoryPanels = {}\n\n\tself.scroll = self:Add(\"DScrollPanel\")\n\tself.scroll:Dock(FILL)\n\n\tself.search = self:Add(\"DTextEntry\")\n\tself.search:Dock(TOP)\n\tself.search:SetTall(36)\n\tself.search:SetFont(\"ixMediumFont\")\n\tself.search:DockMargin(10 + self:GetWide() * 0.35, 0, 5, 5)\n\tself.search.OnTextChanged = function(this)\n\t\tlocal text = self.search:GetText():lower()\n\n\t\tif (self.selected) then\n\t\t\tself:LoadItems(self.selected.category, text:find(\"%S\") and text or nil)\n\t\t\tself.scroll:InvalidateLayout()\n\t\tend\n\tend\n\tself.search.PaintOver = function(this, cw, ch)\n\t\tif (self.search:GetValue() == \"\" and !self.search:HasFocus()) then\n\t\t\tix.util.DrawText(\"V\", 10, ch/2 - 1, color_black, 3, 1, \"ixIconsSmall\")\n\t\tend\n\tend\n\n\tself.itemList = self.scroll:Add(\"DIconLayout\")\n\tself.itemList:Dock(TOP)\n\tself.itemList:DockMargin(10, 1, 5, 5)\n\tself.itemList:SetSpaceX(10)\n\tself.itemList:SetSpaceY(10)\n\tself.itemList:SetMinimumSize(128, 400)\n\n\tself.checkout = self:Add(\"DButton\")\n\tself.checkout:Dock(BOTTOM)\n\tself.checkout:SetTextColor(color_white)\n\tself.checkout:SetTall(36)\n\tself.checkout:SetFont(\"ixMediumFont\")\n\tself.checkout:DockMargin(10, 10, 0, 0)\n\tself.checkout:SetExpensiveShadow(1, Color(0, 0, 0, 150))\n\tself.checkout:SetText(L(\"checkout\", 0))\n\tself.checkout.DoClick = function()\n\t\tif (!IsValid(ix.gui.checkout) and self:GetCartCount() > 0) then\n\t\t\tvgui.Create(\"ixBusinessCheckout\"):SetCart(self.cart)\n\t\tend\n\tend\n\n\tself.cart = {}\n\n\tlocal dark = Color(0, 0, 0, 50)\n\tlocal first = true\n\n\tfor k, v in pairs(ix.item.list) do\n\t\tif (hook.Run(\"CanPlayerUseBusiness\", LocalPlayer(), k) == false) then\n\t\t\tcontinue\n\t\tend\n\n\t\tif (!self.categoryPanels[L(v.category)]) then\n\t\t\tself.categoryPanels[L(v.category)] = v.category\n\t\tend\n\tend\n\n\tfor category, realName in SortedPairs(self.categoryPanels) do\n\t\tlocal button = self.categories:Add(\"DButton\")\n\t\tbutton:SetTall(36)\n\t\tbutton:SetText(category)\n\t\tbutton:Dock(TOP)\n\t\tbutton:SetTextColor(color_white)\n\t\tbutton:DockMargin(5, 5, 5, 0)\n\t\tbutton:SetFont(\"ixMediumFont\")\n\t\tbutton:SetExpensiveShadow(1, Color(0, 0, 0, 150))\n\t\tbutton.Paint = function(this, w, h)\n\t\t\tsurface.SetDrawColor(self.selected == this and ix.config.Get(\"color\") or dark)\n\t\t\tsurface.DrawRect(0, 0, w, h)\n\n\t\t\tsurface.SetDrawColor(0, 0, 0, 50)\n\t\t\tsurface.DrawOutlinedRect(0, 0, w, h)\n\t\tend\n\t\tbutton.DoClick = function(this)\n\t\t\tif (self.selected != this) then\n\t\t\t\tself.selected = this\n\t\t\t\tself:LoadItems(realName)\n\t\t\t\ttimer.Simple(0.01, function()\n\t\t\t\t\tself.scroll:InvalidateLayout()\n\t\t\t\tend)\n\t\t\tend\n\t\tend\n\t\tbutton.category = realName\n\n\t\tif (first) then\n\t\t\tself.selected = button\n\t\t\tfirst = false\n\t\tend\n\n\t\tself.categoryPanels[realName] = button\n\tend\n\n\tif (self.selected) then\n\t\tself:LoadItems(self.selected.category)\n\tend\nend\n\nfunction PANEL:GetCartCount()\n\tlocal count = 0\n\n\tfor _, v in pairs(self.cart) do\n\t\tcount = count + v\n\tend\n\n\treturn count\nend\n\nfunction PANEL:BuyItem(uniqueID)\n\tlocal currentCount = self.cart[uniqueID] or 0\n\n\tif (currentCount >= 10) then\n\t\treturn false\n\tend\n\n\tself.cart[uniqueID] = currentCount + 1\n\tself.checkout:SetText(L(\"checkout\", self:GetCartCount()))\n\n\treturn true\nend\n\nfunction PANEL:LoadItems(category, search)\n\tcategory = category\tor \"misc\"\n\tlocal items = ix.item.list\n\n\tself.itemList:Clear()\n\tself.itemList:InvalidateLayout(true)\n\n\tfor uniqueID, itemTable in SortedPairsByMemberValue(items, \"name\") do\n\t\tif (hook.Run(\"CanPlayerUseBusiness\", LocalPlayer(), uniqueID) == false) then\n\t\t\tcontinue\n\t\tend\n\n\t\tif (itemTable.category == category) then\n\t\t\tif (search and search != \"\" and !L(itemTable.name):lower():find(search, 1, true)) then\n\t\t\t\tcontinue\n\t\t\tend\n\n\t\t\tself.itemList:Add(\"ixBusinessItem\"):SetItem(itemTable)\n\t\tend\n\tend\nend\n\nfunction PANEL:SetPage()\nend\n\nfunction PANEL:GetPageItems()\nend\n\nvgui.Register(\"ixBusiness\", PANEL, \"EditablePanel\")\n\nDEFINE_BASECLASS(\"DFrame\")\nPANEL = {}\n\nfunction PANEL:Init()\n\tif (IsValid(ix.gui.checkout)) then\n\t\tix.gui.checkout:Remove()\n\tend\n\n\tix.gui.checkout = self\n\n\tself:SetTitle(L(\"checkout\", 0))\n\tself:SetSize(ScrW() / 4 > 200 and ScrW() / 4 or ScrW() / 2, ScrH() / 2 > 300 and ScrH() / 2 or ScrH())\n\tself:MakePopup()\n\tself:Center()\n\tself:SetBackgroundBlur(true)\n\tself:SetSizable(true)\n\n\tself.items = self:Add(\"DScrollPanel\")\n\tself.items:Dock(FILL)\n\tself.items.Paint = function(this, w, h)\n\t\tsurface.SetDrawColor(0, 0, 0, 100)\n\t\tsurface.DrawRect(0, 0, w, h)\n\tend\n\tself.items:DockMargin(0, 0, 0, 4)\n\n\tself.buy = self:Add(\"DButton\")\n\tself.buy:Dock(BOTTOM)\n\tself.buy:SetText(L\"purchase\")\n\tself.buy:SetTextColor(color_white)\n\tself.buy.DoClick = function(this)\n\t\tif ((this.nextClick or 0) < CurTime()) then\n\t\t\tthis.nextClick = CurTime() + 0.5\n\t\telse\n\t\t\treturn\n\t\tend\n\n\t\tif (self.preventBuy) then\n\t\t\tself.finalGlow:SetText(self.final:GetText())\n\t\t\tself.finalGlow:SetAlpha(255)\n\t\t\tself.finalGlow:AlphaTo(0, 0.5)\n\n\t\t\treturn surface.PlaySound(\"buttons/button11.wav\")\n\t\tend\n\n\t\tnet.Start(\"ixBusinessBuy\")\n\t\tnet.WriteUInt(table.Count(self.itemData), 8)\n\n\t\tfor k, v in pairs(self.itemData) do\n\t\t\tnet.WriteString(k)\n\t\t\tnet.WriteUInt(v, 8)\n\t\tend\n\n\t\tnet.SendToServer()\n\n\t\tself.itemData = {}\n\t\tself.items:Remove()\n\t\tself.data:Remove()\n\t\tself.buy:Remove()\n\t\tself:ShowCloseButton(false)\n\n\t\tif (IsValid(ix.gui.business)) then\n\t\t\tix.gui.business.cart = {}\n\t\t\tix.gui.business.checkout:SetText(L(\"checkout\", 0))\n\t\tend\n\n\t\tself.text = self:Add(\"DLabel\")\n\t\tself.text:Dock(FILL)\n\t\tself.text:SetContentAlignment(5)\n\t\tself.text:SetTextColor(color_white)\n\t\tself.text:SetText(L\"purchasing\")\n\t\tself.text:SetFont(\"ixMediumFont\")\n\n\t\tnet.Receive(\"ixBusinessResponse\", function()\n\t\t\tif (IsValid(self)) then\n\t\t\t\tself.text:SetText(L\"buyGood\")\n\t\t\t\tself.done = true\n\t\t\t\tself:ShowCloseButton(true)\n\n\t\t\t\tsurface.PlaySound(\"buttons/button3.wav\")\n\t\t\tend\n\t\tend)\n\n\t\ttimer.Simple(4, function()\n\t\t\tif (IsValid(self) and !self.done) then\n\t\t\t\tself.text:SetText(L\"buyFailed\")\n\t\t\t\tself:ShowCloseButton(true)\n\n\t\t\t\tsurface.PlaySound(\"buttons/button11.wav\")\n\t\t\tend\n\t\tend)\n\tend\n\n\tself.data = self:Add(\"DPanel\")\n\tself.data:Dock(BOTTOM)\n\tself.data:SetTall(64)\n\tself.data:DockMargin(0, 0, 0, 4)\n\n\tself.current = self.data:Add(\"DLabel\")\n\tself.current:SetFont(\"ixSmallFont\")\n\tself.current:SetContentAlignment(6)\n\tself.current:SetTextColor(color_white)\n\tself.current:Dock(TOP)\n\tself.current:SetTextInset(4, 0)\n\n\tself.total = self.data:Add(\"DLabel\")\n\tself.total:SetFont(\"ixSmallFont\")\n\tself.total:SetContentAlignment(6)\n\tself.total:SetTextColor(color_white)\n\tself.total:Dock(TOP)\n\tself.total:SetTextInset(4, 0)\n\n\tlocal line = self.data:Add(\"DPanel\")\n\tline:SetTall(1)\n\tline:DockMargin(128, 0, 4, 0)\n\tline:Dock(TOP)\n\tline.Paint = function(this, w, h)\n\t\tsurface.SetDrawColor(255, 255, 255, 150)\n\t\tsurface.DrawLine(0, 0, w, 0)\n\tend\n\n\tself.final = self.data:Add(\"DLabel\")\n\tself.final:SetFont(\"ixSmallFont\")\n\tself.final:SetContentAlignment(6)\n\tself.final:SetTextColor(color_white)\n\tself.final:Dock(TOP)\n\tself.final:SetTextInset(4, 0)\n\n\tself.finalGlow = self.final:Add(\"DLabel\")\n\tself.finalGlow:Dock(FILL)\n\tself.finalGlow:SetFont(\"ixSmallFont\")\n\tself.finalGlow:SetTextColor(color_white)\n\tself.finalGlow:SetContentAlignment(6)\n\tself.finalGlow:SetAlpha(0)\n\tself.finalGlow:SetTextInset(4, 0)\n\n\tself:SetFocusTopLevel(true)\n\tself.itemData = {}\n\tself:OnQuantityChanged()\nend\n\nfunction PANEL:OnQuantityChanged()\n\tlocal price = 0\n\tlocal money = LocalPlayer():GetCharacter():GetMoney()\n\tlocal valid = 0\n\n\tfor k, v in pairs(self.itemData) do\n\t\tlocal itemTable = ix.item.list[k]\n\n\t\tif (itemTable and v > 0) then\n\t\t\tvalid = valid + 1\n\t\t\tprice = price + (v * (itemTable.price or 0))\n\t\tend\n\tend\n\n\tself.current:SetText(L\"currentMoney\" .. ix.currency.Get(money))\n\tself.total:SetText(\"- \" .. ix.currency.Get(price))\n\tself.final:SetText(L\"moneyLeft\" .. ix.currency.Get(money - price))\n\tself.final:SetTextColor((money - price) >= 0 and Color(46, 204, 113) or Color(217, 30, 24))\n\n\tself.preventBuy = (money - price) < 0 or valid == 0\n\n\tif (IsValid(ix.gui.business)) then\n\t\tix.gui.business.checkout:SetText(L(\"checkout\", ix.gui.business:GetCartCount()))\n\tend\nend\n\nfunction PANEL:SetCart(items)\n\tself.itemData = items\n\n\tfor k, v in SortedPairs(items) do\n\t\tlocal itemTable = ix.item.list[k]\n\n\t\tif (itemTable) then\n\t\t\tlocal slot = self.items:Add(\"DPanel\")\n\t\t\tslot:SetTall(36)\n\t\t\tslot:Dock(TOP)\n\t\t\tslot:DockMargin(5, 5, 5, 0)\n\n\t\t\tslot.icon = slot:Add(\"SpawnIcon\")\n\t\t\tslot.icon:SetPos(2, 2)\n\t\t\tslot.icon:SetSize(32, 32)\n\t\t\tslot.icon:SetModel(itemTable:GetModel())\n\t\t\tslot.icon:SetTooltip()\n\n\t\t\tslot.name = slot:Add(\"DLabel\")\n\t\t\tslot.name:SetPos(40, 2)\n\t\t\tslot.name:SetFont(\"ixChatFont\")\n\t\t\tslot.name:SetText(string.format(\n\t\t\t\t\"%s (%s)\",\n\t\t\t\tL(itemTable.GetName and itemTable:GetName() or L(itemTable.name)),\n\t\t\t\titemTable.price and ix.currency.Get(itemTable.price) or L\"free\":utf8upper()\n\t\t\t))\n\t\t\tslot.name:SetTextColor(color_white)\n\t\t\tslot.name:SizeToContents()\n\t\t\tslot.name:DockMargin(40, 0, 0, 0)\n\t\t\tslot.name:Dock(FILL)\n\n\t\t\tslot.quantity = slot:Add(\"DTextEntry\")\n\t\t\tslot.quantity:SetSize(32, 32)\n\t\t\tslot.quantity:Dock(RIGHT)\n\t\t\tslot.quantity:DockMargin(4, 4, 4, 4)\n\t\t\tslot.quantity:SetContentAlignment(5)\n\t\t\tslot.quantity:SetNumeric(true)\n\t\t\tslot.quantity:SetText(v)\n\t\t\tslot.quantity:SetFont(\"ixChatFont\")\n\t\t\tslot.quantity.OnTextChanged = function(this)\n\t\t\t\tlocal value = tonumber(this:GetValue())\n\n\t\t\t\tif (!value) then\n\t\t\t\t\tthis:SetValue(1)\n\t\t\t\t\treturn\n\t\t\t\tend\n\n\t\t\t\tvalue = math.Clamp(math.Round(value), 0, 10)\n\n\t\t\t\tif (value == 0) then\n\t\t\t\t\titems[k] = nil\n\n\t\t\t\t\tslot:Remove()\n\t\t\t\telse\n\t\t\t\t\titems[k] = value\n\t\t\t\tend\n\n\t\t\t\tself:OnQuantityChanged()\n\t\t\tend\n\t\t\tslot.quantity.OnLoseFocus = function(this)\n\t\t\t\tlocal value = math.Clamp(tonumber(this:GetValue()) or 1, 0, 10)\n\t\t\t\tthis:SetText(value)\n\t\t\tend\n\t\telse\n\t\t\titems[k] = nil\n\t\tend\n\tend\n\n\tself:OnQuantityChanged()\nend\n\nfunction PANEL:Think()\n\tif (!self:HasFocus()) then\n\t\tself:MakePopup()\n\tend\n\n\tBaseClass.Think(self)\nend\n\nvgui.Register(\"ixBusinessCheckout\", PANEL, \"DFrame\")\n\nhook.Add(\"CreateMenuButtons\", \"ixBusiness\", function(tabs)\n\tif (hook.Run(\"BuildBusinessMenu\") != false) then\n\t\ttabs[\"business\"] = function(container)\n\t\t\tcontainer:Add(\"ixBusiness\")\n\t\tend\n\tend\nend)\n"
  },
  {
    "path": "gamemode/core/derma/cl_character.lua",
    "content": "\r\nlocal gradient = surface.GetTextureID(\"vgui/gradient-d\")\r\nlocal audioFadeInTime = 2\r\nlocal animationTime = 0.5\r\nlocal matrixZScale = Vector(1, 1, 0.0001)\r\n\r\n-- character menu panel\r\nDEFINE_BASECLASS(\"ixSubpanelParent\")\r\nlocal PANEL = {}\r\n\r\nfunction PANEL:Init()\r\n\tself:SetSize(self:GetParent():GetSize())\r\n\tself:SetPos(0, 0)\r\n\r\n\tself.childPanels = {}\r\n\tself.subpanels = {}\r\n\tself.activeSubpanel = \"\"\r\n\r\n\tself.currentDimAmount = 0\r\n\tself.currentY = 0\r\n\tself.currentScale = 1\r\n\tself.currentAlpha = 255\r\n\tself.targetDimAmount = 255\r\n\tself.targetScale = 0.9\r\nend\r\n\r\nfunction PANEL:Dim(length, callback)\r\n\tlength = length or animationTime\r\n\tself.currentDimAmount = 0\r\n\r\n\tself:CreateAnimation(length, {\r\n\t\ttarget = {\r\n\t\t\tcurrentDimAmount = self.targetDimAmount,\r\n\t\t\tcurrentScale = self.targetScale\r\n\t\t},\r\n\t\teasing = \"outCubic\",\r\n\t\tOnComplete = callback\r\n\t})\r\n\r\n\tself:OnDim()\r\nend\r\n\r\nfunction PANEL:Undim(length, callback)\r\n\tlength = length or animationTime\r\n\tself.currentDimAmount = self.targetDimAmount\r\n\r\n\tself:CreateAnimation(length, {\r\n\t\ttarget = {\r\n\t\t\tcurrentDimAmount = 0,\r\n\t\t\tcurrentScale = 1\r\n\t\t},\r\n\t\teasing = \"outCubic\",\r\n\t\tOnComplete = callback\r\n\t})\r\n\r\n\tself:OnUndim()\r\nend\r\n\r\nfunction PANEL:OnDim()\r\nend\r\n\r\nfunction PANEL:OnUndim()\r\nend\r\n\r\nfunction PANEL:Paint(width, height)\r\n\tlocal amount = self.currentDimAmount\r\n\tlocal bShouldScale = self.currentScale != 1\r\n\tlocal matrix\r\n\r\n\t-- draw child panels with scaling if needed\r\n\tif (bShouldScale) then\r\n\t\tmatrix = Matrix()\r\n\t\tmatrix:Scale(matrixZScale * self.currentScale)\r\n\t\tmatrix:Translate(Vector(\r\n\t\t\tScrW() * 0.5 - (ScrW() * self.currentScale * 0.5),\r\n\t\t\tScrH() * 0.5 - (ScrH() * self.currentScale * 0.5),\r\n\t\t\t1\r\n\t\t))\r\n\r\n\t\tcam.PushModelMatrix(matrix)\r\n\t\tself.currentMatrix = matrix\r\n\tend\r\n\r\n\tBaseClass.Paint(self, width, height)\r\n\r\n\tif (bShouldScale) then\r\n\t\tcam.PopModelMatrix()\r\n\t\tself.currentMatrix = nil\r\n\tend\r\n\r\n\tif (amount > 0) then\r\n\t\tlocal color = Color(0, 0, 0, amount)\r\n\r\n\t\tsurface.SetDrawColor(color)\r\n\t\tsurface.DrawRect(0, 0, width, height)\r\n\tend\r\nend\r\n\r\nvgui.Register(\"ixCharMenuPanel\", PANEL, \"ixSubpanelParent\")\r\n\r\n-- character menu main button list\r\nPANEL = {}\r\n\r\nfunction PANEL:Init()\r\n\tlocal parent = self:GetParent()\r\n\tself:SetSize(parent:GetWide() * 0.25, parent:GetTall())\r\n\r\n\tself:GetVBar():SetWide(0)\r\n\tself:GetVBar():SetVisible(false)\r\nend\r\n\r\nfunction PANEL:Add(name)\r\n\tlocal panel = vgui.Create(name, self)\r\n\tpanel:Dock(TOP)\r\n\r\n\treturn panel\r\nend\r\n\r\nfunction PANEL:SizeToContents()\r\n\tself:GetCanvas():InvalidateLayout(true)\r\n\r\n\t-- if the canvas has extra space, forcefully dock to the bottom so it doesn't anchor to the top\r\n\tif (self:GetTall() > self:GetCanvas():GetTall()) then\r\n\t\tself:GetCanvas():Dock(BOTTOM)\r\n\telse\r\n\t\tself:GetCanvas():Dock(NODOCK)\r\n\tend\r\nend\r\n\r\nvgui.Register(\"ixCharMenuButtonList\", PANEL, \"DScrollPanel\")\r\n\r\n-- main character menu panel\r\nPANEL = {}\r\n\r\nAccessorFunc(PANEL, \"bUsingCharacter\", \"UsingCharacter\", FORCE_BOOL)\r\n\r\nfunction PANEL:Init()\r\n\tlocal parent = self:GetParent()\r\n\tlocal padding = self:GetPadding()\r\n\tlocal halfWidth = ScrW() * 0.5\r\n\tlocal halfPadding = padding * 0.5\r\n\tlocal bHasCharacter = #ix.characters > 0\r\n\r\n\tself.bUsingCharacter = LocalPlayer().GetCharacter and LocalPlayer():GetCharacter()\r\n\tself:DockPadding(padding, padding, padding, padding)\r\n\r\n\tlocal infoLabel = self:Add(\"DLabel\")\r\n\tinfoLabel:SetTextColor(Color(255, 255, 255, 25))\r\n\tinfoLabel:SetFont(\"ixMenuMiniFont\")\r\n\tinfoLabel:SetText(L(\"helix\") .. \" \" .. GAMEMODE.Version)\r\n\tinfoLabel:SizeToContents()\r\n\tinfoLabel:SetPos(ScrW() - infoLabel:GetWide() - 4, ScrH() - infoLabel:GetTall() - 4)\r\n\r\n\tlocal logoPanel = self:Add(\"Panel\")\r\n\tlogoPanel:SetSize(ScrW(), ScrH() * 0.25)\r\n\tlogoPanel:SetPos(0, ScrH() * 0.25)\r\n\tlogoPanel.Paint = function(panel, width, height)\r\n\t\tlocal matrix = self.currentMatrix\r\n\r\n\t\t-- don't scale the background because it fucks the blur\r\n\t\tif (matrix) then\r\n\t\t\tcam.PopModelMatrix()\r\n\t\tend\r\n\r\n\t\tlocal newHeight = Lerp(1 - (self.currentDimAmount / 255), 0, height)\r\n\t\tlocal y = height * 0.5 - newHeight * 0.5\r\n\t\tlocal _, screenY = panel:LocalToScreen(0, 0)\r\n\t\tscreenY = screenY + y\r\n\r\n\t\trender.SetScissorRect(0, screenY, width, screenY + newHeight, true)\r\n\t\tix.util.DrawBlur(panel, 15, nil, 200)\r\n\r\n\t\t-- background dim\r\n\t\tsurface.SetDrawColor(0, 0, 0, 100)\r\n\t\tsurface.DrawRect(0, y, width, newHeight)\r\n\r\n\t\t-- border lines\r\n\t\tsurface.SetDrawColor(ix.config.Get(\"color\") or color_white)\r\n\t\tsurface.DrawRect(0, y, width, 1)\r\n\t\tsurface.DrawRect(0, y + newHeight - 1, width, 1)\r\n\r\n\t\tif (matrix) then\r\n\t\t\tcam.PushModelMatrix(matrix)\r\n\t\tend\r\n\r\n\t\tfor _, v in ipairs(panel:GetChildren()) do\r\n\t\t\tv:PaintManual()\r\n\t\tend\r\n\r\n\t\trender.SetScissorRect(0, 0, 0, 0, false)\r\n\tend\r\n\r\n\t-- draw schema logo material instead of text if available\r\n\tlocal logo = Schema.logo and ix.util.GetMaterial(Schema.logo)\r\n\r\n\tif (logo and !logo:IsError()) then\r\n\t\tlocal logoImage = logoPanel:Add(\"DImage\")\r\n\t\tlogoImage:SetMaterial(logo)\r\n\t\tlogoImage:SetSize(halfWidth, halfWidth * logo:Height() / logo:Width())\r\n\t\tlogoImage:SetPos(halfWidth - logoImage:GetWide() * 0.5, halfPadding)\r\n\t\tlogoImage:SetPaintedManually(true)\r\n\r\n\t\tlogoPanel:SetTall(logoImage:GetTall() + padding)\r\n\telse\r\n\t\tlocal newHeight = padding\r\n\t\tlocal subtitle = L2(\"schemaDesc\") or Schema.description\r\n\r\n\t\tlocal titleLabel = logoPanel:Add(\"DLabel\")\r\n\t\ttitleLabel:SetTextColor(color_white)\r\n\t\ttitleLabel:SetFont(\"ixTitleFont\")\r\n\t\ttitleLabel:SetText(L2(\"schemaName\") or Schema.name or L\"unknown\")\r\n\t\ttitleLabel:SizeToContents()\r\n\t\ttitleLabel:SetPos(halfWidth - titleLabel:GetWide() * 0.5, halfPadding)\r\n\t\ttitleLabel:SetPaintedManually(true)\r\n\t\tnewHeight = newHeight + titleLabel:GetTall()\r\n\r\n\t\tif (subtitle) then\r\n\t\t\tlocal subtitleLabel = logoPanel:Add(\"DLabel\")\r\n\t\t\tsubtitleLabel:SetTextColor(color_white)\r\n\t\t\tsubtitleLabel:SetFont(\"ixSubTitleFont\")\r\n\t\t\tsubtitleLabel:SetText(subtitle)\r\n\t\t\tsubtitleLabel:SizeToContents()\r\n\t\t\tsubtitleLabel:SetPos(halfWidth - subtitleLabel:GetWide() * 0.5, 0)\r\n\t\t\tsubtitleLabel:MoveBelow(titleLabel)\r\n\t\t\tsubtitleLabel:SetPaintedManually(true)\r\n\t\t\tnewHeight = newHeight + subtitleLabel:GetTall()\r\n\t\tend\r\n\r\n\t\tlogoPanel:SetTall(newHeight)\r\n\tend\r\n\r\n\t-- button list\r\n\tself.mainButtonList = self:Add(\"ixCharMenuButtonList\")\r\n\tself.mainButtonList:Dock(LEFT)\r\n\r\n\t-- create character button\r\n\tlocal createButton = self.mainButtonList:Add(\"ixMenuButton\")\r\n\tcreateButton:SetText(\"create\")\r\n\tcreateButton:SizeToContents()\r\n\tcreateButton.DoClick = function()\r\n\t\tlocal maximum = hook.Run(\"GetMaxPlayerCharacter\", LocalPlayer()) or ix.config.Get(\"maxCharacters\", 5)\r\n\t\t-- don't allow creation if we've hit the character limit\r\n\t\tif (#ix.characters >= maximum) then\r\n\t\t\tself:GetParent():ShowNotice(3, L(\"maxCharacters\"))\r\n\t\t\treturn\r\n\t\tend\r\n\r\n\t\tself:Dim()\r\n\t\tparent.newCharacterPanel:SetActiveSubpanel(\"faction\", 0)\r\n\t\tparent.newCharacterPanel:SlideUp()\r\n\tend\r\n\r\n\t-- load character button\r\n\tself.loadButton = self.mainButtonList:Add(\"ixMenuButton\")\r\n\tself.loadButton:SetText(\"load\")\r\n\tself.loadButton:SizeToContents()\r\n\tself.loadButton.DoClick = function()\r\n\t\tself:Dim()\r\n\t\tparent.loadCharacterPanel:SlideUp()\r\n\tend\r\n\r\n\tif (!bHasCharacter) then\r\n\t\tself.loadButton:SetDisabled(true)\r\n\tend\r\n\r\n\t-- community button\r\n\tlocal extraURL = ix.config.Get(\"communityURL\", \"\")\r\n\tlocal extraText = ix.config.Get(\"communityText\", \"@community\")\r\n\r\n\tif (extraURL != \"\" and extraText != \"\") then\r\n\t\tif (extraText:sub(1, 1) == \"@\") then\r\n\t\t\textraText = L(extraText:sub(2))\r\n\t\tend\r\n\r\n\t\tlocal extraButton = self.mainButtonList:Add(\"ixMenuButton\")\r\n\t\textraButton:SetText(extraText, true)\r\n\t\textraButton:SizeToContents()\r\n\t\textraButton.DoClick = function()\r\n\t\t\tgui.OpenURL(extraURL)\r\n\t\tend\r\n\tend\r\n\r\n\t-- leave/return button\r\n\tself.returnButton = self.mainButtonList:Add(\"ixMenuButton\")\r\n\tself:UpdateReturnButton()\r\n\tself.returnButton.DoClick = function()\r\n\t\tif (self.bUsingCharacter) then\r\n\t\t\tparent:Close()\r\n\t\telse\r\n\t\t\tRunConsoleCommand(\"disconnect\")\r\n\t\tend\r\n\tend\r\n\r\n\tself.mainButtonList:SizeToContents()\r\nend\r\n\r\nfunction PANEL:UpdateReturnButton(bValue)\r\n\tif (bValue != nil) then\r\n\t\tself.bUsingCharacter = bValue\r\n\tend\r\n\r\n\tself.returnButton:SetText(self.bUsingCharacter and \"return\" or \"leave\")\r\n\tself.returnButton:SizeToContents()\r\nend\r\n\r\nfunction PANEL:OnDim()\r\n\t-- disable input on this panel since it will still be in the background while invisible - prone to stray clicks if the\r\n\t-- panels overtop slide out of the way\r\n\tself:SetMouseInputEnabled(false)\r\n\tself:SetKeyboardInputEnabled(false)\r\nend\r\n\r\nfunction PANEL:OnUndim()\r\n\tself:SetMouseInputEnabled(true)\r\n\tself:SetKeyboardInputEnabled(true)\r\n\r\n\t-- we may have just deleted a character so update the status of the return button\r\n\tself.bUsingCharacter = LocalPlayer().GetCharacter and LocalPlayer():GetCharacter()\r\n\tself:UpdateReturnButton()\r\nend\r\n\r\nfunction PANEL:OnClose()\r\n\tfor _, v in pairs(self:GetChildren()) do\r\n\t\tif (IsValid(v)) then\r\n\t\t\tv:SetVisible(false)\r\n\t\tend\r\n\tend\r\nend\r\n\r\nfunction PANEL:PerformLayout(width, height)\r\n\tlocal padding = self:GetPadding()\r\n\r\n\tself.mainButtonList:SetPos(padding, height - self.mainButtonList:GetTall() - padding)\r\nend\r\n\r\nvgui.Register(\"ixCharMenuMain\", PANEL, \"ixCharMenuPanel\")\r\n\r\n-- container panel\r\nPANEL = {}\r\n\r\nfunction PANEL:Init()\r\n\tif (IsValid(ix.gui.loading)) then\r\n\t\tix.gui.loading:Remove()\r\n\tend\r\n\r\n\tif (IsValid(ix.gui.characterMenu)) then\r\n\t\tif (IsValid(ix.gui.characterMenu.channel)) then\r\n\t\t\tix.gui.characterMenu.channel:Stop()\r\n\t\tend\r\n\r\n\t\tix.gui.characterMenu:Remove()\r\n\tend\r\n\r\n\tself:SetSize(ScrW(), ScrH())\r\n\tself:SetPos(0, 0)\r\n\r\n\t-- main menu panel\r\n\tself.mainPanel = self:Add(\"ixCharMenuMain\")\r\n\r\n\t-- new character panel\r\n\tself.newCharacterPanel = self:Add(\"ixCharMenuNew\")\r\n\tself.newCharacterPanel:SlideDown(0)\r\n\r\n\t-- load character panel\r\n\tself.loadCharacterPanel = self:Add(\"ixCharMenuLoad\")\r\n\tself.loadCharacterPanel:SlideDown(0)\r\n\r\n\t-- notice bar\r\n\tself.notice = self:Add(\"ixNoticeBar\")\r\n\r\n\t-- finalization\r\n\tself:MakePopup()\r\n\tself.currentAlpha = 255\r\n\tself.volume = 0\r\n\r\n\tix.gui.characterMenu = self\r\n\r\n\tif (!IsValid(ix.gui.intro)) then\r\n\t\tself:PlayMusic()\r\n\tend\r\n\r\n\thook.Run(\"OnCharacterMenuCreated\", self)\r\nend\r\n\r\nfunction PANEL:PlayMusic()\r\n\tlocal path = \"sound/\" .. ix.config.Get(\"music\")\r\n\tlocal url = path:match(\"http[s]?://.+\")\r\n\tlocal play = url and sound.PlayURL or sound.PlayFile\r\n\tpath = url and url or path\r\n\r\n\tplay(path, \"noplay\", function(channel, error, message)\r\n\t\tif (!IsValid(self) or !IsValid(channel)) then\r\n\t\t\treturn\r\n\t\tend\r\n\r\n\t\tchannel:SetVolume(self.volume or 0)\r\n\t\tchannel:Play()\r\n\r\n\t\tself.channel = channel\r\n\r\n\t\tself:CreateAnimation(audioFadeInTime, {\r\n\t\t\tindex = 10,\r\n\t\t\ttarget = {volume = 1},\r\n\r\n\t\t\tThink = function(animation, panel)\r\n\t\t\t\tif (IsValid(panel.channel)) then\r\n\t\t\t\t\tpanel.channel:SetVolume(self.volume * 0.5)\r\n\t\t\t\tend\r\n\t\t\tend\r\n\t\t})\r\n\tend)\r\nend\r\n\r\nfunction PANEL:ShowNotice(type, text)\r\n\tself.notice:SetType(type)\r\n\tself.notice:SetText(text)\r\n\tself.notice:Show()\r\nend\r\n\r\nfunction PANEL:HideNotice()\r\n\tif (IsValid(self.notice) and !self.notice:GetHidden()) then\r\n\t\tself.notice:Slide(\"up\", 0.5, true)\r\n\tend\r\nend\r\n\r\nfunction PANEL:OnCharacterDeleted(character)\r\n\tif (#ix.characters == 0) then\r\n\t\tself.mainPanel.loadButton:SetDisabled(true)\r\n\t\tself.mainPanel:Undim() -- undim since the load panel will slide down\r\n\telse\r\n\t\tself.mainPanel.loadButton:SetDisabled(false)\r\n\tend\r\n\r\n\tself.loadCharacterPanel:OnCharacterDeleted(character)\r\nend\r\n\r\nfunction PANEL:OnCharacterLoadFailed(error)\r\n\tself.loadCharacterPanel:SetMouseInputEnabled(true)\r\n\tself.loadCharacterPanel:SlideUp()\r\n\tself:ShowNotice(3, error)\r\nend\r\n\r\nfunction PANEL:IsClosing()\r\n\treturn self.bClosing\r\nend\r\n\r\nfunction PANEL:Close(bFromMenu)\r\n\tself.bClosing = true\r\n\tself.bFromMenu = bFromMenu\r\n\r\n\tlocal fadeOutTime = animationTime * 8\r\n\r\n\tself:CreateAnimation(fadeOutTime, {\r\n\t\tindex = 1,\r\n\t\ttarget = {currentAlpha = 0},\r\n\r\n\t\tThink = function(animation, panel)\r\n\t\t\tpanel:SetAlpha(panel.currentAlpha)\r\n\t\tend,\r\n\r\n\t\tOnComplete = function(animation, panel)\r\n\t\t\tpanel:Remove()\r\n\t\tend\r\n\t})\r\n\r\n\tself:CreateAnimation(fadeOutTime - 0.1, {\r\n\t\tindex = 10,\r\n\t\ttarget = {volume = 0},\r\n\r\n\t\tThink = function(animation, panel)\r\n\t\t\tif (IsValid(panel.channel)) then\r\n\t\t\t\tpanel.channel:SetVolume(self.volume * 0.5)\r\n\t\t\tend\r\n\t\tend,\r\n\r\n\t\tOnComplete = function(animation, panel)\r\n\t\t\tif (IsValid(panel.channel)) then\r\n\t\t\t\tpanel.channel:Stop()\r\n\t\t\t\tpanel.channel = nil\r\n\t\t\tend\r\n\t\tend\r\n\t})\r\n\r\n\t-- hide children if we're already dimmed\r\n\tif (bFromMenu) then\r\n\t\tfor _, v in pairs(self:GetChildren()) do\r\n\t\t\tif (IsValid(v)) then\r\n\t\t\t\tv:SetVisible(false)\r\n\t\t\tend\r\n\t\tend\r\n\telse\r\n\t\t-- fade out the main panel quicker because it significantly blocks the screen\r\n\t\tself.mainPanel.currentAlpha = 255\r\n\r\n\t\tself.mainPanel:CreateAnimation(animationTime * 2, {\r\n\t\t\ttarget = {currentAlpha = 0},\r\n\t\t\teasing = \"outQuint\",\r\n\r\n\t\t\tThink = function(animation, panel)\r\n\t\t\t\tpanel:SetAlpha(panel.currentAlpha)\r\n\t\t\tend,\r\n\r\n\t\t\tOnComplete = function(animation, panel)\r\n\t\t\t\tpanel:SetVisible(false)\r\n\t\t\tend\r\n\t\t})\r\n\tend\r\n\r\n\t-- relinquish mouse control\r\n\tself:SetMouseInputEnabled(false)\r\n\tself:SetKeyboardInputEnabled(false)\r\n\tgui.EnableScreenClicker(false)\r\nend\r\n\r\nfunction PANEL:Paint(width, height)\r\n\tsurface.SetTexture(gradient)\r\n\tsurface.SetDrawColor(0, 0, 0, 255)\r\n\tsurface.DrawTexturedRect(0, 0, width, height)\r\n\r\n\tif (!ix.option.Get(\"cheapBlur\", false)) then\r\n\t\tsurface.SetDrawColor(0, 0, 0, 150)\r\n\t\tsurface.DrawTexturedRect(0, 0, width, height)\r\n\t\tix.util.DrawBlur(self, Lerp((self.currentAlpha - 200) / 255, 0, 10))\r\n\tend\r\nend\r\n\r\nfunction PANEL:PaintOver(width, height)\r\n\tif (self.bClosing and self.bFromMenu) then\r\n\t\tsurface.SetDrawColor(color_black)\r\n\t\tsurface.DrawRect(0, 0, width, height)\r\n\tend\r\nend\r\n\r\nfunction PANEL:OnRemove()\r\n\tif (IsValid(self.channel)) then\r\n\t\tself.channel:Stop()\r\n\t\tself.channel = nil\r\n\tend\r\nend\r\n\r\nvgui.Register(\"ixCharMenu\", PANEL, \"EditablePanel\")\r\n\r\nif (IsValid(ix.gui.characterMenu)) then\r\n\tix.gui.characterMenu:Remove()\r\n\r\n\t--TODO: REMOVE ME\r\n\tix.gui.characterMenu = vgui.Create(\"ixCharMenu\")\r\nend\r\n"
  },
  {
    "path": "gamemode/core/derma/cl_charcreate.lua",
    "content": "\nlocal padding = ScreenScale(32)\n\n-- create character panel\nDEFINE_BASECLASS(\"ixCharMenuPanel\")\nlocal PANEL = {}\n\nfunction PANEL:Init()\n\tlocal parent = self:GetParent()\n\tlocal halfWidth = parent:GetWide() * 0.5 - (padding * 2)\n\tlocal halfHeight = parent:GetTall() * 0.5 - (padding * 2)\n\tlocal modelFOV = (ScrW() > ScrH() * 1.8) and 100 or 78\n\n\tself:ResetPayload(true)\n\n\tself.factionButtons = {}\n\tself.repopulatePanels = {}\n\n\t-- faction selection subpanel\n\tself.factionPanel = self:AddSubpanel(\"faction\", true)\n\tself.factionPanel:SetTitle(\"chooseFaction\")\n\tself.factionPanel.OnSetActive = function()\n\t\t-- if we only have one faction, we are always selecting that one so we can skip to the description section\n\t\tif (#self.factionButtons == 1) then\n\t\t\tself:SetActiveSubpanel(\"description\", 0)\n\t\tend\n\tend\n\n\tlocal modelList = self.factionPanel:Add(\"Panel\")\n\tmodelList:Dock(RIGHT)\n\tmodelList:SetSize(halfWidth + padding * 2, halfHeight)\n\n\tlocal proceed = modelList:Add(\"ixMenuButton\")\n\tproceed:SetText(\"proceed\")\n\tproceed:SetContentAlignment(6)\n\tproceed:Dock(BOTTOM)\n\tproceed:SizeToContents()\n\tproceed.DoClick = function()\n\t\tself.progress:IncrementProgress()\n\n\t\tself:Populate()\n\t\tself:SetActiveSubpanel(\"description\")\n\tend\n\n\tself.factionModel = modelList:Add(\"ixModelPanel\")\n\tself.factionModel:Dock(FILL)\n\tself.factionModel:SetModel(\"models/error.mdl\")\n\tself.factionModel:SetFOV(modelFOV)\n\tself.factionModel.PaintModel = self.factionModel.Paint\n\n\tself.factionButtonsPanel = self.factionPanel:Add(\"ixCharMenuButtonList\")\n\tself.factionButtonsPanel:SetWide(halfWidth)\n\tself.factionButtonsPanel:Dock(FILL)\n\n\tlocal factionBack = self.factionPanel:Add(\"ixMenuButton\")\n\tfactionBack:SetText(\"return\")\n\tfactionBack:SizeToContents()\n\tfactionBack:Dock(BOTTOM)\n\tfactionBack.DoClick = function()\n\t\tself.progress:DecrementProgress()\n\n\t\tself:SetActiveSubpanel(\"faction\", 0)\n\t\tself:SlideDown()\n\n\t\tparent.mainPanel:Undim()\n\tend\n\n\t-- character customization subpanel\n\tself.description = self:AddSubpanel(\"description\")\n\tself.description:SetTitle(\"chooseDescription\")\n\n\tlocal descriptionModelList = self.description:Add(\"Panel\")\n\tdescriptionModelList:Dock(LEFT)\n\tdescriptionModelList:SetSize(halfWidth, halfHeight)\n\n\tlocal descriptionBack = descriptionModelList:Add(\"ixMenuButton\")\n\tdescriptionBack:SetText(\"return\")\n\tdescriptionBack:SetContentAlignment(4)\n\tdescriptionBack:SizeToContents()\n\tdescriptionBack:Dock(BOTTOM)\n\tdescriptionBack.DoClick = function()\n\t\tself.progress:DecrementProgress()\n\n\t\tif (#self.factionButtons == 1) then\n\t\t\tfactionBack:DoClick()\n\t\telse\n\t\t\tself:SetActiveSubpanel(\"faction\")\n\t\tend\n\tend\n\n\tself.descriptionModel = descriptionModelList:Add(\"ixModelPanel\")\n\tself.descriptionModel:Dock(FILL)\n\tself.descriptionModel:SetModel(self.factionModel:GetModel())\n\tself.descriptionModel:SetFOV(modelFOV - 13)\n\tself.descriptionModel.PaintModel = self.descriptionModel.Paint\n\n\tself.descriptionPanel = self.description:Add(\"Panel\")\n\tself.descriptionPanel:SetWide(halfWidth + padding * 2)\n\tself.descriptionPanel:Dock(RIGHT)\n\n\tlocal descriptionProceed = self.descriptionPanel:Add(\"ixMenuButton\")\n\tdescriptionProceed:SetText(\"proceed\")\n\tdescriptionProceed:SetContentAlignment(6)\n\tdescriptionProceed:SizeToContents()\n\tdescriptionProceed:Dock(BOTTOM)\n\tdescriptionProceed.DoClick = function()\n\t\tif (self:VerifyProgression(\"description\")) then\n\t\t\t-- there are no panels on the attributes section other than the create button, so we can just create the character\n\t\t\tif (#self.attributesPanel:GetChildren() < 2) then\n\t\t\t\tself:SendPayload()\n\t\t\t\treturn\n\t\t\tend\n\n\t\t\tself.progress:IncrementProgress()\n\t\t\tself:SetActiveSubpanel(\"attributes\")\n\t\tend\n\tend\n\n\t-- attributes subpanel\n\tself.attributes = self:AddSubpanel(\"attributes\")\n\tself.attributes:SetTitle(\"chooseSkills\")\n\n\tlocal attributesModelList = self.attributes:Add(\"Panel\")\n\tattributesModelList:Dock(LEFT)\n\tattributesModelList:SetSize(halfWidth, halfHeight)\n\n\tlocal attributesBack = attributesModelList:Add(\"ixMenuButton\")\n\tattributesBack:SetText(\"return\")\n\tattributesBack:SetContentAlignment(4)\n\tattributesBack:SizeToContents()\n\tattributesBack:Dock(BOTTOM)\n\tattributesBack.DoClick = function()\n\t\tself.progress:DecrementProgress()\n\t\tself:SetActiveSubpanel(\"description\")\n\tend\n\n\tself.attributesModel = attributesModelList:Add(\"ixModelPanel\")\n\tself.attributesModel:Dock(FILL)\n\tself.attributesModel:SetModel(self.factionModel:GetModel())\n\tself.attributesModel:SetFOV(modelFOV - 13)\n\tself.attributesModel.PaintModel = self.attributesModel.Paint\n\n\tself.attributesPanel = self.attributes:Add(\"Panel\")\n\tself.attributesPanel:SetWide(halfWidth + padding * 2)\n\tself.attributesPanel:Dock(RIGHT)\n\n\tlocal create = self.attributesPanel:Add(\"ixMenuButton\")\n\tcreate:SetText(\"finish\")\n\tcreate:SetContentAlignment(6)\n\tcreate:SizeToContents()\n\tcreate:Dock(BOTTOM)\n\tcreate.DoClick = function()\n\t\tself:SendPayload()\n\tend\n\n\t-- creation progress panel\n\tself.progress = self:Add(\"ixSegmentedProgress\")\n\tself.progress:SetBarColor(ix.config.Get(\"color\"))\n\tself.progress:SetSize(parent:GetWide(), 0)\n\tself.progress:SizeToContents()\n\tself.progress:SetPos(0, parent:GetTall() - self.progress:GetTall())\n\n\t-- setup payload hooks\n\tself:AddPayloadHook(\"model\", function(value)\n\t\tlocal faction = ix.faction.indices[self.payload.faction]\n\n\t\tif (faction) then\n\t\t\tlocal model = faction:GetModels(LocalPlayer())[value]\n\n\t\t\t-- assuming bodygroups\n\t\t\tif (istable(model)) then\n\t\t\t\tself.factionModel:SetModel(model[1], model[2] or 0, model[3])\n\t\t\t\tself.descriptionModel:SetModel(model[1], model[2] or 0, model[3])\n\t\t\t\tself.attributesModel:SetModel(model[1], model[2] or 0, model[3])\n\t\t\telse\n\t\t\t\tself.factionModel:SetModel(model)\n\t\t\t\tself.descriptionModel:SetModel(model)\n\t\t\t\tself.attributesModel:SetModel(model)\n\t\t\tend\n\t\tend\n\tend)\n\n\t-- setup character creation hooks\n\tnet.Receive(\"ixCharacterAuthed\", function()\n\t\ttimer.Remove(\"ixCharacterCreateTimeout\")\n\t\tself.awaitingResponse = false\n\n\t\tlocal id = net.ReadUInt(32)\n\t\tlocal indices = net.ReadUInt(6)\n\t\tlocal charList = {}\n\n\t\tfor _ = 1, indices do\n\t\t\tcharList[#charList + 1] = net.ReadUInt(32)\n\t\tend\n\n\t\tix.characters = charList\n\n\t\tself:SlideDown()\n\n\t\tif (!IsValid(self) or !IsValid(parent)) then\n\t\t\treturn\n\t\tend\n\n\t\tif (LocalPlayer():GetCharacter()) then\n\t\t\tparent.mainPanel:Undim()\n\t\t\tparent:ShowNotice(2, L(\"charCreated\"))\n\t\telseif (id) then\n\t\t\tself.bMenuShouldClose = true\n\n\t\t\tnet.Start(\"ixCharacterChoose\")\n\t\t\t\tnet.WriteUInt(id, 32)\n\t\t\tnet.SendToServer()\n\t\telse\n\t\t\tself:SlideDown()\n\t\tend\n\tend)\n\n\tnet.Receive(\"ixCharacterAuthFailed\", function()\n\t\ttimer.Remove(\"ixCharacterCreateTimeout\")\n\t\tself.awaitingResponse = false\n\n\t\tlocal fault = net.ReadString()\n\t\tlocal args = net.ReadTable()\n\n\t\tself:SlideDown()\n\n\t\tparent.mainPanel:Undim()\n\t\tparent:ShowNotice(3, L(fault, unpack(args)))\n\tend)\nend\n\nfunction PANEL:SendPayload()\n\tif (self.awaitingResponse or !self:VerifyProgression()) then\n\t\treturn\n\tend\n\n\tself.awaitingResponse = true\n\n\ttimer.Create(\"ixCharacterCreateTimeout\", 10, 1, function()\n\t\tif (IsValid(self) and self.awaitingResponse) then\n\t\t\tlocal parent = self:GetParent()\n\n\t\t\tself.awaitingResponse = false\n\t\t\tself:SlideDown()\n\n\t\t\tparent.mainPanel:Undim()\n\t\t\tparent:ShowNotice(3, L(\"unknownError\"))\n\t\tend\n\tend)\n\n\tself.payload:Prepare()\n\n\tnet.Start(\"ixCharacterCreate\")\n\tnet.WriteUInt(table.Count(self.payload), 8)\n\n\tfor k, v in pairs(self.payload) do\n\t\tnet.WriteString(k)\n\t\tnet.WriteType(v)\n\tend\n\n\tnet.SendToServer()\nend\n\nfunction PANEL:OnSlideUp()\n\tself:ResetPayload()\n\tself:Populate()\n\tself.progress:SetProgress(1)\n\n\t-- the faction subpanel will skip to next subpanel if there is only one faction to choose from,\n\t-- so we don't have to worry about it here\n\tself:SetActiveSubpanel(\"faction\", 0)\nend\n\nfunction PANEL:OnSlideDown()\nend\n\nfunction PANEL:ResetPayload(bWithHooks)\n\tif (bWithHooks) then\n\t\tself.hooks = {}\n\tend\n\n\tself.payload = {}\n\n\t-- TODO: eh..\n\tfunction self.payload.Set(payload, key, value)\n\t\tself:SetPayload(key, value)\n\tend\n\n\tfunction self.payload.AddHook(payload, key, callback)\n\t\tself:AddPayloadHook(key, callback)\n\tend\n\n\tfunction self.payload.Prepare(payload)\n\t\tself.payload.Set = nil\n\t\tself.payload.AddHook = nil\n\t\tself.payload.Prepare = nil\n\tend\nend\n\nfunction PANEL:SetPayload(key, value)\n\tself.payload[key] = value\n\tself:RunPayloadHook(key, value)\nend\n\nfunction PANEL:AddPayloadHook(key, callback)\n\tif (!self.hooks[key]) then\n\t\tself.hooks[key] = {}\n\tend\n\n\tself.hooks[key][#self.hooks[key] + 1] = callback\nend\n\nfunction PANEL:RunPayloadHook(key, value)\n\tlocal hooks = self.hooks[key] or {}\n\n\tfor _, v in ipairs(hooks) do\n\t\tv(value)\n\tend\nend\n\nfunction PANEL:GetContainerPanel(name)\n\t-- TODO: yuck\n\tif (name == \"description\") then\n\t\treturn self.descriptionPanel\n\telseif (name == \"attributes\") then\n\t\treturn self.attributesPanel\n\tend\n\n\treturn self.descriptionPanel\nend\n\nfunction PANEL:AttachCleanup(panel)\n\tself.repopulatePanels[#self.repopulatePanels + 1] = panel\nend\n\nfunction PANEL:Populate()\n\tif (!self.bInitialPopulate) then\n\t\t-- setup buttons for the faction panel\n\t\t-- TODO: make this a bit less janky\n\t\tlocal lastSelected\n\n\t\tfor _, v in pairs(self.factionButtons) do\n\t\t\tif (v:GetSelected()) then\n\t\t\t\tlastSelected = v.faction\n\t\t\tend\n\n\t\t\tif (IsValid(v)) then\n\t\t\t\tv:Remove()\n\t\t\tend\n\t\tend\n\n\t\tself.factionButtons = {}\n\n\t\tfor _, v in SortedPairs(ix.faction.teams) do\n\t\t\tif (ix.faction.HasWhitelist(v.index)) then\n\t\t\t\tlocal button = self.factionButtonsPanel:Add(\"ixMenuSelectionButton\")\n\t\t\t\tbutton:SetBackgroundColor(v.color or color_white)\n\t\t\t\tbutton:SetText(L(v.name):utf8upper())\n\t\t\t\tbutton:SizeToContents()\n\t\t\t\tbutton:SetButtonList(self.factionButtons)\n\t\t\t\tbutton.faction = v.index\n\t\t\t\tbutton.OnSelected = function(panel)\n\t\t\t\t\tlocal faction = ix.faction.indices[panel.faction]\n\t\t\t\t\tlocal models = faction:GetModels(LocalPlayer())\n\n\t\t\t\t\tself.payload:Set(\"faction\", panel.faction)\n\t\t\t\t\tself.payload:Set(\"model\", math.random(1, #models))\n\t\t\t\tend\n\n\t\t\t\tif ((lastSelected and lastSelected == v.index) or (!lastSelected and v.isDefault)) then\n\t\t\t\t\tbutton:SetSelected(true)\n\t\t\t\t\tlastSelected = v.index\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend\n\n\t-- remove panels created for character vars\n\tfor i = 1, #self.repopulatePanels do\n\t\tself.repopulatePanels[i]:Remove()\n\tend\n\n\tself.repopulatePanels = {}\n\n\t-- payload is empty because we attempted to send it - for whatever reason we're back here again so we need to repopulate\n\tif (!self.payload.faction) then\n\t\tfor _, v in pairs(self.factionButtons) do\n\t\t\tif (v:GetSelected()) then\n\t\t\t\tv:SetSelected(true)\n\t\t\t\tbreak\n\t\t\tend\n\t\tend\n\tend\n\n\tself.factionButtonsPanel:SizeToContents()\n\n\tlocal zPos = 1\n\n\t-- set up character vars\n\tfor k, v in SortedPairsByMemberValue(ix.char.vars, \"index\") do\n\t\tif (!v.bNoDisplay and k != \"__SortedIndex\") then\n\t\t\tlocal container = self:GetContainerPanel(v.category or \"description\")\n\n\t\t\tif (v.ShouldDisplay and v:ShouldDisplay(container, self.payload) == false) then\n\t\t\t\tcontinue\n\t\t\tend\n\n\t\t\tlocal panel\n\n\t\t\t-- if the var has a custom way of displaying, we'll use that instead\n\t\t\tif (v.OnDisplay) then\n\t\t\t\tpanel = v:OnDisplay(container, self.payload)\n\t\t\telseif (isstring(v.default)) then\n\t\t\t\tpanel = container:Add(\"ixTextEntry\")\n\t\t\t\tpanel:Dock(TOP)\n\t\t\t\tpanel:SetFont(\"ixMenuButtonHugeFont\")\n\t\t\t\tpanel:SetUpdateOnType(true)\n\t\t\t\tpanel.OnValueChange = function(this, text)\n\t\t\t\t\tself.payload:Set(k, text)\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tif (IsValid(panel)) then\n\t\t\t\t-- add label for entry\n\t\t\t\tlocal label = container:Add(\"DLabel\")\n\t\t\t\tlabel:SetFont(\"ixMenuButtonLabelFont\")\n\t\t\t\tlabel:SetText(L(k):utf8upper())\n\t\t\t\tlabel:SizeToContents()\n\t\t\t\tlabel:DockMargin(0, 16, 0, 2)\n\t\t\t\tlabel:Dock(TOP)\n\n\t\t\t\t-- we need to set the docking order so the label is above the panel\n\t\t\t\tlabel:SetZPos(zPos - 1)\n\t\t\t\tpanel:SetZPos(zPos)\n\n\t\t\t\tself:AttachCleanup(label)\n\t\t\t\tself:AttachCleanup(panel)\n\n\t\t\t\tif (v.OnPostSetup) then\n\t\t\t\t\tv:OnPostSetup(panel, self.payload)\n\t\t\t\tend\n\n\t\t\t\tzPos = zPos + 2\n\t\t\tend\n\t\tend\n\tend\n\n\tif (!self.bInitialPopulate) then\n\t\t-- setup progress bar segments\n\t\tif (#self.factionButtons > 1) then\n\t\t\tself.progress:AddSegment(\"@faction\")\n\t\tend\n\n\t\tself.progress:AddSegment(\"@description\")\n\n\t\tif (#self.attributesPanel:GetChildren() > 1) then\n\t\t\tself.progress:AddSegment(\"@skills\")\n\t\tend\n\n\t\t-- we don't need to show the progress bar if there's only one segment\n\t\tif (#self.progress:GetSegments() == 1) then\n\t\t\tself.progress:SetVisible(false)\n\t\tend\n\tend\n\n\tself.bInitialPopulate = true\nend\n\nfunction PANEL:VerifyProgression(name)\n\tfor k, v in SortedPairsByMemberValue(ix.char.vars, \"index\") do\n\t\tif (name ~= nil and (v.category or \"description\") != name) then\n\t\t\tcontinue\n\t\tend\n\n\t\tlocal value = self.payload[k]\n\n\t\tif (!v.bNoDisplay or v.OnValidate) then\n\t\t\tif (v.OnValidate) then\n\t\t\t\tlocal result = {v:OnValidate(value, self.payload, LocalPlayer())}\n\n\t\t\t\tif (result[1] == false) then\n\t\t\t\t\tself:GetParent():ShowNotice(3, L(unpack(result, 2)))\n\t\t\t\t\treturn false\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tself.payload[k] = value\n\t\tend\n\tend\n\n\treturn true\nend\n\nfunction PANEL:Paint(width, height)\n\tderma.SkinFunc(\"PaintCharacterCreateBackground\", self, width, height)\n\tBaseClass.Paint(self, width, height)\nend\n\nvgui.Register(\"ixCharMenuNew\", PANEL, \"ixCharMenuPanel\")\n"
  },
  {
    "path": "gamemode/core/derma/cl_charload.lua",
    "content": "\nlocal errorModel = \"models/error.mdl\"\nlocal PANEL = {}\n\nAccessorFunc(PANEL, \"animationTime\", \"AnimationTime\", FORCE_NUMBER)\n\nlocal function SetCharacter(self, character)\n\tself.character = character\n\n\tif (character) then\n\t\tself:SetModel(character:GetModel())\n\t\tself:SetSkin(character:GetData(\"skin\", 0))\n\n\t\tfor i = 0, (self:GetNumBodyGroups() - 1) do\n\t\t\tself:SetBodygroup(i, 0)\n\t\tend\n\n\t\tlocal bodygroups = character:GetData(\"groups\", nil)\n\n\t\tif (istable(bodygroups)) then\n\t\t\tfor k, v in pairs(bodygroups) do\n\t\t\t\tself:SetBodygroup(k, v)\n\t\t\tend\n\t\tend\n\telse\n\t\tself:SetModel(errorModel)\n\tend\nend\n\nlocal function GetCharacter(self)\n\treturn self.character\nend\n\nfunction PANEL:Init()\n\tself.activeCharacter = ClientsideModel(errorModel)\n\tself.activeCharacter:SetNoDraw(true)\n\tself.activeCharacter.SetCharacter = SetCharacter\n\tself.activeCharacter.GetCharacter = GetCharacter\n\n\tself.lastCharacter = ClientsideModel(errorModel)\n\tself.lastCharacter:SetNoDraw(true)\n\tself.lastCharacter.SetCharacter = SetCharacter\n\tself.lastCharacter.GetCharacter = GetCharacter\n\n\tself.animationTime = 0.5\n\n\tself.shadeY = 0\n\tself.shadeHeight = 0\n\n\tself.cameraPosition = Vector(80, 0, 35)\n\tself.cameraAngle = Angle(0, 180, 0)\n\tself.lastPaint = 0\nend\n\nfunction PANEL:ResetSequence(model, lastModel)\n\tlocal sequence = model:LookupSequence(\"idle_unarmed\")\n\n\tif (sequence <= 0) then\n\t\tsequence = model:SelectWeightedSequence(ACT_IDLE)\n\tend\n\n\tif (sequence > 0) then\n\t\tmodel:ResetSequence(sequence)\n\telse\n\t\tlocal found = false\n\n\t\tfor _, v in ipairs(model:GetSequenceList()) do\n\t\t\tif ((v:lower():find(\"idle\") or v:lower():find(\"fly\")) and v != \"idlenoise\") then\n\t\t\t\tmodel:ResetSequence(v)\n\t\t\t\tfound = true\n\n\t\t\t\tbreak\n\t\t\tend\n\t\tend\n\n\t\tif (!found) then\n\t\t\tmodel:ResetSequence(4)\n\t\tend\n\tend\n\n\tmodel:SetIK(false)\n\n\t-- copy cycle if we can to avoid a jarring transition from resetting the sequence\n\tif (lastModel) then\n\t\tmodel:SetCycle(lastModel:GetCycle())\n\tend\nend\n\nfunction PANEL:RunAnimation(model)\n\tmodel:FrameAdvance((RealTime() - self.lastPaint) * 0.5)\nend\n\nfunction PANEL:LayoutEntity(model)\n\tmodel:SetIK(false)\n\n\tself:RunAnimation(model)\nend\n\nfunction PANEL:SetActiveCharacter(character)\n\tself.shadeY = self:GetTall()\n\tself.shadeHeight = self:GetTall()\n\n\t-- set character immediately if we're an error (something isn't selected yet)\n\tif (self.activeCharacter:GetModel() == errorModel) then\n\t\tself.activeCharacter:SetCharacter(character)\n\t\tself:ResetSequence(self.activeCharacter)\n\n\t\treturn\n\tend\n\n\t-- if the animation is already playing, we update its parameters so we can avoid restarting\n\tlocal shade = self:GetTweenAnimation(1)\n\tlocal shadeHide = self:GetTweenAnimation(2)\n\n\tif (shade) then\n\t\tshade.newCharacter = character\n\t\treturn\n\telseif (shadeHide) then\n\t\tshadeHide.queuedCharacter = character\n\t\treturn\n\tend\n\n\tself.lastCharacter:SetCharacter(self.activeCharacter:GetCharacter())\n\tself:ResetSequence(self.lastCharacter, self.activeCharacter)\n\n\tshade = self:CreateAnimation(self.animationTime * 0.5, {\n\t\tindex = 1,\n\t\ttarget = {\n\t\t\tshadeY = 0,\n\t\t\tshadeHeight = self:GetTall()\n\t\t},\n\t\teasing = \"linear\",\n\n\t\tOnComplete = function(shadeAnimation, shadePanel)\n\t\t\tshadePanel.activeCharacter:SetCharacter(shadeAnimation.newCharacter)\n\t\t\tshadePanel:ResetSequence(shadePanel.activeCharacter)\n\n\t\t\tshadePanel:CreateAnimation(shadePanel.animationTime, {\n\t\t\t\tindex = 2,\n\t\t\t\ttarget = {shadeHeight = 0},\n\t\t\t\teasing = \"outQuint\",\n\n\t\t\t\tOnComplete = function(animation, panel)\n\t\t\t\t\tif (animation.queuedCharacter) then\n\t\t\t\t\t\tpanel:SetActiveCharacter(animation.queuedCharacter)\n\t\t\t\t\telse\n\t\t\t\t\t\tpanel.lastCharacter:SetCharacter(nil)\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\t})\n\t\tend\n\t})\n\n\tshade.newCharacter = character\nend\n\nfunction PANEL:Paint(width, height)\n\tlocal x, y = self:LocalToScreen(0, 0)\n\tlocal bTransition = self.lastCharacter:GetModel() != errorModel\n\tlocal modelFOV = (ScrW() > ScrH() * 1.8) and 92 or 70\n\n\tcam.Start3D(self.cameraPosition, self.cameraAngle, modelFOV, x, y, width, height)\n\t\trender.SuppressEngineLighting(true)\n\t\trender.SetLightingOrigin(self.activeCharacter:GetPos())\n\n\t\t-- setup lighting\n\t\trender.SetModelLighting(0, 1.5, 1.5, 1.5)\n\n\t\tfor i = 1, 4 do\n\t\t\trender.SetModelLighting(i, 0.4, 0.4, 0.4)\n\t\tend\n\n\t\trender.SetModelLighting(5, 0.04, 0.04, 0.04)\n\n\t\t-- clip anything out of bounds\n\t\tlocal curparent = self\n\t\tlocal rightx = self:GetWide()\n\t\tlocal leftx = 0\n\t\tlocal topy = 0\n\t\tlocal bottomy = self:GetTall()\n\t\tlocal previous = curparent\n\n\t\twhile (curparent:GetParent() != nil) do\n\t\t\tlocal lastX, lastY = previous:GetPos()\n\t\t\tcurparent = curparent:GetParent()\n\n\t\t\ttopy = math.Max(lastY, topy + lastY)\n\t\t\tleftx = math.Max(lastX, leftx + lastX)\n\t\t\tbottomy = math.Min(lastY + previous:GetTall(), bottomy + lastY)\n\t\t\trightx = math.Min(lastX + previous:GetWide(), rightx + lastX)\n\n\t\t\tprevious = curparent\n\t\tend\n\n\t\tix.util.ResetStencilValues()\n\t\trender.SetStencilEnable(true)\n\t\t\trender.SetStencilWriteMask(30)\n\t\t\trender.SetStencilTestMask(30)\n\t\t\trender.SetStencilReferenceValue(31)\n\n\t\t\trender.SetStencilCompareFunction(STENCIL_ALWAYS)\n\t\t\trender.SetStencilPassOperation(STENCIL_REPLACE)\n\t\t\trender.SetStencilFailOperation(STENCIL_KEEP)\n\t\t\trender.SetStencilZFailOperation(STENCIL_KEEP)\n\n\t\t\tself:LayoutEntity(self.activeCharacter)\n\n\t\t\tif (bTransition) then\n\t\t\t\t-- only need to layout while it's used\n\t\t\t\tself:LayoutEntity(self.lastCharacter)\n\n\t\t\t\trender.SetScissorRect(leftx, topy, rightx, bottomy - (self:GetTall() - self.shadeHeight), true)\n\t\t\t\tself.lastCharacter:DrawModel()\n\n\t\t\t\trender.SetScissorRect(leftx, topy + self.shadeHeight, rightx, bottomy, true)\n\t\t\t\tself.activeCharacter:DrawModel()\n\n\t\t\t\trender.SetScissorRect(leftx, topy, rightx, bottomy, true)\n\t\t\telse\n\t\t\t\tself.activeCharacter:DrawModel()\n\t\t\tend\n\n\t\t\trender.SetStencilCompareFunction(STENCIL_EQUAL)\n\t\t\trender.SetStencilPassOperation(STENCIL_KEEP)\n\n\t\t\tcam.Start2D()\n\t\t\t\tderma.SkinFunc(\"PaintCharacterTransitionOverlay\", self, 0, self.shadeY, width, self.shadeHeight)\n\t\t\tcam.End2D()\n\t\trender.SetStencilEnable(false)\n\n\t\trender.SetScissorRect(0, 0, 0, 0, false)\n\t\trender.SuppressEngineLighting(false)\n\tcam.End3D()\n\n\tself.lastPaint = RealTime()\nend\n\nfunction PANEL:OnRemove()\n\tself.lastCharacter:Remove()\n\tself.activeCharacter:Remove()\nend\n\nvgui.Register(\"ixCharMenuCarousel\", PANEL, \"Panel\")\n\n-- character load panel\nPANEL = {}\n\nAccessorFunc(PANEL, \"animationTime\", \"AnimationTime\", FORCE_NUMBER)\nAccessorFunc(PANEL, \"backgroundFraction\", \"BackgroundFraction\", FORCE_NUMBER)\n\nfunction PANEL:Init()\n\tlocal parent = self:GetParent()\n\tlocal padding = self:GetPadding()\n\tlocal halfWidth = parent:GetWide() * 0.5 - (padding * 2)\n\tlocal halfHeight = parent:GetTall() * 0.5 - (padding * 2)\n\tlocal modelFOV = (ScrW() > ScrH() * 1.8) and 102 or 78\n\n\tself.animationTime = 1\n\tself.backgroundFraction = 1\n\n\t-- main panel\n\tself.panel = self:AddSubpanel(\"main\")\n\tself.panel:SetTitle(\"loadTitle\")\n\tself.panel.OnSetActive = function()\n\t\tself:CreateAnimation(self.animationTime, {\n\t\t\tindex = 2,\n\t\t\ttarget = {backgroundFraction = 1},\n\t\t\teasing = \"outQuint\",\n\t\t})\n\tend\n\n\t-- character button list\n\tlocal controlList = self.panel:Add(\"Panel\")\n\tcontrolList:Dock(LEFT)\n\tcontrolList:SetSize(halfWidth, halfHeight)\n\n\tlocal back = controlList:Add(\"ixMenuButton\")\n\tback:Dock(BOTTOM)\n\tback:SetText(\"return\")\n\tback:SizeToContents()\n\tback.DoClick = function()\n\t\tself:SlideDown()\n\t\tparent.mainPanel:Undim()\n\tend\n\n\tself.characterList = controlList:Add(\"ixCharMenuButtonList\")\n\tself.characterList.buttons = {}\n\tself.characterList:Dock(FILL)\n\n\t-- right-hand side with carousel and buttons\n\tlocal infoPanel = self.panel:Add(\"Panel\")\n\tinfoPanel:Dock(FILL)\n\n\tlocal infoButtons = infoPanel:Add(\"Panel\")\n\tinfoButtons:Dock(BOTTOM)\n\tinfoButtons:SetTall(back:GetTall()) -- hmm...\n\n\tlocal continueButton = infoButtons:Add(\"ixMenuButton\")\n\tcontinueButton:Dock(FILL)\n\tcontinueButton:SetText(\"choose\")\n\tcontinueButton:SetContentAlignment(6)\n\tcontinueButton:SizeToContents()\n\tcontinueButton.DoClick = function()\n\t\tself:SlideDown(self.animationTime, function()\n\t\t\tnet.Start(\"ixCharacterChoose\")\n\t\t\t\tnet.WriteUInt(self.character:GetID(), 32)\n\t\t\tnet.SendToServer()\n\t\tend, true)\n\tend\n\n\tlocal deleteButton = infoButtons:Add(\"ixMenuButton\")\n\tdeleteButton:Dock(LEFT)\n\tdeleteButton:SetText(\"delete\")\n\tdeleteButton:SetContentAlignment(5)\n\tdeleteButton:SetTextInset(0, 0)\n\tdeleteButton:SizeToContents()\n\tdeleteButton:SetTextColor(derma.GetColor(\"Error\", deleteButton))\n\tdeleteButton.DoClick = function()\n\t\tself:SetActiveSubpanel(\"delete\")\n\tend\n\n\tself.carousel = infoPanel:Add(\"ixCharMenuCarousel\")\n\tself.carousel:Dock(FILL)\n\n\t-- character deletion panel\n\tself.delete = self:AddSubpanel(\"delete\")\n\tself.delete:SetTitle(nil)\n\tself.delete.OnSetActive = function()\n\t\tself.deleteModel:SetModel(self.character:GetModel())\n\t\tself:CreateAnimation(self.animationTime, {\n\t\t\tindex = 2,\n\t\t\ttarget = {backgroundFraction = 0},\n\t\t\teasing = \"outQuint\"\n\t\t})\n\tend\n\n\tlocal deleteInfo = self.delete:Add(\"Panel\")\n\tdeleteInfo:SetSize(parent:GetWide() * 0.5, parent:GetTall())\n\tdeleteInfo:Dock(LEFT)\n\n\tlocal deleteReturn = deleteInfo:Add(\"ixMenuButton\")\n\tdeleteReturn:Dock(BOTTOM)\n\tdeleteReturn:SetText(\"no\")\n\tdeleteReturn:SizeToContents()\n\tdeleteReturn.DoClick = function()\n\t\tself:SetActiveSubpanel(\"main\")\n\tend\n\n\tlocal deleteConfirm = self.delete:Add(\"ixMenuButton\")\n\tdeleteConfirm:Dock(BOTTOM)\n\tdeleteConfirm:SetText(\"yes\")\n\tdeleteConfirm:SetContentAlignment(6)\n\tdeleteConfirm:SizeToContents()\n\tdeleteConfirm:SetTextColor(derma.GetColor(\"Error\", deleteConfirm))\n\tdeleteConfirm.DoClick = function()\n\t\tlocal id = self.character:GetID()\n\n\t\tparent:ShowNotice(1, L(\"deleteComplete\", self.character:GetName()))\n\t\tself:Populate(id)\n\t\tself:SetActiveSubpanel(\"main\")\n\n\t\tnet.Start(\"ixCharacterDelete\")\n\t\t\tnet.WriteUInt(id, 32)\n\t\tnet.SendToServer()\n\tend\n\n\tself.deleteModel = deleteInfo:Add(\"ixModelPanel\")\n\tself.deleteModel:Dock(FILL)\n\tself.deleteModel:SetModel(errorModel)\n\tself.deleteModel:SetFOV(modelFOV)\n\tself.deleteModel.PaintModel = self.deleteModel.Paint\n\n\tlocal deleteNag = self.delete:Add(\"Panel\")\n\tdeleteNag:SetTall(parent:GetTall() * 0.5)\n\tdeleteNag:Dock(BOTTOM)\n\n\tlocal deleteTitle = deleteNag:Add(\"DLabel\")\n\tdeleteTitle:SetFont(\"ixTitleFont\")\n\tdeleteTitle:SetText(L(\"areYouSure\"):utf8upper())\n\tdeleteTitle:SetTextColor(ix.config.Get(\"color\"))\n\tdeleteTitle:SizeToContents()\n\tdeleteTitle:Dock(TOP)\n\n\tlocal deleteText = deleteNag:Add(\"DLabel\")\n\tdeleteText:SetFont(\"ixMenuButtonFont\")\n\tdeleteText:SetText(L(\"deleteConfirm\"))\n\tdeleteText:SetTextColor(color_white)\n\tdeleteText:SetContentAlignment(7)\n\tdeleteText:Dock(FILL)\n\n\t-- finalize setup\n\tself:SetActiveSubpanel(\"main\", 0)\nend\n\nfunction PANEL:OnCharacterDeleted(character)\n\tif (self.bActive and #ix.characters == 0) then\n\t\tself:SlideDown()\n\tend\nend\n\nfunction PANEL:Populate(ignoreID)\n\tself.characterList:Clear()\n\tself.characterList.buttons = {}\n\n\tlocal bSelected\n\n\t-- loop backwards to preserve order since we're docking to the bottom\n\tfor i = 1, #ix.characters do\n\t\tlocal id = ix.characters[i]\n\t\tlocal character = ix.char.loaded[id]\n\n\t\tif (!character or character:GetID() == ignoreID) then\n\t\t\tcontinue\n\t\tend\n\n\t\tlocal index = character:GetFaction()\n\t\tlocal faction = ix.faction.indices[index]\n\t\tlocal color = faction and faction.color or color_white\n\n\t\tlocal button = self.characterList:Add(\"ixMenuSelectionButton\")\n\t\tbutton:SetBackgroundColor(color)\n\t\tbutton:SetText(character:GetName())\n\t\tbutton:SizeToContents()\n\t\tbutton:SetButtonList(self.characterList.buttons)\n\t\tbutton.character = character\n\t\tbutton.OnSelected = function(panel)\n\t\t\tself:OnCharacterButtonSelected(panel)\n\t\tend\n\n\t\t-- select currently loaded character if available\n\t\tlocal localCharacter = LocalPlayer().GetCharacter and LocalPlayer():GetCharacter()\n\n\t\tif (localCharacter and character:GetID() == localCharacter:GetID()) then\n\t\t\tbutton:SetSelected(true)\n\t\t\tself.characterList:ScrollToChild(button)\n\n\t\t\tbSelected = true\n\t\tend\n\tend\n\n\tif (!bSelected) then\n\t\tlocal buttons = self.characterList.buttons\n\n\t\tif (#buttons > 0) then\n\t\t\tlocal button = buttons[#buttons]\n\n\t\t\tbutton:SetSelected(true)\n\t\t\tself.characterList:ScrollToChild(button)\n\t\telse\n\t\t\tself.character = nil\n\t\tend\n\tend\n\n\tself.characterList:SizeToContents()\nend\n\nfunction PANEL:OnSlideUp()\n\tself.bActive = true\n\tself:Populate()\nend\n\nfunction PANEL:OnSlideDown()\n\tself.bActive = false\nend\n\nfunction PANEL:OnCharacterButtonSelected(panel)\n\tself.carousel:SetActiveCharacter(panel.character)\n\tself.character = panel.character\nend\n\nfunction PANEL:Paint(width, height)\n\tderma.SkinFunc(\"PaintCharacterLoadBackground\", self, width, height)\nend\n\nvgui.Register(\"ixCharMenuLoad\", PANEL, \"ixCharMenuPanel\")\n"
  },
  {
    "path": "gamemode/core/derma/cl_classes.lua",
    "content": "\nlocal PANEL = {}\n\nfunction PANEL:Init()\n\tself:SetTall(64)\n\n\tlocal function AssignClick(panel)\n\t\tpanel.OnMousePressed = function()\n\t\t\tself.pressing = -1\n\t\t\tself:OnClick()\n\t\tend\n\n\t\tpanel.OnMouseReleased = function()\n\t\t\tif (self.pressing) then\n\t\t\t\tself.pressing = nil\n\t\t\tend\n\t\tend\n\tend\n\n\n\tself.icon = self:Add(\"SpawnIcon\")\n\tself.icon:SetSize(128, 64)\n\tself.icon:InvalidateLayout(true)\n\tself.icon:Dock(LEFT)\n\tself.icon.PaintOver = function(this, w, h)\n\tend\n\tAssignClick(self.icon)\n\n\tself.limit = self:Add(\"DLabel\")\n\tself.limit:Dock(RIGHT)\n\tself.limit:SetMouseInputEnabled(true)\n\tself.limit:SetCursor(\"hand\")\n\tself.limit:SetExpensiveShadow(1, Color(0, 0, 60))\n\tself.limit:SetContentAlignment(5)\n\tself.limit:SetFont(\"ixMediumFont\")\n\tself.limit:SetWide(64)\n\tAssignClick(self.limit)\n\n\tself.label = self:Add(\"DLabel\")\n\tself.label:Dock(FILL)\n\tself.label:SetMouseInputEnabled(true)\n\tself.label:SetCursor(\"hand\")\n\tself.label:SetExpensiveShadow(1, Color(0, 0, 60))\n\tself.label:SetContentAlignment(5)\n\tself.label:SetFont(\"ixMediumFont\")\n\tAssignClick(self.label)\nend\n\nfunction PANEL:OnClick()\n\tix.command.Send(\"BecomeClass\", self.class)\nend\n\nfunction PANEL:SetNumber(number)\n\tlocal limit = self.data.limit\n\n\tif (limit > 0) then\n\t\tself.limit:SetText(Format(\"%s/%s\", number, limit))\n\telse\n\t\tself.limit:SetText(\"∞\")\n\tend\nend\n\nfunction PANEL:SetClass(data)\n\tif (data.model) then\n\t\tlocal model = data.model\n\t\tif (istable(model)) then\n\t\t\tmodel = model[ math.random(#model) ]\n\t\tend\n\n\t\tself.icon:SetModel(model)\n\telse\n\t\tlocal char = LocalPlayer():GetCharacter()\n\t\tlocal model = LocalPlayer():GetModel()\n\n\t\tif (char) then\n\t\t\tmodel = char:GetModel()\n\t\tend\n\n\t\tself.icon:SetModel(model)\n\tend\n\n\tself.label:SetText(L(data.name))\n\tself.data = data\n\tself.class = data.index\n\n\tself:SetNumber(#ix.class.GetPlayers(data.index))\nend\n\nvgui.Register(\"ixClassPanel\", PANEL, \"DPanel\")\n\nPANEL = {}\n\nfunction PANEL:Init()\n\tix.gui.classes = self\n\n\tself:SetSize(self:GetParent():GetSize())\n\n\tself.list = vgui.Create(\"DPanelList\", self)\n\tself.list:Dock(FILL)\n\tself.list:EnableVerticalScrollbar()\n\tself.list:SetSpacing(5)\n\tself.list:SetPadding(5)\n\n\tself.classPanels = {}\n\tself:LoadClasses()\nend\n\nfunction PANEL:LoadClasses()\n\tself.list:Clear()\n\n\tfor k, v in ipairs(ix.class.list) do\n\t\tlocal no, why = ix.class.CanSwitchTo(LocalPlayer(), k)\n\t\tlocal itsFull = (\"class is full\" == why)\n\n\t\tif (no or itsFull) then\n\t\t\tlocal panel = vgui.Create(\"ixClassPanel\", self.list)\n\t\t\tpanel:SetClass(v)\n\t\t\ttable.insert(self.classPanels, panel)\n\n\t\t\tself.list:AddItem(panel)\n\t\tend\n\tend\nend\n\nvgui.Register(\"ixClasses\", PANEL, \"EditablePanel\")\n\nhook.Add(\"CreateMenuButtons\", \"ixClasses\", function(tabs)\n\tlocal cnt = table.Count(ix.class.list)\n\n\tif (cnt <= 1) then return end\n\n\tfor k, _ in ipairs(ix.class.list) do\n\t\tif (!ix.class.CanSwitchTo(LocalPlayer(), k)) then\n\t\t\tcontinue\n\t\telse\n\t\t\ttabs[\"classes\"] = function(container)\n\t\t\t\tcontainer:Add(\"ixClasses\")\n\t\t\tend\n\n\t\t\treturn\n\t\tend\n\tend\nend)\n\nnet.Receive(\"ixClassUpdate\", function()\n\tlocal client = net.ReadEntity()\n\n\tif (ix.gui.classes and ix.gui.classes:IsVisible()) then\n\t\tif (client == LocalPlayer()) then\n\t\t\tix.gui.classes:LoadClasses()\n\t\telse\n\t\t\tfor _, v in ipairs(ix.gui.classes.classPanels) do\n\t\t\t\tlocal data = v.data\n\n\t\t\t\tv:SetNumber(#ix.class.GetPlayers(data.index))\n\t\t\tend\n\t\tend\n\tend\nend)\n"
  },
  {
    "path": "gamemode/core/derma/cl_config.lua",
    "content": "\n-- config manager\nlocal PANEL = {}\n\nfunction PANEL:Init()\n\tself:Dock(FILL)\n\tself:SetSearchEnabled(true)\n\n\tself:Populate()\nend\n\nfunction PANEL:Populate()\n\t-- gather categories\n\tlocal categories = {}\n\tlocal categoryIndices = {}\n\n\tfor k, v in pairs(ix.config.stored) do\n\t\tlocal index = v.data and v.data.category or \"misc\"\n\n\t\tcategories[index] = categories[index] or {}\n\t\tcategories[index][k] = v\n\tend\n\n\t-- sort by category phrase\n\tfor k, _ in pairs(categories) do\n\t\tcategoryIndices[#categoryIndices + 1] = k\n\tend\n\n\ttable.sort(categoryIndices, function(a, b)\n\t\treturn L(a) < L(b)\n\tend)\n\n\t-- add panels\n\tfor _, category in ipairs(categoryIndices) do\n\t\tlocal categoryPhrase = L(category)\n\t\tself:AddCategory(categoryPhrase)\n\n\t\t-- we can use sortedpairs since configs don't have phrases to account for\n\t\tfor k, v in SortedPairs(categories[category]) do\n\t\t\tif (isfunction(v.hidden) and v.hidden()) then\n\t\t\t\tcontinue\n\t\t\tend\n\n\t\t\tlocal data = v.data.data\n\t\t\tlocal type = v.type\n\t\t\tlocal value = ix.util.SanitizeType(type, ix.config.Get(k))\n\n\t\t\tlocal row = self:AddRow(type, categoryPhrase)\n\t\t\trow:SetText(ix.util.ExpandCamelCase(k))\n\t\t\trow:Populate(k, v.data)\n\n\t\t\t-- type-specific properties\n\t\t\tif (type == ix.type.number) then\n\t\t\t\trow:SetMin(data and data.min or 0)\n\t\t\t\trow:SetMax(data and data.max or 1)\n\t\t\t\trow:SetDecimals(data and data.decimals or 0)\n\t\t\tend\n\n\t\t\trow:SetValue(value, true)\n\t\t\trow:SetShowReset(value != v.default, k, v.default)\n\n\t\t\trow.OnValueChanged = function(panel)\n\t\t\t\tlocal newValue = ix.util.SanitizeType(type, panel:GetValue())\n\n\t\t\t\tpanel:SetShowReset(newValue != v.default, k, v.default)\n\n\t\t\t\tnet.Start(\"ixConfigSet\")\n\t\t\t\t\tnet.WriteString(k)\n\t\t\t\t\tnet.WriteType(newValue)\n\t\t\t\tnet.SendToServer()\n\t\t\tend\n\n\t\t\trow.OnResetClicked = function(panel)\n\t\t\t\tpanel:SetValue(v.default, true)\n\t\t\t\tpanel:SetShowReset(false)\n\n\t\t\t\tnet.Start(\"ixConfigSet\")\n\t\t\t\t\tnet.WriteString(k)\n\t\t\t\t\tnet.WriteType(v.default)\n\t\t\t\tnet.SendToServer()\n\t\t\tend\n\n\t\t\trow:GetLabel():SetHelixTooltip(function(tooltip)\n\t\t\t\tlocal title = tooltip:AddRow(\"name\")\n\t\t\t\ttitle:SetImportant()\n\t\t\t\ttitle:SetText(k)\n\t\t\t\ttitle:SizeToContents()\n\t\t\t\ttitle:SetMaxWidth(math.max(title:GetMaxWidth(), ScrW() * 0.5))\n\n\t\t\t\tlocal description = tooltip:AddRow(\"description\")\n\t\t\t\tdescription:SetText(v.description)\n\t\t\t\tdescription:SizeToContents()\n\t\t\tend)\n\t\tend\n\tend\n\n\tself:SizeToContents()\nend\n\nvgui.Register(\"ixConfigManager\", PANEL, \"ixSettings\")\n\n-- plugin manager\nPANEL = {}\n\nfunction PANEL:Init()\n\tself:Dock(FILL)\n\tself:SetSearchEnabled(true)\n\n\tself.loadedCategory = L(\"loadedPlugins\")\n\tself.unloadedCategory = L(\"unloadedPlugins\")\n\n\tif (!ix.gui.bReceivedUnloadedPlugins) then\n\t\tnet.Start(\"ixConfigRequestUnloadedList\")\n\t\tnet.SendToServer()\n\tend\n\n\tself:Populate()\nend\n\nfunction PANEL:OnPluginToggled(uniqueID, bEnabled)\n\tnet.Start(\"ixConfigPluginToggle\")\n\t\tnet.WriteString(uniqueID)\n\t\tnet.WriteBool(bEnabled)\n\tnet.SendToServer()\nend\n\nfunction PANEL:Populate()\n\tself:AddCategory(self.loadedCategory)\n\tself:AddCategory(self.unloadedCategory)\n\n\t-- add loaded plugins\n\tfor k, v in SortedPairsByMemberValue(ix.plugin.list, \"name\") do\n\t\tlocal row = self:AddRow(ix.type.bool, self.loadedCategory)\n\t\trow.id = k\n\n\t\trow.setting:SetEnabledText(L(\"on\"):utf8upper())\n\t\trow.setting:SetDisabledText(L(\"off\"):utf8upper())\n\t\trow.setting:SizeToContents()\n\n\t\t-- if this plugin is not in the unloaded list currently, then it's queued for an unload\n\t\trow:SetValue(!ix.plugin.unloaded[k], true)\n\t\trow:SetText(v.name)\n\n\t\trow.OnValueChanged = function(panel, bEnabled)\n\t\t\tself:OnPluginToggled(k, bEnabled)\n\t\tend\n\n\t\trow:GetLabel():SetHelixTooltip(function(tooltip)\n\t\t\tlocal title = tooltip:AddRow(\"name\")\n\t\t\ttitle:SetImportant()\n\t\t\ttitle:SetText(v.name)\n\t\t\ttitle:SizeToContents()\n\t\t\ttitle:SetMaxWidth(math.max(title:GetMaxWidth(), ScrW() * 0.5))\n\n\t\t\tlocal description = tooltip:AddRow(\"description\")\n\t\t\tdescription:SetText(v.description)\n\t\t\tdescription:SizeToContents()\n\t\tend)\n\tend\n\n\tself:UpdateUnloaded(true)\n\tself:SizeToContents()\nend\n\nfunction PANEL:UpdatePlugin(uniqueID, bEnabled)\n\tfor _, v in pairs(self:GetRows()) do\n\t\tif (v.id == uniqueID) then\n\t\t\tv:SetValue(bEnabled, true)\n\t\tend\n\tend\nend\n\n-- called from Populate and from the ixConfigUnloadedList net message\nfunction PANEL:UpdateUnloaded(bNoSizeToContents)\n\tfor _, v in pairs(self:GetRows()) do\n\t\tif (ix.plugin.unloaded[v.id]) then\n\t\t\tv:SetValue(false, true)\n\t\tend\n\tend\n\n\tfor k, v in SortedPairs(ix.plugin.unloaded) do\n\t\tif (ix.plugin.list[k]) then\n\t\t\t-- if this plugin is in the loaded plugins list then it's queued for an unload - don't display it in this category\n\t\t\tcontinue\n\t\tend\n\n\t\tlocal row = self:AddRow(ix.type.bool, self.unloadedCategory)\n\t\trow.id = k\n\n\t\trow.setting:SetEnabledText(L(\"on\"):utf8upper())\n\t\trow.setting:SetDisabledText(L(\"off\"):utf8upper())\n\t\trow.setting:SizeToContents()\n\n\t\trow:SetText(k)\n\t\trow:SetValue(!v, true)\n\n\t\trow.OnValueChanged = function(panel, bEnabled)\n\t\t\tself:OnPluginToggled(k, bEnabled)\n\t\tend\n\tend\n\n\tif (!bNoSizeToContents) then\n\t\tself:SizeToContents()\n\tend\nend\n\nvgui.Register(\"ixPluginManager\", PANEL, \"ixSettings\")\n"
  },
  {
    "path": "gamemode/core/derma/cl_credits.lua",
    "content": "\nlocal CREDITS = {\n\t{\"Alex Grist\", \"76561197979205163\", {\"creditLeadDeveloper\", \"creditManager\"}},\n\t{\"Igor Radovanovic\", \"76561197990111113\", {\"creditLeadDeveloper\", \"creditUIDesigner\"}},\n\t{\"Jaydawg\", \"76561197970371430\", {\"creditTester\"}}\n}\n\nlocal SPECIALS = {\n\t{\n\t\t{\"Luna\", \"76561197988658543\"},\n\t\t{\"Rain GBizzle\", \"76561198036111376\"}\n\t},\n\t{\n\t\t{\"Black Tea\", \"76561197999893894\"}\n\t}\n}\n\nlocal MISC = {\n\t{\"nebulous\", \"Staff members finding bugs and providing input\"},\n\t{\"Contributors\", \"Ongoing support from various developers via GitHub\"},\n\t{\"NutScript\", \"Providing the base framework to build upon\"}\n}\n\nlocal url = \"https://gethelix.co/\"\nlocal padding = 32\n\n-- logo\nlocal PANEL = {}\n\nfunction PANEL:Init()\n\tself:SetTall(ScrH() * 0.60)\n\tself:Dock(TOP)\nend\n\nfunction PANEL:Paint(width, height)\n\tderma.SkinFunc(\"DrawHelixCurved\", width * 0.5, height * 0.5, width * 0.25)\n\n\t-- title\n\tsurface.SetFont(\"ixIntroSubtitleFont\")\n\tlocal text = L(\"helix\"):lower()\n\tlocal textWidth, textHeight = surface.GetTextSize(text)\n\n\tsurface.SetTextColor(color_white)\n\tsurface.SetTextPos(width * 0.5 - textWidth * 0.5, height * 0.5 - textHeight * 0.5)\n\tsurface.DrawText(text)\n\n\t-- version\n\tsurface.SetFont(\"ixMenuMiniFont\")\n\tsurface.SetTextColor(200, 200, 200, 255)\n\tsurface.SetTextPos(width * 0.5 + textWidth * 0.5, height * 0.5 - textHeight * 0.5)\n\tsurface.DrawText(GAMEMODE.Version)\nend\n\nvgui.Register(\"ixCreditsLogo\", PANEL, \"Panel\")\n\n-- nametag\nPANEL = {}\n\nfunction PANEL:Init()\n\tself.name = self:Add(\"DLabel\")\n\tself.name:SetFont(\"ixMenuButtonFontThick\")\n\n\tself.avatar = self:Add(\"AvatarImage\")\nend\n\nfunction PANEL:SetName(name)\n\tself.name:SetText(name)\nend\n\nfunction PANEL:SetAvatar(steamid)\n\tself.avatar:SetSteamID(steamid, 64)\nend\n\nfunction PANEL:PerformLayout(width, height)\n\tself.name:SetPos(width - self.name:GetWide(), 0)\n\tself.avatar:MoveLeftOf(self.name, padding * 0.5)\nend\n\nfunction PANEL:SizeToContents()\n\tself.name:SizeToContents()\n\n\tlocal tall = self.name:GetTall()\n\tself.avatar:SetSize(tall, tall)\n\tself:SetSize(self.name:GetWide() + self.avatar:GetWide() + padding * 0.5, self.name:GetTall())\nend\n\nvgui.Register(\"ixCreditsNametag\", PANEL, \"Panel\")\n\n-- name row\nPANEL = {}\n\nfunction PANEL:Init()\n\tself:DockMargin(0, padding, 0, 0)\n\tself:Dock(TOP)\n\n\tself.nametag = self:Add(\"ixCreditsNametag\")\n\n\tself.tags = self:Add(\"DLabel\")\n\tself.tags:SetFont(\"ixMenuButtonFont\")\n\n\tself:SizeToContents()\nend\n\nfunction PANEL:SetName(name)\n\tself.nametag:SetName(name)\nend\n\nfunction PANEL:SetAvatar(steamid)\n\tself.nametag:SetAvatar(steamid)\nend\n\nfunction PANEL:SetTags(tags)\n\tfor i = 1, #tags do\n\t\ttags[i] = L(tags[i])\n\tend\n\n\tself.tags:SetText(table.concat(tags, \"\\n\"))\nend\n\nfunction PANEL:Paint(width, height)\n\tsurface.SetDrawColor(ix.config.Get(\"color\"))\n\tsurface.DrawRect(width * 0.5 - 1, 0, 1, height)\nend\n\nfunction PANEL:PerformLayout(width, height)\n\tself.nametag:SetPos(width * 0.5 - self.nametag:GetWide() - padding, 0)\n\tself.tags:SetPos(width * 0.5 + padding, 0)\nend\n\nfunction PANEL:SizeToContents()\n\tself.nametag:SizeToContents()\n\tself.tags:SizeToContents()\n\n\tself:SetTall(math.max(self.nametag:GetTall(), self.tags:GetTall()))\nend\n\nvgui.Register(\"ixCreditsRow\", PANEL, \"Panel\")\n\nPANEL = {}\n\nfunction PANEL:Init()\n\tself.left = {}\n\tself.right = {}\nend\n\nfunction PANEL:AddLeft(name, steamid)\n\tlocal nametag = self:Add(\"ixCreditsNametag\")\n\tnametag:SetName(name)\n\tnametag:SetAvatar(steamid)\n\tnametag:SizeToContents()\n\n\tself.left[#self.left + 1] = nametag\nend\n\nfunction PANEL:AddRight(name, steamid)\n\tlocal nametag = self:Add(\"ixCreditsNametag\")\n\tnametag:SetName(name)\n\tnametag:SetAvatar(steamid)\n\tnametag:SizeToContents()\n\n\tself.right[#self.right + 1] = nametag\nend\n\nfunction PANEL:PerformLayout(width, height)\n\tlocal y = 0\n\n\tfor _, v in ipairs(self.left) do\n\t\tv:SetPos(width * 0.25 - v:GetWide() * 0.5, y)\n\t\ty = y + v:GetTall() + padding\n\tend\n\n\ty = 0\n\n\tfor _, v in ipairs(self.right) do\n\t\tv:SetPos(width * 0.75 - v:GetWide() * 0.5, y)\n\t\ty = y + v:GetTall() + padding\n\tend\n\n\tif (IsValid(self.center)) then\n\t\tself.center:SetPos(width * 0.5 - self.center:GetWide() * 0.5, y)\n\tend\nend\n\nfunction PANEL:SizeToContents()\n\tlocal heightLeft, heightRight, centerHeight = 0, 0, 0\n\n\tif (#self.left > #self.right) then\n\t\tlocal center = self.left[#self.left]\n\t\tcenterHeight = center:GetTall()\n\n\t\tself.center = center\n\t\tself.left[#self.left] = nil\n\telseif (#self.right > #self.left) then\n\t\tlocal center = self.right[#self.right]\n\t\tcenterHeight = center:GetTall()\n\n\t\tself.center = center\n\t\tself.right[#self.right] = nil\n\tend\n\n\tfor _, v in ipairs(self.left) do\n\t\theightLeft = heightLeft + v:GetTall() + padding\n\tend\n\n\tfor _, v in ipairs(self.right) do\n\t\theightRight = heightRight + v:GetTall() + padding\n\tend\n\n\tself:SetTall(math.max(heightLeft, heightRight) + centerHeight)\nend\n\nvgui.Register(\"ixCreditsSpecials\", PANEL, \"Panel\")\n\nPANEL = {}\n\nfunction PANEL:Init()\n\tself:Add(\"ixCreditsLogo\")\n\n\tlocal link = self:Add(\"DLabel\", self)\n\tlink:SetFont(\"ixMenuMiniFont\")\n\tlink:SetTextColor(Color(200, 200, 200, 255))\n\tlink:SetText(url)\n\tlink:SetContentAlignment(5)\n\tlink:Dock(TOP)\n\tlink:SizeToContents()\n\tlink:SetMouseInputEnabled(true)\n\tlink:SetCursor(\"hand\")\n\tlink.OnMousePressed = function()\n\t\tgui.OpenURL(url)\n\tend\n\n\tfor _, v in ipairs(CREDITS) do\n\t\tlocal row = self:Add(\"ixCreditsRow\")\n\t\trow:SetName(v[1])\n\t\trow:SetAvatar(v[2])\n\t\trow:SetTags(v[3])\n\t\trow:SizeToContents()\n\tend\n\n\tlocal specials = self:Add(\"ixLabel\")\n\tspecials:SetFont(\"ixMenuButtonFont\")\n\tspecials:SetText(L(\"creditSpecial\"):utf8upper())\n\tspecials:SetTextColor(ix.config.Get(\"color\"))\n\tspecials:SetDropShadow(1)\n\tspecials:SetKerning(16)\n\tspecials:SetContentAlignment(5)\n\tspecials:DockMargin(0, padding * 2, 0, padding)\n\tspecials:Dock(TOP)\n\tspecials:SizeToContents()\n\n\tlocal specialList = self:Add(\"ixCreditsSpecials\")\n\tspecialList:DockMargin(0, padding, 0, 0)\n\tspecialList:Dock(TOP)\n\n\tfor _, v in ipairs(SPECIALS[1]) do\n\t\tspecialList:AddLeft(v[1], v[2])\n\tend\n\n\tfor _, v in ipairs(SPECIALS[2]) do\n\t\tspecialList:AddRight(v[1], v[2])\n\tend\n\n\tspecialList:SizeToContents()\n\n\t-- less more padding if there's a center column nametag\n\tif (IsValid(specialList.center)) then\n\t\tspecialList:DockMargin(0, padding, 0, padding)\n\tend\n\n\tfor _, v in ipairs(MISC) do\n\t\tlocal title = self:Add(\"DLabel\")\n\t\ttitle:SetFont(\"ixMenuButtonFontThick\")\n\t\ttitle:SetText(v[1])\n\t\ttitle:SetContentAlignment(5)\n\t\ttitle:SizeToContents()\n\t\ttitle:DockMargin(0, padding, 0, 0)\n\t\ttitle:Dock(TOP)\n\n\t\tlocal description = self:Add(\"DLabel\")\n\t\tdescription:SetFont(\"ixSmallTitleFont\")\n\t\tdescription:SetText(v[2])\n\t\tdescription:SetContentAlignment(5)\n\t\tdescription:SizeToContents()\n\t\tdescription:Dock(TOP)\n\tend\n\n\tself:Dock(TOP)\n\tself:SizeToContents()\nend\n\nfunction PANEL:SizeToContents()\n\tlocal height = padding\n\n\tfor _, v in pairs(self:GetChildren()) do\n\t\tlocal _, top, _, bottom = v:GetDockMargin()\n\t\theight = height + v:GetTall() + top + bottom\n\tend\n\n\tself:SetTall(height)\nend\n\nvgui.Register(\"ixCredits\", PANEL, \"Panel\")\n\nhook.Add(\"PopulateHelpMenu\", \"ixCredits\", function(tabs)\n\ttabs[\"credits\"] = function(container)\n\t\tcontainer:Add(\"ixCredits\")\n\tend\nend)\n"
  },
  {
    "path": "gamemode/core/derma/cl_deathscreen.lua",
    "content": "\nlocal PANEL = {}\n\nfunction PANEL:Init()\n\tlocal scrW, scrH = ScrW(), ScrH()\n\n\tself:SetSize(scrW, scrH)\n\tself:SetPos(0, 0)\n\n\tlocal text = string.utf8upper(L(\"youreDead\"))\n\n\tsurface.SetFont(\"ixMenuButtonHugeFont\")\n\tlocal textW, textH = surface.GetTextSize(text)\n\n\tself.label = self:Add(\"DLabel\")\n\tself.label:SetPaintedManually(true)\n\tself.label:SetPos(scrW * 0.5 - textW * 0.5, scrH * 0.5 - textH * 0.5)\n\tself.label:SetFont(\"ixMenuButtonHugeFont\")\n\tself.label:SetText(text)\n\tself.label:SizeToContents()\n\n\tself.progress = 0\n\n\tself:CreateAnimation(ix.config.Get(\"spawnTime\", 5), {\n\t\tbIgnoreConfig = true,\n\t\ttarget = {progress = 1},\n\n\t\tOnComplete = function(animation, panel)\n\t\t\tif (!panel:IsClosing()) then\n\t\t\t\tpanel:Close()\n\t\t\tend\n\t\tend\n\t})\nend\n\nfunction PANEL:Think()\n\tself.label:SetAlpha(((self.progress - 0.3) / 0.3) * 255)\nend\n\nfunction PANEL:IsClosing()\n\treturn self.bIsClosing\nend\n\nfunction PANEL:Close()\n\tself.bIsClosing = true\n\n\tself:CreateAnimation(2, {\n\t\tindex = 2,\n\t\tbIgnoreConfig = true,\n\t\ttarget = {progress = 0},\n\n\t\tOnComplete = function(animation, panel)\n\t\t\tpanel:Remove()\n\t\tend\n\t})\nend\n\nfunction PANEL:Paint(width, height)\n\tderma.SkinFunc(\"PaintDeathScreenBackground\", self, width, height, self.progress)\n\t\tself.label:PaintManual()\n\tderma.SkinFunc(\"PaintDeathScreen\", self, width, height, self.progress)\nend\n\nvgui.Register(\"ixDeathScreen\", PANEL, \"Panel\")\n"
  },
  {
    "path": "gamemode/core/derma/cl_dev_icon.lua",
    "content": "--Icon Editor Base and Math Scale Functions from: https://github.com/TeslaCloud/flux-ce/tree/master\n\nlocal scaleFactorX = 1 / 1920\nlocal scaleFactorY = 1 / 1080\n\nlocal function scale(size)\n    return math.floor(size * (ScrH() * scaleFactorY))\nend\n\nlocal function scale_x(size)\n    return math.floor(size * (ScrW() * scaleFactorX))\nend\n\nlocal PANEL = {}\n\nfunction PANEL:Init()\n    if (IsValid(ix.gui.dev_icon)) then\n        ix.gui.dev_icon:Remove()\n    end\n\n    ix.gui.dev_icon = self\n    local pW, pH = ScrW() * 0.6, ScrH() * 0.6\n    self:SetSize(pW, pH)\n    self:MakePopup()\n    self:Center()\n\n    local buttonSize = scale(48)\n\n    self.model = vgui.Create(\"DAdjustableModelPanel\", self)\n    self.model:SetSize(pW * 0.5, pH - buttonSize)\n    self.model:Dock(LEFT)\n    self.model:DockMargin(0, 0, 0, buttonSize + scale(8))\n    self.model:SetModel(\"models/props_borealis/bluebarrel001.mdl\")\n    self.model:SetLookAt(Vector(0, 0, 0))\n\n    self.model.LayoutEntity = function() end\n\n    local x = scale_x(4)\n    self.best = vgui.Create(\"DButton\", self)\n    self.best:SetSize(buttonSize, buttonSize)\n    self.best:SetPos(x, pH - buttonSize - scale(4))\n    self.best:SetFont(\"ixIconsMenuButton\")\n    self.best:SetText(\"b\")\n    self.best:SetTooltip(L(\"iconEditorAlignBest\"))\n\n    self.best.DoClick = function()\n        local entity = self.model:GetEntity()\n        local pos = entity:GetPos()\n        local camData = PositionSpawnIcon(entity, pos)\n\n        if (camData) then\n            self.model:SetCamPos(camData.origin)\n            self.model:SetFOV(camData.fov)\n            self.model:SetLookAng(camData.angles)\n        end\n    end\n\n    x = x + buttonSize + scale_x(4)\n\n    self.front = vgui.Create(\"DButton\", self)\n    self.front:SetSize(buttonSize, buttonSize)\n    self.front:SetPos(x, pH - buttonSize - scale(4))\n    self.front:SetFont(\"ixIconsMenuButton\")\n    self.front:SetText(\"m\")\n    self.front:SetTooltip(L(\"iconEditorAlignFront\"))\n\n    self.front.DoClick = function()\n        local entity = self.model:GetEntity()\n        local pos = entity:GetPos()\n        local camPos = pos + Vector(-200, 0, 0)\n        self.model:SetCamPos(camPos)\n        self.model:SetFOV(45)\n        self.model:SetLookAng((camPos * -1):Angle())\n    end\n\n    x = x + buttonSize + scale_x(4)\n\n    self.above = vgui.Create(\"DButton\", self)\n    self.above:SetSize(buttonSize, buttonSize)\n    self.above:SetPos(x, pH - buttonSize - scale(4))\n    self.above:SetFont(\"ixIconsMenuButton\")\n    self.above:SetText(\"u\")\n    self.above:SetTooltip(L(\"iconEditorAlignAbove\"))\n\n    self.above.DoClick = function()\n        local entity = self.model:GetEntity()\n        local pos = entity:GetPos()\n        local camPos = pos + Vector(0, 0, 200)\n        self.model:SetCamPos(camPos)\n        self.model:SetFOV(45)\n        self.model:SetLookAng((camPos * -1):Angle())\n    end\n\n    x = x + buttonSize + scale_x(4)\n\n    self.right = vgui.Create(\"DButton\", self)\n    self.right:SetSize(buttonSize, buttonSize)\n    self.right:SetPos(x, pH - buttonSize - scale(4))\n    self.right:SetFont(\"ixIconsMenuButton\")\n    self.right:SetText(\"t\")\n    self.right:SetTooltip(L(\"iconEditorAlignRight\"))\n\n    self.right.DoClick = function()\n        local entity = self.model:GetEntity()\n        local pos = entity:GetPos()\n        local camPos = pos + Vector(0, 200, 0)\n        self.model:SetCamPos(camPos)\n        self.model:SetFOV(45)\n        self.model:SetLookAng((camPos * -1):Angle())\n    end\n\n    x = x + buttonSize + scale_x(4)\n\n    self.center = vgui.Create(\"DButton\", self)\n    self.center:SetSize(buttonSize, buttonSize)\n    self.center:SetPos(x, pH - buttonSize - scale(4))\n    self.center:SetFont(\"ixIconsMenuButton\")\n    self.center:SetText(\"T\")\n    self.center:SetTooltip(L(\"iconEditorAlignCenter\"))\n\n    self.center.DoClick = function()\n        local entity = self.model:GetEntity()\n        local pos = entity:GetPos()\n        self.model:SetCamPos(pos)\n        self.model:SetFOV(45)\n        self.model:SetLookAng(Angle(0, -180, 0))\n    end\n\n    self.best:DoClick()\n    self.preview = vgui.Create(\"DPanel\", self)\n    self.preview:Dock(FILL)\n    self.preview:DockMargin(scale_x(4), 0, 0, 0)\n    self.preview:DockPadding(scale_x(4), scale(4), scale_x(4), scale(4))\n\n    self.preview.Paint = function(pnl, w, h)\n        draw.RoundedBox(0, 0, 0, w, h, Color(0, 0, 0, 100))\n    end\n\n    self.modelLabel = self.preview:Add(\"DLabel\")\n    self.modelLabel:Dock(TOP)\n    self.modelLabel:SetText(\"Model\")\n    self.modelLabel:SetFont(\"ixMenuButtonFontSmall\")\n    self.modelLabel:DockMargin(4, 4, 4, 4)\n\n    self.modelPath = vgui.Create(\"ixTextEntry\", self.preview)\n    self.modelPath:SetValue(self.model:GetModel())\n    self.modelPath:Dock(TOP)\n    self.modelPath:SetFont(\"ixMenuButtonFontSmall\")\n    self.modelPath:SetPlaceholderText(\"Model...\")\n\n    self.modelPath.OnEnter = function(pnl)\n        local model = pnl:GetValue()\n\n        if (model and model != \"\") then\n            self.model:SetModel(model)\n            self.item:Rebuild()\n        end\n    end\n\n    self.modelPath.OnLoseFocus = function(pnl)\n        local model = pnl:GetValue()\n\n        if (model and model != \"\") then\n            self.model:SetModel(model)\n            self.item:Rebuild()\n        end\n    end\n\n    self.width = vgui.Create(\"ixSettingsRowNumber\", self.preview)\n    self.width:Dock(TOP)\n    self.width:SetText(L(\"iconEditorWidth\"))\n    self.width:SetMin(1)\n    self.width:SetMax(24)\n    self.width:SetDecimals(0)\n    self.width:SetValue(1)\n\n    self.width.OnValueChanged = function(pnl, value)\n        self.item:Rebuild()\n    end\n\n    self.height = vgui.Create(\"ixSettingsRowNumber\", self.preview)\n    self.height:Dock(TOP)\n    self.height:SetText(L(\"iconEditorHeight\"))\n    self.height:SetMin(1)\n    self.height:SetMax(24)\n    self.height:SetDecimals(0)\n    self.height:SetValue(1)\n\n    self.height.OnValueChanged = function(pnl, value)\n        self.item:Rebuild()\n    end\n\n    self.itemPanel = vgui.Create(\"DPanel\", self.preview)\n    self.itemPanel:Dock(FILL)\n\n    self.itemPanel.Paint = function(pnl, w, h)\n        draw.RoundedBox(0, 0, 0, w, h, Color(0, 0, 0, 100))\n    end\n\n    self.item = vgui.Create(\"DModelPanel\", self.itemPanel)\n    self.item:SetMouseInputEnabled(false)\n    self.item.LayoutEntity = function() end\n\n    self.item.PaintOver = function(pnl, w, h)\n        surface.SetDrawColor(color_white)\n        surface.DrawOutlinedRect(0, 0, w, h)\n    end\n\n    self.item.Rebuild = function(pnl)\n        local slotSize = 64\n        local padding = scale(2)\n        local slotWidth, slotHeight = math.Round(self.width:GetValue()), math.Round(self.height:GetValue())\n        local w, h = slotWidth * (slotSize + padding) - padding, slotHeight * (slotSize + padding) - padding\n        pnl:SetModel(self.model:GetModel())\n        pnl:SetCamPos(self.model:GetCamPos())\n        pnl:SetFOV(self.model:GetFOV())\n        pnl:SetLookAng(self.model:GetLookAng())\n        pnl:SetSize(w, h)\n        pnl:Center()\n    end\n\n    self.item:Rebuild()\n\n    timer.Create(\"ix_icon_editor_update\", 0.5, 0, function()\n        if IsValid(self) and IsValid(self.model) then\n            self.item:Rebuild()\n        else\n            timer.Remove(\"ix_icon_editor_update\")\n        end\n    end)\n\n    self.copy = vgui.Create(\"DButton\", self)\n    self.copy:SetSize(buttonSize, buttonSize)\n    self.copy:SetPos(pW - buttonSize - scale_x(12), pH - buttonSize - scale(12))\n    self.copy:SetFont(\"ixIconsMenuButton\")\n    self.copy:SetText(\"}\")\n    self.copy:SetTooltip(L(\"iconEditorCopy\"))\n    self.copy.DoClick = function()\n        local camPos = self.model:GetCamPos()\n        local camAng = self.model:GetLookAng()\n\t\tlocal str = \"ITEM.model = \\\"\"..self.model:GetModel()..\"\\\"\\n\"\n\t\t..\"ITEM.width = \"..math.Round(self.width:GetValue())..\"\\n\"\n\t\t..\"ITEM.height = \"..math.Round(self.height:GetValue())..\"\\n\"\n\t\t..\"ITEM.iconCam = {\\n\"\n\t\t..\"\\tpos = Vector(\"..math.Round(camPos.x, 2)..\", \"..math.Round(camPos.y, 2)..\", \"..math.Round(camPos.z, 2)..\"),\\n\"\n\t\t..\"\\tang = Angle(\"..math.Round(camAng.p, 2)..\", \"..math.Round(camAng.y, 2)..\", \"..math.Round(camAng.r, 2)..\"),\\n\"\n\t\t..\"\\tfov = \"..math.Round(self.model:GetFOV(), 2)..\"\\n\"\n\t\t..\"}\\n\"\n\n        SetClipboardText(str)\n        ix.util.Notify(L(\"iconEditorCopied\"))\n    end\nend\n\nvgui.Register(\"ix_icon_editor\", PANEL, \"DFrame\")\n\nconcommand.Add(\"ix_dev_icon\", function()\n    if (LocalPlayer():IsAdmin()) then\n        vgui.Create(\"ix_icon_editor\")\n    end\nend)\n"
  },
  {
    "path": "gamemode/core/derma/cl_entitymenu.lua",
    "content": "\nlocal animationTime = 1\nlocal padding = 32\n\n-- entity menu button\nDEFINE_BASECLASS(\"ixMenuButton\")\nlocal PANEL = {}\n\nAccessorFunc(PANEL, \"callback\", \"Callback\")\n\nfunction PANEL:Init()\n\tself:SetTall(ScrH() * 0.1)\n\tself:Dock(TOP)\nend\n\nfunction PANEL:DoClick()\n\tlocal bStatus = true\n\tlocal parent = ix.menu.panel\n\tlocal entity = parent:GetEntity()\n\n\tif (parent.bClosing) then\n\t\treturn\n\tend\n\n\tif (isfunction(self.callback)) then\n\t\tbStatus = self.callback()\n\tend\n\n\tif (bStatus != false) then\n\t\tix.menu.NetworkChoice(entity, self.originalText, bStatus)\n\tend\n\n\tparent:Remove()\nend\n\nfunction PANEL:SetText(text)\n\tself.originalText = text\n\tBaseClass.SetText(self, text)\nend\n\nvgui.Register(\"ixEntityMenuButton\", PANEL, \"ixMenuButton\")\n\n-- entity menu list\nDEFINE_BASECLASS(\"EditablePanel\")\nPANEL = {}\n\nfunction PANEL:Init()\n\tself.list = {}\nend\n\nfunction PANEL:AddOption(text, callback)\n\tlocal panel = self:Add(\"ixEntityMenuButton\")\n\tpanel:SetText(text)\n\tpanel:SetCallback(callback)\n\tpanel:Dock(TOP)\n\n\tif (self.bPaintedManually) then\n\t\tpanel:SetPaintedManually(true)\n\tend\n\n\tself.list[#self.list + 1] = panel\nend\n\nfunction PANEL:SizeToContents()\n\tlocal height = 0\n\n\tfor i = 1, #self.list do\n\t\theight = height + self.list[i]:GetTall()\n\tend\n\n\tself:SetSize(ScrW() * 0.5 - padding * 2, height)\nend\n\nfunction PANEL:Center()\n\tlocal parent = self:GetParent()\n\n\tself:SetPos(\n\t\tScrW() * 0.5 + padding,\n\t\tparent:GetTall() * 0.5 - self:GetTall() * 0.5\n\t)\nend\n\nfunction PANEL:SetPaintedManually(bValue)\n\tif (bValue) then\n\t\tfor i = 1, #self.list do\n\t\t\tself.list[i]:SetPaintedManually(true)\n\t\tend\n\n\t\tself.bPaintedManually = true\n\tend\n\n\tBaseClass.SetPaintedManually(self, bValue)\nend\n\nfunction PANEL:PaintManual()\n\tBaseClass.PaintManual(self)\n\tlocal list = self.list\n\n\tfor i = 1, #list do\n\t\tlist[i]:PaintManual()\n\tend\nend\n\nvgui.Register(\"ixEntityMenuList\", PANEL, \"EditablePanel\")\n\n-- entity menu\nDEFINE_BASECLASS(\"EditablePanel\")\nPANEL = {}\n\nAccessorFunc(PANEL, \"entity\", \"Entity\")\nAccessorFunc(PANEL, \"bClosing\", \"IsClosing\", FORCE_BOOL)\nAccessorFunc(PANEL, \"desiredHeight\", \"DesiredHeight\", FORCE_NUMBER)\n\nfunction PANEL:Init()\n\tif (IsValid(ix.menu.panel)) then\n\t\tself:Remove()\n\t\treturn\n\tend\n\n\t-- close entity tooltip if it's open\n\tif (IsValid(ix.gui.entityInfo)) then\n\t\tix.gui.entityInfo:Remove()\n\tend\n\n\tix.menu.panel = self\n\n\tself:SetSize(ScrW(), ScrH())\n\tself:SetPos(0, 0)\n\n\tself.list = self:Add(\"ixEntityMenuList\")\n\tself.list:SetPaintedManually(true)\n\n\tself.desiredHeight = 0\n\tself.blur = 0\n\tself.alpha = 1\n\tself.bClosing = false\n\tself.lastPosition = vector_origin\n\n\tself:CreateAnimation(animationTime, {\n\t\ttarget = {blur = 1},\n\t\teasing = \"outQuint\"\n\t})\n\n\tself:MakePopup()\nend\n\nfunction PANEL:SetOptions(options)\n\tfor k, v in pairs(options) do\n\t\tself.list:AddOption(k, v)\n\tend\n\n\tself.list:SizeToContents()\n\tself.list:Center()\nend\n\nfunction PANEL:GetApproximateScreenHeight(entity, distanceSqr)\n\treturn IsValid(entity) and\n\t\t(entity:BoundingRadius() * (entity:IsPlayer() and 1.5 or 1) or 0) / math.sqrt(distanceSqr) * self:GetTall() or 0\nend\n\nfunction PANEL:Think()\n\tlocal entity = self.entity\n\tlocal distance = 0\n\n\tif (IsValid(entity)) then\n\t\tlocal position = entity:GetPos()\n\t\tdistance = LocalPlayer():GetShootPos():DistToSqr(position)\n\n\t\tif (distance > 65536) then\n\t\t\tself:Remove()\n\t\t\treturn\n\t\tend\n\n\t\tself.lastPosition = position\n\tend\n\n\tself.desiredHeight = math.max(self.list:GetTall() + padding * 2, self:GetApproximateScreenHeight(entity, distance))\nend\n\nfunction PANEL:Paint(width, height) -- luacheck: ignore 312\n\tlocal selfHalf = self:GetTall() * 0.5\n\tlocal entity = self.entity\n\n\theight = self.desiredHeight + padding * 2\n\twidth = self.blur * width\n\n\tlocal y = selfHalf - height * 0.5\n\n\tDisableClipping(true) -- for cheap blur\n\trender.SetScissorRect(0, y, width, y + height, true)\n\t\tif (IsValid(entity)) then\n\t\t\tcam.Start3D()\n\t\t\t\tix.util.ResetStencilValues()\n\t\t\t\trender.SetStencilEnable(true)\n\t\t\t\tcam.IgnoreZ(true)\n\t\t\t\t\trender.SetStencilWriteMask(29)\n\t\t\t\t\trender.SetStencilTestMask(29)\n\t\t\t\t\trender.SetStencilReferenceValue(29)\n\n\t\t\t\t\trender.SetStencilCompareFunction(STENCIL_ALWAYS)\n\t\t\t\t\trender.SetStencilPassOperation(STENCIL_REPLACE)\n\t\t\t\t\trender.SetStencilFailOperation(STENCIL_KEEP)\n\t\t\t\t\trender.SetStencilZFailOperation(STENCIL_KEEP)\n\n\t\t\t\t\tentity:DrawModel()\n\n\t\t\t\t\trender.SetStencilCompareFunction(STENCIL_NOTEQUAL)\n\t\t\t\t\trender.SetStencilPassOperation(STENCIL_KEEP)\n\n\t\t\t\t\tcam.Start2D()\n\t\t\t\t\t\tix.util.DrawBlur(self, 10)\n\t\t\t\t\tcam.End2D()\n\t\t\t\tcam.IgnoreZ(false)\n\t\t\t\trender.SetStencilEnable(false)\n\t\t\tcam.End3D()\n\t\telse\n\t\t\tix.util.DrawBlur(self, 10)\n\t\tend\n\trender.SetScissorRect(0, 0, 0, 0, false)\n\tDisableClipping(false)\n\n\t-- scissor again because 3d rendering messes with the clipping apparently?\n\trender.SetScissorRect(0, y, width, y + height, true)\n\t\tsurface.SetDrawColor(ix.config.Get(\"color\"))\n\t\tsurface.DrawRect(ScrW() * 0.5, y + padding, 1, height - padding * 2)\n\n\t\tself.list:PaintManual()\n\trender.SetScissorRect(0, 0, 0, 0, false)\nend\n\nfunction PANEL:GetOverviewInfo(origin, angles)\n\tlocal entity = self.entity\n\n\tif (IsValid(entity)) then\n\t\tlocal radius = entity:BoundingRadius() * (entity:IsPlayer() and 0.5 or 1)\n\t\tlocal center = entity:LocalToWorld(entity:OBBCenter()) + LocalPlayer():GetRight() * radius\n\n\t\treturn LerpAngle(self.bClosing and self.alpha or self.blur, angles, (center - origin):Angle())\n\tend\n\n\treturn angles\nend\n\nfunction PANEL:OnMousePressed(code)\n\tif (code == MOUSE_LEFT) then\n\t\tself:Remove()\n\tend\nend\n\nfunction PANEL:Remove()\n\tif (self.bClosing) then\n\t\treturn\n\tend\n\n\tself.bClosing = true\n\n\tself:SetMouseInputEnabled(false)\n\tself:SetKeyboardInputEnabled(false)\n\tgui.EnableScreenClicker(false)\n\n\tself:CreateAnimation(animationTime * 0.5, {\n\t\ttarget = {alpha = 0},\n\t\tindex = 2,\n\t\teasing = \"outQuint\",\n\n\t\tThink = function(animation, panel)\n\t\t\tpanel:SetAlpha(panel.alpha * 255)\n\t\tend,\n\n\t\tOnComplete = function(animation, panel)\n\t\t\tix.menu.panel = nil\n\t\t\tBaseClass.Remove(self)\n\t\tend\n\t})\nend\n\nvgui.Register(\"ixEntityMenu\", PANEL, \"EditablePanel\")\n"
  },
  {
    "path": "gamemode/core/derma/cl_generic.lua",
    "content": "\n-- generic panels that are applicable anywhere\n\n-- used for prominent text entries\nDEFINE_BASECLASS(\"DTextEntry\")\nlocal PANEL = {}\n\nAccessorFunc(PANEL, \"backgroundColor\", \"BackgroundColor\")\n\nfunction PANEL:Init()\n\tself:SetPaintBackground(false)\n\tself:SetTextColor(color_white)\n\n\tself.backgroundColor = Color(255, 255, 255, 25)\nend\n\nfunction PANEL:SetFont(font)\n\tsurface.SetFont(font)\n\tlocal _, height = surface.GetTextSize(\"W@\")\n\n\tself:SetTall(height)\n\tBaseClass.SetFont(self, font)\nend\n\nfunction PANEL:Paint(width, height)\n\tderma.SkinFunc(\"DrawImportantBackground\", 0, 0, width, height, self.backgroundColor)\n\tBaseClass.Paint(self, width, height)\nend\n\nvgui.Register(\"ixTextEntry\", PANEL, \"DTextEntry\")\n\n-- similar to a frame, but is mainly used for grouping panels together in a list\nPANEL = {}\n\nAccessorFunc(PANEL, \"text\", \"Text\", FORCE_STRING)\nAccessorFunc(PANEL, \"color\", \"Color\")\n\nfunction PANEL:Init()\n\tself.text = \"\"\n\tself.paddingTop = 32\n\n\tlocal skin = self:GetSkin()\n\n\tif (skin and skin.fontCategoryBlur) then\n\t\tsurface.SetFont(skin.fontCategoryBlur)\n\t\tself.paddingTop = select(2, surface.GetTextSize(\"W@\")) + 6\n\tend\n\n\tself:DockPadding(1, self.paddingTop, 1, 1)\nend\n\nfunction PANEL:SizeToContents()\n\tlocal height = self.paddingTop + 1\n\n\tfor _, v in ipairs(self:GetChildren()) do\n\t\tif (IsValid(v) and v:IsVisible()) then\n\t\t\tlocal _, top, _, bottom = v:GetDockMargin()\n\n\t\t\theight = height + v:GetTall() + top + bottom\n\t\tend\n\tend\n\n\tself:SetTall(height)\nend\n\nfunction PANEL:Paint(width, height)\n\tderma.SkinFunc(\"PaintCategoryPanel\", self, self.text, self.color)\nend\n\nvgui.Register(\"ixCategoryPanel\", PANEL, \"EditablePanel\")\n\n-- segmented progress bar\nPANEL = {}\n\nAccessorFunc(PANEL, \"font\", \"Font\", FORCE_STRING)\nAccessorFunc(PANEL, \"barColor\", \"BarColor\")\nAccessorFunc(PANEL, \"textColor\", \"TextColor\")\nAccessorFunc(PANEL, \"progress\", \"Progress\", FORCE_NUMBER)\nAccessorFunc(PANEL, \"padding\", \"Padding\", FORCE_NUMBER)\nAccessorFunc(PANEL, \"animationTime\", \"AnimationTime\", FORCE_NUMBER)\nAccessorFunc(PANEL, \"easingType\", \"EasingType\", FORCE_STRING)\n\nfunction PANEL:Init()\n\tself.segments = {}\n\tself.padding = ScrH() * 0.01\n\tself.fraction = 0\n\tself.animationTime = 0.5\n\tself.easingType = \"outQuint\"\n\tself.progress = 0\nend\n\nfunction PANEL:AddSegment(text)\n\tlocal id = #self.segments + 1\n\n\tif (text:sub(1, 1) == \"@\") then\n\t\ttext = L(text:sub(2))\n\tend\n\n\tself.segments[id] = text\n\treturn id\nend\n\nfunction PANEL:AddSegments(...)\n\tlocal segments = {...}\n\n\tfor i = 1, #segments do\n\t\tself:AddSegment(segments[i])\n\tend\nend\n\nfunction PANEL:GetSegments()\n\treturn self.segments\nend\n\nfunction PANEL:SetProgress(segment)\n\tself.progress = math.Clamp(segment, 0, #self.segments)\n\n\tself:CreateAnimation(self.animationTime, {\n\t\ttarget = {fraction = self.progress / #self.segments},\n\t\teasing = self.easingType\n\t})\nend\n\nfunction PANEL:IncrementProgress(amount)\n\tself:SetProgress(self.progress + (amount or 1))\nend\n\nfunction PANEL:DecrementProgress(amount)\n\tself:SetProgress(self.progress - (amount or 1))\nend\n\nfunction PANEL:GetFraction()\n\treturn self.fraction\nend\n\nfunction PANEL:SizeToContents()\n\tself:SetTall(draw.GetFontHeight(self.font or self:GetSkin().fontSegmentedProgress) + self.padding)\nend\n\nfunction PANEL:Paint(width, height)\n\tderma.SkinFunc(\"PaintSegmentedProgressBackground\", self, width, height)\n\n\tif (#self.segments > 0) then\n\t\tderma.SkinFunc(\"PaintSegmentedProgress\", self, width, height)\n\tend\nend\n\nvgui.Register(\"ixSegmentedProgress\", PANEL, \"Panel\")\n\n-- list of labelled information\nPANEL = {}\n\nAccessorFunc(PANEL, \"labelColor\", \"LabelColor\")\nAccessorFunc(PANEL, \"textColor\", \"TextColor\")\nAccessorFunc(PANEL, \"list\", \"List\")\nAccessorFunc(PANEL, \"minWidth\", \"MinimumWidth\", FORCE_NUMBER)\n\nfunction PANEL:Init()\n\tself.label = self:Add(\"DLabel\")\n\tself.label:SetFont(\"ixMediumFont\")\n\tself.label:SetExpensiveShadow(1)\n\tself.label:SetTextColor(color_white)\n\tself.label:SetText(\"Label\")\n\tself.label:SetContentAlignment(5)\n\tself.label:Dock(LEFT)\n\tself.label:DockMargin(0, 0, 4, 0)\n\tself.label:SizeToContents()\n\tself.label.Paint = function(this, width, height)\n\t\tderma.SkinFunc(\"PaintListRow\", this, width, height)\n\tend\n\n\tself.text = self:Add(\"DLabel\")\n\tself.text:SetFont(\"ixMediumLightFont\")\n\tself.text:SetTextColor(color_white)\n\tself.text:SetText(\"Text\")\n\tself.text:SetTextInset(8, 0)\n\tself.text:Dock(FILL)\n\tself.text:DockMargin(4, 0, 0, 0)\n\tself.text:SizeToContents()\n\tself.text.Paint = function(this, width, height)\n\t\tderma.SkinFunc(\"PaintListRow\", this, width, height)\n\tend\n\n\tself:DockMargin(0, 0, 0, 8)\n\n\tself.list = {}\n\tself.minWidth = 100\nend\n\nfunction PANEL:SetRightPanel(panel)\n\tself.text:Remove()\n\n\tself.text = self:Add(panel)\n\tself.text:Dock(FILL)\n\tself.text:DockMargin(8, 4, 4, 4)\n\tself.text:SizeToContents()\nend\n\nfunction PANEL:SetList(list, bNoAdd)\n\tif (!bNoAdd) then\n\t\tlist[#list + 1] = self\n\tend\n\n\tself.list = list\nend\n\nfunction PANEL:UpdateLabelWidths()\n\tlocal maxWidth = self.label:GetWide()\n\n\tfor i = 1, #self.list do\n\t\tmaxWidth = math.max(self.list[i]:GetLabelWidth(), maxWidth)\n\tend\n\n\tmaxWidth = math.max(self.minWidth, maxWidth)\n\n\tfor i = 1, #self.list do\n\t\tself.list[i]:SetLabelWidth(maxWidth)\n\tend\nend\n\nfunction PANEL:SetLabelColor(color)\n\tself.label:SetTextColor(color)\nend\n\nfunction PANEL:SetTextColor(color)\n\tself.text:SetTextColor(color)\nend\n\nfunction PANEL:SetLabelText(text)\n\tself.label:SetText(text)\n\tself.label:SizeToContents()\n\n\tself:UpdateLabelWidths()\nend\n\nfunction PANEL:SetText(text)\n\tself.text:SetText(text)\n\tself.text:SizeToContents()\nend\n\nfunction PANEL:SetLabelWidth(width)\n\tself.label:SetWide(width)\nend\n\nfunction PANEL:GetLabelWidth(bWithoutMargin)\n\tif (!bWithoutMargin) then\n\t\treturn self.label:GetWide()\n\tend\n\n\tlocal left, _, right, _ = self.label:GetDockMargin()\n\treturn self.label:GetWide() + left + right\nend\n\nfunction PANEL:SizeToContents()\n\tself:SetTall(math.max(self.label:GetTall(), self.text:GetTall()) + 8)\nend\n\nvgui.Register(\"ixListRow\", PANEL, \"Panel\")\n\n-- alternative checkbox\nDEFINE_BASECLASS(\"EditablePanel\")\nPANEL = {}\n\nAccessorFunc(PANEL, \"enabledText\", \"EnabledText\", FORCE_STRING)\nAccessorFunc(PANEL, \"disabledText\", \"DisabledText\", FORCE_STRING)\nAccessorFunc(PANEL, \"font\", \"Font\", FORCE_STRING)\nAccessorFunc(PANEL, \"bChecked\", \"Checked\", FORCE_BOOL)\nAccessorFunc(PANEL, \"animationTime\", \"AnimationTime\", FORCE_NUMBER)\nAccessorFunc(PANEL, \"labelPadding\", \"LabelPadding\", FORCE_NUMBER)\n\nPANEL.GetValue = PANEL.GetChecked\n\nfunction PANEL:Init()\n\tself:SetMouseInputEnabled(true)\n\tself:SetCursor(\"hand\")\n\n\tself.enabledText = L(\"yes\"):utf8upper()\n\tself.disabledText = L(\"no\"):utf8upper()\n\tself.font = \"ixMenuButtonFont\"\n\tself.animationTime = 0.5\n\tself.bChecked = false\n\tself.labelPadding = 8\n\tself.animationOffset = 0\n\n\tself:SizeToContents()\nend\n\nfunction PANEL:SizeToContents()\n\tBaseClass.SizeToContents(self)\n\n\tsurface.SetFont(self.font)\n\tself:SetWide(math.max(surface.GetTextSize(self.enabledText), surface.GetTextSize(self.disabledText)) + self.labelPadding)\nend\n\n-- can be overidden to change audio params\nfunction PANEL:GetAudioFeedback()\n\treturn \"weapons/ar2/ar2_empty.wav\", 75, self.bChecked and 150 or 125, 0.25\nend\n\nfunction PANEL:EmitFeedback()\n\tLocalPlayer():EmitSound(self:GetAudioFeedback())\nend\n\nfunction PANEL:SetChecked(bChecked, bInstant)\n\tself.bChecked = tobool(bChecked)\n\n\tself:CreateAnimation(bInstant and 0 or self.animationTime, {\n\t\tindex = 1,\n\t\ttarget = {\n\t\t\tanimationOffset = bChecked and 1 or 0\n\t\t},\n\t\teasing = \"outElastic\"\n\t})\n\n\tif (!bInstant) then\n\t\tself:EmitFeedback()\n\tend\nend\n\nfunction PANEL:OnMousePressed(code)\n\tif (code == MOUSE_LEFT) then\n\t\tself:SetChecked(!self.bChecked)\n\t\tself:DoClick()\n\tend\nend\n\nfunction PANEL:DoClick()\nend\n\nfunction PANEL:Paint(width, height)\n\tsurface.SetDrawColor(derma.GetColor(\"DarkerBackground\", self))\n\tsurface.DrawRect(0, 0, width, height)\n\n\tlocal offset = self.animationOffset\n\tsurface.SetFont(self.font)\n\n\tlocal text = self.disabledText\n\tlocal textWidth, textHeight = surface.GetTextSize(text)\n\tlocal y = offset * -textHeight\n\n\tsurface.SetTextColor(250, 60, 60, 255)\n\tsurface.SetTextPos(width * 0.5 - textWidth * 0.5, y + height * 0.5 - textHeight * 0.5)\n\tsurface.DrawText(text)\n\n\ttext = self.enabledText\n\ty = y + textHeight\n\ttextWidth, textHeight = surface.GetTextSize(text)\n\n\tsurface.SetTextColor(30, 250, 30, 255)\n\tsurface.SetTextPos(width * 0.5 - textWidth * 0.5, y + height * 0.5 - textHeight * 0.5)\n\tsurface.DrawText(text)\nend\n\nvgui.Register(\"ixCheckBox\", PANEL, \"EditablePanel\")\n\n-- alternative num slider\nPANEL = {}\n\nAccessorFunc(PANEL, \"labelPadding\", \"LabelPadding\", FORCE_NUMBER)\n\nfunction PANEL:Init()\n\tself.labelPadding = 8\n\n\tsurface.SetFont(\"ixMenuButtonFont\")\n\tlocal totalWidth = surface.GetTextSize(\"999\") -- start off with 3 digit width\n\n\tself.label = self:Add(\"DLabel\")\n\tself.label:Dock(RIGHT)\n\tself.label:SetWide(totalWidth + self.labelPadding)\n\tself.label:SetContentAlignment(5)\n\tself.label:SetFont(\"ixMenuButtonFont\")\n\tself.label.Paint = function(panel, width, height)\n\t\tsurface.SetDrawColor(derma.GetColor(\"DarkerBackground\", self))\n\t\tsurface.DrawRect(0, 0, width, height)\n\tend\n\tself.label.SizeToContents = function(panel)\n\t\tsurface.SetFont(panel:GetFont())\n\t\tlocal textWidth = surface.GetTextSize(panel:GetText())\n\n\t\tif (textWidth > totalWidth) then\n\t\t\tpanel:SetWide(textWidth + self.labelPadding)\n\t\telseif (panel:GetWide() > totalWidth + self.labelPadding) then\n\t\t\tpanel:SetWide(totalWidth + self.labelPadding)\n\t\tend\n\tend\n\n\tself.slider = self:Add(\"ixSlider\")\n\tself.slider:Dock(FILL)\n\tself.slider:DockMargin(0, 0, 4, 0)\n\tself.slider.OnValueChanged = function(panel)\n\t\tself:OnValueChanged()\n\tend\n\tself.slider.OnValueUpdated = function(panel)\n\t\tself.label:SetText(string.format(\"%0.\" .. tostring(panel:GetDecimals()) .. \"f\", tostring(panel:GetValue())))\n\t\tself.label:SizeToContents()\n\n\t\tself:OnValueUpdated()\n\tend\nend\n\nfunction PANEL:GetLabel()\n\treturn self.label\nend\n\nfunction PANEL:GetSlider()\n\treturn self.slider\nend\n\nfunction PANEL:SetValue(value, bNoNotify)\n\tvalue = tonumber(value) or self.slider:GetMin()\n\n\tself.slider:SetValue(value, bNoNotify)\n\tself.label:SetText(string.format(\"%0.\" .. tostring(self:GetDecimals()) .. \"f\", tostring(self.slider:GetValue())))\n\tself.label:SizeToContents()\nend\n\nfunction PANEL:GetValue()\n\treturn self.slider:GetValue()\nend\n\nfunction PANEL:GetFraction()\n\treturn self.slider:GetFraction()\nend\n\nfunction PANEL:GetVisualFraction()\n\treturn self.slider:GetVisualFraction()\nend\n\nfunction PANEL:SetMin(value)\n\tself.slider:SetMin(value)\nend\n\nfunction PANEL:SetMax(value)\n\tself.slider:SetMax(value)\nend\n\nfunction PANEL:GetMin()\n\treturn self.slider:GetMin()\nend\n\nfunction PANEL:GetMax()\n\treturn self.slider:GetMax()\nend\n\nfunction PANEL:SetDecimals(value)\n\tself.slider:SetDecimals(value)\nend\n\nfunction PANEL:GetDecimals()\n\treturn self.slider:GetDecimals()\nend\n\n-- called when changed by user\nfunction PANEL:OnValueChanged()\nend\n\n-- called when changed while dragging bar\nfunction PANEL:OnValueUpdated()\nend\n\nvgui.Register(\"ixNumSlider\", PANEL, \"Panel\")\n\n-- alternative slider\nPANEL = {}\n\nAccessorFunc(PANEL, \"bDragging\", \"Dragging\", FORCE_BOOL)\nAccessorFunc(PANEL, \"min\", \"Min\", FORCE_NUMBER)\nAccessorFunc(PANEL, \"max\", \"Max\", FORCE_NUMBER)\nAccessorFunc(PANEL, \"decimals\", \"Decimals\", FORCE_NUMBER)\n\nfunction PANEL:Init()\n\tself.min = 0\n\tself.max = 10\n\tself.value = 0\n\tself.visualValue = 0\n\tself.decimals = 0\n\n\tself:SetCursor(\"hand\")\nend\n\nfunction PANEL:SetValue(value, bNoNotify)\n\tself.value = math.Clamp(math.Round(tonumber(value) or self.min, self.decimals), self.min, self.max)\n\tself:ValueUpdated(bNoNotify)\n\n\tif (!bNoNotify) then\n\t\tself:OnValueChanged()\n\tend\nend\n\nfunction PANEL:GetValue()\n\treturn self.value\nend\n\nfunction PANEL:GetFraction()\n\treturn math.Remap(self.value, self.min, self.max, 0, 1)\nend\n\nfunction PANEL:GetVisualFraction()\n\treturn math.Remap(self.visualValue, self.min, self.max, 0, 1)\nend\n\nfunction PANEL:OnMousePressed(key)\n\tif (key == MOUSE_LEFT) then\n\t\tself.bDragging = true\n\t\tself:MouseCapture(true)\n\n\t\tself:OnCursorMoved(self:CursorPos())\n\tend\nend\n\nfunction PANEL:OnMouseReleased(key)\n\tif (self.bDragging) then\n\t\tself:OnValueChanged()\n\tend\n\n\tself.bDragging = false\n\tself:MouseCapture(false)\nend\n\nfunction PANEL:OnCursorMoved(x, y)\n\tif (!self.bDragging) then\n\t\treturn\n\tend\n\n\tx = math.Clamp(x, 0, self:GetWide())\n\tlocal oldValue = self.value\n\n\tself.value = math.Clamp(math.Round(\n\t\tmath.Remap(x / self:GetWide(), 0, 1, self.min, self.max), self.decimals\n\t), self.min, self.max)\n\n\tself:CreateAnimation(0.5, {\n\t\tindex = 1,\n\t\ttarget = {visualValue = self.value},\n\t\teasing = \"outQuint\"\n\t})\n\n\tif (self.value != oldValue) then\n\t\tself:ValueUpdated()\n\tend\nend\n\nfunction PANEL:OnValueChanged()\nend\n\nfunction PANEL:ValueUpdated(bNoNotify)\n\tself:CreateAnimation(bNoNotify and 0 or 0.5, {\n\t\tindex = 1,\n\t\ttarget = {visualValue = self.value},\n\t\teasing = \"outQuint\"\n\t})\n\n\tif (!bNoNotify) then\n\t\tself:OnValueUpdated()\n\tend\nend\n\nfunction PANEL:OnValueUpdated()\nend\n\nfunction PANEL:Paint(width, height)\n\tderma.SkinFunc(\"PaintHelixSlider\", self, width, height)\nend\n\nvgui.Register(\"ixSlider\", PANEL, \"EditablePanel\")\n\n--- Alternative to DLabel that adds extra functionality.\n-- This panel is meant for drawing single-line text. It can add extra kerning (spaces between letters), and it can forcefully\n-- scale the text down to fit the current width, without cutting off any letters. Text scaling is most useful when docking this\n-- this panel without knowing what the width could be. For example, text scaling is used for the character name in the character\n-- status menu.\n-- \tlocal label = vgui.Create(\"ixLabel\")\n-- \tlabel:SetText(\"hello world\")\n-- \tlabel:SetFont(\"ixMenuButtonHugeFont\")\n-- \tlabel:SetContentAlignment(5)\n-- \tlabel:SetTextColor(Color(255, 255, 255, 255))\n-- \tlabel:SetBackgroundColor(Color(200, 30, 30, 255))\n-- \tlabel:SetPadding(8)\n-- \tlabel:SetScaleWidth(true)\n-- \tlabel:SizeToContents()\n-- @panel ixLabel\nPANEL = {}\n\n--- Sets the text for this label to display.\n-- @realm client\n-- @string text Text to display\n-- @function SetText\n\n--- Returns the current text for this panel.\n-- @realm client\n-- @treturn string Current text\n-- @function GetText\nAccessorFunc(PANEL, \"text\", \"Text\", FORCE_STRING)\n\n--- Sets the color of the text to use when drawing.\n-- @realm client\n-- @color color New color to use\n-- @function SetTextColor\n\n--- Returns the current text color for this panel.\n-- @realm client\n-- @treturn color Current text color\n-- @function GetTextColor\nAccessorFunc(PANEL, \"color\", \"TextColor\")\n\n--- Sets the color of the background to draw behind the text.\n-- @realm client\n-- @color color New color to use\n-- @function SetBackgroundColor\n\n--- Returns the current background color for this panel.\n-- @realm client\n-- @treturn color Current background color\n-- @function GetBackgroundColor\nAccessorFunc(PANEL, \"backgroundColor\", \"BackgroundColor\")\n\n--- Sets the spacing between each character of the text in pixels. Set to `0` to disable. Kerning is disabled by default.\n-- @realm client\n-- @number kerning How far apart to draw each letter\n-- @function SetKerning\n\n--- Returns the current kerning for this panel.\n-- @realm client\n-- @treturn number Current kerning\n-- @function GetKerning\nAccessorFunc(PANEL, \"kerning\", \"Kerning\", FORCE_NUMBER)\n\n--- Sets the font used to draw the text.\n-- @realm client\n-- @string font Name of the font to use\n-- @function SetFont\n\n--- Returns the current font for this panel.\n-- @realm client\n-- @treturn string Name of current font\n-- @function GetFont\nAccessorFunc(PANEL, \"font\", \"Font\", FORCE_STRING)\n\n--- Changes how the text is aligned when drawing. Valid content alignment values include numbers `1` through `9`. Each number's\n-- corresponding alignment is based on its position on a numpad. For example, `1` is bottom-left, `5` is centered, `9` is\n-- top-right, etc.\n-- @realm client\n-- @number alignment Alignment to use\n-- @function SetContentAlignment\n\n--- Returns the current content alignment for this panel.\n-- @realm client\n-- @treturn number Current content alignment\n-- @function GetContentAlignment\nAccessorFunc(PANEL, \"contentAlignment\", \"ContentAlignment\", FORCE_NUMBER)\n\n--- Whether or not to scale the width of the text down to fit the width of this panel, if needed.\n-- @realm client\n-- @bool bScale Whether or not to scale\n-- @function SetScaleWidth\n\n--- Returns whether or not this panel will scale its text down to fit its width.\n-- @realm client\n-- @treturn bool Whether or not this panel will scale its text\n-- @function GetScaleWidth\nAccessorFunc(PANEL, \"bScaleWidth\", \"ScaleWidth\", FORCE_BOOL)\n\n--- How much spacing to use around the text when its drawn. This uses uniform padding on the top, left, right, and bottom of\n-- this panel.\n-- @realm client\n-- @number padding Padding to use\n-- @function SetPadding\n\n--- Returns how much padding this panel has around its text.\n-- @realm client\n-- @treturn number Current padding\n-- @function GetPadding\nAccessorFunc(PANEL, \"padding\", \"Padding\", FORCE_NUMBER)\n\nfunction PANEL:Init()\n\tself.text = \"\"\n\tself.color = color_white\n\tself.backgroundColor = Color(255, 255, 255, 0)\n\tself.kerning = 0\n\tself.font = \"DermaDefault\"\n\tself.scaledFont = \"DermaDefault\"\n\tself.contentAlignment = 5\n\tself.bScaleWidth = false\n\tself.padding = 0\n\n\tself.shadowDistance = 0\n\tself.bCurrentlyScaling = false\nend\n\nfunction PANEL:SetText(text)\n\tself.text = tostring(text)\nend\n\nfunction PANEL:SetFont(font)\n\tself.font = font\n\tself.scaledFont = font\nend\n\n--- Sets the drop shadow to draw behind the text.\n-- @realm client\n-- @number distance How far away to draw the shadow in pixels. Set to `0` to disable\n-- @color[opt] color Color of the shadow. Defaults to a dimmed version of the text color\nfunction PANEL:SetDropShadow(distance, color)\n\tself.shadowDistance = distance or 1\n\tself.shadowColor = color or ix.util.DimColor(self.color, 0.5)\nend\n\nPANEL.SetExpensiveShadow = PANEL.SetDropShadow -- aliasing for easier conversion from DLabels\n\n--- Returns the X and Y location of the text taking into account the text alignment and padding.\n-- @realm client\n-- @internal\n-- @number width Width of the panel\n-- @number height Height of the panel\n-- @number textWidth Width of the text\n-- @number textHeight Height of the text\n-- @treturn number X location to draw the text\n-- @treturn number Y location to draw the text\nfunction PANEL:CalculateAlignment(width, height, textWidth, textHeight)\n\tlocal alignment = self.contentAlignment\n\tlocal x, y\n\n\tif (self.bCurrentlyScaling) then\n\t\t-- if the text is currently being scaled down, then it's always centered\n\t\tx = width * 0.5 - textWidth * 0.5\n\telse\n\t\t-- x alignment\n\t\tif (alignment == 7 or alignment == 4 or alignment == 1) then\n\t\t\t-- left\n\t\t\tx = self.padding\n\t\telseif (alignment == 8 or alignment == 5 or alignment == 2) then\n\t\t\t-- center\n\t\t\tx = width * 0.5 - textWidth * 0.5\n\t\telseif (alignment == 9 or alignment == 6 or alignment == 3) then\n\t\t\tx = width - textWidth - self.padding\n\t\tend\n\tend\n\n\t-- y alignment\n\tif (alignment <= 3) then\n\t\t-- bottom\n\t\ty = height - textHeight - self.padding\n\telseif (alignment <= 6) then\n\t\t-- center\n\t\ty = height * 0.5 - textHeight * 0.5\n\telse\n\t\t-- top\n\t\ty = self.padding\n\tend\n\n\treturn x, y\nend\n\n--- Draws the current text with the current kerning.\n-- @realm client\n-- @internal\n-- @number width Width of the panel\n-- @number height Height of the panel\nfunction PANEL:DrawKernedText(width, height)\n\tlocal contentWidth, contentHeight = self:GetContentSize()\n\tlocal x, y = self:CalculateAlignment(width, height, contentWidth, contentHeight)\n\n\tfor i = 1, self.text:utf8len() do\n\t\tlocal character = self.text:utf8sub(i, i)\n\t\tlocal textWidth, _ = surface.GetTextSize(character)\n\t\tlocal kerning = i == 1 and 0 or self.kerning\n\t\tlocal shadowDistance = self.shadowDistance\n\n\t\t-- shadow\n\t\tif (self.shadowDistance > 0) then\n\t\t\tsurface.SetTextColor(self.shadowColor)\n\t\t\tsurface.SetTextPos(x + kerning + shadowDistance, y + shadowDistance)\n\t\t\tsurface.DrawText(character)\n\t\tend\n\n\t\t-- character\n\t\tsurface.SetTextColor(self.color)\n\t\tsurface.SetTextPos(x + kerning, y)\n\t\tsurface.DrawText(character)\n\n\t\tx = x + textWidth + kerning\n\tend\nend\n\n--- Draws the current text.\n-- @realm client\n-- @internal\n-- @number width Width of the panel\n-- @number height Height of the panel\nfunction PANEL:DrawText(width, height)\n\tlocal textWidth, textHeight = surface.GetTextSize(self.text)\n\tlocal x, y = self:CalculateAlignment(width, height, textWidth, textHeight)\n\n\t-- shadow\n\tif (self.shadowDistance > 0) then\n\t\tsurface.SetTextColor(self.shadowColor)\n\t\tsurface.SetTextPos(x + self.shadowDistance, y + self.shadowDistance)\n\t\tsurface.DrawText(self.text)\n\tend\n\n\t-- text\n\tsurface.SetTextColor(self.color)\n\tsurface.SetTextPos(x, y)\n\tsurface.DrawText(self.text)\nend\n\nfunction PANEL:Paint(width, height)\n\tsurface.SetFont(self.font)\n\tsurface.SetDrawColor(self.backgroundColor)\n\tsurface.DrawRect(0, 0, width, height)\n\n\tif (self.bScaleWidth) then\n\t\tlocal contentWidth, contentHeight = self:GetContentSize()\n\n\t\tif (contentWidth > (width - self.padding * 2)) then\n\t\t\tlocal x, y = self:LocalToScreen(self:GetPos())\n\t\t\tlocal scale = width / (contentWidth + self.padding * 2)\n\t\t\tlocal translation = Vector(x + width * 0.5, y - contentHeight * 0.5 + self.padding, 0)\n\t\t\tlocal matrix = Matrix()\n\n\t\t\tmatrix:Translate(translation)\n\t\t\tmatrix:Scale(Vector(scale, scale, 0))\n\t\t\tmatrix:Translate(-translation)\n\n\t\t\tcam.PushModelMatrix(matrix, true)\n\t\t\trender.PushFilterMin(TEXFILTER.ANISOTROPIC)\n\t\t\tDisableClipping(true)\n\n\t\t\tself.bCurrentlyScaling = true\n\t\tend\n\tend\n\n\tif (self.kerning > 0) then\n\t\tself:DrawKernedText(width, height)\n\telse\n\t\tself:DrawText(width, height)\n\tend\n\n\tif (self.bCurrentlyScaling) then\n\t\tDisableClipping(false)\n\t\trender.PopFilterMin()\n\t\tcam.PopModelMatrix()\n\n\t\tself.bCurrentlyScaling = false\n\tend\nend\n\n--- Returns the size of the text, taking into account the current kerning.\n-- @realm client\n-- @bool[opt=false] bCalculate Whether or not to recalculate the content size instead of using the cached copy\n-- @treturn number Width of the text\n-- @treturn number Height of the text\nfunction PANEL:GetContentSize(bCalculate)\n\tif (bCalculate or !self.contentSize) then\n\t\tsurface.SetFont(self.font)\n\n\t\tif (self.kerning > 0) then\n\t\t\tlocal width = 0\n\n\t\t\tfor i = 1, self.text:utf8len() do\n\t\t\t\tlocal textWidth, _ = surface.GetTextSize(self.text:utf8sub(i, i))\n\t\t\t\twidth = width + textWidth + self.kerning\n\t\t\tend\n\n\t\t\tself.contentSize = {width, draw.GetFontHeight(self.font)}\n\t\telse\n\t\t\tself.contentSize = {surface.GetTextSize(self.text)}\n\t\tend\n\tend\n\n\treturn self.contentSize[1], self.contentSize[2]\nend\n\n--- Sets the size of the panel to fit the content size with the current padding. The content size is recalculated when this\n-- method is called.\n-- @realm client\nfunction PANEL:SizeToContents()\n\tlocal contentWidth, contentHeight = self:GetContentSize(true)\n\n\tself:SetSize(contentWidth + self.padding * 2, contentHeight + self.padding * 2)\nend\n\nvgui.Register(\"ixLabel\", PANEL, \"Panel\")\n\n-- text entry with icon\nDEFINE_BASECLASS(\"ixTextEntry\")\nPANEL = {}\n\nAccessorFunc(PANEL, \"icon\", \"Icon\", FORCE_STRING)\nAccessorFunc(PANEL, \"iconColor\", \"IconColor\")\n\nfunction PANEL:Init()\n\tself:SetIcon(\"V\")\n\tself:SetFont(\"ixSmallTitleFont\")\n\n\tself.iconColor = Color(200, 200, 200, 160)\nend\n\nfunction PANEL:SetIcon(newIcon)\n\tsurface.SetFont(\"ixSmallTitleIcons\")\n\n\tself.iconWidth, self.iconHeight = surface.GetTextSize(newIcon)\n\tself.icon = newIcon\n\n\tself:DockMargin(self.iconWidth + 4, 0, 0, 8)\nend\n\nfunction PANEL:Paint(width, height)\n\tBaseClass.Paint(self, width, height)\n\n\t-- there's no inset for text entries so we'll have to get creative\n\tDisableClipping(true)\n\t\tsurface.SetDrawColor(self:GetBackgroundColor())\n\t\tsurface.DrawRect(-self.iconWidth - 4, 0, self.iconWidth + 4, height)\n\n\t\tsurface.SetFont(\"ixSmallTitleIcons\")\n\t\tsurface.SetTextColor(self.iconColor)\n\t\tsurface.SetTextPos(-self.iconWidth - 2, 0)\n\t\tsurface.DrawText(self:GetIcon())\n\tDisableClipping(false)\nend\n\nvgui.Register(\"ixIconTextEntry\", PANEL, \"ixTextEntry\")\n"
  },
  {
    "path": "gamemode/core/derma/cl_help.lua",
    "content": "\nlocal backgroundColor = Color(0, 0, 0, 66)\n\nlocal PANEL = {}\n\nAccessorFunc(PANEL, \"maxWidth\", \"MaxWidth\", FORCE_NUMBER)\n\nfunction PANEL:Init()\n\tself:SetWide(180)\n\tself:Dock(LEFT)\n\n\tself.maxWidth = ScrW() * 0.2\nend\n\nfunction PANEL:Paint(width, height)\n\tsurface.SetDrawColor(backgroundColor)\n\tsurface.DrawRect(0, 0, width, height)\nend\n\nfunction PANEL:SizeToContents()\n\tlocal width = 0\n\n\tfor _, v in ipairs(self:GetChildren()) do\n\t\twidth = math.max(width, v:GetWide())\n\tend\n\n\tself:SetSize(math.max(32, math.min(width, self.maxWidth)), self:GetParent():GetTall())\nend\n\nvgui.Register(\"ixHelpMenuCategories\", PANEL, \"EditablePanel\")\n\n-- help menu\nPANEL = {}\n\nfunction PANEL:Init()\n\tself:Dock(FILL)\n\n\tself.categories = {}\n\tself.categorySubpanels = {}\n\tself.categoryPanel = self:Add(\"ixHelpMenuCategories\")\n\n\tself.canvasPanel = self:Add(\"EditablePanel\")\n\tself.canvasPanel:Dock(FILL)\n\n\tself.idlePanel = self.canvasPanel:Add(\"Panel\")\n\tself.idlePanel:Dock(FILL)\n\tself.idlePanel:DockMargin(8, 0, 0, 0)\n\tself.idlePanel.Paint = function(_, width, height)\n\t\tsurface.SetDrawColor(backgroundColor)\n\t\tsurface.DrawRect(0, 0, width, height)\n\n\t\tderma.SkinFunc(\"DrawHelixCurved\", width * 0.5, height * 0.5, width * 0.25)\n\n\t\tsurface.SetFont(\"ixIntroSubtitleFont\")\n\t\tlocal text = L(\"helix\"):lower()\n\t\tlocal textWidth, textHeight = surface.GetTextSize(text)\n\n\t\tsurface.SetTextColor(color_white)\n\t\tsurface.SetTextPos(width * 0.5 - textWidth * 0.5, height * 0.5 - textHeight * 0.75)\n\t\tsurface.DrawText(text)\n\n\t\tsurface.SetFont(\"ixMediumLightFont\")\n\t\ttext = L(\"helpIdle\")\n\t\tlocal infoWidth, _ = surface.GetTextSize(text)\n\n\t\tsurface.SetTextColor(color_white)\n\t\tsurface.SetTextPos(width * 0.5 - infoWidth * 0.5, height * 0.5 + textHeight * 0.25)\n\t\tsurface.DrawText(text)\n\tend\n\n\tlocal categories = {}\n\thook.Run(\"PopulateHelpMenu\", categories)\n\n\tfor k, v in SortedPairs(categories) do\n\t\tif (!isstring(k)) then\n\t\t\tErrorNoHalt(\"expected string for help menu key\\n\")\n\t\t\tcontinue\n\t\telseif (!isfunction(v)) then\n\t\t\tErrorNoHalt(string.format(\"expected function for help menu entry '%s'\\n\", k))\n\t\t\tcontinue\n\t\tend\n\n\t\tself:AddCategory(k)\n\t\tself.categories[k] = v\n\tend\n\n\tself.categoryPanel:SizeToContents()\n\n\tif (ix.gui.lastHelpMenuTab) then\n\t\tself:OnCategorySelected(ix.gui.lastHelpMenuTab)\n\tend\nend\n\nfunction PANEL:AddCategory(name)\n\tlocal button = self.categoryPanel:Add(\"ixMenuButton\")\n\tbutton:SetText(L(name))\n\tbutton:SizeToContents()\n\t-- @todo don't hardcode this but it's the only panel that needs docking at the bottom so it'll do for now\n\tbutton:Dock(name == \"credits\" and BOTTOM or TOP)\n\tbutton.DoClick = function()\n\t\tself:OnCategorySelected(name)\n\tend\n\n\tlocal panel = self.canvasPanel:Add(\"DScrollPanel\")\n\tpanel:SetVisible(false)\n\tpanel:Dock(FILL)\n\tpanel:DockMargin(8, 0, 0, 0)\n\tpanel:GetCanvas():DockPadding(8, 8, 8, 8)\n\n\tpanel.Paint = function(_, width, height)\n\t\tsurface.SetDrawColor(backgroundColor)\n\t\tsurface.DrawRect(0, 0, width, height)\n\tend\n\n\t-- reverts functionality back to a standard panel in the case that a category will manage its own scrolling\n\tpanel.DisableScrolling = function()\n\t\tpanel:GetCanvas():SetVisible(false)\n\t\tpanel:GetVBar():SetVisible(false)\n\t\tpanel.OnChildAdded = function() end\n\tend\n\n\tself.categorySubpanels[name] = panel\nend\n\nfunction PANEL:OnCategorySelected(name)\n\tlocal panel = self.categorySubpanels[name]\n\n\tif (!IsValid(panel)) then\n\t\treturn\n\tend\n\n\tif (!panel.bPopulated) then\n\t\tself.categories[name](panel)\n\t\tpanel.bPopulated = true\n\tend\n\n\tif (IsValid(self.activeCategory)) then\n\t\tself.activeCategory:SetVisible(false)\n\tend\n\n\tpanel:SetVisible(true)\n\tself.idlePanel:SetVisible(false)\n\n\tself.activeCategory = panel\n\tix.gui.lastHelpMenuTab = name\nend\n\nvgui.Register(\"ixHelpMenu\", PANEL, \"EditablePanel\")\n\nlocal function DrawHelix(width, height, color) -- luacheck: ignore 211\n\tlocal segments = 76\n\tlocal radius = math.min(width, height) * 0.375\n\n\tsurface.SetTexture(-1)\n\n\tfor i = 1, math.ceil(segments) do\n\t\tlocal angle = math.rad((i / segments) * -360)\n\t\tlocal x = width * 0.5 + math.sin(angle + math.pi * 2) * radius\n\t\tlocal y = height * 0.5 + math.cos(angle + math.pi * 2) * radius\n\t\tlocal barOffset = math.sin(SysTime() + i * 0.5)\n\t\tlocal barHeight = barOffset * radius * 0.25\n\n\t\tif (barOffset > 0) then\n\t\t\tsurface.SetDrawColor(color)\n\t\telse\n\t\t\tsurface.SetDrawColor(color.r * 0.5, color.g * 0.5, color.b * 0.5, color.a)\n\t\tend\n\n\t\tsurface.DrawTexturedRectRotated(x, y, 4, barHeight, math.deg(angle))\n\tend\nend\n\nhook.Add(\"CreateMenuButtons\", \"ixHelpMenu\", function(tabs)\n\ttabs[\"help\"] = function(container)\n\t\tcontainer:Add(\"ixHelpMenu\")\n\tend\nend)\n\nhook.Add(\"PopulateHelpMenu\", \"ixHelpMenu\", function(tabs)\n\ttabs[\"commands\"] = function(container)\n\t\t-- info text\n\t\tlocal info = container:Add(\"DLabel\")\n\t\tinfo:SetFont(\"ixSmallFont\")\n\t\tinfo:SetText(L(\"helpCommands\"))\n\t\tinfo:SetContentAlignment(5)\n\t\tinfo:SetTextColor(color_white)\n\t\tinfo:SetExpensiveShadow(1, color_black)\n\t\tinfo:Dock(TOP)\n\t\tinfo:DockMargin(0, 0, 0, 8)\n\t\tinfo:SizeToContents()\n\t\tinfo:SetTall(info:GetTall() + 16)\n\n\t\tinfo.Paint = function(_, width, height)\n\t\t\tsurface.SetDrawColor(ColorAlpha(derma.GetColor(\"Info\", info), 160))\n\t\t\tsurface.DrawRect(0, 0, width, height)\n\t\tend\n\n\t\t-- commands\n\t\tfor uniqueID, command in SortedPairs(ix.command.list) do\n\t\t\tif (command.OnCheckAccess and !command:OnCheckAccess(LocalPlayer())) then\n\t\t\t\tcontinue\n\t\t\tend\n\n\t\t\tlocal bIsAlias = false\n\t\t\tlocal aliasText = \"\"\n\n\t\t\t-- we want to show aliases in the same entry for better readability\n\t\t\tif (command.alias) then\n\t\t\t\tlocal alias = istable(command.alias) and command.alias or {command.alias}\n\n\t\t\t\tfor _, v in ipairs(alias) do\n\t\t\t\t\tif (v:lower() == uniqueID) then\n\t\t\t\t\t\tbIsAlias = true\n\t\t\t\t\t\tbreak\n\t\t\t\t\tend\n\n\t\t\t\t\taliasText = aliasText .. \", /\" .. v\n\t\t\t\tend\n\n\t\t\t\tif (bIsAlias) then\n\t\t\t\t\tcontinue\n\t\t\t\tend\n\t\t\tend\n\n\t\t\t-- command name\n\t\t\tlocal title = container:Add(\"DLabel\")\n\t\t\ttitle:SetFont(\"ixMediumLightFont\")\n\t\t\ttitle:SetText(\"/\" .. command.name .. aliasText)\n\t\t\ttitle:Dock(TOP)\n\t\t\ttitle:SetTextColor(ix.config.Get(\"color\"))\n\t\t\ttitle:SetExpensiveShadow(1, color_black)\n\t\t\ttitle:SizeToContents()\n\n\t\t\t-- syntax\n\t\t\tlocal syntaxText = command.syntax\n\t\t\tlocal syntax\n\n\t\t\tif (syntaxText != \"\" and syntaxText != \"[none]\") then\n\t\t\t\tsyntax = container:Add(\"DLabel\")\n\t\t\t\tsyntax:SetFont(\"ixMediumLightFont\")\n\t\t\t\tsyntax:SetText(syntaxText)\n\t\t\t\tsyntax:Dock(TOP)\n\t\t\t\tsyntax:SetTextColor(color_white)\n\t\t\t\tsyntax:SetExpensiveShadow(1, color_black)\n\t\t\t\tsyntax:SetWrap(true)\n\t\t\t\tsyntax:SetAutoStretchVertical(true)\n\t\t\t\tsyntax:SizeToContents()\n\t\t\tend\n\n\t\t\t-- description\n\t\t\tlocal descriptionText = command:GetDescription()\n\n\t\t\tif (descriptionText != \"\") then\n\t\t\t\tlocal description = container:Add(\"DLabel\")\n\t\t\t\tdescription:SetFont(\"ixSmallFont\")\n\t\t\t\tdescription:SetText(descriptionText)\n\t\t\t\tdescription:Dock(TOP)\n\t\t\t\tdescription:SetTextColor(color_white)\n\t\t\t\tdescription:SetExpensiveShadow(1, color_black)\n\t\t\t\tdescription:SetWrap(true)\n\t\t\t\tdescription:SetAutoStretchVertical(true)\n\t\t\t\tdescription:SizeToContents()\n\t\t\t\tdescription:DockMargin(0, 0, 0, 8)\n\t\t\telseif (syntax) then\n\t\t\t\tsyntax:DockMargin(0, 0, 0, 8)\n\t\t\telse\n\t\t\t\ttitle:DockMargin(0, 0, 0, 8)\n\t\t\tend\n\t\tend\n\tend\n\n\ttabs[\"flags\"] = function(container)\n\t\t-- info text\n\t\tlocal info = container:Add(\"DLabel\")\n\t\tinfo:SetFont(\"ixSmallFont\")\n\t\tinfo:SetText(L(\"helpFlags\"))\n\t\tinfo:SetContentAlignment(5)\n\t\tinfo:SetTextColor(color_white)\n\t\tinfo:SetExpensiveShadow(1, color_black)\n\t\tinfo:Dock(TOP)\n\t\tinfo:DockMargin(0, 0, 0, 8)\n\t\tinfo:SizeToContents()\n\t\tinfo:SetTall(info:GetTall() + 16)\n\n\t\tinfo.Paint = function(_, width, height)\n\t\t\tsurface.SetDrawColor(ColorAlpha(derma.GetColor(\"Info\", info), 160))\n\t\t\tsurface.DrawRect(0, 0, width, height)\n\t\tend\n\n\t\t-- flags\n\t\tfor k, v in SortedPairs(ix.flag.list) do\n\t\t\tlocal background = ColorAlpha(\n\t\t\t\tLocalPlayer():GetCharacter():HasFlags(k) and derma.GetColor(\"Success\", info) or derma.GetColor(\"Error\", info), 88\n\t\t\t)\n\n\t\t\tlocal panel = container:Add(\"Panel\")\n\t\t\tpanel:Dock(TOP)\n\t\t\tpanel:DockMargin(0, 0, 0, 8)\n\t\t\tpanel:DockPadding(4, 4, 4, 4)\n\t\t\tpanel.Paint = function(_, width, height)\n\t\t\t\tderma.SkinFunc(\"DrawImportantBackground\", 0, 0, width, height, background)\n\t\t\tend\n\n\t\t\tlocal flag = panel:Add(\"DLabel\")\n\t\t\tflag:SetFont(\"ixMonoMediumFont\")\n\t\t\tflag:SetText(string.format(\"[%s]\", k))\n\t\t\tflag:Dock(LEFT)\n\t\t\tflag:SetTextColor(color_white)\n\t\t\tflag:SetExpensiveShadow(1, color_black)\n\t\t\tflag:SetTextInset(4, 0)\n\t\t\tflag:SizeToContents()\n\t\t\tflag:SetTall(flag:GetTall() + 8)\n\n\t\t\tlocal description = panel:Add(\"DLabel\")\n\t\t\tdescription:SetFont(\"ixMediumLightFont\")\n\t\t\tdescription:SetText(v.description)\n\t\t\tdescription:Dock(FILL)\n\t\t\tdescription:SetTextColor(color_white)\n\t\t\tdescription:SetExpensiveShadow(1, color_black)\n\t\t\tdescription:SetTextInset(8, 0)\n\t\t\tdescription:SizeToContents()\n\t\t\tdescription:SetTall(description:GetTall() + 8)\n\n\t\t\tpanel:SizeToChildren(false, true)\n\t\tend\n\tend\n\n\ttabs[\"plugins\"] = function(container)\n\t\tfor _, v in SortedPairsByMemberValue(ix.plugin.list, \"name\") do\n\t\t\t-- name\n\t\t\tlocal title = container:Add(\"DLabel\")\n\t\t\ttitle:SetFont(\"ixMediumLightFont\")\n\t\t\ttitle:SetText(v.name or \"Unknown\")\n\t\t\ttitle:Dock(TOP)\n\t\t\ttitle:SetTextColor(ix.config.Get(\"color\"))\n\t\t\ttitle:SetExpensiveShadow(1, color_black)\n\t\t\ttitle:SizeToContents()\n\n\t\t\t-- author\n\t\t\tlocal author = container:Add(\"DLabel\")\n\t\t\tauthor:SetFont(\"ixSmallFont\")\n\t\t\tauthor:SetText(string.format(\"%s: %s\", L(\"author\"), v.author))\n\t\t\tauthor:Dock(TOP)\n\t\t\tauthor:SetTextColor(color_white)\n\t\t\tauthor:SetExpensiveShadow(1, color_black)\n\t\t\tauthor:SetWrap(true)\n\t\t\tauthor:SetAutoStretchVertical(true)\n\t\t\tauthor:SizeToContents()\n\n\t\t\t-- description\n\t\t\tlocal descriptionText = v.description\n\n\t\t\tif (descriptionText != \"\") then\n\t\t\t\tlocal description = container:Add(\"DLabel\")\n\t\t\t\tdescription:SetFont(\"ixSmallFont\")\n\t\t\t\tdescription:SetText(descriptionText)\n\t\t\t\tdescription:Dock(TOP)\n\t\t\t\tdescription:SetTextColor(color_white)\n\t\t\t\tdescription:SetExpensiveShadow(1, color_black)\n\t\t\t\tdescription:SetWrap(true)\n\t\t\t\tdescription:SetAutoStretchVertical(true)\n\t\t\t\tdescription:SizeToContents()\n\t\t\t\tdescription:DockMargin(0, 0, 0, 8)\n\t\t\telse\n\t\t\t\tauthor:DockMargin(0, 0, 0, 8)\n\t\t\tend\n\t\tend\n\tend\nend)\n"
  },
  {
    "path": "gamemode/core/derma/cl_information.lua",
    "content": "\nlocal PANEL = {}\n\nfunction PANEL:Init()\n\tlocal parent = self:GetParent()\n\n\tself:SetSize(parent:GetWide() * 0.6, parent:GetTall())\n\tself:Dock(RIGHT)\n\tself:DockMargin(0, ScrH() * 0.05, 0, 0)\n\n\tself.VBar:SetWide(0)\n\n\t-- entry setup\n\tlocal suppress = {}\n\thook.Run(\"CanCreateCharacterInfo\", suppress)\n\n\tif (!suppress.time) then\n\t\tlocal format = ix.option.Get(\"24hourTime\", false) and \"%A, %B %d, %Y. %H:%M\" or \"%A, %B %d, %Y. %I:%M %p\"\n\n\t\tself.time = self:Add(\"DLabel\")\n\t\tself.time:SetFont(\"ixMediumFont\")\n\t\tself.time:SetTall(28)\n\t\tself.time:SetContentAlignment(5)\n\t\tself.time:Dock(TOP)\n\t\tself.time:SetTextColor(color_white)\n\t\tself.time:SetExpensiveShadow(1, Color(0, 0, 0, 150))\n\t\tself.time:DockMargin(0, 0, 0, 32)\n\t\tself.time:SetText(ix.date.GetFormatted(format))\n\t\tself.time.Think = function(this)\n\t\t\tif ((this.nextTime or 0) < CurTime()) then\n\t\t\t\tthis:SetText(ix.date.GetFormatted(format))\n\t\t\t\tthis.nextTime = CurTime() + 0.5\n\t\t\tend\n\t\tend\n\tend\n\n\tif (!suppress.name) then\n\t\tself.name = self:Add(\"ixLabel\")\n\t\tself.name:Dock(TOP)\n\t\tself.name:DockMargin(0, 0, 0, 8)\n\t\tself.name:SetFont(\"ixMenuButtonHugeFont\")\n\t\tself.name:SetContentAlignment(5)\n\t\tself.name:SetTextColor(color_white)\n\t\tself.name:SetPadding(8)\n\t\tself.name:SetScaleWidth(true)\n\tend\n\n\tif (!suppress.description) then\n\t\tself.description = self:Add(\"DLabel\")\n\t\tself.description:Dock(TOP)\n\t\tself.description:DockMargin(0, 0, 0, 8)\n\t\tself.description:SetFont(\"ixMenuButtonFont\")\n\t\tself.description:SetTextColor(color_white)\n\t\tself.description:SetContentAlignment(5)\n\t\tself.description:SetMouseInputEnabled(true)\n\t\tself.description:SetCursor(\"hand\")\n\n\t\tself.description.Paint = function(this, width, height)\n\t\t\tsurface.SetDrawColor(0, 0, 0, 150)\n\t\t\tsurface.DrawRect(0, 0, width, height)\n\t\tend\n\n\t\tself.description.OnMousePressed = function(this, code)\n\t\t\tif (code == MOUSE_LEFT) then\n\t\t\t\tix.command.Send(\"CharDesc\")\n\n\t\t\t\tif (IsValid(ix.gui.menu)) then\n\t\t\t\t\tix.gui.menu:Remove()\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\n\t\tself.description.SizeToContents = function(this)\n\t\t\tif (this.bWrap) then\n\t\t\t\t-- sizing contents after initial wrapping does weird things so we'll just ignore (lol)\n\t\t\t\treturn\n\t\t\tend\n\n\t\t\tlocal width, height = this:GetContentSize()\n\n\t\t\tif (width > self:GetWide()) then\n\t\t\t\tthis:SetWide(self:GetWide())\n\t\t\t\tthis:SetTextInset(16, 8)\n\t\t\t\tthis:SetWrap(true)\n\t\t\t\tthis:SizeToContentsY()\n\t\t\t\tthis:SetTall(this:GetTall() + 16) -- eh\n\n\t\t\t\t-- wrapping doesn't like middle alignment so we'll do top-center\n\t\t\t\tself.description:SetContentAlignment(8)\n\t\t\t\tthis.bWrap = true\n\t\t\telse\n\t\t\t\tthis:SetSize(width + 16, height + 16)\n\t\t\tend\n\t\tend\n\tend\n\n\tif (!suppress.characterInfo) then\n\t\tself.characterInfo = self:Add(\"Panel\")\n\t\tself.characterInfo.list = {}\n\t\tself.characterInfo:Dock(TOP) -- no dock margin because this is handled by ixListRow\n\t\tself.characterInfo.SizeToContents = function(this)\n\t\t\tlocal height = 0\n\n\t\t\tfor _, v in ipairs(this:GetChildren()) do\n\t\t\t\tif (IsValid(v) and v:IsVisible()) then\n\t\t\t\t\tlocal _, top, _, bottom = v:GetDockMargin()\n\t\t\t\t\theight = height + v:GetTall() + top + bottom\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tthis:SetTall(height)\n\t\tend\n\n\t\tif (!suppress.faction) then\n\t\t\tself.faction = self.characterInfo:Add(\"ixListRow\")\n\t\t\tself.faction:SetList(self.characterInfo.list)\n\t\t\tself.faction:Dock(TOP)\n\t\tend\n\n\t\tif (!suppress.class) then\n\t\t\tself.class = self.characterInfo:Add(\"ixListRow\")\n\t\t\tself.class:SetList(self.characterInfo.list)\n\t\t\tself.class:Dock(TOP)\n\t\tend\n\n\t\tif (!suppress.money) then\n\t\t\tself.money = self.characterInfo:Add(\"ixListRow\")\n\t\t\tself.money:SetList(self.characterInfo.list)\n\t\t\tself.money:Dock(TOP)\n\t\t\tself.money:SizeToContents()\n\t\tend\n\n\t\thook.Run(\"CreateCharacterInfo\", self.characterInfo)\n\t\tself.characterInfo:SizeToContents()\n\tend\n\n\t-- no need to update since we aren't showing the attributes panel\n\tif (!suppress.attributes) then\n\t\tlocal character = LocalPlayer().GetCharacter and LocalPlayer():GetCharacter()\n\n\t\tif (character) then\n\t\t\tself.attributes = self:Add(\"ixCategoryPanel\")\n\t\t\tself.attributes:SetText(L(\"attributes\"))\n\t\t\tself.attributes:Dock(TOP)\n\t\t\tself.attributes:DockMargin(0, 0, 0, 8)\n\n\t\t\tlocal boost = character:GetBoosts()\n\t\t\tlocal bFirst = true\n\n\t\t\tfor k, v in SortedPairsByMemberValue(ix.attributes.list, \"name\") do\n\t\t\t\tlocal attributeBoost = 0\n\n\t\t\t\tif (boost[k]) then\n\t\t\t\t\tfor _, bValue in pairs(boost[k]) do\n\t\t\t\t\t\tattributeBoost = attributeBoost + bValue\n\t\t\t\t\tend\n\t\t\t\tend\n\n\t\t\t\tlocal bar = self.attributes:Add(\"ixAttributeBar\")\n\t\t\t\tbar:Dock(TOP)\n\n\t\t\t\tif (!bFirst) then\n\t\t\t\t\tbar:DockMargin(0, 3, 0, 0)\n\t\t\t\telse\n\t\t\t\t\tbFirst = false\n\t\t\t\tend\n\n\t\t\t\tlocal value = character:GetAttribute(k, 0)\n\n\t\t\t\tif (attributeBoost) then\n\t\t\t\t\tbar:SetValue(value - attributeBoost or 0)\n\t\t\t\telse\n\t\t\t\t\tbar:SetValue(value)\n\t\t\t\tend\n\n\t\t\t\tlocal maximum = v.maxValue or ix.config.Get(\"maxAttributes\", 100)\n\t\t\t\tbar:SetMax(maximum)\n\t\t\t\tbar:SetReadOnly()\n\t\t\t\tbar:SetText(Format(\"%s [%.1f/%.1f] (%.1f%%)\", L(v.name), value, maximum, value / maximum * 100))\n\n\t\t\t\tif (attributeBoost) then\n\t\t\t\t\tbar:SetBoost(attributeBoost)\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tself.attributes:SizeToContents()\n\t\tend\n\tend\n\n\thook.Run(\"CreateCharacterInfoCategory\", self)\nend\n\nfunction PANEL:Update(character)\n\tif (!character) then\n\t\treturn\n\tend\n\n\tlocal faction = ix.faction.indices[character:GetFaction()]\n\tlocal class = ix.class.list[character:GetClass()]\n\n\tif (self.name) then\n\t\tself.name:SetText(character:GetName())\n\n\t\tif (faction) then\n\t\t\tself.name.backgroundColor = ColorAlpha(faction.color, 150) or Color(0, 0, 0, 150)\n\t\tend\n\n\t\tself.name:SizeToContents()\n\tend\n\n\tif (self.description) then\n\t\tself.description:SetText(character:GetDescription())\n\t\tself.description:SizeToContents()\n\tend\n\n\tif (self.faction) then\n\t\tself.faction:SetLabelText(L(\"faction\"))\n\t\tself.faction:SetText(L(faction.name))\n\t\tself.faction:SizeToContents()\n\tend\n\n\tif (self.class) then\n\t\t-- don't show class label if the class is the same name as the faction\n\t\tif (class and class.name != faction.name) then\n\t\t\tself.class:SetLabelText(L(\"class\"))\n\t\t\tself.class:SetText(L(class.name))\n\t\t\tself.class:SizeToContents()\n\t\telse\n\t\t\tself.class:SetVisible(false)\n\t\tend\n\tend\n\n\tif (self.money) then\n\t\tself.money:SetLabelText(L(\"money\"))\n\t\tself.money:SetText(ix.currency.Get(character:GetMoney()))\n\t\tself.money:SizeToContents()\n\tend\n\n\thook.Run(\"UpdateCharacterInfo\", self.characterInfo, character)\n\n\tself.characterInfo:SizeToContents()\n\n\thook.Run(\"UpdateCharacterInfoCategory\", self, character)\nend\n\nfunction PANEL:OnSubpanelRightClick()\n\tproperties.OpenEntityMenu(LocalPlayer())\nend\n\nvgui.Register(\"ixCharacterInfo\", PANEL, \"DScrollPanel\")\n\nhook.Add(\"CreateMenuButtons\", \"ixCharInfo\", function(tabs)\n\ttabs[\"you\"] = {\n\t\tbHideBackground = true,\n\t\tbuttonColor = team.GetColor(LocalPlayer():Team()),\n\t\tCreate = function(info, container)\n\t\t\tcontainer.infoPanel = container:Add(\"ixCharacterInfo\")\n\n\t\t\tcontainer.OnMouseReleased = function(this, key)\n\t\t\t\tif (key == MOUSE_RIGHT) then\n\t\t\t\t\tthis.infoPanel:OnSubpanelRightClick()\n\t\t\t\tend\n\t\t\tend\n\t\tend,\n\t\tOnSelected = function(info, container)\n\t\t\tcontainer.infoPanel:Update(LocalPlayer():GetCharacter())\n\t\t\tix.gui.menu:SetCharacterOverview(true)\n\t\tend,\n\t\tOnDeselected = function(info, container)\n\t\t\tix.gui.menu:SetCharacterOverview(false)\n\t\tend\n\t}\nend)\n"
  },
  {
    "path": "gamemode/core/derma/cl_intro.lua",
    "content": "\nlocal waveSegments = 32\nlocal helixSegments = 76\nlocal helixHeight = 64\nlocal backgroundColor = Color(115, 53, 142)\nlocal dimColor = Color(165, 134, 179)\n\nDEFINE_BASECLASS(\"EditablePanel\")\n\nlocal PANEL = {}\n\nfunction PANEL:Init()\n\tif (IsValid(ix.gui.intro)) then\n\t\tix.gui.intro:Remove()\n\tend\n\n\tix.gui.intro = self\n\n\tself:SetSize(ScrW(), ScrH())\n\tself:SetPos(0, 0)\n\tself:SetZPos(99999)\n\tself:MakePopup()\n\n\t-- animation parameters\n\tself.bBackground = true\n\tself.volume = 1\n\tself.sunbeamOffset = 0\n\tself.textOne = 0\n\tself.textTwo = 0\n\tself.kickTarget = 0\n\tself.helix = 0\n\tself.helixAlpha = 0\n\tself.continueText = 0\n\tself.pulse = 0\n\n\tself.waves = {\n\t\t{1.1, 0},\n\t\t{1.1, math.pi},\n\t\t{1.1, math.pi * 1.6},\n\t\t{1.1, math.pi * 0.5}\n\t}\nend\n\n-- @todo h a c k\nfunction PANEL:Think()\n\tif (IsValid(LocalPlayer())) then\n\t\tself:BeginIntro()\n\t\tself.Think = nil\n\tend\nend\n\nfunction PANEL:BeginIntro()\n\t-- something could have errored on startup and invalidated all options, so we'll be extra careful with setting the option\n\t-- because if it errors here, the sound will play each tick and proceed to hurt ears\n\tlocal bLoaded = false\n\n\tif (ix and ix.option and ix.option.Set) then\n\t\tlocal bSuccess, _ = pcall(ix.option.Set, \"showIntro\", false)\n\t\tbLoaded = bSuccess\n\tend\n\n\tif (!bLoaded) then\n\t\tself:Remove()\n\n\t\tif (ix and ix.gui and IsValid(ix.gui.characterMenu)) then\n\t\t\tix.gui.characterMenu:Remove()\n\t\tend\n\n\t\tErrorNoHalt(\n\t\t\t\"[Helix] Something has errored and prevented the framework from loading correctly - check your console for errors!\\n\")\n\n\t\treturn\n\tend\n\n\tself:MoveToFront()\n\tself:RequestFocus()\n\n\tsound.PlayFile(\"sound/buttons/combine_button2.wav\", \"\", function()\n\t\ttimer.Create(\"ixIntroStart\", 2, 1, function()\n\t\t\tsound.PlayFile(\"sound/helix/intro.mp3\", \"\", function(channel, status, error)\n\t\t\t\tif (IsValid(channel)) then\n\t\t\t\t\tchannel:SetVolume(self.volume)\n\t\t\t\t\tself.channel = channel\n\t\t\t\tend\n\n\t\t\t\tself:BeginAnimation()\n\t\t\tend)\n\t\tend)\n\tend)\nend\n\nfunction PANEL:AnimateWaves(target, bReverse)\n\tfor i = bReverse and #self.waves or 1,\n\t\tbReverse and 1 or #self.waves,\n\t\tbReverse and -1 or 1 do\n\n\t\tlocal animation = self:CreateAnimation(2, {\n\t\t\tindex = 20 + (bReverse and (#self.waves - i) or i),\n\t\t\tbAutoFire = false,\n\t\t\ttarget = {\n\t\t\t\twaves = {\n\t\t\t\t\t[i] = {target}\n\t\t\t\t}\n\t\t\t},\n\t\t\teasing = bReverse and \"inQuart\" or \"outQuint\"\n\t\t})\n\n\t\ttimer.Simple((bReverse and (#self.waves - i) or i) * 0.1, function()\n\t\t\tif (IsValid(self) and animation) then\n\t\t\t\tanimation:Fire()\n\t\t\tend\n\t\tend)\n\n\t\t-- return last animation that plays\n\t\tif ((bReverse and i == 1) or (!bReverse and i == #self.waves)) then\n\t\t\treturn animation\n\t\tend\n\tend\nend\n\nfunction PANEL:BeginAnimation()\n\tself:CreateAnimation(2, {\n\t\t\ttarget = {textOne = 1},\n\t\t\teasing = \"inQuint\",\n\t\t\tbIgnoreConfig = true\n\t\t})\n\t\t:CreateAnimation(2, {\n\t\t\ttarget = {textOne = 0},\n\t\t\teasing = \"inQuint\",\n\t\t\tbIgnoreConfig = true\n\t\t})\n\t\t:CreateAnimation(2, {\n\t\t\ttarget = {textTwo = 1},\n\t\t\teasing = \"inQuint\",\n\t\t\tbIgnoreConfig = true,\n\t\t\tOnComplete = function(animation, panel)\n\t\t\t\tself:AnimateWaves(0)\n\t\t\tend\n\t\t})\n\t\t:CreateAnimation(2, {\n\t\t\ttarget = {textTwo = 0},\n\t\t\teasing = \"inQuint\",\n\t\t\tbIgnoreConfig = true\n\t\t})\n\t\t:CreateAnimation(4, {\n\t\t\ttarget = {sunbeamOffset = 1},\n\t\t\tbIgnoreConfig = true,\n\t\t\tOnComplete = function()\n\t\t\t\tself:CreateAnimation(2,{\n\t\t\t\t\ttarget = {helixAlpha = 1},\n\t\t\t\t\teasing = \"inCubic\"\n\t\t\t\t})\n\t\t\tend\n\t\t})\n\t\t:CreateAnimation(2, {\n\t\t\ttarget = {helix = 1},\n\t\t\teasing = \"outQuart\",\n\t\t\tbIgnoreConfig = true\n\t\t})\n\t\t:CreateAnimation(2, {\n\t\t\ttarget = {continueText = 1},\n\t\t\teasing = \"linear\",\n\t\t\tbIgnoreConfig = true\n\t\t})\nend\n\nfunction PANEL:PaintCurve(y, width, offset, scale)\n\toffset = offset or 1\n\tscale = scale or 32\n\n\tlocal points = {\n\t\t[1] = {\n\t\t\tx = 0,\n\t\t\ty = ScrH()\n\t\t}\n\t}\n\n\tfor i = 0, waveSegments do\n\t\tlocal angle = math.rad((i / waveSegments) * -360)\n\n\t\tpoints[#points + 1] = {\n\t\t\tx = (width / waveSegments) * i,\n\t\t\ty = y + (math.sin(angle * 0.5 + offset) - 1) * scale\n\t\t}\n\tend\n\n\tpoints[#points + 1] = {\n\t\tx = width,\n\t\ty = ScrH()\n\t}\n\n\tdraw.NoTexture()\n\tsurface.DrawPoly(points)\nend\n\nfunction PANEL:Paint(width, height)\n\tlocal time = SysTime()\n\tlocal text = L(\"helix\"):lower()\n\tlocal centerY = height * self.waves[#self.waves][1] + height * 0.5\n\tlocal sunbeamOffsetEasing = math.sin(math.pi * self.sunbeamOffset)\n\tlocal textWidth, textHeight\n\tlocal fft\n\n\t-- background\n\tif (self.bBackground) then\n\t\tsurface.SetDrawColor(0, 0, 0, 255)\n\t\tsurface.DrawRect(0, 0, width, height)\n\tend\n\n\tif (self.sunbeamOffset == 1) then\n\t\tfft = {}\n\n\t\tif (IsValid(self.channel)) then\n\t\t\tself.channel:FFT(fft, FFT_2048)\n\n\t\t\tlocal kick = (fft[4] or 0) * 8192\n\t\t\tself.kickTarget = math.Approach(self.kickTarget, kick, 8 * math.abs(kick - self.kickTarget) * FrameTime())\n\t\tend\n\tend\n\n\t-- waves\n\tfor i = 1, #self.waves do\n\t\tlocal wave = self.waves[i]\n\t\tlocal ratio = i / #self.waves\n\t\tlocal color = Color(\n\t\t\tbackgroundColor.r * ratio,\n\t\t\tbackgroundColor.g * ratio,\n\t\t\tbackgroundColor.b * ratio,\n\t\t\tself.bBackground and 255 or (ratio * 320)\n\t\t)\n\n\t\tsurface.SetDrawColor(color)\n\t\tself:PaintCurve(height * wave[1], width, wave[2])\n\tend\n\n\t-- helix\n\tif (self.helix > 0) then\n\t\tlocal alpha = self.helixAlpha * 255\n\n\t\tderma.SkinFunc(\"DrawHelixCurved\",\n\t\t\twidth * 0.5, centerY,\n\t\t\tmath.min(ScreenScale(72), 128) * 2, -- font sizes are clamped to 128\n\t\t\thelixSegments * self.helix, helixHeight, self.helix,\n\t\t\tColorAlpha(color_white, alpha),\n\t\t\tColorAlpha(dimColor, alpha)\n\t\t)\n\tend\n\n\t-- title text glow\n\tsurface.SetTextColor(255, 255, 255,\n\t\tself.sunbeamOffset == 1 and self.kickTarget or sunbeamOffsetEasing * 255\n\t)\n\tsurface.SetFont(\"ixIntroTitleBlurFont\")\n\n\tlocal logoTextWidth, logoTextHeight = surface.GetTextSize(text)\n\tsurface.SetTextPos(width * 0.5 - logoTextWidth * 0.5, centerY - logoTextHeight * 0.5)\n\tsurface.DrawText(text)\n\n\t-- title text\n\tsurface.SetTextColor(255, 255, 255, self.sunbeamOffset * 255)\n\tsurface.SetFont(\"ixIntroTitleFont\")\n\n\tlogoTextWidth, logoTextHeight = surface.GetTextSize(text)\n\tsurface.SetTextPos(width * 0.5 - logoTextWidth * 0.5, centerY - logoTextHeight * 0.5)\n\tsurface.DrawText(text)\n\n\t-- text one\n\tsurface.SetFont(\"ixIntroSubtitleFont\")\n\ttext = L(\"introTextOne\"):lower()\n\ttextWidth = surface.GetTextSize(text)\n\n\tsurface.SetTextColor(255, 255, 255, self.textOne * 255)\n\tsurface.SetTextPos(width * 0.5 - textWidth * 0.5, height * 0.66)\n\tsurface.DrawText(text)\n\n\t-- text two\n\ttext = L(\"introTextTwo\", Schema.author or \"nebulous\"):lower()\n\ttextWidth = surface.GetTextSize(text)\n\n\tsurface.SetTextColor(255, 255, 255, self.textTwo * 255)\n\tsurface.SetTextPos(width * 0.5 - textWidth * 0.5, height * 0.66)\n\tsurface.DrawText(text)\n\n\t-- continue text\n\tsurface.SetFont(\"ixIntroSmallFont\")\n\ttext = L(\"introContinue\"):lower()\n\ttextWidth, textHeight = surface.GetTextSize(text)\n\n\tif (self.continueText == 1) then\n\t\tself.pulse = self.pulse + 6 * FrameTime()\n\n\t\tif (self.pulse >= 360) then\n\t\t\tself.pulse = 0\n\t\tend\n\tend\n\n\tsurface.SetTextColor(255, 255, 255, self.continueText * 255 - (math.sin(self.pulse) * 100), 0)\n\tsurface.SetTextPos(width * 0.5 - textWidth * 0.5, centerY * 2 - textHeight * 2)\n\tsurface.DrawText(text)\n\n\t-- sunbeams\n\tif (self.sunbeamOffset > 0 and self.sunbeamOffset != 1) then\n\t\tDrawSunbeams(0.25, sunbeamOffsetEasing * 0.1, 0.02,\n\t\t\t(((width * 0.5 - logoTextWidth * 0.5) - 32) / width) + ((logoTextWidth + 64) / width) * self.sunbeamOffset,\n\t\t\t0.5 + math.sin(time * 2) * 0.01\n\t\t)\n\tend\nend\n\nfunction PANEL:OnKeyCodePressed(key)\n\tif (key == KEY_SPACE and self.continueText > 0.25) then\n\t\tself:Remove()\n\tend\nend\n\nfunction PANEL:OnRemove()\n\ttimer.Remove(\"ixIntroStart\")\n\n\tif (IsValid(self.channel)) then\n\t\tself.channel:Stop()\n\tend\n\n\tif (IsValid(ix.gui.characterMenu)) then\n\t\tix.gui.characterMenu:PlayMusic()\n\tend\nend\n\nfunction PANEL:Remove(bForce)\n\tif (bForce) then\n\t\tBaseClass.Remove(self)\n\t\treturn\n\tend\n\n\tif (self.bClosing) then\n\t\treturn\n\tend\n\n\tself.bClosing = true\n\tself.bBackground = nil\n\n\t-- waves\n\tlocal animation = self:AnimateWaves(1.1, true)\n\n\tanimation.OnComplete = function(anim, panel)\n\t\tpanel:SetMouseInputEnabled(false)\n\t\tpanel:SetKeyboardInputEnabled(false)\n\tend\n\n\t-- audio\n\tself:CreateAnimation(4.5, {\n\t\tindex = 1,\n\t\ttarget = {volume = 0},\n\n\t\tThink = function(anim, panel)\n\t\t\tif (IsValid(panel.channel)) then\n\t\t\t\tpanel.channel:SetVolume(panel.volume)\n\t\t\tend\n\t\tend,\n\n\t\tOnComplete = function()\n\t\t\ttimer.Simple(0, function()\n\t\t\t\tBaseClass.Remove(self)\n\t\t\tend)\n\t\tend\n\t})\nend\n\nvgui.Register(\"ixIntro\", PANEL, \"EditablePanel\")\n"
  },
  {
    "path": "gamemode/core/derma/cl_inventory.lua",
    "content": "\nlocal RECEIVER_NAME = \"ixInventoryItem\"\n\n-- The queue for the rendered icons.\nICON_RENDER_QUEUE = ICON_RENDER_QUEUE or {}\n\n-- To make making inventory variant, This must be followed up.\nlocal function RenderNewIcon(panel, itemTable)\n\tlocal model = itemTable:GetModel()\n\n\t-- re-render icons\n\tif ((itemTable.iconCam and !ICON_RENDER_QUEUE[string.lower(model)]) or itemTable.forceRender) then\n\t\tlocal iconCam = itemTable.iconCam\n\t\ticonCam = {\n\t\t\tcam_pos = iconCam.pos,\n\t\t\tcam_ang = iconCam.ang,\n\t\t\tcam_fov = iconCam.fov,\n\t\t}\n\t\tICON_RENDER_QUEUE[string.lower(model)] = true\n\n\t\tpanel.Icon:RebuildSpawnIconEx(\n\t\t\ticonCam\n\t\t)\n\tend\nend\n\nlocal function InventoryAction(action, itemID, invID, data)\n\tnet.Start(\"ixInventoryAction\")\n\t\tnet.WriteString(action)\n\t\tnet.WriteUInt(itemID, 32)\n\t\tnet.WriteUInt(invID, 32)\n\t\tnet.WriteTable(data or {})\n\tnet.SendToServer()\nend\n\nlocal PANEL = {}\n\nAccessorFunc(PANEL, \"itemTable\", \"ItemTable\")\nAccessorFunc(PANEL, \"inventoryID\", \"InventoryID\")\n\nfunction PANEL:Init()\n\tself:Droppable(RECEIVER_NAME)\nend\n\nfunction PANEL:OnMousePressed(code)\n\tif (code == MOUSE_LEFT and self:IsDraggable()) then\n\t\tself:MouseCapture(true)\n\t\tself:DragMousePress(code)\n\n\t\tself.clickX, self.clickY = input.GetCursorPos()\n\telseif (code == MOUSE_RIGHT and self.DoRightClick) then\n\t\tself:DoRightClick()\n\tend\nend\n\nfunction PANEL:OnMouseReleased(code)\n\t-- move the item into the world if we're dropping on something that doesn't handle inventory item drops\n\tif (!dragndrop.m_ReceiverSlot or dragndrop.m_ReceiverSlot.Name != RECEIVER_NAME) then\n\t\tself:OnDrop(dragndrop.IsDragging())\n\tend\n\n\tself:DragMouseRelease(code)\n\tself:SetZPos(99)\n\tself:MouseCapture(false)\nend\n\nfunction PANEL:DoRightClick()\n\tlocal itemTable = self.itemTable\n\tlocal inventory = self.inventoryID\n\n\tif (itemTable and inventory) then\n\t\titemTable.player = LocalPlayer()\n\n\t\tlocal menu = DermaMenu()\n\t\tlocal override = hook.Run(\"CreateItemInteractionMenu\", self, menu, itemTable)\n\n\t\tif (override == true) then\n\t\t\tif (menu.Remove) then\n\t\t\t\tmenu:Remove()\n\t\t\tend\n\n\t\t\treturn\n\t\tend\n\n\t\tfor k, v in SortedPairs(itemTable.functions) do\n\t\t\tif (k == \"drop\" or k == \"combine\" or (v.OnCanRun and v.OnCanRun(itemTable) == false)) then\n\t\t\t\tcontinue\n\t\t\tend\n\n\t\t\t-- is Multi-Option Function\n\t\t\tif (v.isMulti) then\n\t\t\t\tlocal subMenu, subMenuOption = menu:AddSubMenu(L(v.name or k), function()\n\t\t\t\t\titemTable.player = LocalPlayer()\n\t\t\t\t\t\tlocal send = true\n\n\t\t\t\t\t\tif (v.OnClick) then\n\t\t\t\t\t\t\tsend = v.OnClick(itemTable)\n\t\t\t\t\t\tend\n\n\t\t\t\t\t\tif (v.sound) then\n\t\t\t\t\t\t\tsurface.PlaySound(v.sound)\n\t\t\t\t\t\tend\n\n\t\t\t\t\t\tif (send != false) then\n\t\t\t\t\t\t\tInventoryAction(k, itemTable.id, inventory)\n\t\t\t\t\t\tend\n\t\t\t\t\titemTable.player = nil\n\t\t\t\tend)\n\t\t\t\tsubMenuOption:SetImage(v.icon or \"icon16/brick.png\")\n\n\t\t\t\tif (v.multiOptions) then\n\t\t\t\t\tlocal options = isfunction(v.multiOptions) and v.multiOptions(itemTable, LocalPlayer()) or v.multiOptions\n\n\t\t\t\t\tfor _, sub in pairs(options) do\n\t\t\t\t\t\tsubMenu:AddOption(L(sub.name or \"subOption\"), function()\n\t\t\t\t\t\t\titemTable.player = LocalPlayer()\n\t\t\t\t\t\t\t\tlocal send = true\n\n\t\t\t\t\t\t\t\tif (sub.OnClick) then\n\t\t\t\t\t\t\t\t\tsend = sub.OnClick(itemTable)\n\t\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\t\t\tif (sub.sound) then\n\t\t\t\t\t\t\t\t\tsurface.PlaySound(sub.sound)\n\t\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\t\t\tif (send != false) then\n\t\t\t\t\t\t\t\t\tInventoryAction(k, itemTable.id, inventory, sub.data)\n\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\titemTable.player = nil\n\t\t\t\t\t\tend)\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\telse\n\t\t\t\tmenu:AddOption(L(v.name or k), function()\n\t\t\t\t\titemTable.player = LocalPlayer()\n\t\t\t\t\t\tlocal send = true\n\n\t\t\t\t\t\tif (v.OnClick) then\n\t\t\t\t\t\t\tsend = v.OnClick(itemTable)\n\t\t\t\t\t\tend\n\n\t\t\t\t\t\tif (v.sound) then\n\t\t\t\t\t\t\tsurface.PlaySound(v.sound)\n\t\t\t\t\t\tend\n\n\t\t\t\t\t\tif (send != false) then\n\t\t\t\t\t\t\tInventoryAction(k, itemTable.id, inventory)\n\t\t\t\t\t\tend\n\t\t\t\t\titemTable.player = nil\n\t\t\t\tend):SetImage(v.icon or \"icon16/brick.png\")\n\t\t\tend\n\t\tend\n\n\t\t-- we want drop to show up as the last option\n\t\tlocal info = itemTable.functions.drop\n\n\t\tif (info and info.OnCanRun and info.OnCanRun(itemTable) != false) then\n\t\t\tmenu:AddOption(L(info.name or \"drop\"), function()\n\t\t\t\titemTable.player = LocalPlayer()\n\t\t\t\t\tlocal send = true\n\n\t\t\t\t\tif (info.OnClick) then\n\t\t\t\t\t\tsend = info.OnClick(itemTable)\n\t\t\t\t\tend\n\n\t\t\t\t\tif (info.sound) then\n\t\t\t\t\t\tsurface.PlaySound(info.sound)\n\t\t\t\t\tend\n\n\t\t\t\t\tif (send != false) then\n\t\t\t\t\t\tInventoryAction(\"drop\", itemTable.id, inventory)\n\t\t\t\t\tend\n\t\t\t\titemTable.player = nil\n\t\t\tend):SetImage(info.icon or \"icon16/brick.png\")\n\t\tend\n\n\t\tmenu:Open()\n\t\titemTable.player = nil\n\tend\nend\n\nfunction PANEL:OnDrop(bDragging, inventoryPanel, inventory, gridX, gridY)\n\tlocal item = self.itemTable\n\n\tif (!item or !bDragging) then\n\t\treturn\n\tend\n\n\tif (!IsValid(inventoryPanel)) then\n\t\tlocal inventoryID = self.inventoryID\n\n\t\tif (inventoryID) then\n\t\t\tInventoryAction(\"drop\", item.id, inventoryID, {})\n\t\tend\n\telseif (inventoryPanel:IsAllEmpty(gridX, gridY, item.width, item.height, self)) then\n\t\tlocal oldX, oldY = self.gridX, self.gridY\n\n\t\tif (oldX != gridX or oldY != gridY or self.inventoryID != inventoryPanel.invID) then\n\t\t\tself:Move(gridX, gridY, inventoryPanel)\n\t\tend\n\telseif (inventoryPanel.combineItem) then\n\t\tlocal combineItem = inventoryPanel.combineItem\n\t\tlocal inventoryID = combineItem.invID\n\n\t\tif (inventoryID) then\n\t\t\tcombineItem.player = LocalPlayer()\n\t\t\t\tif (combineItem.functions.combine.sound) then\n\t\t\t\t\tsurface.PlaySound(combineItem.functions.combine.sound)\n\t\t\t\tend\n\n\t\t\t\tInventoryAction(\"combine\", combineItem.id, inventoryID, {item.id})\n\t\t\tcombineItem.player = nil\n\t\tend\n\tend\nend\n\nfunction PANEL:Move(newX, newY, givenInventory, bNoSend)\n\tlocal iconSize = givenInventory.iconSize\n\tlocal oldX, oldY = self.gridX, self.gridY\n\tlocal oldParent = self:GetParent()\n\n\tif (givenInventory:OnTransfer(oldX, oldY, newX, newY, oldParent, bNoSend) == false) then\n\t\treturn\n\tend\n\n\tlocal x = (newX - 1) * iconSize + 4\n\tlocal y = (newY - 1) * iconSize + givenInventory:GetPadding(2)\n\n\tself.gridX = newX\n\tself.gridY = newY\n\n\tself:SetParent(givenInventory)\n\tself:SetPos(x, y)\n\n\tif (self.slots) then\n\t\tfor _, v in ipairs(self.slots) do\n\t\t\tif (IsValid(v) and v.item == self) then\n\t\t\t\tv.item = nil\n\t\t\tend\n\t\tend\n\tend\n\n\tself.slots = {}\n\n\tfor currentX = 1, self.gridW do\n\t\tfor currentY = 1, self.gridH do\n\t\t\tlocal slot = givenInventory.slots[self.gridX + currentX - 1][self.gridY + currentY - 1]\n\n\t\t\tslot.item = self\n\t\t\tself.slots[#self.slots + 1] = slot\n\t\tend\n\tend\nend\n\nfunction PANEL:PaintOver(width, height)\n\tlocal itemTable = self.itemTable\n\n\tif (itemTable and itemTable.PaintOver) then\n\t\titemTable.PaintOver(self, itemTable, width, height)\n\tend\nend\n\nfunction PANEL:ExtraPaint(width, height)\nend\n\nfunction PANEL:Paint(width, height)\n\tsurface.SetDrawColor(0, 0, 0, 85)\n\tsurface.DrawRect(2, 2, width - 4, height - 4)\n\n\tself:ExtraPaint(width, height)\nend\n\nvgui.Register(\"ixItemIcon\", PANEL, \"SpawnIcon\")\n\nPANEL = {}\nDEFINE_BASECLASS(\"DFrame\")\n\nAccessorFunc(PANEL, \"iconSize\", \"IconSize\", FORCE_NUMBER)\nAccessorFunc(PANEL, \"bHighlighted\", \"Highlighted\", FORCE_BOOL)\n\nfunction PANEL:Init()\n\tself:SetIconSize(64)\n\tself:ShowCloseButton(false)\n\tself:SetDraggable(true)\n\tself:SetSizable(true)\n\tself:SetTitle(L\"inv\")\n\tself:Receiver(RECEIVER_NAME, self.ReceiveDrop)\n\n\tself.btnMinim:SetVisible(false)\n\tself.btnMinim:SetMouseInputEnabled(false)\n\tself.btnMaxim:SetVisible(false)\n\tself.btnMaxim:SetMouseInputEnabled(false)\n\n\tself.panels = {}\nend\n\nfunction PANEL:GetPadding(index)\n\treturn select(index, self:GetDockPadding())\nend\n\nfunction PANEL:SetTitle(text)\n\tif (text == nil) then\n\t\tself.oldPadding = {self:GetDockPadding()}\n\n\t\tself.lblTitle:SetText(\"\")\n\t\tself.lblTitle:SetVisible(false)\n\n\t\tself:DockPadding(5, 5, 5, 5)\n\telse\n\t\tif (self.oldPadding) then\n\t\t\tself:DockPadding(unpack(self.oldPadding))\n\t\t\tself.oldPadding = nil\n\t\tend\n\n\t\tBaseClass.SetTitle(self, text)\n\tend\nend\n\nfunction PANEL:FitParent(invWidth, invHeight)\n\tlocal parent = self:GetParent()\n\n\tif (!IsValid(parent)) then\n\t\treturn\n\tend\n\n\tlocal width, height = parent:GetSize()\n\tlocal padding = 4\n\tlocal iconSize\n\n\tif (invWidth > invHeight) then\n\t\ticonSize = (width - padding * 2) / invWidth\n\telseif (invHeight > invWidth) then\n\t\ticonSize = (height - padding * 2) / invHeight\n\telse\n\t\t-- we use height because the titlebar will make it more tall than it is wide\n\t\ticonSize = (height - padding * 2) / invHeight - 4\n\tend\n\n\tself:SetSize(iconSize * invWidth + padding * 2, iconSize * invHeight + padding * 2)\n\tself:SetIconSize(iconSize)\nend\n\nfunction PANEL:OnRemove()\n\tif (self.childPanels) then\n\t\tfor _, v in ipairs(self.childPanels) do\n\t\t\tif (v != self) then\n\t\t\t\tv:Remove()\n\t\t\tend\n\t\tend\n\tend\nend\n\nfunction PANEL:ViewOnly()\n\tself.viewOnly = true\n\n\tfor _, icon in pairs(self.panels) do\n\t\ticon.OnMousePressed = nil\n\t\ticon.OnMouseReleased = nil\n\t\ticon.doRightClick = nil\n\tend\nend\n\nfunction PANEL:SetInventory(inventory, bFitParent)\n\tif (inventory.slots) then\n\t\tlocal invWidth, invHeight = inventory:GetSize()\n\t\tself.invID = inventory:GetID()\n\n\t\tif (IsValid(ix.gui.inv1) and ix.gui.inv1.childPanels and inventory != LocalPlayer():GetCharacter():GetInventory()) then\n\t\t\tself:SetIconSize(ix.gui.inv1:GetIconSize())\n\t\t\tself:SetPaintedManually(true)\n\t\t\tself.bNoBackgroundBlur = true\n\n\t\t\tix.gui.inv1.childPanels[#ix.gui.inv1.childPanels + 1] = self\n\t\telseif (bFitParent) then\n\t\t\tself:FitParent(invWidth, invHeight)\n\t\telse\n\t\t\tself:SetSize(self.iconSize, self.iconSize)\n\t\tend\n\n\t\tself:SetGridSize(invWidth, invHeight)\n\n\t\tfor x, items in pairs(inventory.slots) do\n\t\t\tfor y, data in pairs(items) do\n\t\t\t\tif (!data.id) then continue end\n\n\t\t\t\tlocal item = ix.item.instances[data.id]\n\n\t\t\t\tif (item and !IsValid(self.panels[item.id])) then\n\t\t\t\t\tlocal icon = self:AddIcon(item:GetModel() or \"models/props_junk/popcan01a.mdl\",\n\t\t\t\t\t\tx, y, item.width, item.height, item:GetSkin())\n\n\t\t\t\t\tif (IsValid(icon)) then\n\t\t\t\t\t\ticon:SetHelixTooltip(function(tooltip)\n\t\t\t\t\t\t\tix.hud.PopulateItemTooltip(tooltip, item)\n\t\t\t\t\t\tend)\n\n\t\t\t\t\t\tself.panels[item.id] = icon\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend\nend\n\nfunction PANEL:SetGridSize(w, h)\n\tlocal iconSize = self.iconSize\n\tlocal newWidth = w * iconSize + 8\n\tlocal newHeight = h * iconSize + self:GetPadding(2) + self:GetPadding(4)\n\n\tself.gridW = w\n\tself.gridH = h\n\n\tself:SetSize(newWidth, newHeight)\n\tself:SetMinWidth(newWidth)\n\tself:SetMinHeight(newHeight)\n\tself:BuildSlots()\nend\n\nfunction PANEL:PerformLayout(width, height)\n\tBaseClass.PerformLayout(self, width, height)\n\n\tif (self.Sizing and self.gridW and self.gridH) then\n\t\tlocal newWidth = (width - 8) / self.gridW\n\t\tlocal newHeight = (height - self:GetPadding(2) + self:GetPadding(4)) / self.gridH\n\n\t\tself:SetIconSize((newWidth + newHeight) / 2)\n\t\tself:RebuildItems()\n\tend\nend\n\nfunction PANEL:BuildSlots()\n\tlocal iconSize = self.iconSize\n\n\tself.slots = self.slots or {}\n\n\tfor _, v in ipairs(self.slots) do\n\t\tfor _, v2 in ipairs(v) do\n\t\t\tv2:Remove()\n\t\tend\n\tend\n\n\tself.slots = {}\n\n\tfor x = 1, self.gridW do\n\t\tself.slots[x] = {}\n\n\t\tfor y = 1, self.gridH do\n\t\t\tlocal slot = self:Add(\"DPanel\")\n\t\t\tslot:SetZPos(-999)\n\t\t\tslot.gridX = x\n\t\t\tslot.gridY = y\n\t\t\tslot:SetPos((x - 1) * iconSize + 4, (y - 1) * iconSize + self:GetPadding(2))\n\t\t\tslot:SetSize(iconSize, iconSize)\n\t\t\tslot.Paint = function(panel, width, height)\n\t\t\t\tderma.SkinFunc(\"PaintInventorySlot\", panel, width, height)\n\t\t\tend\n\n\t\t\tself.slots[x][y] = slot\n\t\tend\n\tend\nend\n\nfunction PANEL:RebuildItems()\n\tlocal iconSize = self.iconSize\n\n\tfor x = 1, self.gridW do\n\t\tfor y = 1, self.gridH do\n\t\t\tlocal slot = self.slots[x][y]\n\n\t\t\tslot:SetPos((x - 1) * iconSize + 4, (y - 1) * iconSize + self:GetPadding(2))\n\t\t\tslot:SetSize(iconSize, iconSize)\n\t\tend\n\tend\n\n\tfor _, v in pairs(self.panels) do\n\t\tif (IsValid(v)) then\n\t\t\tv:SetPos(self.slots[v.gridX][v.gridY]:GetPos())\n\t\t\tv:SetSize(v.gridW * iconSize, v.gridH * iconSize)\n\t\tend\n\tend\nend\n\nfunction PANEL:PaintDragPreview(width, height, mouseX, mouseY, itemPanel)\n\tlocal iconSize = self.iconSize\n\tlocal item = itemPanel:GetItemTable()\n\n\tif (item) then\n\t\tlocal inventory = ix.item.inventories[self.invID]\n\t\tlocal dropX = math.ceil((mouseX - 4 - (itemPanel.gridW - 1) * 32) / iconSize)\n\t\tlocal dropY = math.ceil((mouseY - self:GetPadding(2) - (itemPanel.gridH - 1) * 32) / iconSize)\n\n\t\tlocal hoveredPanel = vgui.GetHoveredPanel()\n\n\t\tif (IsValid(hoveredPanel) and hoveredPanel != itemPanel and hoveredPanel.GetItemTable) then\n\t\t\tlocal hoveredItem = hoveredPanel:GetItemTable()\n\n\t\t\tif (hoveredItem) then\n\t\t\t\tlocal info = hoveredItem.functions.combine\n\n\t\t\t\tif (info and info.OnCanRun and info.OnCanRun(hoveredItem, {item.id}) != false) then\n\t\t\t\t\tsurface.SetDrawColor(ColorAlpha(derma.GetColor(\"Info\", self, Color(200, 0, 0)), 20))\n\t\t\t\t\tsurface.DrawRect(\n\t\t\t\t\t\thoveredPanel.x,\n\t\t\t\t\t\thoveredPanel.y,\n\t\t\t\t\t\thoveredPanel:GetWide(),\n\t\t\t\t\t\thoveredPanel:GetTall()\n\t\t\t\t\t)\n\n\t\t\t\t\tself.combineItem = hoveredItem\n\n\t\t\t\t\treturn\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\n\t\tself.combineItem = nil\n\n\t\t-- don't draw grid if we're dragging it out of bounds\n\t\tif (inventory) then\n\t\t\tlocal invWidth, invHeight = inventory:GetSize()\n\n\t\t\tif (dropX < 1 or dropY < 1 or\n\t\t\t\tdropX + itemPanel.gridW - 1 > invWidth or\n\t\t\t\tdropY + itemPanel.gridH - 1 > invHeight) then\n\t\t\t\treturn\n\t\t\tend\n\t\tend\n\n\t\tlocal bEmpty = true\n\n\t\tfor x = 0, itemPanel.gridW - 1 do\n\t\t\tfor y = 0, itemPanel.gridH - 1 do\n\t\t\t\tlocal x2 = dropX + x\n\t\t\t\tlocal y2 = dropY + y\n\n\t\t\t\tbEmpty = self:IsEmpty(x2, y2, itemPanel)\n\n\t\t\t\tif (!bEmpty) then\n\t\t\t\t\t-- no need to iterate further since we know something is blocking the hovered grid cells, break through both loops\n\t\t\t\t\tgoto finish\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\n\t\t::finish::\n\t\tlocal previewColor = ColorAlpha(derma.GetColor(bEmpty and \"Success\" or \"Error\", self, Color(200, 0, 0)), 20)\n\n\t\tsurface.SetDrawColor(previewColor)\n\t\tsurface.DrawRect(\n\t\t\t(dropX - 1) * iconSize + 4,\n\t\t\t(dropY - 1) * iconSize + self:GetPadding(2),\n\t\t\titemPanel:GetWide(),\n\t\t\titemPanel:GetTall()\n\t\t)\n\tend\nend\n\nfunction PANEL:PaintOver(width, height)\n\tlocal panel = self.previewPanel\n\n\tif (IsValid(panel)) then\n\t\tlocal itemPanel = (dragndrop.GetDroppable() or {})[1]\n\n\t\tif (IsValid(itemPanel)) then\n\t\t\tself:PaintDragPreview(width, height, self.previewX, self.previewY, itemPanel)\n\t\tend\n\tend\n\n\tself.previewPanel = nil\nend\n\nfunction PANEL:IsEmpty(x, y, this)\n\treturn (self.slots[x] and self.slots[x][y]) and (!IsValid(self.slots[x][y].item) or self.slots[x][y].item == this)\nend\n\nfunction PANEL:IsAllEmpty(x, y, width, height, this)\n\tfor x2 = 0, width - 1 do\n\t\tfor y2 = 0, height - 1 do\n\t\t\tif (!self:IsEmpty(x + x2, y + y2, this)) then\n\t\t\t\treturn false\n\t\t\tend\n\t\tend\n\tend\n\n\treturn true\nend\n\nfunction PANEL:OnTransfer(oldX, oldY, x, y, oldInventory, noSend)\n\tlocal inventories = ix.item.inventories\n\tlocal inventory = inventories[oldInventory.invID]\n\tlocal inventory2 = inventories[self.invID]\n\tlocal item\n\n\tif (inventory) then\n\t\titem = inventory:GetItemAt(oldX, oldY)\n\n\t\tif (!item) then\n\t\t\treturn false\n\t\tend\n\n\t\tif (hook.Run(\"CanTransferItem\", item, inventories[oldInventory.invID], inventories[self.invID]) == false) then\n\t\t\treturn false, \"notAllowed\"\n\t\tend\n\n\t\tif (item.CanTransfer and\n\t\t\titem:CanTransfer(inventory, inventory != inventory2 and inventory2 or nil) == false) then\n\t\t\treturn false\n\t\tend\n\tend\n\n\tif (!noSend) then\n\t\tnet.Start(\"ixInventoryMove\")\n\t\t\tnet.WriteUInt(oldX, 6)\n\t\t\tnet.WriteUInt(oldY, 6)\n\t\t\tnet.WriteUInt(x, 6)\n\t\t\tnet.WriteUInt(y, 6)\n\t\t\tnet.WriteUInt(oldInventory.invID, 32)\n\t\t\tnet.WriteUInt(self != oldInventory and self.invID or oldInventory.invID, 32)\n\t\tnet.SendToServer()\n\tend\n\n\tif (inventory) then\n\t\tinventory.slots[oldX][oldY] = nil\n\tend\n\n\tif (item and inventory2) then\n\t\tinventory2.slots[x] = inventory2.slots[x] or {}\n\t\tinventory2.slots[x][y] = item\n\tend\nend\n\nfunction PANEL:AddIcon(model, x, y, w, h, skin)\n\tlocal iconSize = self.iconSize\n\n\tw = w or 1\n\th = h or 1\n\n\tif (self.slots[x] and self.slots[x][y]) then\n\t\tlocal panel = self:Add(\"ixItemIcon\")\n\t\tpanel:SetSize(w * iconSize, h * iconSize)\n\t\tpanel:SetZPos(999)\n\t\tpanel:InvalidateLayout(true)\n\t\tpanel:SetModel(model, skin)\n\t\tpanel:SetPos(self.slots[x][y]:GetPos())\n\t\tpanel.gridX = x\n\t\tpanel.gridY = y\n\t\tpanel.gridW = w\n\t\tpanel.gridH = h\n\n\t\tlocal inventory = ix.item.inventories[self.invID]\n\n\t\tif (!inventory) then\n\t\t\treturn\n\t\tend\n\n\t\tlocal itemTable = inventory:GetItemAt(panel.gridX, panel.gridY)\n\n\t\tpanel:SetInventoryID(inventory:GetID())\n\t\tpanel:SetItemTable(itemTable)\n\n\t\tif (self.panels[itemTable:GetID()]) then\n\t\t\tself.panels[itemTable:GetID()]:Remove()\n\t\tend\n\n\t\tif (itemTable.exRender) then\n\t\t\tpanel.Icon:SetVisible(false)\n\t\t\tpanel.ExtraPaint = function(this, panelX, panelY)\n\t\t\t\tlocal exIcon = ikon:GetIcon(itemTable.uniqueID)\n\t\t\t\tif (exIcon) then\n\t\t\t\t\tsurface.SetMaterial(exIcon)\n\t\t\t\t\tsurface.SetDrawColor(color_white)\n\t\t\t\t\tsurface.DrawTexturedRect(0, 0, panelX, panelY)\n\t\t\t\telse\n\t\t\t\t\tikon:renderIcon(\n\t\t\t\t\t\titemTable.uniqueID,\n\t\t\t\t\t\titemTable.width,\n\t\t\t\t\t\titemTable.height,\n\t\t\t\t\t\titemTable:GetModel(),\n\t\t\t\t\t\titemTable.iconCam\n\t\t\t\t\t)\n\t\t\t\tend\n\t\t\tend\n\t\telse\n\t\t\t-- yeah..\n\t\t\tRenderNewIcon(panel, itemTable)\n\t\tend\n\n\t\tpanel.slots = {}\n\n\t\tfor i = 0, w - 1 do\n\t\t\tfor i2 = 0, h - 1 do\n\t\t\t\tlocal slot = self.slots[x + i] and self.slots[x + i][y + i2]\n\n\t\t\t\tif (IsValid(slot)) then\n\t\t\t\t\tslot.item = panel\n\t\t\t\t\tpanel.slots[#panel.slots + 1] = slot\n\t\t\t\telse\n\t\t\t\t\tfor _, v in ipairs(panel.slots) do\n\t\t\t\t\t\tv.item = nil\n\t\t\t\t\tend\n\n\t\t\t\t\tpanel:Remove()\n\n\t\t\t\t\treturn\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\n\t\treturn panel\n\tend\nend\n\nfunction PANEL:ReceiveDrop(panels, bDropped, menuIndex, x, y)\n\tlocal panel = panels[1]\n\n\tif (!IsValid(panel)) then\n\t\tself.previewPanel = nil\n\t\treturn\n\tend\n\n\tif (bDropped) then\n\t\tlocal inventory = ix.item.inventories[self.invID]\n\n\t\tif (inventory and panel.OnDrop) then\n\t\t\tlocal dropX = math.ceil((x - 4 - (panel.gridW - 1) * 32) / self.iconSize)\n\t\t\tlocal dropY = math.ceil((y - self:GetPadding(2) - (panel.gridH - 1) * 32) / self.iconSize)\n\n\t\t\tpanel:OnDrop(true, self, inventory, dropX, dropY)\n\t\tend\n\n\t\tself.previewPanel = nil\n\telse\n\t\tself.previewPanel = panel\n\t\tself.previewX = x\n\t\tself.previewY = y\n\tend\nend\n\nvgui.Register(\"ixInventory\", PANEL, \"DFrame\")\n\nhook.Add(\"CreateMenuButtons\", \"ixInventory\", function(tabs)\n\tif (hook.Run(\"CanPlayerViewInventory\") == false) then\n\t\treturn\n\tend\n\n\ttabs[\"inv\"] = {\n\t\tbDefault = true,\n\t\tCreate = function(info, container)\n\t\t\tlocal canvas = container:Add(\"DTileLayout\")\n\t\t\tlocal canvasLayout = canvas.PerformLayout\n\t\t\tcanvas.PerformLayout = nil -- we'll layout after we add the panels instead of each time one is added\n\t\t\tcanvas:SetBorder(0)\n\t\t\tcanvas:SetSpaceX(2)\n\t\t\tcanvas:SetSpaceY(2)\n\t\t\tcanvas:Dock(FILL)\n\n\t\t\tix.gui.menuInventoryContainer = canvas\n\n\t\t\tlocal panel = canvas:Add(\"ixInventory\")\n\t\t\tpanel:SetPos(0, 0)\n\t\t\tpanel:SetDraggable(false)\n\t\t\tpanel:SetSizable(false)\n\t\t\tpanel:SetTitle(nil)\n\t\t\tpanel.bNoBackgroundBlur = true\n\t\t\tpanel.childPanels = {}\n\n\t\t\tlocal inventory = LocalPlayer():GetCharacter():GetInventory()\n\n\t\t\tif (inventory) then\n\t\t\t\tpanel:SetInventory(inventory)\n\t\t\tend\n\n\t\t\tix.gui.inv1 = panel\n\n\t\t\tif (ix.option.Get(\"openBags\", true)) then\n\t\t\t\tfor k, _ in inventory:Iter() do\n\t\t\t\t\tif (!k.isBag) then\n\t\t\t\t\t\tcontinue\n\t\t\t\t\tend\n\n\t\t\t\t\tk.functions.View.OnClick(k)\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tcanvas.PerformLayout = canvasLayout\n\t\t\tcanvas:Layout()\n\t\tend\n\t}\nend)\n\nhook.Add(\"PostRenderVGUI\", \"ixInvHelper\", function()\n\tlocal pnl = ix.gui.inv1\n\n\thook.Run(\"PostDrawInventory\", pnl)\nend)\n"
  },
  {
    "path": "gamemode/core/derma/cl_menu.lua",
    "content": "\nlocal animationTime = 1\nlocal matrixZScale = Vector(1, 1, 0.0001)\n\nDEFINE_BASECLASS(\"ixSubpanelParent\")\nlocal PANEL = {}\n\nAccessorFunc(PANEL, \"bCharacterOverview\", \"CharacterOverview\", FORCE_BOOL)\n\nfunction PANEL:Init()\n\tif (IsValid(ix.gui.menu)) then\n\t\tix.gui.menu:Remove()\n\tend\n\n\tix.gui.menu = self\n\n\t-- properties\n\tself.manualChildren = {}\n\tself.noAnchor = CurTime() + 0.4\n\tself.anchorMode = true\n\tself.rotationOffset = Angle(0, 180, 0)\n\tself.projectedTexturePosition = Vector(0, 0, 6)\n\tself.projectedTextureRotation = Angle(-45, 60, 0)\n\n\tself.bCharacterOverview = false\n\tself.bOverviewOut = false\n\tself.overviewFraction = 0\n\n\tself.currentAlpha = 0\n\tself.currentBlur = 0\n\n\t-- setup\n\tself:SetPadding(ScreenScale(16), true)\n\tself:SetSize(ScrW(), ScrH())\n\tself:SetPos(0, 0)\n\tself:SetLeftOffset(self:GetWide() * 0.25 + self:GetPadding())\n\n\t-- main button panel\n\tself.buttons = self:Add(\"Panel\")\n\tself.buttons:SetSize(self:GetWide() * 0.25, self:GetTall() - self:GetPadding() * 2)\n\tself.buttons:Dock(LEFT)\n\tself.buttons:SetPaintedManually(true)\n\n\tlocal close = self.buttons:Add(\"ixMenuButton\")\n\tclose:SetText(\"return\")\n\tclose:SizeToContents()\n\tclose:Dock(BOTTOM)\n\tclose.DoClick = function()\n\t\tself:Remove()\n\tend\n\n\tlocal characters = self.buttons:Add(\"ixMenuButton\")\n\tcharacters:SetText(\"characters\")\n\tcharacters:SizeToContents()\n\tcharacters:Dock(BOTTOM)\n\tcharacters.DoClick = function()\n\t\tself:Remove()\n\t\tvgui.Create(\"ixCharMenu\")\n\tend\n\n\t-- @todo make a better way to avoid clicks in the padding PLEASE\n\tself.guard = self:Add(\"Panel\")\n\tself.guard:SetPos(0, 0)\n\tself.guard:SetSize(self:GetPadding(), self:GetTall())\n\n\t-- tabs\n\tself.tabs = self.buttons:Add(\"DScrollPanel\")\n\tself.tabs.buttons = {}\n\tself.tabs:Dock(FILL)\n\tself:PopulateTabs()\n\n\tself:MakePopup()\n\tself:OnOpened()\nend\n\nfunction PANEL:OnOpened()\n\tself:SetAlpha(0)\n\n\tself:CreateAnimation(animationTime, {\n\t\ttarget = {currentAlpha = 255},\n\t\teasing = \"outQuint\",\n\n\t\tThink = function(animation, panel)\n\t\t\tpanel:SetAlpha(panel.currentAlpha)\n\t\tend\n\t})\nend\n\nfunction PANEL:GetActiveTab()\n\treturn (self:GetActiveSubpanel() or {}).subpanelName\nend\n\nfunction PANEL:TransitionSubpanel(id)\n\tlocal lastSubpanel = self:GetActiveSubpanel()\n\n\t-- don't transition to the same panel\n\tif (IsValid(lastSubpanel) and lastSubpanel.subpanelID == id) then\n\t\treturn\n\tend\n\n\tlocal subpanel = self:GetSubpanel(id)\n\n\tif (IsValid(subpanel)) then\n\t\tif (!subpanel.bPopulated) then\n\t\t\t-- we need to set the size of the subpanel if it's a section since it will be 0, 0\n\t\t\tif (subpanel.sectionParent) then\n\t\t\t\tsubpanel:SetSize(self:GetStandardSubpanelSize())\n\t\t\tend\n\n\t\t\tlocal info = subpanel.info\n\t\t\tsubpanel.Paint = nil\n\n\t\t\tif (istable(info) and info.Create) then\n\t\t\t\tinfo:Create(subpanel)\n\t\t\telseif (isfunction(info)) then\n\t\t\t\tinfo(subpanel)\n\t\t\tend\n\n\t\t\thook.Run(\"MenuSubpanelCreated\", subpanel.subpanelName, subpanel)\n\t\t\tsubpanel.bPopulated = true\n\t\tend\n\n\t\t-- only play whoosh sound only when the menu was already open\n\t\tif (IsValid(lastSubpanel)) then\n\t\t\tLocalPlayer():EmitSound(\"Helix.Whoosh\")\n\t\tend\n\n\t\tself:SetActiveSubpanel(id)\n\tend\n\n\tsubpanel = self:GetActiveSubpanel()\n\n\tlocal info = subpanel.info\n\tlocal bHideBackground = istable(info) and (info.bHideBackground != nil and info.bHideBackground or false) or false\n\n\tif (bHideBackground) then\n\t\tself:HideBackground()\n\telse\n\t\tself:ShowBackground()\n\tend\n\n\t-- call hooks if we've changed subpanel\n\tif (IsValid(lastSubpanel) and istable(lastSubpanel.info) and lastSubpanel.info.OnDeselected) then\n\t\tlastSubpanel.info:OnDeselected(lastSubpanel)\n\tend\n\n\tif (IsValid(subpanel) and istable(subpanel.info) and subpanel.info.OnSelected) then\n\t\tsubpanel.info:OnSelected(subpanel)\n\tend\n\n\tix.gui.lastMenuTab = id\nend\n\nfunction PANEL:SetCharacterOverview(bValue, length)\n\tbValue = tobool(bValue)\n\tlength = length or animationTime\n\n\tif (bValue) then\n\t\tif (!IsValid(self.projectedTexture)) then\n\t\t\tself.projectedTexture = ProjectedTexture()\n\t\tend\n\n\t\tlocal faction = ix.faction.indices[LocalPlayer():Team()]\n\t\tlocal color = faction and faction.color or color_white\n\n\t\tself.projectedTexture:SetEnableShadows(false)\n\t\tself.projectedTexture:SetNearZ(12)\n\t\tself.projectedTexture:SetFarZ(64)\n\t\tself.projectedTexture:SetFOV(90)\n\t\tself.projectedTexture:SetColor(color)\n\t\tself.projectedTexture:SetTexture(\"effects/flashlight/soft\")\n\n\t\tself:CreateAnimation(length, {\n\t\t\tindex = 3,\n\t\t\ttarget = {overviewFraction = 1},\n\t\t\teasing = \"outQuint\",\n\t\t\tbIgnoreConfig = true\n\t\t})\n\n\t\tself.bOverviewOut = false\n\t\tself.bCharacterOverview = true\n\telse\n\t\tself:CreateAnimation(length, {\n\t\t\tindex = 3,\n\t\t\ttarget = {overviewFraction = 0},\n\t\t\teasing = \"outQuint\",\n\t\t\tbIgnoreConfig = true,\n\n\t\t\tOnComplete = function(animation, panel)\n\t\t\t\tpanel.bCharacterOverview = false\n\n\t\t\t\tif (IsValid(panel.projectedTexture)) then\n\t\t\t\t\tpanel.projectedTexture:Remove()\n\t\t\t\tend\n\t\t\tend\n\t\t})\n\n\t\tself.bOverviewOut = true\n\tend\nend\n\nfunction PANEL:GetOverviewInfo(origin, angles, fov)\n\tlocal originAngles = Angle(0, angles.yaw, angles.roll)\n\tlocal target = LocalPlayer():GetObserverTarget()\n\tlocal fraction = self.overviewFraction\n\tlocal bDrawPlayer = ((fraction > 0.2) or (!self.bOverviewOut and (fraction > 0.2))) and !IsValid(target)\n\tlocal forward = originAngles:Forward() * 58 - originAngles:Right() * 16\n\tforward.z = 0\n\n\tlocal newOrigin\n\n\tif (IsValid(target)) then\n\t\tnewOrigin = target:GetPos() + forward\n\telse\n\t\tnewOrigin = origin - LocalPlayer():OBBCenter() * 0.6 + forward\n\tend\n\n\tlocal newAngles = originAngles + self.rotationOffset\n\tnewAngles.pitch = 5\n\tnewAngles.roll = 0\n\n\treturn LerpVector(fraction, origin, newOrigin), LerpAngle(fraction, angles, newAngles), Lerp(fraction, fov, 90), bDrawPlayer\nend\n\nfunction PANEL:HideBackground()\n\tself:CreateAnimation(animationTime, {\n\t\tindex = 2,\n\t\ttarget = {currentBlur = 0},\n\t\teasing = \"outQuint\"\n\t})\nend\n\nfunction PANEL:ShowBackground()\n\tself:CreateAnimation(animationTime, {\n\t\tindex = 2,\n\t\ttarget = {currentBlur = 1},\n\t\teasing = \"outQuint\"\n\t})\nend\n\nfunction PANEL:GetStandardSubpanelSize()\n\treturn ScrW() * 0.75 - self:GetPadding() * 3, ScrH() - self:GetPadding() * 2\nend\n\nfunction PANEL:SetupTab(name, info, sectionParent)\n\tlocal bTable = istable(info)\n\tlocal buttonColor = (bTable and info.buttonColor) or (ix.config.Get(\"color\") or Color(140, 140, 140, 255))\n\tlocal bDefault = (bTable and info.bDefault) or false\n\tlocal qualifiedName = sectionParent and (sectionParent.name .. \"/\" .. name) or name\n\n\t-- setup subpanels without populating them so we can retain the order\n\tlocal subpanel = self:AddSubpanel(qualifiedName, true)\n\tlocal id = subpanel.subpanelID\n\tsubpanel.info = info\n\tsubpanel.sectionParent = sectionParent and qualifiedName\n\tsubpanel:SetPaintedManually(true)\n\tsubpanel:SetTitle(nil)\n\n\tif (sectionParent) then\n\t\t-- hide section subpanels if they haven't been populated to seeing more subpanels than necessary\n\t\t-- fly by as you navigate tabs in the menu\n\t\tsubpanel:SetSize(0, 0)\n\telse\n\t\tsubpanel:SetSize(self:GetStandardSubpanelSize())\n\n\t\t-- this is called while the subpanel has not been populated\n\t\tsubpanel.Paint = function(panel, width, height)\n\t\t\tderma.SkinFunc(\"PaintPlaceholderPanel\", panel, width, height)\n\t\tend\n\tend\n\n\tlocal button\n\n\tif (sectionParent) then\n\t\tbutton = sectionParent:AddSection(L(name))\n\t\tname = qualifiedName\n\telse\n\t\tbutton = self.tabs:Add(\"ixMenuSelectionButton\")\n\t\tbutton:SetText(L(name))\n\t\tbutton:SizeToContents()\n\t\tbutton:Dock(TOP)\n\t\tbutton:SetButtonList(self.tabs.buttons)\n\t\tbutton:SetBackgroundColor(buttonColor)\n\tend\n\n\tbutton.name = name\n\tbutton.id = id\n\tbutton.OnSelected = function()\n\t\tself:TransitionSubpanel(id)\n\tend\n\n\tif (bTable and info.PopulateTabButton) then\n\t\tinfo:PopulateTabButton(button)\n\tend\n\n\t-- don't allow sections in sections\n\tif (sectionParent or !bTable or !info.Sections) then\n\t\treturn bDefault, button, subpanel\n\tend\n\n\t-- create button sections\n\tfor sectionName, sectionInfo in pairs(info.Sections) do\n\t\tself:SetupTab(sectionName, sectionInfo, button)\n\tend\n\n\treturn bDefault, button, subpanel\nend\n\nfunction PANEL:PopulateTabs()\n\tlocal default\n\tlocal tabs = {}\n\n\thook.Run(\"CreateMenuButtons\", tabs)\n\n\tfor name, info in SortedPairs(tabs) do\n\t\tlocal bDefault, button = self:SetupTab(name, info)\n\n\t\tif (bDefault) then\n\t\t\tdefault = button\n\t\tend\n\tend\n\n\tif (ix.gui.lastMenuTab) then\n\t\tfor i = 1, #self.tabs.buttons do\n\t\t\tlocal button = self.tabs.buttons[i]\n\n\t\t\tif (button.id == ix.gui.lastMenuTab) then\n\t\t\t\tdefault = button\n\t\t\t\tbreak\n\t\t\tend\n\t\tend\n\tend\n\n\tif (!IsValid(default) and #self.tabs.buttons > 0) then\n\t\tdefault = self.tabs.buttons[1]\n\tend\n\n\tif (IsValid(default)) then\n\t\tdefault:SetSelected(true)\n\t\tself:SetActiveSubpanel(default.id, 0)\n\tend\n\n\tself.buttons:MoveToFront()\n\tself.guard:MoveToBefore(self.buttons)\nend\n\nfunction PANEL:AddManuallyPaintedChild(panel)\n\tpanel:SetParent(self)\n\tpanel:SetPaintedManually(panel)\n\n\tself.manualChildren[#self.manualChildren + 1] = panel\nend\n\nfunction PANEL:OnKeyCodePressed(key)\n\tself.noAnchor = CurTime() + 0.5\n\n\tif (key == KEY_TAB) then\n\t\tself:Remove()\n\tend\nend\n\nfunction PANEL:Think()\n\tif (IsValid(self.projectedTexture)) then\n\t\tlocal forward = LocalPlayer():GetForward()\n\t\tforward.z = 0\n\n\t\tlocal right = LocalPlayer():GetRight()\n\t\tright.z = 0\n\n\t\tself.projectedTexture:SetBrightness(self.overviewFraction * 4)\n\t\tself.projectedTexture:SetPos(LocalPlayer():GetPos() + right * 16 - forward * 8 + self.projectedTexturePosition)\n\t\tself.projectedTexture:SetAngles(forward:Angle() + self.projectedTextureRotation)\n\t\tself.projectedTexture:Update()\n\tend\n\n\tif (self.bClosing) then\n\t\treturn\n\tend\n\n\tlocal bTabDown = input.IsKeyDown(KEY_TAB)\n\n\tif (bTabDown and (self.noAnchor or CurTime() + 0.4) < CurTime() and self.anchorMode) then\n\t\tself.anchorMode = false\n\t\tsurface.PlaySound(\"buttons/lightswitch2.wav\")\n\tend\n\n\tif ((!self.anchorMode and !bTabDown) or gui.IsGameUIVisible()) then\n\t\tself:Remove()\n\n\t\tif (ix.option.Get(\"escCloseMenu\", false)) then\n\t\t\tgui.HideGameUI()\n\t\tend\n\tend\nend\n\nfunction PANEL:Paint(width, height)\n\tderma.SkinFunc(\"PaintMenuBackground\", self, width, height, self.currentBlur)\n\n\tlocal bShouldScale = self.currentAlpha != 255\n\n\tif (bShouldScale) then\n\t\tlocal currentScale = Lerp(self.currentAlpha / 255, 0.9, 1)\n\t\tlocal matrix = Matrix()\n\n\t\tmatrix:Scale(matrixZScale * currentScale)\n\t\tmatrix:Translate(Vector(\n\t\t\tScrW() * 0.5 - (ScrW() * currentScale * 0.5),\n\t\t\tScrH() * 0.5 - (ScrH() * currentScale * 0.5),\n\t\t\t1\n\t\t))\n\n\t\tcam.PushModelMatrix(matrix)\n\tend\n\n\tBaseClass.Paint(self, width, height)\n\tself:PaintSubpanels(width, height)\n\tself.buttons:PaintManual()\n\n\tfor i = 1, #self.manualChildren do\n\t\tself.manualChildren[i]:PaintManual()\n\tend\n\n\tif (IsValid(ix.gui.inv1) and ix.gui.inv1.childPanels) then\n\t\tfor i = 1, #ix.gui.inv1.childPanels do\n\t\t\tlocal panel = ix.gui.inv1.childPanels[i]\n\n\t\t\tif (IsValid(panel)) then\n\t\t\t\tpanel:PaintManual()\n\t\t\tend\n\t\tend\n\tend\n\n\tif (bShouldScale) then\n\t\tcam.PopModelMatrix()\n\tend\nend\n\nfunction PANEL:PerformLayout()\n\tself.guard:SetSize(self.tabs:GetWide() + self:GetPadding() * 2, self:GetTall())\nend\n\nfunction PANEL:Remove()\n\tself.bClosing = true\n\tself:SetMouseInputEnabled(false)\n\tself:SetKeyboardInputEnabled(false)\n\tself:SetCharacterOverview(false, animationTime * 0.5)\n\n\t-- remove input from opened child panels since they grab focus\n\tif (IsValid(ix.gui.inv1) and ix.gui.inv1.childPanels) then\n\t\tfor i = 1, #ix.gui.inv1.childPanels do\n\t\t\tlocal panel = ix.gui.inv1.childPanels[i]\n\n\t\t\tif (IsValid(panel)) then\n\t\t\t\tpanel:SetMouseInputEnabled(false)\n\t\t\t\tpanel:SetKeyboardInputEnabled(false)\n\t\t\tend\n\t\tend\n\tend\n\n\tCloseDermaMenus()\n\tgui.EnableScreenClicker(false)\n\n\tself:CreateAnimation(animationTime * 0.5, {\n\t\tindex = 2,\n\t\ttarget = {currentBlur = 0},\n\t\teasing = \"outQuint\"\n\t})\n\n\tself:CreateAnimation(animationTime * 0.5, {\n\t\ttarget = {currentAlpha = 0},\n\t\teasing = \"outQuint\",\n\n\t\t-- we don't animate the blur because blurring doesn't draw things\n\t\t-- with amount < 1 very well, resulting in jarring transition\n\t\tThink = function(animation, panel)\n\t\t\tpanel:SetAlpha(panel.currentAlpha)\n\t\tend,\n\n\t\tOnComplete = function(animation, panel)\n\t\t\tif (IsValid(panel.projectedTexture)) then\n\t\t\t\tpanel.projectedTexture:Remove()\n\t\t\tend\n\n\t\t\tBaseClass.Remove(panel)\n\t\tend\n\t})\nend\n\nvgui.Register(\"ixMenu\", PANEL, \"ixSubpanelParent\")\n\nif (IsValid(ix.gui.menu)) then\n\tix.gui.menu:Remove()\nend\n\nix.gui.lastMenuTab = nil\n"
  },
  {
    "path": "gamemode/core/derma/cl_menubutton.lua",
    "content": "\nlocal buttonPadding = ScreenScale(14) * 0.5\nlocal animationTime = 0.5\n\n-- base menu button\nDEFINE_BASECLASS(\"DButton\")\nlocal PANEL = {}\n\nAccessorFunc(PANEL, \"backgroundColor\", \"BackgroundColor\")\nAccessorFunc(PANEL, \"backgroundAlpha\", \"BackgroundAlpha\")\n\nfunction PANEL:Init()\n\tself:SetFont(\"ixMenuButtonFont\")\n\tself:SetTextColor(color_white)\n\tself:SetPaintBackground(false)\n\tself:SetContentAlignment(4)\n\tself:SetTextInset(buttonPadding, 0)\n\n\tself.padding = {32, 12, 32, 12} -- left, top, right, bottom\n\tself.backgroundColor = Color(0, 0, 0)\n\tself.backgroundAlpha = 128\n\tself.currentBackgroundAlpha = 0\nend\n\nfunction PANEL:GetPadding()\n\treturn self.padding\nend\n\nfunction PANEL:SetPadding(left, top, right, bottom)\n\tself.padding = {\n\t\tleft or self.padding[1],\n\t\ttop or self.padding[2],\n\t\tright or self.padding[3],\n\t\tbottom or self.padding[4]\n\t}\nend\n\nfunction PANEL:SetText(text, noTranslation)\n\tBaseClass.SetText(self, noTranslation and text:utf8upper() or L(text):utf8upper())\nend\n\nfunction PANEL:SizeToContents()\n\tBaseClass.SizeToContents(self)\n\n\tlocal width, height = self:GetSize()\n\tself:SetSize(width + self.padding[1] + self.padding[3], height + self.padding[2] + self.padding[4])\nend\n\nfunction PANEL:PaintBackground(width, height)\n\tsurface.SetDrawColor(ColorAlpha(self.backgroundColor, self.currentBackgroundAlpha))\n\tsurface.DrawRect(0, 0, width, height)\nend\n\nfunction PANEL:Paint(width, height)\n\tself:PaintBackground(width, height)\n\tBaseClass.Paint(self, width, height)\nend\n\nfunction PANEL:SetTextColorInternal(color)\n\tBaseClass.SetTextColor(self, color)\n\tself:SetFGColor(color)\nend\n\nfunction PANEL:SetTextColor(color)\n\tself:SetTextColorInternal(color)\n\tself.color = color\nend\n\nfunction PANEL:SetDisabled(bValue)\n\tlocal color = self.color\n\n\tif (bValue) then\n\t\tself:SetTextColorInternal(Color(math.max(color.r - 60, 0), math.max(color.g - 60, 0), math.max(color.b - 60, 0)))\n\telse\n\t\tself:SetTextColorInternal(color)\n\tend\n\n\tBaseClass.SetDisabled(self, bValue)\nend\n\nfunction PANEL:OnCursorEntered()\n\tif (self:GetDisabled()) then\n\t\treturn\n\tend\n\n\tlocal color = self:GetTextColor()\n\tself:SetTextColorInternal(Color(math.max(color.r - 25, 0), math.max(color.g - 25, 0), math.max(color.b - 25, 0)))\n\n\tself:CreateAnimation(0.15, {\n\t\ttarget = {currentBackgroundAlpha = self.backgroundAlpha}\n\t})\n\n\tLocalPlayer():EmitSound(\"Helix.Rollover\")\nend\n\nfunction PANEL:OnCursorExited()\n\tif (self:GetDisabled()) then\n\t\treturn\n\tend\n\n\tif (self.color) then\n\t\tself:SetTextColor(self.color)\n\telse\n\t\tself:SetTextColor(color_white)\n\tend\n\n\tself:CreateAnimation(0.15, {\n\t\ttarget = {currentBackgroundAlpha = 0}\n\t})\nend\n\nfunction PANEL:OnMousePressed(code)\n\tif (self:GetDisabled()) then\n\t\treturn\n\tend\n\n\tif (self.color) then\n\t\tself:SetTextColor(self.color)\n\telse\n\t\tself:SetTextColor(ix.config.Get(\"color\"))\n\tend\n\n\tLocalPlayer():EmitSound(\"Helix.Press\")\n\n\tif (code == MOUSE_LEFT and self.DoClick) then\n\t\tself:DoClick(self)\n\telseif (code == MOUSE_RIGHT and self.DoRightClick) then\n\t\tself:DoRightClick(self)\n\tend\nend\n\nfunction PANEL:OnMouseReleased(key)\n\tif (self:GetDisabled()) then\n\t\treturn\n\tend\n\n\tif (self.color) then\n\t\tself:SetTextColor(self.color)\n\telse\n\t\tself:SetTextColor(color_white)\n\tend\nend\n\nvgui.Register(\"ixMenuButton\", PANEL, \"DButton\")\n\n-- selection menu button\nDEFINE_BASECLASS(\"ixMenuButton\")\nPANEL = {}\n\nAccessorFunc(PANEL, \"backgroundColor\", \"BackgroundColor\")\nAccessorFunc(PANEL, \"selected\", \"Selected\", FORCE_BOOL)\nAccessorFunc(PANEL, \"buttonList\", \"ButtonList\")\n\nfunction PANEL:Init()\n\tself.backgroundColor = color_white\n\tself.selected = false\n\tself.buttonList = {}\n\tself.sectionPanel = nil -- sub-sections this button has; created only if it has any sections\nend\n\nfunction PANEL:PaintBackground(width, height)\n\tlocal alpha = self.selected and 255 or self.currentBackgroundAlpha\n\n\tderma.SkinFunc(\"DrawImportantBackground\", 0, 0, width, height, ColorAlpha(self.backgroundColor, alpha))\nend\n\nfunction PANEL:SetSelected(bValue, bSelectedSection)\n\tself.selected = bValue\n\n\tif (bValue) then\n\t\tself:OnSelected()\n\n\t\tif (self.sectionPanel) then\n\t\t\tself.sectionPanel:Show()\n\t\telseif (self.sectionParent) then\n\t\t\tself.sectionParent.sectionPanel:Show()\n\t\tend\n\telseif (self.sectionPanel and self.sectionPanel:IsVisible() and !bSelectedSection) then\n\t\tself.sectionPanel:Hide()\n\tend\nend\n\nfunction PANEL:SetButtonList(list, bNoAdd)\n\tif (!bNoAdd) then\n\t\tlist[#list + 1] = self\n\tend\n\n\tself.buttonList = list\nend\n\nfunction PANEL:GetSectionPanel()\n\treturn self.sectionPanel\nend\n\nfunction PANEL:AddSection(name)\n\tif (!IsValid(self.sectionPanel)) then\n\t\t-- add section panel to regular button list\n\t\tself.sectionPanel = vgui.Create(\"ixMenuSelectionList\", self:GetParent())\n\t\tself.sectionPanel:Dock(self:GetDock())\n\t\tself.sectionPanel:SetParentButton(self)\n\tend\n\n\treturn self.sectionPanel:AddButton(name, self.buttonList)\nend\n\nfunction PANEL:OnMousePressed(key)\n\tfor _, v in pairs(self.buttonList) do\n\t\tif (IsValid(v) and v != self) then\n\t\t\tv:SetSelected(false, self.sectionParent == v)\n\t\tend\n\tend\n\n\tself:SetSelected(true)\n\tBaseClass.OnMousePressed(self, key)\nend\n\nfunction PANEL:OnSelected()\nend\n\nvgui.Register(\"ixMenuSelectionButton\", PANEL, \"ixMenuButton\")\n\n-- collapsable list for menu button sections\nPANEL = {}\nAccessorFunc(PANEL, \"parent\", \"ParentButton\")\n\nfunction PANEL:Init()\n\tself.parent = nil -- button that is responsible for controlling this list\n\tself.height = 0\n\tself.targetHeight = 0\n\n\tself:DockPadding(0, 1, 0, 1)\n\tself:SetVisible(false)\n\tself:SetTall(0)\nend\n\nfunction PANEL:AddButton(name, buttonList)\n\tassert(IsValid(self.parent), \"attempted to add button to ixMenuSelectionList without a ParentButton\")\n\tassert(buttonList ~= nil, \"attempted to add button to ixMenuSelectionList without a buttonList\")\n\n\tlocal button = self:Add(\"ixMenuSelectionButton\")\n\tbutton.sectionParent = self.parent\n\tbutton:SetTextInset(buttonPadding * 2, 0)\n\tbutton:SetPadding(nil, 8, nil, 8)\n\tbutton:SetFont(\"ixMenuButtonFontSmall\")\n\tbutton:Dock(TOP)\n\tbutton:SetText(name)\n\tbutton:SizeToContents()\n\tbutton:SetButtonList(buttonList)\n\tbutton:SetBackgroundColor(self.parent:GetBackgroundColor())\n\n\tself.targetHeight = self.targetHeight + button:GetTall()\n\treturn button\nend\n\nfunction PANEL:Show()\n\tself:SetVisible(true)\n\n\tself:CreateAnimation(animationTime, {\n\t\tindex = 1,\n\t\ttarget = {\n\t\t\theight = self.targetHeight + 2 -- +2 for padding\n\t\t},\n\t\teasing = \"outQuart\",\n\n\t\tThink = function(animation, panel)\n\t\t\tpanel:SetTall(panel.height)\n\t\tend\n\t})\nend\n\nfunction PANEL:Hide()\n\tself:CreateAnimation(animationTime, {\n\t\tindex = 1,\n\t\ttarget = {\n\t\t\theight = 0\n\t\t},\n\t\teasing = \"outQuint\",\n\n\t\tThink = function(animation, panel)\n\t\t\tpanel:SetTall(panel.height)\n\t\tend,\n\n\t\tOnComplete = function(animation, panel)\n\t\t\tpanel:SetVisible(false)\n\t\tend\n\t})\nend\n\nfunction PANEL:Paint(width, height)\n\tsurface.SetDrawColor(Color(255, 255, 255, 33))\n\tsurface.DrawRect(0, 0, width, 1)\n\tsurface.DrawRect(0, height - 1, width, 1)\nend\n\nvgui.Register(\"ixMenuSelectionList\", PANEL, \"Panel\")\n"
  },
  {
    "path": "gamemode/core/derma/cl_modelpanel.lua",
    "content": "\nDEFINE_BASECLASS(\"DModelPanel\")\n\nlocal PANEL = {}\nlocal MODEL_ANGLE = Angle(0, 45, 0)\n\nfunction PANEL:Init()\n\tself.brightness = 1\n\tself:SetCursor(\"none\")\nend\n\nfunction PANEL:SetModel(model, skin, bodygroups)\n\tif (IsValid(self.Entity)) then\n\t\tself.Entity:Remove()\n\t\tself.Entity = nil\n\tend\n\n\tif (!ClientsideModel) then\n\t\treturn\n\tend\n\n\tlocal entity = ClientsideModel(model, RENDERGROUP_OPAQUE)\n\n\tif (!IsValid(entity)) then\n\t\treturn\n\tend\n\n\tentity:SetNoDraw(true)\n\tentity:SetIK(false)\n\n\tif (skin) then\n\t\tentity:SetSkin(skin)\n\tend\n\n\tif (isstring(bodygroups)) then\n\t\tentity:SetBodyGroups(bodygroups)\n\tend\n\n\tlocal sequence = entity:LookupSequence(\"idle_unarmed\")\n\n\tif (sequence <= 0) then\n\t\tsequence = entity:SelectWeightedSequence(ACT_IDLE)\n\tend\n\n\tif (sequence > 0) then\n\t\tentity:ResetSequence(sequence)\n\telse\n\t\tlocal found = false\n\n\t\tfor _, v in ipairs(entity:GetSequenceList()) do\n\t\t\tif ((v:lower():find(\"idle\") or v:lower():find(\"fly\")) and v != \"idlenoise\") then\n\t\t\t\tentity:ResetSequence(v)\n\t\t\t\tfound = true\n\n\t\t\t\tbreak\n\t\t\tend\n\t\tend\n\n\t\tif (!found) then\n\t\t\tentity:ResetSequence(4)\n\t\tend\n\tend\n\n\tself.Entity = entity\nend\n\nfunction PANEL:LayoutEntity()\n\tlocal scrW, scrH = ScrW(), ScrH()\n\tlocal xRatio = gui.MouseX() / scrW\n\tlocal yRatio = gui.MouseY() / scrH\n\tlocal x, _ = self:LocalToScreen(self:GetWide() / 2)\n\tlocal xRatio2 = x / scrW\n\tlocal entity = self.Entity\n\n\tentity:SetPoseParameter(\"head_pitch\", yRatio*90 - 30)\n\tentity:SetPoseParameter(\"head_yaw\", (xRatio - xRatio2)*90 - 5)\n\tentity:SetAngles(MODEL_ANGLE)\n\tentity:SetIK(false)\n\n\tif (self.copyLocalSequence) then\n\t\tentity:SetSequence(LocalPlayer():GetSequence())\n\t\tentity:SetPoseParameter(\"move_yaw\", 360 * LocalPlayer():GetPoseParameter(\"move_yaw\") - 180)\n\tend\n\n\tself:RunAnimation()\nend\n\nfunction PANEL:DrawModel()\n\tlocal brightness = self.brightness * 0.4\n\tlocal brightness2 = self.brightness * 1.5\n\n\trender.SetStencilEnable(false)\n\trender.SetColorMaterial()\n\trender.SetColorModulation(1, 1, 1)\n\trender.SetModelLighting(0, brightness2, brightness2, brightness2)\n\n\tfor i = 1, 4 do\n\t\trender.SetModelLighting(i, brightness, brightness, brightness)\n\tend\n\n\tlocal fraction = (brightness / 1) * 0.1\n\n\trender.SetModelLighting(5, fraction, fraction, fraction)\n\n\t-- Excecute Some stuffs\n\tif (self.enableHook) then\n\t\thook.Run(\"DrawHelixModelView\", self, self.Entity)\n\tend\n\n\tself.Entity:DrawModel()\n\n\tif (self.enableHook) then\n\t\thook.Run(\"PostDrawHelixModelView\", self, self.Entity)\n\tend\nend\n\nfunction PANEL:OnMousePressed()\nend\n\nvgui.Register(\"ixModelPanel\", PANEL, \"DModelPanel\")\n"
  },
  {
    "path": "gamemode/core/derma/cl_notice.lua",
    "content": "\nlocal animationTime = 0.75\n\n-- notice manager\n-- this manages positions/animations for notice panels\nlocal PANEL = {}\n\nAccessorFunc(PANEL, \"padding\", \"Padding\", FORCE_NUMBER)\n\nfunction PANEL:Init()\n\tself:SetSize(ScrW() * 0.4, ScrH())\n\tself:SetPos(ScrW() - ScrW() * 0.4, 0)\n\tself:SetZPos(-99999)\n\tself:SetMouseInputEnabled(false)\n\tself:SetKeyboardInputEnabled(false)\n\n\tself.notices = {}\n\tself.padding = 4\nend\n\nfunction PANEL:GetAll()\n\treturn self.notices\nend\n\nfunction PANEL:Clear()\n\tfor _, v in ipairs(self.notices) do\n\t\tself:RemoveNotice(v)\n\tend\nend\n\nfunction PANEL:AddNotice(text, bError)\n\tif (IsValid(ix.gui.characterMenu) and !ix.gui.characterMenu.bClosing) then\n\t\treturn\n\tend\n\n\tlocal textLength = text:utf8len()\n\n\tlocal panel = self:Add(\"ixNotice\")\n\tpanel:SetText(text)\n\tpanel:SetError(bError or text:utf8sub(textLength, textLength) == \"!\")\n\tpanel:SizeToContents()\n\tpanel.currentY = -panel:GetTall()\n\tpanel:SetPos(self.padding, panel.currentY)\n\n\t-- setup duration timer\n\tpanel:CreateAnimation(ix.option.Get(\"noticeDuration\", 8), {\n\t\tindex = 2,\n\t\ttarget = {duration = 1},\n\t\tbIgnoreConfig = true,\n\n\t\tOnComplete = function(animation, this)\n\t\t\tself:RemoveNotice(this)\n\t\tend\n\t})\n\n\ttable.insert(self.notices, 1, panel)\n\tself:Organize()\n\n\t-- remove old notice if we've hit the limit of notices\n\tif (#self.notices > ix.option.Get(\"noticeMax\", 4)) then\n\t\tfor i = #self.notices, 1, -1 do\n\t\t\tlocal notice = self.notices[i]\n\n\t\t\tif (IsValid(notice) and !notice.bClosing) then\n\t\t\t\tself:RemoveNotice(notice)\n\t\t\t\tbreak\n\t\t\tend\n\t\tend\n\tend\n\n\treturn panel\nend\n\nfunction PANEL:RemoveNotice(panel)\n\tpanel.bClosing = true\n\tpanel:CreateAnimation(animationTime, {\n\t\tindex = 3,\n\t\ttarget = {outAnimation = 0},\n\t\teasing = \"outQuint\",\n\n\t\tOnComplete = function(animation, this)\n\t\t\tlocal toRemove\n\n\t\t\tfor k, v in ipairs(self.notices) do\n\t\t\t\tif (v == this) then\n\t\t\t\t\ttoRemove = k\n\t\t\t\t\tbreak\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tif (toRemove) then\n\t\t\t\ttable.remove(self.notices, toRemove)\n\t\t\tend\n\n\t\t\tthis:SetText(\"\") -- (hack) text remains for a frame after remove is called, so let's make sure we don't draw it\n\t\t\tthis:Remove()\n\t\tend\n\t})\nend\n\n-- update target Y positions and animations\nfunction PANEL:Organize()\n\tlocal currentTarget = self.padding\n\n\tfor _, v in ipairs(self.notices) do\n\t\tv:CreateAnimation(animationTime, {\n\t\t\tindex = 1,\n\t\t\ttarget = {currentY = currentTarget},\n\t\t\teasing = \"outElastic\",\n\n\t\t\tThink = function(animation, panel)\n\t\t\t\tpanel:SetPos(\n\t\t\t\t\tself:GetWide() - panel:GetWide() - self.padding,\n\t\t\t\t\tmath.min(panel.currentY + 1, currentTarget) -- easing eventually hits subpixel movement so we level it off\n\t\t\t\t)\n\t\t\tend\n\t\t})\n\n\t\tcurrentTarget = currentTarget + self.padding + v:GetTall()\n\tend\nend\n\nvgui.Register(\"ixNoticeManager\", PANEL, \"Panel\")\n\n-- notice panel\n-- these do not manage their own enter/exit animations or lifetime\nDEFINE_BASECLASS(\"DLabel\")\nPANEL = {}\n\nAccessorFunc(PANEL, \"bError\", \"Error\", FORCE_BOOL)\nAccessorFunc(PANEL, \"padding\", \"Padding\", FORCE_NUMBER)\n\nfunction PANEL:Init()\n\tself:SetSize(256, 36)\n\tself:SetContentAlignment(5)\n\tself:SetExpensiveShadow(1, Color(0, 0, 0, 150))\n\tself:SetFont(\"ixNoticeFont\")\n\tself:SetTextColor(color_white)\n\tself:SetDrawOnTop(true)\n\tself:DockPadding(0, 0, 0, 0)\n\tself:DockMargin(0, 0, 0, 0)\n\n\tself.bError = false\n\tself.bHovered = false\n\n\tself.errorAnimation = 0\n\tself.padding = 8\n\tself.currentY = 0\n\tself.duration = 0\n\tself.outAnimation = 1\n\tself.alpha = 255\n\n\tLocalPlayer():EmitSound(\"Helix.Notify\")\nend\n\nfunction PANEL:SetError(bValue)\n\tself.bError = tobool(bValue)\n\n\tif (bValue) then\n\t\tself.errorAnimation = 1\n\t\tself:CreateAnimation(animationTime, {\n\t\t\tindex = 5,\n\t\t\ttarget = {errorAnimation = 0},\n\t\t\teasing = \"outQuint\"\n\t\t})\n\tend\nend\n\nfunction PANEL:SizeToContents()\n\tlocal contentWidth, contentHeight = self:GetContentSize()\n\tcontentWidth = contentWidth + self.padding * 2\n\tcontentHeight = contentHeight + self.padding * 2\n\n\tlocal manager = ix.gui.notices\n\tlocal maxWidth = math.min(IsValid(manager) and (manager:GetWide() - manager:GetPadding() * 2) or ScrW(), contentWidth)\n\n\tif (contentWidth > maxWidth) then\n\t\tself:SetWide(maxWidth)\n\t\tself:SetTextInset(self.padding * 2, 0)\n\t\tself:SetWrap(true)\n\n\t\tself:SizeToContentsY()\n\t\tself:SetWide(self:GetContentSize())\n\telse\n\t\tself:SetSize(contentWidth, contentHeight)\n\tend\nend\n\nfunction PANEL:SizeToContentsY()\n\tBaseClass.SizeToContentsY(self)\n\tself:SetTall(self:GetTall() + self.padding * 2)\nend\n\nfunction PANEL:OnMouseHover()\n\tself:CreateAnimation(animationTime * 0.5, {\n\t\tindex = 4,\n\t\ttarget = {alpha = 0},\n\t\teasing = \"outQuint\",\n\n\t\tThink = function(animation, panel)\n\t\t\tpanel:SetAlpha(panel.alpha)\n\t\tend\n\t})\nend\n\nfunction PANEL:OnMouseLeave()\n\tself:CreateAnimation(animationTime * 0.5, {\n\t\tindex = 4,\n\t\ttarget = {alpha = 255},\n\t\teasing = \"outQuint\",\n\n\t\tThink = function(animation, panel)\n\t\t\tpanel:SetAlpha(panel.alpha)\n\t\tend\n\t})\nend\n\nfunction PANEL:Paint(width, height)\n\tif (self.outAnimation < 1) then\n\t\tlocal x, y = self:LocalToScreen(0, 0)\n\t\trender.SetScissorRect(x, y, x + self:GetWide(), y + (self:GetTall() * self.outAnimation), true)\n\tend\n\n\tlocal x, y = self:LocalToScreen(0, 0)\n\tlocal mouseX, mouseY = gui.MousePos()\n\n\tif (mouseX >= x and mouseX <= x + width and\n\t\tmouseY >= y and mouseY <= y + height) then\n\t\tif (!self.bHovered) then\n\t\t\tself.bHovered = true\n\t\t\tself:OnMouseHover()\n\t\tend\n\telseif (self.bHovered) then\n\t\tself.bHovered = false\n\t\tself:OnMouseLeave()\n\tend\n\n\tix.util.DrawBlur(self)\n\n\tif (self.errorAnimation > 0) then\n\t\tlocal color = derma.GetColor(\"Error\", self)\n\n\t\tsurface.SetDrawColor(\n\t\t\tcolor.r * self.errorAnimation,\n\t\t\tcolor.g * self.errorAnimation,\n\t\t\tcolor.b * self.errorAnimation,\n\t\t\tself.errorAnimation * 255 + ((1 - self.errorAnimation) * 66)\n\t\t)\n\telse\n\t\tsurface.SetDrawColor(0, 0, 0, 66)\n\tend\n\n\tsurface.DrawRect(0, 0, width, height)\n\n\tsurface.SetDrawColor(self.bError and derma.GetColor(\"Error\", self) or ix.config.Get(\"color\"))\n\tsurface.DrawRect(0, height - 1, width * self.duration, 1)\nend\n\nfunction PANEL:PaintOver(width, height)\n\trender.SetScissorRect(0, 0, 0, 0, false)\nend\n\nvgui.Register(\"ixNotice\", PANEL, \"DLabel\")\n\nif (IsValid(ix.gui.notices)) then\n\tix.gui.notices:Remove()\n\tix.gui.notices = vgui.Create(\"ixNoticeManager\")\nelse\n\tix.gui.notices = vgui.Create(\"ixNoticeManager\")\nend\n"
  },
  {
    "path": "gamemode/core/derma/cl_noticebar.lua",
    "content": "\nlocal PANEL = {\n\ttypes = {\n\t\t\"Info\", -- info\n\t\t\"Success\", -- success\n\t\t\"Error\" -- error\n\t}\n}\n\nAccessorFunc(PANEL, \"type\", \"Type\", FORCE_NUMBER)\nAccessorFunc(PANEL, \"padding\", \"Padding\", FORCE_NUMBER)\nAccessorFunc(PANEL, \"length\", \"Length\", FORCE_NUMBER)\nAccessorFunc(PANEL, \"hidden\", \"Hidden\", FORCE_BOOL)\n\nfunction PANEL:Init()\n\tself.type = 1\n\tself.padding = 8\n\tself.length = 4\n\tself.currentY = 0\n\tself.hidden = true\n\n\tself.text = self:Add(\"DLabel\")\n\tself.text:SetFont(\"ixNoticeFont\")\n\tself.text:SetContentAlignment(5)\n\tself.text:SetTextColor(color_white)\n\tself.text:SizeToContents()\n\tself.text:Dock(FILL)\n\n\tself:SetSize(self:GetParent():GetWide() - (self.padding * 4), self.text:GetTall() + (self.padding * 2))\n\tself:SetPos(self.padding * 2, -self:GetTall() - self.padding)\nend\n\nfunction PANEL:SetFont(value)\n\tself.text:SetFont(value)\n\tself.text:SizeToContents()\nend\n\nfunction PANEL:SetText(text)\n\tself.text:SetText(text)\n\tself.text:SizeToContents()\nend\n\nfunction PANEL:Slide(direction, length)\n\tdirection = direction or \"up\"\n\tlength = length or 0.5\n\n\ttimer.Remove(\"ixNoticeBarAnimation\")\n\n\tlocal x, _ = self:GetPos()\n\tlocal baseY = direction == \"up\" and self.padding * 2 or (-self:GetTall() - self.padding)\n\tlocal targetY = direction == \"up\" and (-self:GetTall() - self.padding) or self.padding * 2\n\tlocal easing = direction == \"up\" and \"outQuint\" or \"outElastic\"\n\n\tself:SetPos(x, baseY)\n\tself.currentY = baseY\n\tself.hidden = direction == \"up\"\n\n\tself:CreateAnimation(length, {\n\t\ttarget = {currentY = targetY},\n\t\teasing = easing,\n\n\t\tThink = function(animation, panel)\n\t\t\tlocal lastX, _ = panel:GetPos()\n\t\t\tpanel:SetPos(lastX, panel.currentY)\n\t\tend\n\t})\nend\n\nfunction PANEL:Show(bRemove)\n\tself:Slide(\"down\")\n\n\ttimer.Create(\"ixNoticeBarAnimation\", self.length - 0.5, 1, function()\n\t\tif (!IsValid(self)) then\n\t\t\treturn\n\t\tend\n\n\t\tself:Slide(\"up\")\n\tend)\nend\n\nfunction PANEL:Paint(width, height)\n\tlocal color = derma.GetColor(self.types[self.type], self)\n\n\tsurface.SetDrawColor(color)\n\tsurface.DrawRect(0, 0, width, height)\nend\n\nvgui.Register(\"ixNoticeBar\", PANEL, \"Panel\")\n"
  },
  {
    "path": "gamemode/core/derma/cl_overrides.lua",
    "content": "\n-- overrides standard derma panels to add/change functionality\n\nlocal PANEL = {}\nlocal OVERRIDES = {}\n\n-- @todo remove me when autorefresh support is no longer needed\nlocal function OverridePanel(name, func)\n\tPANEL = vgui.GetControlTable(name)\n\n\tif (!istable(PANEL)) then\n\t\treturn\n\tend\n\n\tOVERRIDES = {}\n\tfunc()\n\n\tfor k, _ in pairs(PANEL) do\n\t\tlocal overrideName = \"ix\" .. k\n\n\t\tif (PANEL[overrideName] and !OVERRIDES[k]) then\n\t\t\tprint(\"unhooking override \", overrideName)\n\n\t\t\tPANEL[k] = PANEL[overrideName]\n\t\t\tPANEL[overrideName] = nil\n\t\tend\n\tend\nend\n\nlocal function Override(name)\n\tlocal oldMethod = \"ix\" .. name\n\tOVERRIDES[name] = true\n\n\tif (PANEL[oldMethod]) then\n\t\treturn\n\tend\n\n\tPANEL[oldMethod] = PANEL[name]\nend\n\nOverridePanel(\"DMenuOption\", function()\n\tfunction PANEL:PerformLayout()\n\t\tself:SizeToContents()\n\t\tself:SetWide(self:GetWide() + 30)\n\n\t\tlocal w = math.max(self:GetParent():GetWide(), self:GetWide())\n\n\t\tself:SetSize(w, self:GetTall() + 4)\n\n\t\tif (self.SubMenuArrow) then\n\t\t\tself.SubMenuArrow:SetSize(15, 15)\n\t\t\tself.SubMenuArrow:CenterVertical()\n\t\t\tself.SubMenuArrow:AlignRight(4)\n\t\tend\n\n\t\tDButton.PerformLayout(self)\n\tend\nend)\n\nOverridePanel(\"DMenu\", function()\n\tlocal animationTime = 0.33\n\n\tOverride(\"Init\")\n\tfunction PANEL:Init(...)\n\t\tself:ixInit(...)\n\n\t\tself.ixAnimation = 0\n\tend\n\n\tfunction PANEL:SetFont(font)\n\t\tfor _, v in pairs(self:GetCanvas():GetChildren()) do\n\t\t\tv:SetFont(font)\n\t\t\tv:SizeToContents()\n\t\tend\n\n\t\t-- reposition for the new font\n\t\tself:InvalidateLayout(true)\n\t\tself:Open(self.ixX, self.ixY, false, self.ixOwnerPanel)\n\tend\n\n\tOverride(\"SetSize\")\n\tfunction PANEL:SetSize(width, height)\n\t\tself:ixSetSize(width, height)\n\t\tself.ixTargetHeight = height\n\tend\n\n\tOverride(\"PerformLayout\")\n\tfunction PANEL:PerformLayout(...)\n\t\tself:ixPerformLayout(...)\n\n\t\tif (self.ixAnimating) then\n\t\t\tself.VBar:SetAlpha(0) -- setvisible doesn't seem to work here\n\t\t\tself:SetTall(self.ixAnimation * self.ixTargetHeight)\n\t\telse\n\t\t\tself.VBar:SetAlpha(255)\n\t\tend\n\tend\n\n\tOverride(\"OnMouseWheeled\")\n\tfunction PANEL:OnMouseWheeled(delta)\n\t\tself:ixOnMouseWheeled(delta)\n\n\t\t-- don't allow the input event to fall through\n\t\treturn true\n\tend\n\n\tOverride(\"AddOption\")\n\tfunction PANEL:AddOption(...)\n\t\tlocal panel = self:ixAddOption(...)\n\n\t\tpanel:SetTextColor(derma.GetColor(\"MenuLabel\", self, color_black))\n\t\tpanel:SetTextInset(6, 0) -- there is no icon functionality in DComboBoxes\n\n\t\treturn panel\n\tend\n\n\tOverride(\"AddSubMenu\")\n\tfunction PANEL:AddSubMenu(...)\n\t\tlocal menu, panel = self:ixAddSubMenu(...)\n\n\t\tpanel:SetTextColor(derma.GetColor(\"MenuLabel\", self, color_black))\n\t\tpanel:SetTextInset(6, 0) -- there is no icon functionality in DComboBoxes\n\n\t\treturn menu, panel\n\tend\n\n\tOverride(\"Open\")\n\tfunction PANEL:Open(x, y, bSkipAnimation, ownerPanel)\n\t\tself.ixX, self.ixY, self.ixOwnerPanel = x, y, ownerPanel\n\t\tself:ixOpen(x, y, bSkipAnimation, ownerPanel)\n\n\t\tif (ix.option.Get(\"disableAnimations\")) then\n\t\t\treturn\n\t\tend\n\n\t\t-- remove pac3 derma menu hooks since animations don't play nicely\n\t\thook.Remove(\"CloseDermaMenus\", self)\n\t\thook.Remove(\"Think\", self)\n\n\t\tself.ixAnimating = true\n\t\tself:CreateAnimation(animationTime, {\n\t\t\tindex = 1,\n\t\t\ttarget = {ixAnimation = 1},\n\t\t\teasing = \"outQuint\",\n\n\t\t\tThink = function(animation, panel)\n\t\t\t\tpanel:InvalidateLayout(true)\n\t\t\tend,\n\n\t\t\tOnComplete = function(animation, panel)\n\t\t\t\tpanel.ixAnimating = nil\n\t\t\tend\n\t\t})\n\tend\n\n\tOverride(\"Hide\")\n\tfunction PANEL:Hide()\n\t\tif (ix.option.Get(\"disableAnimations\")) then\n\t\t\tself:ixHide()\n\t\t\treturn\n\t\tend\n\n\t\tself.ixAnimating = true\n\t\tself:SetVisible(true)\n\t\tself:CreateAnimation(animationTime * 0.5, {\n\t\t\tindex = 1,\n\t\t\ttarget = {ixAnimation = 0},\n\t\t\teasing = \"outQuint\",\n\n\t\t\tThink = function(animation, panel)\n\t\t\t\tpanel:InvalidateLayout(true)\n\t\t\tend,\n\n\t\t\tOnComplete = function(animation, panel)\n\t\t\t\tpanel.ixAnimating = false\n\t\t\t\tpanel:ixHide()\n\t\t\tend\n\t\t})\n\tend\n\n\tOverride(\"Remove\")\n\tfunction PANEL:Remove()\n\t\tif (self.ixRemoving) then\n\t\t\treturn\n\t\tend\n\n\t\tif (ix.option.Get(\"disableAnimations\")) then\n\t\t\tself:ixRemove()\n\t\t\treturn\n\t\tend\n\n\t\tself.ixAnimating = true\n\t\tself.ixRemoving = true\n\t\tself:SetVisible(true)\n\n\t\tself:CreateAnimation(animationTime * 0.5, {\n\t\t\tindex = 1,\n\t\t\ttarget = {ixAnimation = 0},\n\t\t\teasing = \"outQuint\",\n\n\t\t\tThink = function(animation, panel)\n\t\t\t\tpanel:InvalidateLayout(true)\n\t\t\tend,\n\n\t\t\tOnComplete = function(animation, panel)\n\t\t\t\tpanel:ixRemove()\n\t\t\tend\n\t\t})\n\tend\nend)\n\nOverridePanel(\"DComboBox\", function()\n\tOverride(\"OpenMenu\")\n\tfunction PANEL:OpenMenu()\n\t\tself:ixOpenMenu()\n\n\t\tif (IsValid(self.Menu)) then\n\t\t\tlocal _, y = self.Menu:LocalToScreen(self.Menu:GetPos())\n\n\t\t\tself.Menu:SetFont(self:GetFont())\n\t\t\tself.Menu:SetMaxHeight(ScrH() - y)\n\t\tend\n\tend\nend)\n\nOverridePanel(\"DScrollPanel\", function()\n\tOverride(\"ScrollToChild\")\n\tfunction PANEL:ScrollToChild(panel)\n\t\t-- docked panels required InvalidateParent in order to retrieve their position correctly\n\t\tif (panel:GetDock() != NODOCK) then\n\t\t\tpanel:InvalidateParent(true)\n\t\telse\n\t\t\tself:PerformLayout()\n\t\tend\n\n\t\tlocal _, y = self.pnlCanvas:GetChildPosition(panel)\n\n\t\ty = y + panel:GetTall() * 0.5\n\t\ty = y - self:GetTall() * 0.5\n\n\t\tself.VBar:SetScroll(y)\n\tend\nend)\n"
  },
  {
    "path": "gamemode/core/derma/cl_scoreboard.lua",
    "content": "local rowPaintFunctions = {\n\tfunction(width, height)\n\tend,\n\n\tfunction(width, height)\n\t\tsurface.SetDrawColor(30, 30, 30, 25)\n\t\tsurface.DrawRect(0, 0, width, height)\n\tend\n}\n\n-- character icon\n-- we can't customize the rendering of ModelImage so we have to do it ourselves\nlocal PANEL = {}\nlocal BODYGROUPS_EMPTY = \"000000000\"\n\nAccessorFunc(PANEL, \"model\", \"Model\", FORCE_STRING)\nAccessorFunc(PANEL, \"bHidden\", \"Hidden\", FORCE_BOOL)\n\nfunction PANEL:Init()\n\tself:SetSize(64, 64)\n\tself.bodygroups = BODYGROUPS_EMPTY\nend\n\nfunction PANEL:SetModel(model, skin, bodygroups)\n\tmodel = model:gsub(\"\\\\\", \"/\")\n\n\tif (isstring(bodygroups)) then\n\t\tif (bodygroups:len() == 9) then\n\t\t\tfor i = 1, bodygroups:len() do\n\t\t\t\tself:SetBodygroup(i, tonumber(bodygroups[i]) or 0)\n\t\t\tend\n\t\telse\n\t\t\tself.bodygroups = BODYGROUPS_EMPTY\n\t\tend\n\tend\n\n\tself.model = model\n\tself.skin = skin\n\tself.path = \"materials/spawnicons/\" ..\n\t\tmodel:sub(1, #model - 4) .. -- remove extension\n\t\t((isnumber(skin) and skin > 0) and (\"_skin\" .. tostring(skin)) or \"\") .. -- skin number\n\t\t(self.bodygroups != BODYGROUPS_EMPTY and (\"_\" .. self.bodygroups) or \"\") .. -- bodygroups\n\t\t\".png\"\n\n\tlocal material = Material(self.path, \"smooth\")\n\n\t-- we don't have a cached spawnicon texture, so we need to forcefully generate one\n\tif (material:IsError()) then\n\t\tself.id = \"ixScoreboardIcon\" .. self.path\n\t\tself.renderer = self:Add(\"ModelImage\")\n\t\tself.renderer:SetVisible(false)\n\t\tself.renderer:SetModel(model, skin, self.bodygroups)\n\t\tself.renderer:RebuildSpawnIcon()\n\n\t\t-- this is the only way to get a callback for generated spawn icons, it's bad but it's only done once\n\t\thook.Add(\"SpawniconGenerated\", self.id, function(lastModel, filePath, modelsLeft)\n\t\t\tfilePath = filePath:gsub(\"\\\\\", \"/\"):lower()\n\n\t\t\tif (filePath == self.path) then\n\t\t\t\thook.Remove(\"SpawniconGenerated\", self.id)\n\n\t\t\t\tself.material = Material(filePath, \"smooth\")\n\t\t\t\tself.renderer:Remove()\n\t\t\tend\n\t\tend)\n\telse\n\t\tself.material = material\n\tend\nend\n\nfunction PANEL:SetBodygroup(k, v)\n\tif (k < 0 or k > 8 or v < 0 or v > 9) then\n\t\treturn\n\tend\n\n\tself.bodygroups = self.bodygroups:SetChar(k + 1, v)\nend\n\nfunction PANEL:GetModel()\n\treturn self.model or \"models/error.mdl\"\nend\n\nfunction PANEL:GetSkin()\n\treturn self.skin or 1\nend\n\nfunction PANEL:DoClick()\nend\n\nfunction PANEL:DoRightClick()\nend\n\nfunction PANEL:OnMouseReleased(key)\n\tif (key == MOUSE_LEFT) then\n\t\tself:DoClick()\n\telseif (key == MOUSE_RIGHT) then\n\t\tself:DoRightClick()\n\tend\nend\n\nfunction PANEL:Paint(width, height)\n\tif (!self.material) then\n\t\treturn\n\tend\n\n\tsurface.SetMaterial(self.material)\n\tsurface.SetDrawColor(self.bHidden and color_black or color_white)\n\tsurface.DrawTexturedRect(0, 0, width, height)\nend\n\nfunction PANEL:Remove()\n\tif (self.id) then\n\t\thook.Remove(\"SpawniconGenerated\", self.id)\n\tend\nend\n\nvgui.Register(\"ixScoreboardIcon\", PANEL, \"Panel\")\n\n-- player row\nPANEL = {}\n\nAccessorFunc(PANEL, \"paintFunction\", \"BackgroundPaintFunction\")\n\nfunction PANEL:Init()\n\tself:SetTall(64)\n\n\tself.icon = self:Add(\"ixScoreboardIcon\")\n\tself.icon:Dock(LEFT)\n\tself.icon.DoRightClick = function()\n\t\tlocal client = self.player\n\n\t\tif (!IsValid(client)) then\n\t\t\treturn\n\t\tend\n\n\t\tlocal menu = DermaMenu()\n\n\t\tmenu:AddOption(L(\"viewProfile\"), function()\n\t\t\tclient:ShowProfile()\n\t\tend)\n\n\t\tmenu:AddOption(L(\"copySteamID\"), function()\n\t\t\tSetClipboardText(client:IsBot() and client:EntIndex() or client:SteamID())\n\t\tend)\n\n\t\thook.Run(\"PopulateScoreboardPlayerMenu\", client, menu)\n\t\tmenu:Open()\n\tend\n\n\tself.icon:SetHelixTooltip(function(tooltip)\n\t\tlocal client = self.player\n\n\t\tif (IsValid(self) and IsValid(client)) then\n\t\t\tix.hud.PopulatePlayerTooltip(tooltip, client)\n\t\tend\n\tend)\n\n\tself.name = self:Add(\"DLabel\")\n\tself.name:DockMargin(4, 4, 0, 0)\n\tself.name:Dock(TOP)\n\tself.name:SetTextColor(color_white)\n\tself.name:SetFont(\"ixGenericFont\")\n\n\tself.description = self:Add(\"DLabel\")\n\tself.description:DockMargin(5, 0, 0, 0)\n\tself.description:Dock(TOP)\n\tself.description:SetTextColor(color_white)\n\tself.description:SetFont(\"ixSmallFont\")\n\n\tself.paintFunction = rowPaintFunctions[1]\n\tself.nextThink = CurTime() + 1\nend\n\nfunction PANEL:Update()\n\tlocal client = self.player\n\tlocal model = client:GetModel()\n\tlocal skin = client:GetSkin()\n\tlocal name = client:GetName()\n\tlocal description = hook.Run(\"GetCharacterDescription\", client) or\n\t\t(client:GetCharacter() and client:GetCharacter():GetDescription()) or \"\"\n\n\tlocal bRecognize = false\n\tlocal localCharacter = LocalPlayer():GetCharacter()\n\tlocal character = IsValid(self.player) and self.player:GetCharacter()\n\n\tif (localCharacter and character) then\n\t\tbRecognize = hook.Run(\"IsCharacterRecognized\", localCharacter, character:GetID())\n\t\t\tor hook.Run(\"IsPlayerRecognized\", self.player)\n\tend\n\n\tself.icon:SetHidden(!bRecognize)\n\tself:SetZPos(bRecognize and 1 or 2)\n\n\t-- no easy way to check bodygroups so we'll just set them anyway\n\tfor _, v in pairs(client:GetBodyGroups()) do\n\t\tself.icon:SetBodygroup(v.id, client:GetBodygroup(v.id))\n\tend\n\n\tif (self.icon:GetModel() != model or self.icon:GetSkin() != skin) then\n\t\tself.icon:SetModel(model, skin)\n\t\tself.icon:SetTooltip(nil)\n\tend\n\n\tif (self.name:GetText() != name) then\n\t\tself.name:SetText(name)\n\t\tself.name:SizeToContents()\n\tend\n\n\tif (self.description:GetText() != description) then\n\t\tself.description:SetText(description)\n\t\tself.description:SizeToContents()\n\tend\nend\n\nfunction PANEL:Think()\n\tif (CurTime() >= self.nextThink) then\n\t\tlocal client = self.player\n\n\t\tif (!IsValid(client) or !client:GetCharacter() or self.character != client:GetCharacter() or self.team != client:Team()) then\n\t\t\tself:Remove()\n\t\t\tself:GetParent():SizeToContents()\n\t\tend\n\n\t\tself.nextThink = CurTime() + 1\n\tend\nend\n\nfunction PANEL:SetPlayer(client)\n\tself.player = client\n\tself.team = client:Team()\n\tself.character = client:GetCharacter()\n\n\tself:Update()\nend\n\nfunction PANEL:Paint(width, height)\n\tself.paintFunction(width, height)\nend\n\nvgui.Register(\"ixScoreboardRow\", PANEL, \"EditablePanel\")\n\n-- faction grouping\nPANEL = {}\n\nAccessorFunc(PANEL, \"faction\", \"Faction\")\n\nfunction PANEL:Init()\n\tself:DockMargin(0, 0, 0, 16)\n\tself:SetTall(32)\n\n\tself.nextThink = 0\nend\n\nfunction PANEL:AddPlayer(client, index)\n\tif (!IsValid(client) or !client:GetCharacter() or hook.Run(\"ShouldShowPlayerOnScoreboard\", client) == false) then\n\t\treturn false\n\tend\n\n\tlocal id = index % 2 == 0 and 1 or 2\n\tlocal panel = self:Add(\"ixScoreboardRow\")\n\tpanel:SetPlayer(client)\n\tpanel:Dock(TOP)\n\tpanel:SetZPos(2)\n\tpanel:SetBackgroundPaintFunction(rowPaintFunctions[id])\n\n\tself:SizeToContents()\n\tclient.ixScoreboardSlot = panel\n\n\treturn true\nend\n\nfunction PANEL:SetFaction(faction)\n\tself:SetColor(faction.color)\n\tself:SetText(L(faction.name))\n\n\tself.faction = faction\nend\n\nfunction PANEL:Update()\n\tlocal faction = self.faction\n\n\tif (team.NumPlayers(faction.index) == 0) then\n\t\tself:SetVisible(false)\n\t\tself:GetParent():InvalidateLayout()\n\telse\n\t\tlocal bHasPlayers\n\n\t\tfor k, v in ipairs(team.GetPlayers(faction.index)) do\n\t\t\tif (!IsValid(v.ixScoreboardSlot)) then\n\t\t\t\tif (self:AddPlayer(v, k)) then\n\t\t\t\t\tbHasPlayers = true\n\t\t\t\tend\n\t\t\telse\n\t\t\t\tv.ixScoreboardSlot:Update()\n\t\t\t\tbHasPlayers = true\n\t\t\tend\n\t\tend\n\n\t\tself:SetVisible(bHasPlayers)\n\tend\nend\n\nvgui.Register(\"ixScoreboardFaction\", PANEL, \"ixCategoryPanel\")\n\n-- main scoreboard panel\nPANEL = {}\n\nfunction PANEL:Init()\n\tif (IsValid(ix.gui.scoreboard)) then\n\t\tix.gui.scoreboard:Remove()\n\tend\n\n\tself:Dock(FILL)\n\n\tself.factions = {}\n\tself.nextThink = 0\n\n\tfor i = 1, #ix.faction.indices do\n\t\tlocal faction = ix.faction.indices[i]\n\n\t\tlocal panel = self:Add(\"ixScoreboardFaction\")\n\t\tpanel:SetFaction(faction)\n\t\tpanel:Dock(TOP)\n\n\t\tself.factions[i] = panel\n\tend\n\n\tix.gui.scoreboard = self\nend\n\nfunction PANEL:Think()\n\tif (CurTime() >= self.nextThink) then\n\t\tfor i = 1, #self.factions do\n\t\t\tlocal factionPanel = self.factions[i]\n\n\t\t\tfactionPanel:Update()\n\t\tend\n\n\t\tself.nextThink = CurTime() + 0.5\n\tend\nend\n\nvgui.Register(\"ixScoreboard\", PANEL, \"DScrollPanel\")\n\nhook.Add(\"CreateMenuButtons\", \"ixScoreboard\", function(tabs)\n\ttabs[\"scoreboard\"] = function(container)\n\t\tcontainer:Add(\"ixScoreboard\")\n\tend\nend)\n"
  },
  {
    "path": "gamemode/core/derma/cl_settings.lua",
    "content": "\nlocal panelMap = {\n\t[ix.type.bool] = \"ixSettingsRowBool\",\n\t[ix.type.array] = \"ixSettingsRowArray\",\n\t[ix.type.string] = \"ixSettingsRowString\",\n\t[ix.type.number] = \"ixSettingsRowNumber\",\n\t[ix.type.color] = \"ixSettingsRowColor\"\n}\n\nlocal function EmitChange(pitch)\n\tLocalPlayer():EmitSound(\"weapons/ar2/ar2_empty.wav\", 75, pitch or 150, 0.25)\nend\n\n-- color setting\nlocal PANEL = {}\n\nAccessorFunc(PANEL, \"padding\", \"Padding\", FORCE_NUMBER)\n\nfunction PANEL:Init()\n\tself.color = table.Copy(color_white)\n\tself.padding = 4\n\n\tself.panel = self:Add(\"Panel\")\n\tself.panel:SetCursor(\"hand\")\n\tself.panel:SetMouseInputEnabled(true)\n\tself.panel:Dock(RIGHT)\n\tself.panel.Paint = function(panel, width, height)\n\t\tlocal padding = self.padding\n\n\t\tsurface.SetDrawColor(derma.GetColor(\"DarkerBackground\", self))\n\t\tsurface.DrawRect(0, 0, width, height)\n\n\t\tsurface.SetDrawColor(self.color)\n\t\tsurface.DrawRect(padding, padding, width - padding * 2, height - padding * 2)\n\tend\n\n\tself.panel.OnMousePressed = function(panel, key)\n\t\tif (key == MOUSE_LEFT) then\n\t\t\tself:OpenPicker()\n\t\tend\n\tend\nend\n\nfunction PANEL:OpenPicker()\n\tif (IsValid(self.picker)) then\n\t\tself.picker:Remove()\n\t\treturn\n\tend\n\n\tself.picker = vgui.Create(\"ixSettingsRowColorPicker\")\n\tself.picker:Attach(self)\n\tself.picker:SetValue(self.color)\n\n\tself.picker.OnValueChanged = function(panel)\n\t\tlocal newColor = panel:GetValue()\n\n\t\tif (newColor != self.color) then\n\t\t\tself.color = newColor\n\t\t\tself:OnValueChanged(newColor)\n\t\tend\n\tend\n\n\tself.picker.OnValueUpdated = function(panel)\n\t\tself.color = panel:GetValue()\n\tend\nend\n\nfunction PANEL:SetValue(value)\n\tself.color = Color(value.r or 255, value.g or 255, value.b or 255, value.a or 255)\nend\n\nfunction PANEL:GetValue()\n\treturn self.color\nend\n\nfunction PANEL:PerformLayout(width, height)\n\tsurface.SetFont(\"ixMenuButtonFont\")\n\tlocal totalWidth = surface.GetTextSize(\"999\")\n\n\tself.panel:SetSize(totalWidth + self.padding * 2, height)\nend\n\nvgui.Register(\"ixSettingsRowColor\", PANEL, \"ixSettingsRow\")\n\n-- color setting picker\nDEFINE_BASECLASS(\"Panel\")\nPANEL = {}\n\nAccessorFunc(PANEL, \"bDeleteSelf\", \"DeleteSelf\", FORCE_BOOL)\n\nfunction PANEL:Init()\n\tself.m_bIsMenuComponent = true\n\tself.bDeleteSelf = true\n\n\tself.realHeight = 200\n\tself.height = 200\n\tself:SetSize(250, 200)\n\tself:DockPadding(4, 4, 4, 4)\n\n\tself.picker = self:Add(\"DColorMixer\")\n\tself.picker:Dock(FILL)\n\tself.picker.ValueChanged = function()\n\t\tself:OnValueUpdated()\n\tend\n\n\tself:MakePopup()\n\tRegisterDermaMenuForClose(self)\nend\n\nfunction PANEL:SetValue(value)\n\tself.picker:SetColor(Color(value.r or 255, value.g or 255, value.b or 255, value.a or 255))\nend\n\nfunction PANEL:GetValue()\n\treturn self.picker:GetColor()\nend\n\nfunction PANEL:Attach(panel)\n\tself.attached = panel\nend\n\nfunction PANEL:Think()\n\tlocal panel = self.attached\n\n\tif (IsValid(panel)) then\n\t\tlocal width, height = self:GetSize()\n\t\tlocal x, y = panel:LocalToScreen(0, 0)\n\n\t\tself:SetPos(\n\t\t\tmath.Clamp(x + panel:GetWide() - width, 0, ScrW() - width),\n\t\t\tmath.Clamp(y + panel:GetTall(), 0, ScrH() - height)\n\t\t)\n\tend\nend\n\nfunction PANEL:Paint(width, height)\n\tsurface.SetDrawColor(derma.GetColor(\"DarkerBackground\", self))\n\tsurface.DrawRect(0, 0, width, height)\nend\n\nfunction PANEL:OnValueChanged()\nend\n\nfunction PANEL:OnValueUpdated()\nend\n\nfunction PANEL:Remove()\n\tif (self.bClosing) then\n\t\treturn\n\tend\n\n\tself:OnValueChanged()\n\n\t-- @todo open/close animations\n\tself.bClosing = true\n\tself:SetMouseInputEnabled(false)\n\tself:SetKeyboardInputEnabled(false)\n\tBaseClass.Remove(self)\nend\n\nvgui.Register(\"ixSettingsRowColorPicker\", PANEL, \"EditablePanel\")\n\n-- number setting\nPANEL = {}\n\nfunction PANEL:Init()\n\tself.setting = self:Add(\"ixNumSlider\")\n\tself.setting.nextUpdate = 0\n\tself.setting:Dock(RIGHT)\n\tself.setting.OnValueChanged = function(panel)\n\t\tself:OnValueChanged(self:GetValue())\n\tend\n\tself.setting.OnValueUpdated = function(panel)\n\t\tlocal fraction = panel:GetFraction()\n\n\t\tif (fraction == 0) then\n\t\t\tEmitChange(75)\n\t\t\treturn\n\t\telseif (fraction == 1) then\n\t\t\tEmitChange(120)\n\t\t\treturn\n\t\tend\n\n\t\tif (SysTime() > panel.nextUpdate) then\n\t\t\tEmitChange(85 + fraction * 15)\n\t\t\tpanel.nextUpdate = SysTime() + 0.05\n\t\tend\n\tend\n\n\tlocal panel = self.setting:GetLabel()\n\tpanel:SetCursor(\"hand\")\n\tpanel:SetMouseInputEnabled(true)\n\tpanel.OnMousePressed = function(_, key)\n\t\tif (key == MOUSE_LEFT) then\n\t\t\tself:OpenEntry()\n\t\tend\n\tend\nend\n\nfunction PANEL:OpenEntry()\n\tif (IsValid(self.entry)) then\n\t\tself.entry:Remove()\n\t\treturn\n\tend\n\n\tself.entry = vgui.Create(\"ixSettingsRowNumberEntry\")\n\tself.entry:Attach(self)\n\tself.entry:SetValue(self:GetValue(), true)\n\tself.entry.OnValueChanged = function(panel)\n\t\tlocal value = math.Round(panel:GetValue(), self:GetDecimals())\n\n\t\tif (value != self:GetValue()) then\n\t\t\tself:SetValue(value, true)\n\t\t\tself:OnValueChanged(value)\n\t\tend\n\tend\nend\n\nfunction PANEL:SetValue(value, bNoNotify)\n\tself.setting:SetValue(value, bNoNotify)\nend\n\nfunction PANEL:GetValue()\n\treturn self.setting:GetValue()\nend\n\nfunction PANEL:SetMin(value)\n\tself.setting:SetMin(value)\nend\n\nfunction PANEL:SetMax(value)\n\tself.setting:SetMax(value)\nend\n\nfunction PANEL:SetDecimals(value)\n\tself.setting:SetDecimals(value)\nend\n\nfunction PANEL:GetDecimals()\n\treturn self.setting:GetDecimals()\nend\n\nfunction PANEL:PerformLayout(width, height)\n\tself.setting:SetWide(width * 0.5)\nend\n\nvgui.Register(\"ixSettingsRowNumber\", PANEL, \"ixSettingsRow\")\n\n-- number setting entry\nDEFINE_BASECLASS(\"Panel\")\nPANEL = {}\n\nAccessorFunc(PANEL, \"bDeleteSelf\", \"DeleteSelf\", FORCE_BOOL)\n\nfunction PANEL:Init()\n\tsurface.SetFont(\"ixMenuButtonFont\")\n\tlocal width, height = surface.GetTextSize(\"999999\")\n\n\tself.m_bIsMenuComponent = true\n\tself.bDeleteSelf = true\n\n\tself.realHeight = 200\n\tself.height = 200\n\tself:SetSize(width, height)\n\tself:DockPadding(4, 4, 4, 4)\n\n\tself.textEntry = self:Add(\"ixTextEntry\")\n\tself.textEntry:SetNumeric(true)\n\tself.textEntry:SetFont(\"ixMenuButtonFont\")\n\tself.textEntry:Dock(FILL)\n\tself.textEntry:RequestFocus()\n\tself.textEntry.OnEnter = function()\n\t\tself:Remove()\n\tend\n\n\tself:MakePopup()\n\tRegisterDermaMenuForClose(self)\nend\n\nfunction PANEL:SetValue(value, bInitial)\n\tvalue = tostring(value)\n\tself.textEntry:SetValue(value)\n\n\tif (bInitial) then\n\t\tself.textEntry:SetCaretPos(value:utf8len())\n\tend\nend\n\nfunction PANEL:GetValue()\n\treturn tonumber(self.textEntry:GetValue()) or 0\nend\n\nfunction PANEL:Attach(panel)\n\tself.attached = panel\nend\n\nfunction PANEL:Think()\n\tlocal panel = self.attached\n\n\tif (IsValid(panel)) then\n\t\tlocal width, height = self:GetSize()\n\t\tlocal x, y = panel:LocalToScreen(0, 0)\n\n\t\tself:SetPos(\n\t\t\tmath.Clamp(x + panel:GetWide() - width, 0, ScrW() - width),\n\t\t\tmath.Clamp(y + panel:GetTall(), 0, ScrH() - height)\n\t\t)\n\tend\nend\n\nfunction PANEL:Paint(width, height)\n\tsurface.SetDrawColor(derma.GetColor(\"DarkerBackground\", self))\n\tsurface.DrawRect(0, 0, width, height)\nend\n\nfunction PANEL:OnValueChanged()\nend\n\nfunction PANEL:OnValueUpdated()\nend\n\nfunction PANEL:Remove()\n\tif (self.bClosing) then\n\t\treturn\n\tend\n\n\tself:OnValueChanged()\n\n\t-- @todo open/close animations\n\tself.bClosing = true\n\tself:SetMouseInputEnabled(false)\n\tself:SetKeyboardInputEnabled(false)\n\tBaseClass.Remove(self)\nend\n\nvgui.Register(\"ixSettingsRowNumberEntry\", PANEL, \"EditablePanel\")\n\n-- string setting\nPANEL = {}\n\nfunction PANEL:Init()\n\tself.setting = self:Add(\"ixTextEntry\")\n\tself.setting:Dock(RIGHT)\n\tself.setting:SetFont(\"ixMenuButtonFont\")\n\tself.setting:SetBackgroundColor(derma.GetColor(\"DarkerBackground\", self))\n\tself.setting.OnEnter = function()\n\t\tself:OnValueChanged(self:GetValue())\n\tend\nend\n\nfunction PANEL:SetValue(value)\n\tself.setting:SetValue(tostring(value))\nend\n\nfunction PANEL:GetValue()\n\treturn self.setting:GetValue()\nend\n\nfunction PANEL:PerformLayout(width, height)\n\tself.setting:SetWide(width * 0.5)\nend\n\nvgui.Register(\"ixSettingsRowString\", PANEL, \"ixSettingsRow\")\n\n-- bool setting\nPANEL = {}\n\nfunction PANEL:Init()\n\tself.setting = self:Add(\"ixCheckBox\")\n\tself.setting:Dock(RIGHT)\n\tself.setting.DoClick = function(panel)\n\t\tself:OnValueChanged(self:GetValue())\n\tend\nend\n\nfunction PANEL:SetValue(bValue)\n\tbValue = tobool(bValue)\n\n\tself.setting:SetChecked(bValue, true)\nend\n\nfunction PANEL:GetValue()\n\treturn self.setting:GetChecked()\nend\n\nvgui.Register(\"ixSettingsRowBool\", PANEL, \"ixSettingsRow\")\n\n-- array setting\nPANEL = {}\n\nfunction PANEL:Init()\n\tself.array = {}\n\n\tself.setting = self:Add(\"DComboBox\")\n\tself.setting:Dock(RIGHT)\n\tself.setting:SetFont(\"ixMenuButtonFont\")\n\tself.setting:SetTextColor(color_white)\n\tself.setting.OnSelect = function(panel)\n\t\tself:OnValueChanged(self:GetValue())\n\n\t\tpanel:SizeToContents()\n\t\tpanel:SetWide(panel:GetWide() + 12) -- padding for arrow (nice)\n\n\t\tif (!self.bInitial) then\n\t\t\tEmitChange()\n\t\tend\n\tend\nend\n\nfunction PANEL:Populate(key, info)\n\tif (!isfunction(info.populate)) then\n\t\tErrorNoHalt(string.format(\"expected populate function for array option '%s'\", key))\n\t\treturn\n\tend\n\n\tlocal entries = info.populate()\n\tlocal i = 1\n\n\tfor k, v in pairs(entries) do\n\t\tself.setting:AddChoice(v, k)\n\t\tself.array[k] = i\n\n\t\ti = i + 1\n\tend\nend\n\nfunction PANEL:SetValue(value)\n\tself.bInitial = true\n\t\tself.setting:ChooseOptionID(self.array[value])\n\tself.bInitial = false\nend\n\nfunction PANEL:GetValue()\n\treturn select(2, self.setting:GetSelected())\nend\n\nvgui.Register(\"ixSettingsRowArray\", PANEL, \"ixSettingsRow\")\n\n-- settings row\nPANEL = {}\n\nAccessorFunc(PANEL, \"backgroundIndex\", \"BackgroundIndex\", FORCE_NUMBER)\nAccessorFunc(PANEL, \"bShowReset\", \"ShowReset\", FORCE_BOOL)\n\nfunction PANEL:Init()\n\tself:DockPadding(4, 4, 4, 4)\n\n\tself.text = self:Add(\"DLabel\")\n\tself.text:Dock(LEFT)\n\tself.text:SetFont(\"ixMenuButtonFont\")\n\tself.text:SetExpensiveShadow(1, color_black)\n\n\tself.backgroundIndex = 0\nend\n\nfunction PANEL:SetShowReset(value, name, default)\n\tvalue = tobool(value)\n\n\tif (value and !IsValid(self.reset)) then\n\t\tself.reset = self:Add(\"DButton\")\n\t\tself.reset:SetFont(\"ixSmallTitleIcons\")\n\t\tself.reset:SetText(\"x\")\n\t\tself.reset:SetTextColor(ColorAlpha(derma.GetColor(\"Warning\", self), 100))\n\t\tself.reset:Dock(LEFT)\n\t\tself.reset:DockMargin(4, 0, 0, 0)\n\t\tself.reset:SizeToContents()\n\t\tself.reset.Paint = nil\n\t\tself.reset.DoClick = function()\n\t\t\tself:OnResetClicked()\n\t\tend\n\t\tself.reset:SetHelixTooltip(function(tooltip)\n\t\t\tlocal title = tooltip:AddRow(\"title\")\n\t\t\ttitle:SetImportant()\n\t\t\ttitle:SetText(L(\"resetDefault\"))\n\t\t\ttitle:SetBackgroundColor(derma.GetColor(\"Warning\", self))\n\t\t\ttitle:SizeToContents()\n\n\t\t\tlocal description = tooltip:AddRow(\"description\")\n\t\t\tdescription:SetText(L(\"resetDefaultDescription\", tostring(name), tostring(default)))\n\t\t\tdescription:SizeToContents()\n\t\tend)\n\telseif (!value and IsValid(self.reset)) then\n\t\tself.reset:Remove()\n\tend\n\n\tself.bShowReset = value\nend\n\nfunction PANEL:Think()\n\tif (IsValid(self.reset)) then\n\t\tself.reset:SetVisible(self:IsHovered() or self:IsOurChild(vgui.GetHoveredPanel()))\n\tend\nend\n\nfunction PANEL:OnResetClicked()\nend\n\nfunction PANEL:GetLabel()\n\treturn self.text\nend\n\nfunction PANEL:SetText(text)\n\tself.text:SetText(text)\n\tself:SizeToContents()\nend\n\nfunction PANEL:GetText()\n\treturn self.text:GetText()\nend\n\n-- implemented by row types\nfunction PANEL:GetValue()\nend\n\nfunction PANEL:SetValue(value)\nend\n\n-- meant for array types to populate combo box values\nfunction PANEL:Populate(key, info)\nend\n\n-- called when value is changed by user\nfunction PANEL:OnValueChanged(newValue)\nend\n\nfunction PANEL:SizeToContents()\n\tlocal _, top, _, bottom = self:GetDockPadding()\n\n\tself.text:SizeToContents()\n\tself:SetTall(self.text:GetTall() + top + bottom)\n\tself.ixRealHeight = self:GetTall()\n\tself.ixHeight = self.ixRealHeight\nend\n\nfunction PANEL:Paint(width, height)\n\tderma.SkinFunc(\"PaintSettingsRowBackground\", self, width, height)\nend\n\nvgui.Register(\"ixSettingsRow\", PANEL, \"EditablePanel\")\n\n-- settings panel\nPANEL = {}\n\nfunction PANEL:Init()\n\tself:Dock(FILL)\n\tself.rows = {}\n\tself.categories = {}\n\n\t-- scroll panel\n\tDEFINE_BASECLASS(\"DScrollPanel\")\n\n\tself.canvas = self:Add(\"DScrollPanel\")\n\tself.canvas:Dock(FILL)\n\tself.canvas.PerformLayout = function(panel)\n\t\tBaseClass.PerformLayout(panel)\n\n\t\tif (!panel.VBar.Enabled) then\n\t\t\tpanel.pnlCanvas:SetWide(panel:GetWide() - panel.VBar:GetWide())\n\t\tend\n\tend\nend\n\nfunction PANEL:GetRowPanelName(type)\n\treturn panelMap[type] or \"ixSettingsRow\"\nend\n\nfunction PANEL:AddCategory(name)\n\tlocal panel = self.categories[name]\n\n\tif (!IsValid(panel)) then\n\t\tpanel = self.canvas:Add(\"ixCategoryPanel\")\n\t\tpanel:SetText(name)\n\t\tpanel:Dock(TOP)\n\t\tpanel:DockMargin(0, 8, 0, 0)\n\n\t\tself.categories[name] = panel\n\t\treturn panel\n\tend\nend\n\nfunction PANEL:AddRow(type, category)\n\tcategory = self.categories[category]\n\tlocal id = panelMap[type]\n\n\tif (!id) then\n\t\tErrorNoHalt(\"attempted to create row with unimplemented type '\" .. tostring(ix.type[type]) .. \"'\\n\")\n\t\tid = \"ixSettingsRow\"\n\tend\n\n\tlocal panel = (IsValid(category) and category or self.canvas):Add(id)\n\tpanel:Dock(TOP)\n\tpanel:SetBackgroundIndex(#self.rows % 2)\n\n\tself.rows[#self.rows + 1] = panel\n\treturn panel\nend\n\nfunction PANEL:GetRows()\n\treturn self.rows\nend\n\nfunction PANEL:Clear()\n\tfor _, v in ipairs(self.rows) do\n\t\tif (IsValid(v)) then\n\t\t\tv:Remove()\n\t\tend\n\tend\n\n\tself.rows = {}\nend\n\nfunction PANEL:SetSearchEnabled(bValue)\n\tif (!bValue) then\n\t\tif (IsValid(self.searchEntry)) then\n\t\t\tself.searchEntry:Remove()\n\t\tend\n\n\t\treturn\n\tend\n\n\t-- search entry\n\tself.searchEntry = self:Add(\"ixIconTextEntry\")\n\tself.searchEntry:Dock(TOP)\n\tself.searchEntry:SetEnterAllowed(false)\n\n\tself.searchEntry.OnChange = function(entry)\n\t\tself:FilterRows(entry:GetValue())\n\tend\nend\n\nfunction PANEL:FilterRows(query)\n\tquery = string.PatternSafe(query:lower())\n\n\tlocal bEmpty = query == \"\"\n\n\tfor categoryName, category in pairs(self.categories) do\n\t\tcategory.size = 0\n\t\tcategory:CreateAnimation(0.5, {\n\t\t\tindex = 21,\n\t\t\ttarget = {size = 1},\n\n\t\t\tThink = function(animation, panel)\n\t\t\t\tpanel:SizeToContents()\n\t\t\tend\n\t\t})\n\n\t\tfor _, row in ipairs(category:GetChildren()) do\n\t\t\tlocal bFound = bEmpty or row:GetText():lower():find(query) or categoryName:lower():find(query)\n\n\t\t\trow:SetVisible(true)\n\t\t\trow:CreateAnimation(0.5, {\n\t\t\t\tindex = 21,\n\t\t\t\ttarget = {ixHeight = bFound and row.ixRealHeight or 0},\n\t\t\t\teasing = \"outQuint\",\n\n\t\t\t\tThink = function(animation, panel)\n\t\t\t\t\tpanel:SetTall(bFound and math.min(panel.ixHeight + 2, panel.ixRealHeight) or math.max(panel.ixHeight - 2, 0))\n\t\t\t\tend,\n\n\t\t\t\tOnComplete = function(animation, panel)\n\t\t\t\t\tpanel:SetVisible(bFound)\n\n\t\t\t\t\t-- need this so categories are sized properly when animations are disabled - there is no guaranteed order\n\t\t\t\t\t-- that animations will think so we SizeToContents here. putting it here will result in redundant calls but\n\t\t\t\t\t-- I guess we have the performance to spare\n\t\t\t\t\tif (ix.option.Get(\"disableAnimations\", false)) then\n\t\t\t\t\t\tcategory:SizeToContents()\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\t})\n\t\tend\n\tend\nend\n\nfunction PANEL:Paint(width, height)\nend\n\nfunction PANEL:SizeToContents()\n\tfor _, v in pairs(self.categories) do\n\t\tv:SizeToContents()\n\tend\nend\n\nvgui.Register(\"ixSettings\", PANEL, \"Panel\")\n\nhook.Add(\"CreateMenuButtons\", \"ixSettings\", function(tabs)\n\ttabs[\"settings\"] = {\n\t\tPopulateTabButton = function(info, button)\n\t\t\tlocal menu = ix.gui.menu\n\n\t\t\tif (!IsValid(menu)) then\n\t\t\t\treturn\n\t\t\tend\n\n\t\t\tDEFINE_BASECLASS(\"ixMenuButton\")\n\t\t\tbutton:SetZPos(9999)\n\t\t\tbutton.Paint = function(panel, width, height)\n\t\t\t\tBaseClass.Paint(panel, width, height)\n\n\t\t\t\tsurface.SetDrawColor(255, 255, 255, 33)\n\t\t\t\tsurface.DrawRect(0, 0, width, 1)\n\t\t\tend\n\t\tend,\n\n\t\tCreate = function(info, container)\n\t\t\tlocal panel = container:Add(\"ixSettings\")\n\t\t\tpanel:SetSearchEnabled(true)\n\n\t\t\tfor category, options in SortedPairs(ix.option.GetAllByCategories(true)) do\n\t\t\t\tcategory = L(category)\n\t\t\t\tpanel:AddCategory(category)\n\n\t\t\t\t-- sort options by language phrase rather than the key\n\t\t\t\ttable.sort(options, function(a, b)\n\t\t\t\t\treturn L(a.phrase) < L(b.phrase)\n\t\t\t\tend)\n\n\t\t\t\tfor _, data in pairs(options) do\n\t\t\t\t\tlocal key = data.key\n\t\t\t\t\tlocal row = panel:AddRow(data.type, category)\n\t\t\t\t\tlocal value = ix.util.SanitizeType(data.type, ix.option.Get(key))\n\n\t\t\t\t\trow:SetText(L(data.phrase))\n\t\t\t\t\trow:Populate(key, data)\n\n\t\t\t\t\t-- type-specific properties\n\t\t\t\t\tif (data.type == ix.type.number) then\n\t\t\t\t\t\trow:SetMin(data.min or 0)\n\t\t\t\t\t\trow:SetMax(data.max or 10)\n\t\t\t\t\t\trow:SetDecimals(data.decimals or 0)\n\t\t\t\t\tend\n\n\t\t\t\t\trow:SetValue(value, true)\n\t\t\t\t\trow:SetShowReset(value != data.default, key, data.default)\n\t\t\t\t\trow.OnValueChanged = function()\n\t\t\t\t\t\tlocal newValue = row:GetValue()\n\n\t\t\t\t\t\trow:SetShowReset(newValue != data.default, key, data.default)\n\t\t\t\t\t\tix.option.Set(key, newValue)\n\t\t\t\t\tend\n\n\t\t\t\t\trow.OnResetClicked = function()\n\t\t\t\t\t\trow:SetShowReset(false)\n\t\t\t\t\t\trow:SetValue(data.default, true)\n\n\t\t\t\t\t\tix.option.Set(key, data.default)\n\t\t\t\t\tend\n\n\t\t\t\t\trow:GetLabel():SetHelixTooltip(function(tooltip)\n\t\t\t\t\t\tlocal title = tooltip:AddRow(\"name\")\n\t\t\t\t\t\ttitle:SetImportant()\n\t\t\t\t\t\ttitle:SetText(key)\n\t\t\t\t\t\ttitle:SizeToContents()\n\t\t\t\t\t\ttitle:SetMaxWidth(math.max(title:GetMaxWidth(), ScrW() * 0.5))\n\n\t\t\t\t\t\tlocal description = tooltip:AddRow(\"description\")\n\t\t\t\t\t\tdescription:SetText(L(data.description))\n\t\t\t\t\t\tdescription:SizeToContents()\n\t\t\t\t\tend)\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tpanel:SizeToContents()\n\t\t\tcontainer.panel = panel\n\t\tend,\n\n\t\tOnSelected = function(info, container)\n\t\t\tcontainer.panel.searchEntry:RequestFocus()\n\t\tend\n\t}\nend)\n"
  },
  {
    "path": "gamemode/core/derma/cl_shipment.lua",
    "content": "\nlocal PANEL = {}\n\nfunction PANEL:Init()\n\tself:SetSize(460, 360)\n\tself:SetTitle(L\"shipment\")\n\tself:Center()\n\tself:MakePopup()\n\n\tself.scroll = self:Add(\"DScrollPanel\")\n\tself.scroll:Dock(FILL)\n\n\tself.list = self.scroll:Add(\"DListLayout\")\n\tself.list:Dock(FILL)\nend\n\nfunction PANEL:SetItems(entity, items)\n\tself.entity = entity\n\tself.items = true\n\tself.itemPanels = {}\n\n\tfor k, v in SortedPairs(items) do\n\t\tlocal itemTable = ix.item.list[k]\n\n\t\tif (itemTable) then\n\t\t\tlocal item = self.list:Add(\"DPanel\")\n\t\t\titem:SetTall(36)\n\t\t\titem:Dock(TOP)\n\t\t\titem:DockMargin(4, 4, 4, 0)\n\n\t\t\titem.icon = item:Add(\"SpawnIcon\")\n\t\t\titem.icon:SetPos(2, 2)\n\t\t\titem.icon:SetSize(32, 32)\n\t\t\titem.icon:SetModel(itemTable:GetModel())\n\t\t\titem.icon:SetHelixTooltip(function(tooltip)\n\t\t\t\tix.hud.PopulateItemTooltip(tooltip, itemTable)\n\t\t\tend)\n\n\t\t\titem.quantity = item.icon:Add(\"DLabel\")\n\t\t\titem.quantity:SetSize(32, 32)\n\t\t\titem.quantity:SetContentAlignment(3)\n\t\t\titem.quantity:SetTextInset(0, 0)\n\t\t\titem.quantity:SetText(v)\n\t\t\titem.quantity:SetFont(\"DermaDefaultBold\")\n\t\t\titem.quantity:SetExpensiveShadow(1, Color(0, 0, 0, 150))\n\n\t\t\titem.name = item:Add(\"DLabel\")\n\t\t\titem.name:SetPos(38, 0)\n\t\t\titem.name:SetSize(200, 36)\n\t\t\titem.name:SetFont(\"ixSmallFont\")\n\t\t\titem.name:SetText(L(itemTable.name))\n\t\t\titem.name:SetContentAlignment(4)\n\t\t\titem.name:SetTextColor(color_white)\n\n\t\t\titem.take = item:Add(\"DButton\")\n\t\t\titem.take:Dock(RIGHT)\n\t\t\titem.take:SetText(L\"take\")\n\t\t\titem.take:SetWide(48)\n\t\t\titem.take:DockMargin(3, 3, 3, 3)\n\t\t\titem.take:SetTextColor(color_white)\n\t\t\titem.take.DoClick = function(this)\n\t\t\t\tnet.Start(\"ixShipmentUse\")\n\t\t\t\t\tnet.WriteString(k)\n\t\t\t\t\tnet.WriteBool(false)\n\t\t\t\tnet.SendToServer()\n\n\t\t\t\titems[k] = items[k] - 1\n\n\t\t\t\titem.quantity:SetText(items[k])\n\n\t\t\t\tif (items[k] <= 0) then\n\t\t\t\t\titem:Remove()\n\t\t\t\t\titems[k] = nil\n\t\t\t\tend\n\n\t\t\t\tif (table.IsEmpty(items)) then\n\t\t\t\t\tself:Remove()\n\t\t\t\tend\n\t\t\tend\n\n\t\t\titem.drop = item:Add(\"DButton\")\n\t\t\titem.drop:Dock(RIGHT)\n\t\t\titem.drop:SetText(L\"drop\")\n\t\t\titem.drop:SetWide(48)\n\t\t\titem.drop:DockMargin(3, 3, 0, 3)\n\t\t\titem.drop:SetTextColor(color_white)\n\t\t\titem.drop.DoClick = function(this)\n\t\t\t\tnet.Start(\"ixShipmentUse\")\n\t\t\t\t\tnet.WriteString(k)\n\t\t\t\t\tnet.WriteBool(true)\n\t\t\t\tnet.SendToServer()\n\n\t\t\t\titems[k] = items[k] - 1\n\n\t\t\t\titem.quantity:SetText(items[k])\n\n\t\t\t\tif (items[k] <= 0) then\n\t\t\t\t\titem:Remove()\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tself.itemPanels[k] = item\n\t\tend\n\tend\nend\n\nfunction PANEL:Close()\n\tnet.Start(\"ixShipmentClose\")\n\tnet.SendToServer()\n\n\tself:Remove()\nend\n\nfunction PANEL:Think()\n\tif (self.items and !IsValid(self.entity)) then\n\t\tself:Remove()\n\tend\nend\n\nvgui.Register(\"ixShipment\", PANEL, \"DFrame\")\n"
  },
  {
    "path": "gamemode/core/derma/cl_spawnicon.lua",
    "content": "\nDEFINE_BASECLASS(\"DModelPanel\")\n\nlocal PANEL = {}\n\nfunction PANEL:Init()\n\tself.defaultEyeTarget = Vector(0, 0, 64)\n\tself:SetHidden(false)\n\n\tfor i = 0, 5 do\n\t\tif (i == 1 or i == 5) then\n\t\t\tself:SetDirectionalLight(i, Color(155, 155, 155))\n\t\telse\n\t\t\tself:SetDirectionalLight(i, Color(255, 255, 255))\n\t\tend\n\tend\nend\n\nfunction PANEL:SetModel(model, skin, hidden)\n\tBaseClass.SetModel(self, model)\n\n\tlocal entity = self.Entity\n\n\tif (skin) then\n\t\tentity:SetSkin(skin)\n\tend\n\n\tlocal sequence = entity:SelectWeightedSequence(ACT_IDLE)\n\n\tif (sequence <= 0) then\n\t\tsequence = entity:LookupSequence(\"idle_unarmed\")\n\tend\n\n\tif (sequence > 0) then\n\t\tentity:ResetSequence(sequence)\n\telse\n\t\tlocal found = false\n\n\t\tfor _, v in ipairs(entity:GetSequenceList()) do\n\t\t\tif ((v:lower():find(\"idle\") or v:lower():find(\"fly\")) and v != \"idlenoise\") then\n\t\t\t\tentity:ResetSequence(v)\n\t\t\t\tfound = true\n\n\t\t\t\tbreak\n\t\t\tend\n\t\tend\n\n\t\tif (!found) then\n\t\t\tentity:ResetSequence(4)\n\t\tend\n\tend\n\n\tlocal data = PositionSpawnIcon(entity, entity:GetPos())\n\n\tif (data) then\n\t\tself:SetFOV(data.fov)\n\t\tself:SetCamPos(data.origin)\n\t\tself:SetLookAng(data.angles)\n\tend\n\n\tentity:SetIK(false)\n\tentity:SetEyeTarget(self.defaultEyeTarget)\nend\n\nfunction PANEL:SetHidden(hidden)\n\tif (hidden) then\n\t\tself:SetAmbientLight(color_black)\n\t\tself:SetColor(Color(0, 0, 0))\n\n\t\tfor i = 0, 5 do\n\t\t\tself:SetDirectionalLight(i, color_black)\n\t\tend\n\telse\n\t\tself:SetAmbientLight(Color(20, 20, 20))\n\t\tself:SetAlpha(255)\n\n\t\tfor i = 0, 5 do\n\t\t\tif (i == 1 or i == 5) then\n\t\t\t\tself:SetDirectionalLight(i, Color(155, 155, 155))\n\t\t\telse\n\t\t\t\tself:SetDirectionalLight(i, Color(255, 255, 255))\n\t\t\tend\n\t\tend\n\tend\nend\n\nfunction PANEL:LayoutEntity()\n\tself:RunAnimation()\nend\n\nfunction PANEL:OnMousePressed()\n\tif (self.DoClick) then\n\t\tself:DoClick()\n\tend\nend\n\nvgui.Register(\"ixSpawnIcon\", PANEL, \"DModelPanel\")\n"
  },
  {
    "path": "gamemode/core/derma/cl_storage.lua",
    "content": "\nlocal PANEL = {}\n\nAccessorFunc(PANEL, \"money\", \"Money\", FORCE_NUMBER)\n\nfunction PANEL:Init()\n\tself:DockPadding(1, 1, 1, 1)\n\tself:SetTall(64)\n\tself:Dock(BOTTOM)\n\n\tself.moneyLabel = self:Add(\"DLabel\")\n\tself.moneyLabel:Dock(TOP)\n\tself.moneyLabel:SetFont(\"ixGenericFont\")\n\tself.moneyLabel:SetText(\"\")\n\tself.moneyLabel:SetTextInset(2, 0)\n\tself.moneyLabel:SizeToContents()\n\tself.moneyLabel.Paint = function(panel, width, height)\n\t\tderma.SkinFunc(\"DrawImportantBackground\", 0, 0, width, height, ix.config.Get(\"color\"))\n\tend\n\n\tself.amountEntry = self:Add(\"ixTextEntry\")\n\tself.amountEntry:Dock(FILL)\n\tself.amountEntry:SetFont(\"ixGenericFont\")\n\tself.amountEntry:SetNumeric(true)\n\tself.amountEntry:SetValue(\"0\")\n\n\tself.transferButton = self:Add(\"DButton\")\n\tself.transferButton:SetFont(\"ixIconsMedium\")\n\tself:SetLeft(false)\n\tself.transferButton.DoClick = function()\n\t\tlocal amount = math.max(0, math.Round(tonumber(self.amountEntry:GetValue()) or 0))\n\t\tself.amountEntry:SetValue(\"0\")\n\n\t\tif (amount != 0) then\n\t\t\tself:OnTransfer(amount)\n\t\tend\n\tend\n\n\tself.bNoBackgroundBlur = true\nend\n\nfunction PANEL:SetLeft(bValue)\n\tif (bValue) then\n\t\tself.transferButton:Dock(LEFT)\n\t\tself.transferButton:SetText(\"s\")\n\telse\n\t\tself.transferButton:Dock(RIGHT)\n\t\tself.transferButton:SetText(\"t\")\n\tend\nend\n\nfunction PANEL:SetMoney(money)\n\tlocal name = string.gsub(ix.util.ExpandCamelCase(ix.currency.plural), \"%s\", \"\")\n\n\tself.money = math.max(math.Round(tonumber(money) or 0), 0)\n\tself.moneyLabel:SetText(string.format(\"%s: %d\", name, money))\nend\n\nfunction PANEL:OnTransfer(amount)\nend\n\nfunction PANEL:Paint(width, height)\n\tderma.SkinFunc(\"PaintBaseFrame\", self, width, height)\nend\n\nvgui.Register(\"ixStorageMoney\", PANEL, \"EditablePanel\")\n\nDEFINE_BASECLASS(\"Panel\")\nPANEL = {}\n\nAccessorFunc(PANEL, \"fadeTime\", \"FadeTime\", FORCE_NUMBER)\nAccessorFunc(PANEL, \"frameMargin\", \"FrameMargin\", FORCE_NUMBER)\nAccessorFunc(PANEL, \"storageID\", \"StorageID\", FORCE_NUMBER)\n\nfunction PANEL:Init()\n\tif (IsValid(ix.gui.openedStorage)) then\n\t\tix.gui.openedStorage:Remove()\n\tend\n\n\tix.gui.openedStorage = self\n\n\tself:SetSize(ScrW(), ScrH())\n\tself:SetPos(0, 0)\n\tself:SetFadeTime(0.25)\n\tself:SetFrameMargin(4)\n\n\tself.storageInventory = self:Add(\"ixInventory\")\n\tself.storageInventory.bNoBackgroundBlur = true\n\tself.storageInventory:ShowCloseButton(true)\n\tself.storageInventory:SetTitle(\"Storage\")\n\tself.storageInventory.Close = function(this)\n\t\tnet.Start(\"ixStorageClose\")\n\t\tnet.SendToServer()\n\t\tself:Remove()\n\tend\n\n\tself.storageMoney = self.storageInventory:Add(\"ixStorageMoney\")\n\tself.storageMoney:SetVisible(false)\n\tself.storageMoney.OnTransfer = function(_, amount)\n\t\tnet.Start(\"ixStorageMoneyTake\")\n\t\t\tnet.WriteUInt(self.storageID, 32)\n\t\t\tnet.WriteUInt(amount, 32)\n\t\tnet.SendToServer()\n\tend\n\n\tix.gui.inv1 = self:Add(\"ixInventory\")\n\tix.gui.inv1.bNoBackgroundBlur = true\n\tix.gui.inv1:ShowCloseButton(true)\n\tix.gui.inv1.Close = function(this)\n\t\tnet.Start(\"ixStorageClose\")\n\t\tnet.SendToServer()\n\t\tself:Remove()\n\tend\n\n\tself.localMoney = ix.gui.inv1:Add(\"ixStorageMoney\")\n\tself.localMoney:SetVisible(false)\n\tself.localMoney:SetLeft(true)\n\tself.localMoney.OnTransfer = function(_, amount)\n\t\tnet.Start(\"ixStorageMoneyGive\")\n\t\t\tnet.WriteUInt(self.storageID, 32)\n\t\t\tnet.WriteUInt(amount, 32)\n\t\tnet.SendToServer()\n\tend\n\n\tself:SetAlpha(0)\n\tself:AlphaTo(255, self:GetFadeTime())\n\n\tself.storageInventory:MakePopup()\n\tix.gui.inv1:MakePopup()\nend\n\nfunction PANEL:OnChildAdded(panel)\n\tpanel:SetPaintedManually(true)\nend\n\nfunction PANEL:SetLocalInventory(inventory)\n\tif (IsValid(ix.gui.inv1) and !IsValid(ix.gui.menu)) then\n\t\tix.gui.inv1:SetInventory(inventory)\n\t\tix.gui.inv1:SetPos(self:GetWide() / 2 + self:GetFrameMargin() / 2, self:GetTall() / 2 - ix.gui.inv1:GetTall() / 2)\n\tend\nend\n\nfunction PANEL:SetLocalMoney(money)\n\tif (!self.localMoney:IsVisible()) then\n\t\tself.localMoney:SetVisible(true)\n\t\tix.gui.inv1:SetTall(ix.gui.inv1:GetTall() + self.localMoney:GetTall() + 2)\n\tend\n\n\tself.localMoney:SetMoney(money)\nend\n\nfunction PANEL:SetStorageTitle(title)\n\tself.storageInventory:SetTitle(title)\nend\n\nfunction PANEL:SetStorageInventory(inventory)\n\tself.storageInventory:SetInventory(inventory)\n\tself.storageInventory:SetPos(\n\t\tself:GetWide() / 2 - self.storageInventory:GetWide() - 2,\n\t\tself:GetTall() / 2 - self.storageInventory:GetTall() / 2\n\t)\n\n\tix.gui[\"inv\" .. inventory:GetID()] = self.storageInventory\nend\n\nfunction PANEL:SetStorageMoney(money)\n\tif (!self.storageMoney:IsVisible()) then\n\t\tself.storageMoney:SetVisible(true)\n\t\tself.storageInventory:SetTall(self.storageInventory:GetTall() + self.storageMoney:GetTall() + 2)\n\tend\n\n\tself.storageMoney:SetMoney(money)\nend\n\nfunction PANEL:Paint(width, height)\n\tix.util.DrawBlurAt(0, 0, width, height)\n\n\tfor _, v in ipairs(self:GetChildren()) do\n\t\tv:PaintManual()\n\tend\nend\n\nfunction PANEL:Remove()\n\tself:SetAlpha(255)\n\tself:AlphaTo(0, self:GetFadeTime(), 0, function()\n\t\tBaseClass.Remove(self)\n\tend)\nend\n\nfunction PANEL:OnRemove()\n\tif (!IsValid(ix.gui.menu)) then\n\t\tself.storageInventory:Remove()\n\t\tix.gui.inv1:Remove()\n\tend\nend\n\nvgui.Register(\"ixStorageView\", PANEL, \"Panel\")\n"
  },
  {
    "path": "gamemode/core/derma/cl_subpanel.lua",
    "content": "\nlocal DEFAULT_PADDING = ScreenScale(32)\nlocal DEFAULT_ANIMATION_TIME = 1\nlocal DEFAULT_SUBPANEL_ANIMATION_TIME = 0.5\n\n-- parent subpanel\nlocal PANEL = {}\n\nfunction PANEL:Init()\n\tlocal parent = self:GetParent()\n\tlocal padding = parent.GetPadding and parent:GetPadding() or DEFAULT_PADDING\n\n\tself:SetSize(parent:GetWide() - (padding * 2), parent:GetTall() - (padding * 2))\n\tself:Center()\nend\n\nfunction PANEL:SetTitle(text, bNoTranslation, bNoUpper)\n\tif (text == nil) then\n\t\tif (IsValid(self.title)) then\n\t\t\tself.title:Remove()\n\t\tend\n\n\t\treturn\n\telseif (!IsValid(self.title)) then\n\t\tself.title = self:Add(\"DLabel\")\n\t\tself.title:SetFont(\"ixTitleFont\")\n\t\tself.title:SizeToContents()\n\t\tself.title:SetTextColor(ix.config.Get(\"color\") or color_white)\n\t\tself.title:Dock(TOP)\n\tend\n\n\tlocal newText = bNoTranslation and text or L(text)\n\tnewText = bNoUpper and newText or newText:utf8upper()\n\n\tself.title:SetText(newText)\n\tself.title:SizeToContents()\nend\n\nfunction PANEL:SetLeftPanel(panel)\n\tself.left = panel\nend\n\nfunction PANEL:GetLeftPanel()\n\treturn self.left\nend\n\nfunction PANEL:SetRightPanel(panel)\n\tself.right = panel\nend\n\nfunction PANEL:GetRightPanel()\n\treturn self.right\nend\n\nfunction PANEL:OnSetActive()\nend\n\nvgui.Register(\"ixSubpanel\", PANEL, \"EditablePanel\")\n\n-- subpanel parent\nDEFINE_BASECLASS(\"EditablePanel\")\nPANEL = {}\n\nAccessorFunc(PANEL, \"padding\", \"Padding\", FORCE_NUMBER)\nAccessorFunc(PANEL, \"animationTime\", \"AnimationTime\", FORCE_NUMBER)\nAccessorFunc(PANEL, \"subpanelAnimationTime\", \"SubpanelAnimationTime\", FORCE_NUMBER)\nAccessorFunc(PANEL, \"leftOffset\", \"LeftOffset\", FORCE_NUMBER)\n\nfunction PANEL:Init()\n\tself.subpanels = {}\n\tself.childPanels = {}\n\n\tself.currentSubpanelX = DEFAULT_PADDING\n\tself.targetSubpanelX = DEFAULT_PADDING\n\tself.padding = DEFAULT_PADDING\n\tself.leftOffset = 0\n\n\tself.animationTime = DEFAULT_ANIMATION_TIME\n\tself.subpanelAnimationTime = DEFAULT_SUBPANEL_ANIMATION_TIME\nend\n\nfunction PANEL:SetPadding(amount, bSetDockPadding)\n\tself.currentSubpanelX = amount\n\tself.targetSubpanelX = amount\n\tself.padding = amount\n\n\tif (bSetDockPadding) then\n\t\tself:DockPadding(amount, amount, amount, amount)\n\tend\nend\n\nfunction PANEL:Add(name)\n\tlocal panel = BaseClass.Add(self, name)\n\n\tif (panel.SetPaintedManually) then\n\t\tpanel:SetPaintedManually(true)\n\t\tself.childPanels[#self.childPanels + 1] = panel\n\tend\n\n\treturn panel\nend\n\nfunction PANEL:AddSubpanel(name)\n\tlocal id = #self.subpanels + 1\n\tlocal panel = BaseClass.Add(self, \"ixSubpanel\")\n\tpanel.subpanelName = name\n\tpanel.subpanelID = id\n\tpanel:SetTitle(name)\n\n\tself.subpanels[id] = panel\n\tself:SetupSubpanelReferences()\n\n\treturn panel\nend\n\nfunction PANEL:SetupSubpanelReferences()\n\tlocal lastPanel\n\n\tfor i = 1, #self.subpanels do\n\t\tlocal panel = self.subpanels[i]\n\t\tlocal nextPanel = self.subpanels[i + 1]\n\n\t\tif (IsValid(lastPanel)) then\n\t\t\tlastPanel:SetRightPanel(panel)\n\t\t\tpanel:SetLeftPanel(lastPanel)\n\t\tend\n\n\t\tif (IsValid(nextPanel)) then\n\t\t\tpanel:SetRightPanel(nextPanel)\n\t\tend\n\n\t\tlastPanel = panel\n\tend\nend\n\nfunction PANEL:SetSubpanelPos(id, x)\n\tlocal currentPanel = self.subpanels[id]\n\n\tif (!currentPanel) then\n\t\treturn\n\tend\n\n\tlocal _, oldY = currentPanel:GetPos()\n\tcurrentPanel:SetPos(x, oldY)\n\n\t-- traverse left\n\twhile (IsValid(currentPanel)) do\n\t\tlocal left = currentPanel:GetLeftPanel()\n\n\t\tif (IsValid(left)) then\n\t\t\tleft:MoveLeftOf(currentPanel, self.padding + self.leftOffset)\n\t\tend\n\n\t\tcurrentPanel = left\n\tend\n\n\tcurrentPanel = self.subpanels[id]\n\n\t-- traverse right\n\twhile (IsValid(currentPanel)) do\n\t\tlocal right = currentPanel:GetRightPanel()\n\n\t\tif (IsValid(right)) then\n\t\t\tright:MoveRightOf(currentPanel, self.padding)\n\t\tend\n\n\t\tcurrentPanel = right\n\tend\nend\n\nfunction PANEL:SetActiveSubpanel(id, length)\n\tif (isstring(id)) then\n\t\tfor i = 1, #self.subpanels do\n\t\t\tif (self.subpanels[i].subpanelName == id) then\n\t\t\t\tid = i\n\t\t\t\tbreak\n\t\t\tend\n\t\tend\n\tend\n\n\tlocal activePanel = self.subpanels[id]\n\n\tif (!activePanel) then\n\t\treturn false\n\tend\n\n\tif (length == 0 or !self.activeSubpanel) then\n\t\tself:SetSubpanelPos(id, self.padding + self.leftOffset)\n\telse\n\t\tlocal x, _ = activePanel:GetPos()\n\t\tlocal target = self.targetSubpanelX + self.leftOffset\n\t\tself.currentSubpanelX = x + self.padding + self.leftOffset\n\n\t\tself:CreateAnimation(length or self.subpanelAnimationTime, {\n\t\t\tindex = 420,\n\t\t\ttarget = {currentSubpanelX = target},\n\t\t\teasing = \"outQuint\",\n\n\t\t\tThink = function(animation, panel)\n\t\t\t\tpanel:SetSubpanelPos(id, panel.currentSubpanelX)\n\t\t\tend,\n\n\t\t\tOnComplete = function(animation, panel)\n\t\t\t\tpanel:SetSubpanelPos(id, target)\n\t\t\tend\n\t\t})\n\tend\n\n\tself.activeSubpanel = id\n\tactivePanel:OnSetActive()\n\n\treturn true\nend\n\nfunction PANEL:GetSubpanel(id)\n\treturn self.subpanels[id]\nend\n\nfunction PANEL:GetActiveSubpanel()\n\treturn self.subpanels[self.activeSubpanel]\nend\n\nfunction PANEL:GetActiveSubpanelID()\n\treturn self.activeSubpanel\nend\n\nfunction PANEL:Slide(direction, length, callback, bIgnoreConfig)\n\tlocal _, height = self:GetParent():GetSize()\n\tlocal x, _ = self:GetPos()\n\tlocal targetY = direction == \"up\" and 0 or height\n\n\tself:SetVisible(true)\n\n\tif (length == 0) then\n\t\tself:SetPos(x, targetY)\n\telse\n\t\tlength = length or self.animationTime\n\t\tself.currentY = direction == \"up\" and height or 0\n\n\t\tself:CreateAnimation(length or self.animationTime, {\n\t\t\tindex = -1,\n\t\t\ttarget = {currentY = targetY},\n\t\t\teasing = \"outExpo\",\n\t\t\tbIgnoreConfig = bIgnoreConfig,\n\n\t\t\tThink = function(animation, panel)\n\t\t\t\tlocal currentX, _ = panel:GetPos()\n\n\t\t\t\tpanel:SetPos(currentX, panel.currentY)\n\t\t\tend,\n\n\t\t\tOnComplete = function(animation, panel)\n\t\t\t\tif (direction == \"down\") then\n\t\t\t\t\tpanel:SetVisible(false)\n\t\t\t\tend\n\n\t\t\t\tif (callback) then\n\t\t\t\t\tcallback(panel)\n\t\t\t\tend\n\t\t\tend\n\t\t})\n\tend\nend\n\nfunction PANEL:SlideUp(...)\n\tself:SetMouseInputEnabled(true)\n\tself:SetKeyboardInputEnabled(true)\n\n\tself:OnSlideUp()\n\tself:Slide(\"up\", ...)\nend\n\nfunction PANEL:SlideDown(...)\n\tself:SetMouseInputEnabled(false)\n\tself:SetKeyboardInputEnabled(false)\n\n\tself:OnSlideDown()\n\tself:Slide(\"down\", ...)\nend\n\nfunction PANEL:OnSlideUp()\nend\n\nfunction PANEL:OnSlideDown()\nend\n\nfunction PANEL:Paint(width, height)\n\tfor i = 1, #self.childPanels do\n\t\tif not (IsValid(self.childPanels[i])) then\n\t\t\tcontinue\n\t\tend\n\n\t\tself.childPanels[i]:PaintManual()\n\tend\nend\n\nfunction PANEL:PaintSubpanels(width, height)\n\tfor i = 1, #self.subpanels do\n\t\tif not (IsValid(self.subpanels[i])) then\n\t\t\tcontinue\n\t\tend\n\n\t\tself.subpanels[i]:PaintManual()\n\tend\nend\n\n-- ????\nPANEL.Remove = BaseClass.Remove\n\nvgui.Register(\"ixSubpanelParent\", PANEL, \"EditablePanel\")\n"
  },
  {
    "path": "gamemode/core/derma/cl_tooltip.lua",
    "content": "\n--- Text container for `ixTooltip`.\n-- Rows are the main way of interacting with `ixTooltip`s. These derive from\n-- [DLabel](https://wiki.garrysmod.com/page/Category:DLabel) panels, which means that making use of this panel\n-- will be largely the same as any DLabel panel.\n-- @panel ixTooltipRow\n\nlocal animationTime = 1\n\n-- panel meta\ndo\n\tlocal PANEL = FindMetaTable(\"Panel\")\n\tlocal ixChangeTooltip = ChangeTooltip\n\tlocal ixRemoveTooltip = RemoveTooltip\n\tlocal tooltip\n\tlocal lastHover\n\n\tfunction PANEL:SetHelixTooltip(callback)\n\t\tself:SetMouseInputEnabled(true)\n\t\tself.ixTooltip = callback\n\tend\n\n\tfunction ChangeTooltip(panel, ...) -- luacheck: globals ChangeTooltip\n\t\tif (!panel.ixTooltip) then\n\t\t\treturn ixChangeTooltip(panel, ...)\n\t\tend\n\n\t\tRemoveTooltip()\n\n\t\ttimer.Create(\"ixTooltip\", 0.1, 1, function()\n\t\t\tif (!IsValid(panel) or lastHover != panel) then\n\t\t\t\treturn\n\t\t\tend\n\n\t\t\ttooltip = vgui.Create(\"ixTooltip\")\n\t\t\tpanel.ixTooltip(tooltip)\n\t\t\ttooltip:SizeToContents()\n\t\tend)\n\n\t\tlastHover = panel\n\tend\n\n\tfunction RemoveTooltip() -- luacheck: globals RemoveTooltip\n\t\tif (IsValid(tooltip)) then\n\t\t\ttooltip:Remove()\n\t\t\ttooltip = nil\n\t\tend\n\n\t\ttimer.Remove(\"ixTooltip\")\n\t\tlastHover = nil\n\n\t\treturn ixRemoveTooltip()\n\tend\nend\n\nDEFINE_BASECLASS(\"DLabel\")\nlocal PANEL = {}\n\nAccessorFunc(PANEL, \"backgroundColor\", \"BackgroundColor\")\nAccessorFunc(PANEL, \"maxWidth\", \"MaxWidth\", FORCE_NUMBER)\nAccessorFunc(PANEL, \"bNoMinimal\", \"MinimalHidden\", FORCE_BOOL)\n\nfunction PANEL:Init()\n\tself:SetFont(\"ixSmallFont\")\n\tself:SetText(L(\"unknown\"))\n\tself:SetTextColor(color_white)\n\tself:SetTextInset(4, 0)\n\tself:SetContentAlignment(4)\n\tself:Dock(TOP)\n\n\tself.maxWidth = ScrW() * 0.2\n\tself.bNoMinimal = false\n\tself.bMinimal = false\nend\n\n--- Whether or not this tooltip row should be displayed in a minimal format. This usually means no background and/or\n-- smaller font. You probably won't need this if you're using regular `ixTooltipRow` panels, but you should take into\n-- account if you're creating your own panels that derive from `ixTooltipRow`.\n-- @realm client\n-- @treturn bool True if this tooltip row should be displayed in a minimal format\nfunction PANEL:IsMinimal()\n\treturn self.bMinimal\nend\n\n--- Sets this row to be more prominent with a larger font and more noticable background color. This should usually\n-- be used once per tooltip as a title row. For example, item tooltips have one \"important\" row consisting of the\n-- item's name. Note that this function is a fire-and-forget function; you cannot revert a row back to it's regular state\n-- unless you set the font/colors manually.\n-- @realm client\nfunction PANEL:SetImportant()\n\tself:SetFont(\"ixSmallTitleFont\")\n\tself:SetExpensiveShadow(1, color_black)\n\tself:SetBackgroundColor(ix.config.Get(\"color\"))\nend\n\n--- Sets the background color of this row. This should be used sparingly to avoid overwhelming players with a\n-- bunch of different colors that could convey different meanings.\n-- @realm client\n-- @color color New color of the background. The alpha is clamped to 100-255 to ensure visibility\nfunction PANEL:SetBackgroundColor(color)\n\tcolor = table.Copy(color)\n\tcolor.a = math.min(color.a or 255, 100)\n\n\tself.backgroundColor = color\nend\n\n--- Resizes this panel to fit its contents. This should be called after setting the text.\n-- @realm client\nfunction PANEL:SizeToContents()\n\tlocal contentWidth, contentHeight = self:GetContentSize()\n\tcontentWidth = contentWidth + 4\n\tcontentHeight = contentHeight + 4\n\n\tif (contentWidth > self.maxWidth) then\n\t\tself:SetWide(self.maxWidth - 4) -- to account for text inset\n\t\tself:SetTextInset(4, 0)\n\t\tself:SetWrap(true)\n\n\t\tself:SizeToContentsY()\n\telse\n\t\tself:SetSize(contentWidth, contentHeight)\n\tend\nend\n\n--- Resizes the height of this panel to fit its contents.\n-- @internal\n-- @realm client\nfunction PANEL:SizeToContentsY()\n\tBaseClass.SizeToContentsY(self)\n\tself:SetTall(self:GetTall() + 4)\nend\n\n--- Called when the background of this row should be painted. This will paint the background with the\n-- `DrawImportantBackground` function set in the skin by default.\n-- @realm client\n-- @number width Width of the panel\n-- @number height Height of the panel\nfunction PANEL:PaintBackground(width, height)\n\tif (self.backgroundColor) then\n\t\tderma.SkinFunc(\"DrawImportantBackground\", 0, 0, width, height, self.backgroundColor)\n\tend\nend\n\n--- Called when the foreground of this row should be painted. If you are overriding this in a subclassed panel,\n-- make sure you call `ixTooltipRow:PaintBackground` at the *beginning* of your function to make its style\n-- consistent with the rest of the framework.\n-- @realm client\n-- @number width Width of the panel\n-- @number height Height of the panel\nfunction PANEL:Paint(width, height)\n\tself:PaintBackground(width, height)\nend\n\nvgui.Register(\"ixTooltipRow\", PANEL, \"DLabel\")\n\n--- Generic information panel.\n-- Tooltips are used extensively throughout Helix: for item information, character displays, entity status, etc.\n-- The tooltip system can be used on any panel or entity you would like to show standardized information for. Tooltips\n-- consist of the parent container panel (`ixTooltip`), which is filled with rows of information (usually\n-- `ixTooltipRow`, but can be any docked panel if non-text information needs to be shown, like an item's size).\n--\n-- Tooltips can be added to panel with `panel:SetHelixTooltip()`. An example taken from the scoreboard:\n-- \tpanel:SetHelixTooltip(function(tooltip)\n-- \t\tlocal name = tooltip:AddRow(\"name\")\n-- \t\tname:SetImportant()\n-- \t\tname:SetText(client:SteamName())\n-- \t\tname:SetBackgroundColor(team.GetColor(client:Team()))\n-- \t\tname:SizeToContents()\n--\n-- \t\ttooltip:SizeToContents()\n-- \tend)\n-- @panel ixTooltip\nDEFINE_BASECLASS(\"Panel\")\nPANEL = {}\n\nAccessorFunc(PANEL, \"entity\", \"Entity\")\nAccessorFunc(PANEL, \"mousePadding\", \"MousePadding\", FORCE_NUMBER)\nAccessorFunc(PANEL, \"bDrawArrow\", \"DrawArrow\", FORCE_BOOL)\nAccessorFunc(PANEL, \"arrowColor\", \"ArrowColor\")\nAccessorFunc(PANEL, \"bHideArrowWhenRaised\", \"HideArrowWhenRaised\", FORCE_BOOL)\nAccessorFunc(PANEL, \"bArrowFollowEntity\", \"ArrowFollowEntity\", FORCE_BOOL)\n\nfunction PANEL:Init()\n\tself.fraction = 0\n\tself.mousePadding = 16\n\tself.arrowColor = ix.config.Get(\"color\")\n\tself.bHideArrowWhenRaised = true\n\tself.bArrowFollowEntity = true\n\tself.bMinimal = false\n\n\tself.lastX, self.lastY = self:GetCursorPosition()\n\tself.arrowX, self.arrowY = ScrW() * 0.5, ScrH() * 0.5\n\n\tself:SetAlpha(0)\n\tself:SetSize(0, 0)\n\tself:SetDrawOnTop(true)\n\tself:SetMouseInputEnabled(false)\n\n\tself:CreateAnimation(animationTime, {\n\t\tindex = 1,\n\t\ttarget = {fraction = 1},\n\t\teasing = \"outQuint\",\n\n\t\tThink = function(animation, panel)\n\t\t\tpanel:SetAlpha(panel.fraction * 255)\n\t\tend\n\t})\nend\n\n--- Whether or not this tooltip should be displayed in a minimal format.\n-- @realm client\n-- @treturn bool True if this tooltip should be displayed in a minimal format\n-- @see ixTooltipRow:IsMinimal\nfunction PANEL:IsMinimal()\n\treturn self.bMinimal\nend\n\n-- ensure all children are painted manually\nfunction PANEL:Add(...)\n\tlocal panel = BaseClass.Add(self, ...)\n\tpanel:SetPaintedManually(true)\n\n\treturn panel\nend\n\n--- Creates a new `ixTooltipRow` panel and adds it to the bottom of this tooltip.\n-- @realm client\n-- @string id Name of the new row. This is used to reorder rows if needed\n-- @treturn panel Created row\nfunction PANEL:AddRow(id)\n\tlocal panel = self:Add(\"ixTooltipRow\")\n\tpanel.id = id\n\tpanel:SetZPos(#self:GetChildren() * 10)\n\n\treturn panel\nend\n\n--- Creates a new `ixTooltipRow` and adds it after the row with the given `id`. The order of the rows is set via\n-- setting the Z position of the panels, as this is how VGUI handles ordering with docked panels.\n-- @realm client\n-- @string after Name of the row to insert after\n-- @string id Name of the newly created row\n-- @treturn panel Created row\nfunction PANEL:AddRowAfter(after, id)\n\tlocal panel = self:AddRow(id)\n\tafter = self:GetRow(after)\n\n\tif (!IsValid(after)) then\n\t\treturn panel\n\tend\n\n\tpanel:SetZPos(after:GetZPos() + 1)\n\n\treturn panel\nend\n\n--- Sets the entity associated with this tooltip. Note that this function is not how you get entities to show tooltips.\n-- @internal\n-- @realm client\n-- @entity entity Entity to associate with this tooltip\nfunction PANEL:SetEntity(entity)\n\tif (!IsValid(entity)) then\n\t\tself.bEntity = false\n\t\treturn\n\tend\n\n\t-- don't show entity tooltips if we have an entity menu open\n\tif (IsValid(ix.menu.panel)) then\n\t\tself:Remove()\n\t\treturn\n\tend\n\n\tif (entity:IsPlayer()) then\n\t\tlocal character = entity:GetCharacter()\n\n\t\tif (character) then\n\t\t\t-- we want to group things that will most likely have backgrounds (e.g name/health status)\n\t\t\thook.Run(\"PopulateImportantCharacterInfo\", entity, character, self)\n\t\t\thook.Run(\"PopulateCharacterInfo\", entity, character, self)\n\t\tend\n\telse\n\t\tif (entity.OnPopulateEntityInfo) then\n\t\t\tentity:OnPopulateEntityInfo(self)\n\t\telse\n\t\t\thook.Run(\"PopulateEntityInfo\", entity, self)\n\t\tend\n\tend\n\n\tself:SizeToContents()\n\n\tself.entity = entity\n\tself.bEntity = true\nend\n\nfunction PANEL:PaintUnder(width, height)\nend\n\nfunction PANEL:Paint(width, height)\n\tself:PaintUnder()\n\n\t-- directional arrow\n\tself.bRaised = LocalPlayer():IsWepRaised()\n\n\tif (!self.bClosing) then\n\t\tif (self.bEntity and IsValid(self.entity) and self.bArrowFollowEntity) then\n\t\t\tlocal entity = self.entity\n\t\t\tlocal position = select(1, entity:GetBonePosition(entity:LookupBone(\"ValveBiped.Bip01_Spine\") or -1)) or\n\t\t\t\tentity:LocalToWorld(entity:OBBCenter())\n\n\t\t\tposition = position:ToScreen()\n\t\t\tself.arrowX = math.Clamp(position.x, 0, ScrW())\n\t\t\tself.arrowY = math.Clamp(position.y, 0, ScrH())\n\t\tend\n\tend\n\n\t-- arrow\n\tif (self.bDrawArrow or (self.bDrawArrow and self.bRaised and !self.bHideArrowWhenRaised)) then\n\t\tlocal x, y = self:ScreenToLocal(self.arrowX, self.arrowY)\n\n\t\tDisableClipping(true)\n\t\t\tsurface.SetDrawColor(self.arrowColor)\n\t\t\tsurface.DrawLine(0, 0, x * self.fraction, y * self.fraction)\n\t\t\tsurface.DrawRect((x - 2) * self.fraction, (y - 2) * self.fraction, 4, 4)\n\t\tDisableClipping(false)\n\tend\n\n\t-- contents\n\tlocal x, y = self:GetPos()\n\n\trender.SetScissorRect(x, y, x + width * self.fraction, y + height, true)\n\t\tderma.SkinFunc(\"PaintTooltipBackground\", self, width, height)\n\n\t\tfor _, v in ipairs(self:GetChildren()) do\n\t\t\tif (IsValid(v)) then\n\t\t\t\tv:PaintManual()\n\t\t\tend\n\t\tend\n\trender.SetScissorRect(0, 0, 0, 0, false)\nend\n\n--- Returns the current position of the mouse cursor on the screen.\n-- @realm client\n-- @treturn number X position of cursor\n-- @treturn number Y position of cursor\nfunction PANEL:GetCursorPosition()\n\tlocal width, height = self:GetSize()\n\tlocal mouseX, mouseY = gui.MousePos()\n\n\treturn math.Clamp(mouseX + self.mousePadding, 0, ScrW() - width), math.Clamp(mouseY, 0, ScrH() - height)\nend\n\nfunction PANEL:Think()\n\tif (!self.bEntity) then\n\t\tif (!vgui.CursorVisible()) then\n\t\t\tself:SetPos(self.lastX, self.lastY)\n\n\t\t\t-- if the cursor isn't visible then we don't really need the tooltip to be shown\n\t\t\tif (!self.bClosing) then\n\t\t\t\tself:Remove()\n\t\t\tend\n\t\telse\n\t\t\tlocal newX, newY = self:GetCursorPosition()\n\n\t\t\tself:SetPos(newX, newY)\n\t\t\tself.lastX, self.lastY = newX, newY\n\t\tend\n\n\t\tself:MoveToFront() -- dragging a panel w/ tooltip will push the tooltip beneath even the menu panel(???)\n\telseif (IsValid(self.entity) and !self.bClosing) then\n\t\tif (self.bRaised) then\n\t\t\tself:SetPos(\n\t\t\t\tScrW() * 0.5 - self:GetWide() * 0.5,\n\t\t\t\tmath.min(ScrH() * 0.5 + self:GetTall() + 32, ScrH() - self:GetTall())\n\t\t\t)\n\t\telse\n\t\t\tlocal entity = self.entity\n\t\t\tlocal min, max = entity:GetRotatedAABB(entity:OBBMins() * 0.5, entity:OBBMaxs() * 0.5)\n\t\t\tmin = entity:LocalToWorld(min):ToScreen().x\n\t\t\tmax = entity:LocalToWorld(max):ToScreen().x\n\n\t\t\tself:SetPos(\n\t\t\t\tmath.Clamp(math.max(min, max), ScrW() * 0.5 + 64, ScrW() - self:GetWide()),\n\t\t\t\tScrH() * 0.5 - self:GetTall() * 0.5\n\t\t\t)\n\t\tend\n\tend\nend\n\n--- Returns an `ixTooltipRow` corresponding to the given name.\n-- @realm client\n-- @string id Name of the row\n-- @treturn[1] panel Corresponding row\n-- @treturn[2] nil If the row doesn't exist\nfunction PANEL:GetRow(id)\n\tfor _, v in ipairs(self:GetChildren()) do\n\t\tif (IsValid(v) and v.id == id) then\n\t\t\treturn v\n\t\tend\n\tend\nend\n\n--- Resizes the tooltip to fit all of the child panels. You should always call this after you are done\n-- adding all of your rows.\n-- @realm client\nfunction PANEL:SizeToContents()\n\tlocal height = 0\n\tlocal width = 0\n\n\tfor _, v in ipairs(self:GetChildren()) do\n\t\tif (v:GetWide() > width) then\n\t\t\twidth = v:GetWide()\n\t\tend\n\n\t\theight = height + v:GetTall()\n\tend\n\n\tself:SetSize(width, height)\nend\n\nfunction PANEL:Remove()\n\tif (self.bClosing) then\n\t\treturn\n\tend\n\n\tself.bClosing = true\n\tself:CreateAnimation(animationTime * 0.5, {\n\t\ttarget = {fraction = 0},\n\t\teasing = \"outQuint\",\n\n\t\tThink = function(animation, panel)\n\t\t\tpanel:SetAlpha(panel.fraction * 255)\n\t\tend,\n\n\t\tOnComplete = function(animation, panel)\n\t\t\tBaseClass.Remove(panel)\n\t\tend\n\t})\nend\n\nvgui.Register(\"ixTooltip\", PANEL, \"Panel\")\n\n-- legacy tooltip row\n\nPANEL = {}\n\nfunction PANEL:Init()\n\tself.bMinimal = true\n\tself.ixAlpha = 0 -- to avoid conflicts if we're animating a non-tooltip panel\n\n\tself:SetExpensiveShadow(1, color_black)\n\tself:SetContentAlignment(5)\nend\n\nfunction PANEL:SetImportant()\n\tself:SetFont(\"ixMinimalTitleFont\")\n\tself:SetBackgroundColor(ix.config.Get(\"color\"))\nend\n\n-- background color will affect text instead in minimal tooltips\nfunction PANEL:SetBackgroundColor(color)\n\tcolor = table.Copy(color)\n\tcolor.a = math.min(color.a or 255, 100)\n\n\tself:SetTextColor(color)\n\tself.backgroundColor = color\nend\n\nfunction PANEL:PaintBackground()\nend\n\nvgui.Register(\"ixTooltipMinimalRow\", PANEL, \"ixTooltipRow\")\n\n-- legacy tooltip\nDEFINE_BASECLASS(\"ixTooltip\")\nPANEL = {}\n\nfunction PANEL:Init()\n\tself.bMinimal = true\n\n\t-- we don't want to animate the alpha since children will handle their own animation, but we want to keep the fraction\n\t-- for the background to animate\n\tself:CreateAnimation(animationTime, {\n\t\tindex = 1,\n\t\ttarget = {fraction = 1},\n\t\teasing = \"outQuint\",\n\t})\n\n\tself:SetAlpha(255)\nend\n\n-- we don't need the children to be painted manually\nfunction PANEL:Add(...)\n\tlocal panel = BaseClass.Add(self, ...)\n\tpanel:SetPaintedManually(false)\n\n\treturn panel\nend\n\nfunction PANEL:AddRow(id)\n\tlocal panel = self:Add(\"ixTooltipMinimalRow\")\n\tpanel.id = id\n\tpanel:SetZPos(#self:GetChildren() * 10)\n\n\treturn panel\nend\n\nfunction PANEL:Paint(width, height)\n\tself:PaintUnder()\n\n\tderma.SkinFunc(\"PaintTooltipMinimalBackground\", self, width, height)\nend\n\nfunction PANEL:Think()\nend\n\nfunction PANEL:SizeToContents()\n\t-- remove any panels that shouldn't be shown in a minimal tooltip\n\tfor _, v in ipairs(self:GetChildren()) do\n\t\tif (v.bNoMinimal) then\n\t\t\tv:Remove()\n\t\tend\n\tend\n\n\tBaseClass.SizeToContents(self)\n\tself:SetPos(ScrW() * 0.5 - self:GetWide() * 0.5, ScrH() * 0.5 + self.mousePadding)\n\n\t-- we create animation here since this is the only function that usually gets called after all the rows are populated\n\tlocal children = self:GetChildren()\n\n\t-- sort by z index so we can animate them in order\n\ttable.sort(children, function(a, b)\n\t\treturn a:GetZPos() < b:GetZPos()\n\tend)\n\n\tlocal i = 1\n\tlocal count = table.Count(children)\n\n\tfor _, v in ipairs(children) do\n\t\tv.ixAlpha = v.ixAlpha or 0\n\n\t\tv:CreateAnimation((animationTime / count) * i, {\n\t\t\teasing = \"inSine\",\n\t\t\ttarget = {ixAlpha = 255},\n\t\t\tThink = function(animation, panel)\n\t\t\t\tpanel:SetAlpha(panel.ixAlpha)\n\t\t\tend\n\t\t})\n\n\t\ti = i + 1\n\tend\nend\n\nDEFINE_BASECLASS(\"Panel\")\nfunction PANEL:Remove()\n\tif (self.bClosing) then\n\t\treturn\n\tend\n\n\tself.bClosing = true\n\n\t-- we create animation here since this is the only function that usually gets called after all the rows are populated\n\tlocal children = self:GetChildren()\n\n\t-- sort by z index so we can animate them in order\n\ttable.sort(children, function(a, b)\n\t\treturn a:GetZPos() > b:GetZPos()\n\tend)\n\n\tlocal duration = animationTime * 0.5\n\tlocal i = 1\n\tlocal count = table.Count(children)\n\n\tfor _, v in ipairs(children) do\n\t\tv.ixAlpha = v.ixAlpha or 255\n\n\t\tv:CreateAnimation(duration / count * i, {\n\t\t\ttarget = {ixAlpha = 0},\n\t\t\tThink = function(animation, panel)\n\t\t\t\tpanel:SetAlpha(panel.ixAlpha)\n\t\t\tend\n\t\t})\n\n\t\ti = i + 1\n\tend\n\n\tself:CreateAnimation(duration, {\n\t\ttarget = {fraction = 0},\n\t\tOnComplete = function(animation, panel)\n\t\t\tBaseClass.Remove(panel)\n\t\tend\n\t})\nend\n\nvgui.Register(\"ixTooltipMinimal\", PANEL, \"ixTooltip\")\n"
  },
  {
    "path": "gamemode/core/hooks/cl_hooks.lua",
    "content": "\nfunction GM:ForceDermaSkin()\n\treturn \"helix\"\nend\n\nfunction GM:ScoreboardShow()\n\tif (LocalPlayer():GetCharacter()) then\n\t\tvgui.Create(\"ixMenu\")\n\tend\nend\n\nfunction GM:ScoreboardHide()\nend\n\nfunction GM:LoadFonts(font, genericFont)\n\tsurface.CreateFont(\"ix3D2DFont\", {\n\t\tfont = font,\n\t\tsize = 128,\n\t\textended = true,\n\t\tweight = 100\n\t})\n\n\tsurface.CreateFont(\"ix3D2DMediumFont\", {\n\t\tfont = font,\n\t\tsize = 48,\n\t\textended = true,\n\t\tweight = 100\n\t})\n\n\tsurface.CreateFont(\"ix3D2DSmallFont\", {\n\t\tfont = font,\n\t\tsize = 24,\n\t\textended = true,\n\t\tweight = 400\n\t})\n\n\tsurface.CreateFont(\"ixTitleFont\", {\n\t\tfont = font,\n\t\tsize = ScreenScale(30),\n\t\textended = true,\n\t\tweight = 100\n\t})\n\n\tsurface.CreateFont(\"ixSubTitleFont\", {\n\t\tfont = font,\n\t\tsize = ScreenScale(16),\n\t\textended = true,\n\t\tweight = 100\n\t})\n\n\tsurface.CreateFont(\"ixMenuMiniFont\", {\n\t\tfont = \"Roboto\",\n\t\tsize = math.max(ScreenScale(4), 18),\n\t\tweight = 300,\n\t})\n\n\tsurface.CreateFont(\"ixMenuButtonFont\", {\n\t\tfont = \"Roboto Th\",\n\t\tsize = ScreenScale(14),\n\t\textended = true,\n\t\tweight = 100\n\t})\n\n\tsurface.CreateFont(\"ixMenuButtonFontSmall\", {\n\t\tfont = \"Roboto Th\",\n\t\tsize = ScreenScale(10),\n\t\textended = true,\n\t\tweight = 100\n\t})\n\n\tsurface.CreateFont(\"ixMenuButtonFontThick\", {\n\t\tfont = \"Roboto\",\n\t\tsize = ScreenScale(14),\n\t\textended = true,\n\t\tweight = 300\n\t})\n\n\tsurface.CreateFont(\"ixMenuButtonLabelFont\", {\n\t\tfont = \"Roboto Th\",\n\t\tsize = 28,\n\t\textended = true,\n\t\tweight = 100\n\t})\n\n\tsurface.CreateFont(\"ixMenuButtonHugeFont\", {\n\t\tfont = \"Roboto Th\",\n\t\tsize = ScreenScale(24),\n\t\textended = true,\n\t\tweight = 100\n\t})\n\n\tsurface.CreateFont(\"ixToolTipText\", {\n\t\tfont = font,\n\t\tsize = 20,\n\t\textended = true,\n\t\tweight = 500\n\t})\n\n\tsurface.CreateFont(\"ixMonoSmallFont\", {\n\t\tfont = \"Consolas\",\n\t\tsize = 12,\n\t\textended = true,\n\t\tweight = 800\n\t})\n\n\tsurface.CreateFont(\"ixMonoMediumFont\", {\n\t\tfont = \"Consolas\",\n\t\tsize = 22,\n\t\textended = true,\n\t\tweight = 800\n\t})\n\n\t-- The more readable font.\n\tfont = genericFont\n\n\tsurface.CreateFont(\"ixBigFont\", {\n\t\tfont = font,\n\t\tsize = 36,\n\t\textended = true,\n\t\tweight = 1000\n\t})\n\n\tsurface.CreateFont(\"ixMediumFont\", {\n\t\tfont = font,\n\t\tsize = 25,\n\t\textended = true,\n\t\tweight = 1000\n\t})\n\n\tsurface.CreateFont(\"ixNoticeFont\", {\n\t\tfont = font,\n\t\tsize = math.max(ScreenScale(8), 18),\n\t\tweight = 100,\n\t\textended = true,\n\t\tantialias = true\n\t})\n\n\tsurface.CreateFont(\"ixMediumLightFont\", {\n\t\tfont = font,\n\t\tsize = 25,\n\t\textended = true,\n\t\tweight = 200\n\t})\n\n\tsurface.CreateFont(\"ixMediumLightBlurFont\", {\n\t\tfont = font,\n\t\tsize = 25,\n\t\textended = true,\n\t\tweight = 200,\n\t\tblursize = 4\n\t})\n\n\tsurface.CreateFont(\"ixGenericFont\", {\n\t\tfont = font,\n\t\tsize = 20,\n\t\textended = true,\n\t\tweight = 1000\n\t})\n\n\tsurface.CreateFont(\"ixChatFont\", {\n\t\tfont = font,\n\t\tsize = math.max(ScreenScale(7), 17) * ix.option.Get(\"chatFontScale\", 1),\n\t\textended = true,\n\t\tweight = 600,\n\t\tantialias = true\n\t})\n\n\tsurface.CreateFont(\"ixChatFontItalics\", {\n\t\tfont = font,\n\t\tsize = math.max(ScreenScale(7), 17) * ix.option.Get(\"chatFontScale\", 1),\n\t\textended = true,\n\t\tweight = 600,\n\t\tantialias = true,\n\t\titalic = true\n\t})\n\n\tsurface.CreateFont(\"ixSmallTitleFont\", {\n\t\tfont = \"Roboto Th\",\n\t\tsize = math.max(ScreenScale(12), 24),\n\t\textended = true,\n\t\tweight = 100\n\t})\n\n\tsurface.CreateFont(\"ixMinimalTitleFont\", {\n\t\tfont = \"Roboto\",\n\t\tsize = math.max(ScreenScale(8), 22),\n\t\textended = true,\n\t\tweight = 800\n\t})\n\n\tsurface.CreateFont(\"ixSmallFont\", {\n\t\tfont = font,\n\t\tsize = math.max(ScreenScale(6), 17),\n\t\textended = true,\n\t\tweight = 500\n\t})\n\n\tsurface.CreateFont(\"ixItemDescFont\", {\n\t\tfont = font,\n\t\tsize = math.max(ScreenScale(6), 17),\n\t\textended = true,\n\t\tshadow = true,\n\t\tweight = 500\n\t})\n\n\tsurface.CreateFont(\"ixSmallBoldFont\", {\n\t\tfont = font,\n\t\tsize = math.max(ScreenScale(8), 20),\n\t\textended = true,\n\t\tweight = 800\n\t})\n\n\tsurface.CreateFont(\"ixItemBoldFont\", {\n\t\tfont = font,\n\t\tshadow = true,\n\t\tsize = math.max(ScreenScale(8), 20),\n\t\textended = true,\n\t\tweight = 800\n\t})\n\n\t-- Introduction fancy font.\n\tfont = \"Roboto Th\"\n\n\tsurface.CreateFont(\"ixIntroTitleFont\", {\n\t\tfont = font,\n\t\tsize = math.min(ScreenScale(128), 128),\n\t\textended = true,\n\t\tweight = 100\n\t})\n\n\tsurface.CreateFont(\"ixIntroTitleBlurFont\", {\n\t\tfont = font,\n\t\tsize = math.min(ScreenScale(128), 128),\n\t\textended = true,\n\t\tweight = 100,\n\t\tblursize = 4\n\t})\n\n\tsurface.CreateFont(\"ixIntroSubtitleFont\", {\n\t\tfont = font,\n\t\tsize = ScreenScale(24),\n\t\textended = true,\n\t\tweight = 100\n\t})\n\n\tsurface.CreateFont(\"ixIntroSmallFont\", {\n\t\tfont = font,\n\t\tsize = ScreenScale(14),\n\t\textended = true,\n\t\tweight = 100\n\t})\n\n\tsurface.CreateFont(\"ixIconsSmall\", {\n\t\tfont = \"fontello\",\n\t\tsize = 22,\n\t\textended = true,\n\t\tweight = 500\n\t})\n\n\tsurface.CreateFont(\"ixSmallTitleIcons\", {\n\t\tfont = \"fontello\",\n\t\tsize = math.max(ScreenScale(11), 23),\n\t\textended = true,\n\t\tweight = 100\n\t})\n\n\tsurface.CreateFont(\"ixIconsMedium\", {\n\t\tfont = \"fontello\",\n\t\textended = true,\n\t\tsize = 28,\n\t\tweight = 500\n\t})\n\n\tsurface.CreateFont(\"ixIconsMenuButton\", {\n\t\tfont = \"fontello\",\n\t\tsize = ScreenScale(14),\n\t\textended = true,\n\t\tweight = 100\n\t})\n\n\tsurface.CreateFont(\"ixIconsBig\", {\n\t\tfont = \"fontello\",\n\t\textended = true,\n\t\tsize = 48,\n\t\tweight = 500\n\t})\nend\n\nfunction GM:OnCharacterMenuCreated(panel)\n\tif (IsValid(ix.gui.notices)) then\n\t\tix.gui.notices:Clear()\n\tend\nend\n\nlocal LOWERED_ANGLES = Angle(30, 0, -25)\n\nfunction GM:CalcViewModelView(weapon, viewModel, oldEyePos, oldEyeAngles, eyePos, eyeAngles)\n\tif (!IsValid(weapon)) then\n\t\treturn\n\tend\n\n\tlocal client = LocalPlayer()\n\tlocal bWepRaised = client:IsWepRaised()\n\n\t-- update tween if the raised state is out of date\n\tif (client.ixWasWeaponRaised != bWepRaised) then\n\t\tlocal fraction = bWepRaised and 0 or 1\n\n\t\tclient.ixRaisedFraction = 1 - fraction\n\t\tclient.ixRaisedTween = ix.tween.new(0.75, client, {\n\t\t\tixRaisedFraction = fraction\n\t\t}, \"outQuint\")\n\n\t\tclient.ixWasWeaponRaised = bWepRaised\n\tend\n\n\tlocal fraction = client.ixRaisedFraction\n\tlocal rotation = weapon.LowerAngles or LOWERED_ANGLES\n\n\tif (ix.option.Get(\"altLower\", true) and weapon.LowerAngles2) then\n\t\trotation = weapon.LowerAngles2\n\tend\n\n\teyeAngles:RotateAroundAxis(eyeAngles:Up(), rotation.p * fraction)\n\teyeAngles:RotateAroundAxis(eyeAngles:Forward(), rotation.y * fraction)\n\teyeAngles:RotateAroundAxis(eyeAngles:Right(), rotation.r * fraction)\n\n\tviewModel:SetAngles(eyeAngles)\n\treturn self.BaseClass:CalcViewModelView(weapon, viewModel, oldEyePos, oldEyeAngles, eyePos, eyeAngles)\nend\n\nfunction GM:LoadIntro()\n\tif (!IsValid(ix.gui.intro)) then\n\t\tvgui.Create(\"ixIntro\")\n\tend\nend\n\nfunction GM:CharacterLoaded()\n\tlocal menu = ix.gui.characterMenu\n\n\tif (IsValid(menu)) then\n\t\tmenu:Close((LocalPlayer().GetCharacter and LocalPlayer():GetCharacter()) and true or nil)\n\tend\nend\n\nfunction GM:InitializedConfig()\n\tlocal color = ix.config.Get(\"color\")\n\n\thook.Run(\"LoadFonts\", ix.config.Get(\"font\"), ix.config.Get(\"genericFont\"))\n\thook.Run(\"ColorSchemeChanged\", color)\n\n\tif (!ix.config.loaded and !IsValid(ix.gui.loading)) then\n\t\tlocal loader = vgui.Create(\"EditablePanel\")\n\t\tloader:ParentToHUD()\n\t\tloader:Dock(FILL)\n\t\tloader.Paint = function(this, w, h)\n\t\t\tsurface.SetDrawColor(0, 0, 0)\n\t\t\tsurface.DrawRect(0, 0, w, h)\n\t\tend\n\n\t\tlocal statusLabel = loader:Add(\"DLabel\")\n\t\tstatusLabel:Dock(FILL)\n\t\tstatusLabel:SetText(L\"loading\")\n\t\tstatusLabel:SetFont(\"ixTitleFont\")\n\t\tstatusLabel:SetContentAlignment(5)\n\t\tstatusLabel:SetTextColor(color_white)\n\n\t\ttimer.Simple(5, function()\n\t\t\tif (IsValid(ix.gui.loading)) then\n\t\t\t\tlocal fault = GetNetVar(\"dbError\")\n\n\t\t\t\tif (fault) then\n\t\t\t\t\tstatusLabel:SetText(fault and L\"dbError\" or L\"loading\")\n\n\t\t\t\t\tlocal label = loader:Add(\"DLabel\")\n\t\t\t\t\tlabel:DockMargin(0, 64, 0, 0)\n\t\t\t\t\tlabel:Dock(TOP)\n\t\t\t\t\tlabel:SetFont(\"ixSubTitleFont\")\n\t\t\t\t\tlabel:SetText(fault)\n\t\t\t\t\tlabel:SetContentAlignment(5)\n\t\t\t\t\tlabel:SizeToContentsY()\n\t\t\t\t\tlabel:SetTextColor(Color(255, 50, 50))\n\t\t\t\tend\n\t\t\tend\n\t\tend)\n\n\t\tix.gui.loading = loader\n\t\tix.config.loaded = true\n\n\t\tif (ix.config.Get(\"intro\", true) and ix.option.Get(\"showIntro\", true)) then\n\t\t\thook.Run(\"LoadIntro\")\n\t\tend\n\tend\nend\n\nfunction GM:InitPostEntity()\n\tix.joinTime = RealTime() - 0.9716\n\tix.option.Sync()\n\n\tix.gui.bars = vgui.Create(\"ixInfoBarManager\")\nend\n\nfunction GM:NetworkEntityCreated(entity)\n\tif (entity:IsPlayer()) then\n\t\tentity:SetIK(false)\n\n\t\t-- we've just discovered a new player, so we need to update their animation state\n\t\tif (entity != LocalPlayer()) then\n\t\t\t-- we don't need to call the PlayerWeaponChanged hook here since it'll be handled below,\n\t\t\t-- when this player's weapon has been discovered\n\t\t\thook.Run(\"PlayerModelChanged\", entity, entity:GetModel())\n\t\tend\n\telseif (entity:IsWeapon()) then\n\t\tlocal owner = entity:GetOwner()\n\n\t\tif (IsValid(owner) and owner:IsPlayer() and entity == owner:GetActiveWeapon()) then\n\t\t\thook.Run(\"PlayerWeaponChanged\", owner, entity)\n\t\tend\n\tend\nend\n\nlocal vignette = ix.util.GetMaterial(\"helix/gui/vignette.png\")\nlocal vignetteAlphaGoal = 0\nlocal vignetteAlphaDelta = 0\nlocal vignetteTraceHeight = Vector(0, 0, 768)\nlocal blurGoal = 0\nlocal blurDelta = 0\nlocal hasVignetteMaterial = !vignette:IsError()\n\ntimer.Create(\"ixVignetteChecker\", 1, 0, function()\n\tlocal client = LocalPlayer()\n\n\tif (IsValid(client)) then\n\t\tlocal data = {}\n\t\t\tdata.start = client:GetPos()\n\t\t\tdata.endpos = data.start + vignetteTraceHeight\n\t\t\tdata.filter = client\n\t\tlocal trace = util.TraceLine(data)\n\n\t\t-- this timer could run before InitPostEntity is called, so we have to check for the validity of the trace table\n\t\tif (trace and trace.Hit) then\n\t\t\tvignetteAlphaGoal = 80\n\t\telse\n\t\t\tvignetteAlphaGoal = 0\n\t\tend\n\tend\nend)\n\nfunction GM:CalcView(client, origin, angles, fov)\n\tlocal view = self.BaseClass:CalcView(client, origin, angles, fov) or {}\n\tlocal entity = Entity(client:GetLocalVar(\"ragdoll\", 0))\n\tlocal ragdoll = IsValid(client:GetRagdollEntity()) and client:GetRagdollEntity() or entity\n\n\tif ((!client:ShouldDrawLocalPlayer() and IsValid(entity) and entity:IsRagdoll())\n\tor (!LocalPlayer():Alive() and IsValid(ragdoll))) then\n\t\tlocal ent = LocalPlayer():Alive() and entity or ragdoll\n\t\tlocal index = ent:LookupAttachment(\"eyes\")\n\n\t\tif (index) then\n\t\t\tlocal data = ent:GetAttachment(index)\n\n\t\t\tif (data) then\n\t\t\t\tview.origin = data.Pos\n\t\t\t\tview.angles = data.Ang\n\t\t\tend\n\n\t\t\treturn view\n\t\tend\n\tend\n\n\tlocal menu = ix.gui.menu\n\tlocal entityMenu = ix.menu.panel\n\n\tif (IsValid(menu) and menu:IsVisible() and menu:GetCharacterOverview()) then\n\t\tlocal newOrigin, newAngles, newFOV, bDrawPlayer = menu:GetOverviewInfo(origin, angles, fov)\n\n\t\tview.drawviewer = bDrawPlayer\n\t\tview.fov = newFOV\n\t\tview.origin = newOrigin\n\t\tview.angles = newAngles\n\telseif (IsValid(entityMenu)) then\n\t\tview.angles = entityMenu:GetOverviewInfo(origin, angles)\n\tend\n\n\treturn view\nend\n\nlocal hookRun = hook.Run\n\ndo\n\tlocal aimLength = 0.35\n\tlocal aimTime = 0\n\tlocal aimEntity\n\tlocal lastEntity\n\tlocal lastTrace = {}\n\n\ttimer.Create(\"ixCheckTargetEntity\", 0.1, 0, function()\n\t\tlocal client = LocalPlayer()\n\t\tlocal time = SysTime()\n\n\t\tif (!IsValid(client)) then\n\t\t\treturn\n\t\tend\n\n\t\tlocal character = client:GetCharacter()\n\n\t\tif (!character) then\n\t\t\treturn\n\t\tend\n\n\t\tlastTrace.start = client:GetShootPos()\n\t\tlastTrace.endpos = lastTrace.start + client:GetAimVector(client) * 160\n\t\tlastTrace.filter = client\n\t\tlastTrace.mask = MASK_SHOT_HULL\n\n\t\tlastEntity = util.TraceHull(lastTrace).Entity\n\n\t\tif (lastEntity != aimEntity) then\n\t\t\taimTime = time + aimLength\n\t\t\taimEntity = lastEntity\n\t\tend\n\n\t\tlocal panel = ix.gui.entityInfo\n\t\tlocal bShouldShow = time >= aimTime and (!IsValid(ix.gui.menu) or ix.gui.menu.bClosing) and\n\t\t\t(!IsValid(ix.gui.characterMenu) or ix.gui.characterMenu.bClosing)\n\t\tlocal bShouldPopulate = lastEntity.OnShouldPopulateEntityInfo and lastEntity:OnShouldPopulateEntityInfo() or true\n\n\t\tif (bShouldShow and IsValid(lastEntity) and hookRun(\"ShouldPopulateEntityInfo\", lastEntity) != false and\n\t\t\t(lastEntity.PopulateEntityInfo or bShouldPopulate)) then\n\n\t\t\tif (!IsValid(panel) or (IsValid(panel) and panel:GetEntity() != lastEntity)) then\n\t\t\t\tif (IsValid(ix.gui.entityInfo)) then\n\t\t\t\t\tix.gui.entityInfo:Remove()\n\t\t\t\tend\n\n\t\t\t\tlocal infoPanel = vgui.Create(ix.option.Get(\"minimalTooltips\", false) and \"ixTooltipMinimal\" or \"ixTooltip\")\n\t\t\t\tlocal entityPlayer = lastEntity:GetNetVar(\"player\")\n\n\t\t\t\tif (entityPlayer) then\n\t\t\t\t\tinfoPanel:SetEntity(entityPlayer)\n\t\t\t\t\tinfoPanel.entity = lastEntity\n\t\t\t\telse\n\t\t\t\t\tinfoPanel:SetEntity(lastEntity)\n\t\t\t\tend\n\n\t\t\t\tinfoPanel:SetDrawArrow(true)\n\t\t\t\tix.gui.entityInfo = infoPanel\n\t\t\tend\n\t\telseif (IsValid(panel)) then\n\t\t\tpanel:Remove()\n\t\tend\n\tend)\nend\n\nlocal mathApproach = math.Approach\nlocal surface = surface\n\nfunction GM:HUDPaintBackground()\n\tlocal client = LocalPlayer()\n\n\tif (!client:GetCharacter()) then\n\t\treturn\n\tend\n\n\tlocal frameTime = FrameTime()\n\tlocal scrW, scrH = ScrW(), ScrH()\n\n\tif (hasVignetteMaterial and ix.config.Get(\"vignette\")) then\n\t\tvignetteAlphaDelta = mathApproach(vignetteAlphaDelta, vignetteAlphaGoal, frameTime * 30)\n\n\t\tsurface.SetDrawColor(0, 0, 0, 175 + vignetteAlphaDelta)\n\t\tsurface.SetMaterial(vignette)\n\t\tsurface.DrawTexturedRect(0, 0, scrW, scrH)\n\tend\n\n\tblurGoal = client:GetLocalVar(\"blur\", 0) + (hookRun(\"AdjustBlurAmount\", blurGoal) or 0)\n\n\tif (blurDelta != blurGoal) then\n\t\tblurDelta = mathApproach(blurDelta, blurGoal, frameTime * 20)\n\tend\n\n\tif (blurDelta > 0 and !client:ShouldDrawLocalPlayer()) then\n\t\tix.util.DrawBlurAt(0, 0, scrW, scrH, blurDelta)\n\tend\n\n\tself.BaseClass:PaintWorldTips()\n\n\tlocal weapon = client:GetActiveWeapon()\n\n\tif (IsValid(weapon) and hook.Run(\"CanDrawAmmoHUD\", weapon) != false and weapon.DrawAmmo != false) then\n\t\tlocal clip = weapon:Clip1()\n\t\tlocal clipMax = weapon:GetMaxClip1()\n\t\tlocal count = client:GetAmmoCount(weapon:GetPrimaryAmmoType())\n\t\tlocal secondary = client:GetAmmoCount(weapon:GetSecondaryAmmoType())\n\t\tlocal x, y = scrW - 80, scrH - 80\n\n\t\tif (secondary > 0) then\n\t\t\tix.util.DrawBlurAt(x, y, 64, 64)\n\n\t\t\tsurface.SetDrawColor(255, 255, 255, 5)\n\t\t\tsurface.DrawRect(x, y, 64, 64)\n\t\t\tsurface.SetDrawColor(255, 255, 255, 3)\n\t\t\tsurface.DrawOutlinedRect(x, y, 64, 64)\n\n\t\t\tix.util.DrawText(secondary, x + 32, y + 32, nil, 1, 1, \"ixBigFont\")\n\t\tend\n\n\t\tif (weapon:GetClass() != \"weapon_slam\" and clip > 0 or count > 0) then\n\t\t\tx = x - (secondary > 0 and 144 or 64)\n\n\t\t\tix.util.DrawBlurAt(x, y, 128, 64)\n\n\t\t\tsurface.SetDrawColor(255, 255, 255, 5)\n\t\t\tsurface.DrawRect(x, y, 128, 64)\n\t\t\tsurface.SetDrawColor(255, 255, 255, 3)\n\t\t\tsurface.DrawOutlinedRect(x, y, 128, 64)\n\n\t\t\tix.util.DrawText((clip == -1 or clipMax == -1) and count or clip..\"/\"..count, x + 64, y + 32, nil, 1, 1, \"ixBigFont\")\n\t\tend\n\tend\n\n\tif (client:GetLocalVar(\"restricted\") and !client:GetLocalVar(\"restrictNoMsg\")) then\n\t\tix.util.DrawText(L\"restricted\", scrW * 0.5, scrH * 0.33, nil, 1, 1, \"ixBigFont\")\n\tend\nend\n\nfunction GM:PostDrawOpaqueRenderables(bDepth, bSkybox)\n\tif (bDepth or bSkybox or #ix.blurRenderQueue == 0) then\n\t\treturn\n\tend\n\n\tix.util.ResetStencilValues()\n\trender.SetStencilEnable(true)\n\t\trender.SetStencilWriteMask(27)\n\t\trender.SetStencilTestMask(27)\n\t\trender.SetStencilFailOperation(STENCILOPERATION_KEEP)\n\t\trender.SetStencilZFailOperation(STENCILOPERATION_KEEP)\n\t\trender.SetStencilPassOperation(STENCILOPERATION_REPLACE)\n\t\trender.SetStencilCompareFunction(STENCILCOMPARISONFUNCTION_ALWAYS)\n\t\trender.SetStencilReferenceValue(27)\n\n\t\tfor i = 1, #ix.blurRenderQueue do\n\t\t\tix.blurRenderQueue[i]()\n\t\tend\n\n\t\trender.SetStencilReferenceValue(34)\n\t\trender.SetStencilCompareFunction(STENCILCOMPARISONFUNCTION_EQUAL)\n\t\trender.SetStencilPassOperation(STENCILOPERATION_REPLACE)\n\t\trender.SetStencilReferenceValue(27)\n\n\t\tcam.Start2D()\n\t\t\tix.util.DrawBlurAt(0, 0, ScrW(), ScrH())\n\t\tcam.End2D()\n\trender.SetStencilEnable(false)\n\n\tix.blurRenderQueue = {}\nend\n\nfunction GM:PostDrawHUD()\n\tcam.Start2D()\n\t\tix.hud.DrawAll()\n\n\t\tif (!IsValid(ix.gui.deathScreen) and (!IsValid(ix.gui.characterMenu) or ix.gui.characterMenu:IsClosing())) then\n\t\t\tix.bar.DrawAction()\n\t\tend\n\tcam.End2D()\nend\n\nfunction GM:ShouldPopulateEntityInfo(entity)\n\tlocal client = LocalPlayer()\n\tlocal ragdoll = Entity(client:GetLocalVar(\"ragdoll\", 0))\n\tlocal entityPlayer = entity:GetNetVar(\"player\")\n\n\tif (vgui.CursorVisible() or !client:Alive() or IsValid(ragdoll) or entity == client or entityPlayer == client) then\n\t\treturn false\n\tend\nend\n\nlocal injTextTable = {\n\t[.3] = {\"injMajor\", Color(192, 57, 43)},\n\t[.6] = {\"injLittle\", Color(231, 76, 60)},\n}\n\nfunction GM:GetInjuredText(client)\n\tlocal health = client:Health()\n\n\tfor k, v in pairs(injTextTable) do\n\t\tif ((health / client:GetMaxHealth()) < k) then\n\t\t\treturn v[1], v[2]\n\t\tend\n\tend\nend\n\nfunction GM:PopulateImportantCharacterInfo(client, character, container)\n\tlocal color = team.GetColor(client:Team())\n\tcontainer:SetArrowColor(color)\n\n\t-- name\n\tlocal name = container:AddRow(\"name\")\n\tname:SetImportant()\n\tname:SetText(hookRun(\"GetCharacterName\", client) or character:GetName())\n\tname:SetBackgroundColor(color)\n\tname:SizeToContents()\n\n\t-- injured text\n\tlocal injureText, injureTextColor = hookRun(\"GetInjuredText\", client)\n\n\tif (injureText) then\n\t\tlocal injure = container:AddRow(\"injureText\")\n\n\t\tinjure:SetText(L(injureText))\n\t\tinjure:SetBackgroundColor(injureTextColor)\n\t\tinjure:SizeToContents()\n\tend\nend\n\nfunction GM:PopulateCharacterInfo(client, character, container)\n\t-- description\n\tlocal descriptionText = character:GetDescription()\n\tdescriptionText = (descriptionText:utf8len() > 128 and\n\t\tstring.format(\"%s...\", descriptionText:utf8sub(1, 125)) or\n\t\tdescriptionText)\n\n\tif (descriptionText != \"\") then\n\t\tlocal description = container:AddRow(\"description\")\n\t\tdescription:SetText(descriptionText)\n\t\tdescription:SizeToContents()\n\tend\nend\n\nfunction GM:KeyRelease(client, key)\n\tif (!IsFirstTimePredicted()) then\n\t\treturn\n\tend\n\n\tif (key == IN_USE) then\n\t\tif (!ix.menu.IsOpen()) then\n\t\t\tlocal data = {}\n\t\t\tdata.start = client:GetShootPos()\n\t\t\tdata.endpos = data.start + client:GetAimVector() * 96\n\t\t\tdata.filter = client\n\n\t\t\tlocal entity = util.TraceLine(data).Entity\n\n\t\t\tif (IsValid(entity) and isfunction(entity.GetEntityMenu)) then\n\t\t\t\thook.Run(\"ShowEntityMenu\", entity)\n\t\t\tend\n\t\tend\n\n\t\ttimer.Remove(\"ixItemUse\")\n\n\t\tclient.ixInteractionTarget = nil\n\t\tclient.ixInteractionStartTime = nil\n\tend\nend\n\nfunction GM:PlayerBindPress(client, bind, pressed)\n\tbind = bind:lower()\n\n\tif (bind:find(\"use\") and pressed) then\n\t\tlocal pickupTime = ix.config.Get(\"itemPickupTime\", 0.5)\n\n\t\tif (pickupTime > 0) then\n\t\t\tlocal data = {}\n\t\t\t\tdata.start = client:GetShootPos()\n\t\t\t\tdata.endpos = data.start + client:GetAimVector() * 96\n\t\t\t\tdata.filter = client\n\t\t\tlocal entity = util.TraceLine(data).Entity\n\n\t\t\tif (IsValid(entity) and entity.ShowPlayerInteraction and !ix.menu.IsOpen()) then\n\t\t\t\tclient.ixInteractionTarget = entity\n\t\t\t\tclient.ixInteractionStartTime = SysTime()\n\n\t\t\t\ttimer.Create(\"ixItemUse\", pickupTime, 1, function()\n\t\t\t\t\tclient.ixInteractionTarget = nil\n\t\t\t\t\tclient.ixInteractionStartTime = nil\n\t\t\t\tend)\n\t\t\tend\n\t\tend\n\telseif (bind:find(\"jump\")) then\n\t\tlocal entity = Entity(client:GetLocalVar(\"ragdoll\", 0))\n\n\t\tif (IsValid(entity)) then\n\t\t\tix.command.Send(\"CharGetUp\")\n\t\tend\n\telseif (bind:find(\"speed\") and client:KeyDown(IN_WALK) and pressed) then\n\t\tif (LocalPlayer():Crouching()) then\n\t\t\tRunConsoleCommand(\"-duck\")\n\t\telse\n\t\t\tRunConsoleCommand(\"+duck\")\n\t\tend\n\tend\nend\n\nfunction GM:CreateMove(command)\n\tif ((IsValid(ix.gui.characterMenu) and !ix.gui.characterMenu.bClosing) or\n\t\t(IsValid(ix.gui.menu) and !ix.gui.menu.bClosing and ix.gui.menu:GetActiveTab() == \"you\")) then\n\t\tcommand:ClearButtons()\n\t\tcommand:ClearMovement()\n\tend\nend\n\n-- Called when use has been pressed on an item.\nfunction GM:ShowEntityMenu(entity)\n\tlocal options = entity:GetEntityMenu(LocalPlayer())\n\n\tif (istable(options) and !table.IsEmpty(options)) then\n\t\tix.menu.Open(options, entity)\n\tend\nend\n\nlocal hidden = {}\nhidden[\"CHudHealth\"] = true\nhidden[\"CHudBattery\"] = true\nhidden[\"CHudAmmo\"] = true\nhidden[\"CHudSecondaryAmmo\"] = true\nhidden[\"CHudCrosshair\"] = true\nhidden[\"CHudHistoryResource\"] = true\nhidden[\"CHudPoisonDamageIndicator\"] = true\nhidden[\"CHudSquadStatus\"] = true\nhidden[\"CHUDQuickInfo\"] = true\n\nfunction GM:HUDShouldDraw(element)\n\tif (hidden[element]) then\n\t\treturn false\n\tend\n\n\treturn true\nend\n\nfunction GM:ShouldDrawLocalPlayer(client)\n\tif (IsValid(ix.gui.characterMenu) and ix.gui.characterMenu:IsVisible()) then\n\t\treturn false\n\tend\nend\n\nfunction GM:PostProcessPermitted(class)\n\treturn false\nend\n\nfunction GM:RenderScreenspaceEffects()\n\tlocal menu = ix.gui.menu\n\n\tif (IsValid(menu) and menu:GetCharacterOverview()) then\n\t\tlocal client = LocalPlayer()\n\t\tlocal target = client:GetObserverTarget()\n\t\tlocal weapon = client:GetActiveWeapon()\n\n\t\tcam.Start3D()\n\t\t\tix.util.ResetStencilValues()\n\t\t\trender.SetStencilEnable(true)\n\t\t\trender.SuppressEngineLighting(true)\n\t\t\tcam.IgnoreZ(true)\n\t\t\t\trender.SetColorModulation(1, 1, 1)\n\t\t\t\trender.SetStencilWriteMask(28)\n\t\t\t\trender.SetStencilTestMask(28)\n\t\t\t\trender.SetStencilReferenceValue(28)\n\n\t\t\t\trender.SetStencilCompareFunction(STENCIL_ALWAYS)\n\t\t\t\trender.SetStencilPassOperation(STENCIL_REPLACE)\n\t\t\t\trender.SetStencilFailOperation(STENCIL_KEEP)\n\t\t\t\trender.SetStencilZFailOperation(STENCIL_KEEP)\n\n\t\t\t\tif (IsValid(target)) then\n\t\t\t\t\ttarget:DrawModel()\n\t\t\t\telse\n\t\t\t\t\tclient:DrawModel()\n\t\t\t\tend\n\n\t\t\t\tif (IsValid(weapon)) then\n\t\t\t\t\tweapon:DrawModel()\n\t\t\t\tend\n\n\t\t\t\thook.Run(\"DrawCharacterOverview\")\n\n\t\t\t\trender.SetStencilCompareFunction(STENCIL_NOTEQUAL)\n\t\t\t\trender.SetStencilPassOperation(STENCIL_KEEP)\n\n\t\t\t\tcam.Start2D()\n\t\t\t\t\tderma.SkinFunc(\"DrawCharacterStatusBackground\", menu, menu.overviewFraction)\n\t\t\t\tcam.End2D()\n\t\t\tcam.IgnoreZ(false)\n\t\t\trender.SuppressEngineLighting(false)\n\t\t\trender.SetStencilEnable(false)\n\t\tcam.End3D()\n\tend\nend\n\nfunction GM:ShowPlayerOptions(client, options)\n\toptions[\"viewProfile\"] = {\"icon16/user.png\", function()\n\t\tif (IsValid(client)) then\n\t\t\tclient:ShowProfile()\n\t\tend\n\tend}\n\toptions[\"Copy Steam ID\"] = {\"icon16/user.png\", function()\n\t\tif (IsValid(client)) then\n\t\t\tSetClipboardText(client:SteamID())\n\t\tend\n\tend}\nend\n\nfunction GM:DrawHelixModelView(panel, ent)\n\tif (ent.weapon and IsValid(ent.weapon)) then\n\t\tent.weapon:DrawModel()\n\tend\nend\n\nnet.Receive(\"ixStringRequest\", function()\n\tlocal time = net.ReadUInt(32)\n\tlocal title, subTitle = net.ReadString(), net.ReadString()\n\tlocal default = net.ReadString()\n\n\tif (title:sub(1, 1) == \"@\") then\n\t\ttitle = L(title:sub(2))\n\tend\n\n\tif (subTitle:sub(1, 1) == \"@\") then\n\t\tsubTitle = L(subTitle:sub(2))\n\tend\n\n\tDerma_StringRequest(title, subTitle, default or \"\", function(text)\n\t\tnet.Start(\"ixStringRequest\")\n\t\t\tnet.WriteUInt(time, 32)\n\t\t\tnet.WriteString(text)\n\t\tnet.SendToServer()\n\tend)\nend)\n\nnet.Receive(\"ixPlayerDeath\", function()\n\tif (IsValid(ix.gui.deathScreen)) then\n\t\tix.gui.deathScreen:Remove()\n\tend\n\n\tix.gui.deathScreen = vgui.Create(\"ixDeathScreen\")\nend)\n\nfunction GM:Think()\n\tlocal client = LocalPlayer()\n\n\tif (IsValid(client) and client:Alive() and client.ixRaisedTween) then\n\t\tclient.ixRaisedTween:update(FrameTime())\n\tend\nend\n\nfunction GM:ScreenResolutionChanged(oldW, oldH)\n\thook.Run(\"LoadFonts\", ix.config.Get(\"font\"), ix.config.Get(\"genericFont\"))\n\n\tif (IsValid(ix.gui.notices)) then\n\t\tix.gui.notices:Remove()\n\t\tix.gui.notices = vgui.Create(\"ixNoticeManager\")\n\tend\n\n\tif (IsValid(ix.gui.bars)) then\n\t\tix.gui.bars:Remove()\n\t\tix.gui.bars = vgui.Create(\"ixInfoBarManager\")\n\tend\nend\n\nfunction GM:DrawDeathNotice()\n\treturn false\nend\n\nfunction GM:HUDAmmoPickedUp()\n\treturn false\nend\n\nfunction GM:HUDDrawPickupHistory()\n\treturn false\nend\n\nfunction GM:HUDDrawTargetID()\n\treturn false\nend\n\nfunction GM:BuildBusinessMenu()\n\tif (!ix.config.Get(\"allowBusiness\", true)) then\n\t\treturn false\n\tend\nend\n\ngameevent.Listen(\"player_spawn\")\nhook.Add(\"player_spawn\", \"ixPlayerSpawn\", function(data)\n\tlocal client = Player(data.userid)\n\n\tif (IsValid(client)) then\n\t\t-- GetBoneName returns __INVALIDBONE__ for everything the first time you use it, so we'll force an update to make them valid\n\t\tclient:SetupBones()\n\t\tclient:SetIK(false)\n\n\t\tif (client == LocalPlayer() and (IsValid(ix.gui.deathScreen) and !ix.gui.deathScreen:IsClosing())) then\n\t\t\tix.gui.deathScreen:Close()\n\t\tend\n\tend\nend)\n"
  },
  {
    "path": "gamemode/core/hooks/sh_hooks.lua",
    "content": "\nfunction GM:PlayerNoClip(client)\n\treturn client:IsAdmin()\nend\n\n-- luacheck: globals HOLDTYPE_TRANSLATOR\nHOLDTYPE_TRANSLATOR = {}\nHOLDTYPE_TRANSLATOR[\"\"] = \"normal\"\nHOLDTYPE_TRANSLATOR[\"physgun\"] = \"smg\"\nHOLDTYPE_TRANSLATOR[\"ar2\"] = \"smg\"\nHOLDTYPE_TRANSLATOR[\"crossbow\"] = \"shotgun\"\nHOLDTYPE_TRANSLATOR[\"rpg\"] = \"shotgun\"\nHOLDTYPE_TRANSLATOR[\"slam\"] = \"normal\"\nHOLDTYPE_TRANSLATOR[\"grenade\"] = \"grenade\"\nHOLDTYPE_TRANSLATOR[\"fist\"] = \"normal\"\nHOLDTYPE_TRANSLATOR[\"melee2\"] = \"melee\"\nHOLDTYPE_TRANSLATOR[\"passive\"] = \"normal\"\nHOLDTYPE_TRANSLATOR[\"knife\"] = \"melee\"\nHOLDTYPE_TRANSLATOR[\"duel\"] = \"pistol\"\nHOLDTYPE_TRANSLATOR[\"camera\"] = \"smg\"\nHOLDTYPE_TRANSLATOR[\"magic\"] = \"normal\"\nHOLDTYPE_TRANSLATOR[\"revolver\"] = \"pistol\"\n\n-- luacheck: globals  PLAYER_HOLDTYPE_TRANSLATOR\nPLAYER_HOLDTYPE_TRANSLATOR = {}\nPLAYER_HOLDTYPE_TRANSLATOR[\"\"] = \"normal\"\nPLAYER_HOLDTYPE_TRANSLATOR[\"fist\"] = \"normal\"\nPLAYER_HOLDTYPE_TRANSLATOR[\"pistol\"] = \"normal\"\nPLAYER_HOLDTYPE_TRANSLATOR[\"grenade\"] = \"normal\"\nPLAYER_HOLDTYPE_TRANSLATOR[\"melee\"] = \"normal\"\nPLAYER_HOLDTYPE_TRANSLATOR[\"slam\"] = \"normal\"\nPLAYER_HOLDTYPE_TRANSLATOR[\"melee2\"] = \"normal\"\nPLAYER_HOLDTYPE_TRANSLATOR[\"passive\"] = \"normal\"\nPLAYER_HOLDTYPE_TRANSLATOR[\"knife\"] = \"normal\"\nPLAYER_HOLDTYPE_TRANSLATOR[\"duel\"] = \"normal\"\nPLAYER_HOLDTYPE_TRANSLATOR[\"bugbait\"] = \"normal\"\n\nlocal PLAYER_HOLDTYPE_TRANSLATOR = PLAYER_HOLDTYPE_TRANSLATOR\nlocal HOLDTYPE_TRANSLATOR = HOLDTYPE_TRANSLATOR\nlocal animationFixOffset = Vector(16.5438, -0.1642, -20.5493)\n\nfunction GM:TranslateActivity(client, act)\n\tlocal clientInfo = client:GetTable()\n\tlocal modelClass = clientInfo.ixAnimModelClass or \"player\"\n\tlocal bRaised = client:IsWepRaised()\n\n\tif (modelClass == \"player\") then\n\t\tlocal weapon = client:GetActiveWeapon()\n\t\tlocal bAlwaysRaised = ix.config.Get(\"weaponAlwaysRaised\")\n\t\tweapon = IsValid(weapon) and weapon or nil\n\n\t\tif (!bAlwaysRaised and weapon and !bRaised and client:OnGround()) then\n\t\t\tlocal model = string.lower(client:GetModel())\n\n\t\t\tif (string.find(model, \"zombie\")) then\n\t\t\t\tlocal tree = ix.anim.zombie\n\n\t\t\t\tif (string.find(model, \"fast\")) then\n\t\t\t\t\ttree = ix.anim.fastZombie\n\t\t\t\tend\n\n\t\t\t\tif (tree[act]) then\n\t\t\t\t\treturn tree[act]\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tlocal holdType = weapon and (weapon.HoldType or weapon:GetHoldType()) or \"normal\"\n\n\t\t\tif (!bAlwaysRaised and weapon and !bRaised and client:OnGround()) then\n\t\t\t\tholdType = PLAYER_HOLDTYPE_TRANSLATOR[holdType] or \"passive\"\n\t\t\tend\n\n\t\t\tlocal tree = ix.anim.player[holdType]\n\n\t\t\tif (tree and tree[act]) then\n\t\t\t\tif (isstring(tree[act])) then\n\t\t\t\t\tclientInfo.CalcSeqOverride = client:LookupSequence(tree[act])\n\n\t\t\t\t\treturn\n\t\t\t\telse\n\t\t\t\t\treturn tree[act]\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\n\t\treturn self.BaseClass:TranslateActivity(client, act)\n\tend\n\n\tif (clientInfo.ixAnimTable) then\n\t\tlocal glide = clientInfo.ixAnimGlide\n\n\t\tif (client:InVehicle()) then\n\t\t\tact = clientInfo.ixAnimTable[1]\n\n\t\t\tlocal fixVector = clientInfo.ixAnimTable[2]\n\n\t\t\tif (isvector(fixVector)) then\n\t\t\t\tclient:SetLocalPos(animationFixOffset)\n\t\t\tend\n\n\t\t\tif (isstring(act)) then\n\t\t\t\tclientInfo.CalcSeqOverride = client:LookupSequence(act)\n\t\t\telse\n\t\t\t\treturn act\n\t\t\tend\n\t\telseif (client:OnGround()) then\n\t\t\tif (clientInfo.ixAnimTable[act]) then\n\t\t\t\tlocal act2 = clientInfo.ixAnimTable[act][bRaised and 2 or 1]\n\n\t\t\t\tif (isstring(act2)) then\n\t\t\t\t\tclientInfo.CalcSeqOverride = client:LookupSequence(act2)\n\t\t\t\telse\n\t\t\t\t\treturn act2\n\t\t\t\tend\n\t\t\tend\n\t\telseif (glide) then\n\t\t\tif (isstring(glide)) then\n\t\t\t\tclientInfo.CalcSeqOverride = client:LookupSequence(glide)\n\t\t\telse\n\t\t\t\treturn clientInfo.ixAnimGlide\n\t\t\tend\n\t\tend\n\tend\nend\n\nfunction GM:CanPlayerUseBusiness(client, uniqueID)\n\tif (!ix.config.Get(\"allowBusiness\", true)) then\n\t\treturn false\n\tend\n\n\tlocal itemTable = ix.item.list[uniqueID]\n\n\tif (!client:GetCharacter()) then\n\t\treturn false\n\tend\n\n\tif (itemTable.noBusiness) then\n\t\treturn false\n\tend\n\n\tif (itemTable.factions) then\n\t\tlocal allowed = false\n\n\t\tif (istable(itemTable.factions)) then\n\t\t\tfor _, v in pairs(itemTable.factions) do\n\t\t\t\tif (client:Team() == v) then\n\t\t\t\t\tallowed = true\n\n\t\t\t\t\tbreak\n\t\t\t\tend\n\t\t\tend\n\t\telseif (client:Team() != itemTable.factions) then\n\t\t\tallowed = false\n\t\tend\n\n\t\tif (!allowed) then\n\t\t\treturn false\n\t\tend\n\tend\n\n\tif (itemTable.classes) then\n\t\tlocal allowed = false\n\n\t\tif (istable(itemTable.classes)) then\n\t\t\tfor _, v in pairs(itemTable.classes) do\n\t\t\t\tif (client:GetCharacter():GetClass() == v) then\n\t\t\t\t\tallowed = true\n\n\t\t\t\t\tbreak\n\t\t\t\tend\n\t\t\tend\n\t\telseif (client:GetCharacter():GetClass() == itemTable.classes) then\n\t\t\tallowed = true\n\t\tend\n\n\t\tif (!allowed) then\n\t\t\treturn false\n\t\tend\n\tend\n\n\tif (itemTable.flag) then\n\t\tif (!client:GetCharacter():HasFlags(itemTable.flag)) then\n\t\t\treturn false\n\t\tend\n\tend\n\n\treturn true\nend\n\nfunction GM:DoAnimationEvent(client, event, data)\n\tlocal class = client.ixAnimModelClass\n\n\tif (class == \"player\") then\n\t\treturn self.BaseClass:DoAnimationEvent(client, event, data)\n\telse\n\t\tlocal weapon = client:GetActiveWeapon()\n\n\t\tif (IsValid(weapon)) then\n\t\t\tlocal animation = client.ixAnimTable\n\n\t\t\tif (event == PLAYERANIMEVENT_ATTACK_PRIMARY) then\n\t\t\t\tclient:AnimRestartGesture(GESTURE_SLOT_ATTACK_AND_RELOAD, animation.attack or ACT_GESTURE_RANGE_ATTACK_SMG1, true)\n\n\t\t\t\treturn ACT_VM_PRIMARYATTACK\n\t\t\telseif (event == PLAYERANIMEVENT_ATTACK_SECONDARY) then\n\t\t\t\tclient:AnimRestartGesture(GESTURE_SLOT_ATTACK_AND_RELOAD, animation.attack or ACT_GESTURE_RANGE_ATTACK_SMG1, true)\n\n\t\t\t\treturn ACT_VM_SECONDARYATTACK\n\t\t\telseif (event == PLAYERANIMEVENT_RELOAD) then\n\t\t\t\tclient:AnimRestartGesture(GESTURE_SLOT_ATTACK_AND_RELOAD, animation.reload or ACT_GESTURE_RELOAD_SMG1, true)\n\n\t\t\t\treturn ACT_INVALID\n\t\t\telseif (event == PLAYERANIMEVENT_JUMP) then\n\t\t\t\tclient:AnimRestartMainSequence()\n\n\t\t\t\treturn ACT_INVALID\n\t\t\telseif (event == PLAYERANIMEVENT_CANCEL_RELOAD) then\n\t\t\t\tclient:AnimResetGestureSlot(GESTURE_SLOT_ATTACK_AND_RELOAD)\n\n\t\t\t\treturn ACT_INVALID\n\t\t\tend\n\t\tend\n\tend\n\n\treturn ACT_INVALID\nend\n\nfunction GM:EntityEmitSound(data)\n\tif (data.Entity.ixIsMuted) then\n\t\treturn false\n\tend\nend\n\nfunction GM:EntityRemoved(entity)\n\tif (SERVER) then\n\t\tentity:ClearNetVars()\n\telseif (entity:IsWeapon()) then\n\t\tlocal owner = entity:GetOwner()\n\n\t\t-- GetActiveWeapon is the player's new weapon at this point so we'll assume\n\t\t-- that the player switched away from this weapon\n\t\tif (IsValid(owner) and owner:IsPlayer()) then\n\t\t\thook.Run(\"PlayerWeaponChanged\", owner, owner:GetActiveWeapon())\n\t\tend\n\tend\nend\n\nlocal function UpdatePlayerHoldType(client, weapon)\n\tweapon = weapon or client:GetActiveWeapon()\n\tlocal holdType = \"normal\"\n\n\tif (IsValid(weapon)) then\n\t\tholdType = weapon.HoldType or weapon:GetHoldType()\n\t\tholdType = HOLDTYPE_TRANSLATOR[holdType] or holdType\n\tend\n\n\tclient.ixAnimHoldType = holdType\nend\n\nlocal function UpdateAnimationTable(client, vehicle)\n\tlocal baseTable = ix.anim[client.ixAnimModelClass] or {}\n\n\tif (IsValid(client) and IsValid(vehicle)) then\n\t\tlocal vehicleClass = vehicle:IsChair() and \"chair\" or vehicle:GetClass()\n\n\t\tif (baseTable.vehicle and baseTable.vehicle[vehicleClass]) then\n\t\t\tclient.ixAnimTable = baseTable.vehicle[vehicleClass]\n\t\telse\n\t\t\tclient.ixAnimTable = baseTable.normal[ACT_MP_CROUCH_IDLE]\n\t\tend\n\telse\n\t\tclient.ixAnimTable = baseTable[client.ixAnimHoldType]\n\tend\n\n\tclient.ixAnimGlide = baseTable[\"glide\"]\nend\n\nfunction GM:PlayerWeaponChanged(client, weapon)\n\tUpdatePlayerHoldType(client, weapon)\n\tUpdateAnimationTable(client)\n\n\tif (CLIENT) then\n\t\treturn\n\tend\n\n\t-- update weapon raise state\n\tif (weapon.IsAlwaysRaised or ALWAYS_RAISED[weapon:GetClass()]) then\n\t\tclient:SetWepRaised(true, weapon)\n\t\treturn\n\telseif (weapon.IsAlwaysLowered or weapon.NeverRaised) then\n\t\tclient:SetWepRaised(false, weapon)\n\t\treturn\n\tend\n\n\t-- If the player has been forced to have their weapon lowered.\n\tif (client:IsRestricted()) then\n\t\tclient:SetWepRaised(false, weapon)\n\t\treturn\n\tend\n\n\t-- Let the config decide before actual results.\n\tif (ix.config.Get(\"weaponAlwaysRaised\")) then\n\t\tclient:SetWepRaised(true, weapon)\n\t\treturn\n\tend\n\n\tclient:SetWepRaised(false, weapon)\nend\n\nfunction GM:PlayerSwitchWeapon(client, oldWeapon, weapon)\n\tif (!IsFirstTimePredicted()) then\n\t\treturn\n\tend\n\n\t-- the player switched weapon themself (i.e not through SelectWeapon), so we have to network it here\n\tif (SERVER) then\n\t\tnet.Start(\"PlayerSelectWeapon\")\n\t\t\tnet.WriteEntity(client)\n\t\t\tnet.WriteString(weapon:GetClass())\n\t\tnet.Broadcast()\n\tend\n\n\thook.Run(\"PlayerWeaponChanged\", client, weapon)\nend\n\nfunction GM:PlayerModelChanged(client, model)\n\tclient.ixAnimModelClass = ix.anim.GetModelClass(model)\n\n\tUpdateAnimationTable(client)\nend\n\ndo\n\tlocal vectorAngle = FindMetaTable(\"Vector\").Angle\n\tlocal normalizeAngle = math.NormalizeAngle\n\n\tfunction GM:CalcMainActivity(client, velocity)\n\t\tlocal clientInfo = client:GetTable()\n\t\tlocal forcedSequence = client:GetNetVar(\"forcedSequence\")\n\n\t\tif (forcedSequence) then\n\t\t\tif (client:GetSequence() != forcedSequence) then\n\t\t\t\tclient:SetCycle(0)\n\t\t\tend\n\n\t\t\treturn -1, forcedSequence\n\t\tend\n\n\t\tclient:SetPoseParameter(\"move_yaw\", normalizeAngle(vectorAngle(velocity)[2] - client:EyeAngles()[2]))\n\n\t\tlocal sequenceOverride = clientInfo.CalcSeqOverride\n\t\tclientInfo.CalcSeqOverride = -1\n\t\tclientInfo.CalcIdeal = ACT_MP_STAND_IDLE\n\n\t\t-- we could call the baseclass function, but it's faster to do it this way\n\t\tlocal BaseClass = self.BaseClass\n\n\t\tif (BaseClass:HandlePlayerNoClipping(client, velocity) or\n\t\t\tBaseClass:HandlePlayerDriving(client) or\n\t\t\tBaseClass:HandlePlayerVaulting(client, velocity) or\n\t\t\tBaseClass:HandlePlayerJumping(client, velocity) or\n\t\t\tBaseClass:HandlePlayerSwimming(client, velocity) or\n\t\t\tBaseClass:HandlePlayerDucking(client, velocity)) then -- luacheck: ignore 542\n\t\telse\n\t\t\tlocal length = velocity:Length2DSqr()\n\n\t\t\tif (length > 22500) then\n\t\t\t\tclientInfo.CalcIdeal = ACT_MP_RUN\n\t\t\telseif (length > 0.25) then\n\t\t\t\tclientInfo.CalcIdeal = ACT_MP_WALK\n\t\t\tend\n\t\tend\n\n\t\tclientInfo.m_bWasOnGround = client:OnGround()\n\t\tclientInfo.m_bWasNoclipping = (client:GetMoveType() == MOVETYPE_NOCLIP and !client:InVehicle())\n\n\t\treturn clientInfo.CalcIdeal, sequenceOverride or clientInfo.CalcSeqOverride or -1\n\tend\nend\n\ndo\n\tlocal KEY_BLACKLIST = bit.bor( IN_ATTACK, IN_ATTACK2 )\n\n\tfunction GM:StartCommand(client, command)\n\t\tif (!client:CanShootWeapon()) then\n\t\t\tcommand:RemoveKey(KEY_BLACKLIST)\n\t\tend\n\tend\nend\n\nfunction GM:CharacterVarChanged(char, varName, oldVar, newVar)\n\tif (ix.char.varHooks[varName]) then\n\t\tfor _, v in pairs(ix.char.varHooks[varName]) do\n\t\t\tv(char, oldVar, newVar)\n\t\tend\n\tend\nend\n\nfunction GM:CanPlayerThrowPunch(client)\n\tif (!client:IsWepRaised()) then\n\t\treturn false\n\tend\n\n\treturn true\nend\n\nfunction GM:OnCharacterCreated(client, character)\n\tlocal faction = ix.faction.Get(character:GetFaction())\n\n\tif (faction and faction.OnCharacterCreated) then\n\t\tfaction:OnCharacterCreated(client, character)\n\tend\nend\n\nfunction GM:GetDefaultCharacterName(client, faction)\n\tlocal info = ix.faction.indices[faction]\n\n\tif (info and info.GetDefaultName) then\n\t\treturn info:GetDefaultName(client)\n\tend\nend\n\nfunction GM:CanPlayerUseCharacter(client, character)\n\tlocal banned = character:GetData(\"banned\")\n\n\tif (banned) then\n\t\tif (!isnumber(banned)) then\n            return false, \"@charBanned\"\n        else\n            if (banned > os.time()) then\n                return false, \"@charBannedTemp\"\n            end\n        end\n\tend\n\n\tlocal bHasWhitelist = client:HasWhitelist(character:GetFaction())\n\n\tif (!bHasWhitelist) then\n\t\treturn false, \"@noWhitelist\"\n\tend\nend\n\nfunction GM:CanProperty(client, property, entity)\n\tif (client:IsAdmin()) then\n\t\treturn true\n\tend\n\n\tif (CLIENT and (property == \"remover\" or property == \"collision\")) then\n\t\treturn true\n\tend\n\n\treturn false\nend\n\nfunction GM:PhysgunPickup(client, entity)\n\tlocal bPickup = self.BaseClass:PhysgunPickup(client, entity)\n\n\tif (!bPickup and entity:IsPlayer() and (client:IsSuperAdmin() or client:IsAdmin() and !entity:IsSuperAdmin())) then\n\t\tbPickup = true\n\tend\n\n\tif (bPickup) then\n\t\tif (entity:IsPlayer()) then\n\t\t\tentity:SetMoveType(MOVETYPE_NONE)\n\t\telseif (!entity.ixCollisionGroup) then\n\t\t\tentity.ixCollisionGroup = entity:GetCollisionGroup()\n\t\t\tentity:SetCollisionGroup(COLLISION_GROUP_WEAPON)\n\t\tend\n\tend\n\n\treturn bPickup\nend\n\nfunction GM:PhysgunDrop(client, entity)\n\tif (entity:IsPlayer()) then\n\t\tentity:SetMoveType(MOVETYPE_WALK)\n\telseif (entity.ixCollisionGroup) then\n\t\tentity:SetCollisionGroup(entity.ixCollisionGroup)\n\t\tentity.ixCollisionGroup = nil\n\tend\nend\n\ndo\n\tlocal TOOL_DANGEROUS = {}\n\tTOOL_DANGEROUS[\"dynamite\"] = true\n\tTOOL_DANGEROUS[\"duplicator\"] = true\n\n\tfunction GM:CanTool(client, trace, tool)\n\t\tif (client:IsAdmin()) then\n\t\t\treturn true\n\t\tend\n\n\t\tif (TOOL_DANGEROUS[tool]) then\n\t\t\treturn false\n\t\tend\n\n\t\treturn self.BaseClass:CanTool(client, trace, tool)\n\tend\nend\n\nfunction GM:Move(client, moveData)\n\tlocal char = client:GetCharacter()\n\n\tif (char) then\n\t\tif (client:GetNetVar(\"actEnterAngle\")) then\n\t\t\tmoveData:SetForwardSpeed(0)\n\t\t\tmoveData:SetSideSpeed(0)\n\t\t\tmoveData:SetVelocity(vector_origin)\n\t\tend\n\n\t\tif (client:GetMoveType() == MOVETYPE_WALK and moveData:KeyDown(IN_WALK)) then\n\t\t\tlocal mf, ms = 0, 0\n\t\t\tlocal speed = client:GetWalkSpeed()\n\t\t\tlocal ratio = ix.config.Get(\"walkRatio\")\n\n\t\t\tif (moveData:KeyDown(IN_FORWARD)) then\n\t\t\t\tmf = ratio\n\t\t\telseif (moveData:KeyDown(IN_BACK)) then\n\t\t\t\tmf = -ratio\n\t\t\tend\n\n\t\t\tif (moveData:KeyDown(IN_MOVELEFT)) then\n\t\t\t\tms = -ratio\n\t\t\telseif (moveData:KeyDown(IN_MOVERIGHT)) then\n\t\t\t\tms = ratio\n\t\t\tend\n\n\t\t\tmoveData:SetForwardSpeed(mf * speed)\n\t\t\tmoveData:SetSideSpeed(ms * speed)\n\t\tend\n\tend\nend\n\nfunction GM:CanTransferItem(itemObject, curInv, inventory)\n\tif (SERVER) then\n\t\tlocal client = itemObject.GetOwner and itemObject:GetOwner() or nil\n\n\t\tif (IsValid(client) and curInv.GetReceivers) then\n\t\t\tlocal bAuthorized = false\n\n\t\t\tfor _, v in ipairs(curInv:GetReceivers()) do\n\t\t\t\tif (client == v) then\n\t\t\t\t\tbAuthorized = true\n\t\t\t\t\tbreak\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tif (!bAuthorized) then\n\t\t\t\treturn false\n\t\t\tend\n\t\tend\n\tend\n\n\t-- we can transfer anything that isn't a bag\n\tif (!itemObject or !itemObject.isBag) then\n\t\treturn\n\tend\n\n\t-- don't allow bags to be put inside bags\n\tif (inventory.id != 0 and curInv.id != inventory.id) then\n\t\tif (inventory.vars and inventory.vars.isBag) then\n\t\t\tlocal owner = itemObject:GetOwner()\n\n\t\t\tif (IsValid(owner)) then\n\t\t\t\towner:NotifyLocalized(\"nestedBags\")\n\t\t\tend\n\n\t\t\treturn false\n\t\tend\n\telseif (inventory.id != 0 and curInv.id == inventory.id) then\n\t\t-- we are simply moving items around if we're transferring to the same inventory\n\t\treturn\n\tend\n\n\tinventory = ix.item.inventories[itemObject:GetData(\"id\")]\n\n\t-- don't allow transferring items that are in use\n\tif (inventory) then\n\t\tfor k, _ in inventory:Iter() do\n\t\t\tif (k:GetData(\"equip\") == true) then\n\t\t\t\tlocal owner = itemObject:GetOwner()\n\n\t\t\t\tif (owner and IsValid(owner)) then\n\t\t\t\t\towner:NotifyLocalized(\"equippedBag\")\n\t\t\t\tend\n\n\t\t\t\treturn false\n\t\t\tend\n\t\tend\n\tend\nend\n\nfunction GM:CanPlayerEquipItem(client, item)\n\treturn item.invID == client:GetCharacter():GetInventory():GetID()\nend\n\nfunction GM:CanPlayerUnequipItem(client, item)\n\treturn item.invID == client:GetCharacter():GetInventory():GetID()\nend\n\nfunction GM:OnItemTransferred(item, curInv, inventory)\n\tlocal bagInventory = item.GetInventory and item:GetInventory()\n\n\tif (!bagInventory) then\n\t\treturn\n\tend\n\n\t-- we need to retain the receiver if the owner changed while viewing as storage\n\tif (inventory.storageInfo and isfunction(curInv.GetOwner)) then\n\t\tbagInventory:AddReceiver(curInv:GetOwner())\n\tend\nend\n\nfunction GM:ShowHelp() end\n\nfunction GM:PreGamemodeLoaded()\n\thook.Remove(\"PostDrawEffects\", \"RenderWidgets\")\n\thook.Remove(\"PlayerTick\", \"TickWidgets\")\n\thook.Remove(\"RenderScene\", \"RenderStereoscopy\")\nend\n\nfunction GM:PostGamemodeLoaded()\n\tbaseclass.Set(\"ix_character\", ix.meta.character)\n\tbaseclass.Set(\"ix_inventory\", ix.meta.inventory)\n\tbaseclass.Set(\"ix_item\", ix.meta.item)\nend\n\nif (SERVER) then\n\tutil.AddNetworkString(\"PlayerVehicle\")\n\n\tfunction GM:PlayerEnteredVehicle(client, vehicle, role)\n\t\tUpdateAnimationTable(client)\n\n\t\tnet.Start(\"PlayerVehicle\")\n\t\t\tnet.WriteEntity(client)\n\t\t\tnet.WriteEntity(vehicle)\n\t\t\tnet.WriteBool(true)\n\t\tnet.Broadcast()\n\tend\n\n\tfunction GM:PlayerLeaveVehicle(client, vehicle)\n\t\tUpdateAnimationTable(client)\n\n\t\tnet.Start(\"PlayerVehicle\")\n\t\t\tnet.WriteEntity(client)\n\t\t\tnet.WriteEntity(vehicle)\n\t\t\tnet.WriteBool(false)\n\t\tnet.Broadcast()\n\tend\nelse\n\tnet.Receive(\"PlayerVehicle\", function(length)\n\t\tlocal client = net.ReadEntity()\n\t\tlocal vehicle = net.ReadEntity()\n\t\tlocal bEntered = net.ReadBool()\n\n\t\tUpdateAnimationTable(client, bEntered and vehicle or false)\n\tend)\nend\n"
  },
  {
    "path": "gamemode/core/hooks/sv_hooks.lua",
    "content": "\nutil.AddNetworkString(\"ixPlayerDeath\")\n\nfunction GM:PlayerInitialSpawn(client)\n\tclient.ixJoinTime = RealTime()\n\n\tif (client:IsBot()) then\n\t\tlocal botID = os.time() + client:EntIndex()\n\t\tlocal index = math.random(1, table.Count(ix.faction.indices))\n\t\tlocal faction = ix.faction.indices[index]\n\n        local models = faction:GetModels( client )\n\n        local model = models[ math.random( #models ) ]\n        if ( istable( model ) ) then model = model[ 1 ] end\n\n        if ( !isstring( model ) ) then model = \"models/gman.mdl\" end\n\n\t\tlocal character = ix.char.New({\n\t\t\tname = client:Name(),\n\t\t\tfaction = faction and faction.uniqueID or \"unknown\",\n\t\t\tmodel = model,\n\t\t}, botID, client, client:SteamID64())\n\t\tcharacter.isBot = true\n\n\t\tlocal inventory = ix.inventory.Create(ix.config.Get(\"inventoryWidth\"), ix.config.Get(\"inventoryHeight\"), botID)\n\t\tinventory:SetOwner(botID)\n\t\tinventory.noSave = true\n\n\t\tcharacter.vars.inv = {inventory}\n\n\t\tix.char.loaded[botID] = character\n\n\t\tcharacter:Setup()\n\t\tclient:Spawn()\n\n\t\tix.chat.Send(nil, \"connect\", client:SteamName())\n\n\t\treturn\n\tend\n\n\tix.config.Send(client)\n\tix.date.Send(client)\n\n\tclient:LoadData(function(data)\n\t\tif (!IsValid(client)) then return end\n\n\t\t-- Don't use the character cache if they've connected to another server using the same database\n\t\tlocal address = ix.util.GetAddress()\n\t\tlocal bNoCache = client:GetData(\"lastIP\", address) != address\n\t\tclient:SetData(\"lastIP\", address)\n\n\t\tnet.Start(\"ixDataSync\")\n\t\t\tnet.WriteTable(data or {})\n\t\t\tnet.WriteUInt(client.ixPlayTime or 0, 32)\n\t\tnet.Send(client)\n\n\t\tix.char.Restore(client, function(charList)\n\t\t\tif (!IsValid(client)) then return end\n\n\t\t\tMsgN(\"Loaded (\" .. table.concat(charList, \", \") .. \") for \" .. client:Name())\n\n\t\t\tfor _, v in ipairs(charList) do\n\t\t\t\tix.char.loaded[v]:Sync(client)\n\t\t\tend\n\n\t\t\tclient.ixCharList = charList\n\n\t\t\tnet.Start(\"ixCharacterMenu\")\n\t\t\tnet.WriteUInt(#charList, 6)\n\n\t\t\tfor _, v in ipairs(charList) do\n\t\t\t\tnet.WriteUInt(v, 32)\n\t\t\tend\n\n\t\t\tnet.Send(client)\n\n\t\t\tclient.ixLoaded = true\n\t\t\tclient:SetData(\"intro\", true)\n\n\t\t\tfor _, v in player.Iterator() do\n\t\t\t\tif (v:GetCharacter()) then\n\t\t\t\t\tv:GetCharacter():Sync(client)\n\t\t\t\tend\n\t\t\tend\n\t\tend, bNoCache)\n\n\t\tix.chat.Send(nil, \"connect\", client:SteamName())\n\tend)\n\n\tclient:SetNoDraw(true)\n\tclient:SetNotSolid(true)\n\tclient:Lock()\n\tclient:SyncVars()\n\n\ttimer.Simple(1, function()\n\t\tif (!IsValid(client)) then\n\t\t\treturn\n\t\tend\n\n\t\tclient:KillSilent()\n\t\tclient:StripAmmo()\n\tend)\nend\n\nfunction GM:PlayerUse(client, entity)\n\tif (client:IsRestricted() or (isfunction(entity.GetEntityMenu) and entity:GetClass() != \"ix_item\")) then\n\t\treturn false\n\tend\n\n\treturn true\nend\n\nfunction GM:KeyPress(client, key)\n\tif (key == IN_RELOAD) then\n\t\ttimer.Create(\"ixToggleRaise\"..client:SteamID(), ix.config.Get(\"weaponRaiseTime\"), 1, function()\n\t\t\tif (IsValid(client)) then\n\t\t\t\tclient:ToggleWepRaised()\n\t\t\tend\n\t\tend)\n\telseif (key == IN_USE) then\n\t\tlocal data = {}\n\t\t\tdata.start = client:GetShootPos()\n\t\t\tdata.endpos = data.start + client:GetAimVector() * 96\n\t\t\tdata.filter = client\n\t\tlocal entity = util.TraceLine(data).Entity\n\n\t\tif (IsValid(entity) and hook.Run(\"PlayerUse\", client, entity)) then\n\t\t\tif (entity:IsDoor()) then\n\t\t\t\tlocal result = hook.Run(\"CanPlayerUseDoor\", client, entity)\n\n\t\t\t\tif (result != false) then\n\t\t\t\t\thook.Run(\"PlayerUseDoor\", client, entity)\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend\nend\n\nfunction GM:KeyRelease(client, key)\n\tif (key == IN_RELOAD) then\n\t\ttimer.Remove(\"ixToggleRaise\" .. client:SteamID())\n\telseif (key == IN_USE) then\n\t\ttimer.Remove(\"ixCharacterInteraction\" .. client:SteamID())\n\tend\nend\n\nfunction GM:CanPlayerInteractEntity(client, entity, option, data)\n\treturn entity:GetPos():DistToSqr(client:GetPos()) <= 96 ^ 2\nend\n\nfunction GM:CanPlayerInteractItem(client, action, item, data)\n\tif (client:IsRestricted()) then\n\t\treturn false\n\tend\n\n\tif (IsValid(client.ixRagdoll)) then\n\t\tclient:NotifyLocalized(\"notNow\")\n\t\treturn false\n\tend\n\n\tif (action == \"drop\" and hook.Run(\"CanPlayerDropItem\", client, item) == false) then\n\t\treturn false\n\tend\n\n\tif (action == \"take\" and hook.Run(\"CanPlayerTakeItem\", client, item) == false) then\n\t\treturn false\n\tend\n\n\tif (action == \"combine\") then\n\t\tlocal other = data[1]\n\n\t\tif (hook.Run(\"CanPlayerCombineItem\", client, item, other) == false) then\n\t\t\treturn false\n\t\tend\n\n\t\tlocal combineItem = ix.item.instances[other]\n\n\t\tif (combineItem and combineItem.invID != 0) then\n\t\t\tlocal combineInv = ix.item.inventories[combineItem.invID]\n\n\t\t\tif (!combineInv:OnCheckAccess(client)) then\n\t\t\t\treturn false\n\t\t\tend\n\t\telse\n\t\t\treturn false\n\t\tend\n\tend\n\n\tif (isentity(item) and item.ixSteamID and item.ixCharID\n\tand item.ixSteamID == client:SteamID() and item.ixCharID != client:GetCharacter():GetID()\n\tand !item:GetItemTable().bAllowMultiCharacterInteraction) then\n\t\tclient:NotifyLocalized(\"itemOwned\")\n\t\treturn false\n\tend\n\n\treturn client:Alive()\nend\n\nfunction GM:CanPlayerDropItem(client, item)\n\nend\n\nfunction GM:CanPlayerTakeItem(client, item)\n\nend\n\nfunction GM:CanPlayerCombineItem(client, item, other)\n\nend\n\nfunction GM:PlayerShouldTakeDamage(client, attacker)\n\treturn client:GetCharacter() != nil\nend\n\nfunction GM:GetFallDamage(client, speed)\n\treturn (speed - 580) * (100 / 444)\nend\n\nfunction GM:EntityTakeDamage(entity, dmgInfo)\n\tlocal inflictor = dmgInfo:GetInflictor()\n\n\tif (IsValid(inflictor) and inflictor:GetClass() == \"ix_item\") then\n\t\tdmgInfo:SetDamage(0)\n\t\treturn\n\tend\n\n\tif (IsValid(entity.ixPlayer)) then\n\t\tif (IsValid(entity.ixHeldOwner)) then\n\t\t\tdmgInfo:SetDamage(0)\n\t\t\treturn\n\t\tend\n\n\t\tif (dmgInfo:IsDamageType(DMG_CRUSH)) then\n\t\t\tif ((entity.ixFallGrace or 0) < CurTime()) then\n\t\t\t\tif (dmgInfo:GetDamage() <= 10) then\n\t\t\t\t\tdmgInfo:SetDamage(0)\n\t\t\t\tend\n\n\t\t\t\tentity.ixFallGrace = CurTime() + 0.5\n\t\t\telse\n\t\t\t\treturn\n\t\t\tend\n\t\tend\n\n\t\tentity.ixPlayer:TakeDamageInfo(dmgInfo)\n\tend\nend\n\nfunction GM:PrePlayerLoadedCharacter(client, character, lastChar)\n\t-- Reset all bodygroups\n\tclient:ResetBodygroups()\n\n\t-- Remove all skins\n\tclient:SetSkin(0)\nend\n\nfunction GM:PlayerLoadedCharacter(client, character, lastChar)\n\tlocal query = mysql:Update(\"ix_characters\")\n\t\tquery:Where(\"id\", character:GetID())\n\t\tquery:Update(\"last_join_time\", math.floor(os.time()))\n\tquery:Execute()\n\n\tif (lastChar) then\n\t\tlocal charEnts = lastChar:GetVar(\"charEnts\") or {}\n\n\t\tfor _, v in ipairs(charEnts) do\n\t\t\tif (v and IsValid(v)) then\n\t\t\t\tv:Remove()\n\t\t\tend\n\t\tend\n\n\t\tlastChar:SetVar(\"charEnts\", nil)\n\tend\n\n\tif (character) then\n\t\tfor _, v in pairs(ix.class.list) do\n\t\t\tif (v.faction == client:Team() and v.isDefault) then\n\t\t\t\tcharacter:SetClass(v.index)\n\n\t\t\t\tbreak\n\t\t\tend\n\t\tend\n\tend\n\n\tif (IsValid(client.ixRagdoll)) then\n\t\tclient.ixRagdoll.ixNoReset = true\n\t\tclient.ixRagdoll.ixIgnoreDelete = true\n\t\tclient.ixRagdoll:Remove()\n\tend\n\n\tlocal faction = ix.faction.indices[character:GetFaction()]\n\tlocal uniqueID = \"ixSalary\" .. client:SteamID64()\n\n\tif (faction and faction.pay and faction.pay > 0) then\n\t\ttimer.Create(uniqueID, faction.payTime or 300, 0, function()\n\t\t\tif (IsValid(client)) then\n\t\t\t\tif (hook.Run(\"CanPlayerEarnSalary\", client, faction) != false) then\n\t\t\t\t\tlocal pay = hook.Run(\"GetSalaryAmount\", client, faction) or faction.pay\n\n\t\t\t\t\tcharacter:GiveMoney(pay)\n\t\t\t\t\tclient:NotifyLocalized(\"salary\", ix.currency.Get(pay))\n\t\t\t\tend\n\t\t\telse\n\t\t\t\ttimer.Remove(uniqueID)\n\t\t\tend\n\t\tend)\n\telseif (timer.Exists(uniqueID)) then\n\t\ttimer.Remove(uniqueID)\n\tend\n\n\thook.Run(\"PlayerLoadout\", client)\nend\n\nfunction GM:CharacterLoaded(character)\n\tlocal client = character:GetPlayer()\n\n\tif (IsValid(client)) then\n\t\tlocal uniqueID = \"ixSaveChar\"..client:SteamID()\n\n\t\ttimer.Create(uniqueID, ix.config.Get(\"saveInterval\"), 0, function()\n\t\t\tif (IsValid(client) and client:GetCharacter()) then\n\t\t\t\tclient:GetCharacter():Save()\n\t\t\telse\n\t\t\t\ttimer.Remove(uniqueID)\n\t\t\tend\n\t\tend)\n\tend\nend\n\nfunction GM:PlayerSay(client, text)\n\tlocal chatType, message, anonymous = ix.chat.Parse(client, text, true)\n\n\tif (chatType == \"ic\") then\n\t\tif (ix.command.Parse(client, message)) then\n\t\t\treturn \"\"\n\t\tend\n\tend\n\n\ttext = ix.chat.Send(client, chatType, message, anonymous)\n\n\tif (isstring(text) and chatType != \"ic\") then\n\t\tix.log.Add(client, \"chat\", chatType and chatType:utf8upper() or \"??\", text)\n\tend\n\n\thook.Run(\"PostPlayerSay\", client, chatType, message, anonymous)\n\treturn \"\"\nend\n\nfunction GM:CanAutoFormatMessage(client, chatType, message)\n\treturn chatType == \"ic\" or chatType == \"w\" or chatType == \"y\"\nend\n\nfunction GM:PlayerSpawn(client)\n\tclient:SetNoDraw(false)\n\tclient:UnLock()\n\tclient:SetNotSolid(false)\n\tclient:SetMoveType(MOVETYPE_WALK)\n\tclient:SetRagdolled(false)\n\tclient:SetAction()\n\tclient:SetDSP(1)\n\n\thook.Run(\"PlayerLoadout\", client)\nend\n\n-- Shortcuts for (super)admin only things.\nlocal function IsAdmin(_, client)\n\treturn client:IsAdmin()\nend\n\n-- Set the gamemode hooks to the appropriate shortcuts.\nGM.PlayerGiveSWEP = IsAdmin\nGM.PlayerSpawnEffect = IsAdmin\nGM.PlayerSpawnSENT = IsAdmin\n\nfunction GM:PlayerSpawnNPC(client, npcType, weapon)\n\treturn client:IsAdmin() or client:GetCharacter():HasFlags(\"n\")\nend\n\nfunction GM:PlayerSpawnSWEP(client, weapon, info)\n\treturn client:IsAdmin()\nend\n\nfunction GM:PlayerSpawnProp(client)\n\tif (client:GetCharacter() and client:GetCharacter():HasFlags(\"e\")) then\n\t\treturn true\n\tend\n\n\treturn false\nend\n\nfunction GM:PlayerSpawnRagdoll(client)\n\tif (client:GetCharacter() and client:GetCharacter():HasFlags(\"r\")) then\n\t\treturn true\n\tend\n\n\treturn false\nend\n\nfunction GM:PlayerSpawnVehicle(client, model, name, data)\n\tif (client:GetCharacter()) then\n\t\tif (data.Category == \"Chairs\") then\n\t\t\treturn client:GetCharacter():HasFlags(\"c\")\n\t\telse\n\t\t\treturn client:GetCharacter():HasFlags(\"C\")\n\t\tend\n\tend\n\n\treturn false\nend\n\nfunction GM:PlayerSpawnedEffect(client, model, entity)\n\tentity:SetNetVar(\"owner\", client:GetCharacter():GetID())\nend\n\nfunction GM:PlayerSpawnedNPC(client, entity)\n\tentity:SetNetVar(\"owner\", client:GetCharacter():GetID())\nend\n\nfunction GM:PlayerSpawnedProp(client, model, entity)\n\tentity:SetNetVar(\"owner\", client:GetCharacter():GetID())\nend\n\nfunction GM:PlayerSpawnedRagdoll(client, model, entity)\n\tentity:SetNetVar(\"owner\", client:GetCharacter():GetID())\nend\n\nfunction GM:PlayerSpawnedSENT(client, entity)\n\tentity:SetNetVar(\"owner\", client:GetCharacter():GetID())\nend\n\nfunction GM:PlayerSpawnedSWEP(client, entity)\n\tentity:SetNetVar(\"owner\", client:GetCharacter():GetID())\nend\n\nfunction GM:PlayerSpawnedVehicle(client, entity)\n\tentity:SetNetVar(\"owner\", client:GetCharacter():GetID())\nend\n\nix.allowedHoldableClasses = {\n\t[\"ix_item\"] = true,\n\t[\"ix_money\"] = true,\n\t[\"ix_shipment\"] = true,\n\t[\"prop_physics\"] = true,\n\t[\"prop_physics_override\"] = true,\n\t[\"prop_physics_multiplayer\"] = true,\n\t[\"prop_ragdoll\"] = true\n}\n\nfunction GM:CanPlayerHoldObject(client, entity)\n\tif (ix.allowedHoldableClasses[entity:GetClass()]) then\n\t\treturn true\n\tend\nend\n\nlocal voiceDistance = 360000\nlocal function CalcPlayerCanHearPlayersVoice(listener)\n\tif (!IsValid(listener)) then\n\t\treturn\n\tend\n\n\tlistener.ixVoiceHear = listener.ixVoiceHear or {}\n\n\tlocal eyePos = listener:EyePos()\n\tfor _, speaker in player.Iterator() do\n\t\tlocal speakerEyePos = speaker:EyePos()\n\t\tlistener.ixVoiceHear[speaker] = eyePos:DistToSqr(speakerEyePos) < voiceDistance\n\tend\nend\n\nfunction GM:InitializedConfig()\n\tix.date.Initialize()\n\n\tvoiceDistance = ix.config.Get(\"voiceDistance\")\n\tvoiceDistance = voiceDistance * voiceDistance\nend\n\nfunction GM:VoiceToggled(bAllowVoice)\n\tfor _, v in player.Iterator() do\n\t\tlocal uniqueID = v:SteamID64() .. \"ixCanHearPlayersVoice\"\n\n\t\tif (bAllowVoice) then\n\t\t\ttimer.Create(uniqueID, 0.5, 0, function()\n\t\t\t\tCalcPlayerCanHearPlayersVoice(v)\n\t\t\tend)\n\t\telse\n\t\t\ttimer.Remove(uniqueID)\n\n\t\t\tv.ixVoiceHear = nil\n\t\tend\n\tend\nend\n\nfunction GM:VoiceDistanceChanged(distance)\n\tvoiceDistance = distance * distance\nend\n\n-- Called when weapons should be given to a player.\nfunction GM:PlayerLoadout(client)\n\tif (client.ixSkipLoadout) then\n\t\tclient.ixSkipLoadout = nil\n\n\t\treturn\n\tend\n\n\tclient:SetWeaponColor(Vector(client:GetInfo(\"cl_weaponcolor\")))\n\tclient:StripWeapons()\n\tclient:StripAmmo()\n\tclient:SetLocalVar(\"blur\", nil)\n\n\tlocal character = client:GetCharacter()\n\n\t-- Check if they have loaded a character.\n\tif (character) then\n\t\tclient:SetupHands()\n\t\t-- Set their player model to the character's model.\n\t\tclient:SetModel(character:GetModel())\n\t\tclient:Give(\"ix_hands\")\n\t\tclient:SetWalkSpeed(ix.config.Get(\"walkSpeed\"))\n\t\tclient:SetRunSpeed(ix.config.Get(\"runSpeed\"))\n\t\tclient:SetHealth(character:GetData(\"health\", client:GetMaxHealth()))\n\n\t\tlocal faction = ix.faction.indices[client:Team()]\n\n\t\tif (faction) then\n\t\t\t-- If their faction wants to do something when the player spawns, let it.\n\t\t\tif (faction.OnSpawn) then\n\t\t\t\tfaction:OnSpawn(client)\n\t\t\tend\n\n\t\t\t-- @todo add docs for player:Give() failing if player already has weapon - which means if a player is given a weapon\n\t\t\t-- here due to the faction weapons table, the weapon's :Give call in the weapon base will fail since the player\n\t\t\t-- will already have it by then. This will cause issues for weapons that have pac data since the parts are applied\n\t\t\t-- only if the weapon returned by :Give() is valid\n\n\t\t\t-- If the faction has default weapons, give them to the player.\n\t\t\tif (faction.weapons) then\n\t\t\t\tfor _, v in ipairs(faction.weapons) do\n\t\t\t\t\tclient:Give(v)\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\n\t\t-- Ditto, but for classes.\n\t\tlocal class = ix.class.list[client:GetCharacter():GetClass()]\n\n\t\tif (class) then\n\t\t\tif (class.OnSpawn) then\n\t\t\t\tclass:OnSpawn(client)\n\t\t\tend\n\n\t\t\tif (class.weapons) then\n\t\t\t\tfor _, v in ipairs(class.weapons) do\n\t\t\t\t\tclient:Give(v)\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\n\t\t-- Apply any flags as needed.\n\t\tix.flag.OnSpawn(client)\n\t\tix.attributes.Setup(client)\n\n\t\thook.Run(\"PostPlayerLoadout\", client)\n\n\t\tclient:SelectWeapon(\"ix_hands\")\n\telse\n\t\tclient:SetNoDraw(true)\n\t\tclient:Lock()\n\t\tclient:SetNotSolid(true)\n\tend\nend\n\nfunction GM:PostPlayerLoadout(client)\n\t-- Reload All Attrib Boosts\n\tlocal character = client:GetCharacter()\n\n\tif (character:GetInventory()) then\n\t\tfor k, _ in character:GetInventory():Iter() do\n\t\t\tk:Call(\"OnLoadout\", client)\n\n\t\t\tif (k:GetData(\"equip\") and k.attribBoosts) then\n\t\t\t\tfor attribKey, attribValue in pairs(k.attribBoosts) do\n\t\t\t\t\tcharacter:AddBoost(k.uniqueID, attribKey, attribValue)\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend\n\n\tif (ix.config.Get(\"allowVoice\")) then\n\t\ttimer.Create(client:SteamID64() .. \"ixCanHearPlayersVoice\", 0.5, 0, function()\n\t\t\tCalcPlayerCanHearPlayersVoice(client)\n\t\tend)\n\tend\nend\n\nlocal deathSounds = {\n\tSound(\"vo/npc/male01/pain07.wav\"),\n\tSound(\"vo/npc/male01/pain08.wav\"),\n\tSound(\"vo/npc/male01/pain09.wav\")\n}\n\nfunction GM:DoPlayerDeath(client, attacker, damageinfo)\n\tclient:AddDeaths(1)\n\n\tif (hook.Run(\"ShouldSpawnClientRagdoll\", client) != false) then\n\t\tclient:CreateRagdoll()\n\tend\n\n\tif (IsValid(attacker) and attacker:IsPlayer()) then\n\t\tif (client == attacker) then\n\t\t\tattacker:AddFrags(-1)\n\t\telse\n\t\t\tattacker:AddFrags(1)\n\t\tend\n\tend\n\n\tnet.Start(\"ixPlayerDeath\")\n\tnet.Send(client)\n\n\tclient:SetAction(\"@respawning\", ix.config.Get(\"spawnTime\", 5))\n\tclient:SetDSP(31)\nend\n\nfunction GM:PlayerDeath(client, inflictor, attacker)\n\tlocal character = client:GetCharacter()\n\n\tif (character) then\n\t\tif (IsValid(client.ixRagdoll)) then\n\t\t\tclient.ixRagdoll.ixIgnoreDelete = true\n\t\t\tclient:SetLocalVar(\"blur\", nil)\n\n\t\t\tif (hook.Run(\"ShouldRemoveRagdollOnDeath\", client) != false) then\n\t\t\t\tclient.ixRagdoll:Remove()\n\t\t\tend\n\t\tend\n\n\t\tclient:SetNetVar(\"deathStartTime\", CurTime())\n\t\tclient:SetNetVar(\"deathTime\", CurTime() + ix.config.Get(\"spawnTime\", 5))\n\n\t\tcharacter:SetData(\"health\", nil)\n\n\t\tlocal deathSound = hook.Run(\"GetPlayerDeathSound\", client)\n\n\t\tif (deathSound != false) then\n\t\t\tdeathSound = deathSound or deathSounds[math.random(1, #deathSounds)]\n\n\t\t\tif (client:IsFemale() and !deathSound:find(\"female\")) then\n\t\t\t\tdeathSound = deathSound:gsub(\"male\", \"female\")\n\t\t\tend\n\n\t\t\tclient:EmitSound(deathSound)\n\t\tend\n\n\t\tlocal weapon = attacker:IsPlayer() and attacker:GetActiveWeapon()\n\n\t\tix.log.Add(client, \"playerDeath\",\n\t\t\tattacker:GetName() ~= \"\" and attacker:GetName() or attacker:GetClass(), IsValid(weapon) and weapon:GetClass())\n\tend\nend\n\nlocal painSounds = {\n\tSound(\"vo/npc/male01/pain01.wav\"),\n\tSound(\"vo/npc/male01/pain02.wav\"),\n\tSound(\"vo/npc/male01/pain03.wav\"),\n\tSound(\"vo/npc/male01/pain04.wav\"),\n\tSound(\"vo/npc/male01/pain05.wav\"),\n\tSound(\"vo/npc/male01/pain06.wav\")\n}\n\nlocal drownSounds = {\n\tSound(\"player/pl_drown1.wav\"),\n\tSound(\"player/pl_drown2.wav\"),\n\tSound(\"player/pl_drown3.wav\"),\n}\n\nfunction GM:GetPlayerPainSound(client)\n\tif (client:WaterLevel() >= 3) then\n\t\treturn drownSounds[math.random(1, #drownSounds)]\n\tend\nend\n\nfunction GM:PlayerHurt(client, attacker, health, damage)\n\tif ((client.ixNextPain or 0) < CurTime() and health > 0) then\n\t\tlocal painSound = hook.Run(\"GetPlayerPainSound\", client) or painSounds[math.random(1, #painSounds)]\n\n\t\tif (client:IsFemale() and !painSound:find(\"female\")) then\n\t\t\tpainSound = painSound:gsub(\"male\", \"female\")\n\t\tend\n\n\t\tclient:EmitSound(painSound)\n\t\tclient.ixNextPain = CurTime() + 0.33\n\tend\n\n\tix.log.Add(client, \"playerHurt\", damage, attacker:GetName() ~= \"\" and attacker:GetName() or attacker:GetClass())\nend\n\nfunction GM:PlayerDeathThink(client)\n\tif (client:GetCharacter()) then\n\t\tlocal deathTime = client:GetNetVar(\"deathTime\")\n\n\t\tif (deathTime and deathTime <= CurTime()) then\n\t\t\tclient:Spawn()\n\t\tend\n\tend\n\n\treturn false\nend\n\nfunction GM:PlayerDisconnected(client)\n\tclient:SaveData()\n\n\tlocal character = client:GetCharacter()\n\n\tif (character) then\n\t\tlocal charEnts = character:GetVar(\"charEnts\") or {}\n\n\t\tfor _, v in ipairs(charEnts) do\n\t\t\tif (v and IsValid(v)) then\n\t\t\t\tv:Remove()\n\t\t\tend\n\t\tend\n\n\t\thook.Run(\"OnCharacterDisconnect\", client, character)\n\t\t\tcharacter:Save()\n\t\tix.chat.Send(nil, \"disconnect\", client:SteamName())\n\tend\n\n\tif (IsValid(client.ixRagdoll)) then\n\t\tclient.ixRagdoll:Remove()\n\tend\n\n\tclient:ClearNetVars()\n\n\tif (!client.ixVoiceHear) then\n\t\treturn\n\tend\n\n\tfor _, v in player.Iterator() do\n\t\tif (!v.ixVoiceHear) then\n\t\t\tcontinue\n\t\tend\n\n\t\tv.ixVoiceHear[client] = nil\n\tend\n\n\ttimer.Remove(client:SteamID64() .. \"ixCanHearPlayersVoice\")\nend\n\nfunction GM:InitPostEntity()\n\tlocal doors = ents.FindByClass(\"prop_door_rotating\")\n\n\tfor _, v in ipairs(doors) do\n\t\tlocal parent = v:GetOwner()\n\n\t\tif (IsValid(parent)) then\n\t\t\tv.ixPartner = parent\n\t\t\tparent.ixPartner = v\n\t\telse\n\t\t\tfor _, v2 in ipairs(doors) do\n\t\t\t\tif (v2:GetOwner() == v) then\n\t\t\t\t\tv2.ixPartner = v\n\t\t\t\t\tv.ixPartner = v2\n\n\t\t\t\t\tbreak\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend\n\n\ttimer.Simple(2, function()\n\t\tix.entityDataLoaded = true\n\tend)\nend\n\nfunction GM:SaveData()\n\tix.date.Save()\nend\n\nfunction GM:ShutDown()\n\tix.shuttingDown = true\n\tix.config.Save()\n\n\thook.Run(\"SaveData\")\n\n\tfor _, v in player.Iterator() do\n\t\tv:SaveData()\n\n\t\tif (v:GetCharacter()) then\n\t\t\tv:GetCharacter():Save()\n\t\tend\n\tend\nend\n\nfunction GM:GetGameDescription()\n\treturn \"IX: \"..(Schema and Schema.name or \"Unknown\")\nend\n\nfunction GM:OnPlayerUseBusiness(client, item)\n\t-- You can manipulate purchased items with this hook.\n\t-- does not requires any kind of return.\n\t-- ex) item:SetData(\"businessItem\", true)\n\t-- then every purchased item will be marked as Business Item.\nend\n\nfunction GM:PlayerDeathSound()\n\treturn true\nend\n\nfunction GM:InitializedSchema()\n\tgame.ConsoleCommand(\"sbox_persist ix_\"..Schema.folder..\"\\n\")\nend\n\nfunction GM:PlayerCanHearPlayersVoice(listener, speaker)\n\tif (!speaker:Alive()) then\n\t\treturn false\n\tend\n\n\tlocal bCanHear = listener.ixVoiceHear and listener.ixVoiceHear[speaker]\n\treturn bCanHear, true\nend\n\nfunction GM:PlayerCanPickupWeapon(client, weapon)\n\tlocal data = {}\n\t\tdata.start = client:GetShootPos()\n\t\tdata.endpos = data.start + client:GetAimVector() * 96\n\t\tdata.filter = client\n\tlocal trace = util.TraceLine(data)\n\n\tif (trace.Entity == weapon and client:KeyDown(IN_USE)) then\n\t\treturn true\n\tend\n\n\treturn client.ixWeaponGive\nend\n\nfunction GM:OnPhysgunFreeze(weapon, physObj, entity, client)\n    -- Validate the physObj, to prevent errors on entities who have no physics object\n    if (!IsValid(physObj)) then return false end\n\n\t-- Object is already frozen (!?)\n\tif (!physObj:IsMoveable()) then return false end\n\tif (entity:GetUnFreezable()) then return false end\n\n\tphysObj:EnableMotion(false)\n\n\t-- With the jeep we need to pause all of its physics objects\n\t-- to stop it spazzing out and killing the server.\n\tif (entity:GetClass() == \"prop_vehicle_jeep\") then\n\t\tlocal objects = entity:GetPhysicsObjectCount()\n\n\t\tfor i = 0, objects - 1 do\n\t\t\tentity:GetPhysicsObjectNum(i):EnableMotion(false)\n\t\tend\n\tend\n\n\t-- Add it to the player's frozen props\n\tclient:AddFrozenPhysicsObject(entity, physObj)\n\tclient:SendHint(\"PhysgunUnfreeze\", 0.3)\n\tclient:SuppressHint(\"PhysgunFreeze\")\n\n\treturn true\nend\n\nfunction GM:CanPlayerSuicide(client)\n\treturn false\nend\n\nfunction GM:AllowPlayerPickup(client, entity)\n\treturn false\nend\n\nfunction GM:PreCleanupMap()\n\thook.Run(\"SaveData\")\n\thook.Run(\"PersistenceSave\")\nend\n\nfunction GM:PostCleanupMap()\n\tix.plugin.RunLoadData()\nend\n\nfunction GM:CharacterPreSave(character)\n\tlocal client = character:GetPlayer()\n\n\tfor k, _ in character:GetInventory():Iter() do\n\t\tif (k.OnSave) then\n\t\t\tk:Call(\"OnSave\", client)\n\t\tend\n\tend\n\n\tcharacter:SetData(\"health\", client:Alive() and client:Health() or nil)\nend\n\ntimer.Create(\"ixLifeGuard\", 1, 0, function()\n\tfor _, v in player.Iterator() do\n\t\tif (v:GetCharacter() and v:Alive() and hook.Run(\"ShouldPlayerDrowned\", v) != false) then\n\t\t\tif (v:WaterLevel() >= 3) then\n\t\t\t\tif (!v.drowningTime) then\n\t\t\t\t\tv.drowningTime = CurTime() + 30\n\t\t\t\t\tv.nextDrowning = CurTime()\n\t\t\t\t\tv.drownDamage = v.drownDamage or 0\n\t\t\t\tend\n\n\t\t\t\tif (v.drowningTime < CurTime()) then\n\t\t\t\t\tif (v.nextDrowning < CurTime()) then\n\t\t\t\t\t\tv:ScreenFade(1, Color(0, 0, 255, 100), 1, 0)\n\t\t\t\t\t\tv:TakeDamage(10)\n\t\t\t\t\t\tv.drownDamage = v.drownDamage + 10\n\t\t\t\t\t\tv.nextDrowning = CurTime() + 1\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\telse\n\t\t\t\tif (v.drowningTime) then\n\t\t\t\t\tv.drowningTime = nil\n\t\t\t\t\tv.nextDrowning = nil\n\t\t\t\t\tv.nextRecover = CurTime() + 2\n\t\t\t\tend\n\n\t\t\t\tif (v.nextRecover and v.nextRecover < CurTime() and v.drownDamage > 0) then\n\t\t\t\t\tv.drownDamage = v.drownDamage - 10\n\t\t\t\t\tv:SetHealth(math.Clamp(v:Health() + 10, 0, v:GetMaxHealth()))\n\t\t\t\t\tv.nextRecover = CurTime() + 1\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend\nend)\n\nnet.Receive(\"ixStringRequest\", function(length, client)\n\tlocal time = net.ReadUInt(32)\n\tlocal text = net.ReadString()\n\n\tif (client.ixStrReqs and client.ixStrReqs[time]) then\n\t\tclient.ixStrReqs[time](text)\n\t\tclient.ixStrReqs[time] = nil\n\tend\nend)\n\nfunction GM:GetPreferredCarryAngles(entity)\n\tif (entity:GetClass() == \"ix_item\") then\n\t\tlocal itemTable = entity:GetItemTable()\n\n\t\tif (itemTable) then\n\t\t\tlocal preferedAngle = itemTable.preferedAngle\n\n\t\t\tif (preferedAngle) then -- I don't want to return something\n\t\t\t\treturn preferedAngle\n\t\t\tend\n\t\tend\n\tend\nend\n\nfunction GM:PluginShouldLoad(uniqueID)\n\treturn !ix.plugin.unloaded[uniqueID]\nend\n\nfunction GM:DatabaseConnected()\n\t-- Create the SQL tables if they do not exist.\n\tix.db.LoadTables()\n\tix.log.LoadTables()\n\n\tMsgC(Color(0, 255, 0), \"Database Type: \" .. ix.db.config.adapter .. \".\\n\")\n\n\ttimer.Create(\"ixDatabaseThink\", 0.5, 0, function()\n\t\tmysql:Think()\n\tend)\n\n\tix.plugin.RunLoadData()\nend\n"
  },
  {
    "path": "gamemode/core/libs/cl_bar.lua",
    "content": "\r\nix.bar = ix.bar or {}\r\nix.bar.list = {}\r\nix.bar.delta = ix.bar.delta or {}\r\nix.bar.actionText = \"\"\r\nix.bar.actionStart = 0\r\nix.bar.actionEnd = 0\r\nix.bar.totalHeight = 0\r\n\r\n-- luacheck: globals BAR_HEIGHT\r\nBAR_HEIGHT = 10\r\n\r\nfunction ix.bar.Get(identifier)\r\n\tfor _, v in ipairs(ix.bar.list) do\r\n\t\tif (v.identifier == identifier) then\r\n\t\t\treturn v\r\n\t\tend\r\n\tend\r\nend\r\n\r\nfunction ix.bar.Remove(identifier)\r\n\tlocal bar = ix.bar.Get(identifier)\r\n\r\n\tif (bar) then\r\n\t\ttable.remove(ix.bar.list, bar.index)\r\n\r\n\t\tif (IsValid(ix.gui.bars)) then\r\n\t\t\tix.gui.bars:RemoveBar(bar.panel)\r\n\t\tend\r\n\tend\r\nend\r\n\r\nfunction ix.bar.Add(getValue, color, priority, identifier)\r\n\tif (identifier) then\r\n\t\tix.bar.Remove(identifier)\r\n\tend\r\n\r\n\tlocal index = #ix.bar.list + 1\r\n\r\n\tcolor = color or Color(math.random(150, 255), math.random(150, 255), math.random(150, 255))\r\n\tpriority = priority or index\r\n\r\n\tix.bar.list[index] = {\r\n\t\tindex = index,\r\n\t\tcolor = color,\r\n\t\tpriority = priority,\r\n\t\tGetValue = getValue,\r\n\t\tidentifier = identifier,\r\n\t\tpanel = IsValid(ix.gui.bars) and ix.gui.bars:AddBar(index, color, priority)\r\n\t}\r\n\r\n\treturn priority\r\nend\r\n\r\nlocal gradientD = ix.util.GetMaterial(\"vgui/gradient-d\")\r\n\r\nlocal TEXT_COLOR = Color(240, 240, 240)\r\nlocal SHADOW_COLOR = Color(20, 20, 20)\r\n\r\nfunction ix.bar.DrawAction()\r\n\tlocal start, finish = ix.bar.actionStart, ix.bar.actionEnd\r\n\tlocal curTime = CurTime()\r\n\tlocal scrW, scrH = ScrW(), ScrH()\r\n\r\n\tif (finish > curTime) then\r\n\t\tlocal fraction = 1 - math.TimeFraction(start, finish, curTime)\r\n\t\tlocal alpha = fraction * 255\r\n\r\n\t\tif (alpha > 0) then\r\n\t\t\tlocal w, h = scrW * 0.35, 28\r\n\t\t\tlocal x, y = (scrW * 0.5) - (w * 0.5), (scrH * 0.725) - (h * 0.5)\r\n\r\n\t\t\tix.util.DrawBlurAt(x, y, w, h)\r\n\r\n\t\t\tsurface.SetDrawColor(35, 35, 35, 100)\r\n\t\t\tsurface.DrawRect(x, y, w, h)\r\n\r\n\t\t\tsurface.SetDrawColor(0, 0, 0, 120)\r\n\t\t\tsurface.DrawOutlinedRect(x, y, w, h)\r\n\r\n\t\t\tsurface.SetDrawColor(ix.config.Get(\"color\"))\r\n\t\t\tsurface.DrawRect(x + 4, y + 4, math.max(w * fraction, 8) - 8, h - 8)\r\n\r\n\t\t\tsurface.SetDrawColor(200, 200, 200, 20)\r\n\t\t\tsurface.SetMaterial(gradientD)\r\n\t\t\tsurface.DrawTexturedRect(x + 4, y + 4, math.max(w * fraction, 8) - 8, h - 8)\r\n\r\n\t\t\tdraw.SimpleText(ix.bar.actionText, \"ixMediumFont\", x + 2, y - 22, SHADOW_COLOR)\r\n\t\t\tdraw.SimpleText(ix.bar.actionText, \"ixMediumFont\", x, y - 24, TEXT_COLOR)\r\n\t\tend\r\n\tend\r\nend\r\n\r\ndo\r\n\tix.bar.Add(function()\r\n\t\treturn math.max(LocalPlayer():Health() / LocalPlayer():GetMaxHealth(), 0)\r\n\tend, Color(200, 50, 40), nil, \"health\")\r\n\r\n\tix.bar.Add(function()\r\n\t\treturn math.min(LocalPlayer():Armor() / 100, 1)\r\n\tend, Color(30, 70, 180), nil, \"armor\")\r\nend\r\n\r\nnet.Receive(\"ixActionBar\", function()\r\n\tlocal start, finish = net.ReadFloat(), net.ReadFloat()\r\n\tlocal text = net.ReadString()\r\n\r\n\tif (text:sub(1, 1) == \"@\") then\r\n\t\ttext = L2(text:sub(2)) or text\r\n\tend\r\n\r\n\tix.bar.actionStart = start\r\n\tix.bar.actionEnd = finish\r\n\tix.bar.actionText = text:utf8upper()\r\nend)\r\n\r\nnet.Receive(\"ixActionBarReset\", function()\r\n\tix.bar.actionStart = 0\r\n\tix.bar.actionEnd = 0\r\n\tix.bar.actionText = \"\"\r\nend)\r\n"
  },
  {
    "path": "gamemode/core/libs/cl_hud.lua",
    "content": "\nix.hud = {}\n\nfunction ix.hud.DrawItemPickup()\n\tlocal pickupTime = ix.config.Get(\"itemPickupTime\", 0.5)\n\n\tif (pickupTime == 0) then\n\t\treturn\n\tend\n\n\tlocal client = LocalPlayer()\n\tlocal entity = client.ixInteractionTarget\n\tlocal startTime = client.ixInteractionStartTime\n\n\tif (IsValid(entity) and startTime) then\n\t\tlocal sysTime = SysTime()\n\t\tlocal endTime = startTime + pickupTime\n\n\t\tif (sysTime >= endTime or client:GetEyeTrace().Entity != entity) then\n\t\t\tclient.ixInteractionTarget = nil\n\t\t\tclient.ixInteractionStartTime = nil\n\n\t\t\treturn\n\t\tend\n\n\t\tlocal fraction = math.min((endTime - sysTime) / pickupTime, 1)\n\t\tlocal x, y = ScrW() / 2, ScrH() / 2\n\t\tlocal radius, thickness = 32, 6\n\t\tlocal startAngle = 90\n\t\tlocal endAngle = startAngle + (1 - fraction) * 360\n\t\tlocal color = ColorAlpha(color_white, fraction * 255)\n\n\t\tix.util.DrawArc(x, y, radius, thickness, startAngle, endAngle, 2, color)\n\tend\nend\n\nfunction ix.hud.PopulateItemTooltip(tooltip, item)\n\tlocal name = tooltip:AddRow(\"name\")\n\tname:SetImportant()\n\tname:SetText(item.GetName and item:GetName() or L(item.name))\n\tname:SetMaxWidth(math.max(name:GetMaxWidth(), ScrW() * 0.5))\n\tname:SizeToContents()\n\n\tlocal description = tooltip:AddRow(\"description\")\n\tdescription:SetText(item:GetDescription() or \"\")\n\tdescription:SizeToContents()\n\n\tif (item.PopulateTooltip) then\n\t\titem:PopulateTooltip(tooltip)\n\tend\n\n\thook.Run(\"PopulateItemTooltip\", tooltip, item)\nend\n\nfunction ix.hud.PopulatePlayerTooltip(tooltip, client)\n\tlocal name = tooltip:AddRow(\"name\")\n\tname:SetImportant()\n\tname:SetText(client:SteamName())\n\tname:SetBackgroundColor(team.GetColor(client:Team()))\n\tname:SizeToContents()\n\n\tlocal nameHeight = name:GetTall()\n\tname:SetTextInset(nameHeight + 4, 0)\n\tname:SetWide(name:GetWide() + nameHeight + 4)\n\n\tlocal avatar = name:Add(\"AvatarImage\")\n\tavatar:Dock(LEFT)\n\tavatar:SetPlayer(client, nameHeight)\n\tavatar:SetSize(name:GetTall(), name:GetTall())\n\n\tlocal currentPing = client:Ping()\n\n\tlocal ping = tooltip:AddRow(\"ping\")\n\tping:SetText(L(\"ping\", currentPing))\n\tping.Paint = function(_, width, height)\n\t\tsurface.SetDrawColor(ColorAlpha(derma.GetColor(\n\t\t\tcurrentPing < 110 and \"Success\" or (currentPing < 165 and \"Warning\" or \"Error\")\n\t\t, tooltip), 22))\n\t\tsurface.DrawRect(0, 0, width, height)\n\tend\n\tping:SizeToContents()\n\n\thook.Run(\"PopulatePlayerTooltip\", client, tooltip)\nend\n\nfunction ix.hud.DrawAll()\n\tix.hud.DrawItemPickup()\nend\n"
  },
  {
    "path": "gamemode/core/libs/cl_markup.lua",
    "content": "\n-- luacheck: ignore\nix.markup = ix.markup or {}\n\n-- Temporary information used when building text frames.\nlocal colour_stack = { {r=255,g=255,b=255,a=255} }\nlocal font_stack = { \"DermaDefault\" }\nlocal curtag = nil\nlocal blocks = {}\n\nlocal colourmap = {\n-- it's all black and white\n\t[\"black\"] =\t\t{\tr=0,\tg=0,\tb=0,\ta=255\t},\n\t[\"white\"] =\t\t{\tr=255,\tg=255,\tb=255,\ta=255\t},\n-- it's greys\n\t[\"dkgrey\"] =\t{\tr=64,\tg=64,\tb=64,\ta=255\t},\n\t[\"grey\"] =\t\t{\tr=128,\tg=128,\tb=128,\ta=255\t},\n\t[\"ltgrey\"] =\t{\tr=192,\tg=192,\tb=192,\ta=255\t},\n-- account for speeling mistakes\n\t[\"dkgray\"] =\t{\tr=64,\tg=64,\tb=64,\ta=255\t},\n\t[\"gray\"] =\t\t{\tr=128,\tg=128,\tb=128,\ta=255\t},\n\t[\"ltgray\"] =\t{\tr=192,\tg=192,\tb=192,\ta=255\t},\n-- normal colours\n\t[\"red\"] =\t\t{\tr=255,\tg=0,\tb=0,\ta=255\t},\n\t[\"green\"] =\t\t{\tr=0,\tg=255,\tb=0,\ta=255\t},\n\t[\"blue\"] =\t\t{\tr=0,\tg=0,\tb=255,\ta=255\t},\n\t[\"yellow\"] =\t{\tr=255,\tg=255,\tb=0,\ta=255\t},\n\t[\"purple\"] =\t{\tr=255,\tg=0,\tb=255,\ta=255\t},\n\t[\"cyan\"] =\t\t{\tr=0,\tg=255,\tb=255,\ta=255\t},\n\t[\"turq\"] =\t\t{\tr=0,\tg=255,\tb=255,\ta=255\t},\n-- dark variations\n\t[\"dkred\"] =\t\t{\tr=128,\tg=0,\tb=0,\ta=255\t},\n\t[\"dkgreen\"] =\t{\tr=0,\tg=128,\tb=0,\ta=255\t},\n\t[\"dkblue\"] =\t{\tr=0,\tg=0,\tb=128,\ta=255\t},\n\t[\"dkyellow\"] =\t{\tr=128,\tg=128,\tb=0,\ta=255\t},\n\t[\"dkpurple\"] =\t{\tr=128,\tg=0,\tb=128,\ta=255\t},\n\t[\"dkcyan\"] =\t{\tr=0,\tg=128,\tb=128,\ta=255\t},\n\t[\"dkturq\"] =\t{\tr=0,\tg=128,\tb=128,\ta=255\t},\n-- light variations\n\t[\"ltred\"] =\t\t{\tr=255,\tg=128,\tb=128,\ta=255\t},\n\t[\"ltgreen\"] =\t{\tr=128,\tg=255,\tb=128,\ta=255\t},\n\t[\"ltblue\"] =\t{\tr=128,\tg=128,\tb=255,\ta=255\t},\n\t[\"ltyellow\"] =\t{\tr=255,\tg=255,\tb=128,\ta=255\t},\n\t[\"ltpurple\"] =\t{\tr=255,\tg=128,\tb=255,\ta=255\t},\n\t[\"ltcyan\"] =\t{\tr=128,\tg=255,\tb=255,\ta=255\t},\n\t[\"ltturq\"] =\t{\tr=128,\tg=255,\tb=255,\ta=255\t},\n}\n\n--[[\n    Name: colourMatch(c)\n    Desc: Match a colour name to an rgb value.\n   Usage: ** INTERNAL ** Do not use!\n]]\nlocal function colourMatch(c)\n\tc = string.lower(c)\n\n\treturn colourmap[c]\nend\n\n--[[\n    Name: ExtractParams(p1,p2,p3)\n    Desc: This function is used to extract the tag information.\n   Usage: ** INTERNAL ** Do not use!\n]]\nlocal function ExtractParams(p1,p2,p3)\n\n\tif (string.utf8sub(p1, 1, 1) == \"/\") then\n\n\t\tlocal tag = string.utf8sub(p1, 2)\n\n\t\tif (tag == \"color\" or tag == \"colour\") then\n\t\t\ttable.remove(colour_stack)\n\t\telseif (tag == \"font\" or tag == \"face\") then\n\t\t\ttable.remove(font_stack)\n\t\tend\n\n\telse\n\n\t\tif (p1 == \"color\" or p1 == \"colour\") then\n\n\t\t\tlocal rgba = colourMatch(p2)\n\n\t\t\tif (rgba == nil) then\n\t\t\t\trgba = {}\n\t\t\t\tlocal x = { \"r\", \"g\", \"b\", \"a\" }\n\t\t\t\tn = 1\n\t\t\t\tfor k, v in string.gmatch(p2, \"(%d+),?\") do\n\t\t\t\t\trgba[ x[n] ] = k\n\t\t\t\t\tn = n + 1\n\t\t\t\tend\n\t\t\tend\n\n\t\t\ttable.insert(colour_stack, rgba)\n\n\t\telseif (p1 == \"font\" or p1 == \"face\") then\n\n\t\t\ttable.insert(font_stack, tostring(p2))\n\t\telseif (p1 == \"img\" and p2) then\n\t\t\tlocal exploded = string.Explode(\",\", p2)\n\t\t\tlocal material = exploded[1] or p2\n\t\t\tlocal p3 = exploded[2]\n\n\t\t\tlocal found = file.Find(\"materials/\"..material..\".*\", \"GAME\")\n\n\t\t\tif (found[1] and found[1]:find(\"%.png\")) then\n\t\t\t\tmaterial = material..\".png\"\n\t\t\tend\n\n\t\t\tlocal texture = Material(material)\n\t\t\tlocal sizeData = string.Explode(\"x\", p3 or \"16x16\")\n\t\t\tw = tonumber(sizeData[1]) or 16\n\t\t\th = tonumber(sizeData[2]) or 16\n\n\t\t\tif (texture) then\n\t\t\t\ttable.insert(blocks, {\n\t\t\t\t\ttexture = texture,\n\t\t\t\t\tw = w,\n\t\t\t\t\th = h\n\t\t\t\t})\n\t\t\tend\n\t\tend\n\n\tend\nend\n\n--[[\n    Name: CheckTextOrTag(p)\n    Desc: This function places data in the \"blocks\" table\n          depending of if p is a tag, or some text\n   Usage: ** INTERNAL ** Do not use!\n]]\nlocal function CheckTextOrTag(p)\n\tif (p == \"\") then return end\n\tif (p == nil) then return end\n\n\tif (string.utf8sub(p, 1, 1) == \"<\") then\n\t\tstring.gsub(p, \"<([/%a]*)=?([^>]*)\", ExtractParams)\n\telse\n\n\t\tlocal text_block = {}\n\t\ttext_block.text = p\n\t\ttext_block.colour = colour_stack[#colour_stack]\n\t\ttext_block.font = font_stack[#font_stack]\n\t\ttable.insert(blocks, text_block)\n\n\tend\nend\n\n--[[\n    Name: ProcessMatches(p1,p2,p3)\n    Desc: CheckTextOrTag for 3 parameters. Called by string.gsub\n   Usage: ** INTERNAL ** Do not use!\n]]\nlocal function ProcessMatches(p1,p2,p3)\n\tif (p1) then CheckTextOrTag(p1) end\n\tif (p2) then CheckTextOrTag(p2) end\n\tif (p3) then CheckTextOrTag(p3) end\nend\n\nlocal MarkupObject = {}\n\n--[[\n    Name: MarkupObject:Create()\n    Desc: Called by Parse. Creates a new table, and setups the\n          metatable.\n   Usage: ** INTERNAL ** Do not use!\n]]\nfunction MarkupObject:create()\n\tlocal o = {}\n\tsetmetatable(o, self)\n\tself.__index = self\n\n\treturn o\nend\n\n--[[\n    Name: MarkupObject:GetWidth()\n    Desc: Returns the width of a markup block\n   Usage: ml:GetWidth()\n]]\nfunction MarkupObject:GetWidth()\n\treturn self.totalWidth\nend\n\n--[[\n    Name: MarkupObject:GetHeight()\n    Desc: Returns the height of a markup block\n   Usage: ml:GetHeight()\n]]\nfunction MarkupObject:GetHeight()\n\treturn self.totalHeight\nend\n\nfunction MarkupObject:size()\n\treturn self.totalWidth, self.totalHeight\nend\n\n--[[\n    Name: MarkupObject:Draw(xOffset, yOffset, halign, valign, alphaoverride)\n    Desc: Draw the markup text to the screen as position\n          xOffset, yOffset. Halign and Valign can be used\n          to align the text. Alphaoverride can be used to override\n          the alpha value of the text-colour.\n   Usage: MarkupObject:Draw(100, 100)\n]]\nfunction MarkupObject:draw(xOffset, yOffset, halign, valign, alphaoverride)\n\tfor i = 1, #self.blocks do\n\t\tlocal blk = self.blocks[i]\n\n\t\tif (blk.texture) then\n\t\t\tlocal y = yOffset + blk.offset.y\n\t\t\tlocal x = xOffset + blk.offset.x\n\n\t\t\tif (halign == TEXT_ALIGN_CENTER) then\n\t\t\t\tx = x - (self.totalWidth * 0.5)\n\t\t\telseif (halign == TEXT_ALIGN_RIGHT) then\n\t\t\t\tx = x - (self.totalWidth)\n\t\t\tend\n\n\t\t\tsurface.SetDrawColor(blk.colour.r, blk.colour.g, blk.colour.b, alphaoverride or blk.colour.a or 255)\n\t\t\tsurface.SetMaterial(blk.texture)\n\t\t\tsurface.DrawTexturedRect(x, y, blk.w, blk.h)\n\t\telse\n\t\t\tlocal y = yOffset + (blk.height - blk.thisY) + blk.offset.y\n\t\t\tlocal x = xOffset\n\n\t\t\tif (halign == TEXT_ALIGN_CENTER) then\t\tx = x - (self.totalWidth / 2)\n\t\t\telseif (halign == TEXT_ALIGN_RIGHT) then\tx = x - (self.totalWidth)\n\t\t\tend\n\n\t\t\tx = x + blk.offset.x\n\n\t\t\tif (self.onDrawText) then\n\t\t\t\tself.onDrawText(blk.text, blk.font, x, y, blk.colour, halign, valign, alphaoverride, blk)\n\t\t\telse\n\t\t\t\tif (valign == TEXT_ALIGN_CENTER) then\t\ty = y - (self.totalHeight / 2)\n\t\t\t\telseif (valign == TEXT_ALIGN_BOTTOM) then\ty = y - (self.totalHeight)\n\t\t\t\tend\n\n\t\t\t\tlocal alpha = blk.colour.a\n\t\t\t\tif (alphaoverride) then alpha = alphaoverride end\n\n\t\t\t\tsurface.SetFont( blk.font )\n\t\t\t\tsurface.SetTextColor( blk.colour.r, blk.colour.g, blk.colour.b, alpha )\n\t\t\t\tsurface.SetTextPos( x, y )\n\t\t\t\tsurface.DrawText( blk.text )\n\t\t\tend\n\t\tend\n\tend\nend\n\n--[[\n    Name: Parse(ml, maxwidth)\n    Desc: Parses the pseudo-html markup language, and creates a\n          MarkupObject, which can be used to the draw the\n          text to the screen. Valid tags are: font and colour.\n          \\n and \\t are also available to move to the next line,\n          or insert a tab character.\n          Maxwidth can be used to make the text wrap to a specific\n          width.\n   Usage: markup.Parse(\"<font=Default>changed font</font>\\n<colour=255,0,255,255>changed colour</colour>\")\n]]\nfunction ix.markup.Parse(ml, maxwidth)\n\n\tml = utf8.force(ml)\n\n\tcolour_stack = { {r=255,g=255,b=255,a=255} }\n\tfont_stack = { \"DermaDefault\" }\n\tblocks = {}\n\n\tif (not string.find(ml, \"<\")) then\n\t\tml = ml .. \"<nop>\"\n\tend\n\n\tstring.gsub(ml, \"([^<>]*)(<[^>]+.)([^<>]*)\", ProcessMatches)\n\n\tlocal xOffset = 0\n\tlocal yOffset = 0\n\tlocal xSize = 0\n\tlocal xMax = 0\n\tlocal thisMaxY = 0\n\tlocal new_block_list = {}\n\tlocal ymaxes = {}\n\tlocal texOffset = 0\n\n\tlocal lineHeight = 0\n\tfor i = 1, #blocks do\n\t\tlocal block = blocks[i]\n\n\t\tif (block.text) then\n\t\t\tsurface.SetFont(block.font)\n\n\t\t\tlocal thisY = 0\n\t\t\tlocal curString = \"\"\n\t\t\tblock.text = string.gsub(block.text, \"&gt;\", \">\")\n\t\t\tblock.text = string.gsub(block.text, \"&lt;\", \"<\")\n\t\t\tblock.text = string.gsub(block.text, \"&amp;\", \"&\")\n\n\t\t\tfor j=1,string.utf8len(block.text) do\n\t\t\t\tlocal ch = string.utf8sub(block.text,j,j)\n\n\t\t\t\tif (ch == \"\\n\") then\n\t\t\t\t\tif (thisY == 0) then\n\t\t\t\t\t\tthisY = lineHeight + texOffset;\n\t\t\t\t\t\tthisMaxY = lineHeight + texOffset;\n\t\t\t\t\telse\n\t\t\t\t\t\tlineHeight = thisY + texOffset\n\t\t\t\t\tend\n\n\t\t\t\t\tif (string.utf8len(curString) > 0) then\n\t\t\t\t\t\tlocal x1,y1 = surface.GetTextSize(curString)\n\n\t\t\t\t\t\tlocal new_block = {}\n\t\t\t\t\t\tnew_block.text = curString\n\t\t\t\t\t\tnew_block.font = block.font\n\t\t\t\t\t\tnew_block.colour = block.colour\n\t\t\t\t\t\tnew_block.thisY = thisY\n\t\t\t\t\t\tnew_block.thisX = x1\n\t\t\t\t\t\tnew_block.offset = {}\n\t\t\t\t\t\tnew_block.offset.x = xOffset\n\t\t\t\t\t\tnew_block.offset.y = yOffset\n\t\t\t\t\t\ttable.insert(new_block_list, new_block)\n\t\t\t\t\t\tif (xOffset + x1 > xMax) then\n\t\t\t\t\t\t\txMax = xOffset + x1\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\n\t\t\t\t\txOffset = 0\n\t\t\t\t\txSize = 0\n\t\t\t\t\tyOffset = yOffset + thisMaxY;\n\t\t\t\t\tthisY = 0\n\t\t\t\t\tcurString = \"\"\n\t\t\t\t\tthisMaxY = 0\n\t\t\t\telseif (ch == \"\\t\") then\n\n\t\t\t\t\tif (string.utf8len(curString) > 0) then\n\t\t\t\t\t\tlocal x1,y1 = surface.GetTextSize(curString)\n\n\t\t\t\t\t\tlocal new_block = {}\n\t\t\t\t\t\tnew_block.text = curString\n\t\t\t\t\t\tnew_block.font = block.font\n\t\t\t\t\t\tnew_block.colour = block.colour\n\t\t\t\t\t\tnew_block.thisY = thisY\n\t\t\t\t\t\tnew_block.thisX = x1\n\t\t\t\t\t\tnew_block.offset = {}\n\t\t\t\t\t\tnew_block.offset.x = xOffset\n\t\t\t\t\t\tnew_block.offset.y = yOffset\n\t\t\t\t\t\ttable.insert(new_block_list, new_block)\n\t\t\t\t\t\tif (xOffset + x1 > xMax) then\n\t\t\t\t\t\t\txMax = xOffset + x1\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\n\t\t\t\t\tlocal xOldSize = xSize\n\t\t\t\t\txSize = 0\n\t\t\t\t\tcurString = \"\"\n\t\t\t\t\tlocal xOldOffset = xOffset\n\t\t\t\t\txOffset = math.ceil( (xOffset + xOldSize) / 50 ) * 50\n\n\t\t\t\t\tif (xOffset == xOldOffset) then\n\t\t\t\t\t\txOffset = xOffset + 50\n\t\t\t\t\tend\n\t\t\t\telse\n\t\t\t\t\tlocal x,y = surface.GetTextSize(ch)\n\n\t\t\t\t\tif (x == nil) then return end\n\n\t\t\t\t\tif (maxwidth and maxwidth > x) then\n\t\t\t\t\t\tif (xOffset + xSize + x >= maxwidth) then\n\n\t\t\t\t\t\t\t-- need to: find the previous space in the curString\n\t\t\t\t\t\t\t--          if we can't find one, take off the last character\n\t\t\t\t\t\t\t--          and add a -. add the character to ch\n\t\t\t\t\t\t\t--          and insert as a new block, incrementing the y etc\n\n\t\t\t\t\t\t\tlocal lastSpacePos = string.utf8len(curString)\n\t\t\t\t\t\t\tfor k=1,string.utf8len(curString) do\n\t\t\t\t\t\t\t\tlocal chspace = string.utf8sub(curString,k,k)\n\t\t\t\t\t\t\t\tif (chspace == \" \") then\n\t\t\t\t\t\t\t\t\tlastSpacePos = k\n\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\t\tif (lastSpacePos == string.utf8len(curString)) then\n\t\t\t\t\t\t\t\tch = string.utf8sub(curString,lastSpacePos,lastSpacePos) .. ch\n\t\t\t\t\t\t\t\tj = lastSpacePos\n\t\t\t\t\t\t\t\tcurString = string.utf8sub(curString, 1, lastSpacePos-1)\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\tch = string.utf8sub(curString,lastSpacePos+1) .. ch\n\t\t\t\t\t\t\t\tj = lastSpacePos+1\n\t\t\t\t\t\t\t\tcurString = string.utf8sub(curString, 1, lastSpacePos)\n\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\t\tlocal m = 1\n\t\t\t\t\t\t\twhile string.utf8sub(ch, m, m) == \" \" and m <= string.utf8len(ch) do\n\t\t\t\t\t\t\t\tm = m + 1\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\tch = string.utf8sub(ch, m)\n\n\t\t\t\t\t\t\tlocal x1,y1 = surface.GetTextSize(curString)\n\n\t\t\t\t\t\t\tif (y1 > thisMaxY) then thisMaxY = y1; ymaxes[yOffset] = thisMaxY; lineHeight = y1; end\n\n\t\t\t\t\t\t\tlocal new_block = {}\n\t\t\t\t\t\t\tnew_block.text = curString\n\t\t\t\t\t\t\tnew_block.font = block.font\n\t\t\t\t\t\t\tnew_block.colour = block.colour\n\t\t\t\t\t\t\tnew_block.thisY = thisY\n\t\t\t\t\t\t\tnew_block.thisX = x1\n\t\t\t\t\t\t\tnew_block.offset = {}\n\t\t\t\t\t\t\tnew_block.offset.x = xOffset\n\t\t\t\t\t\t\tnew_block.offset.y = yOffset\n\t\t\t\t\t\t\ttable.insert(new_block_list, new_block)\n\n\t\t\t\t\t\t\tif (xOffset + x1 > xMax) then\n\t\t\t\t\t\t\t\txMax = xOffset + x1\n\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\t\txOffset = 0\n\t\t\t\t\t\t\txSize = 0\n\t\t\t\t\t\t\tx,y = surface.GetTextSize(ch)\n\t\t\t\t\t\t\tyOffset = yOffset + thisMaxY;\n\t\t\t\t\t\t\tthisY = 0\n\t\t\t\t\t\t\tcurString = \"\"\n\t\t\t\t\t\t\tthisMaxY = 0\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\n\t\t\t\t\tcurString = curString .. ch\n\n\t\t\t\t\tthisY = y\n\t\t\t\t\txSize = xSize + x\n\n\t\t\t\t\tif (y > thisMaxY) then thisMaxY = y; ymaxes[yOffset] = thisMaxY; lineHeight = y; end\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tif (string.utf8len(curString) > 0) then\n\n\t\t\t\tlocal x1,y1 = surface.GetTextSize(curString)\n\n\t\t\t\tlocal new_block = {}\n\t\t\t\tnew_block.text = curString\n\t\t\t\tnew_block.font = block.font\n\t\t\t\tnew_block.colour = block.colour\n\t\t\t\tnew_block.thisY = thisY\n\t\t\t\tnew_block.thisX = x1\n\t\t\t\tnew_block.offset = {}\n\t\t\t\tnew_block.offset.x = xOffset\n\t\t\t\tnew_block.offset.y = yOffset\n\t\t\t\ttable.insert(new_block_list, new_block)\n\n\t\t\t\tlineHeight = thisY\n\n\t\t\t\tif (xOffset + x1 > xMax) then\n\t\t\t\t\txMax = xOffset + x1\n\t\t\t\tend\n\t\t\t\txOffset = xOffset + x1\n\t\t\tend\n\t\t\txSize = 0\n\t\telseif (block.texture) then\n\t\t\tlocal newBlock = table.Copy(block)\n\t\t\t\tnewBlock.colour = block.colour or {r = 255, g = 255, b = 255, a = 255}\n\t\t\t\tnewBlock.thisX = block.w\n\t\t\t\tnewBlock.thisY = block.h\n\t\t\t\tnewBlock.offset = {\n\t\t\t\t\tx = xOffset,\n\t\t\t\t\ty = 0\n\t\t\t\t}\n\n\t\t\ttable.insert(new_block_list, newBlock)\n\t\t\txOffset = xOffset + block.w + 1\n\t\t\ttexOffset = block.h / 2\n\t\tend\n\tend\n\n\tlocal totalHeight = 0\n\tfor i = 1, #new_block_list do\n\t\tlocal block = new_block_list[i]\n\t\tblock.height = ymaxes[block.offset.y]\n\n\t\tif (block.texture) then\n\t\t\tblock.offset.y = ymaxes[0] * 0.5 - block.h * 0.5\n\t\tend\n\n\t\tif (block.height and block.offset.y + block.height > totalHeight) then\n\t\t\ttotalHeight = block.offset.y + block.height\n\t\tend\n\tend\n\n\tlocal newObject = MarkupObject:create()\n\tnewObject.totalHeight = totalHeight\n\tnewObject.totalWidth = xMax\n\tnewObject.blocks = new_block_list\n\treturn newObject\nend\n"
  },
  {
    "path": "gamemode/core/libs/cl_networking.lua",
    "content": "\nlocal entityMeta = FindMetaTable(\"Entity\")\nlocal playerMeta = FindMetaTable(\"Player\")\n\nix.net = ix.net or {}\nix.net.globals = ix.net.globals or {}\n\nnet.Receive(\"ixGlobalVarSet\", function()\n\tix.net.globals[net.ReadString()] = net.ReadType()\nend)\n\nnet.Receive(\"ixNetVarSet\", function()\n\tlocal index = net.ReadUInt(16)\n\n\tix.net[index] = ix.net[index] or {}\n\tix.net[index][net.ReadString()] = net.ReadType()\nend)\n\nnet.Receive(\"ixNetVarDelete\", function()\n\tix.net[net.ReadUInt(16)] = nil\nend)\n\nnet.Receive(\"ixLocalVarSet\", function()\n\tlocal key = net.ReadString()\n\tlocal var = net.ReadType()\n\n\tix.net[LocalPlayer():EntIndex()] = ix.net[LocalPlayer():EntIndex()] or {}\n\tix.net[LocalPlayer():EntIndex()][key] = var\n\n\thook.Run(\"OnLocalVarSet\", key, var)\nend)\n\nfunction GetNetVar(key, default) -- luacheck: globals GetNetVar\n\tlocal value = ix.net.globals[key]\n\n\treturn value != nil and value or default\nend\n\nfunction entityMeta:GetNetVar(key, default)\n\tlocal index = self:EntIndex()\n\n\tif (ix.net[index] and ix.net[index][key] != nil) then\n\t\treturn ix.net[index][key]\n\tend\n\n\treturn default\nend\n\nplayerMeta.GetLocalVar = entityMeta.GetNetVar\n"
  },
  {
    "path": "gamemode/core/libs/sh_animation.lua",
    "content": "\nfunction ix.util.InstallAnimationMethods(meta)\n\tlocal function TweenAnimationThink(object)\n\t\tfor k, v in pairs(object.tweenAnimations) do\n\t\t\tif (!v.bShouldPlay) then\n\t\t\t\tcontinue\n\t\t\tend\n\n\t\t\tlocal bComplete = v:update(FrameTime())\n\n\t\t\tif (v.Think) then\n\t\t\t\tv:Think(object)\n\t\t\tend\n\n\t\t\tif (bComplete) then\n\t\t\t\tv.bShouldPlay = nil\n\n\t\t\t\tv:ForceComplete()\n\n\t\t\t\tif (v.OnComplete) then\n\t\t\t\t\tv:OnComplete(object)\n\t\t\t\tend\n\n\t\t\t\tif (v.bRemoveOnComplete) then\n\t\t\t\t\tobject.tweenAnimations[k] = nil\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend\n\n\tfunction meta:GetTweenAnimation(index, bNoPlay)\n\t\t-- if we don't need to check if the animation is playing we can just return the animation\n\t\tif (bNoPlay) then\n\t\t\treturn self.tweenAnimations[index]\n\t\telse\n\t\t\tfor k, v in pairs(self.tweenAnimations or {}) do\n\t\t\t\tif (k == index and v.bShouldPlay) then\n\t\t\t\t\treturn v\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend\n\n\tfunction meta:IsPlayingTweenAnimation(index)\n\t\tfor k, v in pairs(self.tweenAnimations or {}) do\n\t\t\tif (v.bShouldPlay and index == k) then\n\t\t\t\treturn true\n\t\t\tend\n\t\tend\n\n\t\treturn false\n\tend\n\n\tfunction meta:StopAnimations(bRemove)\n\t\tfor k, v in pairs(self.tweenAnimations or {}) do\n\t\t\tif (v.bShouldPlay) then\n\t\t\t\tv:ForceComplete()\n\n\t\t\t\tif (bRemove) then\n\t\t\t\t\tself.tweenAnimations[k] = nil\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend\n\n\tfunction meta:CreateAnimation(length, data)\n\t\tlocal animations = self.tweenAnimations or {}\n\t\tself.tweenAnimations = animations\n\n\t\tif (self.SetAnimationEnabled) then\n\t\t\tself:SetAnimationEnabled(true)\n\t\tend\n\n\t\tlocal index = data.index or 1\n\t\tlocal bCancelPrevious = data.bCancelPrevious == nil and false or data.bCancelPrevious\n\t\tlocal bIgnoreConfig = SERVER or (data.bIgnoreConfig == nil and false or data.bIgnoreConfig)\n\n\t\tif (bCancelPrevious and self:IsPlayingTweenAnimation()) then\n\t\t\tfor _, v in pairs(animations) do\n\t\t\t\tv:set(v.duration)\n\t\t\tend\n\t\tend\n\n\t\tlocal animation = ix.tween.new(\n\t\t\t((length == 0 and 1 or length) or 1) * (bIgnoreConfig and 1 or ix.option.Get(\"animationScale\", 1)),\n\t\t\tdata.subject or self,\n\t\t\tdata.target or {},\n\t\t\tdata.easing or \"linear\"\n\t\t)\n\n\t\tanimation.index = index\n\t\tanimation.bIgnoreConfig = bIgnoreConfig\n\t\tanimation.bAutoFire = (data.bAutoFire == nil and true or data.bAutoFire)\n\t\tanimation.bRemoveOnComplete = (data.bRemoveOnComplete == nil and true or data.bRemoveOnComplete)\n\t\tanimation.Think = data.Think\n\t\tanimation.OnComplete = data.OnComplete\n\n\t\tanimation.ForceComplete = function(anim)\n\t\t\tanim:set(anim.duration)\n\t\tend\n\n\t\t-- @todo don't use ridiculous method chaining\n\t\tanimation.CreateAnimation = function(currentAnimation, newLength, newData)\n\t\t\tnewData.bAutoFire = false\n\t\t\tnewData.index = currentAnimation.index + 1\n\n\t\t\tlocal oldOnComplete = currentAnimation.OnComplete\n\t\t\tlocal newAnimation = currentAnimation.subject:CreateAnimation(newLength, newData)\n\n\t\t\tcurrentAnimation.OnComplete = function(...)\n\t\t\t\tif (oldOnComplete) then\n\t\t\t\t\toldOnComplete(...)\n\t\t\t\tend\n\n\t\t\t\tnewAnimation:Fire()\n\t\t\tend\n\n\t\t\treturn newAnimation\n\t\tend\n\n\t\tif (length == 0 or (!animation.bIgnoreConfig and ix.option.Get(\"disableAnimations\", false))) then\n\t\t\tanimation.Fire = function(anim)\n\t\t\t\tanim:set(anim.duration)\n\t\t\t\tanim.bShouldPlay = true\n\t\t\tend\n\t\telse\n\t\t\tanimation.Fire = function(anim)\n\t\t\t\tanim:set(0)\n\t\t\t\tanim.bShouldPlay = true\n\t\t\tend\n\t\tend\n\n\t\t-- we can assume if we're using this library, we're not going to use the built-in\n\t\t-- AnimationTo functions, so override AnimationThink with our own\n\t\tself.AnimationThink = TweenAnimationThink\n\n\t\t-- fire right away if autofire is enabled\n\t\tif (animation.bAutoFire) then\n\t\t\tanimation:Fire()\n\t\tend\n\n\t\tself.tweenAnimations[index] = animation\n\t\treturn animation\n\tend\nend\n\nif (CLIENT) then\n\tlocal panelMeta = FindMetaTable(\"Panel\")\n\tix.util.InstallAnimationMethods(panelMeta)\nend\n"
  },
  {
    "path": "gamemode/core/libs/sh_anims.lua",
    "content": "\n--[[--\nPlayer model animation.\n\nHelix comes with support for using NPC animations/models as regular player models by manually translating animations. There are\na few standard animation sets that are built-in that should cover most non-player models:\n\tcitizen_male\n\tcitizen_female\n\tmetrocop\n\toverwatch\n\tvortigaunt\n\tplayer\n\tzombie\n\tfastZombie\n\nIf you find that your models are T-posing when they work elsewhere, you'll probably need to set the model class for your\nmodel with `ix.anim.SetModelClass` in order for the correct animations to be used. If you'd like to add your own animation\nclass, simply add to the `ix.anim` table with a model class name and the required animation translation table.\n]]\n-- @module ix.anim\n\nix.anim = ix.anim or {}\nix.anim.citizen_male = {\n\tnormal = {\n\t\t[ACT_MP_STAND_IDLE] = {ACT_IDLE, ACT_IDLE_ANGRY_SMG1},\n\t\t[ACT_MP_CROUCH_IDLE] = {ACT_COVER_LOW, ACT_COVER_LOW},\n\t\t[ACT_MP_WALK] = {ACT_WALK, ACT_WALK_AIM_RIFLE_STIMULATED},\n\t\t[ACT_MP_CROUCHWALK] = {ACT_WALK_CROUCH, ACT_WALK_CROUCH_AIM_RIFLE},\n\t\t[ACT_MP_RUN] = {ACT_RUN, ACT_RUN_AIM_RIFLE_STIMULATED},\n\t\t[ACT_LAND] = {ACT_RESET, ACT_RESET}\n\t},\n\tpistol = {\n\t\t[ACT_MP_STAND_IDLE] = {ACT_IDLE, ACT_RANGE_ATTACK_PISTOL},\n\t\t[ACT_MP_CROUCH_IDLE] = {ACT_COVER_LOW, ACT_RANGE_ATTACK_PISTOL_LOW},\n\t\t[ACT_MP_WALK] = {ACT_WALK, ACT_WALK_AIM_RIFLE_STIMULATED},\n\t\t[ACT_MP_CROUCHWALK] = {ACT_WALK_CROUCH, ACT_WALK_CROUCH_AIM_RIFLE},\n\t\t[ACT_MP_RUN] = {ACT_RUN, ACT_RUN_AIM_RIFLE_STIMULATED},\n\t\t[ACT_LAND] = {ACT_RESET, ACT_RESET},\n\t\tattack = ACT_GESTURE_RANGE_ATTACK_PISTOL,\n\t\treload = ACT_RELOAD_PISTOL\n\t},\n\tsmg = {\n\t\t[ACT_MP_STAND_IDLE] = {ACT_IDLE_SMG1_RELAXED, ACT_IDLE_ANGRY_SMG1},\n\t\t[ACT_MP_CROUCH_IDLE] = {ACT_COVER_LOW, ACT_RANGE_AIM_SMG1_LOW},\n\t\t[ACT_MP_WALK] = {ACT_WALK_RIFLE_RELAXED, ACT_WALK_AIM_RIFLE_STIMULATED},\n\t\t[ACT_MP_CROUCHWALK] = {ACT_WALK_CROUCH_RIFLE, ACT_WALK_CROUCH_AIM_RIFLE},\n\t\t[ACT_MP_RUN] = {ACT_RUN_RIFLE_RELAXED, ACT_RUN_AIM_RIFLE_STIMULATED},\n\t\t[ACT_LAND] = {ACT_RESET, ACT_RESET},\n\t\tattack = ACT_GESTURE_RANGE_ATTACK_SMG1,\n\t\treload = ACT_GESTURE_RELOAD_SMG1\n\t},\n\tshotgun = {\n\t\t[ACT_MP_STAND_IDLE] = {ACT_IDLE_SHOTGUN_RELAXED, ACT_IDLE_ANGRY_SMG1},\n\t\t[ACT_MP_CROUCH_IDLE] = {ACT_COVER_LOW, ACT_RANGE_AIM_SMG1_LOW},\n\t\t[ACT_MP_WALK] = {ACT_WALK_RIFLE_RELAXED, ACT_WALK_AIM_RIFLE_STIMULATED},\n\t\t[ACT_MP_CROUCHWALK] = {ACT_WALK_CROUCH_RIFLE, ACT_WALK_CROUCH_RIFLE},\n\t\t[ACT_MP_RUN] = {ACT_RUN_RIFLE_RELAXED, ACT_RUN_AIM_RIFLE_STIMULATED},\n\t\t[ACT_LAND] = {ACT_RESET, ACT_RESET},\n\t\tattack = ACT_GESTURE_RANGE_ATTACK_SHOTGUN\n\t},\n\tgrenade = {\n\t\t[ACT_MP_STAND_IDLE] = {ACT_IDLE, ACT_IDLE_MANNEDGUN},\n\t\t[ACT_MP_CROUCH_IDLE] = {ACT_COVER_LOW, ACT_RANGE_AIM_SMG1_LOW},\n\t\t[ACT_MP_WALK] = {ACT_WALK, ACT_WALK_AIM_RIFLE_STIMULATED},\n\t\t[ACT_MP_CROUCHWALK] = {ACT_WALK_CROUCH, ACT_WALK_CROUCH_AIM_RIFLE},\n\t\t[ACT_MP_RUN] = {ACT_RUN, ACT_RUN_RIFLE_STIMULATED},\n\t\t[ACT_LAND] = {ACT_RESET, ACT_RESET},\n\t\tattack = ACT_RANGE_ATTACK_THROW\n\t},\n\tmelee = {\n\t\t[ACT_MP_STAND_IDLE] = {ACT_IDLE, ACT_IDLE_ANGRY_MELEE},\n\t\t[ACT_MP_CROUCH_IDLE] = {ACT_COVER_LOW, ACT_COVER_LOW},\n\t\t[ACT_MP_WALK] = {ACT_WALK, ACT_WALK_AIM_RIFLE},\n\t\t[ACT_MP_CROUCHWALK] = {ACT_WALK_CROUCH, ACT_WALK_CROUCH},\n\t\t[ACT_MP_RUN] = {ACT_RUN, ACT_RUN},\n\t\t[ACT_LAND] = {ACT_RESET, ACT_RESET},\n\t\tattack = ACT_MELEE_ATTACK_SWING\n\t},\n\tglide = ACT_GLIDE,\n\tvehicle = {\n\t\t[\"prop_vehicle_prisoner_pod\"] = {\"podpose\", Vector(-3, 0, 0)},\n\t\t[\"prop_vehicle_jeep\"] = {ACT_BUSY_SIT_CHAIR, Vector(14, 0, -14)},\n\t\t[\"prop_vehicle_airboat\"] = {ACT_BUSY_SIT_CHAIR, Vector(8, 0, -20)},\n\t\tchair = {ACT_BUSY_SIT_CHAIR, Vector(1, 0, -23)}\n\t},\n}\n\nix.anim.citizen_female = {\n\tnormal = {\n\t\t[ACT_MP_STAND_IDLE] = {ACT_IDLE, ACT_IDLE_ANGRY_SMG1},\n\t\t[ACT_MP_CROUCH_IDLE] = {ACT_COVER_LOW, ACT_COVER_LOW},\n\t\t[ACT_MP_WALK] = {ACT_WALK, ACT_WALK_AIM_RIFLE_STIMULATED},\n\t\t[ACT_MP_CROUCHWALK] = {ACT_WALK_CROUCH, ACT_WALK_CROUCH_AIM_RIFLE},\n\t\t[ACT_MP_RUN] = {ACT_RUN, ACT_RUN_AIM_RIFLE_STIMULATED},\n\t\t[ACT_LAND] = {ACT_RESET, ACT_RESET}\n\t},\n\tpistol = {\n\t\t[ACT_MP_STAND_IDLE] = {ACT_IDLE_PISTOL, ACT_IDLE_ANGRY_PISTOL},\n\t\t[ACT_MP_CROUCH_IDLE] = {ACT_COVER_LOW, ACT_RANGE_AIM_SMG1_LOW},\n\t\t[ACT_MP_WALK] = {ACT_WALK, ACT_WALK_AIM_PISTOL},\n\t\t[ACT_MP_CROUCHWALK] = {ACT_WALK_CROUCH, ACT_WALK_CROUCH_AIM_RIFLE},\n\t\t[ACT_MP_RUN] = {ACT_RUN, ACT_RUN_AIM_PISTOL},\n\t\t[ACT_LAND] = {ACT_RESET, ACT_RESET},\n\t\tattack = ACT_GESTURE_RANGE_ATTACK_PISTOL,\n\t\treload = ACT_RELOAD_PISTOL\n\t},\n\tsmg = {\n\t\t[ACT_MP_STAND_IDLE] = {ACT_IDLE_SMG1_RELAXED, ACT_IDLE_ANGRY_SMG1},\n\t\t[ACT_MP_CROUCH_IDLE] = {ACT_COVER_LOW, ACT_RANGE_AIM_SMG1_LOW},\n\t\t[ACT_MP_WALK] = {ACT_WALK_RIFLE_RELAXED, ACT_WALK_AIM_RIFLE_STIMULATED},\n\t\t[ACT_MP_CROUCHWALK] = {ACT_WALK_CROUCH_RIFLE, ACT_WALK_CROUCH_AIM_RIFLE},\n\t\t[ACT_MP_RUN] = {ACT_RUN_RIFLE_RELAXED, ACT_RUN_AIM_RIFLE_STIMULATED},\n\t\t[ACT_LAND] = {ACT_RESET, ACT_RESET},\n\t\tattack = ACT_GESTURE_RANGE_ATTACK_SMG1,\n\t\treload = ACT_GESTURE_RELOAD_SMG1\n\t},\n\tshotgun = {\n\t\t[ACT_MP_STAND_IDLE] = {ACT_IDLE_SHOTGUN_RELAXED, ACT_IDLE_ANGRY_SMG1},\n\t\t[ACT_MP_CROUCH_IDLE] = {ACT_COVER_LOW, ACT_RANGE_AIM_SMG1_LOW},\n\t\t[ACT_MP_WALK] = {ACT_WALK_RIFLE_RELAXED, ACT_WALK_AIM_RIFLE_STIMULATED},\n\t\t[ACT_MP_CROUCHWALK] = {ACT_WALK_CROUCH_RIFLE, ACT_WALK_CROUCH_AIM_RIFLE},\n\t\t[ACT_MP_RUN] = {ACT_RUN_RIFLE_RELAXED, ACT_RUN_AIM_RIFLE_STIMULATED},\n\t\t[ACT_LAND] = {ACT_RESET, ACT_RESET},\n\t\tattack = ACT_GESTURE_RANGE_ATTACK_SHOTGUN\n\t},\n\tgrenade = {\n\t\t[ACT_MP_STAND_IDLE] = {ACT_IDLE, ACT_IDLE_MANNEDGUN},\n\t\t[ACT_MP_CROUCH_IDLE] = {ACT_COVER_LOW, ACT_RANGE_AIM_SMG1_LOW},\n\t\t[ACT_MP_WALK] = {ACT_WALK, ACT_WALK_AIM_PISTOL},\n\t\t[ACT_MP_CROUCHWALK] = {ACT_WALK_CROUCH, ACT_WALK_CROUCH_AIM_RIFLE},\n\t\t[ACT_MP_RUN] = {ACT_RUN, ACT_RUN_AIM_PISTOL},\n\t\t[ACT_LAND] = {ACT_RESET, ACT_RESET},\n\t\tattack = ACT_RANGE_ATTACK_THROW\n\t},\n\tmelee = {\n\t\t[ACT_MP_STAND_IDLE] = {ACT_IDLE, ACT_IDLE_MANNEDGUN},\n\t\t[ACT_MP_CROUCH_IDLE] = {ACT_COVER_LOW, ACT_COVER_LOW},\n\t\t[ACT_MP_WALK] = {ACT_WALK, ACT_WALK_AIM_RIFLE},\n\t\t[ACT_MP_CROUCHWALK] = {ACT_WALK_CROUCH, ACT_WALK_CROUCH},\n\t\t[ACT_MP_RUN] = {ACT_RUN, ACT_RUN},\n\t\t[ACT_LAND] = {ACT_RESET, ACT_RESET},\n\t\tattack = ACT_MELEE_ATTACK_SWING\n\t},\n\tglide = ACT_GLIDE,\n\tvehicle = ix.anim.citizen_male.vehicle\n}\nix.anim.metrocop = {\n\tnormal = {\n\t\t[ACT_MP_STAND_IDLE] = {ACT_IDLE, ACT_IDLE_ANGRY_SMG1},\n\t\t[ACT_MP_CROUCH_IDLE] = {ACT_COVER_PISTOL_LOW, ACT_COVER_SMG1_LOW},\n\t\t[ACT_MP_WALK] = {ACT_WALK, ACT_WALK_AIM_RIFLE},\n\t\t[ACT_MP_CROUCHWALK] = {ACT_WALK_CROUCH, ACT_WALK_CROUCH},\n\t\t[ACT_MP_RUN] = {ACT_RUN, ACT_RUN},\n\t\t[ACT_LAND] = {ACT_RESET, ACT_RESET}\n\t},\n\tpistol = {\n\t\t[ACT_MP_STAND_IDLE] = {ACT_IDLE_PISTOL, ACT_IDLE_ANGRY_PISTOL},\n\t\t[ACT_MP_CROUCH_IDLE] = {ACT_COVER_PISTOL_LOW, ACT_COVER_PISTOL_LOW},\n\t\t[ACT_MP_WALK] = {ACT_WALK_PISTOL, ACT_WALK_AIM_PISTOL},\n\t\t[ACT_MP_CROUCHWALK] = {ACT_WALK_CROUCH, ACT_WALK_CROUCH},\n\t\t[ACT_MP_RUN] = {ACT_RUN_PISTOL, ACT_RUN_AIM_PISTOL},\n\t\t[ACT_LAND] = {ACT_RESET, ACT_RESET},\n\t\tattack = ACT_GESTURE_RANGE_ATTACK_PISTOL,\n\t\treload = ACT_GESTURE_RELOAD_PISTOL\n\t},\n\tsmg = {\n\t\t[ACT_MP_STAND_IDLE] = {ACT_IDLE_SMG1, ACT_IDLE_ANGRY_SMG1},\n\t\t[ACT_MP_CROUCH_IDLE] = {ACT_COVER_SMG1_LOW, ACT_COVER_SMG1_LOW},\n\t\t[ACT_MP_WALK] = {ACT_WALK_RIFLE, ACT_WALK_AIM_RIFLE},\n\t\t[ACT_MP_CROUCHWALK] = {ACT_WALK_CROUCH, ACT_WALK_CROUCH},\n\t\t[ACT_MP_RUN] = {ACT_RUN_RIFLE, ACT_RUN_AIM_RIFLE},\n\t\t[ACT_LAND] = {ACT_RESET, ACT_RESET}\n\t},\n\tshotgun = {\n\t\t[ACT_MP_STAND_IDLE] = {ACT_IDLE_SMG1, ACT_IDLE_ANGRY_SMG1},\n\t\t[ACT_MP_CROUCH_IDLE] = {ACT_COVER_SMG1_LOW, ACT_COVER_SMG1_LOW},\n\t\t[ACT_MP_WALK] = {ACT_WALK_RIFLE, ACT_WALK_AIM_RIFLE},\n\t\t[ACT_MP_CROUCHWALK] = {ACT_WALK_CROUCH, ACT_WALK_CROUCH},\n\t\t[ACT_MP_RUN] = {ACT_RUN_RIFLE, ACT_RUN_AIM_RIFLE},\n\t\t[ACT_LAND] = {ACT_RESET, ACT_RESET}\n\t},\n\tgrenade = {\n\t\t[ACT_MP_STAND_IDLE] = {ACT_IDLE, ACT_IDLE_ANGRY_MELEE},\n\t\t[ACT_MP_CROUCH_IDLE] = {ACT_COVER_PISTOL_LOW, ACT_COVER_PISTOL_LOW},\n\t\t[ACT_MP_WALK] = {ACT_WALK, ACT_WALK_ANGRY},\n\t\t[ACT_MP_CROUCHWALK] = {ACT_WALK_CROUCH, ACT_WALK_CROUCH},\n\t\t[ACT_MP_RUN] = {ACT_RUN, ACT_RUN},\n\t\t[ACT_LAND] = {ACT_RESET, ACT_RESET},\n\t\tattack = ACT_COMBINE_THROW_GRENADE\n\t},\n\tmelee = {\n\t\t[ACT_MP_STAND_IDLE] = {ACT_IDLE, ACT_IDLE_ANGRY_MELEE},\n\t\t[ACT_MP_CROUCH_IDLE] = {ACT_COVER_PISTOL_LOW, ACT_COVER_PISTOL_LOW},\n\t\t[ACT_MP_WALK] = {ACT_WALK, ACT_WALK_ANGRY},\n\t\t[ACT_MP_CROUCHWALK] = {ACT_WALK_CROUCH, ACT_WALK_CROUCH},\n\t\t[ACT_MP_RUN] = {ACT_RUN, ACT_RUN},\n\t\t[ACT_LAND] = {ACT_RESET, ACT_RESET},\n\t\tattack = ACT_MELEE_ATTACK_SWING_GESTURE\n\t},\n\tglide = ACT_GLIDE,\n\tvehicle = {\n\t\tchair = {ACT_COVER_PISTOL_LOW, Vector(5, 0, -5)},\n\t\t[\"prop_vehicle_airboat\"] = {ACT_COVER_PISTOL_LOW, Vector(10, 0, 0)},\n\t\t[\"prop_vehicle_jeep\"] = {ACT_COVER_PISTOL_LOW, Vector(18, -2, 4)},\n\t\t[\"prop_vehicle_prisoner_pod\"] = {ACT_IDLE, Vector(-4, -0.5, 0)}\n\t}\n}\nix.anim.overwatch = {\n\tnormal = {\n\t\t[ACT_MP_STAND_IDLE] = {\"idle_unarmed\", ACT_IDLE_ANGRY},\n\t\t[ACT_MP_CROUCH_IDLE] = {ACT_CROUCHIDLE, ACT_CROUCHIDLE},\n\t\t[ACT_MP_WALK] = {\"walkunarmed_all\", ACT_WALK_RIFLE},\n\t\t[ACT_MP_CROUCHWALK] = {ACT_WALK_CROUCH_RIFLE, ACT_WALK_CROUCH_RIFLE},\n\t\t[ACT_MP_RUN] = {ACT_RUN_AIM_RIFLE, ACT_RUN_AIM_RIFLE},\n\t\t[ACT_LAND] = {ACT_RESET, ACT_RESET}\n\t},\n\tpistol = {\n\t\t[ACT_MP_STAND_IDLE] = {\"idle_unarmed\", ACT_IDLE_ANGRY_SMG1},\n\t\t[ACT_MP_CROUCH_IDLE] = {ACT_CROUCHIDLE, ACT_CROUCHIDLE},\n\t\t[ACT_MP_WALK] = {\"walkunarmed_all\", ACT_WALK_RIFLE},\n\t\t[ACT_MP_CROUCHWALK] = {ACT_WALK_CROUCH_RIFLE, ACT_WALK_CROUCH_RIFLE},\n\t\t[ACT_MP_RUN] = {ACT_RUN_AIM_RIFLE, ACT_RUN_AIM_RIFLE},\n\t\t[ACT_LAND] = {ACT_RESET, ACT_RESET}\n\t},\n\tsmg = {\n\t\t[ACT_MP_STAND_IDLE] = {ACT_IDLE_SMG1, ACT_IDLE_ANGRY_SMG1},\n\t\t[ACT_MP_CROUCH_IDLE] = {ACT_CROUCHIDLE, ACT_CROUCHIDLE},\n\t\t[ACT_MP_WALK] = {ACT_WALK_RIFLE, ACT_WALK_AIM_RIFLE},\n\t\t[ACT_MP_CROUCHWALK] = {ACT_WALK_CROUCH_RIFLE, ACT_WALK_CROUCH_RIFLE},\n\t\t[ACT_MP_RUN] = {ACT_RUN_RIFLE, ACT_RUN_AIM_RIFLE},\n\t\t[ACT_LAND] = {ACT_RESET, ACT_RESET}\n\t},\n\tshotgun = {\n\t\t[ACT_MP_STAND_IDLE] = {ACT_IDLE_SMG1, ACT_IDLE_ANGRY_SHOTGUN},\n\t\t[ACT_MP_CROUCH_IDLE] = {ACT_CROUCHIDLE, ACT_CROUCHIDLE},\n\t\t[ACT_MP_WALK] = {ACT_WALK_RIFLE, ACT_WALK_AIM_SHOTGUN},\n\t\t[ACT_MP_CROUCHWALK] = {ACT_WALK_CROUCH_RIFLE, ACT_WALK_CROUCH_RIFLE},\n\t\t[ACT_MP_RUN] = {ACT_RUN_RIFLE, ACT_RUN_AIM_SHOTGUN},\n\t\t[ACT_LAND] = {ACT_RESET, ACT_RESET}\n\t},\n\tgrenade = {\n\t\t[ACT_MP_STAND_IDLE] = {\"idle_unarmed\", ACT_IDLE_ANGRY},\n\t\t[ACT_MP_CROUCH_IDLE] = {ACT_CROUCHIDLE, ACT_CROUCHIDLE},\n\t\t[ACT_MP_WALK] = {\"walkunarmed_all\", ACT_WALK_RIFLE},\n\t\t[ACT_MP_CROUCHWALK] = {ACT_WALK_CROUCH_RIFLE, ACT_WALK_CROUCH_RIFLE},\n\t\t[ACT_MP_RUN] = {ACT_RUN_AIM_RIFLE, ACT_RUN_AIM_RIFLE},\n\t\t[ACT_LAND] = {ACT_RESET, ACT_RESET}\n\t},\n\tmelee = {\n\t\t[ACT_MP_STAND_IDLE] = {\"idle_unarmed\", ACT_IDLE_ANGRY},\n\t\t[ACT_MP_CROUCH_IDLE] = {ACT_CROUCHIDLE, ACT_CROUCHIDLE},\n\t\t[ACT_MP_WALK] = {\"walkunarmed_all\", ACT_WALK_RIFLE},\n\t\t[ACT_MP_CROUCHWALK] = {ACT_WALK_CROUCH_RIFLE, ACT_WALK_CROUCH_RIFLE},\n\t\t[ACT_MP_RUN] = {ACT_RUN_AIM_RIFLE, ACT_RUN_AIM_RIFLE},\n\t\t[ACT_LAND] = {ACT_RESET, ACT_RESET},\n\t\tattack = ACT_MELEE_ATTACK_SWING_GESTURE\n\t},\n\tglide = ACT_GLIDE\n}\nix.anim.vortigaunt = {\n\tmelee = {\n\t\t[\"attack\"] = ACT_MELEE_ATTACK1,\n\t\t[ACT_MP_STAND_IDLE] = {ACT_IDLE, \"ActionIdle\"},\n\t\t[ACT_MP_CROUCH_IDLE] = {\"crouchidle\", \"crouchidle\"},\n\t\t[ACT_MP_RUN] = {ACT_RUN, ACT_RUN_AIM},\n\t\t[ACT_MP_CROUCHWALK] = {ACT_WALK, ACT_WALK},\n\t\t[ACT_MP_WALK] = {ACT_WALK, ACT_WALK_AIM},\n\t},\n\tgrenade = {\n\t\t[\"attack\"] = ACT_MELEE_ATTACK1,\n\t\t[ACT_MP_STAND_IDLE] = {ACT_IDLE, \"ActionIdle\"},\n\t\t[ACT_MP_CROUCH_IDLE] = {\"crouchidle\", \"crouchidle\"},\n\t\t[ACT_MP_RUN] = {ACT_RUN, ACT_RUN},\n\t\t[ACT_MP_CROUCHWALK] = {ACT_WALK, ACT_WALK},\n\t\t[ACT_MP_WALK] = {ACT_WALK, ACT_WALK}\n\t},\n\tnormal = {\n\t\t[ACT_MP_STAND_IDLE] = {ACT_IDLE, ACT_IDLE_ANGRY},\n\t\t[ACT_MP_CROUCH_IDLE] = {\"crouchidle\", \"crouchidle\"},\n\t\t[ACT_MP_RUN] = {ACT_RUN, ACT_RUN_AIM},\n\t\t[ACT_MP_CROUCHWALK] = {ACT_WALK, ACT_WALK},\n\t\t[ACT_MP_WALK] = {ACT_WALK, ACT_WALK_AIM},\n\t\t[\"attack\"] = ACT_MELEE_ATTACK1\n\t},\n\tpistol = {\n\t\t[ACT_MP_STAND_IDLE] = {ACT_IDLE, \"TCidlecombat\"},\n\t\t[ACT_MP_CROUCH_IDLE] = {\"crouchidle\", \"crouchidle\"},\n\t\t[\"reload\"] = ACT_IDLE,\n\t\t[ACT_MP_RUN] = {ACT_RUN, \"run_all_TC\"},\n\t\t[ACT_MP_CROUCHWALK] = {ACT_WALK, ACT_WALK},\n\t\t[ACT_MP_WALK] = {ACT_WALK, \"Walk_all_TC\"}\n\t},\n\tshotgun = {\n\t\t[ACT_MP_STAND_IDLE] = {ACT_IDLE, \"TCidlecombat\"},\n\t\t[ACT_MP_CROUCH_IDLE] = {\"crouchidle\", \"crouchidle\"},\n\t\t[\"reload\"] = ACT_IDLE,\n\t\t[ACT_MP_RUN] = {ACT_RUN, \"run_all_TC\"},\n\t\t[ACT_MP_CROUCHWALK] = {ACT_WALK, ACT_WALK},\n\t\t[ACT_MP_WALK] = {ACT_WALK, \"Walk_all_TC\"}\n\t},\n\tsmg = {\n\t\t[ACT_MP_STAND_IDLE] = {ACT_IDLE, \"TCidlecombat\"},\n\t\t[ACT_MP_CROUCH_IDLE] = {\"crouchidle\", \"crouchidle\"},\n\t\t[\"reload\"] = ACT_IDLE,\n\t\t[ACT_MP_RUN] = {ACT_RUN, \"run_all_TC\"},\n\t\t[ACT_MP_CROUCHWALK] = {ACT_WALK, ACT_WALK},\n\t\t[ACT_MP_WALK] = {ACT_WALK, \"Walk_all_TC\"}\n\t},\n\tbeam = {\n\t\t[ACT_MP_STAND_IDLE] = {ACT_IDLE, ACT_IDLE_ANGRY},\n\t\t[ACT_MP_CROUCH_IDLE] = {\"crouchidle\", \"crouchidle\"},\n\t\t[ACT_MP_RUN] = {ACT_RUN, ACT_RUN_AIM},\n\t\t[ACT_MP_CROUCHWALK] = {ACT_WALK, ACT_WALK},\n\t\t[ACT_MP_WALK] = {ACT_WALK, ACT_WALK_AIM},\n\t\t[\"attack\"] = ACT_GESTURE_RANGE_ATTACK1,\n\t\t[\"reload\"] = ACT_IDLE,\n\t\t[\"glide\"] = {ACT_RUN, ACT_RUN}\n\t},\n\tglide = \"jump_holding_glide\"\n}\nix.anim.player = {\n\tnormal = {\n\t\t[ACT_MP_STAND_IDLE] = ACT_HL2MP_IDLE,\n\t\t[ACT_MP_CROUCH_IDLE] = ACT_HL2MP_IDLE_CROUCH,\n\t\t[ACT_MP_WALK] = ACT_HL2MP_WALK,\n\t\t[ACT_MP_RUN] = ACT_HL2MP_RUN,\n\t\t[ACT_LAND] = {ACT_RESET, ACT_RESET}\n\t},\n\tpassive = {\n\t\t[ACT_MP_STAND_IDLE] = ACT_HL2MP_IDLE_PASSIVE,\n\t\t[ACT_MP_WALK] = ACT_HL2MP_WALK_PASSIVE,\n\t\t[ACT_MP_CROUCHWALK] = ACT_HL2MP_WALK_CROUCH_PASSIVE,\n\t\t[ACT_MP_RUN] = ACT_HL2MP_RUN_PASSIVE,\n\t\t[ACT_LAND] = {ACT_RESET, ACT_RESET}\n\t}\n}\nix.anim.zombie = {\n\t[ACT_MP_STAND_IDLE] = ACT_HL2MP_IDLE_ZOMBIE,\n\t[ACT_MP_CROUCH_IDLE] = ACT_HL2MP_IDLE_CROUCH_ZOMBIE,\n\t[ACT_MP_CROUCHWALK] = ACT_HL2MP_WALK_CROUCH_ZOMBIE_01,\n\t[ACT_MP_WALK] = ACT_HL2MP_WALK_ZOMBIE_02,\n\t[ACT_MP_RUN] = ACT_HL2MP_RUN_ZOMBIE,\n\t[ACT_LAND] = {ACT_RESET, ACT_RESET}\n}\nix.anim.fastZombie = {\n\t[ACT_MP_STAND_IDLE] = ACT_HL2MP_WALK_ZOMBIE,\n\t[ACT_MP_CROUCH_IDLE] = ACT_HL2MP_IDLE_CROUCH_ZOMBIE,\n\t[ACT_MP_CROUCHWALK] = ACT_HL2MP_WALK_CROUCH_ZOMBIE_05,\n\t[ACT_MP_WALK] = ACT_HL2MP_WALK_ZOMBIE_06,\n\t[ACT_MP_RUN] = ACT_HL2MP_RUN_ZOMBIE_FAST,\n\t[ACT_LAND] = {ACT_RESET, ACT_RESET}\n}\n\nlocal translations = {}\n\n--- Sets a model's animation class.\n-- @realm shared\n-- @string model Model name to set the animation class for\n-- @string class Animation class to assign to the model\n-- @usage ix.anim.SetModelClass(\"models/police.mdl\", \"metrocop\")\nfunction ix.anim.SetModelClass(model, class)\n\tif (!ix.anim[class]) then\n\t\terror(\"'\" .. tostring(class) .. \"' is not a valid animation class!\")\n\tend\n\n\ttranslations[model:lower()] = class\nend\n\n--- Gets a model's animation class.\n-- @realm shared\n-- @string model Model to get the animation class for\n-- @treturn[1] string Animation class of the model\n-- @treturn[2] nil If there was no animation associated with the given model\n-- @usage ix.anim.GetModelClass(\"models/police.mdl\")\n-- > metrocop\nfunction ix.anim.GetModelClass(model)\n\tmodel = string.lower(model)\n\tlocal class = translations[model]\n\n\tif (!class and string.find(model, \"/player\")) then\n\t\treturn \"player\"\n\tend\n\n\tclass = class or \"citizen_male\"\n\n\tif (class == \"citizen_male\" and (\n\t\tstring.find(model, \"female\") or\n\t\tstring.find(model, \"alyx\") or\n\t\tstring.find(model, \"mossman\"))) then\n\t\tclass = \"citizen_female\"\n\tend\n\n\treturn class\nend\n\nix.anim.SetModelClass(\"models/police.mdl\", \"metrocop\")\nix.anim.SetModelClass(\"models/combine_super_soldier.mdl\", \"overwatch\")\nix.anim.SetModelClass(\"models/combine_soldier_prisonGuard.mdl\", \"overwatch\")\nix.anim.SetModelClass(\"models/combine_soldier.mdl\", \"overwatch\")\nix.anim.SetModelClass(\"models/vortigaunt.mdl\", \"vortigaunt\")\nix.anim.SetModelClass(\"models/vortigaunt_blue.mdl\", \"vortigaunt\")\nix.anim.SetModelClass(\"models/vortigaunt_doctor.mdl\", \"vortigaunt\")\nix.anim.SetModelClass(\"models/vortigaunt_slave.mdl\", \"vortigaunt\")\n\nif (SERVER) then\n\tutil.AddNetworkString(\"ixSequenceSet\")\n\tutil.AddNetworkString(\"ixSequenceReset\")\n\n\tlocal playerMeta = FindMetaTable(\"Player\")\n\n\t--- Player anim methods\n\t-- @classmod Player\n\n\t--- Forces this player's model to play an animation sequence. It also prevents the player from firing their weapon while the\n\t-- animation is playing.\n\t-- @realm server\n\t-- @string sequence Name of the animation sequence to play\n\t-- @func[opt=nil] callback Function to call when the animation finishes. This is also called immediately if the animation\n\t-- fails to play\n\t-- @number[opt=nil] time How long to play the animation for. This defaults to the duration of the animation\n\t-- @bool[opt=false] bNoFreeze Whether or not to avoid freezing this player in place while the animation is playing\n\t-- @see LeaveSequence\n\tfunction playerMeta:ForceSequence(sequence, callback, time, bNoFreeze)\n\t\thook.Run(\"PlayerEnterSequence\", self, sequence, callback, time, bNoFreeze)\n\n\t\tif (!sequence) then\n\t\t\tnet.Start(\"ixSequenceReset\")\n\t\t\t\tnet.WriteEntity(self)\n\t\t\tnet.Broadcast()\n\n\t\t\treturn\n\t\tend\n\n\t\tsequence = self:LookupSequence(tostring(sequence))\n\n\t\tif (sequence and sequence > 0) then\n\t\t\ttime = time or self:SequenceDuration(sequence)\n\n\t\t\tself.ixCouldShoot = self:GetNetVar(\"canShoot\", false)\n\t\t\tself.ixSeqCallback = callback\n\t\t\tself:SetCycle(0)\n\t\t\tself:SetPlaybackRate(1)\n\t\t\tself:SetNetVar(\"forcedSequence\", sequence)\n\t\t\tself:SetNetVar(\"canShoot\", false)\n\n\t\t\tif (!bNoFreeze) then\n\t\t\t\tself:SetMoveType(MOVETYPE_NONE)\n\t\t\tend\n\n\t\t\tif (time > 0) then\n\t\t\t\ttimer.Create(\"ixSeq\"..self:EntIndex(), time, 1, function()\n\t\t\t\t\tif (IsValid(self)) then\n\t\t\t\t\t\tself:LeaveSequence()\n\t\t\t\t\tend\n\t\t\t\tend)\n\t\t\tend\n\n\t\t\tnet.Start(\"ixSequenceSet\")\n\t\t\t\tnet.WriteEntity(self)\n\t\t\tnet.Broadcast()\n\n\t\t\treturn time\n\t\telseif (callback) then\n\t\t\tcallback()\n\t\tend\n\n\t\treturn false\n\tend\n\n\t--- Forcefully stops this player's model from playing an animation that was started by `ForceSequence`.\n\t-- @realm server\n\tfunction playerMeta:LeaveSequence()\n\t\thook.Run(\"PlayerLeaveSequence\", self)\n\n\t\tnet.Start(\"ixSequenceReset\")\n\t\t\tnet.WriteEntity(self)\n\t\tnet.Broadcast()\n\n\t\tself:SetNetVar(\"canShoot\", self.ixCouldShoot)\n\t\tself:SetNetVar(\"forcedSequence\", nil)\n\t\tself:SetMoveType(MOVETYPE_WALK)\n\t\tself.ixCouldShoot = nil\n\n\t\tif (self.ixSeqCallback) then\n\t\t\tself:ixSeqCallback()\n\t\tend\n\tend\nelse\n\tnet.Receive(\"ixSequenceSet\", function()\n\t\tlocal entity = net.ReadEntity()\n\n\t\tif (IsValid(entity)) then\n\t\t\thook.Run(\"PlayerEnterSequence\", entity)\n\t\tend\n\tend)\n\n\tnet.Receive(\"ixSequenceReset\", function()\n\t\tlocal entity = net.ReadEntity()\n\n\t\tif (IsValid(entity)) then\n\t\t\thook.Run(\"PlayerLeaveSequence\", entity)\n\t\tend\n\tend)\nend\n"
  },
  {
    "path": "gamemode/core/libs/sh_attribs.lua",
    "content": "\n-- @module ix.attributes\n\nif (!ix.char) then\n\tinclude(\"sh_character.lua\")\nend\n\nix.attributes = ix.attributes or {}\nix.attributes.list = ix.attributes.list or {}\n\nfunction ix.attributes.LoadFromDir(directory)\n\tfor _, v in ipairs(file.Find(directory..\"/*.lua\", \"LUA\")) do\n\t\tlocal niceName = v:sub(4, -5)\n\n\t\tATTRIBUTE = ix.attributes.list[niceName] or {}\n\t\t\tif (PLUGIN) then\n\t\t\t\tATTRIBUTE.plugin = PLUGIN.uniqueID\n\t\t\tend\n\n\t\t\tix.util.Include(directory..\"/\"..v)\n\n\t\t\tATTRIBUTE.name = ATTRIBUTE.name or \"Unknown\"\n\t\t\tATTRIBUTE.description = ATTRIBUTE.description or \"No description availalble.\"\n\n\t\t\tix.attributes.list[niceName] = ATTRIBUTE\n\t\tATTRIBUTE = nil\n\tend\nend\n\nfunction ix.attributes.Setup(client)\n\tlocal character = client:GetCharacter()\n\n\tif (character) then\n\t\tfor k, v in pairs(ix.attributes.list) do\n\t\t\tif (v.OnSetup) then\n\t\t\t\tv:OnSetup(client, character:GetAttribute(k, 0))\n\t\t\tend\n\t\tend\n\tend\nend\n\ndo\n\t--- Character attribute methods\n\t-- @classmod Character\n\tlocal charMeta = ix.meta.character\n\n\tif (SERVER) then\n\t\tutil.AddNetworkString(\"ixAttributeUpdate\")\n\n\t\t--- Increments one of this character's attributes by the given amount.\n\t\t-- @realm server\n\t\t-- @string key Name of the attribute to update\n\t\t-- @number value Amount to add to the attribute\n\t\tfunction charMeta:UpdateAttrib(key, value)\n\t\t\tlocal attribute = ix.attributes.list[key]\n\t\t\tlocal client = self:GetPlayer()\n\n\t\t\tif (attribute) then\n\t\t\t\tlocal attrib = self:GetAttributes()\n\n\t\t\t\tattrib[key] = math.min((attrib[key] or 0) + value, attribute.maxValue or ix.config.Get(\"maxAttributes\", 100))\n\n\t\t\t\tif (IsValid(client)) then\n\t\t\t\t\tnet.Start(\"ixAttributeUpdate\")\n\t\t\t\t\t\tnet.WriteUInt(self:GetID(), 32)\n\t\t\t\t\t\tnet.WriteString(key)\n\t\t\t\t\t\tnet.WriteFloat(attrib[key])\n\t\t\t\t\tnet.Send(client)\n\n\t\t\t\t\tif (attribute.Setup) then\n\t\t\t\t\t\tattribute.Setup(attrib[key])\n\t\t\t\t\tend\n\t\t\t\tend\n\n\t\t\t\tself:SetAttributes(attrib)\n\t\t\tend\n\n\t\t\thook.Run(\"CharacterAttributeUpdated\", client, self, key, value)\n\t\tend\n\n\t\t--- Sets the value of an attribute for this character.\n\t\t-- @realm server\n\t\t-- @string key Name of the attribute to update\n\t\t-- @number value New value for the attribute\n\t\tfunction charMeta:SetAttrib(key, value)\n\t\t\tlocal attribute = ix.attributes.list[key]\n\t\t\tlocal client = self:GetPlayer()\n\n\t\t\tif (attribute) then\n\t\t\t\tlocal attrib = self:GetAttributes()\n\n\t\t\t\tattrib[key] = value\n\n\t\t\t\tif (IsValid(client)) then\n\t\t\t\t\tnet.Start(\"ixAttributeUpdate\")\n\t\t\t\t\t\tnet.WriteUInt(self:GetID(), 32)\n\t\t\t\t\t\tnet.WriteString(key)\n\t\t\t\t\t\tnet.WriteFloat(attrib[key])\n\t\t\t\t\tnet.Send(client)\n\n\t\t\t\t\tif (attribute.Setup) then\n\t\t\t\t\t\tattribute.Setup(attrib[key])\n\t\t\t\t\tend\n\t\t\t\tend\n\n\t\t\t\tself:SetAttributes(attrib)\n\t\t\tend\n\n\t\t\thook.Run(\"CharacterAttributeUpdated\", client, self, key, value)\n\t\tend\n\n\t\t--- Temporarily increments one of this character's attributes. Useful for things like consumable items.\n\t\t-- @realm server\n\t\t-- @string boostID Unique ID to use for the boost to remove it later\n\t\t-- @string attribID Name of the attribute to boost\n\t\t-- @number boostAmount Amount to increase the attribute by\n\t\tfunction charMeta:AddBoost(boostID, attribID, boostAmount)\n\t\t\tlocal boosts = self:GetVar(\"boosts\", {})\n\n\t\t\tboosts[attribID] = boosts[attribID] or {}\n\t\t\tboosts[attribID][boostID] = boostAmount\n\n\t\t\thook.Run(\"CharacterAttributeBoosted\", self:GetPlayer(), self, attribID, boostID, boostAmount)\n\n\t\t\treturn self:SetVar(\"boosts\", boosts, nil, self:GetPlayer())\n\t\tend\n\n\t\t--- Removes a temporary boost from this character.\n\t\t-- @realm server\n\t\t-- @string boostID Unique ID of the boost to remove\n\t\t-- @string attribID Name of the attribute that was boosted\n\t\tfunction charMeta:RemoveBoost(boostID, attribID)\n\t\t\tlocal boosts = self:GetVar(\"boosts\", {})\n\n\t\t\tboosts[attribID] = boosts[attribID] or {}\n\t\t\tboosts[attribID][boostID] = nil\n\n\t\t\thook.Run(\"CharacterAttributeBoosted\", self:GetPlayer(), self, attribID, boostID, true)\n\n\t\t\treturn self:SetVar(\"boosts\", boosts, nil, self:GetPlayer())\n\t\tend\n\telse\n\t\tnet.Receive(\"ixAttributeUpdate\", function()\n\t\t\tlocal id = net.ReadUInt(32)\n\t\t\tlocal character = ix.char.loaded[id]\n\n\t\t\tif (character) then\n\t\t\t\tlocal key = net.ReadString()\n\t\t\t\tlocal value = net.ReadFloat()\n\n\t\t\t\tcharacter:GetAttributes()[key] = value\n\t\t\tend\n\t\tend)\n\tend\n\n\t--- Returns all boosts that this character has for the given attribute. This is only valid on the server and owning client.\n\t-- @realm shared\n\t-- @string attribID Name of the attribute to find boosts for\n\t-- @treturn[1] table Table of boosts that this character has for the attribute\n\t-- @treturn[2] nil If the character has no boosts for the given attribute\n\tfunction charMeta:GetBoost(attribID)\n\t\tlocal boosts = self:GetBoosts()\n\n\t\treturn boosts[attribID]\n\tend\n\n\t--- Returns all boosts that this character has. This is only valid on the server and owning client.\n\t-- @realm shared\n\t-- @treturn table Table of boosts this character has\n\tfunction charMeta:GetBoosts()\n\t\treturn self:GetVar(\"boosts\", {})\n\tend\n\n\t--- Returns the current value of an attribute. This is only valid on the server and owning client.\n\t-- @realm shared\n\t-- @string key Name of the attribute to get\n\t-- @number default Value to return if the attribute doesn't exist\n\t-- @treturn number Value of the attribute\n\tfunction charMeta:GetAttribute(key, default)\n\t\tlocal att = self:GetAttributes()[key] or default\n\t\tlocal boosts = self:GetBoosts()[key]\n\n\t\tif (boosts) then\n\t\t\tfor _, v in pairs(boosts) do\n\t\t\t\tatt = att + v\n\t\t\tend\n\t\tend\n\n\t\treturn att\n\tend\nend\n"
  },
  {
    "path": "gamemode/core/libs/sh_business.lua",
    "content": "\nif (SERVER) then\n\tutil.AddNetworkString(\"ixBusinessBuy\")\n\tutil.AddNetworkString(\"ixBusinessResponse\")\n\tutil.AddNetworkString(\"ixShipmentUse\")\n\tutil.AddNetworkString(\"ixShipmentOpen\")\n\tutil.AddNetworkString(\"ixShipmentClose\")\n\n\tnet.Receive(\"ixBusinessBuy\", function(length, client)\n\t\tif (client.ixNextBusiness and client.ixNextBusiness > CurTime()) then\n\t\t\tclient:NotifyLocalized(\"businessTooFast\")\n\t\t\treturn\n\t\tend\n\n\t\tlocal char = client:GetCharacter()\n\n\t\tif (!char) then\n\t\t\treturn\n\t\tend\n\n\t\tlocal indicies = net.ReadUInt(8)\n\t\tlocal items = {}\n\n\t\tfor _ = 1, indicies do\n\t\t\titems[net.ReadString()] = net.ReadUInt(8)\n\t\tend\n\n\t\tif (table.IsEmpty(items)) then\n\t\t\treturn\n\t\tend\n\n\t\tlocal cost = 0\n\n\t\tfor k, v in pairs(items) do\n\t\t\tlocal itemTable = ix.item.list[k]\n\n\t\t\tif (itemTable and hook.Run(\"CanPlayerUseBusiness\", client, k) != false) then\n\t\t\t\tlocal amount = math.Clamp(tonumber(v) or 0, 0, 10)\n\t\t\t\titems[k] = amount\n\n\t\t\t\tif (amount == 0) then\n\t\t\t\t\titems[k] = nil\n\t\t\t\telse\n\t\t\t\t\tcost = cost + (amount * (itemTable.price or 0))\n\t\t\t\tend\n\t\t\telse\n\t\t\t\titems[k] = nil\n\t\t\tend\n\t\tend\n\n\t\tif (table.IsEmpty(items)) then\n\t\t\treturn\n\t\tend\n\n\t\tif (char:HasMoney(cost)) then\n\t\t\tchar:TakeMoney(cost)\n\n\t\t\tlocal entity = ents.Create(\"ix_shipment\")\n\t\t\tentity:Spawn()\n\t\t\tentity:SetPos(client:GetItemDropPos(entity))\n\t\t\tentity:SetItems(items)\n\t\t\tentity:SetNetVar(\"owner\", char:GetID())\n\n\t\t\tlocal shipments = char:GetVar(\"charEnts\") or {}\n\t\t\ttable.insert(shipments, entity)\n\t\t\tchar:SetVar(\"charEnts\", shipments, true)\n\n\t\t\tnet.Start(\"ixBusinessResponse\")\n\t\t\tnet.Send(client)\n\n\t\t\thook.Run(\"CreateShipment\", client, entity)\n\n\t\t\tclient.ixNextBusiness = CurTime() + 0.5\n\t\tend\n\tend)\n\n\tnet.Receive(\"ixShipmentUse\", function(length, client)\n\t\tlocal uniqueID = net.ReadString()\n\t\tlocal drop = net.ReadBool()\n\n\t\tlocal entity = client.ixShipment\n\t\tlocal itemTable = ix.item.list[uniqueID]\n\n\t\tif (itemTable and IsValid(entity)) then\n\t\t\tif (entity:GetPos():Distance(client:GetPos()) > 128) then\n\t\t\t\tclient.ixShipment = nil\n\n\t\t\t\treturn\n\t\t\tend\n\n\t\t\tlocal amount = entity.items[uniqueID]\n\n\t\t\tif (amount and amount > 0) then\n\t\t\t\tif (entity.items[uniqueID] <= 0) then\n\t\t\t\t\tentity.items[uniqueID] = nil\n\t\t\t\tend\n\n\t\t\t\tif (drop) then\n\t\t\t\t\tix.item.Spawn(uniqueID, entity:GetPos() + Vector(0, 0, 16), function(item, itemEntity)\n\t\t\t\t\t\tif (IsValid(client)) then\n\t\t\t\t\t\t\titemEntity.ixSteamID = client:SteamID()\n\t\t\t\t\t\t\titemEntity.ixCharID = client:GetCharacter():GetID()\n\t\t\t\t\t\tend\n\t\t\t\t\tend)\n\t\t\t\telse\n\t\t\t\t\tlocal status, _ = client:GetCharacter():GetInventory():Add(uniqueID)\n\n\t\t\t\t\tif (!status) then\n\t\t\t\t\t\treturn client:NotifyLocalized(\"noFit\")\n\t\t\t\t\tend\n\t\t\t\tend\n\n\t\t\t\thook.Run(\"ShipmentItemTaken\", client, uniqueID, amount)\n\n\t\t\t\tentity.items[uniqueID] = entity.items[uniqueID] - 1\n\n\t\t\t\tif (entity:GetItemCount() < 1) then\n\t\t\t\t\tentity:GibBreakServer(Vector(0, 0, 0.5))\n\t\t\t\t\tentity:Remove()\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend)\n\n\tnet.Receive(\"ixShipmentClose\", function(length, client)\n\t\tlocal entity = client.ixShipment\n\n\t\tif (IsValid(entity)) then\n\t\t\tentity.ixInteractionDirty = false\n\t\t\tclient.ixShipment = nil\n\t\tend\n\tend)\nelse\n\tnet.Receive(\"ixShipmentOpen\", function()\n\t\tlocal entity = net.ReadEntity()\n\t\tlocal items = net.ReadTable()\n\n\t\tix.gui.shipment = vgui.Create(\"ixShipment\")\n\t\tix.gui.shipment:SetItems(entity, items)\n\tend)\nend\n"
  },
  {
    "path": "gamemode/core/libs/sh_character.lua",
    "content": "\n--[[--\nCharacter creation and management.\n\n**NOTE:** For the most part you shouldn't use this library unless you know what you're doing. You can very easily corrupt\ncharacter data using these functions!\n]]\n-- @module ix.char\n\nix.char = ix.char or {}\n\n--- Characters that are currently loaded into memory. This is **not** a table of characters that players are currently using.\n-- Characters are automatically loaded when a player joins the server. Entries are not cleared once the player disconnects, as\n-- some data is needed after the player has disconnected. Clients will also keep their own version of this table, so don't\n-- expect it to be the same as the server's.\n--\n-- The keys in this table are the IDs of characters, and the values are the `Character` objects that the ID corresponds to.\n-- @realm shared\n-- @table ix.char.loaded\n-- @usage print(ix.char.loaded[1])\n-- > character[1]\nix.char.loaded = ix.char.loaded or {}\n\n--- Variables that are stored on characters. This table is populated automatically by `ix.char.RegisterVar`.\n-- @realm shared\n-- @table ix.char.vars\n-- @usage print(ix.char.vars[\"name\"])\n-- > table: 0xdeadbeef\nix.char.vars = ix.char.vars or {}\n\n--- Functions similar to `ix.char.loaded`, but is serverside only. This contains a table of all loaded characters grouped by\n-- the SteamID64 of the player that owns them.\n-- @realm server\n-- @table ix.char.cache\nix.char.cache = ix.char.cache or {}\n\nix.util.Include(\"helix/gamemode/core/meta/sh_character.lua\")\n\nif (SERVER) then\n\t--- Creates a character object with its assigned properties and saves it to the database.\n\t-- @realm server\n\t-- @tab data Properties to assign to this character. If fields are missing from the table, then it will use the default\n\t-- value for that property\n\t-- @func callback Function to call after the character saves\n\tfunction ix.char.Create(data, callback)\n\t\tlocal timeStamp = math.floor(os.time())\n\n\t\tdata.money = data.money or ix.config.Get(\"defaultMoney\", 0)\n\t\tdata.schema = Schema and Schema.folder or \"helix\"\n\t\tdata.createTime = timeStamp\n\t\tdata.lastJoinTime = timeStamp\n\n\t\tlocal query = mysql:Insert(\"ix_characters\")\n\t\t\tquery:Insert(\"name\", data.name or \"\")\n\t\t\tquery:Insert(\"description\", data.description or \"\")\n\t\t\tquery:Insert(\"model\", data.model or \"models/error.mdl\")\n\t\t\tquery:Insert(\"schema\", Schema and Schema.folder or \"helix\")\n\t\t\tquery:Insert(\"create_time\", data.createTime)\n\t\t\tquery:Insert(\"last_join_time\", data.lastJoinTime)\n\t\t\tquery:Insert(\"steamid\", data.steamID)\n\t\t\tquery:Insert(\"faction\", data.faction or \"Unknown\")\n\t\t\tquery:Insert(\"money\", data.money)\n\t\t\tquery:Insert(\"data\", util.TableToJSON(data.data or {}))\n\t\t\tquery:Callback(function(result, status, lastID)\n\t\t\t\tlocal invQuery = mysql:Insert(\"ix_inventories\")\n\t\t\t\t\tinvQuery:Insert(\"character_id\", lastID)\n\t\t\t\t\tinvQuery:Callback(function(invResult, invStats, invLastID)\n\t\t\t\t\t\tlocal client = player.GetBySteamID64(data.steamID)\n\n\t\t\t\t\t\tix.char.RestoreVars(data, data)\n\n\t\t\t\t\t\tlocal w, h = ix.config.Get(\"inventoryWidth\"), ix.config.Get(\"inventoryHeight\")\n\t\t\t\t\t\tlocal character = ix.char.New(data, lastID, client, data.steamID)\n\t\t\t\t\t\tlocal inventory = ix.inventory.Create(w, h, invLastID)\n\n\t\t\t\t\t\tcharacter.vars.inv = {inventory}\n\t\t\t\t\t\tinventory:SetOwner(lastID)\n\n\t\t\t\t\t\tix.char.loaded[lastID] = character\n\t\t\t\t\t\ttable.insert(ix.char.cache[data.steamID], lastID)\n\n\t\t\t\t\t\tif (callback) then\n\t\t\t\t\t\t\tcallback(lastID)\n\t\t\t\t\t\tend\n\t\t\t\t\tend)\n\t\t\t\tinvQuery:Execute()\n\t\t\tend)\n\t\tquery:Execute()\n\tend\n\n\t--- Loads all of a player's characters into memory.\n\t-- @realm server\n\t-- @player client Player to load the characters for\n\t-- @func[opt=nil] callback Function to call when the characters have been loaded\n\t-- @bool[opt=false] bNoCache Whether or not to skip the cache; players that leave and join again later will already have\n\t-- their characters loaded which will skip the database query and load quicker\n\t-- @number[opt=nil] id The ID of a specific character to load instead of all of the player's characters\n\tfunction ix.char.Restore(client, callback, bNoCache, id)\n\t\tlocal steamID64 = client:SteamID64()\n\t\tlocal cache = ix.char.cache[steamID64]\n\n\t\tif (cache and !bNoCache) then\n\t\t\tfor _, v in ipairs(cache) do\n\t\t\t\tlocal character = ix.char.loaded[v]\n\n\t\t\t\tif (character and !IsValid(character.client)) then\n\t\t\t\t\tcharacter.player = client\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tif (callback) then\n\t\t\t\tcallback(cache)\n\t\t\tend\n\n\t\t\treturn\n\t\tend\n\n\t\tlocal query = mysql:Select(\"ix_characters\")\n\t\t\tquery:Select(\"id\")\n\n\t\t\tix.char.RestoreVars(query)\n\n\t\t\tquery:Where(\"schema\", Schema.folder)\n\t\t\tquery:Where(\"steamid\", steamID64)\n\n\t\t\tif (id) then\n\t\t\t\tquery:Where(\"id\", id)\n\t\t\tend\n\n\t\t\tquery:Callback(function(result)\n\t\t\t\tlocal characters = {}\n\n\t\t\t\tfor _, v in ipairs(result or {}) do\n\t\t\t\t\tlocal charID = tonumber(v.id)\n\n\t\t\t\t\tif (charID) then\n\t\t\t\t\t\tlocal data = {\n\t\t\t\t\t\t\tsteamID = steamID64\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tix.char.RestoreVars(data, v)\n\n\t\t\t\t\t\tcharacters[#characters + 1] = charID\n\t\t\t\t\t\tlocal character = ix.char.New(data, charID, client)\n\n\t\t\t\t\t\thook.Run(\"CharacterRestored\", character)\n\t\t\t\t\t\tcharacter.vars.inv = {\n\t\t\t\t\t\t\t[1] = -1,\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tlocal invQuery = mysql:Select(\"ix_inventories\")\n\t\t\t\t\t\t\tinvQuery:Select(\"inventory_id\")\n\t\t\t\t\t\t\tinvQuery:Select(\"inventory_type\")\n\t\t\t\t\t\t\tinvQuery:Where(\"character_id\", charID)\n\t\t\t\t\t\t\tinvQuery:Callback(function(info)\n\t\t\t\t\t\t\t\tif (istable(info) and #info > 0) then\n\t\t\t\t\t\t\t\t\tlocal inventories = {}\n\n\t\t\t\t\t\t\t\t\tfor _, v2 in pairs(info) do\n\t\t\t\t\t\t\t\t\t\tif (v2.inventory_type and isstring(v2.inventory_type) and v2.inventory_type == \"NULL\") then\n\t\t\t\t\t\t\t\t\t\t\tv2.inventory_type = nil\n\t\t\t\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\t\t\t\t\tif (hook.Run(\"ShouldRestoreInventory\", charID, v2.inventory_id, v2.inventory_type) != false) then\n\t\t\t\t\t\t\t\t\t\t\tlocal w, h = ix.config.Get(\"inventoryWidth\"), ix.config.Get(\"inventoryHeight\")\n\t\t\t\t\t\t\t\t\t\t\tlocal invType\n\n\t\t\t\t\t\t\t\t\t\t\tif (v2.inventory_type) then\n\t\t\t\t\t\t\t\t\t\t\t\tinvType = ix.item.inventoryTypes[v2.inventory_type]\n\n\t\t\t\t\t\t\t\t\t\t\t\tif (invType) then\n\t\t\t\t\t\t\t\t\t\t\t\t\tw, h = invType.w, invType.h\n\t\t\t\t\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\t\t\t\t\t\tinventories[tonumber(v2.inventory_id)] = {w, h, v2.inventory_type}\n\t\t\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\t\t\t\tix.inventory.Restore(inventories, nil, nil, function(inventory)\n\t\t\t\t\t\t\t\t\t\tlocal inventoryType = inventories[inventory:GetID()][3]\n\n\t\t\t\t\t\t\t\t\t\tif (inventoryType) then\n\t\t\t\t\t\t\t\t\t\t\tinventory.vars.isBag = inventoryType\n\t\t\t\t\t\t\t\t\t\t\ttable.insert(character.vars.inv, inventory)\n\t\t\t\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t\t\t\tcharacter.vars.inv[1] = inventory\n\t\t\t\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\t\t\t\t\tinventory:SetOwner(charID)\n\t\t\t\t\t\t\t\t\tend, true)\n\t\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t\tlocal insertQuery = mysql:Insert(\"ix_inventories\")\n\t\t\t\t\t\t\t\t\t\tinsertQuery:Insert(\"character_id\", charID)\n\t\t\t\t\t\t\t\t\t\tinsertQuery:Callback(function(_, status, lastID)\n\t\t\t\t\t\t\t\t\t\t\tlocal w, h = ix.config.Get(\"inventoryWidth\"), ix.config.Get(\"inventoryHeight\")\n\t\t\t\t\t\t\t\t\t\t\tlocal inventory = ix.inventory.Create(w, h, lastID)\n\t\t\t\t\t\t\t\t\t\t\tinventory:SetOwner(charID)\n\n\t\t\t\t\t\t\t\t\t\t\tcharacter.vars.inv = {\n\t\t\t\t\t\t\t\t\t\t\t\tinventory\n\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\tend)\n\t\t\t\t\t\t\t\t\tinsertQuery:Execute()\n\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\tend)\n\t\t\t\t\t\tinvQuery:Execute()\n\n\t\t\t\t\t\tix.char.loaded[charID] = character\n\t\t\t\t\telse\n\t\t\t\t\t\tErrorNoHalt(\"[Helix] Attempt to load character with invalid ID '\" .. tostring(id) .. \"'!\")\n\t\t\t\t\tend\n\t\t\t\tend\n\n\t\t\t\tif (callback) then\n\t\t\t\t\tcallback(characters)\n\t\t\t\tend\n\n\t\t\t\tix.char.cache[steamID64] = characters\n\t\t\tend)\n\t\tquery:Execute()\n\tend\n\n\t--- Adds character properties to a table. This is done automatically by `ix.char.Restore`, so that should be used instead if\n\t-- you are loading characters.\n\t-- @realm server\n\t-- @internal\n\t-- @tab data Table of fields to apply to the table. If this is an SQL query object, it will instead populate the query with\n\t-- `SELECT` statements for each applicable character var in `ix.char.vars`.\n\t-- @tab characterInfo Table to apply the properties to. This can be left as `nil` if an SQL query object is passed in `data`\n\tfunction ix.char.RestoreVars(data, characterInfo)\n\t\tif (data.queryType) then\n\t\t\t-- populate query\n\t\t\tfor _, v in pairs(ix.char.vars) do\n\t\t\t\tif (v.field and v.fieldType and !v.bSaveLoadInitialOnly) then\n\t\t\t\t\tdata:Select(v.field)\n\n\t\t\t\t\t-- if FilterValues is used, any rows that contain a value in the column that isn't in the valid values table\n\t\t\t\t\t-- will be ignored entirely (i.e the character will not load if it has an invalid value)\n\t\t\t\t\tif (v.FilterValues) then\n\t\t\t\t\t\tdata:WhereIn(v.field, v:FilterValues())\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\t\telse\n\t\t\t-- populate character data\n\t\t\tfor k, v in pairs(ix.char.vars) do\n\t\t\t\tif (v.field and characterInfo[v.field] and !v.bSaveLoadInitialOnly) then\n\t\t\t\t\tlocal value = characterInfo[v.field]\n\n\t\t\t\t\tif (isnumber(v.default)) then\n\t\t\t\t\t\tvalue = tonumber(value) or v.default\n\t\t\t\t\telseif (isstring(v.default)) then\n\t\t\t\t\t\tvalue = tostring(value) == \"NULL\" and v.default or tostring(value or v.default)\n\t\t\t\t\telseif (isbool(v.default)) then\n\t\t\t\t\t\tif (tostring(value) != \"NULL\") then\n\t\t\t\t\t\t\tvalue = tobool(value)\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tvalue = v.default\n\t\t\t\t\t\tend\n\t\t\t\t\telseif (istable(v.default)) then\n\t\t\t\t\t\tvalue = istable(value) and value or util.JSONToTable(value)\n\t\t\t\t\tend\n\n\t\t\t\t\tdata[k] = value\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend\nend\n\n--- Creates a new empty `Character` object. If you are looking to create a usable character, see `ix.char.Create`.\n-- @realm shared\n-- @internal\n-- @tab data Character vars to assign\n-- @number id Unique ID of the character\n-- @player client Player that will own the character\n-- @string[opt=client:SteamID64()] steamID SteamID64 of the player that will own the character\nfunction ix.char.New(data, id, client, steamID)\n\tif (data.name) then\n\t\tdata.name = data.name:gsub(\"#\", \"#​\")\n\tend\n\n\tif (data.description) then\n\t\tdata.description = data.description:gsub(\"#\", \"#​\")\n\tend\n\n\tlocal character = setmetatable({vars = {}}, ix.meta.character)\n\t\tfor k, v in pairs(data) do\n\t\t\tif (v != nil) then\n\t\t\t\tcharacter.vars[k] = v\n\t\t\tend\n\t\tend\n\n\t\tcharacter.id = id or 0\n\t\tcharacter.player = client\n\n\t\tif (SERVER and IsValid(client) or steamID) then\n\t\t\tcharacter.steamID = IsValid(client) and client:SteamID64() or steamID\n\t\tend\n\treturn character\nend\n\nix.char.varHooks = ix.char.varHooks or {}\nfunction ix.char.HookVar(varName, hookName, func)\n\tix.char.varHooks[varName] = ix.char.varHooks[varName] or {}\n\n\tix.char.varHooks[varName][hookName] = func\nend\n\ndo\n\t--- Default character vars\n\t-- @classmod Character\n\n\t--- Sets this character's name. This is automatically networked.\n\t-- @realm server\n\t-- @string name New name for the character\n\t-- @function SetName\n\n\t--- Returns this character's name\n\t-- @realm shared\n\t-- @treturn string This character's current name\n\t-- @function GetName\n\tix.char.RegisterVar(\"name\", {\n\t\tfield = \"name\",\n\t\tfieldType = ix.type.string,\n\t\tdefault = \"John Doe\",\n\t\tindex = 1,\n\t\tOnValidate = function(self, value, payload, client)\n\t\t\tif (!value) then\n\t\t\t\treturn false, \"invalid\", \"name\"\n\t\t\tend\n\n\t\t\tvalue = tostring(value):gsub(\"\\r\\n\", \"\"):gsub(\"\\n\", \"\")\n\t\t\tvalue = string.Trim(value)\n\n\t\t\tlocal minLength = ix.config.Get(\"minNameLength\", 4)\n\t\t\tlocal maxLength = ix.config.Get(\"maxNameLength\", 32)\n\n\t\t\tif (value:utf8len() < minLength) then\n\t\t\t\treturn false, \"nameMinLen\", minLength\n\t\t\telseif (!value:find(\"%S\")) then\n\t\t\t\treturn false, \"invalid\", \"name\"\n\t\t\telseif (value:gsub(\"%s\", \"\"):utf8len() > maxLength) then\n\t\t\t\treturn false, \"nameMaxLen\", maxLength\n\t\t\tend\n\n\t\t\treturn hook.Run(\"GetDefaultCharacterName\", client, payload.faction) or value:utf8sub(1, 70)\n\t\tend,\n\t\tOnPostSetup = function(self, panel, payload)\n\t\t\tlocal faction = ix.faction.indices[payload.faction]\n\t\t\tlocal name, disabled = hook.Run(\"GetDefaultCharacterName\", LocalPlayer(), payload.faction)\n\n\t\t\tif (name) then\n\t\t\t\tpanel:SetText(name)\n\t\t\t\tpayload:Set(\"name\", name)\n\t\t\tend\n\n\t\t\tif (disabled) then\n\t\t\t\tpanel:SetDisabled(true)\n\t\t\t\tpanel:SetEditable(false)\n\t\t\tend\n\n\t\t\tpanel:SetBackgroundColor(faction.color or Color(255, 255, 255, 25))\n\t\tend\n\t})\n\n\t--- Sets this character's physical description. This is automatically networked.\n\t-- @realm server\n\t-- @string description New description for this character\n\t-- @function SetDescription\n\n\t--- Returns this character's physical description.\n\t-- @realm shared\n\t-- @treturn string This character's current description\n\t-- @function GetDescription\n\tix.char.RegisterVar(\"description\", {\n\t\tfield = \"description\",\n\t\tfieldType = ix.type.text,\n\t\tdefault = \"\",\n\t\tindex = 2,\n\t\tOnValidate = function(self, value, payload)\n\t\t\tvalue = string.Trim((tostring(value):gsub(\"\\r\\n\", \"\"):gsub(\"\\n\", \"\")))\n\t\t\tlocal minLength = ix.config.Get(\"minDescriptionLength\", 16)\n\n\t\t\tif (value:utf8len() < minLength) then\n\t\t\t\treturn false, \"descMinLen\", minLength\n\t\t\telseif (!value:find(\"%s+\") or !value:find(\"%S\")) then\n\t\t\t\treturn false, \"invalid\", \"description\"\n\t\t\tend\n\n\t\t\treturn value\n\t\tend,\n\t\tOnPostSetup = function(self, panel, payload)\n\t\t\tpanel:SetMultiline(true)\n\t\t\tpanel:SetFont(\"ixMenuButtonFont\")\n\t\t\tpanel:SetTall(panel:GetTall() * 2 + 6) -- add another line\n\t\t\tpanel.AllowInput = function(_, character)\n\t\t\t\tif (character == \"\\n\" or character == \"\\r\") then\n\t\t\t\t\treturn true\n\t\t\t\tend\n\t\t\tend\n\t\tend,\n\t\talias = \"Desc\"\n\t})\n\n\t--- Sets this character's model. This sets the player's current model to the given one, and saves it to the character.\n\t-- It is automatically networked.\n\t-- @realm server\n\t-- @string model New model for the character\n\t-- @function SetModel\n\n\t--- Returns this character's model.\n\t-- @realm shared\n\t-- @treturn string This character's current model\n\t-- @function GetModel\n\tix.char.RegisterVar(\"model\", {\n\t\tfield = \"model\",\n\t\tfieldType = ix.type.string,\n\t\tdefault = \"models/error.mdl\",\n\t\tindex = 3,\n\t\tOnSet = function(character, value)\n\t\t\tlocal client = character:GetPlayer()\n\n\t\t\tif (IsValid(client) and client:GetCharacter() == character) then\n\t\t\t\tclient:SetModel(value)\n\t\t\tend\n\n\t\t\tcharacter.vars.model = value\n\t\tend,\n\t\tOnGet = function(character, default)\n\t\t\treturn character.vars.model or default\n\t\tend,\n\t\tOnDisplay = function(self, container, payload)\n\t\t\tlocal scroll = container:Add(\"DScrollPanel\")\n\t\t\tscroll:Dock(FILL) -- TODO: don't fill so we can allow other panels\n\t\t\tscroll.Paint = function(panel, width, height)\n\t\t\t\tderma.SkinFunc(\"DrawImportantBackground\", 0, 0, width, height, Color(255, 255, 255, 25))\n\t\t\tend\n\n\t\t\tlocal layout = scroll:Add(\"DIconLayout\")\n\t\t\tlayout:Dock(FILL)\n\t\t\tlayout:SetSpaceX(1)\n\t\t\tlayout:SetSpaceY(1)\n\n\t\t\tlocal faction = ix.faction.indices[payload.faction]\n\n\t\t\tif (faction) then\n\t\t\t\tlocal models = faction:GetModels(LocalPlayer())\n\n\t\t\t\tfor k, v in SortedPairs(models) do\n\t\t\t\t\tlocal icon = layout:Add(\"SpawnIcon\")\n\t\t\t\t\ticon:SetSize(64, 128)\n\t\t\t\t\ticon:InvalidateLayout(true)\n\t\t\t\t\ticon.DoClick = function(this)\n\t\t\t\t\t\tpayload:Set(\"model\", k)\n\t\t\t\t\tend\n\t\t\t\t\ticon.PaintOver = function(this, w, h)\n\t\t\t\t\t\tif (payload.model == k) then\n\t\t\t\t\t\t\tlocal color = ix.config.Get(\"color\", color_white)\n\n\t\t\t\t\t\t\tsurface.SetDrawColor(color.r, color.g, color.b, 200)\n\n\t\t\t\t\t\t\tfor i = 1, 3 do\n\t\t\t\t\t\t\t\tlocal i2 = i * 2\n\t\t\t\t\t\t\t\tsurface.DrawOutlinedRect(i, i, w - i2, h - i2)\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\n\t\t\t\t\tif (isstring(v)) then\n\t\t\t\t\t\ticon:SetModel(v)\n\t\t\t\t\telse\n\t\t\t\t\t\ticon:SetModel(v[1], v[2] or 0, v[3])\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\n\t\t\treturn scroll\n\t\tend,\n\t\tOnValidate = function(self, value, payload, client)\n\t\t\tlocal faction = ix.faction.indices[payload.faction]\n\n\t\t\tif (faction) then\n\t\t\t\tlocal models = faction:GetModels(client)\n\n\t\t\t\tif (!payload.model or !models[payload.model]) then\n\t\t\t\t\treturn false, \"needModel\"\n\t\t\t\tend\n\t\t\telse\n\t\t\t\treturn false, \"needModel\"\n\t\t\tend\n\t\tend,\n\t\tOnAdjust = function(self, client, data, value, newData)\n\t\t\tlocal faction = ix.faction.indices[data.faction]\n\n\t\t\tif (faction) then\n\t\t\t\tlocal model = faction:GetModels(client)[value]\n\n\t\t\t\tif (isstring(model)) then\n\t\t\t\t\tnewData.model = model\n\t\t\t\telseif (istable(model)) then\n\t\t\t\t\tnewData.model = model[1]\n\n\t\t\t\t\t-- save skin/bodygroups to character data\n\t\t\t\t\tlocal bodygroups = {}\n\n\t\t\t\t\tfor i = 1, #model[3] do\n\t\t\t\t\t\tbodygroups[i - 1] = tonumber(model[3][i]) or 0\n\t\t\t\t\tend\n\n\t\t\t\t\tnewData.data = newData.data or {}\n\t\t\t\t\tnewData.data.skin = model[2] or 0\n\t\t\t\t\tnewData.data.groups = bodygroups\n\t\t\t\tend\n\t\t\tend\n\t\tend,\n\t\tShouldDisplay = function(self, container, payload)\n\t\t\tlocal faction = ix.faction.indices[payload.faction]\n\t\t\treturn #faction:GetModels(LocalPlayer()) > 1\n\t\tend\n\t})\n\n\t-- SetClass shouldn't be used here, character:JoinClass should be used instead\n\n\t--- Returns this character's current class.\n\t-- @realm shared\n\t-- @treturn number Index of the class this character is in\n\t-- @function GetClass\n\tix.char.RegisterVar(\"class\", {\n\t\tbNoDisplay = true,\n\t})\n\n\t--- Sets this character's faction. Note that this doesn't do the initial setup for the player after the faction has been\n\t-- changed, so you'll have to update some character vars manually.\n\t-- @realm server\n\t-- @number faction Index of the faction to transfer this character to\n\t-- @function SetFaction\n\n\t--- Returns this character's faction.\n\t-- @realm shared\n\t-- @treturn number Index of the faction this character is currently in\n\t-- @function GetFaction\n\tix.char.RegisterVar(\"faction\", {\n\t\tfield = \"faction\",\n\t\tfieldType = ix.type.string,\n\t\tdefault = \"Citizen\",\n\t\tbNoDisplay = true,\n\t\tFilterValues = function(self)\n\t\t\t-- make sequential table of faction unique IDs\n\t\t\tlocal values = {}\n\n\t\t\tfor k, v in ipairs(ix.faction.indices) do\n\t\t\t\tvalues[k] = v.uniqueID\n\t\t\tend\n\n\t\t\treturn values\n\t\tend,\n\t\tOnSet = function(self, value)\n\t\t\tlocal client = self:GetPlayer()\n\n\t\t\tif (IsValid(client)) then\n\t\t\t\tlocal oldVar = self:GetFaction()\n\t\t\t\tself.vars.faction = ix.faction.indices[value] and ix.faction.indices[value].uniqueID\n\n\t\t\t\tclient:SetTeam(value)\n\n\t\t\t\t-- @todo refactor networking of character vars so this doesn't need to be repeated on every OnSet override\n\t\t\t\tnet.Start(\"ixCharacterVarChanged\")\n\t\t\t\t\tnet.WriteUInt(self:GetID(), 32)\n\t\t\t\t\tnet.WriteString(\"faction\")\n\t\t\t\t\tnet.WriteType(self.vars.faction)\n\t\t\t\tnet.Broadcast()\n\n\t\t\t\thook.Run(\"CharacterVarChanged\", self, \"faction\", oldVar, value)\n\t\t\tend\n\t\tend,\n\t\tOnGet = function(self, default)\n\t\t\tlocal faction = ix.faction.teams[self.vars.faction]\n\n\t\t\treturn faction and faction.index or 0\n\t\tend,\n\t\tOnValidate = function(self, index, data, client)\n\t\t\tif (index and client:HasWhitelist(index)) then\n\t\t\t\treturn true\n\t\t\tend\n\n\t\t\treturn false\n\t\tend,\n\t\tOnAdjust = function(self, client, data, value, newData)\n\t\t\tnewData.faction = ix.faction.indices[value].uniqueID\n\t\tend\n\t})\n\n\t-- attribute manipulation should be done with methods from the ix.attributes library\n\tix.char.RegisterVar(\"attributes\", {\n\t\tfield = \"attributes\",\n\t\tfieldType = ix.type.text,\n\t\tdefault = {},\n\t\tindex = 4,\n\t\tcategory = \"attributes\",\n\t\tisLocal = true,\n\t\tOnDisplay = function(self, container, payload)\n\t\t\tlocal maximum = hook.Run(\"GetDefaultAttributePoints\", LocalPlayer(), payload) or 10\n\n\t\t\tif (maximum < 1) then\n\t\t\t\treturn\n\t\t\tend\n\n\t\t\tlocal attributes = container:Add(\"DPanel\")\n\t\t\tattributes:Dock(TOP)\n\n\t\t\tlocal y\n\t\t\tlocal total = 0\n\n\t\t\tpayload.attributes = {}\n\n\t\t\t-- total spendable attribute points\n\t\t\tlocal totalBar = attributes:Add(\"ixAttributeBar\")\n\t\t\ttotalBar:SetMax(maximum)\n\t\t\ttotalBar:SetValue(maximum)\n\t\t\ttotalBar:Dock(TOP)\n\t\t\ttotalBar:DockMargin(2, 2, 2, 2)\n\t\t\ttotalBar:SetText(L(\"attribPointsLeft\"))\n\t\t\ttotalBar:SetReadOnly(true)\n\t\t\ttotalBar:SetColor(Color(20, 120, 20, 255))\n\n\t\t\ty = totalBar:GetTall() + 4\n\n\t\t\tfor k, v in SortedPairsByMemberValue(ix.attributes.list, \"name\") do\n\t\t\t\tpayload.attributes[k] = 0\n\n\t\t\t\tlocal bar = attributes:Add(\"ixAttributeBar\")\n\t\t\t\tbar:SetMax(v.maxValue or maximum)\n\t\t\t\tbar:Dock(TOP)\n\t\t\t\tbar:DockMargin(2, 2, 2, 2)\n\t\t\t\tbar:SetText(L(v.name))\n\t\t\t\tbar.OnChanged = function(this, difference)\n\t\t\t\t\tif ((total + difference) > maximum) then\n\t\t\t\t\t\treturn false\n\t\t\t\t\tend\n\n\t\t\t\t\ttotal = total + difference\n\t\t\t\t\tpayload.attributes[k] = payload.attributes[k] + difference\n\n\t\t\t\t\ttotalBar:SetValue(totalBar.value - difference)\n\t\t\t\tend\n\n\t\t\t\tif (v.noStartBonus) then\n\t\t\t\t\tbar:SetReadOnly()\n\t\t\t\tend\n\n\t\t\t\ty = y + bar:GetTall() + 4\n\t\t\tend\n\n\t\t\tattributes:SetTall(y)\n\t\t\treturn attributes\n\t\tend,\n\t\tOnValidate = function(self, value, data, client)\n\t\t\tif (value != nil) then\n\t\t\t\tif (istable(value)) then\n\t\t\t\t\tlocal count = 0\n\n\t\t\t\t\tfor _, v in pairs(value) do\n\t\t\t\t\t\tcount = count + v\n\t\t\t\t\tend\n\n\t\t\t\t\tif (count > (hook.Run(\"GetDefaultAttributePoints\", client, count) or 10)) then\n\t\t\t\t\t\treturn false, \"unknownError\"\n\t\t\t\t\tend\n\t\t\t\telse\n\t\t\t\t\treturn false, \"unknownError\"\n\t\t\t\tend\n\t\t\tend\n\t\tend,\n\t\tShouldDisplay = function(self, container, payload)\n\t\t\treturn !table.IsEmpty(ix.attributes.list)\n\t\tend\n\t})\n\n\t--- Sets this character's current money. Money is only networked to the player that owns this character.\n\t-- @realm server\n\t-- @number money New amount of money this character should have\n\t-- @function SetMoney\n\n\t--- Returns this character's money. This is only valid on the server and the owning client.\n\t-- @realm shared\n\t-- @treturn number Current money of this character\n\t-- @function GetMoney\n\tix.char.RegisterVar(\"money\", {\n\t\tfield = \"money\",\n\t\tfieldType = ix.type.number,\n\t\tdefault = 0,\n\t\tisLocal = true,\n\t\tbNoDisplay = true\n\t})\n\n\t--- Sets a data field on this character. This is useful for storing small bits of data that you need persisted on this\n\t-- character. This is networked only to the owning client. If you are going to be accessing this data field frequently with\n\t-- a getter/setter, consider using `ix.char.RegisterVar` instead.\n\t-- @realm server\n\t-- @string key Name of the field that holds the data\n\t-- @param value Any value to store in the field, as long as it's supported by GMod's JSON parser\n\t-- @function SetData\n\n\t--- Returns a data field set on this character. If it doesn't exist, it will return the given default or `nil`. This is only\n\t-- valid on the server and the owning client.\n\t-- @realm shared\n\t-- @string key Name of the field that's holding the data\n\t-- @param default Value to return if the given key doesn't exist, or is `nil`\n\t-- @return[1] Data stored in the field\n\t-- @treturn[2] nil If the data doesn't exist, or is `nil`\n\t-- @function GetData\n\tix.char.RegisterVar(\"data\", {\n\t\tdefault = {},\n\t\tisLocal = true,\n\t\tbNoDisplay = true,\n\t\tfield = \"data\",\n\t\tfieldType = ix.type.text,\n\t\tOnSet = function(character, key, value, noReplication, receiver)\n\t\t\tlocal data = character:GetData()\n\t\t\tlocal client = character:GetPlayer()\n\n\t\t\tdata[key] = value\n\n\t\t\tif (!noReplication and IsValid(client)) then\n\t\t\t\tnet.Start(\"ixCharacterData\")\n\t\t\t\t\tnet.WriteUInt(character:GetID(), 32)\n\t\t\t\t\tnet.WriteString(key)\n\t\t\t\t\tnet.WriteType(value)\n\t\t\t\tnet.Send(receiver or client)\n\t\t\tend\n\n\t\t\tcharacter.vars.data = data\n\t\tend,\n\t\tOnGet = function(character, key, default)\n\t\t\tlocal data = character.vars.data or {}\n\n\t\t\tif (key) then\n\t\t\t\tif (!data) then\n\t\t\t\t\treturn default\n\t\t\t\tend\n\n\t\t\t\tlocal value = data[key]\n\n\t\t\t\treturn value == nil and default or value\n\t\t\telse\n\t\t\t\treturn default or data\n\t\t\tend\n\t\tend\n\t})\n\n\tix.char.RegisterVar(\"var\", {\n\t\tdefault = {},\n\t\tbNoDisplay = true,\n\t\tOnSet = function(character, key, value, noReplication, receiver)\n\t\t\tlocal data = character:GetVar()\n\t\t\tlocal client = character:GetPlayer()\n\n\t\t\tdata[key] = value\n\n\t\t\tif (!noReplication and IsValid(client)) then\n\t\t\t\tlocal id\n\n\t\t\t\tif (client:GetCharacter() and client:GetCharacter():GetID() == character:GetID()) then\n\t\t\t\t\tid = client:GetCharacter():GetID()\n\t\t\t\telse\n\t\t\t\t\tid = character:GetID()\n\t\t\t\tend\n\n\t\t\t\tnet.Start(\"ixCharacterVar\")\n\t\t\t\t\tnet.WriteUInt(id, 32)\n\t\t\t\t\tnet.WriteString(key)\n\t\t\t\t\tnet.WriteType(value)\n\t\t\t\tnet.Send(receiver or client)\n\t\t\tend\n\n\t\t\tcharacter.vars.vars = data\n\t\tend,\n\t\tOnGet = function(character, key, default)\n\t\t\tcharacter.vars.vars = character.vars.vars or {}\n\t\t\tlocal data = character.vars.vars or {}\n\n\t\t\tif (key) then\n\t\t\t\tif (!data) then\n\t\t\t\t\treturn default\n\t\t\t\tend\n\n\t\t\t\tlocal value = data[key]\n\n\t\t\t\treturn value == nil and default or value\n\t\t\telse\n\t\t\t\treturn default or data\n\t\t\tend\n\t\tend\n\t})\n\n\t--- Returns the Unix timestamp of when this character was created (i.e the value of `os.time()` at the time of creation).\n\t-- @realm server\n\t-- @treturn number Unix timestamp of when this character was created\n\t-- @function GetCreateTime\n\tix.char.RegisterVar(\"createTime\", {\n\t\tfield = \"create_time\",\n\t\tfieldType = ix.type.number,\n\t\tbNoDisplay = true,\n\t\tbNoNetworking = true,\n\t\tbNotModifiable = true\n\t})\n\n\t--- Returns the Unix timestamp of when this character was last used by its owning player.\n\t-- @realm server\n\t-- @treturn number Unix timestamp of when this character was last used\n\t-- @function GetLastJoinTime\n\tix.char.RegisterVar(\"lastJoinTime\", {\n\t\tfield = \"last_join_time\",\n\t\tfieldType = ix.type.number,\n\t\tbNoDisplay = true,\n\t\tbNoNetworking = true,\n\t\tbNotModifiable = true,\n\t\tbSaveLoadInitialOnly = true\n\t})\n\n\t--- Returns the schema that this character belongs to. This is useful if you are running multiple schemas off of the same\n\t-- database, and need to differentiate between them.\n\t-- @realm server\n\t-- @treturn string Schema this character belongs to\n\t-- @function GetSchema\n\tix.char.RegisterVar(\"schema\", {\n\t\tfield = \"schema\",\n\t\tfieldType = ix.type.string,\n\t\tbNoDisplay = true,\n\t\tbNoNetworking = true,\n\t\tbNotModifiable = true,\n\t\tbSaveLoadInitialOnly = true\n\t})\n\n\t--- Returns the 64-bit Steam ID of the player that owns this character.\n\t-- @realm server\n\t-- @treturn string Owning player's Steam ID\n\t-- @function GetSteamID\n\tix.char.RegisterVar(\"steamID\", {\n\t\tfield = \"steamid\",\n\t\tfieldType = ix.type.steamid,\n\t\tbNoDisplay = true,\n\t\tbNoNetworking = true,\n\t\tbNotModifiable = true,\n\t\tbSaveLoadInitialOnly = true\n\t})\nend\n\n-- Networking information here.\ndo\n\tif (SERVER) then\n\t\tutil.AddNetworkString(\"ixCharacterMenu\")\n\t\tutil.AddNetworkString(\"ixCharacterChoose\")\n\t\tutil.AddNetworkString(\"ixCharacterCreate\")\n\t\tutil.AddNetworkString(\"ixCharacterDelete\")\n\t\tutil.AddNetworkString(\"ixCharacterLoaded\")\n\t\tutil.AddNetworkString(\"ixCharacterLoadFailure\")\n\n\t\tutil.AddNetworkString(\"ixCharacterAuthed\")\n\t\tutil.AddNetworkString(\"ixCharacterAuthFailed\")\n\n\t\tutil.AddNetworkString(\"ixCharacterInfo\")\n\t\tutil.AddNetworkString(\"ixCharacterData\")\n\t\tutil.AddNetworkString(\"ixCharacterKick\")\n\t\tutil.AddNetworkString(\"ixCharacterSet\")\n\t\tutil.AddNetworkString(\"ixCharacterVar\")\n\t\tutil.AddNetworkString(\"ixCharacterVarChanged\")\n\n\t\tnet.Receive(\"ixCharacterChoose\", function(length, client)\n\t\t\tlocal id = net.ReadUInt(32)\n\n\t\t\tif (client:GetCharacter() and client:GetCharacter():GetID() == id) then\n\t\t\t\tnet.Start(\"ixCharacterLoadFailure\")\n\t\t\t\t\tnet.WriteString(\"@usingChar\")\n\t\t\t\tnet.Send(client)\n\t\t\t\treturn\n\t\t\tend\n\n\t\t\tlocal character = ix.char.loaded[id]\n\n\t\t\tif (character and character:GetPlayer() == client) then\n\t\t\t\tlocal status, result = hook.Run(\"CanPlayerUseCharacter\", client, character)\n\n\t\t\t\tif (status == false) then\n\t\t\t\t\tnet.Start(\"ixCharacterLoadFailure\")\n\t\t\t\t\t\tnet.WriteString(result or \"\")\n\t\t\t\t\tnet.Send(client)\n\t\t\t\t\treturn\n\t\t\t\tend\n\n\t\t\t\tlocal currentChar = client:GetCharacter()\n\n\t\t\t\tif (currentChar) then\n\t\t\t\t\tcurrentChar:Save()\n\n\t\t\t\t\tfor _, v in ipairs(currentChar:GetInventory(true)) do\n\t\t\t\t\t\tif (istable(v)) then\n\t\t\t\t\t\t\tv:RemoveReceiver(client)\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\t\t\t\tend\n\n\t\t\t\thook.Run(\"PrePlayerLoadedCharacter\", client, character, currentChar)\n\t\t\t\tcharacter:Setup()\n\t\t\t\tclient:Spawn()\n\n\t\t\t\thook.Run(\"PlayerLoadedCharacter\", client, character, currentChar)\n\t\t\telse\n\t\t\t\tnet.Start(\"ixCharacterLoadFailure\")\n\t\t\t\t\tnet.WriteString(\"@unknownError\")\n\t\t\t\tnet.Send(client)\n\n\t\t\t\tErrorNoHalt(\"[Helix] Attempt to load invalid character '\" .. id .. \"'\\n\")\n\t\t\tend\n\t\tend)\n\n\t\tnet.Receive(\"ixCharacterCreate\", function(length, client)\n\t\t\tif ((client.ixNextCharacterCreate or 0) > RealTime()) then\n\t\t\t\treturn\n\t\t\tend\n\n\t\t\tlocal maxChars = hook.Run(\"GetMaxPlayerCharacter\", client) or ix.config.Get(\"maxCharacters\", 5)\n\t\t\tlocal charList = client.ixCharList\n\t\t\tlocal charCount = table.Count(charList)\n\n\t\t\tif (charCount >= maxChars) then\n\t\t\t\tnet.Start(\"ixCharacterAuthFailed\")\n\t\t\t\t\tnet.WriteString(\"maxCharacters\")\n\t\t\t\t\tnet.WriteTable({})\n\t\t\t\tnet.Send(client)\n\n\t\t\t\treturn\n\t\t\tend\n\n\t\t\tclient.ixNextCharacterCreate = RealTime() + 1\n\n\t\t\tlocal indicies = net.ReadUInt(8)\n\t\t\tlocal payload = {}\n\n\t\t\tfor _ = 1, indicies do\n\t\t\t\tpayload[net.ReadString()] = net.ReadType()\n\t\t\tend\n\n\t\t\tlocal newPayload = {}\n\t\t\tlocal results = {hook.Run(\"CanPlayerCreateCharacter\", client, payload)}\n\n\t\t\tif (table.remove(results, 1) == false) then\n\t\t\t\tnet.Start(\"ixCharacterAuthFailed\")\n\t\t\t\t\tnet.WriteString(table.remove(results, 1) or \"unknownError\")\n\t\t\t\t\tnet.WriteTable(results)\n\t\t\t\tnet.Send(client)\n\n\t\t\t\treturn\n\t\t\tend\n\n\t\t\tfor k, _ in pairs(payload) do\n\t\t\t\tlocal info = ix.char.vars[k]\n\n\t\t\t\tif (!info or (!info.OnValidate and info.bNoDisplay)) then\n\t\t\t\t\tpayload[k] = nil\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tfor k, v in SortedPairsByMemberValue(ix.char.vars, \"index\") do\n\t\t\t\tlocal value = payload[k]\n\n\t\t\t\tif (v.OnValidate) then\n\t\t\t\t\tlocal result = {v:OnValidate(value, payload, client)}\n\n\t\t\t\t\tif (result[1] == false) then\n\t\t\t\t\t\tlocal fault = result[2]\n\n\t\t\t\t\t\ttable.remove(result, 2)\n\t\t\t\t\t\ttable.remove(result, 1)\n\n\t\t\t\t\t\tnet.Start(\"ixCharacterAuthFailed\")\n\t\t\t\t\t\t\tnet.WriteString(fault)\n\t\t\t\t\t\t\tnet.WriteTable(result)\n\t\t\t\t\t\tnet.Send(client)\n\n\t\t\t\t\t\treturn\n\t\t\t\t\telse\n\t\t\t\t\t\tif (result[1] != nil) then\n\t\t\t\t\t\t\tpayload[k] = result[1]\n\t\t\t\t\t\tend\n\n\t\t\t\t\t\tif (v.OnAdjust) then\n\t\t\t\t\t\t\tv:OnAdjust(client, payload, value, newPayload)\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tpayload.steamID = client:SteamID64()\n\t\t\t\thook.Run(\"AdjustCreationPayload\", client, payload, newPayload)\n\t\t\tpayload = table.Merge(payload, newPayload)\n\n\t\t\tix.char.Create(payload, function(id)\n\t\t\t\tif (IsValid(client)) then\n\t\t\t\t\tix.char.loaded[id]:Sync(client)\n\n\t\t\t\t\tnet.Start(\"ixCharacterAuthed\")\n\t\t\t\t\tnet.WriteUInt(id, 32)\n\t\t\t\t\tnet.WriteUInt(#client.ixCharList, 6)\n\n\t\t\t\t\tfor _, v in ipairs(client.ixCharList) do\n\t\t\t\t\t\tnet.WriteUInt(v, 32)\n\t\t\t\t\tend\n\n\t\t\t\t\tnet.Send(client)\n\n\t\t\t\t\tMsgN(\"Created character '\" .. id .. \"' for \" .. client:SteamName() .. \".\")\n\t\t\t\t\thook.Run(\"OnCharacterCreated\", client, ix.char.loaded[id])\n\t\t\t\tend\n\t\t\tend)\n\t\tend)\n\n\t\tnet.Receive(\"ixCharacterDelete\", function(length, client)\n\t\t\tlocal id = net.ReadUInt(32)\n\t\t\tlocal character = ix.char.loaded[id]\n\t\t\tlocal steamID = client:SteamID64()\n\t\t\tlocal isCurrentChar = client:GetCharacter() and client:GetCharacter():GetID() == id\n\n\t\t\tif (character and character.steamID == steamID) then\n\t\t\t\tfor k, v in ipairs(client.ixCharList or {}) do\n\t\t\t\t\tif (v == id) then\n\t\t\t\t\t\ttable.remove(client.ixCharList, k)\n\t\t\t\t\tend\n\t\t\t\tend\n\n\t\t\t\thook.Run(\"PreCharacterDeleted\", client, character)\n\t\t\t\tix.char.loaded[id] = nil\n\n\t\t\t\tnet.Start(\"ixCharacterDelete\")\n\t\t\t\t\tnet.WriteUInt(id, 32)\n\t\t\t\tnet.Broadcast()\n\n\t\t\t\t-- remove character from database\n\t\t\t\tlocal query = mysql:Delete(\"ix_characters\")\n\t\t\t\t\tquery:Where(\"id\", id)\n\t\t\t\t\tquery:Where(\"steamid\", client:SteamID64())\n\t\t\t\tquery:Execute()\n\n\t\t\t\t-- DBTODO: setup relations instead\n\t\t\t\t-- remove inventory from database\n\t\t\t\tquery = mysql:Select(\"ix_inventories\")\n\t\t\t\t\tquery:Select(\"inventory_id\")\n\t\t\t\t\tquery:Where(\"character_id\", id)\n\t\t\t\t\tquery:Callback(function(result)\n\t\t\t\t\t\tif (istable(result)) then\n\t\t\t\t\t\t\t-- remove associated items from database\n\t\t\t\t\t\t\tfor _, v in ipairs(result) do\n\t\t\t\t\t\t\t\tlocal itemQuery = mysql:Delete(\"ix_items\")\n\t\t\t\t\t\t\t\t\titemQuery:Where(\"inventory_id\", v.inventory_id)\n\t\t\t\t\t\t\t\titemQuery:Execute()\n\n\t\t\t\t\t\t\t\tix.item.inventories[tonumber(v.inventory_id)] = nil\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\tend\n\n\t\t\t\t\t\tlocal invQuery = mysql:Delete(\"ix_inventories\")\n\t\t\t\t\t\t\tinvQuery:Where(\"character_id\", id)\n\t\t\t\t\t\tinvQuery:Execute()\n\t\t\t\t\tend)\n\t\t\t\tquery:Execute()\n\n\t\t\t\t-- other plugins might need to deal with deleted characters.\n\t\t\t\thook.Run(\"CharacterDeleted\", client, id, isCurrentChar)\n\n\t\t\t\tif (isCurrentChar) then\n\t\t\t\t\tclient:SetNetVar(\"char\", nil)\n\t\t\t\t\tclient:KillSilent()\n\t\t\t\t\tclient:StripAmmo()\n\t\t\t\tend\n\t\t\tend\n\t\tend)\n\telse\n\t\tnet.Receive(\"ixCharacterInfo\", function()\n\t\t\tlocal data = net.ReadTable()\n\t\t\tlocal id = net.ReadUInt(32)\n\t\t\tlocal client = net.ReadUInt(8)\n\n\t\t\tix.char.loaded[id] = ix.char.New(data, id, client)\n\t\tend)\n\n\t\tnet.Receive(\"ixCharacterVarChanged\", function()\n\t\t\tlocal id = net.ReadUInt(32)\n\t\t\tlocal character = ix.char.loaded[id]\n\n\t\t\tif (character) then\n\t\t\t\tlocal key = net.ReadString()\n\t\t\t\tlocal value = net.ReadType()\n\n\t\t\t\tcharacter.vars[key] = value\n\t\t\tend\n\t\tend)\n\n\t\t-- Used for setting random access vars on the \"var\" character var (really stupid).\n\t\t-- Clean this up someday.\n\t\tnet.Receive(\"ixCharacterVar\", function()\n\t\t\tlocal id = net.ReadUInt(32)\n\t\t\tlocal character = ix.char.loaded[id]\n\n\t\t\tif (character) then\n\t\t\t\tlocal key = net.ReadString()\n\t\t\t\tlocal value = net.ReadType()\n\t\t\t\tlocal oldVar = character:GetVar()[key]\n\t\t\t\tcharacter:GetVar()[key] = value\n\n\t\t\t\thook.Run(\"CharacterVarChanged\", character, key, oldVar, value)\n\t\t\tend\n\t\tend)\n\n\t\tnet.Receive(\"ixCharacterMenu\", function()\n\t\t\tlocal indices = net.ReadUInt(6)\n\t\t\tlocal charList = {}\n\n\t\t\tfor _ = 1, indices do\n\t\t\t\tcharList[#charList + 1] = net.ReadUInt(32)\n\t\t\tend\n\n\t\t\tif (charList) then\n\t\t\t\tix.characters = charList\n\t\t\tend\n\n\t\t\tvgui.Create(\"ixCharMenu\")\n\t\tend)\n\n\t\tnet.Receive(\"ixCharacterLoadFailure\", function()\n\t\t\tlocal message = net.ReadString()\n\n\t\t\tif (isstring(message) and message:sub(1, 1) == \"@\") then\n\t\t\t\tmessage = L(message:sub(2))\n\t\t\tend\n\n\t\t\tmessage = message != \"\" and message or L(\"unknownError\")\n\n\t\t\tif (IsValid(ix.gui.characterMenu)) then\n\t\t\t\tix.gui.characterMenu:OnCharacterLoadFailed(message)\n\t\t\telse\n\t\t\t\tix.util.Notify(message)\n\t\t\tend\n\t\tend)\n\n\t\tnet.Receive(\"ixCharacterData\", function()\n\t\t\tlocal id = net.ReadUInt(32)\n\t\t\tlocal key = net.ReadString()\n\t\t\tlocal value = net.ReadType()\n\t\t\tlocal character = ix.char.loaded[id]\n\n\t\t\tif (character) then\n\t\t\t\tcharacter.vars.data = character.vars.data or {}\n\t\t\t\tcharacter:GetData()[key] = value\n\t\t\tend\n\t\tend)\n\n\t\tnet.Receive(\"ixCharacterDelete\", function()\n\t\t\tlocal id = net.ReadUInt(32)\n\t\t\tlocal isCurrentChar = LocalPlayer():GetCharacter() and LocalPlayer():GetCharacter():GetID() == id\n\t\t\tlocal character = ix.char.loaded[id]\n\n\t\t\tix.char.loaded[id] = nil\n\n\t\t\tfor k, v in ipairs(ix.characters) do\n\t\t\t\tif (v == id) then\n\t\t\t\t\ttable.remove(ix.characters, k)\n\n\t\t\t\t\tif (IsValid(ix.gui.characterMenu)) then\n\t\t\t\t\t\tix.gui.characterMenu:OnCharacterDeleted(character)\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tif (isCurrentChar and !IsValid(ix.gui.characterMenu)) then\n\t\t\t\tvgui.Create(\"ixCharMenu\")\n\t\t\tend\n\t\tend)\n\n\t\tnet.Receive(\"ixCharacterKick\", function()\n\t\t\tlocal isCurrentChar = net.ReadBool()\n\n\t\t\tif (ix.gui.menu and ix.gui.menu:IsVisible()) then\n\t\t\t\tix.gui.menu:Remove()\n\t\t\tend\n\n\t\t\tif (!IsValid(ix.gui.characterMenu)) then\n\t\t\t\tvgui.Create(\"ixCharMenu\")\n\t\t\telseif (ix.gui.characterMenu:IsClosing()) then\n\t\t\t\tix.gui.characterMenu:Remove()\n\t\t\t\tvgui.Create(\"ixCharMenu\")\n\t\t\tend\n\n\t\t\tif (isCurrentChar) then\n\t\t\t\tix.gui.characterMenu.mainPanel:UpdateReturnButton(false)\n\t\t\tend\n\t\tend)\n\n\t\tnet.Receive(\"ixCharacterLoaded\", function()\n\t\t\thook.Run(\"CharacterLoaded\", ix.char.loaded[net.ReadUInt(32)])\n\t\tend)\n\tend\nend\n\ndo\n\t--- Character util functions for player\n\t-- @classmod Player\n\n\tlocal playerMeta = FindMetaTable(\"Player\")\n\tplayerMeta.SteamName = playerMeta.SteamName or playerMeta.Name\n\n\t--- Returns this player's currently possessed `Character` object if it exists.\n\t-- @realm shared\n\t-- @treturn[1] Character Currently loaded character\n\t-- @treturn[2] nil If this player has no character loaded\n\tfunction playerMeta:GetCharacter()\n\t\treturn ix.char.loaded[self:GetNetVar(\"char\")]\n\tend\n\n\tplayerMeta.GetChar = playerMeta.GetCharacter\n\n\t--- Returns this player's current name.\n\t-- @realm shared\n\t-- @treturn[1] string Name of this player's currently loaded character\n\t-- @treturn[2] string Steam name of this player if the player has no character loaded\n\tfunction playerMeta:GetName()\n\t\tlocal character = self:GetCharacter()\n\n\t\treturn character and character:GetName() or self:SteamName()\n\tend\n\n\tplayerMeta.Nick = playerMeta.GetName\n\tplayerMeta.Name = playerMeta.GetName\nend\n"
  },
  {
    "path": "gamemode/core/libs/sh_chatbox.lua",
    "content": "\r\n--[[--\r\nChat manipulation and helper functions.\r\n\r\nChat messages are a core part of the framework - it's takes up a good chunk of the gameplay, and is also used to interact with\r\nthe framework. Chat messages can have types or \"classes\" that describe how the message should be interpreted. All chat messages\r\nwill have some type of class: `ic` for regular in-character speech, `me` for actions, `ooc` for out-of-character, etc. These\r\nchat classes can affect how the message is displayed in each player's chatbox. See `ix.chat.Register` and `ChatClassStructure`\r\nto create your own chat classes.\r\n]]\r\n-- @module ix.chat\r\n\r\nix.chat = ix.chat or {}\r\n\r\n--- List of all chat classes that have been registered by the framework, where each key is the name of the chat class, and value\r\n-- is the chat class data. Accessing a chat class's data is useful for when you want to copy some functionality or properties\r\n-- to use in your own. Note that if you're accessing this table, you should do so inside of the `InitializedChatClasses` hook.\r\n-- @realm shared\r\n-- @table ix.chat.classes\r\n-- @usage print(ix.chat.classes.ic.format)\r\n-- > \"%s says \\\"%s\\\"\"\r\nix.chat.classes = ix.chat.classes or {}\r\n\r\nif (!ix.command) then\r\n\tinclude(\"sh_command.lua\")\r\nend\r\n\r\nCAMI.RegisterPrivilege({\r\nName = \"Helix - Bypass OOC Timer\",\r\n\tMinAccess = \"admin\"\r\n})\r\n\r\n-- note we can't use commas in the \"color\" field's default value since the metadata is separated by commas which will break the\r\n-- formatting for that field\r\n\r\n--- Chat messages can have different classes or \"types\" of messages that have different properties. This can include how the\r\n-- text is formatted, color, hearing distance, etc.\r\n-- @realm shared\r\n-- @table ChatClassStructure\r\n-- @see ix.chat.Register\r\n-- @field[type=string] prefix What the player must type before their message in order to use this chat class. For example,\r\n-- having a prefix of `/Y` will require to type `/Y I am yelling` in order to send a message with this chat class. This can also\r\n-- be a table of strings if you want to allow multiple prefixes, such as `{\"//\", \"/OOC\"}`.\r\n--\r\n-- **NOTE:** the prefix should usually start with a `/` to be consistent with the rest of the framework. However, you are able\r\n-- to use something different like the `LOOC` chat class where the prefixes are `.//`, `[[`, and `/LOOC`.\r\n-- @field[type=bool,opt=false] noSpaceAfter Whether or not the `prefix` can be used without a space after it. For example, the\r\n-- `OOC` chat class allows you to type `//my message` instead of `// my message`. **NOTE:** this only works if the last\r\n-- character in the prefix is non-alphanumeric (i.e `noSpaceAfter` with `/Y` will not work, but `/!` will).\r\n-- @field[type=string,opt] description Description to show to the user in the chatbox when they're using this chat class\r\n-- @field[type=string,opt=\"%s: \\\"%s\\\"\"] format How to format a message with this chat class. The first `%s` will be the speaking\r\n-- player's name, and the second one will be their message\r\n-- @field[type=color,opt=Color(242 230 160)] color Color to use when displaying a message with this chat class\r\n-- @field[type=string,opt=\"chatTyping\"] indicator Language phrase to use when displaying the typing indicator above the\r\n-- speaking player's head\r\n-- @field[type=bool,opt=false] bNoIndicator Whether or not to avoid showing the typing indicator above the speaking player's\r\n-- head\r\n-- @field[type=string,opt=ixChatFont] font Font to use for displaying a message with this chat class\r\n-- @field[type=bool,opt=false] deadCanChat Whether or not a dead player can send a message with this chat class\r\n-- @field[type=number] CanHear This can be either a `number` representing how far away another player can hear this message.\r\n-- IC messages will use the `chatRange` config, for example. This can also be a function, which returns `true` if the given\r\n-- listener can hear the message emitted from a speaker.\r\n-- \t-- message can be heard by any player 1000 units away from the speaking player\r\n-- \tCanHear = 1000\r\n-- OR\r\n-- \tCanHear = function(self, speaker, listener)\r\n-- \t\t-- the speaking player will be heard by everyone\r\n-- \t\treturn true\r\n-- \tend\r\n-- @field[type=function,opt] CanSay Function to run to check whether or not a player can send a message with this chat class.\r\n-- By default, it will return `false` if the player is dead and `deadCanChat` is `false`. Overriding this function will prevent\r\n-- `deadCanChat` from working, and you must implement this functionality manually.\r\n-- \tCanSay = function(self, speaker, text)\r\n-- \t\t-- the speaker will never be able to send a message with this chat class\r\n-- \t\treturn false\r\n-- \tend\r\n-- @field[type=function,opt] GetColor Function to run to set the color of a message with this chat class. You should generally\r\n-- stick to using `color`, but this is useful for when you want the color of the message to change with some criteria.\r\n-- \tGetColor = function(self, speaker, text)\r\n-- \t\t-- each message with this chat class will be colored a random shade of red\r\n-- \t\treturn Color(math.random(120, 200), 0, 0)\r\n-- \tend\r\n-- @field[type=function,opt] OnChatAdd Function to run when a message with this chat class should be added to the chatbox. If\r\n-- using this function, make sure you end the function by calling `chat.AddText` in order for the text to show up.\r\n--\r\n-- **NOTE:** using your own `OnChatAdd` function will prevent `color`, `GetColor`, or `format` from being used since you'll be\r\n-- overriding the base function that uses those properties. In such cases you'll need to add that functionality back in\r\n-- manually. In general, you should avoid overriding this function where possible. The `data` argument in the function is\r\n-- whatever is passed into the same `data` argument in `ix.chat.Send`.\r\n--\r\n-- \tOnChatAdd = function(self, speaker, text, bAnonymous, data)\r\n-- \t\t-- adds white text in the form of \"Player Name: Message contents\"\r\n-- \t\tchat.AddText(color_white, speaker:GetName(), \": \", text)\r\n-- \tend\r\n\r\n--- Registers a new chat type with the information provided. Chat classes should usually be created inside of the\r\n-- `InitializedChatClasses` hook.\r\n-- @realm shared\r\n-- @string chatType Name of the chat type\r\n-- @tparam ChatClassStructure data Properties and functions to assign to this chat class\r\n-- @usage -- this is the \"me\" chat class taken straight from the framework as an example\r\n-- ix.chat.Register(\"me\", {\r\n-- \tformat = \"** %s %s\",\r\n-- \tcolor = Color(255, 50, 50),\r\n-- \tCanHear = ix.config.Get(\"chatRange\", 280) * 2,\r\n-- \tprefix = {\"/Me\", \"/Action\"},\r\n-- \tdescription = \"@cmdMe\",\r\n-- \tindicator = \"chatPerforming\",\r\n-- \tdeadCanChat = true\r\n-- })\r\n-- @see ChatClassStructure\r\nfunction ix.chat.Register(chatType, data)\r\n\tchatType = string.lower(chatType)\r\n\r\n\tif (!data.CanHear) then\r\n\t\t-- Have a substitute if the canHear property is not found.\r\n\t\tfunction data:CanHear(speaker, listener)\r\n\t\t\t-- The speaker will be heard by everyone.\r\n\t\t\treturn true\r\n\t\tend\r\n\telseif (isnumber(data.CanHear)) then\r\n\t\t-- Use the value as a range and create a function to compare distances.\r\n\t\tlocal range = data.CanHear * data.CanHear\r\n\t\tdata.range = range\r\n\r\n\t\tfunction data:CanHear(speaker, listener)\r\n\t\t\t-- Length2DSqr is faster than Length2D, so just check the squares.\r\n\t\t\treturn (speaker:GetPos() - listener:GetPos()):LengthSqr() <= self.range\r\n\t\tend\r\n\tend\r\n\r\n\t-- Allow players to use this chat type by default.\r\n\tif (!data.CanSay) then\r\n\t\tfunction data:CanSay(speaker, text)\r\n\t\t\tif (!self.deadCanChat and !speaker:Alive()) then\r\n\t\t\t\tspeaker:NotifyLocalized(\"noPerm\")\r\n\r\n\t\t\t\treturn false\r\n\t\t\tend\r\n\r\n\t\t\treturn true\r\n\t\tend\r\n\tend\r\n\r\n\t-- Chat text color.\r\n\tdata.color = data.color or Color(242, 230, 160)\r\n\r\n\tif (!data.OnChatAdd) then\r\n\t\tdata.format = data.format or \"%s: \\\"%s\\\"\"\r\n\r\n\t\tfunction data:OnChatAdd(speaker, text, anonymous, info)\r\n\t\t\tlocal color = self.color\r\n\t\t\tlocal name = anonymous and\r\n\t\t\t\tL\"someone\" or hook.Run(\"GetCharacterName\", speaker, chatType) or\r\n\t\t\t\t(IsValid(speaker) and speaker:Name() or \"Console\")\r\n\r\n\t\t\tif (self.GetColor) then\r\n\t\t\t\tcolor = self:GetColor(speaker, text, info)\r\n\t\t\tend\r\n\r\n\t\t\tlocal translated = L2(chatType..\"Format\", name, text)\r\n\r\n\t\t\tchat.AddText(color, translated or string.format(self.format, name, text))\r\n\t\tend\r\n\tend\r\n\r\n\tif (CLIENT and data.prefix) then\r\n\t\tif (istable(data.prefix)) then\r\n\t\t\tfor _, v in ipairs(data.prefix) do\r\n\t\t\t\tif (v:utf8sub(1, 1) == \"/\") then\r\n\t\t\t\t\tix.command.Add(v:utf8sub(2), {\r\n\t\t\t\t\t\tdescription = data.description,\r\n\t\t\t\t\t\targuments = ix.type.text,\r\n\t\t\t\t\t\tindicator = data.indicator,\r\n\t\t\t\t\t\tbNoIndicator = data.bNoIndicator,\r\n\t\t\t\t\t\tchatClass = data,\r\n\t\t\t\t\t\tOnCheckAccess = function() return true end,\r\n\t\t\t\t\t\tOnRun = function(self, client, message) end\r\n\t\t\t\t\t})\r\n\t\t\t\tend\r\n\t\t\tend\r\n\t\telse\r\n\t\t\tix.command.Add(isstring(data.prefix) and data.prefix:utf8sub(2) or chatType, {\r\n\t\t\t\tdescription = data.description,\r\n\t\t\t\targuments = ix.type.text,\r\n\t\t\t\tindicator = data.indicator,\r\n\t\t\t\tbNoIndicator = data.bNoIndicator,\r\n\t\t\t\tchatClass = data,\r\n\t\t\t\tOnCheckAccess = function() return true end,\r\n\t\t\t\tOnRun = function(self, client, message) end\r\n\t\t\t})\r\n\t\tend\r\n\tend\r\n\r\n\tdata.uniqueID = chatType\r\n\tix.chat.classes[chatType] = data\r\nend\r\n\r\n--- Identifies which chat mode should be used.\r\n-- @realm shared\r\n-- @player client Player who is speaking\r\n-- @string message Message to parse\r\n-- @bool[opt=false] bNoSend Whether or not to send the chat message after parsing\r\n-- @treturn string Name of the chat type\r\n-- @treturn string Message that was parsed\r\n-- @treturn bool Whether or not the speaker should be anonymous\r\nfunction ix.chat.Parse(client, message, bNoSend)\r\n\tlocal anonymous = false\r\n\tlocal chatType = \"ic\"\r\n\r\n\t-- Loop through all chat classes and see if the message contains their prefix.\r\n\tfor k, v in pairs(ix.chat.classes) do\r\n\t\tlocal isChosen = false\r\n\t\tlocal chosenPrefix = \"\"\r\n\t\tlocal noSpaceAfter = v.noSpaceAfter\r\n\r\n\t\t-- Check through all prefixes if the chat type has more than one.\r\n\t\tif (istable(v.prefix)) then\r\n\t\t\tfor _, prefix in ipairs(v.prefix) do\r\n\t\t\t\tprefix = prefix:utf8lower()\r\n\t\t\t\tlocal fullPrefix = prefix .. (noSpaceAfter and \"\" or \" \")\r\n\r\n\t\t\t\t-- Checking if the start of the message has the prefix.\r\n\t\t\t\tif (message:utf8sub(1, prefix:utf8len() + (noSpaceAfter and 0 or 1)):utf8lower() == fullPrefix:utf8lower()) then\r\n\t\t\t\t\tisChosen = true\r\n\t\t\t\t\tchosenPrefix = fullPrefix\r\n\r\n\t\t\t\t\tbreak\r\n\t\t\t\tend\r\n\t\t\tend\r\n\t\t-- Otherwise the prefix itself is checked.\r\n\t\telseif (isstring(v.prefix)) then\r\n\t\t\tlocal prefix = v.prefix:utf8lower()\r\n\t\t\tlocal fullPrefix = prefix .. (noSpaceAfter and \"\" or \" \")\r\n\r\n\t\t\tisChosen = message:utf8sub(1, prefix:utf8len() + (noSpaceAfter and 0 or 1)):utf8lower() == fullPrefix:utf8lower()\r\n\t\t\tchosenPrefix = fullPrefix\r\n\t\tend\r\n\r\n\t\t-- If the checks say we have the proper chat type, then the chat type is the chosen one!\r\n\t\t-- If this is not chosen, the loop continues. If the loop doesn't find the correct chat\r\n\t\t-- type, then it falls back to IC chat as seen by the chatType variable above.\r\n\t\tif (isChosen) then\r\n\t\t\t-- Set the chat type to the chosen one.\r\n\t\t\tchatType = k\r\n\t\t\t-- Remove the prefix from the chat type so it does not show in the message.\r\n\t\t\tmessage = message:utf8sub(chosenPrefix:utf8len() + 1)\r\n\r\n\t\t\tif (ix.chat.classes[k].noSpaceAfter and message:utf8sub(1, 1):match(\"%s\")) then\r\n\t\t\t\tmessage = message:utf8sub(2)\r\n\t\t\tend\r\n\r\n\t\t\tbreak\r\n\t\tend\r\n\tend\r\n\r\n\tif (!message:find(\"%S\")) then\r\n\t\treturn\r\n\tend\r\n\r\n\t-- Only send if needed.\r\n\tif (SERVER and !bNoSend) then\r\n\t\t-- Send the correct chat type out so other player see the message.\r\n\t\tix.chat.Send(client, chatType, hook.Run(\"PlayerMessageSend\", client, chatType, message, anonymous) or message, anonymous)\r\n\tend\r\n\r\n\t-- Return the chosen chat type and the message that was sent if needed for some reason.\r\n\t-- This would be useful if you want to send the message on your own.\r\n\treturn chatType, message, anonymous\r\nend\r\n\r\n--- Formats a string to fix basic grammar - removing extra spacing at the beginning and end, capitalizing the first character,\r\n-- and making sure it ends in punctuation.\r\n-- @realm shared\r\n-- @string text String to format\r\n-- @treturn string Formatted string\r\n-- @usage print(ix.chat.Format(\"hello\"))\r\n-- > Hello.\r\n-- @usage print(ix.chat.Format(\"wow!\"))\r\n-- > Wow!\r\nfunction ix.chat.Format(text)\r\n\ttext = string.Trim(text)\r\n\tlocal last = text:utf8sub(-1)\r\n\r\n\tif (last != \".\" and last != \"?\" and last != \"!\" and last != \"-\" and last != \"\\\"\") then\r\n\t\ttext = text .. \".\"\r\n\tend\r\n\r\n\treturn text:utf8sub(1, 1):utf8upper() .. text:utf8sub(2)\r\nend\r\n\r\nif (SERVER) then\r\n\tutil.AddNetworkString(\"ixChatMessage\")\r\n\r\n\t--- Send a chat message using the specified chat type.\r\n\t-- @realm server\r\n\t-- @player speaker Player who is speaking\r\n\t-- @string chatType Name of the chat type\r\n\t-- @string text Message to send\r\n\t-- @bool[opt=false] bAnonymous Whether or not the speaker should be anonymous\r\n\t-- @tab[opt=nil] receivers The players to replicate send the message to\r\n\t-- @tab[opt=nil] data Additional data for this chat message\r\n\tfunction ix.chat.Send(speaker, chatType, text, bAnonymous, receivers, data)\r\n\t\tif (!chatType) then\r\n\t\t\treturn\r\n\t\tend\r\n\r\n\t\tdata = data or {}\r\n\t\tchatType = string.lower(chatType)\r\n\r\n\t\tif (IsValid(speaker) and hook.Run(\"PrePlayerMessageSend\", speaker, chatType, text, bAnonymous) == false) then\r\n\t\t\treturn\r\n\t\tend\r\n\r\n\t\tlocal class = ix.chat.classes[chatType]\r\n\r\n\t\tif (class and class:CanSay(speaker, text, data) != false) then\r\n\t\t\tif (class.CanHear and !receivers) then\r\n\t\t\t\treceivers = {}\r\n\r\n\t\t\t\tfor _, v in player.Iterator() do\r\n\t\t\t\t\tif (v:GetCharacter() and class:CanHear(speaker, v, data) != false) then\r\n\t\t\t\t\t\treceivers[#receivers + 1] = v\r\n\t\t\t\t\tend\r\n\t\t\t\tend\r\n\r\n\t\t\t\tif (#receivers == 0) then\r\n\t\t\t\t\treturn\r\n\t\t\t\tend\r\n\t\t\tend\r\n\r\n\t\t\t-- Format the message if needed before we run the hook.\r\n\t\t\tlocal rawText = text\r\n\t\t\tlocal maxLength = ix.config.Get(\"chatMax\")\r\n\r\n\t\t\t-- Trim the text and remove extra spaces.\r\n\t\t\ttext = string.gsub(text, \"%s+\", \" \")\r\n\r\n\t\t\tif (text:utf8len() > maxLength) then\r\n\t\t\t\ttext = text:utf8sub(0, maxLength)\r\n\t\t\tend\r\n\r\n\t\t\tif (ix.config.Get(\"chatAutoFormat\") and hook.Run(\"CanAutoFormatMessage\", speaker, chatType, text)) then\r\n\t\t\t\ttext = ix.chat.Format(text)\r\n\t\t\tend\r\n\r\n\t\t\ttext = hook.Run(\"PlayerMessageSend\", speaker, chatType, text, bAnonymous, receivers, rawText) or text\r\n\r\n\t\t\tnet.Start(\"ixChatMessage\")\r\n\t\t\t\tnet.WriteEntity(speaker)\r\n\t\t\t\tnet.WriteString(chatType)\r\n\t\t\t\tnet.WriteString(text)\r\n\t\t\t\tnet.WriteBool(bAnonymous or false)\r\n\t\t\t\tnet.WriteTable(data or {})\r\n\t\t\tnet.Send(receivers)\r\n\r\n\t\t\treturn text\r\n\t\tend\r\n\tend\r\nelse\r\n\tfunction ix.chat.Send(speaker, chatType, text, anonymous, data)\r\n\t\tlocal class = ix.chat.classes[chatType]\r\n\r\n\t\tif (class) then\r\n\t\t\t-- Trim the text and remove extra spaces.\r\n\t\t\ttext = string.gsub(text, \"%s+\", \" \")\r\n\r\n\t\t\t-- luacheck: globals CHAT_CLASS\r\n\t\t\tCHAT_CLASS = class\r\n\t\t\t\tclass:OnChatAdd(speaker, text, anonymous, data)\r\n\t\t\tCHAT_CLASS = nil\r\n\t\tend\r\n\tend\r\n\r\n\t-- Call OnChatAdd for the appropriate chatType.\r\n\tnet.Receive(\"ixChatMessage\", function()\r\n\t\tlocal client = net.ReadEntity()\r\n\t\tlocal chatType = net.ReadString()\r\n\t\tlocal text = net.ReadString()\r\n\t\tlocal anonymous = net.ReadBool()\r\n\t\tlocal data = net.ReadTable()\r\n\r\n\t\tif (IsValid(client)) then\r\n\t\t\tlocal info = {\r\n\t\t\t\tchatType = chatType,\r\n\t\t\t\ttext = text,\r\n\t\t\t\tanonymous = anonymous,\r\n\t\t\t\tdata = data\r\n\t\t\t}\r\n\r\n\t\t\thook.Run(\"MessageReceived\", client, info)\r\n\t\t\tix.chat.Send(client, info.chatType or chatType, info.text or text, info.anonymous or anonymous, info.data)\r\n\t\telse\r\n\t\t\tix.chat.Send(nil, chatType, text, anonymous, data)\r\n\t\tend\r\n\tend)\r\nend\r\n\r\n-- Add the default chat types here.\r\ndo\r\n\t-- Load the chat types after the configs so we can access changed configs.\r\n\thook.Add(\"InitializedConfig\", \"ixChatTypes\", function()\r\n\t\t-- The default in-character chat.\r\n\t\tix.chat.Register(\"ic\", {\r\n\t\t\tformat = \"%s says \\\"%s\\\"\",\r\n\t\t\tindicator = \"chatTalking\",\r\n\t\t\tGetColor = function(self, speaker, text)\r\n\t\t\t\t-- If you are looking at the speaker, make it greener to easier identify who is talking.\r\n\t\t\t\tif (LocalPlayer():GetEyeTrace().Entity == speaker) then\r\n\t\t\t\t\treturn ix.config.Get(\"chatListenColor\")\r\n\t\t\t\tend\r\n\r\n\t\t\t\t-- Otherwise, use the normal chat color.\r\n\t\t\t\treturn ix.config.Get(\"chatColor\")\r\n\t\t\tend,\r\n\t\t\tCanHear = ix.config.Get(\"chatRange\", 280)\r\n\t\t})\r\n\r\n\t\t-- Actions and such.\r\n\t\tix.chat.Register(\"me\", {\r\n\t\t\tformat = \"** %s %s\",\r\n\t\t\tGetColor = ix.chat.classes.ic.GetColor,\r\n\t\t\tCanHear = ix.config.Get(\"chatRange\", 280) * 2,\r\n\t\t\tprefix = {\"/Me\", \"/Action\"},\r\n\t\t\tdescription = \"@cmdMe\",\r\n\t\t\tindicator = \"chatPerforming\",\r\n\t\t\tdeadCanChat = true\r\n\t\t})\r\n\r\n\t\t-- Actions and such.\r\n\t\tix.chat.Register(\"it\", {\r\n\t\t\tOnChatAdd = function(self, speaker, text)\r\n\t\t\t\tchat.AddText(ix.config.Get(\"chatColor\"), \"** \"..text)\r\n\t\t\tend,\r\n\t\t\tCanHear = ix.config.Get(\"chatRange\", 280) * 2,\r\n\t\t\tprefix = {\"/It\"},\r\n\t\t\tdescription = \"@cmdIt\",\r\n\t\t\tindicator = \"chatPerforming\",\r\n\t\t\tdeadCanChat = true\r\n\t\t})\r\n\r\n\t\t-- Whisper chat.\r\n\t\tix.chat.Register(\"w\", {\r\n\t\t\tformat = \"%s whispers \\\"%s\\\"\",\r\n\t\t\tGetColor = function(self, speaker, text)\r\n\t\t\t\tlocal color = ix.chat.classes.ic:GetColor(speaker, text)\r\n\r\n\t\t\t\t-- Make the whisper chat slightly darker than IC chat.\r\n\t\t\t\treturn Color(color.r - 35, color.g - 35, color.b - 35)\r\n\t\t\tend,\r\n\t\t\tCanHear = ix.config.Get(\"chatRange\", 280) * 0.25,\r\n\t\t\tprefix = {\"/W\", \"/Whisper\"},\r\n\t\t\tdescription = \"@cmdW\",\r\n\t\t\tindicator = \"chatWhispering\"\r\n\t\t})\r\n\r\n\t\t-- Yelling out loud.\r\n\t\tix.chat.Register(\"y\", {\r\n\t\t\tformat = \"%s yells \\\"%s\\\"\",\r\n\t\t\tGetColor = function(self, speaker, text)\r\n\t\t\t\tlocal color = ix.chat.classes.ic:GetColor(speaker, text)\r\n\r\n\t\t\t\t-- Make the yell chat slightly brighter than IC chat.\r\n\t\t\t\treturn Color(color.r + 35, color.g + 35, color.b + 35)\r\n\t\t\tend,\r\n\t\t\tCanHear = ix.config.Get(\"chatRange\", 280) * 2,\r\n\t\t\tprefix = {\"/Y\", \"/Yell\"},\r\n\t\t\tdescription = \"@cmdY\",\r\n\t\t\tindicator = \"chatYelling\"\r\n\t\t})\r\n\r\n\t\t-- Out of character.\r\n\t\tix.chat.Register(\"ooc\", {\r\n\t\t\tCanSay = function(self, speaker, text)\r\n\t\t\t\tif (!ix.config.Get(\"allowGlobalOOC\")) then\r\n\t\t\t\t\tspeaker:NotifyLocalized(\"Global OOC is disabled on this server.\")\r\n\t\t\t\t\treturn false\r\n\t\t\t\telse\r\n\t\t\t\t\tlocal delay = ix.config.Get(\"oocDelay\", 10)\r\n\r\n\t\t\t\t\t-- Only need to check the time if they have spoken in OOC chat before.\r\n\t\t\t\t\tif (delay > 0 and speaker.ixLastOOC) then\r\n\t\t\t\t\t\tlocal lastOOC = CurTime() - speaker.ixLastOOC\r\n\r\n\t\t\t\t\t\t-- Use this method of checking time in case the oocDelay config changes.\r\n\t\t\t\t\t\tif (lastOOC <= delay and !CAMI.PlayerHasAccess(speaker, \"Helix - Bypass OOC Timer\", nil)) then\r\n\t\t\t\t\t\t\tspeaker:NotifyLocalized(\"oocDelay\", delay - math.ceil(lastOOC))\r\n\r\n\t\t\t\t\t\t\treturn false\r\n\t\t\t\t\t\tend\r\n\t\t\t\t\tend\r\n\r\n\t\t\t\t\t-- Save the last time they spoke in OOC.\r\n\t\t\t\t\tspeaker.ixLastOOC = CurTime()\r\n\t\t\t\tend\r\n\t\t\tend,\r\n\t\t\tOnChatAdd = function(self, speaker, text)\r\n\t\t\t\t-- @todo remove and fix actual cause of speaker being nil\r\n\t\t\t\tif (!IsValid(speaker)) then\r\n\t\t\t\t\treturn\r\n\t\t\t\tend\r\n\r\n\t\t\t\tlocal icon = \"icon16/user.png\"\r\n\r\n\t\t\t\tif (speaker:IsSuperAdmin()) then\r\n\t\t\t\t\ticon = \"icon16/shield.png\"\r\n\t\t\t\telseif (speaker:IsAdmin()) then\r\n\t\t\t\t\ticon = \"icon16/star.png\"\r\n\t\t\t\telseif (speaker:IsUserGroup(\"moderator\") or speaker:IsUserGroup(\"operator\")) then\r\n\t\t\t\t\ticon = \"icon16/wrench.png\"\r\n\t\t\t\telseif (speaker:IsUserGroup(\"vip\") or speaker:IsUserGroup(\"donator\") or speaker:IsUserGroup(\"donor\")) then\r\n\t\t\t\t\ticon = \"icon16/heart.png\"\r\n\t\t\t\tend\r\n\r\n\t\t\t\ticon = Material(hook.Run(\"GetPlayerIcon\", speaker) or icon)\r\n\r\n\t\t\t\tchat.AddText(icon, Color(255, 50, 50), \"[OOC] \", speaker, color_white, \": \"..text)\r\n\t\t\tend,\r\n\t\t\tprefix = {\"//\", \"/OOC\"},\r\n\t\t\tdescription = \"@cmdOOC\",\r\n\t\t\tnoSpaceAfter = true\r\n\t\t})\r\n\r\n\t\t-- Local out of character.\r\n\t\tix.chat.Register(\"looc\", {\r\n\t\t\tCanSay = function(self, speaker, text)\r\n\t\t\t\tlocal delay = ix.config.Get(\"loocDelay\", 0)\r\n\r\n\t\t\t\t-- Only need to check the time if they have spoken in OOC chat before.\r\n\t\t\t\tif (delay > 0 and speaker.ixLastLOOC) then\r\n\t\t\t\t\tlocal lastLOOC = CurTime() - speaker.ixLastLOOC\r\n\r\n\t\t\t\t\t-- Use this method of checking time in case the oocDelay config changes.\r\n\t\t\t\t\tif (lastLOOC <= delay and !CAMI.PlayerHasAccess(speaker, \"Helix - Bypass OOC Timer\", nil)) then\r\n\t\t\t\t\t\tspeaker:NotifyLocalized(\"loocDelay\", delay - math.ceil(lastLOOC))\r\n\r\n\t\t\t\t\t\treturn false\r\n\t\t\t\t\tend\r\n\t\t\t\tend\r\n\r\n\t\t\t\t-- Save the last time they spoke in OOC.\r\n\t\t\t\tspeaker.ixLastLOOC = CurTime()\r\n\t\t\tend,\r\n\t\t\tOnChatAdd = function(self, speaker, text)\r\n\t\t\t\tchat.AddText(Color(255, 50, 50), \"[LOOC] \", ix.config.Get(\"chatColor\"), speaker:Name()..\": \"..text)\r\n\t\t\tend,\r\n\t\t\tCanHear = ix.config.Get(\"chatRange\", 280),\r\n\t\t\tprefix = {\".//\", \"[[\", \"/LOOC\"},\r\n\t\t\tdescription = \"@cmdLOOC\",\r\n\t\t\tnoSpaceAfter = true\r\n\t\t})\r\n\r\n\t\t-- Roll information in chat.\r\n\t\tix.chat.Register(\"roll\", {\r\n\t\t\tformat = \"** %s has rolled %s out of %s.\",\r\n\t\t\tcolor = Color(155, 111, 176),\r\n\t\t\tCanHear = ix.config.Get(\"chatRange\", 280),\r\n\t\t\tdeadCanChat = true,\r\n\t\t\tOnChatAdd = function(self, speaker, text, bAnonymous, data)\r\n\t\t\t\tlocal max = data.max or 100\r\n\t\t\t\tlocal translated = L2(self.uniqueID..\"Format\", speaker:Name(), text, max)\r\n\r\n\t\t\t\tchat.AddText(self.color, translated and \"** \"..translated or string.format(self.format,\r\n\t\t\t\t\tspeaker:Name(), text, max\r\n\t\t\t\t))\r\n\t\t\tend\r\n\t\t})\r\n\r\n\t\t-- run a hook after we add the basic chat classes so schemas/plugins can access their info as soon as possible if needed\r\n\t\thook.Run(\"InitializedChatClasses\")\r\n\tend)\r\nend\r\n\r\n-- Private messages between players.\r\nix.chat.Register(\"pm\", {\r\n\tformat = \"[PM] %s -> %s: %s\",\r\n\tcolor = Color(125, 150, 75, 255),\r\n\tdeadCanChat = true,\r\n\r\n\tOnChatAdd = function(self, speaker, text, bAnonymous, data)\r\n\t\tchat.AddText(self.color, string.format(self.format, speaker:GetName(), data.target:GetName(), text))\r\n\r\n\t\tif (LocalPlayer() != speaker) then\r\n\t\t\tsurface.PlaySound(\"hl1/fvox/bell.wav\")\r\n\t\tend\r\n\tend\r\n})\r\n\r\n-- Global events.\r\nix.chat.Register(\"event\", {\r\n\tCanHear = 1000000,\r\n\tOnChatAdd = function(self, speaker, text)\r\n\t\tchat.AddText(Color(255, 150, 0), text)\r\n\tend,\r\n\tindicator = \"chatPerforming\"\r\n})\r\n\r\nix.chat.Register(\"connect\", {\r\n\tCanSay = function(self, speaker, text)\r\n\t\treturn !IsValid(speaker)\r\n\tend,\r\n\tOnChatAdd = function(self, speaker, text)\r\n\t\tlocal icon = ix.util.GetMaterial(\"icon16/user_add.png\")\r\n\r\n\t\tchat.AddText(icon, Color(150, 150, 200), L(\"playerConnected\", text))\r\n\tend,\r\n\tnoSpaceAfter = true\r\n})\r\n\r\nix.chat.Register(\"disconnect\", {\r\n\tCanSay = function(self, speaker, text)\r\n\t\treturn !IsValid(speaker)\r\n\tend,\r\n\tOnChatAdd = function(self, speaker, text)\r\n\t\tlocal icon = ix.util.GetMaterial(\"icon16/user_delete.png\")\r\n\r\n\t\tchat.AddText(icon, Color(200, 150, 200), L(\"playerDisconnected\", text))\r\n\tend,\r\n\tnoSpaceAfter = true\r\n})\r\n\r\nix.chat.Register(\"notice\", {\r\n\tCanSay = function(self, speaker, text)\r\n\t\treturn !IsValid(speaker)\r\n\tend,\r\n\tOnChatAdd = function(self, speaker, text, bAnonymous, data)\r\n\t\tlocal icon = ix.util.GetMaterial(data.bError and \"icon16/comment_delete.png\" or \"icon16/comment.png\")\r\n\t\tchat.AddText(icon, data.bError and Color(200, 175, 200, 255) or Color(175, 200, 255), text)\r\n\tend,\r\n\tnoSpaceAfter = true\r\n})\r\n\r\n-- Why does ULX even have a /me command?\r\nhook.Remove(\"PlayerSay\", \"ULXMeCheck\")\r\n"
  },
  {
    "path": "gamemode/core/libs/sh_class.lua",
    "content": "\n--[[--\nHelper library for loading/getting class information.\n\nClasses are temporary assignments for characters - analogous to a \"job\" in a faction. For example, you may have a police faction\nin your schema, and have \"police recruit\" and \"police chief\" as different classes in your faction. Anyone can join a class in\ntheir faction by default, but you can restrict this as you need with `CLASS.CanSwitchTo`.\n]]\n-- @module ix.class\n\n--- Class definition table returned by `ix.class.Get`.\n-- @realm shared\n-- @tab class Class definition table.\n-- Default keys:\n--\n-- - `index` (number) - numeric class ID\n-- - `uniqueID` (string) - stable identifier (e.g. `citizen`)\n-- - `name` (string)\n-- - `description` (string)\n-- - `faction` (number) - Faction ID this class belongs to\n-- - `isDefault` (boolean) - whether it is the default class for the faction\n-- - `limit` (number) - maximum number of players allowed in this class (0 for unlimited)\n-- - `CanSwitchTo` (function|nil) - optional callback to check if a player may switch\n\n\nif (SERVER) then\n\tutil.AddNetworkString(\"ixClassUpdate\")\nend\n\nix.class = ix.class or {}\nix.class.list = {}\n\nlocal charMeta = ix.meta.character\n\n--- Loads classes from a directory.\n-- @realm shared\n-- @internal\n-- @string directory The path to the class files.\nfunction ix.class.LoadFromDir(directory)\n\tfor _, v in ipairs(file.Find(directory..\"/*.lua\", \"LUA\")) do\n\t\t-- Get the name without the \"sh_\" prefix and \".lua\" suffix.\n\t\tlocal niceName = v:sub(4, -5)\n\t\t-- Determine a numeric identifier for this class.\n\t\tlocal index = #ix.class.list + 1\n\t\tlocal halt\n\n\t\tfor _, v2 in ipairs(ix.class.list) do\n\t\t\tif (v2.uniqueID == niceName) then\n\t\t\t\thalt = true\n\n                break\n\t\t\tend\n\t\tend\n\n\t\tif (halt == true) then\n\t\t\tcontinue\n\t\tend\n\n\t\t-- Set up a global table so the file has access to the class table.\n\t\tCLASS = {index = index, uniqueID = niceName}\n\t\t\tCLASS.name = \"Unknown\"\n\t\t\tCLASS.description = \"No description available.\"\n\t\t\tCLASS.limit = 0\n\n\t\t\t-- For future use with plugins.\n\t\t\tif (PLUGIN) then\n\t\t\t\tCLASS.plugin = PLUGIN.uniqueID\n\t\t\tend\n\n\t\t\tix.util.Include(directory..\"/\"..v, \"shared\")\n\n\t\t\t-- Why have a class without a faction?\n\t\t\tif (!CLASS.faction or !team.Valid(CLASS.faction)) then\n\t\t\t\tErrorNoHalt(\"Class '\"..niceName..\"' does not have a valid faction!\\n\")\n\t\t\t\tCLASS = nil\n\n\t\t\t\tcontinue\n\t\t\tend\n\n\t\t\t-- Allow classes to be joinable by default.\n\t\t\tif (!CLASS.CanSwitchTo) then\n\t\t\t\tCLASS.CanSwitchTo = function(client)\n\t\t\t\t\treturn true\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tix.class.list[index] = CLASS\n\t\tCLASS = nil\n\tend\nend\n\n--- Determines if a player is allowed to join a specific class.\n-- @realm shared\n-- @player client Player to check\n-- @number class Index of the class\n-- @treturn bool Whether or not the player can switch to the class.\n-- @treturn string The reason why the player cannot switch (if applicable).\n-- @usage -- Check if a player can join class ID 2.\n-- -- For our example, they can't- because they are in the wrong faction.\n-- local canJoin, reason = ix.class.CanSwitchTo(player, 2)\n-- if (!canJoin) then\n--     print(\"Player cannot join class: \"..reason)\n-- end\n-- > Player cannot join class: not correct team\nfunction ix.class.CanSwitchTo(client, class)\n\t-- Get the class table by its numeric identifier.\n\tlocal info = ix.class.list[class]\n\n\t-- See if the class exists.\n\tif (!info) then\n\t\treturn false, \"no info\"\n\tend\n\n\t-- If the player's faction matches the class's faction.\n\tif (client:Team() != info.faction) then\n\t\treturn false, \"not correct team\"\n\tend\n\n\tif (client:GetCharacter():GetClass() == class) then\n\t\treturn false, \"same class request\"\n\tend\n\n\tif (info.limit > 0) then\n\t\tif (#ix.class.GetPlayers(info.index) >= info.limit) then\n\t\t\treturn false, \"class is full\"\n\t\tend\n\tend\n\n\tif (hook.Run(\"CanPlayerJoinClass\", client, class, info) == false) then\n\t\treturn false\n\tend\n\n\t-- See if the class allows the player to join it.\n\treturn info:CanSwitchTo(client)\nend\n\n--- Gets a class definition table by numeric identifier.\n-- @realm shared\n-- @tparam number identifier Numeric class identifier.\n-- @treturn table|nil Class definition table (see `class` table docs).\n-- @usage -- Print the name of class ID 1\n-- print(ix.class.Get(1).name)\n-- > Citizen\nfunction ix.class.Get(identifier)\n\treturn ix.class.list[identifier]\nend\n\n--- Retrieves all players currently assigned to a specific class.\n-- @realm shared\n-- @number class Index of the class\n-- @treturn table Numerically indexed table of players in the class, or an empty table if none are found.\n-- @usage -- Print all players in class ID 1\n-- for _, ply in ipairs(ix.class.GetPlayers(1)) do\n--     print(ply:GetName())\n-- end\n-- > Player1\n-- > Player2\n-- > etc\nfunction ix.class.GetPlayers(class)\n\tlocal players = {}\n\n\tfor _, v in player.Iterator() do\n\t\tlocal char = v:GetCharacter()\n\n\t\tif (char and char:GetClass() == class) then\n\t\t\ttable.insert(players, v)\n\t\tend\n\tend\n\n\treturn players\nend\n\nif (SERVER) then\n\t--- Character class methods\n\t-- @classmod Character\n\n\t--- Makes this character join a class. This automatically calls `KickClass` for you.\n\t-- @realm server\n\t-- @number class Index of the class to join\n\t-- @treturn bool Whether or not the character has successfully joined the class\n\tfunction charMeta:JoinClass(class)\n\t\tif (!class) then\n\t\t\tself:KickClass()\n\t\t\treturn false\n\t\tend\n\n\t\tlocal oldClass = self:GetClass()\n\t\tlocal client = self:GetPlayer()\n\n\t\tif (ix.class.CanSwitchTo(client, class)) then\n\t\t\tself:SetClass(class)\n\t\t\thook.Run(\"PlayerJoinedClass\", client, class, oldClass)\n\n\t\t\treturn true\n\t\tend\n\n\t\treturn false\n\tend\n\n\t--- Kicks this character out of the class they are currently in.\n\t-- @realm server\n\tfunction charMeta:KickClass()\n\t\tlocal client = self:GetPlayer()\n\t\tif (!client) then return end\n\n\t\tlocal goClass\n\n\t\tfor k, v in pairs(ix.class.list) do\n\t\t\tif (v.faction == client:Team() and v.isDefault) then\n\t\t\t\tgoClass = k\n\n\t\t\t\tbreak\n\t\t\tend\n\t\tend\n\n\t\tif (!goClass) then\n\t\t\tErrorNoHaltWithStack(\"[Helix] No default class set for faction '\" .. team.GetName(client:Team()) .. \"'\")\n\n\t\t\treturn\n\t\tend\n\n\t\tself:JoinClass(goClass)\n\n\t\thook.Run(\"PlayerJoinedClass\", client, goClass)\n\tend\n\n\tfunction GM:PlayerJoinedClass(client, class, oldClass)\n\t\tlocal info = ix.class.list[class]\n\t\tlocal info2 = ix.class.list[oldClass]\n\n\t\tif (info.OnSet) then\n\t\t\tinfo:OnSet(client)\n\t\tend\n\n\t\tif (info2 and info2.OnLeave) then\n\t\t\tinfo2:OnLeave(client)\n\t\tend\n\n\t\tnet.Start(\"ixClassUpdate\")\n\t\t\tnet.WriteEntity(client)\n\t\tnet.Broadcast()\n\tend\nend\n"
  },
  {
    "path": "gamemode/core/libs/sh_command.lua",
    "content": "\n--[[--\nRegistration, parsing, and handling of commands.\n\nCommands can be ran through the chat with slash commands or they can be executed through the console. Commands can be manually\nrestricted to certain usergroups using a [CAMI](https://github.com/glua/CAMI)-compliant admin mod.\n]]\n-- @module ix.command\n\n--- When registering commands with `ix.command.Add`, you'll need to pass in a valid command structure. This is simply a table\n-- with various fields defined to describe the functionality of the command.\n-- @realm shared\n-- @table CommandStructure\n-- @field[type=function] OnRun This function is called when the command has passed all the checks and can execute. The first two\n-- arguments will be the running command table and the calling player. If the arguments field has been specified, the arguments\n-- will be passed as regular function parameters rather than in a table.\n--\n-- When the arguments field is defined: `OnRun(self, client, target, length, message)`\n--\n-- When the arguments field is NOT defined: `OnRun(self, client, arguments)`\n-- @field[type=string,opt=\"@noDesc\"] description The help text that appears when the user types in the command. If the string is\n-- prefixed with `\"@\"`, it will use a language phrase.\n-- @field[type=table,opt=nil] argumentNames An array of strings corresponding to each argument of the command. This ignores the\n-- name that's specified in the `OnRun` function arguments and allows you to use any string to change the text that displays\n-- in the command's syntax help. When using this field, make sure that the amount is equal to the amount of arguments, as such:\n-- \tCOMMAND.arguments = {ix.type.character, ix.type.number}\n-- \tCOMMAND.argumentNames = {\"target char\", \"cash (1-1000)\"}\n-- @field[type=table,opt] arguments If this field is defined, then additional checks will be performed to ensure that the\n-- arguments given to the command are valid. This removes extra boilerplate code since all the passed arguments are guaranteed\n-- to be valid. See `CommandArgumentsStructure` for more information.\n-- @field[type=boolean,opt=false] adminOnly Provides an additional check to see if the user is an admin before running.\n-- @field[type=boolean,opt=false] superAdminOnly Provides an additional check to see if the user is a superadmin before running.\n-- @field[type=string,opt=nil] privilege Manually specify a privilege name for this command. It will always be prefixed with\n-- `\"Helix - \"`. This is used in the case that you want to group commands under the same privilege, or use a privilege that\n-- you've already defined (i.e grouping `/CharBan` and `/CharUnban` into the `Helix - Ban Character` privilege).\n-- @field[type=function,opt=nil] OnCheckAccess This callback checks whether or not the player is allowed to run the command.\n-- This callback should **NOT** be used in conjunction with `adminOnly` or `superAdminOnly`, as populating those\n-- fields create a custom a `OnCheckAccess` callback for you internally. This is used in cases where you want more fine-grained\n-- access control for your command.\n--\n-- Keep in mind that this is a **SHARED** callback; the command will not show up the client if the callback returns `false`.\n\n--- Rather than checking the validity for arguments in your command's `OnRun` function, you can have Helix do it for you to\n-- reduce the amount of boilerplate code that needs to be written. This can be done by populating the `arguments` field.\n--\n-- When using the `arguments` field in your command, you are specifying specific types that you expect to receive when the\n-- command is ran successfully. This means that before `OnRun` is called, the arguments passed to the command from a user will\n-- be verified to be valid. Each argument is an `ix.type` entry that specifies the expected type for that argument. Optional\n-- arguments can be specified by using a bitwise OR with the special `ix.type.optional` type. When specified as optional, the\n-- argument can be `nil` if the user has not entered anything for that argument - otherwise it will be valid.\n--\n-- Note that optional arguments must always be at the end of a list of arguments - or rather, they must not follow a required\n-- argument. The `syntax` field will be automatically populated when using strict arguments, which means you shouldn't fill out\n-- the `syntax` field yourself. The arguments you specify will have the same names as the arguments in your OnRun function.\n--\n-- Consider this example command:\n-- \tix.command.Add(\"CharSlap\", {\n-- \t\tdescription = \"Slaps a character with a large trout.\",\n-- \t\tadminOnly = true,\n-- \t\targuments = {\n-- \t\t\tix.type.character,\n-- \t\t\tbit.bor(ix.type.number, ix.type.optional)\n-- \t\t},\n-- \t\tOnRun = function(self, client, target, damage)\n-- \t\t\t-- WHAM!\n-- \t\tend\n-- \t})\n-- Here, we've specified the first argument called `target` to be of type `character`, and the second argument called `damage`\n-- to be of type `number`. The `damage` argument is optional, meaning that the command will still run if the user has not\n-- specified any value for the damage. In this case, we'll need to check if it was specified by doing a simple\n-- `if (damage) then`. The syntax field will be automatically populated with the value `\"<target: character> [damage: number]\"`.\n-- @realm shared\n-- @table CommandArgumentsStructure\n\nix.command = ix.command or {}\nix.command.list = ix.command.list or {}\n\nlocal COMMAND_PREFIX = \"/\"\n\nlocal function ArgumentCheckStub(command, client, given)\n\tlocal arguments = command.arguments\n\tlocal result = {}\n\n\tfor i = 1, #arguments do\n\t\tlocal bOptional = bit.band(arguments[i], ix.type.optional) == ix.type.optional\n\t\tlocal argType = bOptional and bit.bxor(arguments[i], ix.type.optional) or arguments[i]\n\t\tlocal argument = given[i]\n\n\t\tif (!argument and !bOptional) then\n\t\t\treturn L(\"invalidArg\", client, i)\n\t\tend\n\n\t\tif (argType == ix.type.string) then\n\t\t\tif (!argument and bOptional) then\n\t\t\t\tresult[#result + 1] = nil\n\t\t\telse\n\t\t\t\tresult[#result + 1] = tostring(argument)\n\t\t\tend\n\t\telseif (argType == ix.type.text) then\n\t\t\tresult[#result + 1] = table.concat(given, \" \", i) or \"\"\n\t\t\tbreak\n\t\telseif (argType == ix.type.number) then\n\t\t\tlocal value = tonumber(argument)\n\n\t\t\tif (!bOptional and !value) then\n\t\t\t\treturn L(\"invalidArg\", client, i)\n\t\t\tend\n\n\t\t\tresult[#result + 1] = value\n\t\telseif (argType == ix.type.player or argType == ix.type.character) then\n\t\t\tlocal bPlayer = argType == ix.type.player\n\t\t\tlocal value = ix.util.FindPlayer(argument or \"\") -- argument could be nil due to optional type\n\n\t\t\t-- FindPlayer emits feedback for us\n\t\t\tif (!value and !bOptional) then\n\t\t\t\treturn L(bPlayer and \"plyNoExist\" or \"charNoExist\", client)\n\t\t\tend\n\n\t\t\t-- check for the character if we're using the character type\n\t\t\tif (!bPlayer) then\n\t\t\t\tlocal character = value:GetCharacter()\n\n\t\t\t\tif (!character) then\n\t\t\t\t\treturn L(\"charNoExist\", client)\n\t\t\t\tend\n\n\t\t\t\tvalue = character\n\t\t\tend\n\n\t\t\tresult[#result + 1] = value\n\t\telseif (argType == ix.type.steamid) then\n\t\t\tlocal value = argument:match(\"STEAM_(%d+):(%d+):(%d+)\")\n\n\t\t\tif (!value and bOptional) then\n\t\t\t\treturn L(\"invalidArg\", client, i)\n\t\t\tend\n\n\t\t\tresult[#result + 1] = value\n\t\telseif (argType == ix.type.bool) then\n\t\t\tif (argument == nil and bOptional) then\n\t\t\t\tresult[#result + 1] = nil\n\t\t\telse\n\t\t\t\tresult[#result + 1] = tobool(argument)\n\t\t\tend\n\t\tend\n\tend\n\n\treturn result\nend\n\n--- Creates a new command.\n-- @realm shared\n-- @string command Name of the command (recommended in UpperCamelCase)\n-- @tparam CommandStructure data Data describing the command\n-- @see CommandStructure\n-- @see CommandArgumentsStructure\nfunction ix.command.Add(command, data)\n\tdata.name = string.gsub(command, \"%s\", \"\")\n\tdata.description = data.description or \"\"\n\n\tcommand = command:lower()\n\tdata.uniqueID = command\n\n\t-- Why bother adding a command if it doesn't do anything.\n\tif (!data.OnRun) then\n\t\treturn ErrorNoHalt(\"Command '\"..command..\"' does not have a callback, not adding!\\n\")\n\tend\n\n\t-- Add a function to get the description that can be overridden.\n\tif (!data.GetDescription) then\n\t\t-- Check if the description is using a language string.\n\t\tif (data.description:sub(1, 1) == \"@\") then\n\t\t\tfunction data:GetDescription()\n\t\t\t\treturn L(self.description:sub(2))\n\t\t\tend\n\t\telse\n\t\t\t-- Otherwise just return the raw description.\n\t\t\tfunction data:GetDescription()\n\t\t\t\treturn self.description\n\t\t\tend\n\t\tend\n\tend\n\n\t-- OnCheckAccess by default will rely on CAMI for access information with adminOnly/superAdminOnly being fallbacks\n\tif (!data.OnCheckAccess) then\n\t\tif (data.group) then\n\t\t\tErrorNoHalt(\"Command '\" .. data.name .. \"' tried to use the deprecated field 'group'!\\n\")\n\t\t\treturn\n\t\tend\n\n\t\tlocal privilege = \"Helix - \" .. (isstring(data.privilege) and data.privilege or data.name)\n\n\t\t-- we could be using a previously-defined privilege\n\t\tif (!CAMI.GetPrivilege(privilege)) then\n\t\t\tCAMI.RegisterPrivilege({\n\t\t\t\tName = privilege,\n\t\t\t\tMinAccess = data.superAdminOnly and \"superadmin\" or (data.adminOnly and \"admin\" or \"user\"),\n\t\t\t\tDescription = data.description\n\t\t\t})\n\t\tend\n\n\t\tfunction data:OnCheckAccess(client)\n\t\t\tlocal bHasAccess, _ = CAMI.PlayerHasAccess(client, privilege, nil)\n\t\t\treturn bHasAccess\n\t\tend\n\tend\n\n\t-- if we have an arguments table, then we're using the new command format\n\tif (data.arguments) then\n\t\tlocal bFirst = true\n\t\tlocal bLastOptional = false\n\t\tlocal bHasArgumentNames = istable(data.argumentNames)\n\n\t\tdata.syntax = \"\" -- @todo deprecate this in favour of argumentNames\n\t\tdata.argumentNames = bHasArgumentNames and data.argumentNames or {}\n\n\t\t-- if one argument is supplied by itself, put it into a table\n\t\tif (!istable(data.arguments)) then\n\t\t\tdata.arguments = {data.arguments}\n\t\tend\n\n\t\tif (bHasArgumentNames and #data.argumentNames != #data.arguments) then\n\t\t\treturn ErrorNoHalt(string.format(\n\t\t\t\t\"Command '%s' doesn't have argument names that correspond to each argument\\n\", command\n\t\t\t))\n\t\tend\n\n\t\t-- check the arguments table to see if its entries are valid\n\t\tfor i = 1, #data.arguments do\n\t\t\tlocal argument = data.arguments[i]\n\t\t\tlocal argumentName = debug.getlocal(data.OnRun, 2 + i)\n\n\t\t\tif (argument == ix.type.optional) then\n\t\t\t\treturn ErrorNoHalt(string.format(\n\t\t\t\t\t\"Command '%s' tried to use an optional argument for #%d without specifying type\\n\", command, i\n\t\t\t\t))\n\t\t\telseif (!isnumber(argument)) then\n\t\t\t\treturn ErrorNoHalt(string.format(\n\t\t\t\t\t\"Command '%s' tried to use an invalid type for argument #%d\\n\", command, i\n\t\t\t\t))\n\t\t\telseif (argument == ix.type.array or bit.band(argument, ix.type.array) > 0) then\n\t\t\t\treturn ErrorNoHalt(string.format(\n\t\t\t\t\t\"Command '%s' tried to use an unsupported type 'array' for argument #%d\\n\", command, i\n\t\t\t\t))\n\t\t\tend\n\n\t\t\tlocal bOptional = bit.band(argument, ix.type.optional) > 0\n\t\t\targument = bOptional and bit.bxor(argument, ix.type.optional) or argument\n\n\t\t\tif (!ix.type[argument]) then\n\t\t\t\treturn ErrorNoHalt(string.format(\n\t\t\t\t\t\"Command '%s' tried to use an invalid type for argument #%d\\n\", command, i\n\t\t\t\t))\n\t\t\telseif (!isstring(argumentName)) then\n\t\t\t\treturn ErrorNoHalt(string.format(\n\t\t\t\t\t\"Command '%s' is missing function argument for command argument #%d\\n\", command, i\n\t\t\t\t))\n\t\t\telseif (argument == ix.type.text and i != #data.arguments) then\n\t\t\t\treturn ErrorNoHalt(string.format(\n\t\t\t\t\t\"Command '%s' tried to use a text argument outside of the last argument\\n\", command\n\t\t\t\t))\n\t\t\telseif (!bOptional and bLastOptional) then\n\t\t\t\treturn ErrorNoHalt(string.format(\n\t\t\t\t\t\"Command '%s' tried to use an required argument after an optional one\\n\", command\n\t\t\t\t))\n\t\t\tend\n\n\t\t\t-- text is always optional and will return an empty string if nothing is specified, rather than nil\n\t\t\tif (argument == ix.type.text) then\n\t\t\t\tdata.arguments[i] = bit.bor(ix.type.text, ix.type.optional)\n\t\t\t\tbOptional = true\n\t\t\tend\n\n\t\t\tif (!bHasArgumentNames) then\n\t\t\t\tdata.argumentNames[i] = argumentName\n\t\t\tend\n\n\t\t\tdata.syntax = data.syntax .. (bFirst and \"\" or \" \") ..\n\t\t\t\tstring.format((bOptional and \"[%s: %s]\" or \"<%s: %s>\"), argumentName, ix.type[argument])\n\n\t\t\tbFirst = false\n\t\t\tbLastOptional = bOptional\n\t\tend\n\n\t\tif (data.syntax:utf8len() == 0) then\n\t\t\tdata.syntax = \"<none>\"\n\t\tend\n\telse\n\t\tdata.syntax = data.syntax or \"<none>\"\n\tend\n\n\t-- Add the command to the list of commands.\n\tlocal alias = data.alias\n\n\tif (alias) then\n\t\tif (istable(alias)) then\n\t\t\tfor _, v in ipairs(alias) do\n\t\t\t\tix.command.list[v:lower()] = data\n\t\t\tend\n\t\telseif (isstring(alias)) then\n\t\t\tix.command.list[alias:lower()] = data\n\t\tend\n\tend\n\n\tix.command.list[command] = data\nend\n\n--- Returns true if a player is allowed to run a certain command.\n-- @realm shared\n-- @player client Player to check access for\n-- @string command Name of the command to check access for\n-- @treturn bool Whether or not the player is allowed to run the command\nfunction ix.command.HasAccess(client, command)\n\tcommand = ix.command.list[command:lower()]\n\n\tif (command) then\n\t\tif (command.OnCheckAccess) then\n\t\t\treturn command:OnCheckAccess(client)\n\t\telse\n\t\t\treturn true\n\t\tend\n\tend\n\n\treturn false\nend\n\n--- Returns a table of arguments from a given string.\n-- Words separated by spaces will be considered one argument. To have an argument containing multiple words, they must be\n-- contained within quotation marks.\n-- @realm shared\n-- @string text String to extract arguments from\n-- @treturn table Arguments extracted from string\n-- @usage PrintTable(ix.command.ExtractArgs(\"these are \\\"some arguments\\\"\"))\n-- > 1 = these\n-- > 2 = are\n-- > 3 = some arguments\nfunction ix.command.ExtractArgs(text)\n\tlocal skip = 0\n\tlocal arguments = {}\n\tlocal curString = \"\"\n\n\tfor i = 1, text:utf8len() do\n\t\tif (i <= skip) then continue end\n\n\t\tlocal c = text:utf8sub(i, i)\n\n\t\tif (c == \"\\\"\") then\n\t\t\tlocal match = text:utf8sub(i):match(\"%b\\\"\\\"\")\n\n\t\t\tif (match) then\n\t\t\t\tcurString = \"\"\n\t\t\t\tskip = i + match:utf8len()\n\t\t\t\targuments[#arguments + 1] = match:utf8sub(2, -2)\n\t\t\telse\n\t\t\t\tcurString = curString..c\n\t\t\tend\n\t\telseif (c == \" \" and curString != \"\") then\n\t\t\targuments[#arguments + 1] = curString\n\t\t\tcurString = \"\"\n\t\telse\n\t\t\tif (c == \" \" and curString == \"\") then\n\t\t\t\tcontinue\n\t\t\tend\n\n\t\t\tcurString = curString..c\n\t\tend\n\tend\n\n\tif (curString != \"\") then\n\t\targuments[#arguments + 1] = curString\n\tend\n\n\treturn arguments\nend\n\n--- Returns an array of potential commands by unique id.\n-- When bSorted is true, the commands will be sorted by name. When bReorganize is true, it will move any exact match to the top\n-- of the array. When bRemoveDupes is true, it will remove any commands that have the same NAME.\n-- @realm shared\n-- @string identifier Search query\n-- @bool[opt=false] bSorted Whether or not to sort the commands by name\n-- @bool[opt=false] bReorganize Whether or not any exact match will be moved to the top of the array\n-- @bool[opt=false] bRemoveDupes Whether or not to remove any commands that have the same name\n-- @treturn table Array of command tables whose name partially or completely matches the search query\nfunction ix.command.FindAll(identifier, bSorted, bReorganize, bRemoveDupes)\n\tlocal result = {}\n\tlocal iterator = bSorted and SortedPairs or pairs\n\tlocal fullMatch\n\n\tidentifier = identifier:lower()\n\n\tif (identifier == \"/\") then\n\t\t-- we don't simply copy because we need numeric indices\n\t\tfor _, v in iterator(ix.command.list) do\n\t\t\tresult[#result + 1] = v\n\t\tend\n\n\t\treturn result\n\telseif (identifier:utf8sub(1, 1) == \"/\") then\n\t\tidentifier = identifier:utf8sub(2)\n\tend\n\n\tfor k, v in iterator(ix.command.list) do\n\t\tif (k:match(identifier)) then\n\t\t\tlocal index = #result + 1\n\t\t\tresult[index] = v\n\n\t\t\tif (k == identifier) then\n\t\t\t\tfullMatch = index\n\t\t\tend\n\t\tend\n\tend\n\n\tif (bReorganize and fullMatch and fullMatch != 1) then\n\t\tresult[1], result[fullMatch] = result[fullMatch], result[1]\n\tend\n\n\tif (bRemoveDupes) then\n\t\tlocal commandNames = {}\n\n\t\t-- using pairs intead of ipairs because we might remove from array\n\t\tfor k, v in pairs(result) do\n\t\t\tif (commandNames[v.name]) then\n\t\t\t\ttable.remove(result, k)\n\t\t\tend\n\n\t\t\tcommandNames[v.name] = true\n\t\tend\n\tend\n\n\treturn result\nend\n\nif (SERVER) then\n\tutil.AddNetworkString(\"ixCommand\")\n\n\t--- Attempts to find a player by an identifier. If unsuccessful, a notice will be displayed to the specified player. The\n\t-- search criteria is derived from `ix.util.FindPlayer`.\n\t-- @realm server\n\t-- @player client Player to give a notification to if the player could not be found\n\t-- @string name Search query\n\t-- @treturn[1] player Player that matches the given search query\n\t-- @treturn[2] nil If a player could not be found\n\t-- @see ix.util.FindPlayer\n\tfunction ix.command.FindPlayer(client, name)\n\t\tlocal target = isstring(name) and ix.util.FindPlayer(name) or NULL\n\n\t\tif (IsValid(target)) then\n\t\t\treturn target\n\t\telse\n\t\t\tclient:NotifyLocalized(\"plyNoExist\")\n\t\tend\n\tend\n\n\t--- Forces a player to execute a command by name.\n\t-- @realm server\n\t-- @player client Player who is executing the command\n\t-- @string command Full name of the command to be executed. This string gets lowered, but it's good practice to stick with\n\t-- the exact name of the command\n\t-- @tab arguments Array of arguments to be passed to the command\n\t-- @usage ix.command.Run(player.GetByID(1), \"Roll\", {10})\n\tfunction ix.command.Run(client, command, arguments)\n\t\tif ((client.ixCommandCooldown or 0) > RealTime()) then\n\t\t\treturn\n\t\tend\n\n\t\tcommand = ix.command.list[tostring(command):lower()]\n\n\t\tif (!command) then\n\t\t\treturn\n\t\tend\n\n\t\t-- we throw it into a table since arguments get unpacked and only\n\t\t-- the arguments table gets passed in by default\n\t\tlocal argumentsTable = arguments\n\t\targuments = {argumentsTable}\n\n\t\t-- if feedback is non-nil, we can assume that the command failed\n\t\t-- and is a phrase string\n\t\tlocal feedback\n\n\t\t-- check for group access\n\t\tif (command.OnCheckAccess) then\n\t\t\tlocal bSuccess, phrase = command:OnCheckAccess(client)\n\t\t\tfeedback = !bSuccess and L(phrase and phrase or \"noPerm\", client) or nil\n\t\tend\n\n\t\t-- check for strict arguments\n\t\tif (!feedback and command.arguments) then\n\t\t\targuments = ArgumentCheckStub(command, client, argumentsTable)\n\n\t\t\tif (isstring(arguments)) then\n\t\t\t\tfeedback = arguments\n\t\t\tend\n\t\tend\n\n\t\t-- run the command if all the checks passed\n\t\tif (!feedback) then\n\t\t\tlocal results = {command:OnRun(client, unpack(arguments))}\n\t\t\tlocal phrase = results[1]\n\n\t\t\t-- check to see if the command has returned a phrase string and display it\n\t\t\tif (isstring(phrase)) then\n\t\t\t\tif (IsValid(client)) then\n\t\t\t\t\tif (phrase:sub(1, 1) == \"@\") then\n\t\t\t\t\t\tclient:NotifyLocalized(phrase:sub(2), unpack(results, 2))\n\t\t\t\t\telse\n\t\t\t\t\t\tclient:Notify(phrase)\n\t\t\t\t\tend\n\t\t\t\telse\n\t\t\t\t\t-- print message since we're running from the server console\n\t\t\t\t\tprint(phrase)\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tclient.ixCommandCooldown = RealTime() + 0.5\n\n\t\t\tif (IsValid(client)) then\n\t\t\t\tix.log.Add(client, \"command\", COMMAND_PREFIX .. command.name, argumentsTable and table.concat(argumentsTable, \" \"))\n\t\t\tend\n\t\telse\n\t\t\tclient:Notify(feedback)\n\t\tend\n\tend\n\n\t--- Parses a chat string and runs the command if one is found. Specifically, it checks for commands in a string with the\n\t-- format `/CommandName some arguments`\n\t-- @realm server\n\t-- @player client Player who is executing the command\n\t-- @string text Input string to search for the command format\n\t-- @string[opt] realCommand Specific command to check for. If this is specified, it will not try to run any command that's\n\t-- found at the beginning - only if it matches `realCommand`\n\t-- @tab[opt] arguments Array of arguments to pass to the command. If not specified, it will try to extract it from the\n\t-- string specified in `text` using `ix.command.ExtractArgs`\n\t-- @treturn bool Whether or not a command has been found\n\t-- @usage ix.command.Parse(player.GetByID(1), \"/roll 10\")\n\tfunction ix.command.Parse(client, text, realCommand, arguments)\n\t\tif (realCommand or text:utf8sub(1, 1) == COMMAND_PREFIX) then\n\t\t\t-- See if the string contains a command.\n\t\t\tlocal match = realCommand or text:utf8lower():match(COMMAND_PREFIX..\"([_%w]+)\")\n\n\t\t\t-- is it unicode text?\n\t\t\tif (!match) then\n\t\t\t\tlocal post = string.Explode(\" \", text)\n\t\t\t\tlocal len = string.len(post[1])\n\n\t\t\t\tmatch = post[1]:utf8sub(2, len)\n\t\t\tend\n\n\t\t\tmatch = match:utf8lower()\n\n\t\t\tlocal command = ix.command.list[match]\n\t\t\t-- We have a valid, registered command.\n\t\t\tif (command) then\n\t\t\t\t-- Get the arguments like a console command.\n\t\t\t\tif (!arguments) then\n\t\t\t\t\targuments = ix.command.ExtractArgs(text:utf8sub(match:utf8len() + 3))\n\t\t\t\tend\n\n\t\t\t\t-- Runs the actual command.\n\t\t\t\tix.command.Run(client, match, arguments)\n\t\t\telse\n\t\t\t\tif (IsValid(client)) then\n\t\t\t\t\tclient:NotifyLocalized(\"cmdNoExist\")\n\t\t\t\telse\n\t\t\t\t\tprint(\"Sorry, that command does not exist.\")\n\t\t\t\tend\n\t\t\tend\n\n\t\t\treturn true\n\t\tend\n\n\t\treturn false\n\tend\n\n\tconcommand.Add(\"ix\", function(client, _, arguments)\n\t\tlocal command = arguments[1]\n\t\ttable.remove(arguments, 1)\n\n\t\tix.command.Parse(client, nil, command or \"\", arguments)\n\tend)\n\n\tnet.Receive(\"ixCommand\", function(length, client)\n\t\tif ((client.ixNextCmd or 0) < CurTime()) then\n\t\t\tlocal command = net.ReadString()\n\t\t\tlocal indices = net.ReadUInt(4)\n\t\t\tlocal arguments = {}\n\n\t\t\tfor _ = 1, indices do\n\t\t\t\tlocal value = net.ReadType()\n\n\t\t\t\tif (isstring(value) or isnumber(value)) then\n\t\t\t\t\targuments[#arguments + 1] = tostring(value)\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tix.command.Parse(client, nil, command, arguments)\n\t\t\tclient.ixNextCmd = CurTime() + 0.2\n\t\tend\n\tend)\nelse\n\t--- Request the server to run a command. This mimics similar functionality to the client typing `/CommandName` in the chatbox.\n\t-- @realm client\n\t-- @string command Unique ID of the command\n\t-- @param ... Arguments to pass to the command\n\t-- @usage ix.command.Send(\"roll\", 10)\n\tfunction ix.command.Send(command, ...)\n\t\tlocal arguments =  {...}\n\n\t\tnet.Start(\"ixCommand\")\n\t\tnet.WriteString(command)\n\t\tnet.WriteUInt(#arguments, 4)\n\n\t\tfor _, v in ipairs(arguments) do\n\t\t\tnet.WriteType(v)\n\t\tend\n\n\t\tnet.SendToServer()\n\tend\n\n\tconcommand.Add(\"ix\", function(client, _, arguments)\n\t\tix.command.Send(table.remove(arguments, 1), unpack(arguments))\n\tend, function(_, arguments)\n\t\targuments = arguments:TrimLeft()\n\n\t\tlocal autocomplete = {}\n\t\tlocal command = string.Explode(\" \", arguments)[1]\n\n\t\tfor _, v in pairs(ix.command.FindAll(command, true, true)) do\n\t\t\tif (v.OnCheckAccess and !v:OnCheckAccess(LocalPlayer())) then\n\t\t\t\tcontinue\n\t\t\tend\n\n\t\t\tif (arguments:find(v.uniqueID, 1, true) == 1) then\n\t\t\t\treturn {\n\t\t\t\t\t\"ix \" .. arguments,\n\t\t\t\t\tv:GetDescription(),\n\t\t\t\t\tL(\"syntax\", v.syntax)\n\t\t\t\t}\n\t\t\tend\n\n\t\t\ttable.insert(autocomplete, \"ix \" .. v.uniqueID)\n\t\tend\n\n\t\treturn autocomplete\n\tend)\nend\n"
  },
  {
    "path": "gamemode/core/libs/sh_currency.lua",
    "content": "\r\n--- A library representing the server's currency system.\r\n-- @module ix.currency\r\n\r\nix.currency = ix.currency or {}\r\nix.currency.symbol = ix.currency.symbol or \"$\"\r\nix.currency.singular = ix.currency.singular or \"dollar\"\r\nix.currency.plural = ix.currency.plural or \"dollars\"\r\nix.currency.model = ix.currency.model or \"models/props_lab/box01a.mdl\"\r\n\r\n--- Sets the currency type.\r\n-- @realm shared\r\n-- @string symbol The symbol of the currency.\r\n-- @string singular The name of the currency in it's singular form.\r\n-- @string plural The name of the currency in it's plural form.\r\n-- @string model The model of the currency entity.\r\nfunction ix.currency.Set(symbol, singular, plural, model)\r\n\tix.currency.symbol = symbol\r\n\tix.currency.singular = singular\r\n\tix.currency.plural = plural\r\n\tix.currency.model = model\r\nend\r\n\r\n--- Returns a formatted string according to the current currency.\r\n-- @realm shared\r\n-- @number amount The amount of cash being formatted.\r\n-- @treturn string The formatted string.\r\nfunction ix.currency.Get(amount)\r\n\tif (amount == 1) then\r\n\t\treturn ix.currency.symbol..\"1 \"..ix.currency.singular\r\n\telse\r\n\t\treturn ix.currency.symbol..amount..\" \"..ix.currency.plural\r\n\tend\r\nend\r\n\r\n--- Spawns an amount of cash at a specific location on the map.\r\n-- @realm shared\r\n-- @vector pos The position of the money to be spawned.\r\n-- @number amount The amount of cash being spawned.\r\n-- @angle[opt=angle_zero] angle The angle of the entity being spawned.\r\n-- @treturn entity The spawned money entity.\r\nfunction ix.currency.Spawn(pos, amount, angle)\r\n\tif (!amount or amount < 0) then\r\n\t\tprint(\"[Helix] Can't create currency entity: Invalid Amount of money\")\r\n\t\treturn\r\n\tend\r\n\r\n\tlocal money = ents.Create(\"ix_money\")\r\n\tmoney:Spawn()\r\n\r\n\tif (IsValid(pos) and pos:IsPlayer()) then\r\n\t\tpos = pos:GetItemDropPos(money)\r\n\telseif (!isvector(pos)) then\r\n\t\tprint(\"[Helix] Can't create currency entity: Invalid Position\")\r\n\r\n\t\tmoney:Remove()\r\n\t\treturn\r\n\tend\r\n\r\n\tmoney:SetPos(pos)\r\n\t-- double check for negative.\r\n\tmoney:SetAmount(math.Round(math.abs(amount)))\r\n\tmoney:SetAngles(angle or angle_zero)\r\n\tmoney:Activate()\r\n\r\n\treturn money\r\nend\r\n\r\nfunction GM:OnPickupMoney(client, moneyEntity)\r\n\tif (IsValid(moneyEntity)) then\r\n\t\tlocal amount = moneyEntity:GetAmount()\r\n\r\n\t\tclient:GetCharacter():GiveMoney(amount)\r\n\tend\r\nend\r\n\r\ndo\r\n\tlocal character = ix.meta.character\r\n\r\n\tfunction character:HasMoney(amount)\r\n\t\tif (amount < 0) then\r\n\t\t\tprint(\"Negative Money Check Received.\")\r\n\t\tend\r\n\r\n\t\treturn self:GetMoney() >= amount\r\n\tend\r\n\r\n\tfunction character:GiveMoney(amount, bNoLog)\r\n\t\tamount = math.abs(amount)\r\n\r\n\t\tif (!bNoLog) then\r\n\t\t\tix.log.Add(self:GetPlayer(), \"money\", amount)\r\n\t\tend\r\n\r\n\t\tself:SetMoney(self:GetMoney() + amount)\r\n\r\n\t\treturn true\r\n\tend\r\n\r\n\tfunction character:TakeMoney(amount, bNoLog)\r\n\t\tamount = math.abs(amount)\r\n\r\n\t\tif (!bNoLog) then\r\n\t\t\tix.log.Add(self:GetPlayer(), \"money\", -amount)\r\n\t\tend\r\n\r\n\t\tself:SetMoney(self:GetMoney() - amount)\r\n\r\n\t\treturn true\r\n\tend\r\nend\r\n"
  },
  {
    "path": "gamemode/core/libs/sh_date.lua",
    "content": "\n--[[--\nPersistent date and time handling.\n\nAll of Lua's time functions are dependent on the Unix epoch, which means we can't have dates that go further than 1970. This\nlibrary remedies this problem. Time/date is represented by a `date` object that is queried, instead of relying on the seconds\nsince the epoch.\n\n## Futher documentation\nThis library makes use of a third-party date library found at https://github.com/Tieske/date - you can find all documentation\nregarding the `date` object and its methods there.\n]]\n-- @module ix.date\n\nix.date = ix.date or {}\nix.date.lib = ix.date.lib or include(\"thirdparty/sh_date.lua\")\nix.date.timeScale = ix.date.timeScale or ix.config.Get(\"secondsPerMinute\", 60) -- seconds per minute\nix.date.current = ix.date.current or ix.date.lib() -- current in-game date/time\nix.date.start = ix.date.start or CurTime() -- arbitrary start time for calculating date/time offset\n\nif (SERVER) then\n\tutil.AddNetworkString(\"ixDateSync\")\n\n\t--- Loads the date from disk.\n\t-- @realm server\n\t-- @internal\n\tfunction ix.date.Initialize()\n\t\tlocal currentDate = ix.data.Get(\"date\", nil, false, true)\n\n\t\t-- construct new starting date if we don't have it saved already\n\t\tif (!currentDate) then\n\t\t\tcurrentDate = {\n\t\t\t\tyear = ix.config.Get(\"year\"),\n\t\t\t\tmonth = ix.config.Get(\"month\"),\n\t\t\t\tday = ix.config.Get(\"day\"),\n\t\t\t\thour = tonumber(os.date(\"%H\")) or 0,\n\t\t\t\tmin = tonumber(os.date(\"%M\")) or 0,\n\t\t\t\tsec = tonumber(os.date(\"%S\")) or 0\n\t\t\t}\n\n\t\t\tcurrentDate = ix.date.lib.serialize(ix.date.lib(currentDate))\n\t\t\tix.data.Set(\"date\", currentDate, false, true)\n\t\tend\n\n\t\tix.date.timeScale = ix.config.Get(\"secondsPerMinute\", 60)\n\t\tix.date.current = ix.date.lib.construct(currentDate)\n\tend\n\n\t--- Updates the internal in-game date/time representation and resets the offset.\n\t-- @realm server\n\t-- @internal\n\tfunction ix.date.ResolveOffset()\n\t\tix.date.current = ix.date.Get()\n\t\tix.date.start = CurTime()\n\tend\n\n\t--- Updates the time scale of the in-game date/time. The time scale is given in seconds per minute (i.e how many real life\n\t-- seconds it takes for an in-game minute to pass). You should avoid using this function and use the in-game config menu to\n\t-- change the time scale instead.\n\t-- @realm server\n\t-- @internal\n\t-- @number secondsPerMinute New time scale\n\tfunction ix.date.UpdateTimescale(secondsPerMinute)\n\t\tix.date.ResolveOffset()\n\t\tix.date.timeScale = secondsPerMinute\n\tend\n\n\t--- Sends the current date to a player. This is done automatically when the player joins the server.\n\t-- @realm server\n\t-- @internal\n\t-- @player[opt=nil] client Player to send the date to, or `nil` to send to everyone\n\tfunction ix.date.Send(client)\n\t\tnet.Start(\"ixDateSync\")\n\n\t\tnet.WriteFloat(ix.date.timeScale)\n\t\tnet.WriteTable(ix.date.current)\n\t\tnet.WriteFloat(ix.date.start)\n\n\t\tif (client) then\n\t\t\tnet.Send(client)\n\t\telse\n\t\t\tnet.Broadcast()\n\t\tend\n\tend\n\n\t--- Saves the current in-game date to disk.\n\t-- @realm server\n\t-- @internal\n\tfunction ix.date.Save()\n\t\tix.date.bSaving = true\n\n\t\tix.date.ResolveOffset() -- resolve offset so we save the actual time to disk\n\t\tix.data.Set(\"date\", ix.date.lib.serialize(ix.date.current), false, true)\n\n\t\t-- update config to reflect current saved date\n\t\tix.config.Set(\"year\", ix.date.current:getyear())\n\t\tix.config.Set(\"month\", ix.date.current:getmonth())\n\t\tix.config.Set(\"day\", ix.date.current:getday())\n\n\t\tix.date.bSaving = nil\n\tend\nelse\n\tnet.Receive(\"ixDateSync\", function()\n\t\tlocal timeScale = net.ReadFloat()\n\t\tlocal currentDate = ix.date.lib.construct(net.ReadTable())\n\t\tlocal startTime = net.ReadFloat()\n\n\t\tix.date.timeScale = timeScale\n\t\tix.date.current = currentDate\n\t\tix.date.start = startTime\n\tend)\nend\n\n--- Returns the currently set date.\n-- @realm shared\n-- @treturn date Current in-game date\nfunction ix.date.Get()\n\tlocal minutesSinceStart = (CurTime() - ix.date.start) / ix.date.timeScale\n\n\treturn ix.date.current:copy():addminutes(minutesSinceStart)\nend\n\n--- Returns a string formatted version of a date.\n-- @realm shared\n-- @string format Format string\n-- @date[opt=nil] currentDate Date to format. If nil, it will use the currently set date\n-- @treturn string Formatted date\nfunction ix.date.GetFormatted(format, currentDate)\n\treturn (currentDate or ix.date.Get()):fmt(format)\nend\n\n--- Returns a serialized version of a date. This is useful when you need to network a date to clients, or save a date to disk.\n-- @realm shared\n-- @date[opt=nil] currentDate Date to serialize. If nil, it will use the currently set date\n-- @treturn table Serialized date\nfunction ix.date.GetSerialized(currentDate)\n\treturn ix.date.lib.serialize(currentDate or ix.date.Get())\nend\n\n--- Returns a date object from a table or serialized date.\n-- @realm shared\n-- @param currentDate Date to construct\n-- @treturn date Constructed date object\nfunction ix.date.Construct(currentDate)\n\treturn ix.date.lib.construct(currentDate)\nend\n"
  },
  {
    "path": "gamemode/core/libs/sh_faction.lua",
    "content": "\n--- Helper library for loading/getting faction information.\n-- @module ix.faction\n\nix.faction = ix.faction or {}\nix.faction.teams = ix.faction.teams or {}\nix.faction.indices = ix.faction.indices or {}\n\nlocal CITIZEN_MODELS = {\n\t\"models/humans/group01/male_01.mdl\",\n\t\"models/humans/group01/male_02.mdl\",\n\t\"models/humans/group01/male_04.mdl\",\n\t\"models/humans/group01/male_05.mdl\",\n\t\"models/humans/group01/male_06.mdl\",\n\t\"models/humans/group01/male_07.mdl\",\n\t\"models/humans/group01/male_08.mdl\",\n\t\"models/humans/group01/male_09.mdl\",\n\t\"models/humans/group02/male_01.mdl\",\n\t\"models/humans/group02/male_03.mdl\",\n\t\"models/humans/group02/male_05.mdl\",\n\t\"models/humans/group02/male_07.mdl\",\n\t\"models/humans/group02/male_09.mdl\",\n\t\"models/humans/group01/female_01.mdl\",\n\t\"models/humans/group01/female_02.mdl\",\n\t\"models/humans/group01/female_03.mdl\",\n\t\"models/humans/group01/female_06.mdl\",\n\t\"models/humans/group01/female_07.mdl\",\n\t\"models/humans/group02/female_01.mdl\",\n\t\"models/humans/group02/female_03.mdl\",\n\t\"models/humans/group02/female_06.mdl\",\n\t\"models/humans/group01/female_04.mdl\"\n}\n\n--- Loads factions from a directory.\n-- @realm shared\n-- @string directory The path to the factions files.\nfunction ix.faction.LoadFromDir(directory)\n\tfor _, v in ipairs(file.Find(directory..\"/*.lua\", \"LUA\")) do\n\t\tlocal niceName = v:sub(4, -5)\n\n\t\tFACTION = ix.faction.teams[niceName] or {index = table.Count(ix.faction.teams) + 1, isDefault = false}\n\t\t\tif (PLUGIN) then\n\t\t\t\tFACTION.plugin = PLUGIN.uniqueID\n\t\t\tend\n\n\t\t\tix.util.Include(directory..\"/\"..v, \"shared\")\n\n\t\t\tif (!FACTION.name) then\n\t\t\t\tFACTION.name = \"Unknown\"\n\t\t\t\tErrorNoHalt(\"Faction '\"..niceName..\"' is missing a name. You need to add a FACTION.name = \\\"Name\\\"\\n\")\n\t\t\tend\n\n\t\t\tif (!FACTION.color) then\n\t\t\t\tFACTION.color = Color(150, 150, 150)\n\t\t\t\tErrorNoHalt(\"Faction '\"..niceName..\"' is missing a color. You need to add FACTION.color = Color(1, 2, 3)\\n\")\n\t\t\tend\n\n\t\t\tteam.SetUp(FACTION.index, FACTION.name or \"Unknown\", FACTION.color or Color(125, 125, 125))\n\n\t\t\tFACTION.models = FACTION.models or CITIZEN_MODELS\n\t\t\tFACTION.uniqueID = FACTION.uniqueID or niceName\n\n\t\t\tfor _, v2 in pairs(FACTION.models) do\n\t\t\t\tif (isstring(v2)) then\n\t\t\t\t\tutil.PrecacheModel(v2)\n\t\t\t\telseif (istable(v2)) then\n\t\t\t\t\tutil.PrecacheModel(v2[1])\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tif (!FACTION.GetModels) then\n\t\t\t\tfunction FACTION:GetModels(client)\n\t\t\t\t\treturn self.models\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tix.faction.indices[FACTION.index] = FACTION\n\t\t\tix.faction.teams[niceName] = FACTION\n\t\tFACTION = nil\n\tend\nend\n\n--- Retrieves a faction table.\n-- @realm shared\n-- @param identifier Index or name of the faction\n-- @treturn table Faction table\n-- @usage print(ix.faction.Get(Entity(1):Team()).name)\n-- > \"Citizen\"\nfunction ix.faction.Get(identifier)\n\treturn ix.faction.indices[identifier] or ix.faction.teams[identifier]\nend\n\n--- Retrieves a faction index.\n-- @realm shared\n-- @string uniqueID Unique ID of the faction\n-- @treturn number Faction index\nfunction ix.faction.GetIndex(uniqueID)\n\tfor k, v in ipairs(ix.faction.indices) do\n\t\tif (v.uniqueID == uniqueID) then\n\t\t\treturn k\n\t\tend\n\tend\nend\n\nif (CLIENT) then\n\t--- Returns true if a faction requires a whitelist.\n\t-- @realm client\n\t-- @number faction Index of the faction\n\t-- @treturn bool Whether or not the faction requires a whitelist\n\tfunction ix.faction.HasWhitelist(faction)\n\t\tlocal data = ix.faction.indices[faction]\n\n\t\tif (data) then\n\t\t\tif (data.isDefault) then\n\t\t\t\treturn true\n\t\t\tend\n\n\t\t\tlocal ixData = ix.localData and ix.localData.whitelists or {}\n\n\t\t\treturn ixData[Schema.folder] and ixData[Schema.folder][data.uniqueID] == true or false\n\t\tend\n\n\t\treturn false\n\tend\nend\n"
  },
  {
    "path": "gamemode/core/libs/sh_flag.lua",
    "content": "\n--[[--\nGrants abilities to characters.\n\nFlags are a simple way of adding/removing certain abilities to players on a per-character basis. Helix comes with a few flags\nby default, for example to restrict spawning of props, usage of the physgun, etc. All flags will be listed in the\n`Flags` section of the `Help` menu. Flags are usually used when server validation is required to allow a player to do something\non their character. However, it's usually preferable to use in-character methods over flags when possible (i.e restricting\nthe business menu to characters that have a permit item, rather than using flags to determine availability).\n\nFlags are a single alphanumeric character that can be checked on the server. Serverside callbacks can be used to provide\nfunctionality whenever the flag is added or removed. For example:\n\tix.flag.Add(\"z\", \"Access to some cool stuff.\", function(client, bGiven)\n\t\tprint(\"z flag given:\", bGiven)\n\tend)\n\n\tEntity(1):GetCharacter():GiveFlags(\"z\")\n\t> z flag given: true\n\n\tEntity(1):GetCharacter():TakeFlags(\"z\")\n\t> z flag given: false\n\n\tprint(Entity(1):GetCharacter():HasFlags(\"z\"))\n\t> false\n\nCheck out `Character:GiveFlags` and `Character:TakeFlags` for additional info.\n]]\n-- @module ix.flag\n\nix.flag = ix.flag or {}\nix.flag.list = ix.flag.list or {}\n\n--- Creates a flag. This should be called shared in order for the client to be aware of the flag's existence.\n-- @realm shared\n-- @string flag Alphanumeric character to use for the flag\n-- @string description Description of the flag\n-- @func callback Function to call when the flag is given or taken from a player\nfunction ix.flag.Add(flag, description, callback)\n\tix.flag.list[flag] = {\n\t\tdescription = description,\n\t\tcallback = callback\n\t}\nend\n\nif (SERVER) then\n\t-- Called to apply flags when a player has spawned.\n\t-- @realm server\n\t-- @internal\n\t-- @player client Player to setup flags for\n\tfunction ix.flag.OnSpawn(client)\n\t\t-- Check if they have a valid character.\n\t\tif (client:GetCharacter()) then\n\t\t\t-- Get all of the character's flags.\n\t\t\tlocal flags = client:GetCharacter():GetFlags()\n\n\t\t\tfor i = 1, #flags do\n\t\t\t\t-- Get each individual flag.\n\t\t\t\tlocal flag = flags[i]\n\t\t\t\tlocal info = ix.flag.list[flag]\n\n\t\t\t\t-- Check if the flag has a callback.\n\t\t\t\tif (info and info.callback) then\n\t\t\t\t\t-- Run the callback, passing the player and true so they get whatever benefits.\n\t\t\t\t\tinfo.callback(client, true)\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend\nend\n\ndo\n\tlocal character = ix.meta.character\n\n\tif (SERVER) then\n\t\t--- Flag util functions for character\n\t\t-- @classmod Character\n\n\t\t--- Sets this character's accessible flags. Note that this method overwrites **all** flags instead of adding them.\n\t\t-- @realm server\n\t\t-- @string flags Flag(s) this charater is allowed to have\n\t\t-- @see GiveFlags\n\t\tfunction character:SetFlags(flags)\n\t\t\tself:SetData(\"f\", flags)\n\t\tend\n\n\t\t--- Adds a flag to the list of this character's accessible flags. This does not overwrite existing flags.\n\t\t-- @realm server\n\t\t-- @string flags Flag(s) this character should be given\n\t\t-- @usage character:GiveFlags(\"pet\")\n\t\t-- -- gives p, e, and t flags to the character\n\t\t-- @see HasFlags\n\t\tfunction character:GiveFlags(flags)\n\t\t\tlocal addedFlags = \"\"\n\n\t\t\t-- Get the individual flags within the flag string.\n\t\t\tfor i = 1, #flags do\n\t\t\t\tlocal flag = flags[i]\n\t\t\t\tlocal info = ix.flag.list[flag]\n\n\t\t\t\tif (info) then\n\t\t\t\t\tif (!self:HasFlags(flag)) then\n\t\t\t\t\t\taddedFlags = addedFlags..flag\n\t\t\t\t\tend\n\n\t\t\t\t\tif (info.callback) then\n\t\t\t\t\t\t-- Pass the player and true (true for the flag being given.)\n\t\t\t\t\t\tinfo.callback(self:GetPlayer(), true)\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\n\t\t\t-- Only change the flag string if it is different.\n\t\t\tif (addedFlags != \"\") then\n\t\t\t\tself:SetFlags(self:GetFlags()..addedFlags)\n\t\t\tend\n\t\tend\n\n\t\t--- Removes this character's access to the given flags.\n\t\t-- @realm server\n\t\t-- @string flags Flag(s) to remove from this character\n\t\t-- @usage -- for a character with \"pet\" flags\n\t\t-- character:TakeFlags(\"p\")\n\t\t-- -- character now has e, and t flags\n\t\tfunction character:TakeFlags(flags)\n\t\t\tlocal oldFlags = self:GetFlags()\n\t\t\tlocal newFlags = oldFlags\n\n\t\t\t-- Get the individual flags within the flag string.\n\t\t\tfor i = 1, #flags do\n\t\t\t\tlocal flag = flags[i]\n\t\t\t\tlocal info = ix.flag.list[flag]\n\n\t\t\t\t-- Call the callback if the flag has been registered.\n\t\t\t\tif (info and info.callback) then\n\t\t\t\t\t-- Pass the player and false (false since the flag is being taken)\n\t\t\t\t\tinfo.callback(self:GetPlayer(), false)\n\t\t\t\tend\n\n\t\t\t\tnewFlags = newFlags:gsub(flag, \"\")\n\t\t\tend\n\n\t\t\tif (newFlags != oldFlags) then\n\t\t\t\tself:SetFlags(newFlags)\n\t\t\tend\n\t\tend\n\tend\n\n\t--- Returns all of the flags this character has.\n\t-- @realm shared\n\t-- @treturn string Flags this character has represented as one string. You can access individual flags by iterating through\n\t-- the string letter by letter\n\tfunction character:GetFlags()\n\t\treturn self:GetData(\"f\", \"\")\n\tend\n\n\t--- Returns `true` if the character has the given flag(s).\n\t-- @realm shared\n\t-- @string flags Flag(s) to check access for\n\t-- @treturn bool Whether or not this character has access to the given flag(s)\n\tfunction character:HasFlags(flags)\n\t\tlocal bHasFlag = hook.Run(\"CharacterHasFlags\", self, flags)\n\n\t\tif (bHasFlag == true) then\n\t\t\treturn true\n\t\tend\n\n\t\tlocal flagList = self:GetFlags()\n\n\t\tfor i = 1, #flags do\n\t\t\tif (flagList:find(flags[i], 1, true)) then\n\t\t\t\treturn true\n\t\t\tend\n\t\tend\n\n\t\treturn false\n\tend\nend\n\ndo\n\tix.flag.Add(\"p\", \"Access to the physgun.\", function(client, isGiven)\n\t\tif (isGiven) then\n\t\t\tclient:Give(\"weapon_physgun\")\n\t\t\tclient:SelectWeapon(\"weapon_physgun\")\n\t\telse\n\t\t\tclient:StripWeapon(\"weapon_physgun\")\n\t\tend\n\tend)\n\n\tix.flag.Add(\"t\", \"Access to the toolgun\", function(client, isGiven)\n\t\tif (isGiven) then\n\t\t\tclient:Give(\"gmod_tool\")\n\t\t\tclient:SelectWeapon(\"gmod_tool\")\n\t\telse\n\t\t\tclient:StripWeapon(\"gmod_tool\")\n\t\tend\n\tend)\n\n\tix.flag.Add(\"c\", \"Access to spawn chairs.\")\n\tix.flag.Add(\"C\", \"Access to spawn vehicles.\")\n\tix.flag.Add(\"r\", \"Access to spawn ragdolls.\")\n\tix.flag.Add(\"e\", \"Access to spawn props.\")\n\tix.flag.Add(\"n\", \"Access to spawn NPCs.\")\nend\n"
  },
  {
    "path": "gamemode/core/libs/sh_inventory.lua",
    "content": "\n--[[--\nInventory manipulation and helper functions.\n]]\n-- @module ix.inventory\n\nix.inventory = ix.inventory or {}\n\nix.util.Include(\"helix/gamemode/core/meta/sh_inventory.lua\")\n\n--- Retrieves an inventory table.\n-- @realm shared\n-- @number invID Index of the inventory\n-- @treturn Inventory Inventory table\nfunction ix.inventory.Get(invID)\n\treturn ix.item.inventories[invID]\nend\n\nfunction ix.inventory.Create(width, height, id)\n\tlocal inventory = ix.meta.inventory:New(id, width, height)\n\t\tix.item.inventories[id] = inventory\n\treturn inventory\nend\n\n--- Loads an inventory and associated items from the database into memory. If you are passing a table into `invID`, it\n-- requires a table where the key is the inventory ID, and the value is a table of the width and height values. See below\n-- for an example.\n-- @realm server\n-- @param invID Inventory ID or table of inventory IDs\n-- @number width Width of inventory (this is not used when passing a table to `invID`)\n-- @number height Height of inventory (this is not used when passing a table to `invID`)\n-- @func callback Function to call when inventory is restored\n-- @usage ix.inventory.Restore({\n-- \t[10] = {5, 5},\n-- \t[11] = {7, 4}\n-- })\n-- -- inventories 10 and 11 with sizes (5, 5) and (7, 4) will be loaded\nfunction ix.inventory.Restore(invID, width, height, callback)\n\tlocal inventories = {}\n\n\tif (!istable(invID)) then\n\t\tif (!isnumber(invID) or invID < 0) then\n\t\t\terror(\"Attempt to restore inventory with an invalid ID!\")\n\t\tend\n\n\t\tinventories[invID] = {width, height}\n\t\tix.inventory.Create(width, height, invID)\n\telse\n\t\tfor k, v in pairs(invID) do\n\t\t\tinventories[k] = {v[1], v[2]}\n\t\t\tix.inventory.Create(v[1], v[2], k)\n\t\tend\n\tend\n\n\tlocal query = mysql:Select(\"ix_items\")\n\t\tquery:Select(\"item_id\")\n\t\tquery:Select(\"inventory_id\")\n\t\tquery:Select(\"unique_id\")\n\t\tquery:Select(\"data\")\n\t\tquery:Select(\"character_id\")\n\t\tquery:Select(\"player_id\")\n\t\tquery:Select(\"x\")\n\t\tquery:Select(\"y\")\n\t\tquery:WhereIn(\"inventory_id\", table.GetKeys(inventories))\n\t\tquery:Callback(function(result)\n\t\t\tif (istable(result) and #result > 0) then\n\t\t\t\tlocal invSlots = {}\n\n\t\t\t\tfor _, item in ipairs(result) do\n\t\t\t\t\tlocal itemInvID = tonumber(item.inventory_id)\n\t\t\t\t\tlocal invInfo = inventories[itemInvID]\n\n\t\t\t\t\tif (!itemInvID or !invInfo) then\n\t\t\t\t\t\t-- don't restore items with an invalid inventory id or type\n\t\t\t\t\t\tcontinue\n\t\t\t\t\tend\n\n\t\t\t\t\tlocal inventory = ix.item.inventories[itemInvID]\n\t\t\t\t\tlocal x, y = tonumber(item.x), tonumber(item.y)\n\t\t\t\t\tlocal itemID = tonumber(item.item_id)\n\t\t\t\t\tlocal data = util.JSONToTable(item.data or \"[]\")\n\t\t\t\t\tlocal characterID, playerID = tonumber(item.character_id), tostring(item.player_id)\n\n\t\t\t\t\tif (x and y and itemID) then\n\t\t\t\t\t\tif (x <= inventory.w and x > 0 and y <= inventory.h and y > 0) then\n\t\t\t\t\t\t\tlocal item2 = ix.item.New(item.unique_id, itemID)\n\n\t\t\t\t\t\t\tif (item2) then\n\t\t\t\t\t\t\t\tinvSlots[itemInvID] = invSlots[itemInvID] or {}\n\t\t\t\t\t\t\t\tlocal slots = invSlots[itemInvID]\n\n\t\t\t\t\t\t\t\titem2.data = {}\n\n\t\t\t\t\t\t\t\tif (data) then\n\t\t\t\t\t\t\t\t\titem2.data = data\n\t\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\t\t\titem2.gridX = x\n\t\t\t\t\t\t\t\titem2.gridY = y\n\t\t\t\t\t\t\t\titem2.invID = itemInvID\n\t\t\t\t\t\t\t\titem2.characterID = characterID\n\t\t\t\t\t\t\t\titem2.playerID = (playerID == \"\" or playerID == \"NULL\") and nil or playerID\n\n\t\t\t\t\t\t\t\tfor x2 = 0, item2.width - 1 do\n\t\t\t\t\t\t\t\t\tfor y2 = 0, item2.height - 1 do\n\t\t\t\t\t\t\t\t\t\tslots[x + x2] = slots[x + x2] or {}\n\t\t\t\t\t\t\t\t\t\tslots[x + x2][y + y2] = item2\n\t\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\t\t\tif (item2.OnRestored) then\n\t\t\t\t\t\t\t\t\titem2:OnRestored(item2, itemInvID)\n\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\t\t\t\tend\n\n\t\t\t\tfor k, v in pairs(invSlots) do\n\t\t\t\t\tix.item.inventories[k].slots = v\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tif (callback) then\n\t\t\t\tfor k, _ in pairs(inventories) do\n\t\t\t\t\tcallback(ix.item.inventories[k])\n\t\t\t\tend\n\t\t\tend\n\t\tend)\n\tquery:Execute()\nend\n\nfunction ix.inventory.New(owner, invType, callback)\n\tlocal invData = ix.item.inventoryTypes[invType] or {w = 1, h = 1}\n\n\tlocal query = mysql:Insert(\"ix_inventories\")\n\t\tquery:Insert(\"inventory_type\", invType)\n\t\tquery:Insert(\"character_id\", owner)\n\t\tquery:Callback(function(result, status, lastID)\n\t\t\tlocal inventory = ix.inventory.Create(invData.w, invData.h, lastID)\n\n\t\t\tif (invType) then\n\t\t\t\tinventory.vars.isBag = invType\n\t\t\tend\n\n\t\t\tif (isnumber(owner) and owner > 0) then\n\t\t\t\tlocal character = ix.char.loaded[owner]\n\t\t\t\tlocal client = character:GetPlayer()\n\n\t\t\t\tinventory:SetOwner(owner)\n\n\t\t\t\tif (IsValid(client)) then\n\t\t\t\t\tinventory:Sync(client)\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tif (callback) then\n\t\t\t\tcallback(inventory)\n\t\t\tend\n\t\tend)\n\tquery:Execute()\nend\n\nfunction ix.inventory.Register(invType, w, h, isBag)\n\tix.item.inventoryTypes[invType] = {w = w, h = h}\n\n\tif (isBag) then\n\t\tix.item.inventoryTypes[invType].isBag = invType\n\tend\n\n\treturn ix.item.inventoryTypes[invType]\nend\n"
  },
  {
    "path": "gamemode/core/libs/sh_item.lua",
    "content": "--[[--\nItem manipulation and helper functions.\n]]\n-- @module ix.item\n\nix.item = ix.item or {}\nix.item.list = ix.item.list or {}\nix.item.base = ix.item.base or {}\nix.item.instances = ix.item.instances or {}\nix.item.inventories = ix.item.inventories or {\n\t[0] = {}\n}\nix.item.inventoryTypes = ix.item.inventoryTypes or {}\n\nix.util.Include(\"helix/gamemode/core/meta/sh_item.lua\")\n\n-- Declare some supports for logic inventory\nlocal zeroInv = ix.item.inventories[0]\n\nfunction zeroInv:GetID()\n\treturn 0\nend\n\nfunction zeroInv:OnCheckAccess(client)\n\treturn true\nend\n\n-- WARNING: You have to manually sync the data to client if you're trying to use item in the logical inventory in the vgui.\nfunction zeroInv:Add(uniqueID, quantity, data, x, y)\n\tquantity = quantity or 1\n\n\tif (quantity > 0) then\n\t\tif (!isnumber(uniqueID)) then\n\t\t\tif (quantity > 1) then\n\t\t\t\tfor _ = 1, quantity do\n\t\t\t\t\tself:Add(uniqueID, 1, data)\n\t\t\t\tend\n\n\t\t\t\treturn\n\t\t\tend\n\n\t\t\tlocal itemTable = ix.item.list[uniqueID]\n\n\t\t\tif (!itemTable) then\n\t\t\t\treturn false, \"invalidItem\"\n\t\t\tend\n\n\t\t\tix.item.Instance(0, uniqueID, data, x, y, function(item)\n\t\t\t\tself[item:GetID()] = item\n\t\t\tend)\n\n\t\t\treturn nil, nil, 0\n\t\tend\n\telse\n\t\treturn false, \"notValid\"\n\tend\nend\n\nfunction ix.item.Instance(index, uniqueID, itemData, x, y, callback, characterID, playerID)\n\tif (!uniqueID or ix.item.list[uniqueID]) then\n\t\titemData = istable(itemData) and itemData or {}\n\n\t\tlocal query = mysql:Insert(\"ix_items\")\n\t\t\tquery:Insert(\"inventory_id\", index)\n\t\t\tquery:Insert(\"unique_id\", uniqueID)\n\t\t\tquery:Insert(\"data\", util.TableToJSON(itemData))\n\t\t\tquery:Insert(\"x\", x)\n\t\t\tquery:Insert(\"y\", y)\n\n\t\t\tif (characterID) then\n\t\t\t\tquery:Insert(\"character_id\", characterID)\n\t\t\tend\n\n\t\t\tif (playerID) then\n\t\t\t\tquery:Insert(\"player_id\", playerID)\n\t\t\tend\n\n\t\t\tquery:Callback(function(result, status, lastID)\n\t\t\t\tlocal item = ix.item.New(uniqueID, lastID)\n\n\t\t\t\tif (item) then\n\t\t\t\t\titem.data = table.Copy(itemData)\n\t\t\t\t\titem.invID = index\n\t\t\t\t\titem.characterID = characterID\n\t\t\t\t\titem.playerID = playerID\n\n\t\t\t\t\tif (callback) then\n\t\t\t\t\t\tcallback(item)\n\t\t\t\t\tend\n\n\t\t\t\t\tif (item.OnInstanced) then\n\t\t\t\t\t\titem:OnInstanced(index, x, y, item)\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend)\n\t\tquery:Execute()\n\telse\n\t\tErrorNoHalt(\"[Helix] Attempt to give an invalid item! (\" .. (uniqueID or \"nil\") .. \")\\n\")\n\tend\nend\n\n--- Retrieves an item table.\n-- @realm shared\n-- @string identifier Unique ID of the item\n-- @treturn item Item table\n-- @usage print(ix.item.Get(\"example\"))\n-- > \"item[example][0]\"\nfunction ix.item.Get(identifier)\n\treturn ix.item.base[identifier] or ix.item.list[identifier]\nend\n\nfunction ix.item.Load(path, baseID, isBaseItem)\n\tlocal uniqueID = path:match(\"sh_([_%w]+)%.lua\")\n\n\tif (uniqueID) then\n\t\tuniqueID = (isBaseItem and \"base_\" or \"\")..uniqueID\n\t\tix.item.Register(uniqueID, baseID, isBaseItem, path)\n\telse\n\t\tif (!path:find(\".txt\")) then\n\t\t\tErrorNoHalt(\"[Helix] Item at '\"..path..\"' follows invalid naming convention!\\n\")\n\t\tend\n\tend\nend\n\nfunction ix.item.Register(uniqueID, baseID, isBaseItem, path, luaGenerated)\n\tlocal meta = ix.meta.item\n\n\tif (uniqueID) then\n\t\tITEM = (isBaseItem and ix.item.base or ix.item.list)[uniqueID] or setmetatable({}, meta)\n\t\t\tITEM.uniqueID = uniqueID\n\t\t\tITEM.base = baseID or ITEM.base\n\t\t\tITEM.isBase = isBaseItem or false\n\t\t\tITEM.hooks = ITEM.hooks or {}\n\t\t\tITEM.postHooks = ITEM.postHooks or {}\n\t\t\tITEM.functions = ITEM.functions or {}\n\t\t\tITEM.functions.drop = ITEM.functions.drop or {\n\t\t\t\ttip = \"dropTip\",\n\t\t\t\ticon = \"icon16/world.png\",\n\t\t\t\tOnRun = function(item)\n\t\t\t\t\tlocal bSuccess, error = item:Transfer(nil, nil, nil, item.player)\n\n\t\t\t\t\tif (!bSuccess and isstring(error)) then\n\t\t\t\t\t\titem.player:NotifyLocalized(error)\n\t\t\t\t\telse\n\t\t\t\t\t\titem.player:EmitSound(\"npc/zombie/foot_slide\" .. math.random(1, 3) .. \".wav\", 75, math.random(90, 120), 1)\n\t\t\t\t\tend\n\n\t\t\t\t\treturn false\n\t\t\t\tend,\n\t\t\t\tOnCanRun = function(item)\n\t\t\t\t\treturn !IsValid(item.entity) and !item.noDrop\n\t\t\t\tend\n\t\t\t}\n\t\t\tITEM.functions.take = ITEM.functions.take or {\n\t\t\t\ttip = \"takeTip\",\n\t\t\t\ticon = \"icon16/box.png\",\n\t\t\t\tOnRun = function(item)\n\t\t\t\t\tlocal client = item.player\n\t\t\t\t\tlocal bSuccess, error = item:Transfer(client:GetCharacter():GetInventory():GetID(), nil, nil, client)\n\n\t\t\t\t\tif (!bSuccess) then\n\t\t\t\t\t\tclient:NotifyLocalized(error or \"unknownError\")\n\t\t\t\t\t\treturn false\n\t\t\t\t\telse\n\t\t\t\t\t\tclient:EmitSound(\"npc/zombie/foot_slide\" .. math.random(1, 3) .. \".wav\", 75, math.random(90, 120), 1)\n\n\t\t\t\t\t\tif (item.data) then -- I don't like it but, meh...\n\t\t\t\t\t\t\tfor k, v in pairs(item.data) do\n\t\t\t\t\t\t\t\titem:SetData(k, v)\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\n\t\t\t\t\treturn true\n\t\t\t\tend,\n\t\t\t\tOnCanRun = function(item)\n\t\t\t\t\treturn IsValid(item.entity)\n\t\t\t\tend\n\t\t\t}\n\n\t\t\tlocal oldBase = ITEM.base\n\n\t\t\tif (ITEM.base) then\n\t\t\t\tlocal baseTable = ix.item.base[ITEM.base]\n\n\t\t\t\tif (baseTable) then\n\t\t\t\t\tfor k, v in pairs(baseTable) do\n\t\t\t\t\t\tif (ITEM[k] == nil) then\n\t\t\t\t\t\t\tITEM[k] = v\n\t\t\t\t\t\tend\n\n\t\t\t\t\t\tITEM.baseTable = baseTable\n\t\t\t\t\tend\n\n\t\t\t\t\tlocal mergeTable = table.Copy(baseTable)\n\t\t\t\t\tITEM = table.Merge(mergeTable, ITEM)\n\t\t\t\telse\n\t\t\t\t\tErrorNoHalt(\"[Helix] Item '\"..ITEM.uniqueID..\"' has a non-existent base! (\"..ITEM.base..\")\\n\")\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tif (PLUGIN) then\n\t\t\t\tITEM.plugin = PLUGIN.uniqueID\n\t\t\tend\n\n\t\t\tif (!luaGenerated and path) then\n\t\t\t\tix.util.Include(path)\n\t\t\tend\n\n\t\t\tif (ITEM.base and oldBase != ITEM.base) then\n\t\t\t\tlocal baseTable = ix.item.base[ITEM.base]\n\n\t\t\t\tif (baseTable) then\n\t\t\t\t\tfor k, v in pairs(baseTable) do\n\t\t\t\t\t\tif (ITEM[k] == nil) then\n\t\t\t\t\t\t\tITEM[k] = v\n\t\t\t\t\t\tend\n\n\t\t\t\t\t\tITEM.baseTable = baseTable\n\t\t\t\t\tend\n\n\t\t\t\t\tlocal mergeTable = table.Copy(baseTable)\n\t\t\t\t\tITEM = table.Merge(mergeTable, ITEM)\n\t\t\t\telse\n\t\t\t\t\tErrorNoHalt(\"[Helix] Item '\"..ITEM.uniqueID..\"' has a non-existent base! (\"..ITEM.base..\")\\n\")\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tITEM.description = ITEM.description or \"noDesc\"\n\t\t\tITEM.width = ITEM.width or 1\n\t\t\tITEM.height = ITEM.height or 1\n\t\t\tITEM.category = ITEM.category or \"misc\"\n\n\t\t\tif (ITEM.OnRegistered) then\n\t\t\t\tITEM:OnRegistered()\n\t\t\tend\n\n\t\t\t(isBaseItem and ix.item.base or ix.item.list)[ITEM.uniqueID] = ITEM\n\n\t\t\tif (IX_RELOADED) then\n\t\t\t\t-- we don't know which item was actually edited, so we'll refresh all of them\n\t\t\t\tfor _, v in pairs(ix.item.instances) do\n\t\t\t\t\tif (v.uniqueID == uniqueID) then\n\t\t\t\t\t\tix.util.MetatableSafeTableMerge(v, ITEM)\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\t\tif (luaGenerated) then\n\t\t\treturn ITEM\n\t\telse\n\t\t\tITEM = nil\n\t\tend\n\telse\n\t\tErrorNoHalt(\"[Helix] You tried to register an item without uniqueID!\\n\")\n\tend\nend\n\nfunction ix.item.LoadFromDir(directory)\n\tlocal files, folders\n\n\tfiles = file.Find(directory..\"/base/*.lua\", \"LUA\")\n\n\tfor _, v in ipairs(files) do\n\t\tix.item.Load(directory..\"/base/\"..v, nil, true)\n\tend\n\n\tfiles, folders = file.Find(directory..\"/*\", \"LUA\")\n\n\tfor _, v in ipairs(folders) do\n\t\tif (v == \"base\") then\n\t\t\tcontinue\n\t\tend\n\n\t\tfor _, v2 in ipairs(file.Find(directory..\"/\"..v..\"/*.lua\", \"LUA\")) do\n\t\t\tix.item.Load(directory..\"/\"..v .. \"/\".. v2, \"base_\"..v)\n\t\tend\n\tend\n\n\tfor _, v in ipairs(files) do\n\t\tix.item.Load(directory..\"/\"..v)\n\tend\nend\n\nfunction ix.item.New(uniqueID, id)\n\tif (ix.item.instances[id] and ix.item.instances[id].uniqueID == uniqueID) then\n\t\treturn ix.item.instances[id]\n\tend\n\n\tlocal stockItem = ix.item.list[uniqueID]\n\n\tif (stockItem) then\n\t\tlocal item = setmetatable({id = id, data = {}}, {\n\t\t\t__index = stockItem,\n\t\t\t__eq = stockItem.__eq,\n\t\t\t__tostring = stockItem.__tostring\n\t\t})\n\n\t\tix.item.instances[id] = item\n\n\t\treturn item\n\telse\n\t\tErrorNoHalt(\"[Helix] Attempt to index unknown item '\"..uniqueID..\"'\\n\")\n\tend\nend\n\ndo\n\tfunction ix.item.GetInv(invID)\n\t\tErrorNoHalt(\"ix.item.GetInv is deprecated. Use ix.inventory.Get instead!\\n\")\n\t\treturn ix.inventory.Get(invID)\n\tend\n\n\tfunction ix.item.RegisterInv(invType, w, h, isBag)\n\t\tErrorNoHalt(\"ix.item.RegisterInv is deprecated. Use ix.inventory.Register instead!\\n\")\n\t\treturn ix.inventory.Register(invType, w, h, isBag)\n\tend\n\n\tfunction ix.item.NewInv(owner, invType, callback)\n\t\tErrorNoHalt(\"ix.item.NewInv is deprecated. Use ix.inventory.New instead!\\n\")\n\t\treturn ix.inventory.New(owner, invType, callback)\n\tend\n\n\tfunction ix.item.CreateInv(width, height, id)\n\t\tErrorNoHalt(\"ix.item.CreateInv is deprecated. Use ix.inventory.Create instead!\\n\")\n\t\treturn ix.inventory.Create(width, height, id)\n\tend\n\n\tfunction ix.item.RestoreInv(invID, width, height, callback)\n\t\tErrorNoHalt(\"ix.item.RestoreInv is deprecated. Use ix.inventory.Restore instead!\\n\")\n\t\treturn ix.inventory.Restore(invID, width, height, callback)\n\tend\n\n\tif (CLIENT) then\n\t\tnet.Receive(\"ixInventorySync\", function()\n\t\t\tlocal slots = net.ReadTable()\n\t\t\tlocal id = net.ReadUInt(32)\n\t\t\tlocal w, h = net.ReadUInt(6), net.ReadUInt(6)\n\t\t\tlocal owner = net.ReadType()\n\t\t\tlocal vars = net.ReadTable()\n\n\t\t\tif (!LocalPlayer():GetCharacter()) then\n\t\t\t\treturn\n\t\t\tend\n\n\t\t\tlocal character = owner and ix.char.loaded[owner]\n\t\t\tlocal inventory = ix.inventory.Create(w, h, id)\n\t\t\tinventory.slots = {}\n\t\t\tinventory.vars = vars\n\n\t\t\tlocal x, y\n\n\t\t\tfor _, v in ipairs(slots) do\n\t\t\t\tx, y = v[1], v[2]\n\n\t\t\t\tinventory.slots[x] = inventory.slots[x] or {}\n\n\t\t\t\tlocal item = ix.item.New(v[3], v[4])\n\n\t\t\t\titem.data = {}\n\t\t\t\tif (v[5]) then\n\t\t\t\t\titem.data = v[5]\n\t\t\t\tend\n\n\t\t\t\titem.invID = item.invID or id\n\t\t\t\tinventory.slots[x][y] = item\n\t\t\tend\n\n\t\t\tif (character) then\n\t\t\t\tinventory:SetOwner(character:GetID())\n\t\t\t\tcharacter.vars.inv = character.vars.inv or {}\n\n\t\t\t\tfor k, v in ipairs(character:GetInventory(true)) do\n\t\t\t\t\tif (v:GetID() == id) then\n\t\t\t\t\t\tcharacter:GetInventory(true)[k] = inventory\n\n\t\t\t\t\t\treturn\n\t\t\t\t\tend\n\t\t\t\tend\n\n\t\t\t\ttable.insert(character.vars.inv, inventory)\n\t\t\tend\n\t\tend)\n\n\t\tnet.Receive(\"ixInventoryData\", function()\n\t\t\tlocal id = net.ReadUInt(32)\n\t\t\tlocal item = ix.item.instances[id]\n\n\t\t\tif (item) then\n\t\t\t\tlocal key = net.ReadString()\n\t\t\t\tlocal value = net.ReadType()\n\n\t\t\t\titem.data = item.data or {}\n\t\t\t\titem.data[key] = value\n\n\t\t\t\tlocal invID = item.invID == LocalPlayer():GetCharacter():GetInventory():GetID() and 1 or item.invID\n\t\t\t\tlocal panel = ix.gui[\"inv\" .. invID]\n\n\t\t\t\tif (panel and panel.panels) then\n\t\t\t\t\tlocal icon = panel.panels[id]\n\n\t\t\t\t\tif (icon) then\n\t\t\t\t\t\ticon:SetHelixTooltip(function(tooltip)\n\t\t\t\t\t\t\tix.hud.PopulateItemTooltip(tooltip, item)\n\t\t\t\t\t\tend)\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\t\tend)\n\n\t\tnet.Receive(\"ixInventorySet\", function()\n\t\t\tlocal invID = net.ReadUInt(32)\n\t\t\tlocal x, y = net.ReadUInt(6), net.ReadUInt(6)\n\t\t\tlocal uniqueID = net.ReadString()\n\t\t\tlocal id = net.ReadUInt(32)\n\t\t\tlocal owner = net.ReadUInt(32)\n\t\t\tlocal data = net.ReadTable()\n\n\t\t\tlocal character = owner != 0 and ix.char.loaded[owner] or LocalPlayer():GetCharacter()\n\n\t\t\tif (character) then\n\t\t\t\tlocal inventory = ix.item.inventories[invID]\n\n\t\t\t\tif (inventory) then\n\t\t\t\t\tlocal item = (uniqueID != \"\" and id != 0) and ix.item.New(uniqueID, id) or nil\n\t\t\t\t\titem.invID = invID\n\t\t\t\t\titem.data = {}\n\n\t\t\t\t\tif (data) then\n\t\t\t\t\t\titem.data = data\n\t\t\t\t\tend\n\n\t\t\t\t\tinventory.slots[x] = inventory.slots[x] or {}\n\t\t\t\t\tinventory.slots[x][y] = item\n\n\t\t\t\t\tinvID = invID == LocalPlayer():GetCharacter():GetInventory():GetID() and 1 or invID\n\n\t\t\t\t\tlocal panel = ix.gui[\"inv\" .. invID]\n\n\t\t\t\t\tif (IsValid(panel)) then\n\t\t\t\t\t\tlocal icon = panel:AddIcon(\n\t\t\t\t\t\t\titem:GetModel() or \"models/props_junk/popcan01a.mdl\", x, y, item.width, item.height, item:GetSkin()\n\t\t\t\t\t\t)\n\n\t\t\t\t\t\tif (IsValid(icon)) then\n\t\t\t\t\t\t\ticon:SetHelixTooltip(function(tooltip)\n\t\t\t\t\t\t\t\tix.hud.PopulateItemTooltip(tooltip, item)\n\t\t\t\t\t\t\tend)\n\n\t\t\t\t\t\t\ticon.itemID = item.id\n\t\t\t\t\t\t\tpanel.panels[item.id] = icon\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\t\tend)\n\n\t\tnet.Receive(\"ixInventoryMove\", function()\n\t\t\tlocal invID = net.ReadUInt(32)\n\t\t\tlocal inventory = ix.item.inventories[invID]\n\n\t\t\tif (!inventory) then\n\t\t\t\treturn\n\t\t\tend\n\n\t\t\tlocal itemID = net.ReadUInt(32)\n\t\t\tlocal oldX = net.ReadUInt(6)\n\t\t\tlocal oldY = net.ReadUInt(6)\n\t\t\tlocal x = net.ReadUInt(6)\n\t\t\tlocal y = net.ReadUInt(6)\n\n\t\t\tinvID = invID == LocalPlayer():GetCharacter():GetInventory():GetID() and 1 or invID\n\n\t\t\tlocal item = ix.item.instances[itemID]\n\t\t\tlocal panel = ix.gui[\"inv\" .. invID]\n\n\t\t\t-- update inventory UI if it's open\n\t\t\tif (IsValid(panel)) then\n\t\t\t\tlocal icon = panel.panels[itemID]\n\n\t\t\t\tif (IsValid(icon)) then\n\t\t\t\t\ticon:Move(x, y, panel, true)\n\t\t\t\tend\n\t\t\tend\n\n\t\t\t-- update inventory slots\n\t\t\tif (item) then\n\t\t\t\tinventory.slots[oldX][oldY] = nil\n\n\t\t\t\tinventory.slots[x] = inventory.slots[x] or {}\n\t\t\t\tinventory.slots[x][y] = item\n\t\t\tend\n\t\tend)\n\n\t\tnet.Receive(\"ixInventoryRemove\", function()\n\t\t\tlocal id = net.ReadUInt(32)\n\t\t\tlocal invID = net.ReadUInt(32)\n\n\t\t\tlocal inventory = ix.item.inventories[invID]\n\n\t\t\tif (!inventory) then\n\t\t\t\treturn\n\t\t\tend\n\n\t\t\tinventory:Remove(id)\n\n\t\t\tinvID = invID == LocalPlayer():GetCharacter():GetInventory():GetID() and 1 or invID\n\t\t\tlocal panel = ix.gui[\"inv\" .. invID]\n\n\t\t\tif (IsValid(panel)) then\n\t\t\t\tlocal icon = panel.panels[id]\n\n\t\t\t\tif (IsValid(icon)) then\n\t\t\t\t\tfor _, v in ipairs(icon.slots or {}) do\n\t\t\t\t\t\tif (v.item == icon) then\n\t\t\t\t\t\t\tv.item = nil\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\n\t\t\t\t\ticon:Remove()\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tlocal item = ix.item.instances[id]\n\n\t\t\tif (!item) then\n\t\t\t\treturn\n\t\t\tend\n\n\t\t\t-- we need to close any bag windows that are open because of this item\n\t\t\tif (item.isBag) then\n\t\t\t\tlocal itemInv = item:GetInventory()\n\n\t\t\t\tif (itemInv) then\n\t\t\t\t\tlocal frame = ix.gui[\"inv\" .. itemInv:GetID()]\n\n\t\t\t\t\tif (IsValid(frame)) then\n\t\t\t\t\t\tframe:Remove()\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\t\tend)\n\telse\n\t\tutil.AddNetworkString(\"ixInventorySync\")\n\t\tutil.AddNetworkString(\"ixInventorySet\")\n\t\tutil.AddNetworkString(\"ixInventoryMove\")\n\t\tutil.AddNetworkString(\"ixInventoryRemove\")\n\t\tutil.AddNetworkString(\"ixInventoryData\")\n\t\tutil.AddNetworkString(\"ixInventoryAction\")\n\n\t\tfunction ix.item.LoadItemByID(itemIndex, recipientFilter)\n\t\t\tlocal query = mysql:Select(\"ix_items\")\n\t\t\t\tquery:Select(\"item_id\")\n\t\t\t\tquery:Select(\"unique_id\")\n\t\t\t\tquery:Select(\"data\")\n\t\t\t\tquery:Select(\"character_id\")\n\t\t\t\tquery:Select(\"player_id\")\n\t\t\t\tquery:WhereIn(\"item_id\", itemIndex)\n\t\t\t\tquery:Callback(function(result)\n\t\t\t\t\tif (istable(result)) then\n\t\t\t\t\t\tfor _, v in ipairs(result) do\n\t\t\t\t\t\t\tlocal itemID = tonumber(v.item_id)\n\t\t\t\t\t\t\tlocal data = util.JSONToTable(v.data or \"[]\")\n\t\t\t\t\t\t\tlocal uniqueID = v.unique_id\n\t\t\t\t\t\t\tlocal itemTable = ix.item.list[uniqueID]\n\t\t\t\t\t\t\tlocal characterID = tonumber(v.character_id)\n\t\t\t\t\t\t\tlocal playerID = tostring(v.player_id)\n\n\t\t\t\t\t\t\tif (itemTable and itemID) then\n\t\t\t\t\t\t\t\tlocal item = ix.item.New(uniqueID, itemID)\n\n\t\t\t\t\t\t\t\titem.data = data or {}\n\t\t\t\t\t\t\t\titem.invID = 0\n\t\t\t\t\t\t\t\titem.characterID = characterID\n\t\t\t\t\t\t\t\titem.playerID = (playerID == \"\" or playerID == \"NULL\") and nil or playerID\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\t\t\t\tend)\n\t\t\tquery:Execute()\n\t\tend\n\n\t\tfunction ix.item.PerformInventoryAction(client, action, item, invID, data)\n\t\t\tlocal character = client:GetCharacter()\n\n\t\t\tif (!character) then\n\t\t\t\treturn\n\t\t\tend\n\n\t\t\tlocal inventory = ix.item.inventories[invID or 0]\n\n\t\t\tif (hook.Run(\"CanPlayerInteractItem\", client, action, item, data) == false) then\n\t\t\t\treturn\n\t\t\tend\n\n\t\t\tif (!inventory:OnCheckAccess(client)) then\n\t\t\t\treturn\n\t\t\tend\n\n\t\t\tif (isentity(item)) then\n\t\t\t\tif (IsValid(item)) then\n\t\t\t\t\tlocal entity = item\n\t\t\t\t\tlocal itemID = item.ixItemID\n\t\t\t\t\titem = ix.item.instances[itemID]\n\n\t\t\t\t\tif (!item) then\n\t\t\t\t\t\treturn\n\t\t\t\t\tend\n\n\t\t\t\t\titem.entity = entity\n\t\t\t\t\titem.player = client\n\t\t\t\telse\n\t\t\t\t\treturn\n\t\t\t\tend\n\t\t\telseif (isnumber(item)) then\n\t\t\t\titem = ix.item.instances[item]\n\n\t\t\t\tif (!item) then\n\t\t\t\t\treturn\n\t\t\t\tend\n\n\t\t\t\titem.player = client\n\t\t\tend\n\n\t\t\tif (item.entity) then\n\t\t\t\tif (client:GetShootPos():DistToSqr(item.entity:GetPos()) > 96 * 96) then\n\t\t\t\t\treturn\n\t\t\t\tend\n\n\t\t\t\tlocal useEntity = client:GetUseEntity()\n\t\t\t\tif (IsValid(useEntity) and item.entity != useEntity) then\n\t\t\t\t\treturn\n\t\t\t\tend\n\t\t\telseif (!inventory:GetItemByID(item.id)) then\n\t\t\t\treturn\n\t\t\tend\n\n\t\t\tif (!item.bAllowMultiCharacterInteraction and IsValid(client) and client:GetCharacter()) then\n\t\t\t\tlocal itemPlayerID = item:GetPlayerID()\n\t\t\t\tlocal itemCharacterID = item:GetCharacterID()\n\t\t\t\tlocal playerID = client:SteamID64()\n\t\t\t\tlocal characterID = client:GetCharacter():GetID()\n\n\t\t\t\tif (itemPlayerID and itemCharacterID and itemPlayerID == playerID and itemCharacterID != characterID) then\n\t\t\t\t\tclient:NotifyLocalized(\"itemOwned\")\n\n\t\t\t\t\titem.player = nil\n\t\t\t\t\titem.entity = nil\n\t\t\t\t\treturn\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tlocal callback = item.functions[action]\n\n\t\t\tif (callback) then\n\t\t\t\tif (callback.OnCanRun and callback.OnCanRun(item, data) == false) then\n\t\t\t\t\titem.entity = nil\n\t\t\t\t\titem.player = nil\n\n\t\t\t\t\treturn\n\t\t\t\tend\n\n\t\t\t\thook.Run(\"PlayerInteractItem\", client, action, item)\n\n\t\t\t\tlocal entity = item.entity\n\t\t\t\tlocal result\n\n\t\t\t\tif (item.hooks[action]) then\n\t\t\t\t\tresult = item.hooks[action](item, data)\n\t\t\t\tend\n\n\t\t\t\tif (result == nil) then\n\t\t\t\t\tresult = callback.OnRun(item, data)\n\t\t\t\tend\n\n\t\t\t\tif (item.postHooks[action]) then\n\t\t\t\t\t-- Posthooks shouldn't override the result from OnRun\n\t\t\t\t\titem.postHooks[action](item, result, data)\n\t\t\t\tend\n\n\t\t\t\tif (result != false) then\n\t\t\t\t\tif (IsValid(entity)) then\n\t\t\t\t\t\tentity.ixIsSafe = true\n\t\t\t\t\t\tentity:Remove()\n\t\t\t\t\telse\n\t\t\t\t\t\titem:Remove()\n\t\t\t\t\tend\n\t\t\t\tend\n\n\t\t\t\titem.entity = nil\n\t\t\t\titem.player = nil\n\n\t\t\t\treturn result != false\n\t\t\tend\n\t\tend\n\n\t\tlocal function NetworkInventoryMove(receiver, invID, itemID, oldX, oldY, x, y)\n\t\t\tnet.Start(\"ixInventoryMove\")\n\t\t\t\tnet.WriteUInt(invID, 32)\n\t\t\t\tnet.WriteUInt(itemID, 32)\n\t\t\t\tnet.WriteUInt(oldX, 6)\n\t\t\t\tnet.WriteUInt(oldY, 6)\n\t\t\t\tnet.WriteUInt(x, 6)\n\t\t\t\tnet.WriteUInt(y, 6)\n\t\t\tnet.Send(receiver)\n\t\tend\n\n\t\tnet.Receive(\"ixInventoryMove\", function(length, client)\n\t\t\tlocal oldX, oldY, x, y = net.ReadUInt(6), net.ReadUInt(6), net.ReadUInt(6), net.ReadUInt(6)\n\t\t\tlocal invID, newInvID = net.ReadUInt(32), net.ReadUInt(32)\n\n\t\t\tlocal character = client:GetCharacter()\n\n\t\t\tif (character) then\n\t\t\t\tlocal inventory = ix.item.inventories[invID]\n\n\t\t\t\tif (!inventory) then\n\t\t\t\t\treturn\n\t\t\t\tend\n\n\t\t\t\tif ((inventory.owner and inventory.owner == character:GetID()) or inventory:OnCheckAccess(client)) then\n\t\t\t\t\tlocal item = inventory:GetItemAt(oldX, oldY)\n\n\t\t\t\t\tif (item) then\n\t\t\t\t\t\tif (newInvID and invID != newInvID) then\n\t\t\t\t\t\t\tlocal inventory2 = ix.item.inventories[newInvID]\n\n\t\t\t\t\t\t\tif (inventory2) then\n\t\t\t\t\t\t\t\tlocal bStatus, error = item:Transfer(newInvID, x, y, client)\n\n\t\t\t\t\t\t\t\tif (!bStatus) then\n\t\t\t\t\t\t\t\t\tNetworkInventoryMove(\n\t\t\t\t\t\t\t\t\t\tclient, item.invID, item:GetID(), item.gridX, item.gridY, item.gridX, item.gridY\n\t\t\t\t\t\t\t\t\t)\n\n\t\t\t\t\t\t\t\t\tclient:NotifyLocalized(error or \"unknownError\")\n\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\t\treturn\n\t\t\t\t\t\tend\n\n\t\t\t\t\t\tif (inventory:CanItemFit(x, y, item.width, item.height, item)) then\n\t\t\t\t\t\t\titem.gridX = x\n\t\t\t\t\t\t\titem.gridY = y\n\n\t\t\t\t\t\t\tfor x2 = 0, item.width - 1 do\n\t\t\t\t\t\t\t\tfor y2 = 0, item.height - 1 do\n\t\t\t\t\t\t\t\t\tlocal previousX = inventory.slots[oldX + x2]\n\n\t\t\t\t\t\t\t\t\tif (previousX) then\n\t\t\t\t\t\t\t\t\t\tpreviousX[oldY + y2] = nil\n\t\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\t\tfor x2 = 0, item.width - 1 do\n\t\t\t\t\t\t\t\tfor y2 = 0, item.height - 1 do\n\t\t\t\t\t\t\t\t\tinventory.slots[x + x2] = inventory.slots[x + x2] or {}\n\t\t\t\t\t\t\t\t\tinventory.slots[x + x2][y + y2] = item\n\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\t\tlocal receivers = inventory:GetReceivers()\n\n\t\t\t\t\t\t\tif (istable(receivers)) then\n\t\t\t\t\t\t\t\tlocal filtered = {}\n\n\t\t\t\t\t\t\t\tfor _, v in ipairs(receivers) do\n\t\t\t\t\t\t\t\t\tif (v != client) then\n\t\t\t\t\t\t\t\t\t\tfiltered[#filtered + 1] = v\n\t\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\t\t\tif (#filtered > 0) then\n\t\t\t\t\t\t\t\t\tNetworkInventoryMove(\n\t\t\t\t\t\t\t\t\t\tfiltered, invID, item:GetID(), oldX, oldY, x, y\n\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\t\tif (!inventory.noSave) then\n\t\t\t\t\t\t\t\tlocal query = mysql:Update(\"ix_items\")\n\t\t\t\t\t\t\t\t\tquery:Update(\"x\", x)\n\t\t\t\t\t\t\t\t\tquery:Update(\"y\", y)\n\t\t\t\t\t\t\t\t\tquery:Where(\"item_id\", item.id)\n\t\t\t\t\t\t\t\tquery:Execute()\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tNetworkInventoryMove(\n\t\t\t\t\t\t\t\tclient, item.invID, item:GetID(), item.gridX, item.gridY, item.gridX, item.gridY\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\t\t\t\telse\n\t\t\t\t\tlocal item = inventory:GetItemAt(oldX, oldY)\n\n\t\t\t\t\tif (item) then\n\t\t\t\t\t\tNetworkInventoryMove(\n\t\t\t\t\t\t\tclient, item.invID, item.invID, item:GetID(), item.gridX, item.gridY, item.gridX, item.gridY\n\t\t\t\t\t\t)\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\t\tend)\n\n\t\tnet.Receive(\"ixInventoryAction\", function(length, client)\n\t\t\tix.item.PerformInventoryAction(client, net.ReadString(), net.ReadUInt(32), net.ReadUInt(32), net.ReadTable())\n\t\tend)\n\tend\n\n\t--- Instances and spawns a given item type.\n\t-- @realm server\n\t-- @string uniqueID Unique ID of the item\n\t-- @vector position The position in which the item's entity will be spawned\n\t-- @func[opt=nil] callback Function to call when the item entity is created\n\t-- @angle[opt=angle_zero] angles The angles at which the item's entity will spawn\n\t-- @tab[opt=nil] data Additional data for this item instance\n\tfunction ix.item.Spawn(uniqueID, position, callback, angles, data)\n\t\tix.item.Instance(0, uniqueID, data or {}, 1, 1, function(item)\n\t\t\tlocal entity = item:Spawn(position, angles)\n\n\t\t\tif (callback) then\n\t\t\t\tcallback(item, entity)\n\t\t\tend\n\t\tend)\n\tend\nend\n\n--- Inventory util functions for character\n-- @classmod Character\n\n--- Returns this character's associated `Inventory` object.\n-- @function GetInventory\n-- @realm shared\n-- @treturn Inventory This character's inventory\nix.char.RegisterVar(\"Inventory\", {\n\tbNoNetworking = true,\n\tbNoDisplay = true,\n\tOnGet = function(character, index)\n\t\tif (index and !isnumber(index)) then\n\t\t\treturn character.vars.inv or {}\n\t\tend\n\n\t\treturn character.vars.inv and character.vars.inv[index or 1]\n\tend,\n\talias = \"Inv\"\n})\n"
  },
  {
    "path": "gamemode/core/libs/sh_language.lua",
    "content": "\n--[[--\nMulti-language phrase support.\n\nHelix has support for multiple languages, and you can easily leverage this system for use in your own schema, plugins, etc.\nLanguages will be loaded from the schema and any plugins in `languages/sh_languagename.lua`, where `languagename` is the id of a\nlanguage (`english` for English, `french` for French, etc). The structure of a language file is a table of phrases with the key\nas its phrase ID and the value as its translation for that language. For example, in `plugins/area/languages/sh_english.lua`:\n\tLANGUAGE = {\n\t\tarea = \"Area\",\n\t\tareas = \"Areas\",\n\t\tareaEditMode = \"Area Edit Mode\",\n\t\t-- etc.\n\t}\n\nThe phrases defined in these language files can be used with the `L` global function:\n\tprint(L(\"areaEditMode\"))\n\t> Area Edit Mode\n\nAll phrases are formatted with `string.format`, so if you wish to add some info in a phrase you can use standard Lua string\nformatting arguments:\n\tprint(L(\"areaDeleteConfirm\", \"Test\"))\n\t> Are you sure you want to delete the area \"Test\"?\n\nPhrases are also usable on the server, but only when trying to localize a phrase based on a client's preferences. The server\ndoes not have a set language. An example:\n\tEntity(1):ChatPrint(L(\"areaEditMode\"))\n\t> -- \"Area Edit Mode\" will print in the player's chatbox\n]]\n-- @module ix.lang\n\nix.lang = ix.lang or {}\nix.lang.stored = ix.lang.stored or {}\nix.lang.names = ix.lang.names or {}\n\n--- Loads language files from a directory.\n-- @realm shared\n-- @internal\n-- @string directory Directory to load language files from\nfunction ix.lang.LoadFromDir(directory)\n\tfor _, v in ipairs(file.Find(directory..\"/sh_*.lua\", \"LUA\")) do\n\t\tlocal niceName = v:sub(4, -5):lower()\n\n\t\tix.util.Include(directory..\"/\"..v, \"shared\")\n\n\t\tif (LANGUAGE) then\n\t\t\tif (NAME) then\n\t\t\t\tix.lang.names[niceName] = NAME\n\t\t\t\tNAME = nil\n\t\t\tend\n\n\t\t\tix.lang.AddTable(niceName, LANGUAGE)\n\t\t\tLANGUAGE = nil\n\t\tend\n\tend\nend\n\n--- Adds phrases to a language. This is used when you aren't adding entries through the files in the `languages/` folder. A\n-- common use case is adding language phrases in a single-file plugin.\n-- @realm shared\n-- @string language The ID of the language\n-- @tab data Language data to add to the given language\n-- @usage ix.lang.AddTable(\"english\", {\n-- \tmyPhrase = \"My Phrase\"\n-- })\nfunction ix.lang.AddTable(language, data)\n\tlanguage = tostring(language):lower()\n\tix.lang.stored[language] = table.Merge(ix.lang.stored[language] or {}, data)\nend\n\nif (SERVER) then\n\t-- luacheck: globals L\n\tfunction L(key, client, ...)\n\t\tlocal languages = ix.lang.stored\n\t\tlocal langKey = ix.option.Get(client, \"language\", \"english\")\n\t\tlocal info = languages[langKey] or languages.english\n\n\t\treturn string.format(info and info[key] or languages.english[key] or key, ...)\n\tend\n\n\t-- luacheck: globals L2\n\tfunction L2(key, client, ...)\n\t\tlocal languages = ix.lang.stored\n\t\tlocal langKey = ix.option.Get(client, \"language\", \"english\")\n\t\tlocal info = languages[langKey] or languages.english\n\n\t\tif (info and info[key]) then\n\t\t\treturn string.format(info[key], ...)\n\t\tend\n\tend\nelse\n\tfunction L(key, ...)\n\t\tlocal languages = ix.lang.stored\n\t\tlocal langKey = ix.option.Get(\"language\", \"english\")\n\t\tlocal info = languages[langKey] or languages.english\n\n\t\treturn string.format(info and info[key] or languages.english[key] or key, ...)\n\tend\n\n\tfunction L2(key, ...)\n\t\tlocal langKey = ix.option.Get(\"language\", \"english\")\n\t\tlocal info = ix.lang.stored[langKey]\n\n\t\tif (info and info[key]) then\n\t\t\treturn string.format(info[key], ...)\n\t\tend\n\tend\nend\n"
  },
  {
    "path": "gamemode/core/libs/sh_log.lua",
    "content": "\n--[[--\nLogging helper functions.\n\nPredefined flags:\n\tFLAG_NORMAL\n\tFLAG_SUCCESS\n\tFLAG_WARNING\n\tFLAG_DANGER\n\tFLAG_SERVER\n\tFLAG_DEV\n]]\n-- @module ix.log\n\n-- luacheck: globals FLAG_NORMAL FLAG_SUCCESS FLAG_WARNING FLAG_DANGER FLAG_SERVER FLAG_DEV\nFLAG_NORMAL = 0\nFLAG_SUCCESS = 1\nFLAG_WARNING = 2\nFLAG_DANGER = 3\nFLAG_SERVER = 4\nFLAG_DEV = 5\n\nix.log = ix.log or {}\nix.log.color = {\n\t[FLAG_NORMAL] = Color(200, 200, 200),\n\t[FLAG_SUCCESS] = Color(50, 200, 50),\n\t[FLAG_WARNING] = Color(255, 255, 0),\n\t[FLAG_DANGER] = Color(255, 50, 50),\n\t[FLAG_SERVER] = Color(200, 200, 220),\n\t[FLAG_DEV] = Color(200, 200, 220),\n}\n\nCAMI.RegisterPrivilege({\n\tName = \"Helix - Logs\",\n\tMinAccess = \"admin\"\n})\n\nlocal consoleColor = Color(50, 200, 50)\n\nif (SERVER) then\n\tif (!ix.db) then\n\t\tinclude(\"sv_database.lua\")\n\tend\n\n\tutil.AddNetworkString(\"ixLogStream\")\n\n\tfunction ix.log.LoadTables()\n\t\tix.log.CallHandler(\"Load\")\n\tend\n\n\tix.log.types = ix.log.types or {}\n\n\t--- Registers a log type for `ix.log.Add`.\n\t-- `format` is either a format string or a function `(client, ...) -> string`.\n\t--\n\t-- `flag` is a display/severity tag (see `FLAG_*`). If omitted, it behaves like a normal log.\n\t-- @realm server\n\t-- @tparam string logType Type name used when calling `ix.log.Add`.\n\t-- @tparam string|function format Format string or formatter function.\n\t-- @tparam[opt] number flag One of the `FLAG_*` constants.\n\t-- @usage\n\t-- ix.log.AddType(\"charMoneyGive\", \"%s gave %s %d tokens.\", FLAG_SUCCESS)\n\tfunction ix.log.AddType(logType, format, flag)\n\t\tix.log.types[logType] = {format = format, flag = flag}\n\tend\n\n\t--- Turns a log type + args into `(message, flag)`.\n\t-- Missing type falls back to a warning string. No formatter returns `-1` (caller bails).\n\t-- @internal\n\t-- @realm server\n\t-- @tparam Player|nil client Player tied to the log.\n\t-- @tparam string logType Log type name.\n\t-- @param ... Arguments for the formatter.\n\t-- @treturn string|number Message string, or -1 to skip logging.\n\t-- @treturn number Flag from the registered type.\n\tfunction ix.log.Parse(client, logType, ...)\n\t\tlocal info = ix.log.types[logType]\n\n\t\tif (!info) then\n\t\t\tErrorNoHalt(\"attempted to add entry to non-existent log type \\\"\" .. tostring(logType) .. \"\\\"\\n\")\n\t\t\tlocal fallback = logType..\" : \"\n\t\t\tif (client) then\n\t\t\t\tfallback = fallback..client:Name()..\" - \"\n\t\t\tend\n\t\t\tfor _, v in ipairs({...}) do\n\t\t\t\tfallback = fallback..tostring(v)..\" \"\n\t\t\tend\n\t\t\treturn fallback, FLAG_WARNING\n\t\tend\n\n\t\tlocal text = info and info.format\n\n\t\tif (text) then\n\t\t\tif (isfunction(text)) then\n\t\t\t\ttext = text(client, ...)\n\t\t\tend\n\t\telse\n\t\t\ttext = -1\n\t\tend\n\n\t\treturn text, info.flag\n\tend\n\n\t--- Adds a log entry without using a log type.\n\t-- Sends the message to admins with log access, prints it to the server console,\n\t-- and writes it through log handlers unless `bNoSave` is true.\n\t-- @realm server\n\t-- @tparam string logString Pre-formatted log message.\n\t-- @tparam[opt] boolean bNoSave If true, skips handler output (e.g. file logging).\n\t-- @usage ix.log.AddRaw(\"Server is restarting in 5 minutes.\")\n\t-- @see ix.log.Add\n\tfunction ix.log.AddRaw(logString, bNoSave)\n\t\tCAMI.GetPlayersWithAccess(\"Helix - Logs\", function(receivers)\n\t\t\tix.log.Send(receivers, logString)\n\t\tend)\n\n\t\tMsg(\"[LOG] \", logString .. \"\\n\")\n\n\t\tif (!bNoSave) then\n\t\t\tix.log.CallHandler(\"Write\", nil, logString)\n\t\tend\n\tend\n\n\t--- Adds a typed log entry.\n\t-- The entry is visible to admins with log access, printed to the server console,\n\t-- and written through the logging system.\n\t-- @realm server\n\t-- @tparam Player|nil client Player tied to the log (can be nil).\n\t-- @tparam string logType Type registered with `ix.log.AddType`.\n\t-- @param ... Arguments for the formatter.\n\t-- @usage ix.log.Add(client, \"charMoneyGive\", giverName, receiverName, amount)\n\t-- @see ix.log.AddType\n\t-- @see ix.log.AddRaw\n\tfunction ix.log.Add(client, logType, ...)\n\t\tlocal logString, logFlag = ix.log.Parse(client, logType, ...)\n\t\tif (logString == -1) then return end\n\n\t\tCAMI.GetPlayersWithAccess(\"Helix - Logs\", function(receivers)\n\t\t\tix.log.Send(receivers, logString, logFlag)\n\t\tend)\n\n\t\tMsg(\"[LOG] \", logString .. \"\\n\")\n\n\t\tix.log.CallHandler(\"Write\", client, logString, logFlag, logType, {...})\n\tend\n\n\t--- Sends a log entry to clients.\n\t-- @internal\n\t-- @realm server\n\t-- @tparam Player|table receivers Target player(s).\n\t-- @tparam string logString Log message.\n\t-- @tparam[opt] number flag `FLAG_*` value (defaults to normal).\n\tfunction ix.log.Send(receivers, logString, flag)\n\t\tnet.Start(\"ixLogStream\")\n\t\t\tnet.WriteString(logString)\n\t\t\tnet.WriteUInt(flag or 0, 4)\n\t\tnet.Send(receivers)\n\tend\n\n\tix.log.handlers = ix.log.handlers or {}\n\n\t--- Calls a specific event on all registered log handlers.\n\t-- @realm server\n\t-- @internal\n\tfunction ix.log.CallHandler(event, ...)\n\t\tfor _, v in pairs(ix.log.handlers) do\n\t\t\tif (isfunction(v[event])) then\n\t\t\t\tv[event](...)\n\t\t\tend\n\t\tend\n\tend\n\n\t-- Register a log handler\n\t-- @realm server\n\t-- @internal\n\tfunction ix.log.RegisterHandler(name, data)\n\t\tdata.name = string.gsub(name, \"%s\", \"\")\n\t\t\tname = name:lower()\n\t\tdata.uniqueID = name\n\n\t\tix.log.handlers[name] = data\n\tend\n\n\tdo\n\t\tlocal HANDLER = {}\n\n\t\tfunction HANDLER.Load()\n\t\t\tfile.CreateDir(\"helix/logs\")\n\t\tend\n\n\t\tfunction HANDLER.Write(client, message)\n\t\t\tfile.Append(\"helix/logs/\" .. os.date(\"%x\"):gsub(\"/\", \"-\") .. \".txt\", \"[\" .. os.date(\"%X\") .. \"]\\t\" .. message .. \"\\r\\n\")\n\t\tend\n\n\t\tix.log.RegisterHandler(\"File\", HANDLER)\n\tend\nelse\n\tnet.Receive(\"ixLogStream\", function(length)\n\t\tlocal logString = net.ReadString()\n\t\tlocal flag = net.ReadUInt(4)\n\n\t\tif (isstring(logString) and isnumber(flag)) then\n\t\t\tMsgC(consoleColor, \"[SERVER] \", ix.log.color[flag], logString .. \"\\n\")\n\t\tend\n\tend)\nend\n\n"
  },
  {
    "path": "gamemode/core/libs/sh_menu.lua",
    "content": "\n--[[--\nEntity menu manipulation.\n\nThe `menu` library allows you to open up a context menu of arbitrary options whose callbacks will be ran when they are selected\nfrom the panel that shows up for the player.\n]]\n-- @module ix.menu\n\n--- You'll need to pass a table of options to `ix.menu.Open` to populate the menu. This table consists of strings as its keys\n-- and functions as its values. These correspond to the text displayed in the menu and the callback to run, respectively.\n--\n-- Example usage:\n-- \tix.menu.Open({\n-- \t\tDrink = function()\n-- \t\t\tprint(\"Drink option selected!\")\n-- \t\tend,\n-- \t\tTake = function()\n-- \t\t\tprint(\"Take option selected!\")\n-- \t\tend\n-- \t}, ents.GetByIndex(1))\n-- This opens a menu with the options `\"Drink\"` and `\"Take\"` which will print a message when you click on either of the options.\n-- @realm client\n-- @table MenuOptionsStructure\n\nix.menu = ix.menu or {}\n\nif (CLIENT) then\n\t--- Opens up a context menu for the given entity.\n\t-- @realm client\n\t-- @tparam MenuOptionsStructure options Data describing what options to display\n\t-- @entity[opt] entity Entity to send commands to\n\t-- @treturn boolean Whether or not the menu opened successfully. It will fail when there is already a menu open.\n\tfunction ix.menu.Open(options, entity)\n\t\tif (IsValid(ix.menu.panel)) then\n\t\t\treturn false\n\t\tend\n\n\t\tlocal panel = vgui.Create(\"ixEntityMenu\")\n\t\tpanel:SetEntity(entity)\n\t\tpanel:SetOptions(options)\n\n\t\treturn true\n\tend\n\n\t--- Checks whether or not an entity menu is currently open.\n\t-- @realm client\n\t-- @treturn boolean Whether or not an entity menu is open\n\tfunction ix.menu.IsOpen()\n\t\treturn IsValid(ix.menu.panel)\n\tend\n\n\t--- Notifies the server of an option that was chosen for the given entity.\n\t-- @realm client\n\t-- @entity entity Entity to call option on\n\t-- @string choice Option that was chosen\n\t-- @param data Extra data to send to the entity\n\tfunction ix.menu.NetworkChoice(entity, choice, data)\n\t\tif (IsValid(entity)) then\n\t\t\tnet.Start(\"ixEntityMenuSelect\")\n\t\t\t\tnet.WriteEntity(entity)\n\t\t\t\tnet.WriteString(choice)\n\t\t\t\tnet.WriteType(data)\n\t\t\tnet.SendToServer()\n\t\tend\n\tend\nelse\n\tutil.AddNetworkString(\"ixEntityMenuSelect\")\n\n\tnet.Receive(\"ixEntityMenuSelect\", function(length, client)\n\t\tlocal entity = net.ReadEntity()\n\t\tlocal option = net.ReadString()\n\t\tlocal data = net.ReadType()\n\n\t\tif (!IsValid(entity)\n\t\t\tor !isstring(option)\n\t\t\tor hook.Run(\"CanPlayerInteractEntity\", client, entity, option, data) == false\n\t\t) then\n\t\t\treturn\n\t\tend\n\n\t\thook.Run(\"PlayerInteractEntity\", client, entity, option, data)\n\n\t\tlocal callbackName = \"OnSelect\" .. option:gsub(\"%s\", \"\")\n\n\t\tif (entity[callbackName]) then\n\t\t\tentity[callbackName](entity, client, data)\n\t\telse\n\t\t\tentity:OnOptionSelected(client, option, data)\n\t\tend\n\tend)\nend\n\ndo\n\tlocal PLAYER = FindMetaTable(\"Player\")\n\n\tif (CLIENT) then\n\t\tfunction PLAYER:GetEntityMenu()\n\t\t\tlocal options = {}\n\n\t\t\thook.Run(\"GetPlayerEntityMenu\", self, options)\n\t\t\treturn options\n\t\tend\n\telse\n\t\tfunction PLAYER:OnOptionSelected(client, option)\n\t\t\thook.Run(\"OnPlayerOptionSelected\", self, client, option)\n\t\tend\n\tend\nend\n\n"
  },
  {
    "path": "gamemode/core/libs/sh_notice.lua",
    "content": "\n--- Notification helper functions\n-- @module ix.notice\n\nif (SERVER) then\n\tutil.AddNetworkString(\"ixNotify\")\n\tutil.AddNetworkString(\"ixNotifyLocalized\")\n\n\t--- Sends a notification to a specified recipient.\n\t-- @realm server\n\t-- @string message Message to notify\n\t-- @player[opt=nil] recipient Player to be notified\n\tfunction ix.util.Notify(message, recipient)\n\t\tnet.Start(\"ixNotify\")\n\t\tnet.WriteString(message)\n\n\t\tif (recipient == nil) then\n\t\t\tnet.Broadcast()\n\t\telse\n\t\t\tnet.Send(recipient)\n\t\tend\n\tend\n\n\t--- Sends a translated notification to a specified recipient.\n\t-- @realm server\n\t-- @string message Message to notify\n\t-- @player[opt=nil] recipient Player to be notified\n\t-- @param ... Arguments to pass to the translated message\n\tfunction ix.util.NotifyLocalized(message, recipient, ...)\n\t\tnet.Start(\"ixNotifyLocalized\")\n\t\tnet.WriteString(message)\n\t\tnet.WriteTable({...})\n\n\t\tif (recipient == nil) then\n\t\t\tnet.Broadcast()\n\t\telse\n\t\t\tnet.Send(recipient)\n\t\tend\n\tend\n\n\tdo\n\t\t--- Notification util functions for players\n\t\t-- @classmod Player\n\n\t\tlocal playerMeta = FindMetaTable(\"Player\")\n\n\t\t--- Displays a prominent notification in the top-right of this player's screen.\n\t\t-- @realm shared\n\t\t-- @string message Text to display in the notification\n\t\tfunction playerMeta:Notify(message)\n\t\t\tix.util.Notify(message, self)\n\t\tend\n\n\t\t--- Displays a notification for this player with the given language phrase.\n\t\t-- @realm shared\n\t\t-- @string message ID of the phrase to display to the player\n\t\t-- @param ... Arguments to pass to the phrase\n\t\t-- @usage client:NotifyLocalized(\"mapRestarting\", 10)\n\t\t-- -- displays \"The map will restart in 10 seconds!\" if the player's language is set to English\n\t\t-- @see ix.lang\n\t\tfunction playerMeta:NotifyLocalized(message, ...)\n\t\t\tix.util.NotifyLocalized(message, self, ...)\n\t\tend\n\n\t\t--- Displays a notification for this player in the chatbox.\n\t\t-- @realm shared\n\t\t-- @string message Text to display in the notification\n\t\tfunction playerMeta:ChatNotify(message)\n\t\t\tlocal messageLength = message:utf8len()\n\n\t\t\tix.chat.Send(nil, \"notice\", message, false, {self}, {\n\t\t\t\tbError = message:utf8sub(messageLength, messageLength) == \"!\"\n\t\t\t})\n\t\tend\n\n\t\t--- Displays a notification for this player in the chatbox with the given language phrase.\n\t\t-- @realm shared\n\t\t-- @string message ID of the phrase to display to the player\n\t\t-- @param ... Arguments to pass to the phrase\n\t\t-- @see NotifyLocalized\n\t\tfunction playerMeta:ChatNotifyLocalized(message, ...)\n\t\t\tmessage = L(message, self, ...)\n\n\t\t\tlocal messageLength = message:utf8len()\n\n\t\t\tix.chat.Send(nil, \"notice\", message, false, {self}, {\n\t\t\t\tbError = message:utf8sub(messageLength, messageLength) == \"!\"\n\t\t\t})\n\t\tend\n\tend\nelse\n\t-- Create a notification panel.\n\tfunction ix.util.Notify(message)\n\t\tif (ix.option.Get(\"chatNotices\", false)) then\n\t\t\tlocal messageLength = message:utf8len()\n\n\t\t\tix.chat.Send(LocalPlayer(), \"notice\", message, false, {\n\t\t\t\tbError = message:utf8sub(messageLength, messageLength) == \"!\"\n\t\t\t})\n\n\t\t\treturn\n\t\tend\n\n\t\tif (IsValid(ix.gui.notices)) then\n\t\t\tix.gui.notices:AddNotice(message)\n\t\tend\n\n\t\tMsgC(Color(0, 255, 255), message .. \"\\n\")\n\tend\n\n\t-- Creates a translated notification.\n\tfunction ix.util.NotifyLocalized(message, ...)\n\t\tix.util.Notify(L(message, ...))\n\tend\n\n\t-- shortcut notify functions\n\tdo\n\t\tlocal playerMeta = FindMetaTable(\"Player\")\n\n\t\tfunction playerMeta:Notify(message)\n\t\t\tif (self == LocalPlayer()) then\n\t\t\t\tix.util.Notify(message)\n\t\t\tend\n\t\tend\n\n\t\tfunction playerMeta:NotifyLocalized(message, ...)\n\t\t\tif (self == LocalPlayer()) then\n\t\t\t\tix.util.NotifyLocalized(message, ...)\n\t\t\tend\n\t\tend\n\n\t\tfunction playerMeta:ChatNotify(message)\n\t\t\tif (self == LocalPlayer()) then\n\t\t\t\tlocal messageLength = message:utf8len()\n\n\t\t\t\tix.chat.Send(LocalPlayer(), \"notice\", message, false, {\n\t\t\t\t\tbError = message:utf8sub(messageLength, messageLength) == \"!\"\n\t\t\t\t})\n\t\t\tend\n\t\tend\n\n\t\tfunction playerMeta:ChatNotifyLocalized(message, ...)\n\t\t\tif (self == LocalPlayer()) then\n\t\t\t\tmessage = L(message, ...)\n\n\t\t\t\tlocal messageLength = message:utf8len()\n\n\t\t\t\tix.chat.Send(LocalPlayer(), \"notice\", message, false, {\n\t\t\t\t\tbError = message:utf8sub(messageLength, messageLength) == \"!\"\n\t\t\t\t})\n\t\t\tend\n\t\tend\n\tend\n\n\t-- Receives a notification from the server.\n\tnet.Receive(\"ixNotify\", function()\n\t\tix.util.Notify(net.ReadString())\n\tend)\n\n\t-- Receives a notification from the server.\n\tnet.Receive(\"ixNotifyLocalized\", function()\n\t\tix.util.NotifyLocalized(net.ReadString(), unpack(net.ReadTable()))\n\tend)\nend\n"
  },
  {
    "path": "gamemode/core/libs/sh_option.lua",
    "content": "\n--[[--\nClient-side configuration management.\n\nThe `option` library provides a cleaner way to manage any arbitrary data on the client without the hassle of managing CVars. It\nis analagous to the `ix.config` library, but it only deals with data that needs to be stored on the client.\n\nTo get started, you'll need to define an option in a client realm so the framework can be aware of its existence. This can be\ndone in the `cl_init.lua` file of your schema, or in an `if (CLIENT) then` statement in the `sh_plugin.lua` file of your plugin:\n\tix.option.Add(\"headbob\", ix.type.bool, true)\n\nIf you need to get the value of an option on the server, you'll need to specify `true` for the `bNetworked` argument in\n`ix.option.Add`. *NOTE:* You also need to define your option in a *shared* realm, since the server now also needs to be aware\nof its existence. This makes it so that the client will send that option's value to the server whenever it changes, which then\nmeans that the server can now retrieve the value that the client has the option set to. For example, if you need to get what\nlanguage a client is using, you can simply do the following:\n\tix.option.Get(player.GetByID(1), \"language\", \"english\")\n\nThis will return the language of the player, or `\"english\"` if one isn't found. Note that `\"language\"` is a networked option\nthat is already defined in the framework, so it will always be available. All options will show up in the options menu on the\nclient, unless `hidden` returns `true` when using `ix.option.Add`.\n\nNote that the labels for each option in the menu will use a language phrase to show the name. For example, if your option is\nnamed `headbob`, then you'll need to define a language phrase called `optHeadbob` that will be used as the option title.\n]]\n-- @module ix.option\n\nix.option = ix.option or {}\nix.option.stored = ix.option.stored or {}\nix.option.categories = ix.option.categories or {}\n\n--- Creates a client-side configuration option with the given information.\n-- @realm shared\n-- @string key Unique ID for this option\n-- @ixtype optionType Type of this option\n-- @param default Default value that this option will have - this can be nil if needed\n-- @tparam OptionStructure data Additional settings for this option\n-- @usage ix.option.Add(\"animationScale\", ix.type.number, 1, {\n-- \tcategory = \"appearance\",\n-- \tmin = 0.3,\n-- \tmax = 2,\n-- \tdecimals = 1\n-- })\nfunction ix.option.Add(key, optionType, default, data)\n\tassert(isstring(key) and key:find(\"%S\"), \"expected a non-empty string for the key\")\n\n\tdata = data or {}\n\n\tlocal oldOption = ix.option.stored[key]\n\tlocal categories = ix.option.categories\n\tlocal category = data.category or \"misc\"\n\tlocal upperName = key:sub(1, 1):upper() .. key:sub(2)\n\n\tcategories[category] = categories[category] or {}\n\tcategories[category][key] = true\n\n\t-- using explicit nil comparisons so we don't get caught by a config's value being `false`\n\tif (oldOption != nil and oldOption.default != nil) then\n\t\tdefault = oldOption.default\n\tend\n\n\t--- You can specify additional optional arguments for `ix.option.Add` by passing in a table of specific fields as the fourth\n\t-- argument.\n\t-- @table OptionStructure\n\t-- @realm shared\n\t-- @field[type=string,opt=\"opt\" .. key] phrase The phrase to use when displaying in the UI. The default value is your option\n\t-- key in UpperCamelCase, prefixed with `\"opt\"`. For example, if your key is `\"exampleOption\"`, the default phrase will be\n\t-- `\"optExampleOption\"`.\n\t-- @field[type=string,opt=\"optd\" .. key] description The phrase to use in the tooltip when hovered in the UI. The default\n\t-- value is your option key in UpperCamelCase, prefixed with `\"optd\"`. For example, if your key is `\"exampleOption\"`, the\n\t-- default phrase will be `\"optdExampleOption\"`.\n\t-- @field[type=string,opt=\"misc\"] category The category that this option should reside in. This is purely for\n\t-- aesthetic reasons when displaying the options in the options menu. When displayed in the UI, it will take the form of\n\t-- `L(\"category name\")`. This means that you must create a language phrase for the category name - otherwise it will only\n\t-- show as the exact string you've specified. If no category is set, it will default to `\"misc\"`.\n\t-- @field[type=number,opt=0] min The minimum allowed amount when setting this option. This field is not\n\t-- applicable to any type other than `ix.type.number`.\n\t-- @field[type=number,opt=10] max The maximum allowed amount when setting this option. This field is not\n\t-- applicable to any type other than `ix.type.number`.\n\t-- @field[type=number,opt=0] decimals How many decimals to constrain to when using a number type. This field is not\n\t-- applicable to any type other than `ix.type.number`.\n\t-- @field[type=boolean,opt=false] bNetworked Whether or not the server should be aware of this option for each client.\n\t-- @field[type=function,opt] OnChanged The function to run when this option is changed - this includes whether it was set\n\t-- by the player, or through code using `ix.option.Set`.\n\t-- \tOnChanged = function(oldValue, value)\n\t-- \t\tprint(\"new value is\", value)\n\t-- \tend\n\t-- @field[type=function,opt] hidden The function to check whether the option should be hidden from the options menu.\n\t-- @field[type=function,opt] populate The function to run when the option needs to be added to the menu. This is a required\n\t-- field for any array options. It should return a table of entries where the key is the value to set in `ix.option.Set`,\n\t-- and the value is the display name for the entry. An example:\n\t--\n\t-- \tpopulate = function()\n\t-- \t\treturn {\n\t-- \t\t\t[\"english\"] = \"English\",\n\t-- \t\t\t[\"french\"] = \"French\",\n\t-- \t\t\t[\"spanish\"] = \"Spanish\"\n\t-- \t\t}\n\t-- \tend\n\tix.option.stored[key] = {\n\t\tkey = key,\n\t\tphrase = data.phrase or \"opt\" .. upperName,\n\t\tdescription = data.description or \"optd\" .. upperName,\n\t\ttype = optionType,\n\t\tdefault = default,\n\t\tmin = data.min or 0,\n\t\tmax = data.max or 10,\n\t\tdecimals = data.decimals or 0,\n\t\tcategory = data.category or \"misc\",\n\t\tbNetworked = data.bNetworked and true or false,\n\t\thidden = data.hidden or nil,\n\t\tpopulate = data.populate or nil,\n\t\tOnChanged = data.OnChanged or nil\n\t}\nend\n\n--- Sets the default value for a user option.\n-- @realm shared\n-- @string key Unique ID of the option\n-- @param value Default value for the user option\nfunction ix.option.SetDefault(key, value)\n\tlocal option = ix.option.stored[key]\n\n\tif (option) then\n\t\toption.default = value\n\telse\n\t\t-- set up dummy option if we're setting default of option that doesn't exist yet (i.e schema setting framework default)\n\t\tix.option.stored[key] = {\n\t\t\tdefault = value\n\t\t}\n\tend\nend\n\n--- Loads all saved options from disk.\n-- @realm shared\n-- @internal\nfunction ix.option.Load()\n\tix.util.Include(\"helix/gamemode/config/sh_options.lua\")\n\n\tif (CLIENT) then\n\t\tlocal options = ix.data.Get(\"options\", nil, true, true)\n\n\t\tif (options) then\n\t\t\tfor k, v in pairs(options) do\n\t\t\t\tix.option.client[k] = v\n\t\t\tend\n\t\tend\n\n\t\tix.option.Sync()\n\tend\nend\n\n--- Returns all of the available options. Note that this does contain the actual values of the options, just their properties.\n-- @realm shared\n-- @treturn table Table of all options\n-- @usage PrintTable(ix.option.GetAll())\n-- > language:\n-- >\tbNetworked = true\n-- >\tdefault = english\n-- >\ttype = 512\n-- -- etc.\nfunction ix.option.GetAll()\n\treturn ix.option.stored\nend\n\n\n--- Returns all of the available options grouped by their categories. The returned table contains category tables, that contain\n-- all the options in that category as an array (this is so you can sort them if you'd like).\n-- @realm shared\n-- @bool[opt=false] bRemoveHidden Remove entries that are marked as hidden\n-- @treturn table Table of all options\n-- @usage PrintTable(ix.option.GetAllByCategories())\n-- > general:\n-- >\t1:\n-- >\t\tkey = language\n-- >\t\tbNetworked = true\n-- >\t\tdefault = english\n-- >\t\ttype = 512\n-- -- etc.\nfunction ix.option.GetAllByCategories(bRemoveHidden)\n\tlocal result = {}\n\n\tfor k, v in pairs(ix.option.categories) do\n\t\tfor k2, _ in pairs(v) do\n\t\t\tlocal option = ix.option.stored[k2]\n\n\t\t\tif (bRemoveHidden and isfunction(option.hidden) and option.hidden()) then\n\t\t\t\tcontinue\n\t\t\tend\n\n\t\t\t-- we create the category table here because it could contain all hidden options which makes the table empty\n\t\t\tresult[k] = result[k] or {}\n\t\t\tresult[k][#result[k] + 1] = option\n\t\tend\n\tend\n\n\treturn result\nend\n\nif (CLIENT) then\n\tix.option.client = ix.option.client or {}\n\n\t--- Sets an option value for the local player.\n\t-- This function will error when an invalid key is passed.\n\t-- @realm client\n\t-- @string key Unique ID of the option\n\t-- @param value New value to assign to the option\n\t-- @bool[opt=false] bNoSave Whether or not to avoid saving\n\tfunction ix.option.Set(key, value, bNoSave)\n\t\tlocal option = assert(ix.option.stored[key], \"invalid option key \\\"\" .. tostring(key) .. \"\\\"\")\n\n\t\tif (option.type == ix.type.number) then\n\t\t\tvalue = math.Clamp(math.Round(value, option.decimals), option.min, option.max)\n\t\tend\n\n\t\tlocal oldValue = ix.option.client[key]\n\t\tix.option.client[key] = value\n\n\t\tif (option.bNetworked) then\n\t\t\tnet.Start(\"ixOptionSet\")\n\t\t\t\tnet.WriteString(key)\n\t\t\t\tnet.WriteType(value)\n\t\t\tnet.SendToServer()\n\t\tend\n\n\t\tif (!bNoSave) then\n\t\t\tix.option.Save()\n\t\tend\n\n\t\tif (isfunction(option.OnChanged)) then\n\t\t\toption.OnChanged(oldValue, value)\n\t\tend\n\tend\n\n\t--- Retrieves an option value for the local player. If it is not set, it'll return the default that you've specified.\n\t-- @realm client\n\t-- @string key Unique ID of the option\n\t-- @param default Default value to return if the option is not set\n\t-- @return[1] Value associated with the key\n\t-- @return[2] The given default if the option is not set\n\tfunction ix.option.Get(key, default)\n\t\tlocal option = ix.option.stored[key]\n\n\t\tif (option) then\n\t\t\tlocal localValue = ix.option.client[key]\n\n\t\t\tif (localValue != nil) then\n\t\t\t\treturn localValue\n\t\t\tend\n\n\t\t\treturn option.default\n\t\tend\n\n\t\treturn default\n\tend\n\n\t--- Saves all options to disk.\n\t-- @realm client\n\t-- @internal\n\tfunction ix.option.Save()\n\t\tix.data.Set(\"options\", ix.option.client, true, true)\n\tend\n\n\t--- Syncs all networked options to the server.\n\t-- @realm client\n\tfunction ix.option.Sync()\n\t\tlocal options = {}\n\n\t\tfor k, v in pairs(ix.option.stored) do\n\t\t\tif (v.bNetworked) then\n\t\t\t\toptions[#options + 1] = {k, ix.option.client[k]}\n\t\t\tend\n\t\tend\n\n\t\tif (#options > 0) then\n\t\t\tnet.Start(\"ixOptionSync\")\n\t\t\tnet.WriteUInt(#options, 8)\n\n\t\t\tfor _, v in ipairs(options) do\n\t\t\t\tnet.WriteString(v[1])\n\t\t\t\tnet.WriteType(v[2])\n\t\t\tend\n\n\t\t\tnet.SendToServer()\n\t\tend\n\tend\nelse\n\tutil.AddNetworkString(\"ixOptionSet\")\n\tutil.AddNetworkString(\"ixOptionSync\")\n\n\tix.option.clients = ix.option.clients or {}\n\n\t--- Retrieves an option value from the specified player. If it is not set, it'll return the default that you've specified.\n\t-- This function will error when an invalid player is passed.\n\t-- @realm server\n\t-- @player client Player to retrieve option value from\n\t-- @string key Unique ID of the option\n\t-- @param default Default value to return if the option is not set\n\t-- @return[1] Value associated with the key\n\t-- @return[2] The given default if the option is not set\n\tfunction ix.option.Get(client, key, default)\n\t\tassert(IsValid(client) and client:IsPlayer(), \"expected valid player for argument #1\")\n\n\t\tlocal option = ix.option.stored[key]\n\n\t\tif (option) then\n\t\t\tlocal clientOptions = ix.option.clients[client:SteamID64()]\n\n\t\t\tif (clientOptions) then\n\t\t\t\tlocal clientOption = clientOptions[key]\n\n\t\t\t\tif (clientOption != nil) then\n\t\t\t\t\treturn clientOption\n\t\t\t\tend\n\t\t\tend\n\n\t\t\treturn option.default\n\t\tend\n\n\t\treturn default\n\tend\n\n\t-- sent whenever a client's networked option has changed\n\tnet.Receive(\"ixOptionSet\", function(length, client)\n\t\tlocal key = net.ReadString()\n\t\tlocal value = net.ReadType()\n\n\t\tlocal steamID = client:SteamID64()\n\t\tlocal option = ix.option.stored[key]\n\n\t\tif (option) then\n\t\t\tix.option.clients[steamID] = ix.option.clients[steamID] or {}\n\t\t\tix.option.clients[steamID][key] = value\n\t\telse\n\t\t\tErrorNoHalt(string.format(\n\t\t\t\t\"'%s' attempted to set option with invalid key '%s'\\n\", tostring(client) .. client:SteamID(), key\n\t\t\t))\n\t\tend\n\tend)\n\n\t-- sent on first load to sync all networked option values\n\tnet.Receive(\"ixOptionSync\", function(length, client)\n\t\tlocal indices = net.ReadUInt(8)\n\t\tlocal data = {}\n\n\t\tfor _ = 1, indices do\n\t\t\tdata[net.ReadString()] = net.ReadType()\n\t\tend\n\n\t\tlocal steamID = client:SteamID64()\n\t\tix.option.clients[steamID] = ix.option.clients[steamID] or {}\n\n\t\tfor k, v in pairs(data) do\n\t\t\tlocal option = ix.option.stored[k]\n\n\t\t\tif (option) then\n\t\t\t\tix.option.clients[steamID][k] = v\n\t\t\telse\n\t\t\t\treturn ErrorNoHalt(string.format(\n\t\t\t\t\t\"'%s' attempted to sync option with invalid key '%s'\\n\", tostring(client) .. client:SteamID(), k\n\t\t\t\t))\n\t\t\tend\n\t\tend\n\tend)\nend\n"
  },
  {
    "path": "gamemode/core/libs/sh_player.lua",
    "content": "local playerMeta = FindMetaTable(\"Player\")\n\n-- ixData information for the player.\ndo\n\tif (SERVER) then\n\t\tfunction playerMeta:GetData(key, default)\n\t\t\tif (key == true) then\n\t\t\t\treturn self.ixData\n\t\t\tend\n\n\t\t\tlocal data = self.ixData and self.ixData[key]\n\n\t\t\tif (data == nil) then\n\t\t\t\treturn default\n\t\t\telse\n\t\t\t\treturn data\n\t\t\tend\n\t\tend\n\telse\n\t\tfunction playerMeta:GetData(key, default)\n\t\t\tlocal data = ix.localData and ix.localData[key]\n\n\t\t\tif (data == nil) then\n\t\t\t\treturn default\n\t\t\telse\n\t\t\t\treturn data\n\t\t\tend\n\t\tend\n\n\t\tnet.Receive(\"ixDataSync\", function()\n\t\t\tix.localData = net.ReadTable()\n\t\t\tix.playTime = net.ReadUInt(32)\n\t\tend)\n\n\t\tnet.Receive(\"ixData\", function()\n\t\t\tix.localData = ix.localData or {}\n\t\t\tix.localData[net.ReadString()] = net.ReadType()\n\t\tend)\n\tend\nend\n\n-- Whitelist networking information here.\ndo\n\tfunction playerMeta:HasWhitelist(faction)\n\t\tlocal data = ix.faction.indices[faction]\n\n\t\tif (data) then\n\t\t\tif (data.isDefault) then\n\t\t\t\treturn true\n\t\t\tend\n\n\t\t\tlocal ixData = self:GetData(\"whitelists\", {})\n\n\t\t\treturn ixData[Schema.folder] and ixData[Schema.folder][data.uniqueID] == true or false\n\t\tend\n\n\t\treturn false\n\tend\n\n\tfunction playerMeta:GetItems()\n\t\tlocal char = self:GetCharacter()\n\n\t\tif (char) then\n\t\t\tlocal inv = char:GetInventory()\n\n\t\t\tif (inv) then\n\t\t\t\treturn inv:GetItems()\n\t\t\tend\n\t\tend\n\tend\n\n\tfunction playerMeta:GetClassData()\n\t\tlocal char = self:GetCharacter()\n\n\t\tif (char) then\n\t\t\tlocal class = char:GetClass()\n\n\t\t\tif (class) then\n\t\t\t\tlocal classData = ix.class.list[class]\n\n\t\t\t\treturn classData\n\t\t\tend\n\t\tend\n\tend\nend\n\ndo\n\tif (SERVER) then\n\t\tutil.AddNetworkString(\"PlayerModelChanged\")\n\t\tutil.AddNetworkString(\"PlayerSelectWeapon\")\n\n\t\tlocal entityMeta = FindMetaTable(\"Entity\")\n\n\t\tentityMeta.ixSetModel = entityMeta.ixSetModel or entityMeta.SetModel\n\t\tplayerMeta.ixSelectWeapon = playerMeta.ixSelectWeapon or playerMeta.SelectWeapon\n\n\t\tfunction entityMeta:SetModel(model)\n\t\t\tlocal oldModel = self:GetModel()\n\n\t\t\tif (self:IsPlayer()) then\n\t\t\t\thook.Run(\"PlayerModelChanged\", self, model, oldModel)\n\n\t\t\t\tnet.Start(\"PlayerModelChanged\")\n\t\t\t\t\tnet.WriteEntity(self)\n\t\t\t\t\tnet.WriteString(model)\n\t\t\t\t\tnet.WriteString(oldModel)\n\t\t\t\tnet.Broadcast()\n\t\t\tend\n\n\t\t\treturn self:ixSetModel(model)\n\t\tend\n\n\t\tfunction playerMeta:SelectWeapon(className)\n\t\t\tnet.Start(\"PlayerSelectWeapon\")\n\t\t\t\tnet.WriteEntity(self)\n\t\t\t\tnet.WriteString(className)\n\t\t\tnet.Broadcast()\n\n\t\t\treturn self:ixSelectWeapon(className)\n\t\tend\n\telse\n\t\tnet.Receive(\"PlayerModelChanged\", function(length)\n\t\t\thook.Run(\"PlayerModelChanged\", net.ReadEntity(), net.ReadString(), net.ReadString())\n\t\tend)\n\n\t\tnet.Receive(\"PlayerSelectWeapon\", function(length)\n\t\t\tlocal client = net.ReadEntity()\n\t\t\tlocal className = net.ReadString()\n\n\t\t\tif (!IsValid(client)) then\n\t\t\t\thook.Run(\"PlayerWeaponChanged\", client, NULL)\n\t\t\t\treturn\n\t\t\tend\n\n\t\t\tfor _, v in ipairs(client:GetWeapons()) do\n\t\t\t\tif (v:GetClass() == className) then\n\t\t\t\t\thook.Run(\"PlayerWeaponChanged\", client, v)\n\t\t\t\t\tbreak\n\t\t\t\tend\n\t\t\tend\n\t\tend)\n\tend\nend\n"
  },
  {
    "path": "gamemode/core/libs/sh_plugin.lua",
    "content": "\nix.plugin = ix.plugin or {}\nix.plugin.list = ix.plugin.list or {}\nix.plugin.unloaded = ix.plugin.unloaded or {}\n\nix.util.Include(\"helix/gamemode/core/meta/sh_tool.lua\")\n\n-- luacheck: globals HOOKS_CACHE\nHOOKS_CACHE = {}\n\nfunction ix.plugin.Load(uniqueID, path, isSingleFile, variable)\n\tif (hook.Run(\"PluginShouldLoad\", uniqueID) == false) then return end\n\n\tvariable = variable or \"PLUGIN\"\n\n\t-- Plugins within plugins situation?\n\tlocal oldPlugin = PLUGIN\n\tlocal PLUGIN = {\n\t\tfolder = path,\n\t\tplugin = oldPlugin,\n\t\tuniqueID = uniqueID,\n\t\tname = \"Unknown\",\n\t\tdescription = \"Description not available\",\n\t\tauthor = \"Anonymous\"\n\t}\n\n\tif (uniqueID == \"schema\") then\n\t\tif (Schema) then\n\t\t\tPLUGIN = Schema\n\t\tend\n\n\t\tvariable = \"Schema\"\n\t\tPLUGIN.folder = engine.ActiveGamemode()\n\telseif (ix.plugin.list[uniqueID]) then\n\t\tPLUGIN = ix.plugin.list[uniqueID]\n\tend\n\n\t_G[variable] = PLUGIN\n\tPLUGIN.loading = true\n\n\tif (!isSingleFile) then\n\t\tix.lang.LoadFromDir(path..\"/languages\")\n\t\tix.util.IncludeDir(path..\"/libs\", true)\n\t\tix.attributes.LoadFromDir(path..\"/attributes\")\n\t\tix.faction.LoadFromDir(path..\"/factions\")\n\t\tix.class.LoadFromDir(path..\"/classes\")\n\t\tix.item.LoadFromDir(path..\"/items\")\n\t\tix.plugin.LoadFromDir(path..\"/plugins\")\n\t\tix.util.IncludeDir(path..\"/derma\", true)\n\t\tix.plugin.LoadEntities(path..\"/entities\")\n\n\t\thook.Run(\"DoPluginIncludes\", path, PLUGIN)\n\tend\n\n\tix.util.Include(isSingleFile and path or path..\"/sh_\"..variable:lower()..\".lua\", \"shared\")\n\tPLUGIN.loading = false\n\n\tlocal uniqueID2 = uniqueID\n\n\tif (uniqueID2 == \"schema\") then\n\t\tuniqueID2 = PLUGIN.name\n\tend\n\n\tfunction PLUGIN:SetData(value, global, ignoreMap)\n\t\tix.data.Set(uniqueID2, value, global, ignoreMap)\n\tend\n\n\tfunction PLUGIN:GetData(default, global, ignoreMap, refresh)\n\t\treturn ix.data.Get(uniqueID2, default, global, ignoreMap, refresh) or {}\n\tend\n\n\thook.Run(\"PluginLoaded\", uniqueID, PLUGIN)\n\n\tif (uniqueID != \"schema\") then\n\t\tPLUGIN.name = PLUGIN.name or \"Unknown\"\n\t\tPLUGIN.description = PLUGIN.description or \"No description available.\"\n\n\t\tfor k, v in pairs(PLUGIN) do\n\t\t\tif (isfunction(v)) then\n\t\t\t\tHOOKS_CACHE[k] = HOOKS_CACHE[k] or {}\n\t\t\t\tHOOKS_CACHE[k][PLUGIN] = v\n\t\t\tend\n\t\tend\n\n\t\tix.plugin.list[uniqueID] = PLUGIN\n\t\t_G[variable] = oldPlugin\n\tend\n\n\tif (PLUGIN.OnLoaded) then\n\t\tPLUGIN:OnLoaded()\n\tend\nend\n\nfunction ix.plugin.GetHook(pluginName, hookName)\n\tlocal h = HOOKS_CACHE[hookName]\n\n\tif (h) then\n\t\tlocal p = ix.plugin.list[pluginName]\n\n\t\tif (p) then\n\t\t\treturn h[p]\n\t\tend\n\tend\n\n\treturn\nend\n\nfunction ix.plugin.LoadEntities(path)\n\tlocal bLoadedTools\n\tlocal files, folders\n\n\tlocal function IncludeFiles(path2, bClientOnly)\n\t\tif (SERVER and !bClientOnly) then\n\t\t\tif (file.Exists(path2..\"init.lua\", \"LUA\")) then\n\t\t\t\tix.util.Include(path2..\"init.lua\", \"server\")\n\t\t\telseif (file.Exists(path2..\"shared.lua\", \"LUA\")) then\n\t\t\t\tix.util.Include(path2..\"shared.lua\")\n\t\t\tend\n\n\t\t\tif (file.Exists(path2..\"cl_init.lua\", \"LUA\")) then\n\t\t\t\tix.util.Include(path2..\"cl_init.lua\", \"client\")\n\t\t\tend\n\t\telseif (file.Exists(path2..\"cl_init.lua\", \"LUA\")) then\n\t\t\tix.util.Include(path2..\"cl_init.lua\", \"client\")\n\t\telseif (file.Exists(path2..\"shared.lua\", \"LUA\")) then\n\t\t\tix.util.Include(path2..\"shared.lua\")\n\t\tend\n\tend\n\n\tlocal function HandleEntityInclusion(folder, variable, register, default, clientOnly, create, complete)\n\t\tfiles, folders = file.Find(path..\"/\"..folder..\"/*\", \"LUA\")\n\t\tdefault = default or {}\n\n\t\tfor _, v in ipairs(folders) do\n\t\t\tlocal path2 = path..\"/\"..folder..\"/\"..v..\"/\"\n\t\t\tv = ix.util.StripRealmPrefix(v)\n\n\t\t\t_G[variable] = table.Copy(default)\n\n\t\t\tif (!isfunction(create)) then\n\t\t\t\t_G[variable].ClassName = v\n\t\t\telse\n\t\t\t\tcreate(v)\n\t\t\tend\n\n\t\t\tIncludeFiles(path2, clientOnly)\n\n\t\t\tif (clientOnly) then\n\t\t\t\tif (CLIENT) then\n\t\t\t\t\tregister(_G[variable], v)\n\t\t\t\tend\n\t\t\telse\n\t\t\t\tregister(_G[variable], v)\n\t\t\tend\n\n\t\t\tif (isfunction(complete)) then\n\t\t\t\tcomplete(_G[variable])\n\t\t\tend\n\n\t\t\t_G[variable] = nil\n\t\tend\n\n\t\tfor _, v in ipairs(files) do\n\t\t\tlocal niceName = ix.util.StripRealmPrefix(string.StripExtension(v))\n\n\t\t\t_G[variable] = table.Copy(default)\n\n\t\t\tif (!isfunction(create)) then\n\t\t\t\t_G[variable].ClassName = niceName\n\t\t\telse\n\t\t\t\tcreate(niceName)\n\t\t\tend\n\n\t\t\tix.util.Include(path..\"/\"..folder..\"/\"..v, clientOnly and \"client\" or \"shared\")\n\n\t\t\tif (clientOnly) then\n\t\t\t\tif (CLIENT) then\n\t\t\t\t\tregister(_G[variable], niceName)\n\t\t\t\tend\n\t\t\telse\n\t\t\t\tregister(_G[variable], niceName)\n\t\t\tend\n\n\t\t\tif (isfunction(complete)) then\n\t\t\t\tcomplete(_G[variable])\n\t\t\tend\n\n\t\t\t_G[variable] = nil\n\t\tend\n\tend\n\n\tlocal function RegisterTool(tool, className)\n\t\tlocal gmodTool = weapons.GetStored(\"gmod_tool\")\n\n\t\tif (className:sub(1, 3) == \"sh_\") then\n\t\t\tclassName = className:sub(4)\n\t\tend\n\n\t\tif (gmodTool) then\n\t\t\tgmodTool.Tool[className] = tool\n\t\telse\n\t\t\t-- this should never happen\n\t\t\tErrorNoHalt(string.format(\"attempted to register tool '%s' with invalid gmod_tool weapon\", className))\n\t\tend\n\n\t\tbLoadedTools = true\n\tend\n\n\t-- Include entities.\n\tHandleEntityInclusion(\"entities\", \"ENT\", scripted_ents.Register, {\n\t\tType = \"anim\",\n\t\tBase = \"base_gmodentity\",\n\t\tSpawnable = true\n\t}, false, nil, function(ent)\n\t\tif (SERVER and ent.Holdable == true) then\n\t\t\tix.allowedHoldableClasses[ent.ClassName] = true\n\t\tend\n\tend)\n\n\t-- Include weapons.\n\tHandleEntityInclusion(\"weapons\", \"SWEP\", weapons.Register, {\n\t\tPrimary = {},\n\t\tSecondary = {},\n\t\tBase = \"weapon_base\"\n\t})\n\n\tHandleEntityInclusion(\"tools\", \"TOOL\", RegisterTool, {}, false, function(className)\n\t\tif (className:sub(1, 3) == \"sh_\") then\n\t\t\tclassName = className:sub(4)\n\t\tend\n\n\t\tTOOL = ix.meta.tool:Create()\n\t\tTOOL.Mode = className\n\t\tTOOL:CreateConVars()\n\tend)\n\n\t-- Include effects.\n\tHandleEntityInclusion(\"effects\", \"EFFECT\", effects and effects.Register, nil, true)\n\n\t-- only reload spawn menu if any new tools were registered\n\tif (CLIENT and bLoadedTools) then\n\t\tRunConsoleCommand(\"spawnmenu_reload\")\n\tend\nend\n\nfunction ix.plugin.Initialize()\n\tif SERVER then\n\t\tix.plugin.unloaded = ix.data.Get(\"unloaded\", {}, true, true)\n\tend\n\n\tix.plugin.LoadFromDir(\"helix/plugins\")\n\n\tix.plugin.Load(\"schema\", engine.ActiveGamemode()..\"/schema\")\n\thook.Run(\"InitializedSchema\")\n\n\tix.plugin.LoadFromDir(engine.ActiveGamemode()..\"/plugins\")\n\thook.Run(\"InitializedPlugins\")\nend\n\nfunction ix.plugin.Get(identifier)\n\treturn ix.plugin.list[identifier]\nend\n\nfunction ix.plugin.LoadFromDir(directory)\n\tlocal files, folders = file.Find(directory..\"/*\", \"LUA\")\n\n\tfor _, v in ipairs(folders) do\n\t\tix.plugin.Load(v, directory..\"/\"..v)\n\tend\n\n\tfor _, v in ipairs(files) do\n\t\tix.plugin.Load(string.StripExtension(v), directory..\"/\"..v, true)\n\tend\nend\n\nfunction ix.plugin.SetUnloaded(uniqueID, state, bNoSave)\n\tlocal plugin = ix.plugin.list[uniqueID]\n\n\tif (state) then\n\t\tif (plugin and plugin.OnUnload) then\n\t\t\tplugin:OnUnload()\n\t\tend\n\n\t\tix.plugin.unloaded[uniqueID] = true\n\telseif (ix.plugin.unloaded[uniqueID]) then\n\t\tix.plugin.unloaded[uniqueID] = false\n\telse\n\t\treturn false\n\tend\n\n\tif (SERVER and !bNoSave) then\n\t\tlocal status\n\n\t\tif (state) then\n\t\t\tstatus = true\n\t\tend\n\n\t\tlocal unloaded = ix.data.Get(\"unloaded\", {}, true, true)\n\t\t\tunloaded[uniqueID] = status\n\t\tix.data.Set(\"unloaded\", unloaded, true, true)\n\tend\n\n\tif (state) then\n\t\thook.Run(\"PluginUnloaded\", uniqueID)\n\tend\n\n\treturn true\nend\n\nif (SERVER) then\n\t--- Runs the `LoadData` and `PostLoadData` hooks for the gamemode, schema, and plugins. Any plugins that error during the\n\t-- hook will have their `SaveData` and `PostLoadData` hooks removed to prevent them from saving junk data.\n\t-- @internal\n\t-- @realm server\n\tfunction ix.plugin.RunLoadData()\n\t\tlocal errors = hook.SafeRun(\"LoadData\")\n\n\t\t-- remove the SaveData and PostLoadData hooks for any plugins that error during LoadData since they would probably be\n\t\t-- saving bad data. this doesn't prevent plugins from saving data via other means, but there's only so much we can do\n\t\tfor _, v in pairs(errors or {}) do\n\t\t\tif (v.plugin) then\n\t\t\t\tlocal plugin = ix.plugin.Get(v.plugin)\n\n\t\t\t\tif (plugin) then\n\t\t\t\t\tlocal saveDataHooks = HOOKS_CACHE[\"SaveData\"] or {}\n\t\t\t\t\tsaveDataHooks[plugin] = nil\n\n\t\t\t\t\tlocal postLoadDataHooks = HOOKS_CACHE[\"PostLoadData\"] or {}\n\t\t\t\t\tpostLoadDataHooks[plugin] = nil\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\n\t\thook.Run(\"PostLoadData\")\n\tend\nend\n\ndo\n\t-- luacheck: globals hook\n\thook.ixCall = hook.ixCall or hook.Call\n\n\tfunction hook.Call(name, gm, ...)\n\t\tlocal cache = HOOKS_CACHE[name]\n\n\t\tif (cache) then\n\t\t\tfor k, v in pairs(cache) do\n\t\t\t\tlocal a, b, c, d, e, f = v(k, ...)\n\n\t\t\t\tif (a != nil) then\n\t\t\t\t\treturn a, b, c, d, e, f\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\n\t\tif (Schema and Schema[name]) then\n\t\t\tlocal a, b, c, d, e, f = Schema[name](Schema, ...)\n\n\t\t\tif (a != nil) then\n\t\t\t\treturn a, b, c, d, e, f\n\t\t\tend\n\t\tend\n\n\t\treturn hook.ixCall(name, gm, ...)\n\tend\n\n\t--- Runs the given hook in a protected call so that the calling function will continue executing even if any errors occur\n\t-- while running the hook. This function is much more expensive to call than `hook.Run`, so you should avoid using it unless\n\t-- you absolutely need to avoid errors from stopping the execution of your function.\n\t-- @internal\n\t-- @realm shared\n\t-- @string name Name of the hook to run\n\t-- @param ... Arguments to pass to the hook functions\n\t-- @treturn[1] table Table of error data if an error occurred while running\n\t-- @treturn[1] ... Any arguments returned by the hook functions\n\t-- @usage local errors, bCanSpray = hook.SafeRun(\"PlayerSpray\", Entity(1))\n\t-- if (!errors) then\n\t-- \t-- do stuff with bCanSpray\n\t-- else\n\t-- \tPrintTable(errors)\n\t-- end\n\tfunction hook.SafeRun(name, ...)\n\t\tlocal errors = {}\n\t\tlocal gm = gmod and gmod.GetGamemode() or nil\n\t\tlocal cache = HOOKS_CACHE[name]\n\n\t\tif (cache) then\n\t\t\tfor k, v in pairs(cache) do\n\t\t\t\tlocal bSuccess, a, b, c, d, e, f = pcall(v, k, ...)\n\n\t\t\t\tif (bSuccess) then\n\t\t\t\t\tif (a != nil) then\n\t\t\t\t\t\treturn errors, a, b, c, d, e, f\n\t\t\t\t\tend\n\t\t\t\telse\n\t\t\t\t\tErrorNoHalt(string.format(\"[Helix] hook.SafeRun error for plugin hook \\\"%s:%s\\\":\\n\\t%s\\n%s\\n\",\n\t\t\t\t\t\ttostring(k and k.uniqueID or nil), tostring(name), tostring(a), debug.traceback()))\n\n\t\t\t\t\terrors[#errors + 1] = {\n\t\t\t\t\t\tname = name,\n\t\t\t\t\t\tplugin = k and k.uniqueID or nil,\n\t\t\t\t\t\terrorMessage = tostring(a)\n\t\t\t\t\t}\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\n\t\tif (Schema and Schema[name]) then\n\t\t\tlocal bSuccess, a, b, c, d, e, f = pcall(Schema[name], Schema, ...)\n\n\t\t\tif (bSuccess) then\n\t\t\t\tif (a != nil) then\n\t\t\t\t\treturn errors, a, b, c, d, e, f\n\t\t\t\tend\n\t\t\telse\n\t\t\t\tErrorNoHalt(string.format(\"[Helix] hook.SafeRun error for schema hook \\\"%s\\\":\\n\\t%s\\n%s\\n\",\n\t\t\t\t\ttostring(name), tostring(a), debug.traceback()))\n\n\t\t\t\terrors[#errors + 1] = {\n\t\t\t\t\tname = name,\n\t\t\t\t\tschema = Schema.name,\n\t\t\t\t\terrorMessage = tostring(a)\n\t\t\t\t}\n\t\t\tend\n\t\tend\n\n\t\tlocal bSuccess, a, b, c, d, e, f = pcall(hook.ixCall, name, gm, ...)\n\n\t\tif (bSuccess) then\n\t\t\treturn errors, a, b, c, d, e, f\n\t\telse\n\t\t\tErrorNoHalt(string.format(\"[Helix] hook.SafeRun error for gamemode hook \\\"%s\\\":\\n\\t%s\\n%s\\n\",\n\t\t\t\ttostring(name), tostring(a), debug.traceback()))\n\n\t\t\terrors[#errors + 1] = {\n\t\t\t\tname = name,\n\t\t\t\tgamemode = \"gamemode\",\n\t\t\t\terrorMessage = tostring(a)\n\t\t\t}\n\n\t\t\treturn errors\n\t\tend\n\tend\nend\n"
  },
  {
    "path": "gamemode/core/libs/sh_storage.lua",
    "content": "\n--[[--\nPlayer manipulation of inventories.\n\nThis library provides an easy way for players to manipulate other inventories. The only functions that you should need are\n`ix.storage.Open` and `ix.storage.Close`. When opening an inventory as a storage item, it will display both the given inventory\nand the player's inventory in the player's UI, which allows them to drag items to and from the given inventory.\n\nExample usage:\n\tix.storage.Open(client, inventory, {\n\t\tname = \"Filing Cabinet\",\n\t\tentity = ents.GetByIndex(3),\n\t\tbMultipleUsers = true,\n\t\tsearchText = \"Rummaging...\",\n\t\tsearchTime = 4\n\t})\n]]\n-- @module ix.storage\n\n--- There are some parameters you can customize when opening an inventory as a storage object with `ix.storage.Open`.\n-- @realm server\n-- @table StorageInfoStructure\n-- @field[type=entity] entity Entity to \"attach\" the inventory to. This is used to provide a location for the inventory for\n-- things like making sure the player doesn't move too far away from the inventory, etc. This can also be a `player` object.\n-- @field[type=number,opt=inventory id] id The ID of the nventory. This defaults to the inventory passed into `ix.Storage.Open`.\n-- @field[type=string,opt=\"Storage\"] name Title to display in the UI when the inventory is open.\n-- @field[type=boolean,opt=false] bMultipleUsers Whether or not multiple players are allowed to view this inventory at the\n-- same time.\n-- @field[type=number,opt=0] searchTime How long the player has to wait before the inventory is opened.\n-- @field[type=string,opt=\"@storageSearching\"] text Text to display to the user while opening the inventory. If prefixed with\n-- `\"@\"`, it will display a language phrase.\n-- @field[type=function,opt] OnPlayerClose Called when a player who was accessing the inventory has closed it. The\n-- argument passed to the callback is the player who closed it.\n-- @field[type=table,opt={}] data Table of arbitrary data to send to the client when the inventory has been opened.\n\nix.storage = ix.storage or {}\n\nif (SERVER) then\n\tutil.AddNetworkString(\"ixStorageOpen\")\n\tutil.AddNetworkString(\"ixStorageClose\")\n\tutil.AddNetworkString(\"ixStorageExpired\")\n\tutil.AddNetworkString(\"ixStorageMoneyTake\")\n\tutil.AddNetworkString(\"ixStorageMoneyGive\")\n\tutil.AddNetworkString(\"ixStorageMoneyUpdate\")\n\n\t--- Returns whether or not the given inventory has a storage context and is being looked at by other players.\n\t-- @realm server\n\t-- @inventory inventory Inventory to check\n\t-- @treturn bool Whether or not `inventory` is in use\n\tfunction ix.storage.InUse(inventory)\n\t\tif (inventory.storageInfo) then\n\t\t\tfor _, v in pairs(inventory:GetReceivers()) do\n\t\t\t\tif (IsValid(v) and v:IsPlayer() and v != inventory.storageInfo.entity) then\n\t\t\t\t\treturn true\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\n\t\treturn false\n\tend\n\n\t--- Returns whether or not an inventory is in use by a specific player.\n\t-- @realm server\n\t-- @inventory inventory Inventory to check\n\t-- @player client Player to check\n\t-- @treturn bool Whether or not the player is using the given `inventory`\n\tfunction ix.storage.InUseBy(inventory, client)\n\t\tif (inventory.storageInfo) then\n\t\t\tfor _, v in pairs(inventory:GetReceivers()) do\n\t\t\t\tif (IsValid(v) and v:IsPlayer() and v == client) then\n\t\t\t\t\treturn true\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\n\t\treturn false\n\tend\n\n\t--- Creates a storage context on the given inventory.\n\t-- @realm server\n\t-- @internal\n\t-- @inventory inventory Inventory to create a storage context for\n\t-- @tab info Information to store on the context\n\tfunction ix.storage.CreateContext(inventory, info)\n\t\tinfo = info or {}\n\n\t\tinfo.id = inventory:GetID()\n\t\tinfo.name = info.name or \"Storage\"\n\t\tinfo.entity = assert(IsValid(info.entity), \"expected valid entity in info table\") and info.entity\n\t\tinfo.bMultipleUsers = info.bMultipleUsers == nil and false or info.bMultipleUsers\n\t\tinfo.searchTime = tonumber(info.searchTime) or 0\n\t\tinfo.searchText = info.searchText or \"@storageSearching\"\n\t\tinfo.data = info.data or {}\n\n\t\tinventory.storageInfo = info\n\n\t\t-- remove context from any bags this inventory might have\n\t\tfor k, _ in inventory:Iter() do\n\t\t\tif (k.isBag and k:GetInventory()) then\n\t\t\t\tix.storage.CreateContext(k:GetInventory(), table.Copy(info))\n\t\t\tend\n\t\tend\n\tend\n\n\t--- Removes a storage context from an inventory if it exists.\n\t-- @realm server\n\t-- @internal\n\t-- @inventory inventory Inventory to remove a storage context from\n\tfunction ix.storage.RemoveContext(inventory)\n\t\tinventory.storageInfo = nil\n\n\t\t-- remove context from any bags this inventory might have\n\t\tfor k, _ in inventory:Iter() do\n\t\t\tif (k.isBag and k:GetInventory()) then\n\t\t\t\tix.storage.RemoveContext(k:GetInventory())\n\t\t\tend\n\t\tend\n\tend\n\n\t--- Synchronizes an inventory with a storage context to the given client.\n\t-- @realm server\n\t-- @internal\n\t-- @player client Player to sync storage for\n\t-- @inventory inventory Inventory to sync storage for\n\tfunction ix.storage.Sync(client, inventory)\n\t\tlocal info = inventory.storageInfo\n\n\t\t-- we'll retrieve the money value as we're syncing because it may have changed while\n\t\t-- we were waiting for the timer to finish\n\t\tif (info.entity.GetMoney) then\n\t\t\tinfo.data.money = info.entity:GetMoney()\n\t\telseif (info.entity:IsPlayer() and info.entity:GetCharacter()) then\n\t\t\tinfo.data.money = info.entity:GetCharacter():GetMoney()\n\t\tend\n\n\t\t-- bags are automatically sync'd when the owning inventory is sync'd\n\t\tinventory:Sync(client)\n\n\t\tnet.Start(\"ixStorageOpen\")\n\t\t\tnet.WriteUInt(info.id, 32)\n\t\t\tnet.WriteEntity(info.entity)\n\t\t\tnet.WriteString(info.name)\n\t\t\tnet.WriteTable(info.data)\n\t\tnet.Send(client)\n\tend\n\n\t--- Adds a receiver to a given inventory with a storage context.\n\t-- @realm server\n\t-- @internal\n\t-- @player client Player to sync storage for\n\t-- @inventory inventory Inventory to sync storage for\n\t-- @bool bDontSync Whether or not to skip syncing the storage to the client. If this is `true`, the storage panel will not\n\t-- show up for the player\n\tfunction ix.storage.AddReceiver(client, inventory, bDontSync)\n\t\tlocal info = inventory.storageInfo\n\n\t\tif (info) then\n\t\t\tinventory:AddReceiver(client)\n\t\t\tclient.ixOpenStorage = inventory\n\n\t\t\t-- update receivers for any bags this inventory might have\n\t\t\tfor k, _ in inventory:Iter() do\n\t\t\t\tif (k.isBag and k:GetInventory()) then\n\t\t\t\t\tk:GetInventory():AddReceiver(client)\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tif (isfunction(info.OnPlayerOpen)) then\n\t\t\t\tinfo.OnPlayerOpen(client)\n\t\t\tend\n\n\t\t\tif (!bDontSync) then\n\t\t\t\tix.storage.Sync(client, inventory)\n\t\t\tend\n\n\t\t\treturn true\n\t\tend\n\n\t\treturn false\n\tend\n\n\t--- Removes a storage receiver and removes the context if there are no more receivers.\n\t-- @realm server\n\t-- @internal\n\t-- @player client Player to remove from receivers\n\t-- @inventory inventory Inventory with storage context to remove receiver from\n\t-- @bool bDontRemove Whether or not to skip removing the storage context if there are no more receivers\n\tfunction ix.storage.RemoveReceiver(client, inventory, bDontRemove)\n\t\tlocal info = inventory.storageInfo\n\n\t\tif (info) then\n\t\t\tinventory:RemoveReceiver(client)\n\n\t\t\t-- update receivers for any bags this inventory might have\n\t\t\tfor k, _ in inventory:Iter() do\n\t\t\t\tif (k.isBag and k:GetInventory()) then\n\t\t\t\t\tk:GetInventory():RemoveReceiver(client)\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tif (isfunction(info.OnPlayerClose)) then\n\t\t\t\tinfo.OnPlayerClose(client)\n\t\t\tend\n\n\t\t\tif (!bDontRemove and !ix.storage.InUse(inventory)) then\n\t\t\t\tix.storage.RemoveContext(inventory)\n\t\t\tend\n\n\t\t\tclient.ixOpenStorage = nil\n\t\t\treturn true\n\t\tend\n\n\t\treturn false\n\tend\n\n\t--- Makes a player open an inventory that they can interact with. This can be called multiple times on the same inventory,\n\t-- if the info passed allows for multiple users.\n\t-- @realm server\n\t-- @player client Player to open the inventory for\n\t-- @inventory inventory Inventory to open\n\t-- @tab info `StorageInfoStructure` describing the storage properties\n\tfunction ix.storage.Open(client, inventory, info)\n\t\tassert(IsValid(client) and client:IsPlayer(), \"expected valid player\")\n\t\tassert(type(inventory) == \"table\" and inventory:IsInstanceOf(ix.meta.inventory), \"expected valid inventory\")\n\n\t\t-- create storage context if one isn't already created\n\t\tif (!inventory.storageInfo) then\n\t\t\tinfo = info or {}\n\t\t\tix.storage.CreateContext(inventory, info)\n\t\tend\n\n\t\tlocal storageInfo = inventory.storageInfo\n\n\t\t-- add the client to the list of receivers if we're allowed to have multiple users\n\t\t-- or if nobody else is occupying this inventory, otherwise nag the player\n\t\tif (storageInfo.bMultipleUsers or !ix.storage.InUse(inventory)) then\n\t\t\tix.storage.AddReceiver(client, inventory, true)\n\t\telse\n\t\t\tclient:NotifyLocalized(\"storageInUse\")\n\t\t\treturn\n\t\tend\n\n\t\tif (storageInfo.searchTime > 0) then\n\t\t\tclient:SetAction(storageInfo.searchText, storageInfo.searchTime)\n\t\t\tclient:DoStaredAction(storageInfo.entity, function()\n\t\t\t\tif (IsValid(client) and IsValid(storageInfo.entity) and inventory.storageInfo) then\n\t\t\t\t\tix.storage.Sync(client, inventory)\n\t\t\t\tend\n\t\t\tend, storageInfo.searchTime, function()\n\t\t\t\tif (IsValid(client)) then\n\t\t\t\t\tix.storage.RemoveReceiver(client, inventory)\n\t\t\t\t\tclient:SetAction()\n\t\t\t\tend\n\t\t\tend)\n\t\telse\n\t\t\tix.storage.Sync(client, inventory)\n\t\tend\n\tend\n\n\t--- Forcefully makes clients close this inventory if they have it open.\n\t-- @realm server\n\t-- @inventory inventory Inventory to close\n\tfunction ix.storage.Close(inventory)\n\t\tlocal receivers = inventory:GetReceivers()\n\n\t\tif (#receivers > 0) then\n\t\t\tnet.Start(\"ixStorageExpired\")\n\t\t\t\tnet.WriteUInt(inventory.storageInfo.id, 32)\n\t\t\tnet.Send(receivers)\n\t\tend\n\n\t\tix.storage.RemoveContext(inventory)\n\tend\n\n\tnet.Receive(\"ixStorageClose\", function(length, client)\n\t\tlocal inventory = client.ixOpenStorage\n\n\t\tif (inventory) then\n\t\t\tix.storage.RemoveReceiver(client, inventory)\n\t\tend\n\tend)\n\n\tnet.Receive(\"ixStorageMoneyTake\", function(length, client)\n\t\tif (CurTime() < (client.ixStorageMoneyTimer or 0)) then\n\t\t\treturn\n\t\tend\n\n\t\tlocal character = client:GetCharacter()\n\n\t\tif (!character) then\n\t\t\treturn\n\t\tend\n\n\t\tlocal storageID = net.ReadUInt(32)\n\t\tlocal amount = net.ReadUInt(32)\n\n\t\tlocal inventory = client.ixOpenStorage\n\n\t\tif (!inventory or !inventory.storageInfo or storageID != inventory:GetID()) then\n\t\t\treturn\n\t\tend\n\n\t\tlocal entity = inventory.storageInfo.entity\n\n\t\tif (!IsValid(entity) or\n\t\t\t(!entity:IsPlayer() and (!isfunction(entity.GetMoney) or !isfunction(entity.SetMoney))) or\n\t\t\t(entity:IsPlayer() and !entity:GetCharacter())) then\n\t\t\treturn\n\t\tend\n\n\t\tentity = entity:IsPlayer() and entity:GetCharacter() or entity\n\t\tamount = math.Clamp(math.Round(tonumber(amount) or 0), 0, entity:GetMoney())\n\n\t\tif (amount == 0) then\n\t\t\treturn\n\t\tend\n\n\t\tcharacter:SetMoney(character:GetMoney() + amount)\n\n\t\tlocal total = entity:GetMoney() - amount\n\t\tentity:SetMoney(total)\n\n\t\tnet.Start(\"ixStorageMoneyUpdate\")\n\t\t\tnet.WriteUInt(storageID, 32)\n\t\t\tnet.WriteUInt(total, 32)\n\t\tnet.Send(inventory:GetReceivers())\n\n\t\tix.log.Add(client, \"storageMoneyTake\", entity, amount, total)\n\n\t\tclient.ixStorageMoneyTimer = CurTime() + 0.5\n\tend)\n\n\tnet.Receive(\"ixStorageMoneyGive\", function(length, client)\n\t\tif (CurTime() < (client.ixStorageMoneyTimer or 0)) then\n\t\t\treturn\n\t\tend\n\n\t\tlocal character = client:GetCharacter()\n\n\t\tif (!character) then\n\t\t\treturn\n\t\tend\n\n\t\tlocal storageID = net.ReadUInt(32)\n\t\tlocal amount = net.ReadUInt(32)\n\n\t\tlocal inventory = client.ixOpenStorage\n\n\t\tif (!inventory or !inventory.storageInfo or storageID != inventory:GetID()) then\n\t\t\treturn\n\t\tend\n\n\t\tlocal entity = inventory.storageInfo.entity\n\n\t\tif (!IsValid(entity) or\n\t\t\t(!entity:IsPlayer() and (!isfunction(entity.GetMoney) or !isfunction(entity.SetMoney))) or\n\t\t\t(entity:IsPlayer() and !entity:GetCharacter())) then\n\t\t\treturn\n\t\tend\n\n\t\tentity = entity:IsPlayer() and entity:GetCharacter() or entity\n\t\tamount = math.Clamp(math.Round(tonumber(amount) or 0), 0, character:GetMoney())\n\n\t\tif (amount == 0) then\n\t\t\treturn\n\t\tend\n\n\t\tcharacter:SetMoney(character:GetMoney() - amount)\n\n\t\tlocal total = entity:GetMoney() + amount\n\t\tentity:SetMoney(total)\n\n\t\tnet.Start(\"ixStorageMoneyUpdate\")\n\t\t\tnet.WriteUInt(storageID, 32)\n\t\t\tnet.WriteUInt(total, 32)\n\t\tnet.Send(inventory:GetReceivers())\n\n\t\tix.log.Add(client, \"storageMoneyGive\", entity, amount, total)\n\n\t\tclient.ixStorageMoneyTimer = CurTime() + 0.5\n\tend)\nelse\n\tnet.Receive(\"ixStorageOpen\", function()\n\t\tif (IsValid(ix.gui.menu)) then\n\t\t\tnet.Start(\"ixStorageClose\")\n\t\t\tnet.SendToServer()\n\t\t\treturn\n\t\tend\n\n\t\tlocal id = net.ReadUInt(32)\n\t\tlocal entity = net.ReadEntity()\n\t\tlocal name = net.ReadString()\n\t\tlocal data = net.ReadTable()\n\n\t\tlocal inventory = ix.item.inventories[id]\n\n\t\tif (IsValid(entity) and inventory and inventory.slots) then\n\t\t\tlocal localInventory = LocalPlayer():GetCharacter():GetInventory()\n\t\t\tlocal panel = vgui.Create(\"ixStorageView\")\n\n\t\t\tif (localInventory) then\n\t\t\t\tpanel:SetLocalInventory(localInventory)\n\t\t\tend\n\n\t\t\tpanel:SetStorageID(id)\n\t\t\tpanel:SetStorageTitle(name)\n\t\t\tpanel:SetStorageInventory(inventory)\n\n\t\t\tif (data.money) then\n\t\t\t\tif (localInventory) then\n\t\t\t\t\tpanel:SetLocalMoney(LocalPlayer():GetCharacter():GetMoney())\n\t\t\t\tend\n\n\t\t\t\tpanel:SetStorageMoney(data.money)\n\t\t\tend\n\t\tend\n\tend)\n\n\tnet.Receive(\"ixStorageExpired\", function()\n\t\tif (IsValid(ix.gui.openedStorage)) then\n\t\t\tix.gui.openedStorage:Remove()\n\t\tend\n\n\t\tlocal id = net.ReadUInt(32)\n\n\t\tif (id != 0) then\n\t\t\tix.item.inventories[id] = nil\n\t\tend\n\tend)\n\n\tnet.Receive(\"ixStorageMoneyUpdate\", function()\n\t\tlocal storageID = net.ReadUInt(32)\n\t\tlocal amount = net.ReadUInt(32)\n\n\t\tlocal panel = ix.gui.openedStorage\n\n\t\tif (!IsValid(panel) or panel:GetStorageID() != storageID) then\n\t\t\treturn\n\t\tend\n\n\t\tpanel:SetStorageMoney(amount)\n\t\tpanel:SetLocalMoney(LocalPlayer():GetCharacter():GetMoney())\n\tend)\nend\n"
  },
  {
    "path": "gamemode/core/libs/sv_database.lua",
    "content": "\nix.db = ix.db or {\n\tschema = {},\n\tschemaQueue = {},\n\ttype = {\n\t\t-- TODO: more specific types, lengths, and defaults\n\t\t-- i.e INT(11) UNSIGNED, SMALLINT(4), LONGTEXT, VARCHAR(350), NOT NULL, DEFAULT NULL, etc\n\t\t[ix.type.string] = \"VARCHAR(255)\",\n\t\t[ix.type.text] = \"TEXT\",\n\t\t[ix.type.number] = \"INT(11)\",\n\t\t[ix.type.steamid] = \"VARCHAR(20)\",\n\t\t[ix.type.bool] = \"TINYINT(1)\"\n\t}\n}\n\nix.db.config = ix.config.server.database or {}\n\nfunction ix.db.Connect()\n\tix.db.config.adapter = ix.db.config.adapter or \"sqlite\"\n\n\tlocal dbmodule = ix.db.config.adapter\n\tlocal hostname = ix.db.config.hostname\n\tlocal username = ix.db.config.username\n\tlocal password = ix.db.config.password\n\tlocal database = ix.db.config.database\n\tlocal port = ix.db.config.port\n\n\tmysql:SetModule(dbmodule)\n\tmysql:Connect(hostname, username, password, database, port)\nend\n\nfunction ix.db.AddToSchema(schemaType, field, fieldType)\n\tif (!ix.db.type[fieldType]) then\n\t\terror(string.format(\"attempted to add field in schema with invalid type '%s'\", fieldType))\n\t\treturn\n\tend\n\n\tif (!mysql:IsConnected() or !ix.db.schema[schemaType]) then\n\t\tix.db.schemaQueue[#ix.db.schemaQueue + 1] = {schemaType, field, fieldType}\n\t\treturn\n\tend\n\n\tix.db.InsertSchema(schemaType, field, fieldType)\nend\n\n-- this is only ever used internally\nfunction ix.db.InsertSchema(schemaType, field, fieldType)\n\tlocal schema = ix.db.schema[schemaType]\n\n\tif (!schema) then\n\t\terror(string.format(\"attempted to insert into schema with invalid schema type '%s'\", schemaType))\n\t\treturn\n\tend\n\n\tif (!schema[field]) then\n\t\tschema[field] = true\n\n\t\tlocal query = mysql:Update(\"ix_schema\")\n\t\t\tquery:Update(\"columns\", util.TableToJSON(schema))\n\t\t\tquery:Where(\"table\", schemaType)\n\t\tquery:Execute()\n\n\t\tquery = mysql:Alter(schemaType)\n\t\t\tquery:Add(field, ix.db.type[fieldType])\n\t\tquery:Execute()\n\tend\nend\n\nfunction ix.db.LoadTables()\n\tlocal query\n\n\tquery = mysql:Create(\"ix_schema\")\n\t\tquery:Create(\"table\", \"VARCHAR(64) NOT NULL\")\n\t\tquery:Create(\"columns\", \"TEXT NOT NULL\")\n\t\tquery:PrimaryKey(\"table\")\n\tquery:Execute()\n\n\t-- table structure will be populated with more fields when vars\n\t-- are registered using ix.char.RegisterVar\n\tquery = mysql:Create(\"ix_characters\")\n\t\tquery:Create(\"id\", \"INT(11) UNSIGNED NOT NULL AUTO_INCREMENT\")\n\t\tquery:PrimaryKey(\"id\")\n\tquery:Execute()\n\n\tquery = mysql:Create(\"ix_inventories\")\n\t\tquery:Create(\"inventory_id\", \"INT(11) UNSIGNED NOT NULL AUTO_INCREMENT\")\n\t\tquery:Create(\"character_id\", \"INT(11) UNSIGNED NOT NULL\")\n\t\tquery:Create(\"inventory_type\", \"VARCHAR(150) DEFAULT NULL\")\n\t\tquery:PrimaryKey(\"inventory_id\")\n\tquery:Execute()\n\n\tquery = mysql:Create(\"ix_items\")\n\t\tquery:Create(\"item_id\", \"INT(11) UNSIGNED NOT NULL AUTO_INCREMENT\")\n\t\tquery:Create(\"inventory_id\", \"INT(11) UNSIGNED NOT NULL\")\n\t\tquery:Create(\"unique_id\", \"VARCHAR(60) NOT NULL\")\n\t\tquery:Create(\"character_id\", \"INT(11) UNSIGNED DEFAULT NULL\")\n\t\tquery:Create(\"player_id\", \"VARCHAR(20) DEFAULT NULL\")\n\t\tquery:Create(\"data\", \"TEXT DEFAULT NULL\")\n\t\tquery:Create(\"x\", \"SMALLINT(4) NOT NULL\")\n\t\tquery:Create(\"y\", \"SMALLINT(4) NOT NULL\")\n\t\tquery:PrimaryKey(\"item_id\")\n\tquery:Execute()\n\n\tquery = mysql:Create(\"ix_players\")\n\t\tquery:Create(\"steamid\", \"VARCHAR(20) NOT NULL\")\n\t\tquery:Create(\"steam_name\", \"VARCHAR(32) NOT NULL\")\n\t\tquery:Create(\"play_time\", \"INT(11) UNSIGNED DEFAULT NULL\")\n\t\tquery:Create(\"address\", \"VARCHAR(15) DEFAULT NULL\")\n\t\tquery:Create(\"last_join_time\", \"INT(11) UNSIGNED DEFAULT NULL\")\n\t\tquery:Create(\"data\", \"TEXT\")\n\t\tquery:PrimaryKey(\"steamid\")\n\tquery:Execute()\n\n\t-- populate schema table if rows don't exist\n\tquery = mysql:InsertIgnore(\"ix_schema\")\n\t\tquery:Insert(\"table\", \"ix_characters\")\n\t\tquery:Insert(\"columns\", util.TableToJSON({}))\n\tquery:Execute()\n\n\t-- load schema from database\n\tquery = mysql:Select(\"ix_schema\")\n\t\tquery:Callback(function(result)\n\t\t\tif (!istable(result)) then\n\t\t\t\treturn\n\t\t\tend\n\n\t\t\tfor _, v in pairs(result) do\n\t\t\t\tix.db.schema[v.table] = util.JSONToTable(v.columns)\n\t\t\tend\n\n\t\t\t-- update schema if needed\n\t\t\tfor i = 1, #ix.db.schemaQueue do\n\t\t\t\tlocal entry = ix.db.schemaQueue[i]\n\t\t\t\tix.db.InsertSchema(entry[1], entry[2], entry[3])\n\t\t\tend\n\t\tend)\n\tquery:Execute()\nend\n\nfunction ix.db.WipeTables(callback)\n\tlocal query\n\n\tquery = mysql:Drop(\"ix_schema\")\n\tquery:Execute()\n\n\tquery = mysql:Drop(\"ix_characters\")\n\tquery:Execute()\n\n\tquery = mysql:Drop(\"ix_inventories\")\n\tquery:Execute()\n\n\tquery = mysql:Drop(\"ix_items\")\n\tquery:Execute()\n\n\tquery = mysql:Drop(\"ix_players\")\n\t\tquery:Callback(callback)\n\tquery:Execute()\nend\n\nhook.Add(\"InitPostEntity\", \"ixDatabaseConnect\", function()\n\t-- Connect to the database using SQLite, mysqoo, or tmysql4.\n\tix.db.Connect()\nend)\n\nlocal resetCalled = 0\n\nconcommand.Add(\"ix_wipedb\", function(client, cmd, arguments)\n\t-- can only be ran through the server's console\n\tif (!IsValid(client)) then\n\t\tif (resetCalled < RealTime()) then\n\t\t\tresetCalled = RealTime() + 3\n\n\t\t\tMsgC(Color(255, 0, 0),\n\t\t\t\t\"[Helix] WIPING THE DATABASE WILL PERMENANTLY REMOVE ALL PLAYER, CHARACTER, ITEM, AND INVENTORY DATA.\\n\")\n\t\t\tMsgC(Color(255, 0, 0), \"[Helix] THE SERVER WILL RESTART TO APPLY THESE CHANGES WHEN COMPLETED.\\n\")\n\t\t\tMsgC(Color(255, 0, 0), \"[Helix] TO CONFIRM DATABASE RESET, RUN 'ix_wipedb' AGAIN WITHIN 3 SECONDS.\\n\")\n\t\telse\n\t\t\tresetCalled = 0\n\t\t\tMsgC(Color(255, 0, 0), \"[Helix] DATABASE WIPE IN PROGRESS...\\n\")\n\n\t\t\thook.Run(\"OnWipeTables\")\n\t\t\tix.db.WipeTables(function()\n\t\t\t\tMsgC(Color(255, 255, 0), \"[Helix] DATABASE WIPE COMPLETED!\\n\")\n\t\t\t\tRunConsoleCommand(\"changelevel\", game.GetMap())\n\t\t\tend)\n\t\tend\n\tend\nend)\n"
  },
  {
    "path": "gamemode/core/libs/sv_networking.lua",
    "content": "\n-- @module ix.net\n\nlocal entityMeta = FindMetaTable(\"Entity\")\nlocal playerMeta = FindMetaTable(\"Player\")\n\nix.net = ix.net or {}\nix.net.list = ix.net.list or {}\nix.net.locals = ix.net.locals or {}\nix.net.globals = ix.net.globals or {}\n\nutil.AddNetworkString(\"ixGlobalVarSet\")\nutil.AddNetworkString(\"ixLocalVarSet\")\nutil.AddNetworkString(\"ixNetVarSet\")\nutil.AddNetworkString(\"ixNetVarDelete\")\n\n-- Check if there is an attempt to send a function. Can't send those.\nlocal function CheckBadType(name, object)\n\tif (isfunction(object)) then\n\t\tErrorNoHalt(\"Net var '\" .. name .. \"' contains a bad object type!\")\n\n\t\treturn true\n\telseif (istable(object)) then\n\t\tfor k, v in pairs(object) do\n\t\t\t-- Check both the key and the value for tables, and has recursion.\n\t\t\tif (CheckBadType(name, k) or CheckBadType(name, v)) then\n\t\t\t\treturn true\n\t\t\tend\n\t\tend\n\tend\nend\n\nfunction GetNetVar(key, default) -- luacheck: globals GetNetVar\n\tlocal value = ix.net.globals[key]\n\n\treturn value != nil and value or default\nend\n\nfunction SetNetVar(key, value, receiver) -- luacheck: globals SetNetVar\n\tif (CheckBadType(key, value)) then return end\n\tif (GetNetVar(key) == value and !istable(value)) then return end\n\n\tix.net.globals[key] = value\n\n\tnet.Start(\"ixGlobalVarSet\")\n\tnet.WriteString(key)\n\tnet.WriteType(value)\n\n\tif (receiver == nil) then\n\t\tnet.Broadcast()\n\telse\n\t\tnet.Send(receiver)\n\tend\nend\n\n--- Player networked variable functions\n-- @classmod Player\n\n--- Synchronizes networked variables to the client.\n-- @realm server\n-- @internal\nfunction playerMeta:SyncVars()\n\tfor k, v in pairs(ix.net.globals) do\n\t\tnet.Start(\"ixGlobalVarSet\")\n\t\t\tnet.WriteString(k)\n\t\t\tnet.WriteType(v)\n\t\tnet.Send(self)\n\tend\n\n\tfor k, v in pairs(ix.net.locals[self] or {}) do\n\t\tnet.Start(\"ixLocalVarSet\")\n\t\t\tnet.WriteString(k)\n\t\t\tnet.WriteType(v)\n\t\tnet.Send(self)\n\tend\n\n\tfor entity, data in pairs(ix.net.list) do\n\t\tif (IsValid(entity)) then\n\t\t\tlocal index = entity:EntIndex()\n\n\t\t\tfor k, v in pairs(data) do\n\t\t\t\tnet.Start(\"ixNetVarSet\")\n\t\t\t\t\tnet.WriteUInt(index, 16)\n\t\t\t\t\tnet.WriteString(k)\n\t\t\t\t\tnet.WriteType(v)\n\t\t\t\tnet.Send(self)\n\t\t\tend\n\t\tend\n\tend\nend\n\n--- Retrieves a local networked variable. If it is not set, it'll return the default that you've specified.\n-- Locally networked variables can only be retrieved from the owning player when used from the client.\n-- @realm shared\n-- @string key Identifier of the local variable\n-- @param default Default value to return if the local variable is not set\n-- @return Value associated with the key, or the default that was given if it doesn't exist\n-- @usage print(client:GetLocalVar(\"secret\"))\n-- > 12345678\n-- @see SetLocalVar\nfunction playerMeta:GetLocalVar(key, default)\n\tif (ix.net.locals[self] and ix.net.locals[self][key] != nil) then\n\t\treturn ix.net.locals[self][key]\n\tend\n\n\treturn default\nend\n\n--- Sets the value of a local networked variable.\n-- @realm server\n-- @string key Identifier of the local variable\n-- @param value New value to assign to the local variable\n-- @usage client:SetLocalVar(\"secret\", 12345678)\n-- @see GetLocalVar\nfunction playerMeta:SetLocalVar(key, value)\n\tif (CheckBadType(key, value)) then return end\n\n\tix.net.locals[self] = ix.net.locals[self] or {}\n\tix.net.locals[self][key] = value\n\n\tnet.Start(\"ixLocalVarSet\")\n\t\tnet.WriteString(key)\n\t\tnet.WriteType(value)\n\tnet.Send(self)\nend\n\n--- Entity networked variable functions\n-- @classmod Entity\n\n--- Retrieves a networked variable. If it is not set, it'll return the default that you've specified.\n-- @realm shared\n-- @string key Identifier of the networked variable\n-- @param default Default value to return if the networked variable is not set\n-- @return Value associated with the key, or the default that was given if it doesn't exist\n-- @usage print(client:GetNetVar(\"example\"))\n-- > Hello World!\n-- @see SetNetVar\nfunction entityMeta:GetNetVar(key, default)\n\tif (ix.net.list[self] and ix.net.list[self][key] != nil) then\n\t\treturn ix.net.list[self][key]\n\tend\n\n\treturn default\nend\n\n--- Sets the value of a networked variable.\n-- @realm server\n-- @string key Identifier of the networked variable\n-- @param value New value to assign to the networked variable\n-- @tab[opt=nil] receiver The players to send the networked variable to\n-- @usage client:SetNetVar(\"example\", \"Hello World!\")\n-- @see GetNetVar\nfunction entityMeta:SetNetVar(key, value, receiver)\n\tif (CheckBadType(key, value)) then return end\n\n\tix.net.list[self] = ix.net.list[self] or {}\n\n\tif (ix.net.list[self][key] != value) then\n\t\tix.net.list[self][key] = value\n\tend\n\n\tself:SendNetVar(key, receiver)\nend\n\n--- Sends a networked variable.\n-- @realm server\n-- @internal\n-- @string key Identifier of the networked variable\n-- @tab[opt=nil] receiver The players to send the networked variable to\nfunction entityMeta:SendNetVar(key, receiver)\n\tnet.Start(\"ixNetVarSet\")\n\tnet.WriteUInt(self:EntIndex(), 16)\n\tnet.WriteString(key)\n\tnet.WriteType(ix.net.list[self] and ix.net.list[self][key])\n\n\tif (receiver == nil) then\n\t\tnet.Broadcast()\n\telse\n\t\tnet.Send(receiver)\n\tend\nend\n\n--- Clears all of the networked variables.\n-- @realm server\n-- @internal\n-- @tab[opt=nil] receiver The players to clear the networked variable for\nfunction entityMeta:ClearNetVars(receiver)\n\tix.net.list[self] = nil\n\tix.net.locals[self] = nil\n\n\tnet.Start(\"ixNetVarDelete\")\n\tnet.WriteUInt(self:EntIndex(), 16)\n\n\tif (receiver == nil) then\n\t\tnet.Broadcast()\n\telse\n\t\tnet.Send(receiver)\n\tend\nend\n"
  },
  {
    "path": "gamemode/core/libs/sv_player.lua",
    "content": "local playerMeta = FindMetaTable(\"Player\")\n\n-- Player data (outside of characters) handling.\ndo\n\tutil.AddNetworkString(\"ixData\")\n\tutil.AddNetworkString(\"ixDataSync\")\n\n\tfunction playerMeta:LoadData(callback)\n\t\tlocal name = self:SteamName()\n\t\tlocal steamID64 = self:SteamID64()\n\t\tlocal timestamp = math.floor(os.time())\n\t\tlocal ip = self:IPAddress():match(\"%d+%.%d+%.%d+%.%d+\")\n\n\t\tlocal query = mysql:Select(\"ix_players\")\n\t\t\tquery:Select(\"data\")\n\t\t\tquery:Select(\"play_time\")\n\t\t\tquery:Where(\"steamid\", steamID64)\n\t\t\tquery:Callback(function(result)\n\t\t\t\tif (IsValid(self) and istable(result) and #result > 0 and result[1].data) then\n\t\t\t\t\tlocal updateQuery = mysql:Update(\"ix_players\")\n\t\t\t\t\t\tupdateQuery:Update(\"last_join_time\", timestamp)\n\t\t\t\t\t\tupdateQuery:Update(\"address\", ip)\n\t\t\t\t\t\tupdateQuery:Where(\"steamid\", steamID64)\n\t\t\t\t\tupdateQuery:Execute()\n\n\t\t\t\t\tself.ixPlayTime = tonumber(result[1].play_time) or 0\n\t\t\t\t\tself.ixData = util.JSONToTable(result[1].data)\n\n\t\t\t\t\tif (callback) then\n\t\t\t\t\t\tcallback(self.ixData)\n\t\t\t\t\tend\n\t\t\t\telse\n\t\t\t\t\tlocal insertQuery = mysql:Insert(\"ix_players\")\n\t\t\t\t\t\tinsertQuery:Insert(\"steamid\", steamID64)\n\t\t\t\t\t\tinsertQuery:Insert(\"steam_name\", name)\n\t\t\t\t\t\tinsertQuery:Insert(\"play_time\", 0)\n\t\t\t\t\t\tinsertQuery:Insert(\"address\", ip)\n\t\t\t\t\t\tinsertQuery:Insert(\"last_join_time\", timestamp)\n\t\t\t\t\t\tinsertQuery:Insert(\"data\", util.TableToJSON({}))\n\t\t\t\t\tinsertQuery:Execute()\n\n\t\t\t\t\tif (callback) then\n\t\t\t\t\t\tcallback({})\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend)\n\t\tquery:Execute()\n\tend\n\n\tfunction playerMeta:SaveData()\n\t\tif (self:IsBot()) then return end\n\n\t\tlocal name = self:SteamName()\n\t\tlocal steamID64 = self:SteamID64()\n\n\t\tlocal query = mysql:Update(\"ix_players\")\n\t\t\tquery:Update(\"steam_name\", name)\n\t\t\tquery:Update(\"play_time\", math.floor((self.ixPlayTime or 0) + (RealTime() - (self.ixJoinTime or RealTime() - 1))))\n\t\t\tquery:Update(\"data\", util.TableToJSON(self.ixData))\n\t\t\tquery:Where(\"steamid\", steamID64)\n\t\tquery:Execute()\n\tend\n\n\tfunction playerMeta:SetData(key, value, bNoNetworking)\n\t\tself.ixData = self.ixData or {}\n\t\tself.ixData[key] = value\n\n\t\tif (!bNoNetworking) then\n\t\t\tnet.Start(\"ixData\")\n\t\t\t\tnet.WriteString(key)\n\t\t\t\tnet.WriteType(value)\n\t\t\tnet.Send(self)\n\t\tend\n\tend\nend\n\n-- Whitelisting information for the player.\ndo\n\tfunction playerMeta:SetWhitelisted(faction, whitelisted)\n\t\tif (!whitelisted) then\n\t\t\twhitelisted = nil\n\t\tend\n\n\t\tlocal data = ix.faction.indices[faction]\n\n\t\tif (data) then\n\t\t\tlocal whitelists = self:GetData(\"whitelists\", {})\n\t\t\twhitelists[Schema.folder] = whitelists[Schema.folder] or {}\n\t\t\twhitelists[Schema.folder][data.uniqueID] = whitelisted and true or nil\n\n\t\t\tself:SetData(\"whitelists\", whitelists)\n\t\t\tself:SaveData()\n\n\t\t\treturn true\n\t\tend\n\n\t\treturn false\n\tend\nend\n\ndo\n\tplayerMeta.ixGive = playerMeta.ixGive or playerMeta.Give\n\n\tfunction playerMeta:Give(className, bNoAmmo)\n\t\tlocal weapon\n\n\t\tself.ixWeaponGive = true\n\t\t\tweapon = self:ixGive(className, bNoAmmo)\n\t\tself.ixWeaponGive = nil\n\n\t\treturn weapon\n\tend\nend\n"
  },
  {
    "path": "gamemode/core/libs/thirdparty/cl_ikon.lua",
    "content": "--[[\n\tBLACK TEA ICON LIBRARY FOR NUTSCRIPT 1.1\n\n\tThe MIT License (MIT)\n\n\tCopyright (c) 2017, Kyu Yeon Lee(Black Tea Za rebel1324)\n\n\tPermission is hereby granted, free of charge, to any person obtaining a copy of\n\tthis software and associated documentation files (the \"Software\"), to deal in\n\tthe Software without restriction, including without limitation the rights to\n\tuse, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of\n\tthe Software, and to permit persons to whom the Software is furnished to do so, subject\n\tto the following conditions:\n\n\tThe above copyright notice and thispermission notice shall be included in all copies\n\tor substantial portions of the Software.\n\n\tTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,\n\tINCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR\n\tPURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE\n\tFOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,\n\tARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n\tDEALINGS IN THE SOFTWARE.\n\n\tTL;DR: https://tldrlegal.com/license/mit-license\n\tOK -\n\t\t\tCommercial Use\n\t\t\tModify\n\t\t\tDistribute\n\t\t\tSublicense\n\t\t\tPrivate Use\n\n\tNOT OK -\n\t\t\tHold Liable\n\n\tMUST -\n\t\t\tInclude Copyright\n\t\t\tInclude License\n]]--\n\n--[[\n\tDefault Tables.\n]]--\n\nikon = ikon or {}\nikon.cache = ikon.cache or {}\nikon.requestList = ikon.requestList or {}\nikon.dev = false\nikon.maxSize = 8 -- 8x8 (512^2) is max icon size.\n\nIKON_BUSY = 1\nIKON_PROCESSING = 0\nIKON_SOMETHINGWRONG = -1\n\nlocal schemaName = schemaName or (Schema and Schema.folder)\n\n--[[\n\tInitialize hooks and RT Screens.\n\treturns nothing\n]]--\nfunction ikon:init()\n\tif (self.dev) then\n\t\thook.Add(\"HUDPaint\", \"ikon_dev2\", ikon.showResult)\n\telse\n\t\thook.Remove(\"HUDPaint\", \"ikon_dev2\")\n\tend\n\n\t--[[\n\t\tBeing good at gmod is knowing all of stinky hacks\n\t\t\t\t\t\t\t\t\t\t- Black Tea (2017)\n\t]]--\n\tikon.haloAdd = ikon.haloAdd or halo.Add\n\tfunction halo.Add(...)\n\t\tif (ikon.rendering != true) then\n\t\t\tikon.haloAdd(...)\n\t\tend\n\tend\n\n\tikon.haloRender = ikon.haloRender or halo.Render\n\tfunction halo.Render(...)\n\t\tif (ikon.rendering != true) then\n\t\t\tikon.haloRender(...)\n\t\tend\n\tend\n\n\tfile.CreateDir(\"helix/icons\")\n\tfile.CreateDir(\"helix/icons/\" .. schemaName)\nend\n\n--[[\n\tIKON Library Essential Material/Texture Declare\n]]--\n\nlocal TEXTURE_FLAGS_CLAMP_S = 0x0004\nlocal TEXTURE_FLAGS_CLAMP_T = 0x0008\n\nikon.max = ikon.maxSize * 64\nikon.RT = GetRenderTargetEx(\"ixIconRendered\",\n\tikon.max,\n\tikon.max,\n\tRT_SIZE_NO_CHANGE,\n\tMATERIAL_RT_DEPTH_SHARED,\n\tbit.bor(TEXTURE_FLAGS_CLAMP_S, TEXTURE_FLAGS_CLAMP_T),\n\tCREATERENDERTARGETFLAGS_UNFILTERABLE_OK,\n\tIMAGE_FORMAT_RGBA8888\n)\n\nlocal tex_effect = GetRenderTarget(\"ixIconRenderedOutline\", ikon.max, ikon.max)\nlocal mat_outline = CreateMaterial(\"ixIconRenderedTemp\", \"UnlitGeneric\", {\n\t[\"$basetexture\"] = tex_effect:GetName(),\n\t[\"$translucent\"] = 1\n})\n\nlocal lightPositions = {\n\tBOX_TOP = Color(255, 255, 255),\n\tBOX_FRONT = Color(255, 255, 255),\n}\nfunction ikon:renderHook()\n\tlocal entity = ikon.renderEntity\n\n\tif (halo.RenderedEntity() == entity) then\n\t\treturn\n\tend\n\n\tlocal w, h = ikon.curWidth * 64, ikon.curHeight * 64\n\tlocal x, y = 0, 0\n\tlocal tab\n\n\tif (ikon.info) then\n\t\ttab = {\n\t\t\torigin = ikon.info.pos,\n\t\t\tangles = ikon.info.ang,\n\t\t\tfov = ikon.info.fov,\n\t\t\toutline = ikon.info.outline,\n\t\t\toutCol = ikon.info.outlineColor\n\t\t}\n\n\t\tif (!tab.origin and !tab.angles and !tab.fov) then\n\t\t\ttable.Merge(tab, PositionSpawnIcon(entity, entity:GetPos(), true))\n\t\tend\n\telse\n\t\ttab = PositionSpawnIcon(entity, entity:GetPos(), true)\n\tend\n\n\t-- Taking MDave's Tip\n\txpcall(function()\n\t\t\trender.OverrideAlphaWriteEnable(true, true) -- some playermodel eyeballs will not render without this\n\t\t\trender.SetWriteDepthToDestAlpha(false)\n\t\t\trender.OverrideBlend(true, BLEND_ONE, BLEND_ONE, BLENDFUNC_ADD, BLEND_ONE, BLEND_ONE, BLENDFUNC_ADD)\n\t\t\trender.SuppressEngineLighting(true)\n\t\t\trender.Clear(0, 0, 0, 0, true, true)\n\n\t\t\trender.SetLightingOrigin(vector_origin)\n\t\t\trender.ResetModelLighting(200 / 255, 200 / 255, 200 / 255)\n\t\t\trender.SetColorModulation(1, 1, 1)\n\n\t\t\tfor i = 0, 6 do\n\t\t\t\tlocal col = lightPositions[i]\n\n\t\t\t\tif (col) then\n\t\t\t\t\trender.SetModelLighting(i, col.r / 255, col.g / 255, col.b / 255)\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tif (tab.outline) then\n\t\t\t\tix.util.ResetStencilValues()\n\t\t\t\trender.SetStencilEnable(true)\n\t\t\t\trender.SetStencilWriteMask(137) -- yeah random number to avoid confliction\n\t\t\t\trender.SetStencilCompareFunction(STENCILCOMPARISONFUNCTION_ALWAYS)\n\t\t\t\trender.SetStencilPassOperation(STENCILOPERATION_REPLACE)\n\t\t\t\trender.SetStencilFailOperation(STENCILOPERATION_REPLACE)\n\t\t\tend\n\n\t\t\t--[[\n\t\t\t\tAdd more effects on the Models!\n\t\t\t]]--\n\t\t\tif (ikon.info and ikon.info.drawHook) then\n\t\t\t\tikon.info.drawHook(entity)\n\t\t\tend\n\n\t\t\tcam.Start3D(tab.origin, tab.angles, tab.fov, 0, 0, w, h)\n\t\t\t\trender.SetBlend(1)\n\t\t\t\tentity:DrawModel()\n\t\t\tcam.End3D()\n\n\t\t\tif (tab.outline) then\n\t\t\t\trender.PushRenderTarget(tex_effect)\n\t\t\t\trender.Clear(0, 0, 0, 0)\n\t\t\t\trender.ClearDepth()\n\t\t\t\tcam.Start2D()\n\t\t\t\t\tcam.Start3D(tab.origin, tab.angles, tab.fov, 0, 0, w, h)\n\t\t\t\t\t\t\trender.SetBlend(0)\n\t\t\t\t\t\t\tentity:DrawModel()\n\n\t\t\t\t\t\t\trender.SetStencilWriteMask(138) -- could you please?\n\t\t\t\t\t\t\trender.SetStencilTestMask(1)\n\t\t\t\t\t\t\trender.SetStencilReferenceValue(1)\n\t\t\t\t\t\t\trender.SetStencilCompareFunction(STENCILCOMPARISONFUNCTION_EQUAL)\n\t\t\t\t\t\t\trender.SetStencilPassOperation(STENCILOPERATION_KEEP)\n\t\t\t\t\t\t\trender.SetStencilFailOperation(STENCILOPERATION_KEEP)\n\t\t\t\t\t\t\tcam.Start2D()\n\t\t\t\t\t\t\t\tsurface.SetDrawColor(tab.outCol or color_white)\n\t\t\t\t\t\t\t\tsurface.DrawRect(0, 0, ScrW(), ScrH())\n\t\t\t\t\t\t\tcam.End2D()\n\t\t\t\t\tcam.End3D()\n\t\t\t\tcam.End2D()\n\t\t\t\trender.PopRenderTarget()\n\n\t\t\t\trender.SetBlend(1)\n\t\t\t\trender.SetStencilCompareFunction(STENCILCOMPARISONFUNCTION_NOTEQUAL)\n\n\t\t\t\t--[[\n\t\t\t\t\tThanks for Noiwex\n\t\t\t\t\tNxServ.eu\n\t\t\t\t]]--\n\t\t\t\tcam.Start2D()\n\t\t\t\t\tsurface.SetMaterial(mat_outline)\n\t\t\t\t\tsurface.DrawTexturedRectUV(-2, 0, w, h, 0, 0, w / ikon.max, h / ikon.max)\n\t\t\t\t\tsurface.DrawTexturedRectUV(2, 0, w, h, 0, 0, w / ikon.max, h / ikon.max)\n\t\t\t\t\tsurface.DrawTexturedRectUV(0, 2, w, h, 0, 0, w / ikon.max, h / ikon.max)\n\t\t\t\t\tsurface.DrawTexturedRectUV(0, -2, w, h, 0, 0, w / ikon.max, h / ikon.max)\n\t\t\t\tcam.End2D()\n\n\t\t\t\trender.SetStencilEnable(false)\n\t\t\tend\n\n\t\t\trender.SuppressEngineLighting(false)\n\t\t\trender.SetWriteDepthToDestAlpha(true)\n\t\t\trender.OverrideAlphaWriteEnable(false)\n\tend, function(message)\n\t\tprint(message)\n\tend)\nend\n\nfunction ikon:showResult()\n\tlocal x, y = ScrW() / 2, ScrH() / 2\n\tlocal w, h = ikon.curWidth * 64, ikon.curHeight * 64\n\n\tsurface.SetDrawColor(255, 255, 255, 255)\n\tsurface.DrawOutlinedRect(x, 0, w, h)\n\n\tsurface.SetMaterial(mat_outline)\n\tsurface.DrawTexturedRect(x, 0, w, h)\nend\n\n--[[\n\tRenders the Icon with given arguments.\n\treturns nothing\n]]--\nfunction ikon:renderIcon(name, w, h, mdl, camInfo, updateCache)\n\tif (#ikon.requestList > 0) then return IKON_BUSY end\n\tif (ikon.requestList[name]) then return IKON_PROCESSING end\n\tif (!w or !h or !mdl) then return IKON_SOMETHINGWRONG end\n\n\tlocal capturedIcon\n\tikon.curWidth = w or 1\n\tikon.curHeight = h or 1\n\tikon.renderModel = mdl\n\n\tif (camInfo) then\n\t\tikon.info = camInfo\n\tend\n\n\tlocal w, h = ikon.curWidth * 64, ikon.curHeight * 64\n\tlocal sw, sh = ScrW(), ScrH()\n\n\tif (ikon.renderModel) then\n\t\tif (!IsValid(ikon.renderEntity)) then\n\t\t\tikon.renderEntity = ClientsideModel(ikon.renderModel, RENDERGROUP_BOTH)\n\t\t\tikon.renderEntity:SetNoDraw(true)\n\t\tend\n\tend\n\n\tikon.renderEntity:SetModel(ikon.renderModel)\n\n\tlocal bone = ikon.renderEntity:LookupBone(\"ValveBiped.Bip01_Head1\")\n\n\tif (bone) then\n\t\tikon.renderEntity:SetEyeTarget(ikon.renderEntity:GetBonePosition(bone) + ikon.renderEntity:GetForward() * 32)\n\tend\n\n\tlocal oldRT = render.GetRenderTarget()\n\trender.PushRenderTarget(ikon.RT)\n\n\tikon.rendering = true\n\t\tikon:renderHook()\n\tikon.rendering = nil\n\n\tcapturedIcon = render.Capture({\n\t\tformat = \"png\",\n\t\talpha = true,\n\t\tx = 0,\n\t\ty = 0,\n\t\tw = w,\n\t\th = h\n\t})\n\n\tfile.Write(\"helix/icons/\" .. schemaName .. \"/\" .. name .. \".png\", capturedIcon)\n\tikon.info = nil\n\trender.PopRenderTarget()\n\n\tif (updateCache) then\n\t\tlocal materialID = tostring(os.time())\n\t\tfile.Write(materialID .. \".png\", capturedIcon)\n\n\t\ttimer.Simple(0, function()\n\t\t\tlocal material = Material(\"../data/\".. materialID ..\".png\")\n\n\t\t\tikon.cache[name]  = material\n\t\t\tfile.Delete(materialID .. \".png\")\n\t\tend)\n\tend\n\n\tikon.requestList[name] = nil\n\treturn true\nend\n\n--[[\n\tGets rendered icon with given unique name.\n\treturns IMaterial\n]]--\nfunction ikon:GetIcon(name)\n\tif (ikon.cache[name]) then\n\t\treturn ikon.cache[name] -- yeah return cache\n\tend\n\n\tif (file.Exists(\"helix/icons/\" .. schemaName .. \"/\" .. name .. \".png\", \"DATA\")) then\n\t\tikon.cache[name] = Material(\"../data/helix/icons/\" .. schemaName .. \"/\".. name ..\".png\")\n\t\treturn ikon.cache[name] -- yeah return cache\n\telse\n\t\treturn false -- retryd\n\tend\nend\n\nconcommand.Add(\"ix_flushicon\", function()\n\tlocal root = \"helix/icons/\" .. schemaName\n\n\tfor _, v in ipairs(file.Find(root .. \"/*.png\", \"DATA\")) do\n\t\tfile.Delete(root .. \"/\" .. v)\n\tend\n\n\tikon.cache = {}\nend)\n\nhook.Add(\"InitializedSchema\", \"updatePath\", function()\n\tschemaName = Schema.folder\n\tikon:init()\nend)\n\nif (schemaName) then\n\tikon:init()\nend\n"
  },
  {
    "path": "gamemode/core/libs/thirdparty/data/sh_utf8_casemap.lua",
    "content": "utf8_lc_uc = {\n\t[\"a\"] = \"A\",\n\t[\"b\"] = \"B\",\n\t[\"c\"] = \"C\",\n\t[\"d\"] = \"D\",\n\t[\"e\"] = \"E\",\n\t[\"f\"] = \"F\",\n\t[\"g\"] = \"G\",\n\t[\"h\"] = \"H\",\n\t[\"i\"] = \"I\",\n\t[\"j\"] = \"J\",\n\t[\"k\"] = \"K\",\n\t[\"l\"] = \"L\",\n\t[\"m\"] = \"M\",\n\t[\"n\"] = \"N\",\n\t[\"o\"] = \"O\",\n\t[\"p\"] = \"P\",\n\t[\"q\"] = \"Q\",\n\t[\"r\"] = \"R\",\n\t[\"s\"] = \"S\",\n\t[\"t\"] = \"T\",\n\t[\"u\"] = \"U\",\n\t[\"v\"] = \"V\",\n\t[\"w\"] = \"W\",\n\t[\"x\"] = \"X\",\n\t[\"y\"] = \"Y\",\n\t[\"z\"] = \"Z\",\n\t[\"µ\"] = \"Μ\",\n\t[\"à\"] = \"À\",\n\t[\"á\"] = \"Á\",\n\t[\"â\"] = \"Â\",\n\t[\"ã\"] = \"Ã\",\n\t[\"ä\"] = \"Ä\",\n\t[\"å\"] = \"Å\",\n\t[\"æ\"] = \"Æ\",\n\t[\"ç\"] = \"Ç\",\n\t[\"è\"] = \"È\",\n\t[\"é\"] = \"É\",\n\t[\"ê\"] = \"Ê\",\n\t[\"ë\"] = \"Ë\",\n\t[\"ì\"] = \"Ì\",\n\t[\"í\"] = \"Í\",\n\t[\"î\"] = \"Î\",\n\t[\"ï\"] = \"Ï\",\n\t[\"ð\"] = \"Ð\",\n\t[\"ñ\"] = \"Ñ\",\n\t[\"ò\"] = \"Ò\",\n\t[\"ó\"] = \"Ó\",\n\t[\"ô\"] = \"Ô\",\n\t[\"õ\"] = \"Õ\",\n\t[\"ö\"] = \"Ö\",\n\t[\"ø\"] = \"Ø\",\n\t[\"ù\"] = \"Ù\",\n\t[\"ú\"] = \"Ú\",\n\t[\"û\"] = \"Û\",\n\t[\"ü\"] = \"Ü\",\n\t[\"ý\"] = \"Ý\",\n\t[\"þ\"] = \"Þ\",\n\t[\"ÿ\"] = \"Ÿ\",\n\t[\"ā\"] = \"Ā\",\n\t[\"ă\"] = \"Ă\",\n\t[\"ą\"] = \"Ą\",\n\t[\"ć\"] = \"Ć\",\n\t[\"ĉ\"] = \"Ĉ\",\n\t[\"ċ\"] = \"Ċ\",\n\t[\"č\"] = \"Č\",\n\t[\"ď\"] = \"Ď\",\n\t[\"đ\"] = \"Đ\",\n\t[\"ē\"] = \"Ē\",\n\t[\"ĕ\"] = \"Ĕ\",\n\t[\"ė\"] = \"Ė\",\n\t[\"ę\"] = \"Ę\",\n\t[\"ě\"] = \"Ě\",\n\t[\"ĝ\"] = \"Ĝ\",\n\t[\"ğ\"] = \"Ğ\",\n\t[\"ġ\"] = \"Ġ\",\n\t[\"ģ\"] = \"Ģ\",\n\t[\"ĥ\"] = \"Ĥ\",\n\t[\"ħ\"] = \"Ħ\",\n\t[\"ĩ\"] = \"Ĩ\",\n\t[\"ī\"] = \"Ī\",\n\t[\"ĭ\"] = \"Ĭ\",\n\t[\"į\"] = \"Į\",\n\t[\"ı\"] = \"I\",\n\t[\"ĳ\"] = \"Ĳ\",\n\t[\"ĵ\"] = \"Ĵ\",\n\t[\"ķ\"] = \"Ķ\",\n\t[\"ĺ\"] = \"Ĺ\",\n\t[\"ļ\"] = \"Ļ\",\n\t[\"ľ\"] = \"Ľ\",\n\t[\"ŀ\"] = \"Ŀ\",\n\t[\"ł\"] = \"Ł\",\n\t[\"ń\"] = \"Ń\",\n\t[\"ņ\"] = \"Ņ\",\n\t[\"ň\"] = \"Ň\",\n\t[\"ŋ\"] = \"Ŋ\",\n\t[\"ō\"] = \"Ō\",\n\t[\"ŏ\"] = \"Ŏ\",\n\t[\"ő\"] = \"Ő\",\n\t[\"œ\"] = \"Œ\",\n\t[\"ŕ\"] = \"Ŕ\",\n\t[\"ŗ\"] = \"Ŗ\",\n\t[\"ř\"] = \"Ř\",\n\t[\"ś\"] = \"Ś\",\n\t[\"ŝ\"] = \"Ŝ\",\n\t[\"ş\"] = \"Ş\",\n\t[\"š\"] = \"Š\",\n\t[\"ţ\"] = \"Ţ\",\n\t[\"ť\"] = \"Ť\",\n\t[\"ŧ\"] = \"Ŧ\",\n\t[\"ũ\"] = \"Ũ\",\n\t[\"ū\"] = \"Ū\",\n\t[\"ŭ\"] = \"Ŭ\",\n\t[\"ů\"] = \"Ů\",\n\t[\"ű\"] = \"Ű\",\n\t[\"ų\"] = \"Ų\",\n\t[\"ŵ\"] = \"Ŵ\",\n\t[\"ŷ\"] = \"Ŷ\",\n\t[\"ź\"] = \"Ź\",\n\t[\"ż\"] = \"Ż\",\n\t[\"ž\"] = \"Ž\",\n\t[\"ſ\"] = \"S\",\n\t[\"ƀ\"] = \"Ƀ\",\n\t[\"ƃ\"] = \"Ƃ\",\n\t[\"ƅ\"] = \"Ƅ\",\n\t[\"ƈ\"] = \"Ƈ\",\n\t[\"ƌ\"] = \"Ƌ\",\n\t[\"ƒ\"] = \"Ƒ\",\n\t[\"ƕ\"] = \"Ƕ\",\n\t[\"ƙ\"] = \"Ƙ\",\n\t[\"ƚ\"] = \"Ƚ\",\n\t[\"ƞ\"] = \"Ƞ\",\n\t[\"ơ\"] = \"Ơ\",\n\t[\"ƣ\"] = \"Ƣ\",\n\t[\"ƥ\"] = \"Ƥ\",\n\t[\"ƨ\"] = \"Ƨ\",\n\t[\"ƭ\"] = \"Ƭ\",\n\t[\"ư\"] = \"Ư\",\n\t[\"ƴ\"] = \"Ƴ\",\n\t[\"ƶ\"] = \"Ƶ\",\n\t[\"ƹ\"] = \"Ƹ\",\n\t[\"ƽ\"] = \"Ƽ\",\n\t[\"ƿ\"] = \"Ƿ\",\n\t[\"ǅ\"] = \"Ǆ\",\n\t[\"ǆ\"] = \"Ǆ\",\n\t[\"ǈ\"] = \"Ǉ\",\n\t[\"ǉ\"] = \"Ǉ\",\n\t[\"ǋ\"] = \"Ǌ\",\n\t[\"ǌ\"] = \"Ǌ\",\n\t[\"ǎ\"] = \"Ǎ\",\n\t[\"ǐ\"] = \"Ǐ\",\n\t[\"ǒ\"] = \"Ǒ\",\n\t[\"ǔ\"] = \"Ǔ\",\n\t[\"ǖ\"] = \"Ǖ\",\n\t[\"ǘ\"] = \"Ǘ\",\n\t[\"ǚ\"] = \"Ǚ\",\n\t[\"ǜ\"] = \"Ǜ\",\n\t[\"ǝ\"] = \"Ǝ\",\n\t[\"ǟ\"] = \"Ǟ\",\n\t[\"ǡ\"] = \"Ǡ\",\n\t[\"ǣ\"] = \"Ǣ\",\n\t[\"ǥ\"] = \"Ǥ\",\n\t[\"ǧ\"] = \"Ǧ\",\n\t[\"ǩ\"] = \"Ǩ\",\n\t[\"ǫ\"] = \"Ǫ\",\n\t[\"ǭ\"] = \"Ǭ\",\n\t[\"ǯ\"] = \"Ǯ\",\n\t[\"ǲ\"] = \"Ǳ\",\n\t[\"ǳ\"] = \"Ǳ\",\n\t[\"ǵ\"] = \"Ǵ\",\n\t[\"ǹ\"] = \"Ǹ\",\n\t[\"ǻ\"] = \"Ǻ\",\n\t[\"ǽ\"] = \"Ǽ\",\n\t[\"ǿ\"] = \"Ǿ\",\n\t[\"ȁ\"] = \"Ȁ\",\n\t[\"ȃ\"] = \"Ȃ\",\n\t[\"ȅ\"] = \"Ȅ\",\n\t[\"ȇ\"] = \"Ȇ\",\n\t[\"ȉ\"] = \"Ȉ\",\n\t[\"ȋ\"] = \"Ȋ\",\n\t[\"ȍ\"] = \"Ȍ\",\n\t[\"ȏ\"] = \"Ȏ\",\n\t[\"ȑ\"] = \"Ȑ\",\n\t[\"ȓ\"] = \"Ȓ\",\n\t[\"ȕ\"] = \"Ȕ\",\n\t[\"ȗ\"] = \"Ȗ\",\n\t[\"ș\"] = \"Ș\",\n\t[\"ț\"] = \"Ț\",\n\t[\"ȝ\"] = \"Ȝ\",\n\t[\"ȟ\"] = \"Ȟ\",\n\t[\"ȣ\"] = \"Ȣ\",\n\t[\"ȥ\"] = \"Ȥ\",\n\t[\"ȧ\"] = \"Ȧ\",\n\t[\"ȩ\"] = \"Ȩ\",\n\t[\"ȫ\"] = \"Ȫ\",\n\t[\"ȭ\"] = \"Ȭ\",\n\t[\"ȯ\"] = \"Ȯ\",\n\t[\"ȱ\"] = \"Ȱ\",\n\t[\"ȳ\"] = \"Ȳ\",\n\t[\"ȼ\"] = \"Ȼ\",\n\t[\"ɂ\"] = \"Ɂ\",\n\t[\"ɇ\"] = \"Ɇ\",\n\t[\"ɉ\"] = \"Ɉ\",\n\t[\"ɋ\"] = \"Ɋ\",\n\t[\"ɍ\"] = \"Ɍ\",\n\t[\"ɏ\"] = \"Ɏ\",\n\t[\"ɓ\"] = \"Ɓ\",\n\t[\"ɔ\"] = \"Ɔ\",\n\t[\"ɖ\"] = \"Ɖ\",\n\t[\"ɗ\"] = \"Ɗ\",\n\t[\"ə\"] = \"Ə\",\n\t[\"ɛ\"] = \"Ɛ\",\n\t[\"ɠ\"] = \"Ɠ\",\n\t[\"ɣ\"] = \"Ɣ\",\n\t[\"ɨ\"] = \"Ɨ\",\n\t[\"ɩ\"] = \"Ɩ\",\n\t[\"ɫ\"] = \"Ɫ\",\n\t[\"ɯ\"] = \"Ɯ\",\n\t[\"ɲ\"] = \"Ɲ\",\n\t[\"ɵ\"] = \"Ɵ\",\n\t[\"ɽ\"] = \"Ɽ\",\n\t[\"ʀ\"] = \"Ʀ\",\n\t[\"ʃ\"] = \"Ʃ\",\n\t[\"ʈ\"] = \"Ʈ\",\n\t[\"ʉ\"] = \"Ʉ\",\n\t[\"ʊ\"] = \"Ʊ\",\n\t[\"ʋ\"] = \"Ʋ\",\n\t[\"ʌ\"] = \"Ʌ\",\n\t[\"ʒ\"] = \"Ʒ\",\n\t[\"ͅ\"] = \"Ι\",\n\t[\"ͻ\"] = \"Ͻ\",\n\t[\"ͼ\"] = \"Ͼ\",\n\t[\"ͽ\"] = \"Ͽ\",\n\t[\"ά\"] = \"Ά\",\n\t[\"έ\"] = \"Έ\",\n\t[\"ή\"] = \"Ή\",\n\t[\"ί\"] = \"Ί\",\n\t[\"α\"] = \"Α\",\n\t[\"β\"] = \"Β\",\n\t[\"γ\"] = \"Γ\",\n\t[\"δ\"] = \"Δ\",\n\t[\"ε\"] = \"Ε\",\n\t[\"ζ\"] = \"Ζ\",\n\t[\"η\"] = \"Η\",\n\t[\"θ\"] = \"Θ\",\n\t[\"ι\"] = \"Ι\",\n\t[\"κ\"] = \"Κ\",\n\t[\"λ\"] = \"Λ\",\n\t[\"μ\"] = \"Μ\",\n\t[\"ν\"] = \"Ν\",\n\t[\"ξ\"] = \"Ξ\",\n\t[\"ο\"] = \"Ο\",\n\t[\"π\"] = \"Π\",\n\t[\"ρ\"] = \"Ρ\",\n\t[\"ς\"] = \"Σ\",\n\t[\"σ\"] = \"Σ\",\n\t[\"τ\"] = \"Τ\",\n\t[\"υ\"] = \"Υ\",\n\t[\"φ\"] = \"Φ\",\n\t[\"χ\"] = \"Χ\",\n\t[\"ψ\"] = \"Ψ\",\n\t[\"ω\"] = \"Ω\",\n\t[\"ϊ\"] = \"Ϊ\",\n\t[\"ϋ\"] = \"Ϋ\",\n\t[\"ό\"] = \"Ό\",\n\t[\"ύ\"] = \"Ύ\",\n\t[\"ώ\"] = \"Ώ\",\n\t[\"ϐ\"] = \"Β\",\n\t[\"ϑ\"] = \"Θ\",\n\t[\"ϕ\"] = \"Φ\",\n\t[\"ϖ\"] = \"Π\",\n\t[\"ϙ\"] = \"Ϙ\",\n\t[\"ϛ\"] = \"Ϛ\",\n\t[\"ϝ\"] = \"Ϝ\",\n\t[\"ϟ\"] = \"Ϟ\",\n\t[\"ϡ\"] = \"Ϡ\",\n\t[\"ϣ\"] = \"Ϣ\",\n\t[\"ϥ\"] = \"Ϥ\",\n\t[\"ϧ\"] = \"Ϧ\",\n\t[\"ϩ\"] = \"Ϩ\",\n\t[\"ϫ\"] = \"Ϫ\",\n\t[\"ϭ\"] = \"Ϭ\",\n\t[\"ϯ\"] = \"Ϯ\",\n\t[\"ϰ\"] = \"Κ\",\n\t[\"ϱ\"] = \"Ρ\",\n\t[\"ϲ\"] = \"Ϲ\",\n\t[\"ϵ\"] = \"Ε\",\n\t[\"ϸ\"] = \"Ϸ\",\n\t[\"ϻ\"] = \"Ϻ\",\n\t[\"а\"] = \"А\",\n\t[\"б\"] = \"Б\",\n\t[\"в\"] = \"В\",\n\t[\"г\"] = \"Г\",\n\t[\"д\"] = \"Д\",\n\t[\"е\"] = \"Е\",\n\t[\"ж\"] = \"Ж\",\n\t[\"з\"] = \"З\",\n\t[\"и\"] = \"И\",\n\t[\"й\"] = \"Й\",\n\t[\"к\"] = \"К\",\n\t[\"л\"] = \"Л\",\n\t[\"м\"] = \"М\",\n\t[\"н\"] = \"Н\",\n\t[\"о\"] = \"О\",\n\t[\"п\"] = \"П\",\n\t[\"р\"] = \"Р\",\n\t[\"с\"] = \"С\",\n\t[\"т\"] = \"Т\",\n\t[\"у\"] = \"У\",\n\t[\"ф\"] = \"Ф\",\n\t[\"х\"] = \"Х\",\n\t[\"ц\"] = \"Ц\",\n\t[\"ч\"] = \"Ч\",\n\t[\"ш\"] = \"Ш\",\n\t[\"щ\"] = \"Щ\",\n\t[\"ъ\"] = \"Ъ\",\n\t[\"ы\"] = \"Ы\",\n\t[\"ь\"] = \"Ь\",\n\t[\"э\"] = \"Э\",\n\t[\"ю\"] = \"Ю\",\n\t[\"я\"] = \"Я\",\n\t[\"ѐ\"] = \"Ѐ\",\n\t[\"ё\"] = \"Ё\",\n\t[\"ђ\"] = \"Ђ\",\n\t[\"ѓ\"] = \"Ѓ\",\n\t[\"є\"] = \"Є\",\n\t[\"ѕ\"] = \"Ѕ\",\n\t[\"і\"] = \"І\",\n\t[\"ї\"] = \"Ї\",\n\t[\"ј\"] = \"Ј\",\n\t[\"љ\"] = \"Љ\",\n\t[\"њ\"] = \"Њ\",\n\t[\"ћ\"] = \"Ћ\",\n\t[\"ќ\"] = \"Ќ\",\n\t[\"ѝ\"] = \"Ѝ\",\n\t[\"ў\"] = \"Ў\",\n\t[\"џ\"] = \"Џ\",\n\t[\"ѡ\"] = \"Ѡ\",\n\t[\"ѣ\"] = \"Ѣ\",\n\t[\"ѥ\"] = \"Ѥ\",\n\t[\"ѧ\"] = \"Ѧ\",\n\t[\"ѩ\"] = \"Ѩ\",\n\t[\"ѫ\"] = \"Ѫ\",\n\t[\"ѭ\"] = \"Ѭ\",\n\t[\"ѯ\"] = \"Ѯ\",\n\t[\"ѱ\"] = \"Ѱ\",\n\t[\"ѳ\"] = \"Ѳ\",\n\t[\"ѵ\"] = \"Ѵ\",\n\t[\"ѷ\"] = \"Ѷ\",\n\t[\"ѹ\"] = \"Ѹ\",\n\t[\"ѻ\"] = \"Ѻ\",\n\t[\"ѽ\"] = \"Ѽ\",\n\t[\"ѿ\"] = \"Ѿ\",\n\t[\"ҁ\"] = \"Ҁ\",\n\t[\"ҋ\"] = \"Ҋ\",\n\t[\"ҍ\"] = \"Ҍ\",\n\t[\"ҏ\"] = \"Ҏ\",\n\t[\"ґ\"] = \"Ґ\",\n\t[\"ғ\"] = \"Ғ\",\n\t[\"ҕ\"] = \"Ҕ\",\n\t[\"җ\"] = \"Җ\",\n\t[\"ҙ\"] = \"Ҙ\",\n\t[\"қ\"] = \"Қ\",\n\t[\"ҝ\"] = \"Ҝ\",\n\t[\"ҟ\"] = \"Ҟ\",\n\t[\"ҡ\"] = \"Ҡ\",\n\t[\"ң\"] = \"Ң\",\n\t[\"ҥ\"] = \"Ҥ\",\n\t[\"ҧ\"] = \"Ҧ\",\n\t[\"ҩ\"] = \"Ҩ\",\n\t[\"ҫ\"] = \"Ҫ\",\n\t[\"ҭ\"] = \"Ҭ\",\n\t[\"ү\"] = \"Ү\",\n\t[\"ұ\"] = \"Ұ\",\n\t[\"ҳ\"] = \"Ҳ\",\n\t[\"ҵ\"] = \"Ҵ\",\n\t[\"ҷ\"] = \"Ҷ\",\n\t[\"ҹ\"] = \"Ҹ\",\n\t[\"һ\"] = \"Һ\",\n\t[\"ҽ\"] = \"Ҽ\",\n\t[\"ҿ\"] = \"Ҿ\",\n\t[\"ӂ\"] = \"Ӂ\",\n\t[\"ӄ\"] = \"Ӄ\",\n\t[\"ӆ\"] = \"Ӆ\",\n\t[\"ӈ\"] = \"Ӈ\",\n\t[\"ӊ\"] = \"Ӊ\",\n\t[\"ӌ\"] = \"Ӌ\",\n\t[\"ӎ\"] = \"Ӎ\",\n\t[\"ӏ\"] = \"Ӏ\",\n\t[\"ӑ\"] = \"Ӑ\",\n\t[\"ӓ\"] = \"Ӓ\",\n\t[\"ӕ\"] = \"Ӕ\",\n\t[\"ӗ\"] = \"Ӗ\",\n\t[\"ә\"] = \"Ә\",\n\t[\"ӛ\"] = \"Ӛ\",\n\t[\"ӝ\"] = \"Ӝ\",\n\t[\"ӟ\"] = \"Ӟ\",\n\t[\"ӡ\"] = \"Ӡ\",\n\t[\"ӣ\"] = \"Ӣ\",\n\t[\"ӥ\"] = \"Ӥ\",\n\t[\"ӧ\"] = \"Ӧ\",\n\t[\"ө\"] = \"Ө\",\n\t[\"ӫ\"] = \"Ӫ\",\n\t[\"ӭ\"] = \"Ӭ\",\n\t[\"ӯ\"] = \"Ӯ\",\n\t[\"ӱ\"] = \"Ӱ\",\n\t[\"ӳ\"] = \"Ӳ\",\n\t[\"ӵ\"] = \"Ӵ\",\n\t[\"ӷ\"] = \"Ӷ\",\n\t[\"ӹ\"] = \"Ӹ\",\n\t[\"ӻ\"] = \"Ӻ\",\n\t[\"ӽ\"] = \"Ӽ\",\n\t[\"ӿ\"] = \"Ӿ\",\n\t[\"ԁ\"] = \"Ԁ\",\n\t[\"ԃ\"] = \"Ԃ\",\n\t[\"ԅ\"] = \"Ԅ\",\n\t[\"ԇ\"] = \"Ԇ\",\n\t[\"ԉ\"] = \"Ԉ\",\n\t[\"ԋ\"] = \"Ԋ\",\n\t[\"ԍ\"] = \"Ԍ\",\n\t[\"ԏ\"] = \"Ԏ\",\n\t[\"ԑ\"] = \"Ԑ\",\n\t[\"ԓ\"] = \"Ԓ\",\n\t[\"ա\"] = \"Ա\",\n\t[\"բ\"] = \"Բ\",\n\t[\"գ\"] = \"Գ\",\n\t[\"դ\"] = \"Դ\",\n\t[\"ե\"] = \"Ե\",\n\t[\"զ\"] = \"Զ\",\n\t[\"է\"] = \"Է\",\n\t[\"ը\"] = \"Ը\",\n\t[\"թ\"] = \"Թ\",\n\t[\"ժ\"] = \"Ժ\",\n\t[\"ի\"] = \"Ի\",\n\t[\"լ\"] = \"Լ\",\n\t[\"խ\"] = \"Խ\",\n\t[\"ծ\"] = \"Ծ\",\n\t[\"կ\"] = \"Կ\",\n\t[\"հ\"] = \"Հ\",\n\t[\"ձ\"] = \"Ձ\",\n\t[\"ղ\"] = \"Ղ\",\n\t[\"ճ\"] = \"Ճ\",\n\t[\"մ\"] = \"Մ\",\n\t[\"յ\"] = \"Յ\",\n\t[\"ն\"] = \"Ն\",\n\t[\"շ\"] = \"Շ\",\n\t[\"ո\"] = \"Ո\",\n\t[\"չ\"] = \"Չ\",\n\t[\"պ\"] = \"Պ\",\n\t[\"ջ\"] = \"Ջ\",\n\t[\"ռ\"] = \"Ռ\",\n\t[\"ս\"] = \"Ս\",\n\t[\"վ\"] = \"Վ\",\n\t[\"տ\"] = \"Տ\",\n\t[\"ր\"] = \"Ր\",\n\t[\"ց\"] = \"Ց\",\n\t[\"ւ\"] = \"Ւ\",\n\t[\"փ\"] = \"Փ\",\n\t[\"ք\"] = \"Ք\",\n\t[\"օ\"] = \"Օ\",\n\t[\"ֆ\"] = \"Ֆ\",\n\t[\"ᵽ\"] = \"Ᵽ\",\n\t[\"ḁ\"] = \"Ḁ\",\n\t[\"ḃ\"] = \"Ḃ\",\n\t[\"ḅ\"] = \"Ḅ\",\n\t[\"ḇ\"] = \"Ḇ\",\n\t[\"ḉ\"] = \"Ḉ\",\n\t[\"ḋ\"] = \"Ḋ\",\n\t[\"ḍ\"] = \"Ḍ\",\n\t[\"ḏ\"] = \"Ḏ\",\n\t[\"ḑ\"] = \"Ḑ\",\n\t[\"ḓ\"] = \"Ḓ\",\n\t[\"ḕ\"] = \"Ḕ\",\n\t[\"ḗ\"] = \"Ḗ\",\n\t[\"ḙ\"] = \"Ḙ\",\n\t[\"ḛ\"] = \"Ḛ\",\n\t[\"ḝ\"] = \"Ḝ\",\n\t[\"ḟ\"] = \"Ḟ\",\n\t[\"ḡ\"] = \"Ḡ\",\n\t[\"ḣ\"] = \"Ḣ\",\n\t[\"ḥ\"] = \"Ḥ\",\n\t[\"ḧ\"] = \"Ḧ\",\n\t[\"ḩ\"] = \"Ḩ\",\n\t[\"ḫ\"] = \"Ḫ\",\n\t[\"ḭ\"] = \"Ḭ\",\n\t[\"ḯ\"] = \"Ḯ\",\n\t[\"ḱ\"] = \"Ḱ\",\n\t[\"ḳ\"] = \"Ḳ\",\n\t[\"ḵ\"] = \"Ḵ\",\n\t[\"ḷ\"] = \"Ḷ\",\n\t[\"ḹ\"] = \"Ḹ\",\n\t[\"ḻ\"] = \"Ḻ\",\n\t[\"ḽ\"] = \"Ḽ\",\n\t[\"ḿ\"] = \"Ḿ\",\n\t[\"ṁ\"] = \"Ṁ\",\n\t[\"ṃ\"] = \"Ṃ\",\n\t[\"ṅ\"] = \"Ṅ\",\n\t[\"ṇ\"] = \"Ṇ\",\n\t[\"ṉ\"] = \"Ṉ\",\n\t[\"ṋ\"] = \"Ṋ\",\n\t[\"ṍ\"] = \"Ṍ\",\n\t[\"ṏ\"] = \"Ṏ\",\n\t[\"ṑ\"] = \"Ṑ\",\n\t[\"ṓ\"] = \"Ṓ\",\n\t[\"ṕ\"] = \"Ṕ\",\n\t[\"ṗ\"] = \"Ṗ\",\n\t[\"ṙ\"] = \"Ṙ\",\n\t[\"ṛ\"] = \"Ṛ\",\n\t[\"ṝ\"] = \"Ṝ\",\n\t[\"ṟ\"] = \"Ṟ\",\n\t[\"ṡ\"] = \"Ṡ\",\n\t[\"ṣ\"] = \"Ṣ\",\n\t[\"ṥ\"] = \"Ṥ\",\n\t[\"ṧ\"] = \"Ṧ\",\n\t[\"ṩ\"] = \"Ṩ\",\n\t[\"ṫ\"] = \"Ṫ\",\n\t[\"ṭ\"] = \"Ṭ\",\n\t[\"ṯ\"] = \"Ṯ\",\n\t[\"ṱ\"] = \"Ṱ\",\n\t[\"ṳ\"] = \"Ṳ\",\n\t[\"ṵ\"] = \"Ṵ\",\n\t[\"ṷ\"] = \"Ṷ\",\n\t[\"ṹ\"] = \"Ṹ\",\n\t[\"ṻ\"] = \"Ṻ\",\n\t[\"ṽ\"] = \"Ṽ\",\n\t[\"ṿ\"] = \"Ṿ\",\n\t[\"ẁ\"] = \"Ẁ\",\n\t[\"ẃ\"] = \"Ẃ\",\n\t[\"ẅ\"] = \"Ẅ\",\n\t[\"ẇ\"] = \"Ẇ\",\n\t[\"ẉ\"] = \"Ẉ\",\n\t[\"ẋ\"] = \"Ẋ\",\n\t[\"ẍ\"] = \"Ẍ\",\n\t[\"ẏ\"] = \"Ẏ\",\n\t[\"ẑ\"] = \"Ẑ\",\n\t[\"ẓ\"] = \"Ẓ\",\n\t[\"ẕ\"] = \"Ẕ\",\n\t[\"ẛ\"] = \"Ṡ\",\n\t[\"ạ\"] = \"Ạ\",\n\t[\"ả\"] = \"Ả\",\n\t[\"ấ\"] = \"Ấ\",\n\t[\"ầ\"] = \"Ầ\",\n\t[\"ẩ\"] = \"Ẩ\",\n\t[\"ẫ\"] = \"Ẫ\",\n\t[\"ậ\"] = \"Ậ\",\n\t[\"ắ\"] = \"Ắ\",\n\t[\"ằ\"] = \"Ằ\",\n\t[\"ẳ\"] = \"Ẳ\",\n\t[\"ẵ\"] = \"Ẵ\",\n\t[\"ặ\"] = \"Ặ\",\n\t[\"ẹ\"] = \"Ẹ\",\n\t[\"ẻ\"] = \"Ẻ\",\n\t[\"ẽ\"] = \"Ẽ\",\n\t[\"ế\"] = \"Ế\",\n\t[\"ề\"] = \"Ề\",\n\t[\"ể\"] = \"Ể\",\n\t[\"ễ\"] = \"Ễ\",\n\t[\"ệ\"] = \"Ệ\",\n\t[\"ỉ\"] = \"Ỉ\",\n\t[\"ị\"] = \"Ị\",\n\t[\"ọ\"] = \"Ọ\",\n\t[\"ỏ\"] = \"Ỏ\",\n\t[\"ố\"] = \"Ố\",\n\t[\"ồ\"] = \"Ồ\",\n\t[\"ổ\"] = \"Ổ\",\n\t[\"ỗ\"] = \"Ỗ\",\n\t[\"ộ\"] = \"Ộ\",\n\t[\"ớ\"] = \"Ớ\",\n\t[\"ờ\"] = \"Ờ\",\n\t[\"ở\"] = \"Ở\",\n\t[\"ỡ\"] = \"Ỡ\",\n\t[\"ợ\"] = \"Ợ\",\n\t[\"ụ\"] = \"Ụ\",\n\t[\"ủ\"] = \"Ủ\",\n\t[\"ứ\"] = \"Ứ\",\n\t[\"ừ\"] = \"Ừ\",\n\t[\"ử\"] = \"Ử\",\n\t[\"ữ\"] = \"Ữ\",\n\t[\"ự\"] = \"Ự\",\n\t[\"ỳ\"] = \"Ỳ\",\n\t[\"ỵ\"] = \"Ỵ\",\n\t[\"ỷ\"] = \"Ỷ\",\n\t[\"ỹ\"] = \"Ỹ\",\n\t[\"ἀ\"] = \"Ἀ\",\n\t[\"ἁ\"] = \"Ἁ\",\n\t[\"ἂ\"] = \"Ἂ\",\n\t[\"ἃ\"] = \"Ἃ\",\n\t[\"ἄ\"] = \"Ἄ\",\n\t[\"ἅ\"] = \"Ἅ\",\n\t[\"ἆ\"] = \"Ἆ\",\n\t[\"ἇ\"] = \"Ἇ\",\n\t[\"ἐ\"] = \"Ἐ\",\n\t[\"ἑ\"] = \"Ἑ\",\n\t[\"ἒ\"] = \"Ἒ\",\n\t[\"ἓ\"] = \"Ἓ\",\n\t[\"ἔ\"] = \"Ἔ\",\n\t[\"ἕ\"] = \"Ἕ\",\n\t[\"ἠ\"] = \"Ἠ\",\n\t[\"ἡ\"] = \"Ἡ\",\n\t[\"ἢ\"] = \"Ἢ\",\n\t[\"ἣ\"] = \"Ἣ\",\n\t[\"ἤ\"] = \"Ἤ\",\n\t[\"ἥ\"] = \"Ἥ\",\n\t[\"ἦ\"] = \"Ἦ\",\n\t[\"ἧ\"] = \"Ἧ\",\n\t[\"ἰ\"] = \"Ἰ\",\n\t[\"ἱ\"] = \"Ἱ\",\n\t[\"ἲ\"] = \"Ἲ\",\n\t[\"ἳ\"] = \"Ἳ\",\n\t[\"ἴ\"] = \"Ἴ\",\n\t[\"ἵ\"] = \"Ἵ\",\n\t[\"ἶ\"] = \"Ἶ\",\n\t[\"ἷ\"] = \"Ἷ\",\n\t[\"ὀ\"] = \"Ὀ\",\n\t[\"ὁ\"] = \"Ὁ\",\n\t[\"ὂ\"] = \"Ὂ\",\n\t[\"ὃ\"] = \"Ὃ\",\n\t[\"ὄ\"] = \"Ὄ\",\n\t[\"ὅ\"] = \"Ὅ\",\n\t[\"ὑ\"] = \"Ὑ\",\n\t[\"ὓ\"] = \"Ὓ\",\n\t[\"ὕ\"] = \"Ὕ\",\n\t[\"ὗ\"] = \"Ὗ\",\n\t[\"ὠ\"] = \"Ὠ\",\n\t[\"ὡ\"] = \"Ὡ\",\n\t[\"ὢ\"] = \"Ὢ\",\n\t[\"ὣ\"] = \"Ὣ\",\n\t[\"ὤ\"] = \"Ὤ\",\n\t[\"ὥ\"] = \"Ὥ\",\n\t[\"ὦ\"] = \"Ὦ\",\n\t[\"ὧ\"] = \"Ὧ\",\n\t[\"ὰ\"] = \"Ὰ\",\n\t[\"ά\"] = \"Ά\",\n\t[\"ὲ\"] = \"Ὲ\",\n\t[\"έ\"] = \"Έ\",\n\t[\"ὴ\"] = \"Ὴ\",\n\t[\"ή\"] = \"Ή\",\n\t[\"ὶ\"] = \"Ὶ\",\n\t[\"ί\"] = \"Ί\",\n\t[\"ὸ\"] = \"Ὸ\",\n\t[\"ό\"] = \"Ό\",\n\t[\"ὺ\"] = \"Ὺ\",\n\t[\"ύ\"] = \"Ύ\",\n\t[\"ὼ\"] = \"Ὼ\",\n\t[\"ώ\"] = \"Ώ\",\n\t[\"ᾀ\"] = \"ᾈ\",\n\t[\"ᾁ\"] = \"ᾉ\",\n\t[\"ᾂ\"] = \"ᾊ\",\n\t[\"ᾃ\"] = \"ᾋ\",\n\t[\"ᾄ\"] = \"ᾌ\",\n\t[\"ᾅ\"] = \"ᾍ\",\n\t[\"ᾆ\"] = \"ᾎ\",\n\t[\"ᾇ\"] = \"ᾏ\",\n\t[\"ᾐ\"] = \"ᾘ\",\n\t[\"ᾑ\"] = \"ᾙ\",\n\t[\"ᾒ\"] = \"ᾚ\",\n\t[\"ᾓ\"] = \"ᾛ\",\n\t[\"ᾔ\"] = \"ᾜ\",\n\t[\"ᾕ\"] = \"ᾝ\",\n\t[\"ᾖ\"] = \"ᾞ\",\n\t[\"ᾗ\"] = \"ᾟ\",\n\t[\"ᾠ\"] = \"ᾨ\",\n\t[\"ᾡ\"] = \"ᾩ\",\n\t[\"ᾢ\"] = \"ᾪ\",\n\t[\"ᾣ\"] = \"ᾫ\",\n\t[\"ᾤ\"] = \"ᾬ\",\n\t[\"ᾥ\"] = \"ᾭ\",\n\t[\"ᾦ\"] = \"ᾮ\",\n\t[\"ᾧ\"] = \"ᾯ\",\n\t[\"ᾰ\"] = \"Ᾰ\",\n\t[\"ᾱ\"] = \"Ᾱ\",\n\t[\"ᾳ\"] = \"ᾼ\",\n\t[\"ι\"] = \"Ι\",\n\t[\"ῃ\"] = \"ῌ\",\n\t[\"ῐ\"] = \"Ῐ\",\n\t[\"ῑ\"] = \"Ῑ\",\n\t[\"ῠ\"] = \"Ῠ\",\n\t[\"ῡ\"] = \"Ῡ\",\n\t[\"ῥ\"] = \"Ῥ\",\n\t[\"ῳ\"] = \"ῼ\",\n\t[\"ⅎ\"] = \"Ⅎ\",\n\t[\"ⅰ\"] = \"Ⅰ\",\n\t[\"ⅱ\"] = \"Ⅱ\",\n\t[\"ⅲ\"] = \"Ⅲ\",\n\t[\"ⅳ\"] = \"Ⅳ\",\n\t[\"ⅴ\"] = \"Ⅴ\",\n\t[\"ⅵ\"] = \"Ⅵ\",\n\t[\"ⅶ\"] = \"Ⅶ\",\n\t[\"ⅷ\"] = \"Ⅷ\",\n\t[\"ⅸ\"] = \"Ⅸ\",\n\t[\"ⅹ\"] = \"Ⅹ\",\n\t[\"ⅺ\"] = \"Ⅺ\",\n\t[\"ⅻ\"] = \"Ⅻ\",\n\t[\"ⅼ\"] = \"Ⅼ\",\n\t[\"ⅽ\"] = \"Ⅽ\",\n\t[\"ⅾ\"] = \"Ⅾ\",\n\t[\"ⅿ\"] = \"Ⅿ\",\n\t[\"ↄ\"] = \"Ↄ\",\n\t[\"ⓐ\"] = \"Ⓐ\",\n\t[\"ⓑ\"] = \"Ⓑ\",\n\t[\"ⓒ\"] = \"Ⓒ\",\n\t[\"ⓓ\"] = \"Ⓓ\",\n\t[\"ⓔ\"] = \"Ⓔ\",\n\t[\"ⓕ\"] = \"Ⓕ\",\n\t[\"ⓖ\"] = \"Ⓖ\",\n\t[\"ⓗ\"] = \"Ⓗ\",\n\t[\"ⓘ\"] = \"Ⓘ\",\n\t[\"ⓙ\"] = \"Ⓙ\",\n\t[\"ⓚ\"] = \"Ⓚ\",\n\t[\"ⓛ\"] = \"Ⓛ\",\n\t[\"ⓜ\"] = \"Ⓜ\",\n\t[\"ⓝ\"] = \"Ⓝ\",\n\t[\"ⓞ\"] = \"Ⓞ\",\n\t[\"ⓟ\"] = \"Ⓟ\",\n\t[\"ⓠ\"] = \"Ⓠ\",\n\t[\"ⓡ\"] = \"Ⓡ\",\n\t[\"ⓢ\"] = \"Ⓢ\",\n\t[\"ⓣ\"] = \"Ⓣ\",\n\t[\"ⓤ\"] = \"Ⓤ\",\n\t[\"ⓥ\"] = \"Ⓥ\",\n\t[\"ⓦ\"] = \"Ⓦ\",\n\t[\"ⓧ\"] = \"Ⓧ\",\n\t[\"ⓨ\"] = \"Ⓨ\",\n\t[\"ⓩ\"] = \"Ⓩ\",\n\t[\"ⰰ\"] = \"Ⰰ\",\n\t[\"ⰱ\"] = \"Ⰱ\",\n\t[\"ⰲ\"] = \"Ⰲ\",\n\t[\"ⰳ\"] = \"Ⰳ\",\n\t[\"ⰴ\"] = \"Ⰴ\",\n\t[\"ⰵ\"] = \"Ⰵ\",\n\t[\"ⰶ\"] = \"Ⰶ\",\n\t[\"ⰷ\"] = \"Ⰷ\",\n\t[\"ⰸ\"] = \"Ⰸ\",\n\t[\"ⰹ\"] = \"Ⰹ\",\n\t[\"ⰺ\"] = \"Ⰺ\",\n\t[\"ⰻ\"] = \"Ⰻ\",\n\t[\"ⰼ\"] = \"Ⰼ\",\n\t[\"ⰽ\"] = \"Ⰽ\",\n\t[\"ⰾ\"] = \"Ⰾ\",\n\t[\"ⰿ\"] = \"Ⰿ\",\n\t[\"ⱀ\"] = \"Ⱀ\",\n\t[\"ⱁ\"] = \"Ⱁ\",\n\t[\"ⱂ\"] = \"Ⱂ\",\n\t[\"ⱃ\"] = \"Ⱃ\",\n\t[\"ⱄ\"] = \"Ⱄ\",\n\t[\"ⱅ\"] = \"Ⱅ\",\n\t[\"ⱆ\"] = \"Ⱆ\",\n\t[\"ⱇ\"] = \"Ⱇ\",\n\t[\"ⱈ\"] = \"Ⱈ\",\n\t[\"ⱉ\"] = \"Ⱉ\",\n\t[\"ⱊ\"] = \"Ⱊ\",\n\t[\"ⱋ\"] = \"Ⱋ\",\n\t[\"ⱌ\"] = \"Ⱌ\",\n\t[\"ⱍ\"] = \"Ⱍ\",\n\t[\"ⱎ\"] = \"Ⱎ\",\n\t[\"ⱏ\"] = \"Ⱏ\",\n\t[\"ⱐ\"] = \"Ⱐ\",\n\t[\"ⱑ\"] = \"Ⱑ\",\n\t[\"ⱒ\"] = \"Ⱒ\",\n\t[\"ⱓ\"] = \"Ⱓ\",\n\t[\"ⱔ\"] = \"Ⱔ\",\n\t[\"ⱕ\"] = \"Ⱕ\",\n\t[\"ⱖ\"] = \"Ⱖ\",\n\t[\"ⱗ\"] = \"Ⱗ\",\n\t[\"ⱘ\"] = \"Ⱘ\",\n\t[\"ⱙ\"] = \"Ⱙ\",\n\t[\"ⱚ\"] = \"Ⱚ\",\n\t[\"ⱛ\"] = \"Ⱛ\",\n\t[\"ⱜ\"] = \"Ⱜ\",\n\t[\"ⱝ\"] = \"Ⱝ\",\n\t[\"ⱞ\"] = \"Ⱞ\",\n\t[\"ⱡ\"] = \"Ⱡ\",\n\t[\"ⱥ\"] = \"Ⱥ\",\n\t[\"ⱦ\"] = \"Ⱦ\",\n\t[\"ⱨ\"] = \"Ⱨ\",\n\t[\"ⱪ\"] = \"Ⱪ\",\n\t[\"ⱬ\"] = \"Ⱬ\",\n\t[\"ⱶ\"] = \"Ⱶ\",\n\t[\"ⲁ\"] = \"Ⲁ\",\n\t[\"ⲃ\"] = \"Ⲃ\",\n\t[\"ⲅ\"] = \"Ⲅ\",\n\t[\"ⲇ\"] = \"Ⲇ\",\n\t[\"ⲉ\"] = \"Ⲉ\",\n\t[\"ⲋ\"] = \"Ⲋ\",\n\t[\"ⲍ\"] = \"Ⲍ\",\n\t[\"ⲏ\"] = \"Ⲏ\",\n\t[\"ⲑ\"] = \"Ⲑ\",\n\t[\"ⲓ\"] = \"Ⲓ\",\n\t[\"ⲕ\"] = \"Ⲕ\",\n\t[\"ⲗ\"] = \"Ⲗ\",\n\t[\"ⲙ\"] = \"Ⲙ\",\n\t[\"ⲛ\"] = \"Ⲛ\",\n\t[\"ⲝ\"] = \"Ⲝ\",\n\t[\"ⲟ\"] = \"Ⲟ\",\n\t[\"ⲡ\"] = \"Ⲡ\",\n\t[\"ⲣ\"] = \"Ⲣ\",\n\t[\"ⲥ\"] = \"Ⲥ\",\n\t[\"ⲧ\"] = \"Ⲧ\",\n\t[\"ⲩ\"] = \"Ⲩ\",\n\t[\"ⲫ\"] = \"Ⲫ\",\n\t[\"ⲭ\"] = \"Ⲭ\",\n\t[\"ⲯ\"] = \"Ⲯ\",\n\t[\"ⲱ\"] = \"Ⲱ\",\n\t[\"ⲳ\"] = \"Ⲳ\",\n\t[\"ⲵ\"] = \"Ⲵ\",\n\t[\"ⲷ\"] = \"Ⲷ\",\n\t[\"ⲹ\"] = \"Ⲹ\",\n\t[\"ⲻ\"] = \"Ⲻ\",\n\t[\"ⲽ\"] = \"Ⲽ\",\n\t[\"ⲿ\"] = \"Ⲿ\",\n\t[\"ⳁ\"] = \"Ⳁ\",\n\t[\"ⳃ\"] = \"Ⳃ\",\n\t[\"ⳅ\"] = \"Ⳅ\",\n\t[\"ⳇ\"] = \"Ⳇ\",\n\t[\"ⳉ\"] = \"Ⳉ\",\n\t[\"ⳋ\"] = \"Ⳋ\",\n\t[\"ⳍ\"] = \"Ⳍ\",\n\t[\"ⳏ\"] = \"Ⳏ\",\n\t[\"ⳑ\"] = \"Ⳑ\",\n\t[\"ⳓ\"] = \"Ⳓ\",\n\t[\"ⳕ\"] = \"Ⳕ\",\n\t[\"ⳗ\"] = \"Ⳗ\",\n\t[\"ⳙ\"] = \"Ⳙ\",\n\t[\"ⳛ\"] = \"Ⳛ\",\n\t[\"ⳝ\"] = \"Ⳝ\",\n\t[\"ⳟ\"] = \"Ⳟ\",\n\t[\"ⳡ\"] = \"Ⳡ\",\n\t[\"ⳣ\"] = \"Ⳣ\",\n\t[\"ⴀ\"] = \"Ⴀ\",\n\t[\"ⴁ\"] = \"Ⴁ\",\n\t[\"ⴂ\"] = \"Ⴂ\",\n\t[\"ⴃ\"] = \"Ⴃ\",\n\t[\"ⴄ\"] = \"Ⴄ\",\n\t[\"ⴅ\"] = \"Ⴅ\",\n\t[\"ⴆ\"] = \"Ⴆ\",\n\t[\"ⴇ\"] = \"Ⴇ\",\n\t[\"ⴈ\"] = \"Ⴈ\",\n\t[\"ⴉ\"] = \"Ⴉ\",\n\t[\"ⴊ\"] = \"Ⴊ\",\n\t[\"ⴋ\"] = \"Ⴋ\",\n\t[\"ⴌ\"] = \"Ⴌ\",\n\t[\"ⴍ\"] = \"Ⴍ\",\n\t[\"ⴎ\"] = \"Ⴎ\",\n\t[\"ⴏ\"] = \"Ⴏ\",\n\t[\"ⴐ\"] = \"Ⴐ\",\n\t[\"ⴑ\"] = \"Ⴑ\",\n\t[\"ⴒ\"] = \"Ⴒ\",\n\t[\"ⴓ\"] = \"Ⴓ\",\n\t[\"ⴔ\"] = \"Ⴔ\",\n\t[\"ⴕ\"] = \"Ⴕ\",\n\t[\"ⴖ\"] = \"Ⴖ\",\n\t[\"ⴗ\"] = \"Ⴗ\",\n\t[\"ⴘ\"] = \"Ⴘ\",\n\t[\"ⴙ\"] = \"Ⴙ\",\n\t[\"ⴚ\"] = \"Ⴚ\",\n\t[\"ⴛ\"] = \"Ⴛ\",\n\t[\"ⴜ\"] = \"Ⴜ\",\n\t[\"ⴝ\"] = \"Ⴝ\",\n\t[\"ⴞ\"] = \"Ⴞ\",\n\t[\"ⴟ\"] = \"Ⴟ\",\n\t[\"ⴠ\"] = \"Ⴠ\",\n\t[\"ⴡ\"] = \"Ⴡ\",\n\t[\"ⴢ\"] = \"Ⴢ\",\n\t[\"ⴣ\"] = \"Ⴣ\",\n\t[\"ⴤ\"] = \"Ⴤ\",\n\t[\"ⴥ\"] = \"Ⴥ\",\n\t[\"ａ\"] = \"Ａ\",\n\t[\"ｂ\"] = \"Ｂ\",\n\t[\"ｃ\"] = \"Ｃ\",\n\t[\"ｄ\"] = \"Ｄ\",\n\t[\"ｅ\"] = \"Ｅ\",\n\t[\"ｆ\"] = \"Ｆ\",\n\t[\"ｇ\"] = \"Ｇ\",\n\t[\"ｈ\"] = \"Ｈ\",\n\t[\"ｉ\"] = \"Ｉ\",\n\t[\"ｊ\"] = \"Ｊ\",\n\t[\"ｋ\"] = \"Ｋ\",\n\t[\"ｌ\"] = \"Ｌ\",\n\t[\"ｍ\"] = \"Ｍ\",\n\t[\"ｎ\"] = \"Ｎ\",\n\t[\"ｏ\"] = \"Ｏ\",\n\t[\"ｐ\"] = \"Ｐ\",\n\t[\"ｑ\"] = \"Ｑ\",\n\t[\"ｒ\"] = \"Ｒ\",\n\t[\"ｓ\"] = \"Ｓ\",\n\t[\"ｔ\"] = \"Ｔ\",\n\t[\"ｕ\"] = \"Ｕ\",\n\t[\"ｖ\"] = \"Ｖ\",\n\t[\"ｗ\"] = \"Ｗ\",\n\t[\"ｘ\"] = \"Ｘ\",\n\t[\"ｙ\"] = \"Ｙ\",\n\t[\"ｚ\"] = \"Ｚ\",\n\t[\"𐐨\"] = \"𐐀\",\n\t[\"𐐩\"] = \"𐐁\",\n\t[\"𐐪\"] = \"𐐂\",\n\t[\"𐐫\"] = \"𐐃\",\n\t[\"𐐬\"] = \"𐐄\",\n\t[\"𐐭\"] = \"𐐅\",\n\t[\"𐐮\"] = \"𐐆\",\n\t[\"𐐯\"] = \"𐐇\",\n\t[\"𐐰\"] = \"𐐈\",\n\t[\"𐐱\"] = \"𐐉\",\n\t[\"𐐲\"] = \"𐐊\",\n\t[\"𐐳\"] = \"𐐋\",\n\t[\"𐐴\"] = \"𐐌\",\n\t[\"𐐵\"] = \"𐐍\",\n\t[\"𐐶\"] = \"𐐎\",\n\t[\"𐐷\"] = \"𐐏\",\n\t[\"𐐸\"] = \"𐐐\",\n\t[\"𐐹\"] = \"𐐑\",\n\t[\"𐐺\"] = \"𐐒\",\n\t[\"𐐻\"] = \"𐐓\",\n\t[\"𐐼\"] = \"𐐔\",\n\t[\"𐐽\"] = \"𐐕\",\n\t[\"𐐾\"] = \"𐐖\",\n\t[\"𐐿\"] = \"𐐗\",\n\t[\"𐑀\"] = \"𐐘\",\n\t[\"𐑁\"] = \"𐐙\",\n\t[\"𐑂\"] = \"𐐚\",\n\t[\"𐑃\"] = \"𐐛\",\n\t[\"𐑄\"] = \"𐐜\",\n\t[\"𐑅\"] = \"𐐝\",\n\t[\"𐑆\"] = \"𐐞\",\n\t[\"𐑇\"] = \"𐐟\",\n\t[\"𐑈\"] = \"𐐠\",\n\t[\"𐑉\"] = \"𐐡\",\n\t[\"𐑊\"] = \"𐐢\",\n\t[\"𐑋\"] = \"𐐣\",\n\t[\"𐑌\"] = \"𐐤\",\n\t[\"𐑍\"] = \"𐐥\",\n\t[\"𐑎\"] = \"𐐦\",\n\t[\"𐑏\"] = \"𐐧\",\n}\n\n\nutf8_uc_lc = {\n\t[\"A\"] = \"a\",\n\t[\"B\"] = \"b\",\n\t[\"C\"] = \"c\",\n\t[\"D\"] = \"d\",\n\t[\"E\"] = \"e\",\n\t[\"F\"] = \"f\",\n\t[\"G\"] = \"g\",\n\t[\"H\"] = \"h\",\n\t[\"I\"] = \"i\",\n\t[\"J\"] = \"j\",\n\t[\"K\"] = \"k\",\n\t[\"L\"] = \"l\",\n\t[\"M\"] = \"m\",\n\t[\"N\"] = \"n\",\n\t[\"O\"] = \"o\",\n\t[\"P\"] = \"p\",\n\t[\"Q\"] = \"q\",\n\t[\"R\"] = \"r\",\n\t[\"S\"] = \"s\",\n\t[\"T\"] = \"t\",\n\t[\"U\"] = \"u\",\n\t[\"V\"] = \"v\",\n\t[\"W\"] = \"w\",\n\t[\"X\"] = \"x\",\n\t[\"Y\"] = \"y\",\n\t[\"Z\"] = \"z\",\n\t[\"À\"] = \"à\",\n\t[\"Á\"] = \"á\",\n\t[\"Â\"] = \"â\",\n\t[\"Ã\"] = \"ã\",\n\t[\"Ä\"] = \"ä\",\n\t[\"Å\"] = \"å\",\n\t[\"Æ\"] = \"æ\",\n\t[\"Ç\"] = \"ç\",\n\t[\"È\"] = \"è\",\n\t[\"É\"] = \"é\",\n\t[\"Ê\"] = \"ê\",\n\t[\"Ë\"] = \"ë\",\n\t[\"Ì\"] = \"ì\",\n\t[\"Í\"] = \"í\",\n\t[\"Î\"] = \"î\",\n\t[\"Ï\"] = \"ï\",\n\t[\"Ð\"] = \"ð\",\n\t[\"Ñ\"] = \"ñ\",\n\t[\"Ò\"] = \"ò\",\n\t[\"Ó\"] = \"ó\",\n\t[\"Ô\"] = \"ô\",\n\t[\"Õ\"] = \"õ\",\n\t[\"Ö\"] = \"ö\",\n\t[\"Ø\"] = \"ø\",\n\t[\"Ù\"] = \"ù\",\n\t[\"Ú\"] = \"ú\",\n\t[\"Û\"] = \"û\",\n\t[\"Ü\"] = \"ü\",\n\t[\"Ý\"] = \"ý\",\n\t[\"Þ\"] = \"þ\",\n\t[\"Ā\"] = \"ā\",\n\t[\"Ă\"] = \"ă\",\n\t[\"Ą\"] = \"ą\",\n\t[\"Ć\"] = \"ć\",\n\t[\"Ĉ\"] = \"ĉ\",\n\t[\"Ċ\"] = \"ċ\",\n\t[\"Č\"] = \"č\",\n\t[\"Ď\"] = \"ď\",\n\t[\"Đ\"] = \"đ\",\n\t[\"Ē\"] = \"ē\",\n\t[\"Ĕ\"] = \"ĕ\",\n\t[\"Ė\"] = \"ė\",\n\t[\"Ę\"] = \"ę\",\n\t[\"Ě\"] = \"ě\",\n\t[\"Ĝ\"] = \"ĝ\",\n\t[\"Ğ\"] = \"ğ\",\n\t[\"Ġ\"] = \"ġ\",\n\t[\"Ģ\"] = \"ģ\",\n\t[\"Ĥ\"] = \"ĥ\",\n\t[\"Ħ\"] = \"ħ\",\n\t[\"Ĩ\"] = \"ĩ\",\n\t[\"Ī\"] = \"ī\",\n\t[\"Ĭ\"] = \"ĭ\",\n\t[\"Į\"] = \"į\",\n\t[\"İ\"] = \"i\",\n\t[\"Ĳ\"] = \"ĳ\",\n\t[\"Ĵ\"] = \"ĵ\",\n\t[\"Ķ\"] = \"ķ\",\n\t[\"Ĺ\"] = \"ĺ\",\n\t[\"Ļ\"] = \"ļ\",\n\t[\"Ľ\"] = \"ľ\",\n\t[\"Ŀ\"] = \"ŀ\",\n\t[\"Ł\"] = \"ł\",\n\t[\"Ń\"] = \"ń\",\n\t[\"Ņ\"] = \"ņ\",\n\t[\"Ň\"] = \"ň\",\n\t[\"Ŋ\"] = \"ŋ\",\n\t[\"Ō\"] = \"ō\",\n\t[\"Ŏ\"] = \"ŏ\",\n\t[\"Ő\"] = \"ő\",\n\t[\"Œ\"] = \"œ\",\n\t[\"Ŕ\"] = \"ŕ\",\n\t[\"Ŗ\"] = \"ŗ\",\n\t[\"Ř\"] = \"ř\",\n\t[\"Ś\"] = \"ś\",\n\t[\"Ŝ\"] = \"ŝ\",\n\t[\"Ş\"] = \"ş\",\n\t[\"Š\"] = \"š\",\n\t[\"Ţ\"] = \"ţ\",\n\t[\"Ť\"] = \"ť\",\n\t[\"Ŧ\"] = \"ŧ\",\n\t[\"Ũ\"] = \"ũ\",\n\t[\"Ū\"] = \"ū\",\n\t[\"Ŭ\"] = \"ŭ\",\n\t[\"Ů\"] = \"ů\",\n\t[\"Ű\"] = \"ű\",\n\t[\"Ų\"] = \"ų\",\n\t[\"Ŵ\"] = \"ŵ\",\n\t[\"Ŷ\"] = \"ŷ\",\n\t[\"Ÿ\"] = \"ÿ\",\n\t[\"Ź\"] = \"ź\",\n\t[\"Ż\"] = \"ż\",\n\t[\"Ž\"] = \"ž\",\n\t[\"Ɓ\"] = \"ɓ\",\n\t[\"Ƃ\"] = \"ƃ\",\n\t[\"Ƅ\"] = \"ƅ\",\n\t[\"Ɔ\"] = \"ɔ\",\n\t[\"Ƈ\"] = \"ƈ\",\n\t[\"Ɖ\"] = \"ɖ\",\n\t[\"Ɗ\"] = \"ɗ\",\n\t[\"Ƌ\"] = \"ƌ\",\n\t[\"Ǝ\"] = \"ǝ\",\n\t[\"Ə\"] = \"ə\",\n\t[\"Ɛ\"] = \"ɛ\",\n\t[\"Ƒ\"] = \"ƒ\",\n\t[\"Ɠ\"] = \"ɠ\",\n\t[\"Ɣ\"] = \"ɣ\",\n\t[\"Ɩ\"] = \"ɩ\",\n\t[\"Ɨ\"] = \"ɨ\",\n\t[\"Ƙ\"] = \"ƙ\",\n\t[\"Ɯ\"] = \"ɯ\",\n\t[\"Ɲ\"] = \"ɲ\",\n\t[\"Ɵ\"] = \"ɵ\",\n\t[\"Ơ\"] = \"ơ\",\n\t[\"Ƣ\"] = \"ƣ\",\n\t[\"Ƥ\"] = \"ƥ\",\n\t[\"Ʀ\"] = \"ʀ\",\n\t[\"Ƨ\"] = \"ƨ\",\n\t[\"Ʃ\"] = \"ʃ\",\n\t[\"Ƭ\"] = \"ƭ\",\n\t[\"Ʈ\"] = \"ʈ\",\n\t[\"Ư\"] = \"ư\",\n\t[\"Ʊ\"] = \"ʊ\",\n\t[\"Ʋ\"] = \"ʋ\",\n\t[\"Ƴ\"] = \"ƴ\",\n\t[\"Ƶ\"] = \"ƶ\",\n\t[\"Ʒ\"] = \"ʒ\",\n\t[\"Ƹ\"] = \"ƹ\",\n\t[\"Ƽ\"] = \"ƽ\",\n\t[\"Ǆ\"] = \"ǆ\",\n\t[\"ǅ\"] = \"ǆ\",\n\t[\"Ǉ\"] = \"ǉ\",\n\t[\"ǈ\"] = \"ǉ\",\n\t[\"Ǌ\"] = \"ǌ\",\n\t[\"ǋ\"] = \"ǌ\",\n\t[\"Ǎ\"] = \"ǎ\",\n\t[\"Ǐ\"] = \"ǐ\",\n\t[\"Ǒ\"] = \"ǒ\",\n\t[\"Ǔ\"] = \"ǔ\",\n\t[\"Ǖ\"] = \"ǖ\",\n\t[\"Ǘ\"] = \"ǘ\",\n\t[\"Ǚ\"] = \"ǚ\",\n\t[\"Ǜ\"] = \"ǜ\",\n\t[\"Ǟ\"] = \"ǟ\",\n\t[\"Ǡ\"] = \"ǡ\",\n\t[\"Ǣ\"] = \"ǣ\",\n\t[\"Ǥ\"] = \"ǥ\",\n\t[\"Ǧ\"] = \"ǧ\",\n\t[\"Ǩ\"] = \"ǩ\",\n\t[\"Ǫ\"] = \"ǫ\",\n\t[\"Ǭ\"] = \"ǭ\",\n\t[\"Ǯ\"] = \"ǯ\",\n\t[\"Ǳ\"] = \"ǳ\",\n\t[\"ǲ\"] = \"ǳ\",\n\t[\"Ǵ\"] = \"ǵ\",\n\t[\"Ƕ\"] = \"ƕ\",\n\t[\"Ƿ\"] = \"ƿ\",\n\t[\"Ǹ\"] = \"ǹ\",\n\t[\"Ǻ\"] = \"ǻ\",\n\t[\"Ǽ\"] = \"ǽ\",\n\t[\"Ǿ\"] = \"ǿ\",\n\t[\"Ȁ\"] = \"ȁ\",\n\t[\"Ȃ\"] = \"ȃ\",\n\t[\"Ȅ\"] = \"ȅ\",\n\t[\"Ȇ\"] = \"ȇ\",\n\t[\"Ȉ\"] = \"ȉ\",\n\t[\"Ȋ\"] = \"ȋ\",\n\t[\"Ȍ\"] = \"ȍ\",\n\t[\"Ȏ\"] = \"ȏ\",\n\t[\"Ȑ\"] = \"ȑ\",\n\t[\"Ȓ\"] = \"ȓ\",\n\t[\"Ȕ\"] = \"ȕ\",\n\t[\"Ȗ\"] = \"ȗ\",\n\t[\"Ș\"] = \"ș\",\n\t[\"Ț\"] = \"ț\",\n\t[\"Ȝ\"] = \"ȝ\",\n\t[\"Ȟ\"] = \"ȟ\",\n\t[\"Ƞ\"] = \"ƞ\",\n\t[\"Ȣ\"] = \"ȣ\",\n\t[\"Ȥ\"] = \"ȥ\",\n\t[\"Ȧ\"] = \"ȧ\",\n\t[\"Ȩ\"] = \"ȩ\",\n\t[\"Ȫ\"] = \"ȫ\",\n\t[\"Ȭ\"] = \"ȭ\",\n\t[\"Ȯ\"] = \"ȯ\",\n\t[\"Ȱ\"] = \"ȱ\",\n\t[\"Ȳ\"] = \"ȳ\",\n\t[\"Ⱥ\"] = \"ⱥ\",\n\t[\"Ȼ\"] = \"ȼ\",\n\t[\"Ƚ\"] = \"ƚ\",\n\t[\"Ⱦ\"] = \"ⱦ\",\n\t[\"Ɂ\"] = \"ɂ\",\n\t[\"Ƀ\"] = \"ƀ\",\n\t[\"Ʉ\"] = \"ʉ\",\n\t[\"Ʌ\"] = \"ʌ\",\n\t[\"Ɇ\"] = \"ɇ\",\n\t[\"Ɉ\"] = \"ɉ\",\n\t[\"Ɋ\"] = \"ɋ\",\n\t[\"Ɍ\"] = \"ɍ\",\n\t[\"Ɏ\"] = \"ɏ\",\n\t[\"Ά\"] = \"ά\",\n\t[\"Έ\"] = \"έ\",\n\t[\"Ή\"] = \"ή\",\n\t[\"Ί\"] = \"ί\",\n\t[\"Ό\"] = \"ό\",\n\t[\"Ύ\"] = \"ύ\",\n\t[\"Ώ\"] = \"ώ\",\n\t[\"Α\"] = \"α\",\n\t[\"Β\"] = \"β\",\n\t[\"Γ\"] = \"γ\",\n\t[\"Δ\"] = \"δ\",\n\t[\"Ε\"] = \"ε\",\n\t[\"Ζ\"] = \"ζ\",\n\t[\"Η\"] = \"η\",\n\t[\"Θ\"] = \"θ\",\n\t[\"Ι\"] = \"ι\",\n\t[\"Κ\"] = \"κ\",\n\t[\"Λ\"] = \"λ\",\n\t[\"Μ\"] = \"μ\",\n\t[\"Ν\"] = \"ν\",\n\t[\"Ξ\"] = \"ξ\",\n\t[\"Ο\"] = \"ο\",\n\t[\"Π\"] = \"π\",\n\t[\"Ρ\"] = \"ρ\",\n\t[\"Σ\"] = \"σ\",\n\t[\"Τ\"] = \"τ\",\n\t[\"Υ\"] = \"υ\",\n\t[\"Φ\"] = \"φ\",\n\t[\"Χ\"] = \"χ\",\n\t[\"Ψ\"] = \"ψ\",\n\t[\"Ω\"] = \"ω\",\n\t[\"Ϊ\"] = \"ϊ\",\n\t[\"Ϋ\"] = \"ϋ\",\n\t[\"Ϙ\"] = \"ϙ\",\n\t[\"Ϛ\"] = \"ϛ\",\n\t[\"Ϝ\"] = \"ϝ\",\n\t[\"Ϟ\"] = \"ϟ\",\n\t[\"Ϡ\"] = \"ϡ\",\n\t[\"Ϣ\"] = \"ϣ\",\n\t[\"Ϥ\"] = \"ϥ\",\n\t[\"Ϧ\"] = \"ϧ\",\n\t[\"Ϩ\"] = \"ϩ\",\n\t[\"Ϫ\"] = \"ϫ\",\n\t[\"Ϭ\"] = \"ϭ\",\n\t[\"Ϯ\"] = \"ϯ\",\n\t[\"ϴ\"] = \"θ\",\n\t[\"Ϸ\"] = \"ϸ\",\n\t[\"Ϲ\"] = \"ϲ\",\n\t[\"Ϻ\"] = \"ϻ\",\n\t[\"Ͻ\"] = \"ͻ\",\n\t[\"Ͼ\"] = \"ͼ\",\n\t[\"Ͽ\"] = \"ͽ\",\n\t[\"Ѐ\"] = \"ѐ\",\n\t[\"Ё\"] = \"ё\",\n\t[\"Ђ\"] = \"ђ\",\n\t[\"Ѓ\"] = \"ѓ\",\n\t[\"Є\"] = \"є\",\n\t[\"Ѕ\"] = \"ѕ\",\n\t[\"І\"] = \"і\",\n\t[\"Ї\"] = \"ї\",\n\t[\"Ј\"] = \"ј\",\n\t[\"Љ\"] = \"љ\",\n\t[\"Њ\"] = \"њ\",\n\t[\"Ћ\"] = \"ћ\",\n\t[\"Ќ\"] = \"ќ\",\n\t[\"Ѝ\"] = \"ѝ\",\n\t[\"Ў\"] = \"ў\",\n\t[\"Џ\"] = \"џ\",\n\t[\"А\"] = \"а\",\n\t[\"Б\"] = \"б\",\n\t[\"В\"] = \"в\",\n\t[\"Г\"] = \"г\",\n\t[\"Д\"] = \"д\",\n\t[\"Е\"] = \"е\",\n\t[\"Ж\"] = \"ж\",\n\t[\"З\"] = \"з\",\n\t[\"И\"] = \"и\",\n\t[\"Й\"] = \"й\",\n\t[\"К\"] = \"к\",\n\t[\"Л\"] = \"л\",\n\t[\"М\"] = \"м\",\n\t[\"Н\"] = \"н\",\n\t[\"О\"] = \"о\",\n\t[\"П\"] = \"п\",\n\t[\"Р\"] = \"р\",\n\t[\"С\"] = \"с\",\n\t[\"Т\"] = \"т\",\n\t[\"У\"] = \"у\",\n\t[\"Ф\"] = \"ф\",\n\t[\"Х\"] = \"х\",\n\t[\"Ц\"] = \"ц\",\n\t[\"Ч\"] = \"ч\",\n\t[\"Ш\"] = \"ш\",\n\t[\"Щ\"] = \"щ\",\n\t[\"Ъ\"] = \"ъ\",\n\t[\"Ы\"] = \"ы\",\n\t[\"Ь\"] = \"ь\",\n\t[\"Э\"] = \"э\",\n\t[\"Ю\"] = \"ю\",\n\t[\"Я\"] = \"я\",\n\t[\"Ѡ\"] = \"ѡ\",\n\t[\"Ѣ\"] = \"ѣ\",\n\t[\"Ѥ\"] = \"ѥ\",\n\t[\"Ѧ\"] = \"ѧ\",\n\t[\"Ѩ\"] = \"ѩ\",\n\t[\"Ѫ\"] = \"ѫ\",\n\t[\"Ѭ\"] = \"ѭ\",\n\t[\"Ѯ\"] = \"ѯ\",\n\t[\"Ѱ\"] = \"ѱ\",\n\t[\"Ѳ\"] = \"ѳ\",\n\t[\"Ѵ\"] = \"ѵ\",\n\t[\"Ѷ\"] = \"ѷ\",\n\t[\"Ѹ\"] = \"ѹ\",\n\t[\"Ѻ\"] = \"ѻ\",\n\t[\"Ѽ\"] = \"ѽ\",\n\t[\"Ѿ\"] = \"ѿ\",\n\t[\"Ҁ\"] = \"ҁ\",\n\t[\"Ҋ\"] = \"ҋ\",\n\t[\"Ҍ\"] = \"ҍ\",\n\t[\"Ҏ\"] = \"ҏ\",\n\t[\"Ґ\"] = \"ґ\",\n\t[\"Ғ\"] = \"ғ\",\n\t[\"Ҕ\"] = \"ҕ\",\n\t[\"Җ\"] = \"җ\",\n\t[\"Ҙ\"] = \"ҙ\",\n\t[\"Қ\"] = \"қ\",\n\t[\"Ҝ\"] = \"ҝ\",\n\t[\"Ҟ\"] = \"ҟ\",\n\t[\"Ҡ\"] = \"ҡ\",\n\t[\"Ң\"] = \"ң\",\n\t[\"Ҥ\"] = \"ҥ\",\n\t[\"Ҧ\"] = \"ҧ\",\n\t[\"Ҩ\"] = \"ҩ\",\n\t[\"Ҫ\"] = \"ҫ\",\n\t[\"Ҭ\"] = \"ҭ\",\n\t[\"Ү\"] = \"ү\",\n\t[\"Ұ\"] = \"ұ\",\n\t[\"Ҳ\"] = \"ҳ\",\n\t[\"Ҵ\"] = \"ҵ\",\n\t[\"Ҷ\"] = \"ҷ\",\n\t[\"Ҹ\"] = \"ҹ\",\n\t[\"Һ\"] = \"һ\",\n\t[\"Ҽ\"] = \"ҽ\",\n\t[\"Ҿ\"] = \"ҿ\",\n\t[\"Ӏ\"] = \"ӏ\",\n\t[\"Ӂ\"] = \"ӂ\",\n\t[\"Ӄ\"] = \"ӄ\",\n\t[\"Ӆ\"] = \"ӆ\",\n\t[\"Ӈ\"] = \"ӈ\",\n\t[\"Ӊ\"] = \"ӊ\",\n\t[\"Ӌ\"] = \"ӌ\",\n\t[\"Ӎ\"] = \"ӎ\",\n\t[\"Ӑ\"] = \"ӑ\",\n\t[\"Ӓ\"] = \"ӓ\",\n\t[\"Ӕ\"] = \"ӕ\",\n\t[\"Ӗ\"] = \"ӗ\",\n\t[\"Ә\"] = \"ә\",\n\t[\"Ӛ\"] = \"ӛ\",\n\t[\"Ӝ\"] = \"ӝ\",\n\t[\"Ӟ\"] = \"ӟ\",\n\t[\"Ӡ\"] = \"ӡ\",\n\t[\"Ӣ\"] = \"ӣ\",\n\t[\"Ӥ\"] = \"ӥ\",\n\t[\"Ӧ\"] = \"ӧ\",\n\t[\"Ө\"] = \"ө\",\n\t[\"Ӫ\"] = \"ӫ\",\n\t[\"Ӭ\"] = \"ӭ\",\n\t[\"Ӯ\"] = \"ӯ\",\n\t[\"Ӱ\"] = \"ӱ\",\n\t[\"Ӳ\"] = \"ӳ\",\n\t[\"Ӵ\"] = \"ӵ\",\n\t[\"Ӷ\"] = \"ӷ\",\n\t[\"Ӹ\"] = \"ӹ\",\n\t[\"Ӻ\"] = \"ӻ\",\n\t[\"Ӽ\"] = \"ӽ\",\n\t[\"Ӿ\"] = \"ӿ\",\n\t[\"Ԁ\"] = \"ԁ\",\n\t[\"Ԃ\"] = \"ԃ\",\n\t[\"Ԅ\"] = \"ԅ\",\n\t[\"Ԇ\"] = \"ԇ\",\n\t[\"Ԉ\"] = \"ԉ\",\n\t[\"Ԋ\"] = \"ԋ\",\n\t[\"Ԍ\"] = \"ԍ\",\n\t[\"Ԏ\"] = \"ԏ\",\n\t[\"Ԑ\"] = \"ԑ\",\n\t[\"Ԓ\"] = \"ԓ\",\n\t[\"Ա\"] = \"ա\",\n\t[\"Բ\"] = \"բ\",\n\t[\"Գ\"] = \"գ\",\n\t[\"Դ\"] = \"դ\",\n\t[\"Ե\"] = \"ե\",\n\t[\"Զ\"] = \"զ\",\n\t[\"Է\"] = \"է\",\n\t[\"Ը\"] = \"ը\",\n\t[\"Թ\"] = \"թ\",\n\t[\"Ժ\"] = \"ժ\",\n\t[\"Ի\"] = \"ի\",\n\t[\"Լ\"] = \"լ\",\n\t[\"Խ\"] = \"խ\",\n\t[\"Ծ\"] = \"ծ\",\n\t[\"Կ\"] = \"կ\",\n\t[\"Հ\"] = \"հ\",\n\t[\"Ձ\"] = \"ձ\",\n\t[\"Ղ\"] = \"ղ\",\n\t[\"Ճ\"] = \"ճ\",\n\t[\"Մ\"] = \"մ\",\n\t[\"Յ\"] = \"յ\",\n\t[\"Ն\"] = \"ն\",\n\t[\"Շ\"] = \"շ\",\n\t[\"Ո\"] = \"ո\",\n\t[\"Չ\"] = \"չ\",\n\t[\"Պ\"] = \"պ\",\n\t[\"Ջ\"] = \"ջ\",\n\t[\"Ռ\"] = \"ռ\",\n\t[\"Ս\"] = \"ս\",\n\t[\"Վ\"] = \"վ\",\n\t[\"Տ\"] = \"տ\",\n\t[\"Ր\"] = \"ր\",\n\t[\"Ց\"] = \"ց\",\n\t[\"Ւ\"] = \"ւ\",\n\t[\"Փ\"] = \"փ\",\n\t[\"Ք\"] = \"ք\",\n\t[\"Օ\"] = \"օ\",\n\t[\"Ֆ\"] = \"ֆ\",\n\t[\"Ⴀ\"] = \"ⴀ\",\n\t[\"Ⴁ\"] = \"ⴁ\",\n\t[\"Ⴂ\"] = \"ⴂ\",\n\t[\"Ⴃ\"] = \"ⴃ\",\n\t[\"Ⴄ\"] = \"ⴄ\",\n\t[\"Ⴅ\"] = \"ⴅ\",\n\t[\"Ⴆ\"] = \"ⴆ\",\n\t[\"Ⴇ\"] = \"ⴇ\",\n\t[\"Ⴈ\"] = \"ⴈ\",\n\t[\"Ⴉ\"] = \"ⴉ\",\n\t[\"Ⴊ\"] = \"ⴊ\",\n\t[\"Ⴋ\"] = \"ⴋ\",\n\t[\"Ⴌ\"] = \"ⴌ\",\n\t[\"Ⴍ\"] = \"ⴍ\",\n\t[\"Ⴎ\"] = \"ⴎ\",\n\t[\"Ⴏ\"] = \"ⴏ\",\n\t[\"Ⴐ\"] = \"ⴐ\",\n\t[\"Ⴑ\"] = \"ⴑ\",\n\t[\"Ⴒ\"] = \"ⴒ\",\n\t[\"Ⴓ\"] = \"ⴓ\",\n\t[\"Ⴔ\"] = \"ⴔ\",\n\t[\"Ⴕ\"] = \"ⴕ\",\n\t[\"Ⴖ\"] = \"ⴖ\",\n\t[\"Ⴗ\"] = \"ⴗ\",\n\t[\"Ⴘ\"] = \"ⴘ\",\n\t[\"Ⴙ\"] = \"ⴙ\",\n\t[\"Ⴚ\"] = \"ⴚ\",\n\t[\"Ⴛ\"] = \"ⴛ\",\n\t[\"Ⴜ\"] = \"ⴜ\",\n\t[\"Ⴝ\"] = \"ⴝ\",\n\t[\"Ⴞ\"] = \"ⴞ\",\n\t[\"Ⴟ\"] = \"ⴟ\",\n\t[\"Ⴠ\"] = \"ⴠ\",\n\t[\"Ⴡ\"] = \"ⴡ\",\n\t[\"Ⴢ\"] = \"ⴢ\",\n\t[\"Ⴣ\"] = \"ⴣ\",\n\t[\"Ⴤ\"] = \"ⴤ\",\n\t[\"Ⴥ\"] = \"ⴥ\",\n\t[\"Ḁ\"] = \"ḁ\",\n\t[\"Ḃ\"] = \"ḃ\",\n\t[\"Ḅ\"] = \"ḅ\",\n\t[\"Ḇ\"] = \"ḇ\",\n\t[\"Ḉ\"] = \"ḉ\",\n\t[\"Ḋ\"] = \"ḋ\",\n\t[\"Ḍ\"] = \"ḍ\",\n\t[\"Ḏ\"] = \"ḏ\",\n\t[\"Ḑ\"] = \"ḑ\",\n\t[\"Ḓ\"] = \"ḓ\",\n\t[\"Ḕ\"] = \"ḕ\",\n\t[\"Ḗ\"] = \"ḗ\",\n\t[\"Ḙ\"] = \"ḙ\",\n\t[\"Ḛ\"] = \"ḛ\",\n\t[\"Ḝ\"] = \"ḝ\",\n\t[\"Ḟ\"] = \"ḟ\",\n\t[\"Ḡ\"] = \"ḡ\",\n\t[\"Ḣ\"] = \"ḣ\",\n\t[\"Ḥ\"] = \"ḥ\",\n\t[\"Ḧ\"] = \"ḧ\",\n\t[\"Ḩ\"] = \"ḩ\",\n\t[\"Ḫ\"] = \"ḫ\",\n\t[\"Ḭ\"] = \"ḭ\",\n\t[\"Ḯ\"] = \"ḯ\",\n\t[\"Ḱ\"] = \"ḱ\",\n\t[\"Ḳ\"] = \"ḳ\",\n\t[\"Ḵ\"] = \"ḵ\",\n\t[\"Ḷ\"] = \"ḷ\",\n\t[\"Ḹ\"] = \"ḹ\",\n\t[\"Ḻ\"] = \"ḻ\",\n\t[\"Ḽ\"] = \"ḽ\",\n\t[\"Ḿ\"] = \"ḿ\",\n\t[\"Ṁ\"] = \"ṁ\",\n\t[\"Ṃ\"] = \"ṃ\",\n\t[\"Ṅ\"] = \"ṅ\",\n\t[\"Ṇ\"] = \"ṇ\",\n\t[\"Ṉ\"] = \"ṉ\",\n\t[\"Ṋ\"] = \"ṋ\",\n\t[\"Ṍ\"] = \"ṍ\",\n\t[\"Ṏ\"] = \"ṏ\",\n\t[\"Ṑ\"] = \"ṑ\",\n\t[\"Ṓ\"] = \"ṓ\",\n\t[\"Ṕ\"] = \"ṕ\",\n\t[\"Ṗ\"] = \"ṗ\",\n\t[\"Ṙ\"] = \"ṙ\",\n\t[\"Ṛ\"] = \"ṛ\",\n\t[\"Ṝ\"] = \"ṝ\",\n\t[\"Ṟ\"] = \"ṟ\",\n\t[\"Ṡ\"] = \"ṡ\",\n\t[\"Ṣ\"] = \"ṣ\",\n\t[\"Ṥ\"] = \"ṥ\",\n\t[\"Ṧ\"] = \"ṧ\",\n\t[\"Ṩ\"] = \"ṩ\",\n\t[\"Ṫ\"] = \"ṫ\",\n\t[\"Ṭ\"] = \"ṭ\",\n\t[\"Ṯ\"] = \"ṯ\",\n\t[\"Ṱ\"] = \"ṱ\",\n\t[\"Ṳ\"] = \"ṳ\",\n\t[\"Ṵ\"] = \"ṵ\",\n\t[\"Ṷ\"] = \"ṷ\",\n\t[\"Ṹ\"] = \"ṹ\",\n\t[\"Ṻ\"] = \"ṻ\",\n\t[\"Ṽ\"] = \"ṽ\",\n\t[\"Ṿ\"] = \"ṿ\",\n\t[\"Ẁ\"] = \"ẁ\",\n\t[\"Ẃ\"] = \"ẃ\",\n\t[\"Ẅ\"] = \"ẅ\",\n\t[\"Ẇ\"] = \"ẇ\",\n\t[\"Ẉ\"] = \"ẉ\",\n\t[\"Ẋ\"] = \"ẋ\",\n\t[\"Ẍ\"] = \"ẍ\",\n\t[\"Ẏ\"] = \"ẏ\",\n\t[\"Ẑ\"] = \"ẑ\",\n\t[\"Ẓ\"] = \"ẓ\",\n\t[\"Ẕ\"] = \"ẕ\",\n\t[\"Ạ\"] = \"ạ\",\n\t[\"Ả\"] = \"ả\",\n\t[\"Ấ\"] = \"ấ\",\n\t[\"Ầ\"] = \"ầ\",\n\t[\"Ẩ\"] = \"ẩ\",\n\t[\"Ẫ\"] = \"ẫ\",\n\t[\"Ậ\"] = \"ậ\",\n\t[\"Ắ\"] = \"ắ\",\n\t[\"Ằ\"] = \"ằ\",\n\t[\"Ẳ\"] = \"ẳ\",\n\t[\"Ẵ\"] = \"ẵ\",\n\t[\"Ặ\"] = \"ặ\",\n\t[\"Ẹ\"] = \"ẹ\",\n\t[\"Ẻ\"] = \"ẻ\",\n\t[\"Ẽ\"] = \"ẽ\",\n\t[\"Ế\"] = \"ế\",\n\t[\"Ề\"] = \"ề\",\n\t[\"Ể\"] = \"ể\",\n\t[\"Ễ\"] = \"ễ\",\n\t[\"Ệ\"] = \"ệ\",\n\t[\"Ỉ\"] = \"ỉ\",\n\t[\"Ị\"] = \"ị\",\n\t[\"Ọ\"] = \"ọ\",\n\t[\"Ỏ\"] = \"ỏ\",\n\t[\"Ố\"] = \"ố\",\n\t[\"Ồ\"] = \"ồ\",\n\t[\"Ổ\"] = \"ổ\",\n\t[\"Ỗ\"] = \"ỗ\",\n\t[\"Ộ\"] = \"ộ\",\n\t[\"Ớ\"] = \"ớ\",\n\t[\"Ờ\"] = \"ờ\",\n\t[\"Ở\"] = \"ở\",\n\t[\"Ỡ\"] = \"ỡ\",\n\t[\"Ợ\"] = \"ợ\",\n\t[\"Ụ\"] = \"ụ\",\n\t[\"Ủ\"] = \"ủ\",\n\t[\"Ứ\"] = \"ứ\",\n\t[\"Ừ\"] = \"ừ\",\n\t[\"Ử\"] = \"ử\",\n\t[\"Ữ\"] = \"ữ\",\n\t[\"Ự\"] = \"ự\",\n\t[\"Ỳ\"] = \"ỳ\",\n\t[\"Ỵ\"] = \"ỵ\",\n\t[\"Ỷ\"] = \"ỷ\",\n\t[\"Ỹ\"] = \"ỹ\",\n\t[\"Ἀ\"] = \"ἀ\",\n\t[\"Ἁ\"] = \"ἁ\",\n\t[\"Ἂ\"] = \"ἂ\",\n\t[\"Ἃ\"] = \"ἃ\",\n\t[\"Ἄ\"] = \"ἄ\",\n\t[\"Ἅ\"] = \"ἅ\",\n\t[\"Ἆ\"] = \"ἆ\",\n\t[\"Ἇ\"] = \"ἇ\",\n\t[\"Ἐ\"] = \"ἐ\",\n\t[\"Ἑ\"] = \"ἑ\",\n\t[\"Ἒ\"] = \"ἒ\",\n\t[\"Ἓ\"] = \"ἓ\",\n\t[\"Ἔ\"] = \"ἔ\",\n\t[\"Ἕ\"] = \"ἕ\",\n\t[\"Ἠ\"] = \"ἠ\",\n\t[\"Ἡ\"] = \"ἡ\",\n\t[\"Ἢ\"] = \"ἢ\",\n\t[\"Ἣ\"] = \"ἣ\",\n\t[\"Ἤ\"] = \"ἤ\",\n\t[\"Ἥ\"] = \"ἥ\",\n\t[\"Ἦ\"] = \"ἦ\",\n\t[\"Ἧ\"] = \"ἧ\",\n\t[\"Ἰ\"] = \"ἰ\",\n\t[\"Ἱ\"] = \"ἱ\",\n\t[\"Ἲ\"] = \"ἲ\",\n\t[\"Ἳ\"] = \"ἳ\",\n\t[\"Ἴ\"] = \"ἴ\",\n\t[\"Ἵ\"] = \"ἵ\",\n\t[\"Ἶ\"] = \"ἶ\",\n\t[\"Ἷ\"] = \"ἷ\",\n\t[\"Ὀ\"] = \"ὀ\",\n\t[\"Ὁ\"] = \"ὁ\",\n\t[\"Ὂ\"] = \"ὂ\",\n\t[\"Ὃ\"] = \"ὃ\",\n\t[\"Ὄ\"] = \"ὄ\",\n\t[\"Ὅ\"] = \"ὅ\",\n\t[\"Ὑ\"] = \"ὑ\",\n\t[\"Ὓ\"] = \"ὓ\",\n\t[\"Ὕ\"] = \"ὕ\",\n\t[\"Ὗ\"] = \"ὗ\",\n\t[\"Ὠ\"] = \"ὠ\",\n\t[\"Ὡ\"] = \"ὡ\",\n\t[\"Ὢ\"] = \"ὢ\",\n\t[\"Ὣ\"] = \"ὣ\",\n\t[\"Ὤ\"] = \"ὤ\",\n\t[\"Ὥ\"] = \"ὥ\",\n\t[\"Ὦ\"] = \"ὦ\",\n\t[\"Ὧ\"] = \"ὧ\",\n\t[\"ᾈ\"] = \"ᾀ\",\n\t[\"ᾉ\"] = \"ᾁ\",\n\t[\"ᾊ\"] = \"ᾂ\",\n\t[\"ᾋ\"] = \"ᾃ\",\n\t[\"ᾌ\"] = \"ᾄ\",\n\t[\"ᾍ\"] = \"ᾅ\",\n\t[\"ᾎ\"] = \"ᾆ\",\n\t[\"ᾏ\"] = \"ᾇ\",\n\t[\"ᾘ\"] = \"ᾐ\",\n\t[\"ᾙ\"] = \"ᾑ\",\n\t[\"ᾚ\"] = \"ᾒ\",\n\t[\"ᾛ\"] = \"ᾓ\",\n\t[\"ᾜ\"] = \"ᾔ\",\n\t[\"ᾝ\"] = \"ᾕ\",\n\t[\"ᾞ\"] = \"ᾖ\",\n\t[\"ᾟ\"] = \"ᾗ\",\n\t[\"ᾨ\"] = \"ᾠ\",\n\t[\"ᾩ\"] = \"ᾡ\",\n\t[\"ᾪ\"] = \"ᾢ\",\n\t[\"ᾫ\"] = \"ᾣ\",\n\t[\"ᾬ\"] = \"ᾤ\",\n\t[\"ᾭ\"] = \"ᾥ\",\n\t[\"ᾮ\"] = \"ᾦ\",\n\t[\"ᾯ\"] = \"ᾧ\",\n\t[\"Ᾰ\"] = \"ᾰ\",\n\t[\"Ᾱ\"] = \"ᾱ\",\n\t[\"Ὰ\"] = \"ὰ\",\n\t[\"Ά\"] = \"ά\",\n\t[\"ᾼ\"] = \"ᾳ\",\n\t[\"Ὲ\"] = \"ὲ\",\n\t[\"Έ\"] = \"έ\",\n\t[\"Ὴ\"] = \"ὴ\",\n\t[\"Ή\"] = \"ή\",\n\t[\"ῌ\"] = \"ῃ\",\n\t[\"Ῐ\"] = \"ῐ\",\n\t[\"Ῑ\"] = \"ῑ\",\n\t[\"Ὶ\"] = \"ὶ\",\n\t[\"Ί\"] = \"ί\",\n\t[\"Ῠ\"] = \"ῠ\",\n\t[\"Ῡ\"] = \"ῡ\",\n\t[\"Ὺ\"] = \"ὺ\",\n\t[\"Ύ\"] = \"ύ\",\n\t[\"Ῥ\"] = \"ῥ\",\n\t[\"Ὸ\"] = \"ὸ\",\n\t[\"Ό\"] = \"ό\",\n\t[\"Ὼ\"] = \"ὼ\",\n\t[\"Ώ\"] = \"ώ\",\n\t[\"ῼ\"] = \"ῳ\",\n\t[\"Ω\"] = \"ω\",\n\t[\"K\"] = \"k\",\n\t[\"Å\"] = \"å\",\n\t[\"Ⅎ\"] = \"ⅎ\",\n\t[\"Ⅰ\"] = \"ⅰ\",\n\t[\"Ⅱ\"] = \"ⅱ\",\n\t[\"Ⅲ\"] = \"ⅲ\",\n\t[\"Ⅳ\"] = \"ⅳ\",\n\t[\"Ⅴ\"] = \"ⅴ\",\n\t[\"Ⅵ\"] = \"ⅵ\",\n\t[\"Ⅶ\"] = \"ⅶ\",\n\t[\"Ⅷ\"] = \"ⅷ\",\n\t[\"Ⅸ\"] = \"ⅸ\",\n\t[\"Ⅹ\"] = \"ⅹ\",\n\t[\"Ⅺ\"] = \"ⅺ\",\n\t[\"Ⅻ\"] = \"ⅻ\",\n\t[\"Ⅼ\"] = \"ⅼ\",\n\t[\"Ⅽ\"] = \"ⅽ\",\n\t[\"Ⅾ\"] = \"ⅾ\",\n\t[\"Ⅿ\"] = \"ⅿ\",\n\t[\"Ↄ\"] = \"ↄ\",\n\t[\"Ⓐ\"] = \"ⓐ\",\n\t[\"Ⓑ\"] = \"ⓑ\",\n\t[\"Ⓒ\"] = \"ⓒ\",\n\t[\"Ⓓ\"] = \"ⓓ\",\n\t[\"Ⓔ\"] = \"ⓔ\",\n\t[\"Ⓕ\"] = \"ⓕ\",\n\t[\"Ⓖ\"] = \"ⓖ\",\n\t[\"Ⓗ\"] = \"ⓗ\",\n\t[\"Ⓘ\"] = \"ⓘ\",\n\t[\"Ⓙ\"] = \"ⓙ\",\n\t[\"Ⓚ\"] = \"ⓚ\",\n\t[\"Ⓛ\"] = \"ⓛ\",\n\t[\"Ⓜ\"] = \"ⓜ\",\n\t[\"Ⓝ\"] = \"ⓝ\",\n\t[\"Ⓞ\"] = \"ⓞ\",\n\t[\"Ⓟ\"] = \"ⓟ\",\n\t[\"Ⓠ\"] = \"ⓠ\",\n\t[\"Ⓡ\"] = \"ⓡ\",\n\t[\"Ⓢ\"] = \"ⓢ\",\n\t[\"Ⓣ\"] = \"ⓣ\",\n\t[\"Ⓤ\"] = \"ⓤ\",\n\t[\"Ⓥ\"] = \"ⓥ\",\n\t[\"Ⓦ\"] = \"ⓦ\",\n\t[\"Ⓧ\"] = \"ⓧ\",\n\t[\"Ⓨ\"] = \"ⓨ\",\n\t[\"Ⓩ\"] = \"ⓩ\",\n\t[\"Ⰰ\"] = \"ⰰ\",\n\t[\"Ⰱ\"] = \"ⰱ\",\n\t[\"Ⰲ\"] = \"ⰲ\",\n\t[\"Ⰳ\"] = \"ⰳ\",\n\t[\"Ⰴ\"] = \"ⰴ\",\n\t[\"Ⰵ\"] = \"ⰵ\",\n\t[\"Ⰶ\"] = \"ⰶ\",\n\t[\"Ⰷ\"] = \"ⰷ\",\n\t[\"Ⰸ\"] = \"ⰸ\",\n\t[\"Ⰹ\"] = \"ⰹ\",\n\t[\"Ⰺ\"] = \"ⰺ\",\n\t[\"Ⰻ\"] = \"ⰻ\",\n\t[\"Ⰼ\"] = \"ⰼ\",\n\t[\"Ⰽ\"] = \"ⰽ\",\n\t[\"Ⰾ\"] = \"ⰾ\",\n\t[\"Ⰿ\"] = \"ⰿ\",\n\t[\"Ⱀ\"] = \"ⱀ\",\n\t[\"Ⱁ\"] = \"ⱁ\",\n\t[\"Ⱂ\"] = \"ⱂ\",\n\t[\"Ⱃ\"] = \"ⱃ\",\n\t[\"Ⱄ\"] = \"ⱄ\",\n\t[\"Ⱅ\"] = \"ⱅ\",\n\t[\"Ⱆ\"] = \"ⱆ\",\n\t[\"Ⱇ\"] = \"ⱇ\",\n\t[\"Ⱈ\"] = \"ⱈ\",\n\t[\"Ⱉ\"] = \"ⱉ\",\n\t[\"Ⱊ\"] = \"ⱊ\",\n\t[\"Ⱋ\"] = \"ⱋ\",\n\t[\"Ⱌ\"] = \"ⱌ\",\n\t[\"Ⱍ\"] = \"ⱍ\",\n\t[\"Ⱎ\"] = \"ⱎ\",\n\t[\"Ⱏ\"] = \"ⱏ\",\n\t[\"Ⱐ\"] = \"ⱐ\",\n\t[\"Ⱑ\"] = \"ⱑ\",\n\t[\"Ⱒ\"] = \"ⱒ\",\n\t[\"Ⱓ\"] = \"ⱓ\",\n\t[\"Ⱔ\"] = \"ⱔ\",\n\t[\"Ⱕ\"] = \"ⱕ\",\n\t[\"Ⱖ\"] = \"ⱖ\",\n\t[\"Ⱗ\"] = \"ⱗ\",\n\t[\"Ⱘ\"] = \"ⱘ\",\n\t[\"Ⱙ\"] = \"ⱙ\",\n\t[\"Ⱚ\"] = \"ⱚ\",\n\t[\"Ⱛ\"] = \"ⱛ\",\n\t[\"Ⱜ\"] = \"ⱜ\",\n\t[\"Ⱝ\"] = \"ⱝ\",\n\t[\"Ⱞ\"] = \"ⱞ\",\n\t[\"Ⱡ\"] = \"ⱡ\",\n\t[\"Ɫ\"] = \"ɫ\",\n\t[\"Ᵽ\"] = \"ᵽ\",\n\t[\"Ɽ\"] = \"ɽ\",\n\t[\"Ⱨ\"] = \"ⱨ\",\n\t[\"Ⱪ\"] = \"ⱪ\",\n\t[\"Ⱬ\"] = \"ⱬ\",\n\t[\"Ⱶ\"] = \"ⱶ\",\n\t[\"Ⲁ\"] = \"ⲁ\",\n\t[\"Ⲃ\"] = \"ⲃ\",\n\t[\"Ⲅ\"] = \"ⲅ\",\n\t[\"Ⲇ\"] = \"ⲇ\",\n\t[\"Ⲉ\"] = \"ⲉ\",\n\t[\"Ⲋ\"] = \"ⲋ\",\n\t[\"Ⲍ\"] = \"ⲍ\",\n\t[\"Ⲏ\"] = \"ⲏ\",\n\t[\"Ⲑ\"] = \"ⲑ\",\n\t[\"Ⲓ\"] = \"ⲓ\",\n\t[\"Ⲕ\"] = \"ⲕ\",\n\t[\"Ⲗ\"] = \"ⲗ\",\n\t[\"Ⲙ\"] = \"ⲙ\",\n\t[\"Ⲛ\"] = \"ⲛ\",\n\t[\"Ⲝ\"] = \"ⲝ\",\n\t[\"Ⲟ\"] = \"ⲟ\",\n\t[\"Ⲡ\"] = \"ⲡ\",\n\t[\"Ⲣ\"] = \"ⲣ\",\n\t[\"Ⲥ\"] = \"ⲥ\",\n\t[\"Ⲧ\"] = \"ⲧ\",\n\t[\"Ⲩ\"] = \"ⲩ\",\n\t[\"Ⲫ\"] = \"ⲫ\",\n\t[\"Ⲭ\"] = \"ⲭ\",\n\t[\"Ⲯ\"] = \"ⲯ\",\n\t[\"Ⲱ\"] = \"ⲱ\",\n\t[\"Ⲳ\"] = \"ⲳ\",\n\t[\"Ⲵ\"] = \"ⲵ\",\n\t[\"Ⲷ\"] = \"ⲷ\",\n\t[\"Ⲹ\"] = \"ⲹ\",\n\t[\"Ⲻ\"] = \"ⲻ\",\n\t[\"Ⲽ\"] = \"ⲽ\",\n\t[\"Ⲿ\"] = \"ⲿ\",\n\t[\"Ⳁ\"] = \"ⳁ\",\n\t[\"Ⳃ\"] = \"ⳃ\",\n\t[\"Ⳅ\"] = \"ⳅ\",\n\t[\"Ⳇ\"] = \"ⳇ\",\n\t[\"Ⳉ\"] = \"ⳉ\",\n\t[\"Ⳋ\"] = \"ⳋ\",\n\t[\"Ⳍ\"] = \"ⳍ\",\n\t[\"Ⳏ\"] = \"ⳏ\",\n\t[\"Ⳑ\"] = \"ⳑ\",\n\t[\"Ⳓ\"] = \"ⳓ\",\n\t[\"Ⳕ\"] = \"ⳕ\",\n\t[\"Ⳗ\"] = \"ⳗ\",\n\t[\"Ⳙ\"] = \"ⳙ\",\n\t[\"Ⳛ\"] = \"ⳛ\",\n\t[\"Ⳝ\"] = \"ⳝ\",\n\t[\"Ⳟ\"] = \"ⳟ\",\n\t[\"Ⳡ\"] = \"ⳡ\",\n\t[\"Ⳣ\"] = \"ⳣ\",\n\t[\"Ａ\"] = \"ａ\",\n\t[\"Ｂ\"] = \"ｂ\",\n\t[\"Ｃ\"] = \"ｃ\",\n\t[\"Ｄ\"] = \"ｄ\",\n\t[\"Ｅ\"] = \"ｅ\",\n\t[\"Ｆ\"] = \"ｆ\",\n\t[\"Ｇ\"] = \"ｇ\",\n\t[\"Ｈ\"] = \"ｈ\",\n\t[\"Ｉ\"] = \"ｉ\",\n\t[\"Ｊ\"] = \"ｊ\",\n\t[\"Ｋ\"] = \"ｋ\",\n\t[\"Ｌ\"] = \"ｌ\",\n\t[\"Ｍ\"] = \"ｍ\",\n\t[\"Ｎ\"] = \"ｎ\",\n\t[\"Ｏ\"] = \"ｏ\",\n\t[\"Ｐ\"] = \"ｐ\",\n\t[\"Ｑ\"] = \"ｑ\",\n\t[\"Ｒ\"] = \"ｒ\",\n\t[\"Ｓ\"] = \"ｓ\",\n\t[\"Ｔ\"] = \"ｔ\",\n\t[\"Ｕ\"] = \"ｕ\",\n\t[\"Ｖ\"] = \"ｖ\",\n\t[\"Ｗ\"] = \"ｗ\",\n\t[\"Ｘ\"] = \"ｘ\",\n\t[\"Ｙ\"] = \"ｙ\",\n\t[\"Ｚ\"] = \"ｚ\",\n\t[\"𐐀\"] = \"𐐨\",\n\t[\"𐐁\"] = \"𐐩\",\n\t[\"𐐂\"] = \"𐐪\",\n\t[\"𐐃\"] = \"𐐫\",\n\t[\"𐐄\"] = \"𐐬\",\n\t[\"𐐅\"] = \"𐐭\",\n\t[\"𐐆\"] = \"𐐮\",\n\t[\"𐐇\"] = \"𐐯\",\n\t[\"𐐈\"] = \"𐐰\",\n\t[\"𐐉\"] = \"𐐱\",\n\t[\"𐐊\"] = \"𐐲\",\n\t[\"𐐋\"] = \"𐐳\",\n\t[\"𐐌\"] = \"𐐴\",\n\t[\"𐐍\"] = \"𐐵\",\n\t[\"𐐎\"] = \"𐐶\",\n\t[\"𐐏\"] = \"𐐷\",\n\t[\"𐐐\"] = \"𐐸\",\n\t[\"𐐑\"] = \"𐐹\",\n\t[\"𐐒\"] = \"𐐺\",\n\t[\"𐐓\"] = \"𐐻\",\n\t[\"𐐔\"] = \"𐐼\",\n\t[\"𐐕\"] = \"𐐽\",\n\t[\"𐐖\"] = \"𐐾\",\n\t[\"𐐗\"] = \"𐐿\",\n\t[\"𐐘\"] = \"𐑀\",\n\t[\"𐐙\"] = \"𐑁\",\n\t[\"𐐚\"] = \"𐑂\",\n\t[\"𐐛\"] = \"𐑃\",\n\t[\"𐐜\"] = \"𐑄\",\n\t[\"𐐝\"] = \"𐑅\",\n\t[\"𐐞\"] = \"𐑆\",\n\t[\"𐐟\"] = \"𐑇\",\n\t[\"𐐠\"] = \"𐑈\",\n\t[\"𐐡\"] = \"𐑉\",\n\t[\"𐐢\"] = \"𐑊\",\n\t[\"𐐣\"] = \"𐑋\",\n\t[\"𐐤\"] = \"𐑌\",\n\t[\"𐐥\"] = \"𐑍\",\n\t[\"𐐦\"] = \"𐑎\",\n\t[\"𐐧\"] = \"𐑏\",\n}\n\n"
  },
  {
    "path": "gamemode/core/libs/thirdparty/sh_cami.lua",
    "content": "--[[\nCAMI - Common Admin Mod Interface.\nCopyright 2020 CAMI Contributors\n\nMakes admin mods intercompatible and provides an abstract privilege interface\nfor third party addons.\n\nFollows the specification on this page:\nhttps://github.com/glua/CAMI/blob/master/README.md\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n]]\n\n-- Version number in YearMonthDay format.\nlocal version = 20211019\n\nif CAMI and CAMI.Version >= version then return end\n\nCAMI = CAMI or {}\nCAMI.Version = version\n\n\n--- @class CAMI_USERGROUP\n--- defines the charactaristics of a usergroup\n--- @field Name string @The name of the usergroup\n--- @field Inherits string @The name of the usergroup this usergroup inherits from\n--- @field CAMI_Source string @The source specified by the admin mod which registered this usergroup (if any, converted to a string)\n\n--- @class CAMI_PRIVILEGE\n--- defines the charactaristics of a privilege\n--- @field Name string @The name of the privilege\n--- @field MinAccess \"'user'\" | \"'admin'\" | \"'superadmin'\" @Default group that should have this privilege\n--- @field Description string | nil @Optional text describing the purpose of the privilege\nlocal CAMI_PRIVILEGE = {}\n--- Optional function to check if a player has access to this privilege\n--- (and optionally execute it on another player)\n---\n--- ⚠ **Warning**: This function may not be called by all admin mods\n--- @param actor GPlayer @The player\n--- @param target GPlayer | nil @Optional - the target\n--- @return boolean @If they can or not\n--- @return string | nil @Optional reason\nfunction CAMI_PRIVILEGE:HasAccess(actor, target)\nend\n\n--- Contains the registered CAMI_USERGROUP usergroup structures.\n--- Indexed by usergroup name.\n--- @type CAMI_USERGROUP[]\nlocal usergroups = CAMI.GetUsergroups and CAMI.GetUsergroups() or {\n    user = {\n        Name = \"user\",\n        Inherits = \"user\",\n        CAMI_Source = \"Garry's Mod\",\n    },\n    admin = {\n        Name = \"admin\",\n        Inherits = \"user\",\n        CAMI_Source = \"Garry's Mod\",\n    },\n    superadmin = {\n        Name = \"superadmin\",\n        Inherits = \"admin\",\n        CAMI_Source = \"Garry's Mod\",\n    }\n}\n\n--- Contains the registered CAMI_PRIVILEGE privilege structures.\n--- Indexed by privilege name.\n--- @type CAMI_PRIVILEGE[]\nlocal privileges = CAMI.GetPrivileges and CAMI.GetPrivileges() or {}\n\n--- Registers a usergroup with CAMI.\n---\n--- Use the source parameter to make sure CAMI.RegisterUsergroup function and\n--- the CAMI.OnUsergroupRegistered hook don't cause an infinite loop\n--- @param usergroup CAMI_USERGROUP @The structure for the usergroup you want to register\n--- @param source any @Identifier for your own admin mod. Can be anything.\n--- @return CAMI_USERGROUP @The usergroup given as an argument\nfunction CAMI.RegisterUsergroup(usergroup, source)\n    if source then\n        usergroup.CAMI_Source = tostring(source)\n    end\n    usergroups[usergroup.Name] = usergroup\n\n    hook.Call(\"CAMI.OnUsergroupRegistered\", nil, usergroup, source)\n    return usergroup\nend\n\n--- Unregisters a usergroup from CAMI. This will call a hook that will notify\n--- all other admin mods of the removal.\n---\n--- ⚠ **Warning**: Call only when the usergroup is to be permanently removed.\n---\n--- Use the source parameter to make sure CAMI.UnregisterUsergroup function and\n--- the CAMI.OnUsergroupUnregistered hook don't cause an infinite loop\n--- @param usergroupName string @The name of the usergroup.\n--- @param source any @Identifier for your own admin mod. Can be anything.\n--- @return boolean @Whether the unregistering succeeded.\nfunction CAMI.UnregisterUsergroup(usergroupName, source)\n    if not usergroups[usergroupName] then return false end\n\n    local usergroup = usergroups[usergroupName]\n    usergroups[usergroupName] = nil\n\n    hook.Call(\"CAMI.OnUsergroupUnregistered\", nil, usergroup, source)\n\n    return true\nend\n\n--- Retrieves all registered usergroups.\n--- @return CAMI_USERGROUP[] @Usergroups indexed by their names.\nfunction CAMI.GetUsergroups()\n    return usergroups\nend\n\n--- Receives information about a usergroup.\n--- @param usergroupName string\n--- @return CAMI_USERGROUP | nil @Returns nil when the usergroup does not exist.\nfunction CAMI.GetUsergroup(usergroupName)\n    return usergroups[usergroupName]\nend\n\n--- Checks to see if potentialAncestor is an ancestor of usergroupName.\n--- All usergroups are ancestors of themselves.\n---\n--- Examples:\n--- * `user` is an ancestor of `admin` and also `superadmin`\n--- * `admin` is an ancestor of `superadmin`, but not `user`\n--- @param usergroupName string @The usergroup to query\n--- @param potentialAncestor string @The ancestor to query\n--- @return boolean @Whether usergroupName inherits potentialAncestor.\nfunction CAMI.UsergroupInherits(usergroupName, potentialAncestor)\n    repeat\n        if usergroupName == potentialAncestor then return true end\n\n        usergroupName = usergroups[usergroupName] and\n                         usergroups[usergroupName].Inherits or\n                         usergroupName\n    until not usergroups[usergroupName] or\n          usergroups[usergroupName].Inherits == usergroupName\n\n    -- One can only be sure the usergroup inherits from user if the\n    -- usergroup isn't registered.\n    return usergroupName == potentialAncestor or potentialAncestor == \"user\"\nend\n\n--- Find the base group a usergroup inherits from.\n---\n--- This function traverses down the inheritence chain, so for example if you have\n--- `user` -> `group1` -> `group2`\n--- this function will return `user` if you pass it `group2`.\n---\n--- ℹ **NOTE**: All usergroups must eventually inherit either user, admin or superadmin.\n--- @param usergroupName string @The name of the usergroup\n--- @return \"'user'\" | \"'admin'\" | \"'superadmin'\" @The name of the root usergroup\nfunction CAMI.InheritanceRoot(usergroupName)\n    if not usergroups[usergroupName] then return end\n\n    local inherits = usergroups[usergroupName].Inherits\n    while inherits ~= usergroups[usergroupName].Inherits do\n        usergroupName = usergroups[usergroupName].Inherits\n    end\n\n    return usergroupName\nend\n\n--- Registers an addon privilege with CAMI.\n---\n--- ⚠ **Warning**: This should only be used by addons. Admin mods must *NOT*\n---  register their privileges using this function.\n--- @param privilege CAMI_PRIVILEGE\n--- @return CAMI_PRIVILEGE @The privilege given as argument.\nfunction CAMI.RegisterPrivilege(privilege)\n    privileges[privilege.Name] = privilege\n\n    hook.Call(\"CAMI.OnPrivilegeRegistered\", nil, privilege)\n\n    return privilege\nend\n\n--- Unregisters a privilege from CAMI.\n--- This will call a hook that will notify any admin mods of the removal.\n---\n--- ⚠ **Warning**: Call only when the privilege is to be permanently removed.\n--- @param privilegeName string @The name of the privilege.\n--- @return boolean @Whether the unregistering succeeded.\nfunction CAMI.UnregisterPrivilege(privilegeName)\n    if not privileges[privilegeName] then return false end\n\n    local privilege = privileges[privilegeName]\n    privileges[privilegeName] = nil\n\n    hook.Call(\"CAMI.OnPrivilegeUnregistered\", nil, privilege)\n\n    return true\nend\n\n--- Retrieves all registered privileges.\n--- @return CAMI_PRIVILEGE[] @All privileges indexed by their names.\nfunction CAMI.GetPrivileges()\n    return privileges\nend\n\n--- Receives information about a privilege.\n--- @param privilegeName string\n--- @return CAMI_PRIVILEGE | nil\nfunction CAMI.GetPrivilege(privilegeName)\n    return privileges[privilegeName]\nend\n\n-- Default access handler\nlocal defaultAccessHandler = {[\"CAMI.PlayerHasAccess\"] =\n    function(_, actorPly, privilegeName, callback, targetPly, extraInfoTbl)\n        -- The server always has access in the fallback\n        if not IsValid(actorPly) then return callback(true, \"Fallback.\") end\n\n        local priv = privileges[privilegeName]\n\n        local fallback = extraInfoTbl and (\n            not extraInfoTbl.Fallback and actorPly:IsAdmin() or\n            extraInfoTbl.Fallback == \"user\" and true or\n            extraInfoTbl.Fallback == \"admin\" and actorPly:IsAdmin() or\n            extraInfoTbl.Fallback == \"superadmin\" and actorPly:IsSuperAdmin())\n\n\n        if not priv then return callback(fallback, \"Fallback.\") end\n\n        local hasAccess =\n            priv.MinAccess == \"user\" or\n            priv.MinAccess == \"admin\" and actorPly:IsAdmin() or\n            priv.MinAccess == \"superadmin\" and actorPly:IsSuperAdmin()\n\n        if hasAccess and priv.HasAccess then\n            hasAccess = priv:HasAccess(actorPly, targetPly)\n        end\n\n        callback(hasAccess, \"Fallback.\")\n    end,\n    [\"CAMI.SteamIDHasAccess\"] =\n    function(_, _, _, callback)\n        callback(false, \"No information available.\")\n    end\n}\n\n--- @class CAMI_ACCESS_EXTRA_INFO\n--- @field Fallback \"'user'\" | \"'admin'\" | \"'superadmin'\" @Fallback status for if the privilege doesn't exist. Defaults to `admin`.\n--- @field IgnoreImmunity boolean @Ignore any immunity mechanisms an admin mod might have.\n--- @field CommandArguments table @Extra arguments that were given to the privilege command.\n\n--- Checks if a player has access to a privilege\n--- (and optionally can execute it on targetPly)\n---\n--- This function is designed to be asynchronous but will be invoked\n---  synchronously if no callback is passed.\n---\n--- ⚠ **Warning**: If the currently installed admin mod does not support\n---                 synchronous queries, this function will throw an error!\n--- @param actorPly GPlayer @The player to query\n--- @param privilegeName string @The privilege to query\n--- @param callback fun(hasAccess: boolean, reason: string|nil) @Callback to receive the answer, or nil for synchronous\n--- @param targetPly GPlayer | nil @Optional - target for if the privilege effects another player (eg kick/ban)\n--- @param extraInfoTbl CAMI_ACCESS_EXTRA_INFO | nil @Table of extra information for the admin mod\n--- @return boolean | nil @Synchronous only - if the player has the privilege\n--- @return string | nil @Synchronous only - optional reason from admin mod\nfunction CAMI.PlayerHasAccess(actorPly, privilegeName, callback, targetPly,\nextraInfoTbl)\n    local hasAccess, reason = nil, nil\n    local callback_ = callback or function(hA, r) hasAccess, reason = hA, r end\n\n    hook.Call(\"CAMI.PlayerHasAccess\", defaultAccessHandler, actorPly,\n        privilegeName, callback_, targetPly, extraInfoTbl)\n\n    if callback ~= nil then return end\n\n    if hasAccess == nil then\n        local err = [[The function CAMI.PlayerHasAccess was used to find out\n        whether Player %s has privilege \"%s\", but an admin mod did not give an\n        immediate answer!]]\n        error(string.format(err,\n            actorPly:IsPlayer() and actorPly:Nick() or tostring(actorPly),\n            privilegeName))\n    end\n\n    return hasAccess, reason\nend\n\n--- Get all the players on the server with a certain privilege\n--- (and optionally who can execute it on targetPly)\n---\n--- ℹ **NOTE**: This is an asynchronous function!\n--- @param privilegeName string @The privilege to query\n--- @param callback fun(players: GPlayer[]) @Callback to receive the answer\n--- @param targetPly GPlayer | nil @Optional - target for if the privilege effects another player (eg kick/ban)\n--- @param extraInfoTbl CAMI_ACCESS_EXTRA_INFO | nil @Table of extra information for the admin mod\nfunction CAMI.GetPlayersWithAccess(privilegeName, callback, targetPly,\nextraInfoTbl)\n    local allowedPlys = {}\n    local allPlys = player.GetAll()\n    local countdown = #allPlys\n\n    local function onResult(ply, hasAccess, _)\n        countdown = countdown - 1\n\n        if hasAccess then table.insert(allowedPlys, ply) end\n        if countdown == 0 then callback(allowedPlys) end\n    end\n\n    for _, ply in ipairs(allPlys) do\n        CAMI.PlayerHasAccess(ply, privilegeName,\n            function(...) onResult(ply, ...) end,\n            targetPly, extraInfoTbl)\n    end\nend\n\n--- @class CAMI_STEAM_ACCESS_EXTRA_INFO\n--- @field IgnoreImmunity boolean @Ignore any immunity mechanisms an admin mod might have.\n--- @field CommandArguments table @Extra arguments that were given to the privilege command.\n\n--- Checks if a (potentially offline) SteamID has access to a privilege\n--- (and optionally if they can execute it on a target SteamID)\n---\n--- ℹ **NOTE**: This is an asynchronous function!\n--- @param actorSteam string | nil @The SteamID to query\n--- @param privilegeName string @The privilege to query\n--- @param callback fun(hasAccess: boolean, reason: string|nil) @Callback to receive  the answer\n--- @param targetSteam string | nil @Optional - target SteamID for if the privilege effects another player (eg kick/ban)\n--- @param extraInfoTbl CAMI_STEAM_ACCESS_EXTRA_INFO | nil @Table of extra information for the admin mod\nfunction CAMI.SteamIDHasAccess(actorSteam, privilegeName, callback,\ntargetSteam, extraInfoTbl)\n    hook.Call(\"CAMI.SteamIDHasAccess\", defaultAccessHandler, actorSteam,\n        privilegeName, callback, targetSteam, extraInfoTbl)\nend\n\n--- Signify that your admin mod has changed the usergroup of a player. This\n--- function communicates to other admin mods what it thinks the usergroup\n--- of a player should be.\n---\n--- Listen to the hook to receive the usergroup changes of other admin mods.\n--- @param ply GPlayer @The player for which the usergroup is changed\n--- @param old string @The previous usergroup of the player.\n--- @param new string @The new usergroup of the player.\n--- @param source any @Identifier for your own admin mod. Can be anything.\nfunction CAMI.SignalUserGroupChanged(ply, old, new, source)\n    hook.Call(\"CAMI.PlayerUsergroupChanged\", nil, ply, old, new, source)\nend\n\n--- Signify that your admin mod has changed the usergroup of a disconnected\n--- player. This communicates to other admin mods what it thinks the usergroup\n--- of a player should be.\n---\n--- Listen to the hook to receive the usergroup changes of other admin mods.\n--- @param steamId string @The steam ID of the player for which the usergroup is changed\n--- @param old string @The previous usergroup of the player.\n--- @param new string @The new usergroup of the player.\n--- @param source any @Identifier for your own admin mod. Can be anything.\nfunction CAMI.SignalSteamIDUserGroupChanged(steamId, old, new, source)\n    hook.Call(\"CAMI.SteamIDUsergroupChanged\", nil, steamId, old, new, source)\nend"
  },
  {
    "path": "gamemode/core/libs/thirdparty/sh_date.lua",
    "content": "---------------------------------------------------------------------------------------\n-- Module for date and time calculations\n--\n-- Version 2.1.1\n-- Copyright (C) 2006, by Jas Latrix (jastejada@yahoo.com)\n-- Copyright (C) 2013-2014, by Thijs Schreijer\n-- Licensed under MIT, http://opensource.org/licenses/MIT\n-- https://github.com/Tieske/date\n\n--[[\nThe MIT License (MIT) http://opensource.org/licenses/MIT\n\nCopyright (c) 2013-2017 Thijs Schreijer\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n]]\n\n--[[ CONSTANTS ]]--\n  local HOURPERDAY  = 24\n  local MINPERHOUR  = 60\n  local MINPERDAY    = 1440  -- 24*60\n  local SECPERMIN   = 60\n  local SECPERHOUR  = 3600  -- 60*60\n  local SECPERDAY   = 86400 -- 24*60*60\n  local TICKSPERSEC = 1000000\n  local TICKSPERDAY = 86400000000\n  local TICKSPERHOUR = 3600000000\n  local TICKSPERMIN = 60000000\n  local DAYNUM_MAX =  365242500 -- Sat Jan 01 1000000 00:00:00\n  local DAYNUM_MIN = -365242500 -- Mon Jan 01 1000000 BCE 00:00:00\n  local DAYNUM_DEF =  0 -- Mon Jan 01 0001 00:00:00\n  local _;\n--[[ LOCAL ARE FASTER ]]--\n  local type     = type\n  local pairs    = pairs\n  local error    = error\n  local assert   = assert\n  local tonumber = tonumber\n  local tostring = tostring\n  local string   = string\n  local math     = math\n  local os       = os\n  local unpack   = unpack or table.unpack\n  local pack     = table.pack or function(...) return { n = select('#', ...), ... } end\n  local setmetatable = setmetatable\n  local getmetatable = getmetatable\n--[[ EXTRA FUNCTIONS ]]--\n  local fmt  = string.format\n  local lwr  = string.lower\n  local upr  = string.upper\n  local rep  = string.rep\n  local len  = string.len\n  local sub  = string.sub\n  local gsub = string.gsub\n  local gmatch = string.gmatch or string.gfind\n  local find = string.find\n  local ostime = os.time\n  local osdate = os.date\n  local floor = math.floor\n  local ceil  = math.ceil\n  local abs   = math.abs\n  -- removes the decimal part of a number\n  local function fix(n) n = tonumber(n) return n and ((n > 0 and floor or ceil)(n)) end\n  -- returns the modulo n % d;\n  local function mod(n,d) return n - d*floor(n/d) end\n  -- rounds a number;\n  local function round(n, d) d=d^10 return floor((n*d)+.5)/d end\n  -- rounds a number to whole;\n  local function whole(n)return floor(n+.5)end\n  -- is `str` in string list `tbl`, `ml` is the minimun len\n  local function inlist(str, tbl, ml, tn)\n    local sl = len(str)\n    if sl < (ml or 0) then return nil end\n    str = lwr(str)\n    for k, v in pairs(tbl) do\n      if str == lwr(sub(v, 1, sl)) then\n        if tn then tn[0] = k end\n        return k\n      end\n    end\n  end\n  local function fnil() end\n  local function fret(x)return x;end\n--[[ DATE FUNCTIONS ]]--\n  local DATE_EPOCH -- to be set later\n  local sl_weekdays = {\n    [0]=\"Sunday\",[1]=\"Monday\",[2]=\"Tuesday\",[3]=\"Wednesday\",[4]=\"Thursday\",[5]=\"Friday\",[6]=\"Saturday\",\n    [7]=\"Sun\",[8]=\"Mon\",[9]=\"Tue\",[10]=\"Wed\",[11]=\"Thu\",[12]=\"Fri\",[13]=\"Sat\",\n  }\n  local sl_meridian = {[-1]=\"AM\", [1]=\"PM\"}\n  local sl_months = {\n    [00]=\"January\", [01]=\"February\", [02]=\"March\",\n    [03]=\"April\",   [04]=\"May\",      [05]=\"June\",\n    [06]=\"July\",    [07]=\"August\",   [08]=\"September\",\n    [09]=\"October\", [10]=\"November\", [11]=\"December\",\n    [12]=\"Jan\", [13]=\"Feb\", [14]=\"Mar\",\n    [15]=\"Apr\", [16]=\"May\", [17]=\"Jun\",\n    [18]=\"Jul\", [19]=\"Aug\", [20]=\"Sep\",\n    [21]=\"Oct\", [22]=\"Nov\", [23]=\"Dec\",\n  }\n  -- added the '.2'  to avoid collision, use `fix` to remove\n  local sl_timezone = {\n    [000]=\"utc\",    [0.2]=\"gmt\",\n    [300]=\"est\",    [240]=\"edt\",\n    [360]=\"cst\",  [300.2]=\"cdt\",\n    [420]=\"mst\",  [360.2]=\"mdt\",\n    [480]=\"pst\",  [420.2]=\"pdt\",\n  }\n  -- set the day fraction resolution\n  local function setticks(t)\n    TICKSPERSEC = t;\n    TICKSPERDAY = SECPERDAY*TICKSPERSEC\n    TICKSPERHOUR= SECPERHOUR*TICKSPERSEC\n    TICKSPERMIN = SECPERMIN*TICKSPERSEC\n  end\n  -- is year y leap year?\n  local function isleapyear(y) -- y must be int!\n    return (mod(y, 4) == 0 and (mod(y, 100) ~= 0 or mod(y, 400) == 0))\n  end\n  -- day since year 0\n  local function dayfromyear(y) -- y must be int!\n    return 365*y + floor(y/4) - floor(y/100) + floor(y/400)\n  end\n  -- day number from date, month is zero base\n  local function makedaynum(y, m, d)\n    local mm = mod(mod(m,12) + 10, 12)\n    return dayfromyear(y + floor(m/12) - floor(mm/10)) + floor((mm*306 + 5)/10) + d - 307\n    --local yy = y + floor(m/12) - floor(mm/10)\n    --return dayfromyear(yy) + floor((mm*306 + 5)/10) + (d - 1)\n  end\n  -- date from day number, month is zero base\n  local function breakdaynum(g)\n    local g = g + 306\n    local y = floor((10000*g + 14780)/3652425)\n    local d = g - dayfromyear(y)\n    if d < 0 then y = y - 1; d = g - dayfromyear(y) end\n    local mi = floor((100*d + 52)/3060)\n    return (floor((mi + 2)/12) + y), mod(mi + 2,12), (d - floor((mi*306 + 5)/10) + 1)\n  end\n  --[[ for floats or int32 Lua Number data type\n  local function breakdaynum2(g)\n    local g, n = g + 306;\n    local n400 = floor(g/DI400Y);n = mod(g,DI400Y);\n    local n100 = floor(n/DI100Y);n = mod(n,DI100Y);\n    local n004 = floor(n/DI4Y);   n = mod(n,DI4Y);\n    local n001 = floor(n/365);   n = mod(n,365);\n    local y = (n400*400) + (n100*100) + (n004*4) + n001  - ((n001 == 4 or n100 == 4) and 1 or 0)\n    local d = g - dayfromyear(y)\n    local mi = floor((100*d + 52)/3060)\n    return (floor((mi + 2)/12) + y), mod(mi + 2,12), (d - floor((mi*306 + 5)/10) + 1)\n  end\n  ]]\n  -- day fraction from time\n  local function makedayfrc(h,r,s,t)\n    return ((h*60 + r)*60 + s)*TICKSPERSEC + t\n  end\n  -- time from day fraction\n  local function breakdayfrc(df)\n    return\n      mod(floor(df/TICKSPERHOUR),HOURPERDAY),\n      mod(floor(df/TICKSPERMIN ),MINPERHOUR),\n      mod(floor(df/TICKSPERSEC ),SECPERMIN),\n      mod(df,TICKSPERSEC)\n  end\n  -- weekday sunday = 0, monday = 1 ...\n  local function weekday(dn) return mod(dn + 1, 7) end\n  -- yearday 0 based ...\n  local function yearday(dn)\n     return dn - dayfromyear((breakdaynum(dn))-1)\n  end\n  -- parse v as a month\n  local function getmontharg(v)\n    local m = tonumber(v);\n    return (m and fix(m - 1)) or inlist(tostring(v) or \"\", sl_months, 2)\n  end\n  -- get daynum of isoweek one of year y\n  local function isow1(y)\n    local f = makedaynum(y, 0, 4) -- get the date for the 4-Jan of year `y`\n    local d = weekday(f)\n    d = d == 0 and 7 or d -- get the ISO day number, 1 == Monday, 7 == Sunday\n    return f + (1 - d)\n  end\n  local function isowy(dn)\n    local w1;\n    local y = (breakdaynum(dn))\n    if dn >= makedaynum(y, 11, 29) then\n      w1 = isow1(y + 1);\n      if dn < w1 then\n        w1 = isow1(y);\n      else\n          y = y + 1;\n      end\n    else\n      w1 = isow1(y);\n      if dn < w1 then\n        w1 = isow1(y-1)\n        y = y - 1\n      end\n    end\n    return floor((dn-w1)/7)+1, y\n  end\n  local function isoy(dn)\n    local y = (breakdaynum(dn))\n    return y + (((dn >= makedaynum(y, 11, 29)) and (dn >= isow1(y + 1))) and 1 or (dn < isow1(y) and -1 or 0))\n  end\n  local function makedaynum_isoywd(y,w,d)\n    return isow1(y) + 7*w + d - 8 -- simplified: isow1(y) + ((w-1)*7) + (d-1)\n  end\n--[[ THE DATE MODULE ]]--\n  local fmtstr  = \"%x %X\";\n--#if not DATE_OBJECT_AFX then\n  local date = {}\n  setmetatable(date, date)\n-- Version:  VMMMRRRR; V-Major, M-Minor, R-Revision;  e.g. 5.45.321 == 50450321\n  date.version = 20010001 -- 2.1.1\n--#end -- not DATE_OBJECT_AFX\n--[[ THE DATE OBJECT ]]--\n  local dobj = {}\n  dobj.__index = dobj\n  dobj.__metatable = dobj\n  -- shout invalid arg\n  local function date_error_arg() return error(\"invalid argument(s)\",0) end\n  -- create new date object\n  local function date_new(dn, df)\n    return setmetatable({daynum=dn, dayfrc=df}, dobj)\n  end\n  -- is `v` a date object?\n  local function date_isdobj(v)\n    return (istable(v) and getmetatable(v) == dobj) and v\n  end\n\n--#if not NO_LOCAL_TIME_SUPPORT then\n  -- magic year table\n  local date_epoch, yt;\n  local function getequivyear(y)\n    assert(not yt)\n    yt = {}\n    local de, dw, dy = date_epoch:copy()\n    for i = 0, 3000 do\n      de:setyear(de:getyear() + 1, 1, 1)\n      dy = de:getyear()\n      dw = de:getweekday() * (isleapyear(dy) and  -1 or 1)\n      if not yt[dw] then yt[dw] = dy end  --print(de)\n      if yt[1] and yt[2] and yt[3] and yt[4] and yt[5] and yt[6] and yt[7] and yt[-1] and yt[-2] and yt[-3] and yt[-4] and yt[-5] and yt[-6] and yt[-7] then\n        getequivyear = function(y)  return yt[ (weekday(makedaynum(y, 0, 1)) + 1) * (isleapyear(y) and  -1 or 1) ]  end\n        return getequivyear(y)\n      end\n    end\n  end\n  -- TimeValue from daynum and dayfrc\n  local function dvtotv(dn, df)\n    return fix(dn - DATE_EPOCH) * SECPERDAY  + (df/1000)\n  end\n  -- TimeValue from date and time\n  local function totv(y,m,d,h,r,s)\n    return (makedaynum(y, m, d) - DATE_EPOCH) * SECPERDAY  + ((h*60 + r)*60 + s)\n  end\n  -- TimeValue from TimeTable\n  local function tmtotv(tm)\n    return tm and totv(tm.year, tm.month - 1, tm.day, tm.hour, tm.min, tm.sec)\n  end\n  -- Returns the bias in seconds of utc time daynum and dayfrc\n  local function getbiasutc2(self)\n    local y,m,d = breakdaynum(self.daynum)\n    local h,r,s = breakdayfrc(self.dayfrc)\n    local tvu = totv(y,m,d,h,r,s) -- get the utc TimeValue of date and time\n    local tml = osdate(\"*t\", tvu) -- get the local TimeTable of tvu\n    if (not tml) or (tml.year > (y+1) or tml.year < (y-1)) then -- failed try the magic\n      y = getequivyear(y)\n      tvu = totv(y,m,d,h,r,s)\n      tml = osdate(\"*t\", tvu)\n    end\n    local tvl = tmtotv(tml)\n    if tvu and tvl then\n      return tvu - tvl, tvu, tvl\n    else\n      return error(\"failed to get bias from utc time\")\n    end\n  end\n  -- Returns the bias in seconds of local time daynum and dayfrc\n  local function getbiasloc2(daynum, dayfrc)\n    local tvu\n    -- extract date and time\n    local y,m,d = breakdaynum(daynum)\n    local h,r,s = breakdayfrc(dayfrc)\n    -- get equivalent TimeTable\n    local tml = {year=y, month=m+1, day=d, hour=h, min=r, sec=s}\n    -- get equivalent TimeValue\n    local tvl = tmtotv(tml)\n\n    local function chkutc()\n      tml.isdst =  nil; local tvug = ostime(tml) if tvug and (tvl == tmtotv(osdate(\"*t\", tvug))) then tvu = tvug return end\n      tml.isdst = true; local tvud = ostime(tml) if tvud and (tvl == tmtotv(osdate(\"*t\", tvud))) then tvu = tvud return end\n      tvu = tvud or tvug\n    end\n    chkutc()\n    if not tvu then\n      tml.year = getequivyear(y)\n      tvl = tmtotv(tml)\n      chkutc()\n    end\n    return ((tvu and tvl) and (tvu - tvl)) or error(\"failed to get bias from local time\"), tvu, tvl\n  end\n--#end -- not NO_LOCAL_TIME_SUPPORT\n\n--#if not DATE_OBJECT_AFX then\n  -- the date parser\n  local strwalker = {} -- ^Lua regular expression is not as powerful as Perl$\n  strwalker.__index = strwalker\n  local function newstrwalker(s)return setmetatable({s=s, i=1, e=1, c=len(s)}, strwalker) end\n  function strwalker:aimchr() return \"\\n\" .. self.s .. \"\\n\" .. rep(\".\",self.e-1) .. \"^\" end\n  function strwalker:finish() return self.i > self.c  end\n  function strwalker:back()  self.i = self.e return self  end\n  function strwalker:restart() self.i, self.e = 1, 1 return self end\n  function strwalker:match(s)  return (find(self.s, s, self.i)) end\n  function strwalker:__call(s, f)-- print(\"strwalker:__call \"..s..self:aimchr())\n    local is, ie; is, ie, self[1], self[2], self[3], self[4], self[5] = find(self.s, s, self.i)\n    if is then self.e, self.i = self.i, 1+ie; if f then f(unpack(self)) end return self end\n  end\n   local function date_parse(str)\n    local y,m,d, h,r,s,  z,  w,u, j,  e,  k,  x,v,c,  chkfin,  dn,df;\n    local sw = newstrwalker(gsub(gsub(str, \"(%b())\", \"\"),\"^(%s*)\",\"\")) -- remove comment, trim leading space\n    --local function error_out() print(y,m,d,h,r,s) end\n    local function error_dup(q) --[[error_out()]] error(\"duplicate value: \" .. (q or \"\") .. sw:aimchr()) end\n    local function error_syn(q) --[[error_out()]] error(\"syntax error: \" .. (q or \"\") .. sw:aimchr()) end\n    local function error_inv(q) --[[error_out()]] error(\"invalid date: \" .. (q or \"\") .. sw:aimchr()) end\n    local function sety(q) y = y and error_dup() or tonumber(q); end\n    local function setm(q) m = (m or w or j) and error_dup(m or w or j) or tonumber(q) end\n    local function setd(q) d = d and error_dup() or tonumber(q) end\n    local function seth(q) h = h and error_dup() or tonumber(q) end\n    local function setr(q) r = r and error_dup() or tonumber(q) end\n    local function sets(q) s = s and error_dup() or tonumber(q) end\n    local function adds(q) s = s + tonumber(q) end\n    local function setj(q) j = (m or w or j) and error_dup() or tonumber(q); end\n    local function setz(q) z = (z ~= 0 and z) and error_dup() or q end\n    local function setzn(zs,zn) zn = tonumber(zn); setz( ((zn<24) and (zn*60) or (mod(zn,100) + floor(zn/100) * 60))*( zs=='+' and -1 or 1) ) end\n    local function setzc(zs,zh,zm) setz( ((tonumber(zh)*60) + tonumber(zm))*( zs=='+' and -1 or 1) ) end\n\n    if not (sw(\"^(%d%d%d%d)\",sety) and (sw(\"^(%-?)(%d%d)%1(%d%d)\",function(_,a,b) setm(tonumber(a)); setd(tonumber(b)) end) or sw(\"^(%-?)[Ww](%d%d)%1(%d?)\",function(_,a,b) w, u = tonumber(a), tonumber(b or 1) end) or sw(\"^%-?(%d%d%d)\",setj) or sw(\"^%-?(%d%d)\",function(a) setm(a);setd(1) end))\n    and ((sw(\"^%s*[Tt]?(%d%d):?\",seth) and sw(\"^(%d%d):?\",setr) and sw(\"^(%d%d)\",sets) and sw(\"^(%.%d+)\",adds))\n      or sw:finish() or (sw\"^%s*$\" or sw\"^%s*[Zz]%s*$\" or sw(\"^%s-([%+%-])(%d%d):?(%d%d)%s*$\",setzc) or sw(\"^%s*([%+%-])(%d%d)%s*$\",setzn))\n      )  )\n    then --print(y,m,d,h,r,s,z,w,u,j)\n    sw:restart(); y,m,d,h,r,s,z,w,u,j = nil;\n      repeat -- print(sw:aimchr())\n        if sw(\"^[tT:]?%s*(%d%d?):\",seth) then --print(\"$Time\")\n          _ = sw(\"^%s*(%d%d?)\",setr) and sw(\"^%s*:%s*(%d%d?)\",sets) and sw(\"^(%.%d+)\",adds)\n        elseif sw(\"^(%d+)[/\\\\%s,-]?%s*\") then --print(\"$Digits\")\n          x, c = tonumber(sw[1]), len(sw[1])\n          if (x >= 70) or (m and d and (not y)) or (c > 3) then\n            sety( x + ((x >= 100 or c>3)and 0 or 1900) )\n          else\n            if m then setd(x) else m = x end\n          end\n        elseif sw(\"^(%a+)[/\\\\%s,-]?%s*\") then --print(\"$Words\")\n          x = sw[1]\n          if inlist(x, sl_months,   2, sw) then\n            if m and (not d) and (not y) then d, m = m, false end\n            setm(mod(sw[0],12)+1)\n          elseif inlist(x, sl_timezone, 2, sw) then\n            c = fix(sw[0]) -- ignore gmt and utc\n            if c ~= 0 then setz(c, x) end\n          elseif inlist(x, sl_weekdays, 2, sw) then\n            k = sw[0]\n          else\n            sw:back()\n            -- am pm bce ad ce bc\n            if sw(\"^([bB])%s*(%.?)%s*[Cc]%s*(%2)%s*[Ee]%s*(%2)%s*\") or sw(\"^([bB])%s*(%.?)%s*[Cc]%s*(%2)%s*\") then\n              e = e and error_dup() or -1\n            elseif sw(\"^([aA])%s*(%.?)%s*[Dd]%s*(%2)%s*\") or sw(\"^([cC])%s*(%.?)%s*[Ee]%s*(%2)%s*\") then\n              e = e and error_dup() or 1\n            elseif sw(\"^([PApa])%s*(%.?)%s*[Mm]?%s*(%2)%s*\") then\n              x = lwr(sw[1]) -- there should be hour and it must be correct\n              if (not h) or (h > 12) or (h < 0) then return error_inv() end\n              if x == 'a' and h == 12 then h = 0 end -- am\n              if x == 'p' and h ~= 12 then h = h + 12 end -- pm\n            else error_syn() end\n          end\n        elseif not(sw(\"^([+-])(%d%d?):(%d%d)\",setzc) or sw(\"^([+-])(%d+)\",setzn) or sw(\"^[Zz]%s*$\")) then -- sw{\"([+-])\",{\"(%d%d?):(%d%d)\",\"(%d+)\"}}\n          error_syn(\"?\")\n        end\n      sw(\"^%s*\")  until sw:finish()\n    --else print(\"$Iso(Date|Time|Zone)\")\n    end\n    -- if date is given, it must be complete year, month & day\n    if (not y and not h) or ((m and not d) or (d and not m)) or ((m and w) or (m and j) or (j and w)) then return error_inv(\"!\") end\n    -- fix month\n    if m then m = m - 1 end\n    -- fix year if we are on BCE\n    if e and e < 0 and y > 0 then y = 1 - y end\n    --  create date object\n    dn = (y and ((w and makedaynum_isoywd(y,w,u)) or (j and makedaynum(y, 0, j)) or makedaynum(y, m, d))) or DAYNUM_DEF\n    df = makedayfrc(h or 0, r or 0, s or 0, 0) + ((z or 0)*TICKSPERMIN)\n    --print(\"Zone\",h,r,s,z,m,d,y,df)\n    return date_new(dn, df) -- no need to :normalize();\n   end\n  local function date_fromtable(v)\n    local y, m, d = fix(v.year), getmontharg(v.month), fix(v.day)\n    local h, r, s, t = tonumber(v.hour), tonumber(v.min), tonumber(v.sec), tonumber(v.ticks)\n    -- atleast there is time or complete date\n    if (y or m or d) and (not(y and m and d)) then return error(\"incomplete table\")  end\n    return (y or h or r or s or t) and date_new(y and makedaynum(y, m, d) or DAYNUM_DEF, makedayfrc(h or 0, r or 0, s or 0, t or 0))\n  end\n  local tmap = {\n    ['number'] = function(v) return date_epoch:copy():addseconds(v) end,\n    ['string'] = function(v) return date_parse(v) end,\n    ['boolean']= function(v) return date_fromtable(osdate(v and \"!*t\" or \"*t\")) end,\n    ['table']  = function(v) local ref = getmetatable(v) == dobj; return ref and v or date_fromtable(v), ref end\n  }\n  local function date_getdobj(v)\n    local o, r = (tmap[type(v)] or fnil)(v);\n    return (o and o:normalize() or error\"invalid date time value\"), r -- if r is true then o is a reference to a date obj\n  end\n--#end -- not DATE_OBJECT_AFX\n  local function date_from(...)\n    local arg = pack(...)\n    local y, m, d = fix(arg[1]), getmontharg(arg[2]), fix(arg[3])\n    local h, r, s, t = tonumber(arg[4] or 0), tonumber(arg[5] or 0), tonumber(arg[6] or 0), tonumber(arg[7] or 0)\n    if y and m and d and h and r and s and t then\n      return date_new(makedaynum(y, m, d), makedayfrc(h, r, s, t)):normalize()\n    else\n      return date_error_arg()\n    end\n  end\n\n --[[ THE DATE OBJECT METHODS ]]--\n  function dobj:normalize()\n    local dn, df = fix(self.daynum), self.dayfrc\n    self.daynum, self.dayfrc = dn + floor(df/TICKSPERDAY), mod(df, TICKSPERDAY)\n    return (dn >= DAYNUM_MIN and dn <= DAYNUM_MAX) and self or error(\"date beyond imposed limits:\"..self)\n  end\n\n  function dobj:getdate()  local y, m, d = breakdaynum(self.daynum) return y, m+1, d end\n  function dobj:gettime()  return breakdayfrc(self.dayfrc) end\n\n  function dobj:getclockhour() local h = self:gethours() return h>12 and mod(h,12) or (h==0 and 12 or h) end\n\n  function dobj:getyearday() return yearday(self.daynum) + 1 end\n  function dobj:getweekday() return weekday(self.daynum) + 1 end   -- in lua weekday is sunday = 1, monday = 2 ...\n\n  function dobj:getyear()   local r,_,_ = breakdaynum(self.daynum)  return r end\n  function dobj:getmonth() local _,r,_ = breakdaynum(self.daynum)  return r+1 end-- in lua month is 1 base\n  function dobj:getday()   local _,_,r = breakdaynum(self.daynum)  return r end\n  function dobj:gethours()  return mod(floor(self.dayfrc/TICKSPERHOUR),HOURPERDAY) end\n  function dobj:getminutes()  return mod(floor(self.dayfrc/TICKSPERMIN), MINPERHOUR) end\n  function dobj:getseconds()  return mod(floor(self.dayfrc/TICKSPERSEC ),SECPERMIN)  end\n  function dobj:getfracsec()  return mod(floor(self.dayfrc/TICKSPERSEC ),SECPERMIN)+(mod(self.dayfrc,TICKSPERSEC)/TICKSPERSEC) end\n  function dobj:getticks(u)  local x = mod(self.dayfrc,TICKSPERSEC) return u and ((x*u)/TICKSPERSEC) or x  end\n\n  function dobj:getweeknumber(wdb)\n    local wd, yd = weekday(self.daynum), yearday(self.daynum)\n    if wdb then\n      wdb = tonumber(wdb)\n      if wdb then\n        wd = mod(wd-(wdb-1),7)-- shift the week day base\n      else\n        return date_error_arg()\n      end\n    end\n    return (yd < wd and 0) or (floor(yd/7) + ((mod(yd, 7)>=wd) and 1 or 0))\n  end\n\n  function dobj:getisoweekday() return mod(weekday(self.daynum)-1,7)+1 end   -- sunday = 7, monday = 1 ...\n  function dobj:getisoweeknumber() return (isowy(self.daynum)) end\n  function dobj:getisoyear() return isoy(self.daynum)  end\n  function dobj:getisodate()\n    local w, y = isowy(self.daynum)\n    return y, w, self:getisoweekday()\n  end\n  function dobj:setisoyear(y, w, d)\n    local cy, cw, cd = self:getisodate()\n    if y then cy = fix(tonumber(y))end\n    if w then cw = fix(tonumber(w))end\n    if d then cd = fix(tonumber(d))end\n    if cy and cw and cd then\n      self.daynum = makedaynum_isoywd(cy, cw, cd)\n      return self:normalize()\n    else\n      return date_error_arg()\n    end\n  end\n\n  function dobj:setisoweekday(d)    return self:setisoyear(nil, nil, d) end\n  function dobj:setisoweeknumber(w,d)  return self:setisoyear(nil, w, d)  end\n\n  function dobj:setyear(y, m, d)\n    local cy, cm, cd = breakdaynum(self.daynum)\n    if y then cy = fix(tonumber(y))end\n    if m then cm = getmontharg(m)  end\n    if d then cd = fix(tonumber(d))end\n    if cy and cm and cd then\n      self.daynum  = makedaynum(cy, cm, cd)\n      return self:normalize()\n    else\n      return date_error_arg()\n    end\n  end\n\n  function dobj:setmonth(m, d)return self:setyear(nil, m, d) end\n  function dobj:setday(d)    return self:setyear(nil, nil, d) end\n\n  function dobj:sethours(h, m, s, t)\n    local ch,cm,cs,ck = breakdayfrc(self.dayfrc)\n    ch, cm, cs, ck = tonumber(h or ch), tonumber(m or cm), tonumber(s or cs), tonumber(t or ck)\n    if ch and cm and cs and ck then\n      self.dayfrc = makedayfrc(ch, cm, cs, ck)\n      return self:normalize()\n    else\n      return date_error_arg()\n    end\n  end\n\n  function dobj:setminutes(m,s,t)  return self:sethours(nil,   m,   s, t) end\n  function dobj:setseconds(s, t)  return self:sethours(nil, nil,   s, t) end\n  function dobj:setticks(t)    return self:sethours(nil, nil, nil, t) end\n\n  function dobj:spanticks()  return (self.daynum*TICKSPERDAY + self.dayfrc) end\n  function dobj:spanseconds()  return (self.daynum*TICKSPERDAY + self.dayfrc)/TICKSPERSEC  end\n  function dobj:spanminutes()  return (self.daynum*TICKSPERDAY + self.dayfrc)/TICKSPERMIN  end\n  function dobj:spanhours()  return (self.daynum*TICKSPERDAY + self.dayfrc)/TICKSPERHOUR end\n  function dobj:spandays()  return (self.daynum*TICKSPERDAY + self.dayfrc)/TICKSPERDAY  end\n\n  function dobj:addyears(y, m, d)\n    local cy, cm, cd = breakdaynum(self.daynum)\n    if y then y = fix(tonumber(y))else y = 0 end\n    if m then m = fix(tonumber(m))else m = 0 end\n    if d then d = fix(tonumber(d))else d = 0 end\n    if y and m and d then\n      self.daynum  = makedaynum(cy+y, cm+m, cd+d)\n      return self:normalize()\n    else\n      return date_error_arg()\n    end\n  end\n\n  function dobj:addmonths(m, d)\n    return self:addyears(nil, m, d)\n  end\n\n  local function dobj_adddayfrc(self,n,pt,pd)\n    n = tonumber(n)\n    if n then\n      local x = floor(n/pd);\n      self.daynum = self.daynum + x;\n      self.dayfrc = self.dayfrc + (n-x*pd)*pt;\n      return self:normalize()\n    else\n      return date_error_arg()\n    end\n  end\n  function dobj:adddays(n)  return dobj_adddayfrc(self,n,TICKSPERDAY,1) end\n  function dobj:addhours(n)  return dobj_adddayfrc(self,n,TICKSPERHOUR,HOURPERDAY) end\n  function dobj:addminutes(n)  return dobj_adddayfrc(self,n,TICKSPERMIN,MINPERDAY)  end\n  function dobj:addseconds(n)  return dobj_adddayfrc(self,n,TICKSPERSEC,SECPERDAY)  end\n  function dobj:addticks(n)  return dobj_adddayfrc(self,n,1,TICKSPERDAY) end\n  local tvspec = {\n    -- Abbreviated weekday name (Sun)\n    ['%a']=function(self) return sl_weekdays[weekday(self.daynum) + 7] end,\n    -- Full weekday name (Sunday)\n    ['%A']=function(self) return sl_weekdays[weekday(self.daynum)] end,\n    -- Abbreviated month name (Dec)\n    ['%b']=function(self) return sl_months[self:getmonth() - 1 + 12] end,\n    -- Full month name (December)\n    ['%B']=function(self) return sl_months[self:getmonth() - 1] end,\n    -- Year/100 (19, 20, 30)\n    ['%C']=function(self) return fmt(\"%.2d\", fix(self:getyear()/100)) end,\n    -- The day of the month as a number (range 1 - 31)\n    ['%d']=function(self) return fmt(\"%.2d\", self:getday())  end,\n    -- year for ISO 8601 week, from 00 (79)\n    ['%g']=function(self) return fmt(\"%.2d\", mod(self:getisoyear() ,100)) end,\n    -- year for ISO 8601 week, from 0000 (1979)\n    ['%G']=function(self) return fmt(\"%.4d\", self:getisoyear()) end,\n    -- same as %b\n    ['%h']=function(self) return self:fmt0(\"%b\") end,\n    -- hour of the 24-hour day, from 00 (06)\n    ['%H']=function(self) return fmt(\"%.2d\", self:gethours()) end,\n    -- The  hour as a number using a 12-hour clock (01 - 12)\n    ['%I']=function(self) return fmt(\"%.2d\", self:getclockhour()) end,\n    -- The day of the year as a number (001 - 366)\n    ['%j']=function(self) return fmt(\"%.3d\", self:getyearday())  end,\n    -- Month of the year, from 01 to 12\n    ['%m']=function(self) return fmt(\"%.2d\", self:getmonth())  end,\n    -- Minutes after the hour 55\n    ['%M']=function(self) return fmt(\"%.2d\", self:getminutes())end,\n    -- AM/PM indicator (AM)\n    ['%p']=function(self) return sl_meridian[self:gethours() > 11 and 1 or -1] end, --AM/PM indicator (AM)\n    -- The second as a number (59, 20 , 01)\n    ['%S']=function(self) return fmt(\"%.2d\", self:getseconds())  end,\n    -- ISO 8601 day of the week, to 7 for Sunday (7, 1)\n    ['%u']=function(self) return self:getisoweekday() end,\n    -- Sunday week of the year, from 00 (48)\n    ['%U']=function(self) return fmt(\"%.2d\", self:getweeknumber()) end,\n    -- ISO 8601 week of the year, from 01 (48)\n    ['%V']=function(self) return fmt(\"%.2d\", self:getisoweeknumber()) end,\n    -- The day of the week as a decimal, Sunday being 0\n    ['%w']=function(self) return self:getweekday() - 1 end,\n    -- Monday week of the year, from 00 (48)\n    ['%W']=function(self) return fmt(\"%.2d\", self:getweeknumber(2)) end,\n    -- The year as a number without a century (range 00 to 99)\n    ['%y']=function(self) return fmt(\"%.2d\", mod(self:getyear() ,100)) end,\n    -- Year with century (2000, 1914, 0325, 0001)\n    ['%Y']=function(self) return fmt(\"%.4d\", self:getyear()) end,\n    -- Time zone offset, the date object is assumed local time (+1000, -0230)\n    ['%z']=function(self) local b = -self:getbias(); local x = abs(b); return fmt(\"%s%.4d\", b < 0 and \"-\" or \"+\", fix(x/60)*100 + floor(mod(x,60))) end,\n    -- Time zone name, the date object is assumed local time\n    ['%Z']=function(self) return self:gettzname() end,\n    -- Misc --\n    -- Year, if year is in BCE, prints the BCE Year representation, otherwise result is similar to \"%Y\" (1 BCE, 40 BCE)\n    ['%\\b']=function(self) local x = self:getyear() return fmt(\"%.4d%s\", x>0 and x or (-x+1), x>0 and \"\" or \" BCE\") end,\n    -- Seconds including fraction (59.998, 01.123)\n    ['%\\f']=function(self) local x = self:getfracsec() return fmt(\"%s%.9f\",x >= 10 and \"\" or \"0\", x) end,\n    -- percent character %\n    ['%%']=function(self) return \"%\" end,\n    -- Group Spec --\n    -- 12-hour time, from 01:00:00 AM (06:55:15 AM); same as \"%I:%M:%S %p\"\n    ['%r']=function(self) return self:fmt0(\"%I:%M:%S %p\") end,\n    -- hour:minute, from 01:00 (06:55); same as \"%I:%M\"\n    ['%R']=function(self) return self:fmt0(\"%I:%M\")  end,\n    -- 24-hour time, from 00:00:00 (06:55:15); same as \"%H:%M:%S\"\n    ['%T']=function(self) return self:fmt0(\"%H:%M:%S\") end,\n    -- month/day/year from 01/01/00 (12/02/79); same as \"%m/%d/%y\"\n    ['%D']=function(self) return self:fmt0(\"%m/%d/%y\") end,\n    -- year-month-day (1979-12-02); same as \"%Y-%m-%d\"\n    ['%F']=function(self) return self:fmt0(\"%Y-%m-%d\") end,\n    -- The preferred date and time representation;  same as \"%x %X\"\n    ['%c']=function(self) return self:fmt0(\"%x %X\") end,\n    -- The preferred date representation, same as \"%a %b %d %\\b\"\n    ['%x']=function(self) return self:fmt0(\"%a %b %d %\\b\") end,\n    -- The preferred time representation, same as \"%H:%M:%\\f\"\n    ['%X']=function(self) return self:fmt0(\"%H:%M:%\\f\") end,\n    -- GroupSpec --\n    -- Iso format, same as \"%Y-%m-%dT%T\"\n    ['${iso}'] = function(self) return self:fmt0(\"%Y-%m-%dT%T\") end,\n    -- http format, same as \"%a, %d %b %Y %T GMT\"\n    ['${http}'] = function(self) return self:fmt0(\"%a, %d %b %Y %T GMT\") end,\n    -- ctime format, same as \"%a %b %d %T GMT %Y\"\n    ['${ctime}'] = function(self) return self:fmt0(\"%a %b %d %T GMT %Y\") end,\n    -- RFC850 format, same as \"%A, %d-%b-%y %T GMT\"\n    ['${rfc850}'] = function(self) return self:fmt0(\"%A, %d-%b-%y %T GMT\") end,\n    -- RFC1123 format, same as \"%a, %d %b %Y %T GMT\"\n    ['${rfc1123}'] = function(self) return self:fmt0(\"%a, %d %b %Y %T GMT\") end,\n    -- asctime format, same as \"%a %b %d %T %Y\"\n    ['${asctime}'] = function(self) return self:fmt0(\"%a %b %d %T %Y\") end,\n  }\n  function dobj:fmt0(str) return (gsub(str, \"%%[%a%%\\b\\f]\", function(x) local f = tvspec[x];return (f and f(self)) or x end)) end\n  function dobj:fmt(str)\n    str = str or self.fmtstr or fmtstr\n    return self:fmt0((gmatch(str, \"${%w+}\")) and (gsub(str, \"${%w+}\", function(x)local f=tvspec[x];return (f and f(self)) or x end)) or str)\n  end\n\n  dobj.format = dobj.fmt\n\n  function dobj.__lt(a, b) if (a.daynum == b.daynum) then return (a.dayfrc < b.dayfrc) else return (a.daynum < b.daynum) end end\n  function dobj.__le(a, b) if (a.daynum == b.daynum) then return (a.dayfrc <= b.dayfrc) else return (a.daynum <= b.daynum) end end\n  function dobj.__eq(a, b)return (a.daynum == b.daynum) and (a.dayfrc == b.dayfrc) end\n  function dobj.__sub(a,b)\n    local d1, d2 = date_getdobj(a), date_getdobj(b)\n    local d0 = d1 and d2 and date_new(d1.daynum - d2.daynum, d1.dayfrc - d2.dayfrc)\n    return d0 and d0:normalize()\n  end\n  function dobj.__add(a,b)\n    local d1, d2 = date_getdobj(a), date_getdobj(b)\n    local d0 = d1 and d2 and date_new(d1.daynum + d2.daynum, d1.dayfrc + d2.dayfrc)\n    return d0 and d0:normalize()\n  end\n  function dobj.__concat(a, b) return tostring(a) .. tostring(b) end\n  function dobj:__tostring() return self:fmt() end\n\n  function dobj:copy() return date_new(self.daynum, self.dayfrc) end\n\n--[[ THE LOCAL DATE OBJECT METHODS ]]--\n  function dobj:tolocal()\n    local dn,df = self.daynum, self.dayfrc\n    local bias  = getbiasutc2(self)\n    if bias then\n      -- utc = local + bias; local = utc - bias\n      self.daynum = dn\n      self.dayfrc = df - bias*TICKSPERSEC\n      return self:normalize()\n    else\n      return nil\n    end\n  end\n\n  function dobj:toutc()\n    local dn,df = self.daynum, self.dayfrc\n    local bias  = getbiasloc2(dn, df)\n    if bias then\n      -- utc = local + bias;\n      self.daynum = dn\n      self.dayfrc = df + bias*TICKSPERSEC\n      return self:normalize()\n    else\n      return nil\n    end\n  end\n\n  function dobj:getbias()  return (getbiasloc2(self.daynum, self.dayfrc))/SECPERMIN end\n\n  function dobj:gettzname()\n    local _, tvu, _ = getbiasloc2(self.daynum, self.dayfrc)\n    return tvu and osdate(\"%Z\",tvu) or \"\"\n  end\n\n--#if not DATE_OBJECT_AFX then\n  function date.time(h, r, s, t)\n    h, r, s, t = tonumber(h or 0), tonumber(r or 0), tonumber(s or 0), tonumber(t or 0)\n    if h and r and s and t then\n       return date_new(DAYNUM_DEF, makedayfrc(h, r, s, t))\n    else\n      return date_error_arg()\n    end\n  end\n\n  function date:__call(...)\n    local arg = pack(...)\n    if arg.n  > 1 then return (date_from(...))\n    elseif arg.n == 0 then return (date_getdobj(false))\n    else local o, r = date_getdobj(arg[1]);  return r and o:copy() or o end\n  end\n\n  date.diff = dobj.__sub\n\n  function date.isleapyear(v)\n    local y = fix(v);\n    if not y then\n      y = date_getdobj(v)\n      y = y and y:getyear()\n    end\n    return isleapyear(y+0)\n  end\n\n  function date.epoch() return date_epoch:copy()  end\n\n  function date.isodate(y,w,d) return date_new(makedaynum_isoywd(y + 0, w and (w+0) or 1, d and (d+0) or 1), 0)  end\n\n-- Internal functions\n  function date.fmt(str) if str then fmtstr = str end; return fmtstr end\n  function date.daynummin(n)  DAYNUM_MIN = (n and n < DAYNUM_MAX) and n or DAYNUM_MIN  return n and DAYNUM_MIN or date_new(DAYNUM_MIN, 0):normalize()end\n  function date.daynummax(n)  DAYNUM_MAX = (n and n > DAYNUM_MIN) and n or DAYNUM_MAX return n and DAYNUM_MAX or date_new(DAYNUM_MAX, 0):normalize()end\n  function date.ticks(t) if t then setticks(t) end return TICKSPERSEC  end\n--#end -- not DATE_OBJECT_AFX\n\n  local tm = osdate(\"!*t\", 0);\n  if tm then\n    date_epoch = date_new(makedaynum(tm.year, tm.month - 1, tm.day), makedayfrc(tm.hour, tm.min, tm.sec, 0))\n    -- the distance from our epoch to os epoch in daynum\n    DATE_EPOCH = date_epoch and date_epoch:spandays()\n  else -- error will be raise only if called!\n    date_epoch = setmetatable({},{__index = function() error(\"failed to get the epoch date\") end})\n  end\n\n  function date.serialize(object)\n    return {tostring(object.daynum), tostring(object.dayfrc)}\n  end\n\n  function date.construct(object)\n    return date_isdobj(object) or (object.daynum and date_new(object.daynum, object.dayfrc) or date_new(object[1], object[2]))\n  end\n\n--#if not DATE_OBJECT_AFX then\nreturn date\n--#else\n--$return date_from\n--#end\n"
  },
  {
    "path": "gamemode/core/libs/thirdparty/sh_middleclass.lua",
    "content": "local middleclass = {\n  _VERSION     = 'middleclass v4.1.1',\n  _DESCRIPTION = 'Object Orientation for Lua',\n  _URL         = 'https://github.com/kikito/middleclass',\n  _LICENSE     = [[\n    MIT LICENSE\n\n    Copyright (c) 2011 Enrique García Cota\n\n    Permission is hereby granted, free of charge, to any person obtaining a\n    copy of this software and associated documentation files (the\n    \"Software\"), to deal in the Software without restriction, including\n    without limitation the rights to use, copy, modify, merge, publish,\n    distribute, sublicense, and/or sell copies of the Software, and to\n    permit persons to whom the Software is furnished to do so, subject to\n    the following conditions:\n\n    The above copyright notice and this permission notice shall be included\n    in all copies or substantial portions of the Software.\n\n    THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS\n    OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n    IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n    CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n    TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n    SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n  ]]\n}\n\nlocal function _createIndexWrapper(aClass, f)\n  if f == nil then\n    return aClass.__instanceDict\n  else\n    return function(self, name)\n      local value = aClass.__instanceDict[name]\n\n      if value ~= nil then\n        return value\n      elseif type(f) == \"function\" then\n        return (f(self, name))\n      else\n        return f[name]\n      end\n    end\n  end\nend\n\nlocal function _propagateInstanceMethod(aClass, name, f)\n  f = name == \"__index\" and _createIndexWrapper(aClass, f) or f\n  aClass.__instanceDict[name] = f\n\n  for subclass in pairs(aClass.subclasses) do\n    if rawget(subclass.__declaredMethods, name) == nil then\n      _propagateInstanceMethod(subclass, name, f)\n    end\n  end\nend\n\nlocal function _declareInstanceMethod(aClass, name, f)\n  aClass.__declaredMethods[name] = f\n\n  if f == nil and aClass.super then\n    f = aClass.super.__instanceDict[name]\n  end\n\n  _propagateInstanceMethod(aClass, name, f)\nend\n\nlocal function _tostring(self) return \"class \" .. self.name end\nlocal function _call(self, ...) return self:New(...) end\n\nlocal function _createClass(name, super)\n  local dict = {}\n  dict.__index = dict\n\n  local aClass = { name = name, super = super, static = {},\n                   __instanceDict = dict, __declaredMethods = {},\n                   subclasses = setmetatable({}, {__mode='k'})  }\n\n  if super then\n    setmetatable(aClass.static, {\n      __index = function(_,k)\n        local result = rawget(dict,k)\n        if result == nil then\n          return super.static[k]\n        end\n        return result\n      end\n    })\n  else\n    setmetatable(aClass.static, { __index = function(_,k) return rawget(dict,k) end })\n  end\n\n  setmetatable(aClass, { __index = aClass.static, __tostring = _tostring,\n                         __call = _call, __newindex = _declareInstanceMethod })\n\n  return aClass\nend\n\nlocal function _includeMixin(aClass, mixin)\n  assert(type(mixin) == 'table', \"mixin must be a table\")\n\n  for name,method in pairs(mixin) do\n    if name ~= \"Included\" and name ~= \"static\" then aClass[name] = method end\n  end\n\n  for name,method in pairs(mixin.static or {}) do\n    aClass.static[name] = method\n  end\n\n  if type(mixin.Included)==\"function\" then mixin:Included(aClass) end\n  return aClass\nend\n\nlocal DefaultMixin = {\n  __tostring   = function(self) return \"instance of \" .. tostring(self.class) end,\n\n  Initialize   = function(self, ...) end,\n\n  IsInstanceOf = function(self, aClass)\n    return type(aClass) == 'table'\n       and type(self) == 'table'\n       and (self.class == aClass\n            or type(self.class) == 'table'\n            and type(self.class.IsSubclassOf) == 'function'\n            and self.class:IsSubclassOf(aClass))\n  end,\n\n  static = {\n    Allocate = function(self)\n      assert(type(self) == 'table', \"Make sure that you are using 'Class:Allocate' instead of 'Class.Allocate'\")\n      return setmetatable({ class = self }, self.__instanceDict)\n    end,\n\n    New = function(self, ...)\n      assert(type(self) == 'table', \"Make sure that you are using 'Class:New' instead of 'Class.New'\")\n      local instance = self:Allocate()\n      instance:Initialize(...)\n      return instance\n    end,\n\n    Subclass = function(self, name)\n      assert(type(self) == 'table', \"Make sure that you are using 'Class:Subclass' instead of 'Class.Subclass'\")\n      assert(type(name) == \"string\", \"You must provide a name(string) for your class\")\n\n      local subclass = _createClass(name, self)\n\n      for methodName, f in pairs(self.__instanceDict) do\n        _propagateInstanceMethod(subclass, methodName, f)\n      end\n      subclass.Initialize = function(instance, ...) return self.Initialize(instance, ...) end\n\n      self.subclasses[subclass] = true\n      self:Subclassed(subclass)\n\n      return subclass\n    end,\n\n    Subclassed = function(self, other) end,\n\n    IsSubclassOf = function(self, other)\n      return type(other)      == 'table' and\n             type(self.super) == 'table' and\n             ( self.super == other or self.super:IsSubclassOf(other) )\n    end,\n\n    Include = function(self, ...)\n      assert(type(self) == 'table', \"Make sure you that you are using 'Class:Include' instead of 'Class.Include'\")\n      for _,mixin in ipairs({...}) do _includeMixin(self, mixin) end\n      return self\n    end\n  }\n}\n\nfunction middleclass.class(name, super)\n  assert(type(name) == 'string', \"A name (string) is needed for the new class\")\n  return super and super:Subclass(name) or _includeMixin(_createClass(name), DefaultMixin)\nend\n\nsetmetatable(middleclass, { __call = function(_, ...) return middleclass.class(...) end })\n\nix.middleclass = middleclass\n"
  },
  {
    "path": "gamemode/core/libs/thirdparty/sh_pon.lua",
    "content": "--[[\nDEVELOPMENTAL VERSION;\nVERSION 1.2.2\nCopyright thelastpenguin™\n\tYou may use this for any purpose as long as:\n\t-\tYou don't remove this copyright notice.\n\t-\tYou don't claim this to be your own.\n\t-\tYou properly credit the author, thelastpenguin™, if you publish your work based on (and/or using) this.\n\tIf you modify the code for any purpose, the above still applies to the modified code.\n\tThe author is not held responsible for any damages incured from the use of pon, you use it at your own risk.\nDATA TYPES SUPPORTED:\n - tables  - \t\tk,v - pointers\n - strings - \t\tk,v - pointers\n - numbers -\t\tk,v\n - booleans- \t\tk,v\n - Vectors - \t\tk,v\n - Angles  -\t\tk,v\n - Entities- \t\tk,v\n - Players - \t\tk,v\nCHANGE LOG\nV 1.1.0\n - Added Vehicle, NPC, NextBot, Player, Weapon\nV 1.2.0\n - Added custom handling for k,v tables without any array component.\nV 1.2.1\n - fixed deserialization bug.\nTHANKS TO...\n - VERCAS for the inspiration.\n]]\n\n\nlocal pon = {};\n_G.pon = pon;\n\nlocal type, count = type, table.Count ;\nlocal tonumber = tonumber ;\nlocal format = string.format;\ndo\n\tlocal type, count = type, table.Count ;\n\tlocal tonumber = tonumber ;\n\tlocal format = string.format;\n\n\tlocal encode = {};\n\n\tlocal tryCache ;\n\n\tlocal cacheSize = 0;\n\n\tencode['table'] = function( self, tbl, output, cache )\n\n\t\tif( cache[ tbl ] )then\n\t\t\toutput[ #output + 1 ] = format('(%x)',  cache[tbl] );\n\t\t\treturn ;\n\t\telse\n\t\t\tcacheSize = cacheSize + 1;\n\t\t\tcache[ tbl ] = cacheSize;\n\t\tend\n\n\n    local first = next(tbl, nil)\n    local predictedNumeric = 1\n    local lastKey = nil\n    -- starts with a numeric dealio\n    if first == 1 then\n      output[#output + 1] = '{'\n\n      for k,v in next, tbl do\n        if k == predictedNumeric then\n          predictedNumeric = predictedNumeric + 1\n\n          local tv = type(v)\n          if tv == 'string' then\n            local pid = cache[v]\n            if pid then\n              output[#output + 1] = format('(%x)', pid)\n            else\n              cacheSize = cacheSize + 1\n              cache[v] = cacheSize\n              self.string(self, v, output, cache)\n            end\n          else\n            self[tv](self, v, output, cache)\n          end\n\n        else\n          break\n        end\n      end\n\n\t\t\tpredictedNumeric = predictedNumeric - 1\n    else\n      predictedNumeric = nil\n    end\n\n    if predictedNumeric == nil then\n      output[#output + 1] = '[' -- no array component\n    else\n      output[#output + 1] = '~' -- array component came first so shit needs to happen\n    end\n\n    for k, v in next, tbl, predictedNumeric do\n      local tk, tv = type(k), type(v)\n\n      -- WRITE KEY\n      if tk == 'string' then\n        local pid = cache[ k ];\n        if( pid )then\n          output[ #output + 1 ] = format('(%x)',  pid );\n        else\n          cacheSize = cacheSize + 1;\n          cache[ k ] = cacheSize;\n\n          self.string( self, k, output, cache );\n        end\n      else\n        self[tk](self, k, output, cache)\n      end\n\n      -- WRITE VALUE\n      if( tv == 'string' )then\n        local pid = cache[ v ];\n        if( pid )then\n          output[ #output + 1 ] = format('(%x)',  pid );\n        else\n          cacheSize = cacheSize + 1;\n          cache[ v ] = cacheSize;\n\n          self.string( self, v, output, cache );\n        end\n      else\n        self[ tv ]( self, v, output, cache );\n      end\n    end\n\n    output[#output + 1] = '}'\n\tend\n\t--    ENCODE STRING\n\tlocal gsub = string.gsub ;\n\tencode['string'] = function( self, str, output )\n\t\t--if tryCache( str, output ) then return end\n\t\tlocal estr, count = gsub( str, \";\", \"\\\\;\");\n\t\tif( count == 0 )then\n\t\t\toutput[ #output + 1 ] = '\\''..str..';';\n\t\telse\n\t\t\toutput[ #output + 1 ] = '\"'..estr..'\";';\n\t\tend\n\tend\n\t--    ENCODE NUMBER\n\tencode['number'] = function( self, num, output )\n\t\tif num%1 == 0 then\n\t\t\tif num < 0 then\n\t\t\t\toutput[ #output + 1 ] = format( 'x%x;', -num );\n\t\t\telse\n\t\t\t\toutput[ #output + 1 ] = format('X%x;', num );\n\t\t\tend\n\t\telse\n\t\t\toutput[ #output + 1 ] = tonumber( num )..';';\n\t\tend\n\tend\n\t--    ENCODE BOOLEAN\n\tencode['boolean'] = function( self, val, output )\n\t\toutput[ #output + 1 ] = val and 't' or 'f'\n\tend\n\t--    ENCODE VECTOR\n\tencode['Vector'] = function( self, val, output )\n\t\toutput[ #output + 1 ] = ('v'..val.x..','..val.y)..(','..val.z..';');\n\tend\n\t--    ENCODE ANGLE\n\tencode['Angle'] = function( self, val, output )\n\t\toutput[ #output + 1 ] = ('a'..val.p..','..val.y)..(','..val.r..';');\n\tend\n\tencode['Entity'] = function( self, val, output )\n\t\toutput[ #output + 1] = 'E'..(IsValid( val ) and (val:EntIndex( )..';') or '#');\n\tend\n\tencode['Player']  = encode['Entity'];\n\tencode['Vehicle'] = encode['Entity'];\n\tencode['Weapon']  = encode['Entity'];\n\tencode['NPC']     = encode['Entity'];\n\tencode['NextBot'] = encode['Entity'];\n\tencode['PhysObj'] = encode['Entity'];\n\n\tencode['nil'] = function()\n\t\toutput[ #output + 1 ] = '?';\n\tend\n\tencode.__index = function( key )\n\t\tErrorNoHalt('Type: '..key..' can not be encoded. Encoded as as pass-over value.');\n\t\treturn encode['nil'];\n\tend\n\n\tdo\n\t\tlocal empty, concat = table.Empty, table.concat ;\n\t\tfunction pon.encode( tbl )\n\t\t\tlocal output = {};\n\t\t\tcacheSize = 0;\n\t\t\tencode[ 'table' ]( encode, tbl, output, {} );\n\t\t\tlocal res = concat( output );\n\n\t\t\treturn res;\n\t\tend\n\tend\nend\n\ndo\n\tlocal tonumber = tonumber ;\n\tlocal find, sub, gsub, Explode = string.find, string.sub, string.gsub, string.Explode ;\n\tlocal Vector, Angle, Entity = Vector, Angle, Entity ;\n\n\tlocal decode = {};\n\tdecode['{'] = function( self, index, str, cache )\n\n\t\tlocal cur = {};\n\t\tcache[ #cache + 1 ] = cur;\n\n\t\tlocal k, v, tk, tv = 1, nil, nil, nil;\n\t\twhile( true )do\n\t\t\ttv = sub( str, index, index );\n\t\t\tif( not tv or tv == '~' )then\n\t\t\t\tindex = index + 1;\n\t\t\t\tbreak ;\n\t\t\tend\n\t\t\tif( tv == '}' )then\n\t\t\t\treturn index + 1, cur;\n\t\t\tend\n\n\t\t\t-- READ THE VALUE\n\t\t\tindex = index + 1;\n\t\t\tindex, v = self[ tv ]( self, index, str, cache );\n\t\t\tcur[ k ] = v;\n\n\t\t\tk = k + 1;\n\t\tend\n\n\t\twhile( true )do\n\t\t\ttk = sub( str, index, index );\n\t\t\tif( not tk or tk == '}' )then\n\t\t\t\tindex = index + 1;\n\t\t\t\tbreak ;\n\t\t\tend\n\n\t\t\t-- READ THE KEY\n\n\t\t\tindex = index + 1;\n\t\t\tindex, k = self[ tk ]( self, index, str, cache );\n\n\t\t\t-- READ THE VALUE\n\t\t\ttv = sub( str, index, index );\n\t\t\tindex = index + 1;\n\t\t\tindex, v = self[ tv ]( self, index, str, cache );\n\n\t\t\tcur[ k ] = v;\n\t\tend\n\n\t\treturn index, cur;\n\tend\n\tdecode['['] = function( self, index, str, cache )\n\n\t\tlocal cur = {};\n\t\tcache[ #cache + 1 ] = cur;\n\n\t\tlocal k, v, tk, tv = 1, nil, nil, nil;\n\t\twhile( true )do\n\t\t\ttk = sub( str, index, index );\n\t\t\tif( not tk or tk == '}' )then\n\t\t\t\tindex = index + 1;\n\t\t\t\tbreak ;\n\t\t\tend\n\n\t\t\t-- READ THE KEY\n\t\t\tindex = index + 1;\n\t\t\tindex, k = self[ tk ]( self, index, str, cache );\n\t\t\tif not k then continue end\n\n\t\t\t-- READ THE VALUE\n\t\t\ttv = sub( str, index, index );\n\t\t\tindex = index + 1;\n\t\t\tif not self[tv] then\n\t\t\t\tprint('did not find type: '..tv)\n\t\t\tend\n\t\t\tindex, v = self[ tv ]( self, index, str, cache );\n\n\t\t\tcur[ k ] = v;\n\t\tend\n\n\t\treturn index, cur;\n\tend\n\n\t-- STRING\n\tdecode['\"'] = function( self, index, str, cache )\n\t\tlocal finish = find( str, '\";', index, true );\n\t\tlocal res = gsub( sub( str, index, finish - 1 ), '\\\\;', ';' );\n\t\tindex = finish + 2;\n\n\t\tcache[ #cache + 1 ] = res;\n\t\treturn index, res;\n\tend\n\t-- STRING NO ESCAPING NEEDED\n\tdecode['\\''] = function( self, index, str, cache )\n\t\tlocal finish = find( str, ';', index, true );\n\t\tlocal res = sub( str, index, finish - 1 )\n\t\tindex = finish + 1;\n\n\t\tcache[ #cache + 1 ] = res;\n\t\treturn index, res;\n\tend\n\n\t-- NUMBER\n\tdecode['n'] = function( self, index, str, cache )\n\t\tindex = index - 1;\n\t\tlocal finish = find( str, ';', index, true );\n\t\tlocal num = tonumber( sub( str, index, finish - 1 ) );\n\t\tindex = finish + 1;\n\t\treturn index, num;\n\tend\n\tdecode['0'] = decode['n'];\n\tdecode['1'] = decode['n'];\n\tdecode['2'] = decode['n'];\n\tdecode['3'] = decode['n'];\n\tdecode['4'] = decode['n'];\n\tdecode['5'] = decode['n'];\n\tdecode['6'] = decode['n'];\n\tdecode['7'] = decode['n'];\n\tdecode['8'] = decode['n'];\n\tdecode['9'] = decode['n'];\n\tdecode['-'] = decode['n'];\n\t-- positive hex\n\tdecode['X'] = function( self, index, str, cache )\n\t\tlocal finish = find( str, ';', index, true );\n\t\tlocal num = tonumber( sub( str, index, finish - 1), 16 );\n\t\tindex = finish + 1;\n\t\treturn index, num;\n\tend\n\t-- negative hex\n\tdecode['x'] = function( self, index, str, cache )\n\t\tlocal finish = find( str, ';', index, true );\n\t\tlocal num = -tonumber( sub( str, index, finish - 1), 16 );\n\t\tindex = finish + 1;\n\t\treturn index, num;\n\tend\n\n\t-- POINTER\n\tdecode['('] = function( self, index, str, cache )\n\t\tlocal finish = find( str, ')', index, true );\n\t\tlocal num = tonumber( sub( str, index, finish - 1), 16 );\n\t\tindex = finish + 1;\n\t\treturn index, cache[ num ];\n\tend\n\n\t-- BOOLEAN. ONE DATA TYPE FOR YES, ANOTHER FOR NO.\n\tdecode[ 't' ] = function( self, index )\n\t\treturn index, true;\n\tend\n\tdecode[ 'f' ] = function( self, index )\n\t\treturn index, false;\n\tend\n\n\t-- VECTOR\n\tdecode[ 'v' ] = function( self, index, str, cache )\n\t\tlocal finish =  find( str, ';', index, true );\n\t\tlocal vecStr = sub( str, index, finish - 1 );\n\t\tindex = finish + 1; -- update the index.\n\t\tlocal segs = Explode( ',', vecStr, false );\n\t\treturn index, Vector( tonumber( segs[1] ), tonumber( segs[2] ), tonumber( segs[3] ) );\n\tend\n\t-- ANGLE\n\tdecode[ 'a' ] = function( self, index, str, cache )\n\t\tlocal finish =  find( str, ';', index, true );\n\t\tlocal angStr = sub( str, index, finish - 1 );\n\t\tindex = finish + 1; -- update the index.\n\t\tlocal segs = Explode( ',', angStr, false );\n\t\treturn index, Angle( tonumber( segs[1] ), tonumber( segs[2] ), tonumber( segs[3] ) );\n\tend\n\t-- ENTITY\n\tdecode[ 'E' ] = function( self, index, str, cache )\n\t\tif( str[index] == '#' )then\n\t\t\tindex = index + 1;\n\t\t\treturn index, NULL ;\n\t\telse\n\t\t\tlocal finish = find( str, ';', index, true );\n\t\t\tlocal num = tonumber( sub( str, index, finish - 1 ) );\n\t\t\tindex = finish + 1;\n\t\t\treturn index, Entity( num );\n\t\tend\n\tend\n\t-- PLAYER\n\tdecode[ 'P' ] = function( self, index, str, cache )\n\t\tlocal finish = find( str, ';', index, true );\n\t\tlocal num = tonumber( sub( str, index, finish - 1 ) );\n\t\tindex = finish + 1;\n\t\treturn index, Entity( num ) or NULL;\n\tend\n\t-- NIL\n\tdecode['?'] = function( self, index, str, cache )\n\t\treturn index + 1, nil;\n\tend\n\n\n\tfunction pon.decode( data )\n\t\tlocal _, res = decode[sub(data,1,1)]( decode, 2, data, {});\n\t\treturn res;\n\tend\nend"
  },
  {
    "path": "gamemode/core/libs/thirdparty/sh_tween.lua",
    "content": "local tween = {\n  _VERSION     = 'tween 2.1.1',\n  _DESCRIPTION = 'tweening for lua',\n  _URL         = 'https://github.com/kikito/tween.lua',\n  _LICENSE     = [[\n    MIT LICENSE\n\n    Copyright (c) 2014 Enrique García Cota, Yuichi Tateno, Emmanuel Oga\n\n    Permission is hereby granted, free of charge, to any person obtaining a\n    copy of this software and associated documentation files (the\n    \"Software\"), to deal in the Software without restriction, including\n    without limitation the rights to use, copy, modify, merge, publish,\n    distribute, sublicense, and/or sell copies of the Software, and to\n    permit persons to whom the Software is furnished to do so, subject to\n    the following conditions:\n\n    The above copyright notice and this permission notice shall be included\n    in all copies or substantial portions of the Software.\n\n    THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS\n    OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n    IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n    CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n    TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n    SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n  ]]\n}\n\n-- easing\n\n-- Adapted from https://github.com/EmmanuelOga/easing. See LICENSE.txt for credits.\n-- For all easing functions:\n-- t = time == how much time has to pass for the tweening to complete\n-- b = begin == starting property value\n-- c = change == ending - beginning\n-- d = duration == running time. How much time has passed *right now*\n\nlocal pow, sin, cos, pi, sqrt, abs, asin = math.pow, math.sin, math.cos, math.pi, math.sqrt, math.abs, math.asin\n\n-- linear\nlocal function linear(t, b, c, d) return c * t / d + b end\n\n-- quad\nlocal function inQuad(t, b, c, d) return c * pow(t / d, 2) + b end\nlocal function outQuad(t, b, c, d)\n  t = t / d\n  return -c * t * (t - 2) + b\nend\nlocal function inOutQuad(t, b, c, d)\n  t = t / d * 2\n  if t < 1 then return c / 2 * pow(t, 2) + b end\n  return -c / 2 * ((t - 1) * (t - 3) - 1) + b\nend\nlocal function outInQuad(t, b, c, d)\n  if t < d / 2 then return outQuad(t * 2, b, c / 2, d) end\n  return inQuad((t * 2) - d, b + c / 2, c / 2, d)\nend\n\n-- cubic\nlocal function inCubic (t, b, c, d) return c * pow(t / d, 3) + b end\nlocal function outCubic(t, b, c, d) return c * (pow(t / d - 1, 3) + 1) + b end\nlocal function inOutCubic(t, b, c, d)\n  t = t / d * 2\n  if t < 1 then return c / 2 * t * t * t + b end\n  t = t - 2\n  return c / 2 * (t * t * t + 2) + b\nend\nlocal function outInCubic(t, b, c, d)\n  if t < d / 2 then return outCubic(t * 2, b, c / 2, d) end\n  return inCubic((t * 2) - d, b + c / 2, c / 2, d)\nend\n\n-- quart\nlocal function inQuart(t, b, c, d) return c * pow(t / d, 4) + b end\nlocal function outQuart(t, b, c, d) return -c * (pow(t / d - 1, 4) - 1) + b end\nlocal function inOutQuart(t, b, c, d)\n  t = t / d * 2\n  if t < 1 then return c / 2 * pow(t, 4) + b end\n  return -c / 2 * (pow(t - 2, 4) - 2) + b\nend\nlocal function outInQuart(t, b, c, d)\n  if t < d / 2 then return outQuart(t * 2, b, c / 2, d) end\n  return inQuart((t * 2) - d, b + c / 2, c / 2, d)\nend\n\n-- quint\nlocal function inQuint(t, b, c, d) return c * pow(t / d, 5) + b end\nlocal function outQuint(t, b, c, d) return c * (pow(t / d - 1, 5) + 1) + b end\nlocal function inOutQuint(t, b, c, d)\n  t = t / d * 2\n  if t < 1 then return c / 2 * pow(t, 5) + b end\n  return c / 2 * (pow(t - 2, 5) + 2) + b\nend\nlocal function outInQuint(t, b, c, d)\n  if t < d / 2 then return outQuint(t * 2, b, c / 2, d) end\n  return inQuint((t * 2) - d, b + c / 2, c / 2, d)\nend\n\n-- sine\nlocal function inSine(t, b, c, d) return -c * cos(t / d * (pi / 2)) + c + b end\nlocal function outSine(t, b, c, d) return c * sin(t / d * (pi / 2)) + b end\nlocal function inOutSine(t, b, c, d) return -c / 2 * (cos(pi * t / d) - 1) + b end\nlocal function outInSine(t, b, c, d)\n  if t < d / 2 then return outSine(t * 2, b, c / 2, d) end\n  return inSine((t * 2) -d, b + c / 2, c / 2, d)\nend\n\n-- expo\nlocal function inExpo(t, b, c, d)\n  if t == 0 then return b end\n  return c * pow(2, 10 * (t / d - 1)) + b - c * 0.001\nend\nlocal function outExpo(t, b, c, d)\n  if t == d then return b + c end\n  return c * 1.001 * (-pow(2, -10 * t / d) + 1) + b\nend\nlocal function inOutExpo(t, b, c, d)\n  if t == 0 then return b end\n  if t == d then return b + c end\n  t = t / d * 2\n  if t < 1 then return c / 2 * pow(2, 10 * (t - 1)) + b - c * 0.0005 end\n  return c / 2 * 1.0005 * (-pow(2, -10 * (t - 1)) + 2) + b\nend\nlocal function outInExpo(t, b, c, d)\n  if t < d / 2 then return outExpo(t * 2, b, c / 2, d) end\n  return inExpo((t * 2) - d, b + c / 2, c / 2, d)\nend\n\n-- circ\nlocal function inCirc(t, b, c, d) return(-c * (sqrt(1 - pow(t / d, 2)) - 1) + b) end\nlocal function outCirc(t, b, c, d)  return(c * sqrt(1 - pow(t / d - 1, 2)) + b) end\nlocal function inOutCirc(t, b, c, d)\n  t = t / d * 2\n  if t < 1 then return -c / 2 * (sqrt(1 - t * t) - 1) + b end\n  t = t - 2\n  return c / 2 * (sqrt(1 - t * t) + 1) + b\nend\nlocal function outInCirc(t, b, c, d)\n  if t < d / 2 then return outCirc(t * 2, b, c / 2, d) end\n  return inCirc((t * 2) - d, b + c / 2, c / 2, d)\nend\n\n-- elastic\nlocal function calculatePAS(p,a,c,d)\n  p, a = p or d * 0.3, a or 0\n  if a < abs(c) then return p, c, p / 4 end -- p, a, s\n  return p, a, p / (2 * pi) * asin(c/a) -- p,a,s\nend\nlocal function inElastic(t, b, c, d, a, p)\n  local s\n  if t == 0 then return b end\n  t = t / d\n  if t == 1  then return b + c end\n  p,a,s = calculatePAS(p,a,c,d)\n  t = t - 1\n  return -(a * pow(2, 10 * t) * sin((t * d - s) * (2 * pi) / p)) + b\nend\nlocal function outElastic(t, b, c, d, a, p)\n  local s\n  if t == 0 then return b end\n  t = t / d\n  if t == 1 then return b + c end\n  p,a,s = calculatePAS(p,a,c,d)\n  return a * pow(2, -10 * t) * sin((t * d - s) * (2 * pi) / p) + c + b\nend\nlocal function inOutElastic(t, b, c, d, a, p)\n  local s\n  if t == 0 then return b end\n  t = t / d * 2\n  if t == 2 then return b + c end\n  p,a,s = calculatePAS(p,a,c,d)\n  t = t - 1\n  if t < 0 then return -0.5 * (a * pow(2, 10 * t) * sin((t * d - s) * (2 * pi) / p)) + b end\n  return a * pow(2, -10 * t) * sin((t * d - s) * (2 * pi) / p ) * 0.5 + c + b\nend\nlocal function outInElastic(t, b, c, d, a, p)\n  if t < d / 2 then return outElastic(t * 2, b, c / 2, d, a, p) end\n  return inElastic((t * 2) - d, b + c / 2, c / 2, d, a, p)\nend\n\n-- back\nlocal function inBack(t, b, c, d, s)\n  s = s or 1.70158\n  t = t / d\n  return c * t * t * ((s + 1) * t - s) + b\nend\nlocal function outBack(t, b, c, d, s)\n  s = s or 1.70158\n  t = t / d - 1\n  return c * (t * t * ((s + 1) * t + s) + 1) + b\nend\nlocal function inOutBack(t, b, c, d, s)\n  s = (s or 1.70158) * 1.525\n  t = t / d * 2\n  if t < 1 then return c / 2 * (t * t * ((s + 1) * t - s)) + b end\n  t = t - 2\n  return c / 2 * (t * t * ((s + 1) * t + s) + 2) + b\nend\nlocal function outInBack(t, b, c, d, s)\n  if t < d / 2 then return outBack(t * 2, b, c / 2, d, s) end\n  return inBack((t * 2) - d, b + c / 2, c / 2, d, s)\nend\n\n-- bounce\nlocal function outBounce(t, b, c, d)\n  t = t / d\n  if t < 1 / 2.75 then return c * (7.5625 * t * t) + b end\n  if t < 2 / 2.75 then\n    t = t - (1.5 / 2.75)\n    return c * (7.5625 * t * t + 0.75) + b\n  elseif t < 2.5 / 2.75 then\n    t = t - (2.25 / 2.75)\n    return c * (7.5625 * t * t + 0.9375) + b\n  end\n  t = t - (2.625 / 2.75)\n  return c * (7.5625 * t * t + 0.984375) + b\nend\nlocal function inBounce(t, b, c, d) return c - outBounce(d - t, 0, c, d) + b end\nlocal function inOutBounce(t, b, c, d)\n  if t < d / 2 then return inBounce(t * 2, 0, c, d) * 0.5 + b end\n  return outBounce(t * 2 - d, 0, c, d) * 0.5 + c * .5 + b\nend\nlocal function outInBounce(t, b, c, d)\n  if t < d / 2 then return outBounce(t * 2, b, c / 2, d) end\n  return inBounce((t * 2) - d, b + c / 2, c / 2, d)\nend\n\ntween.easing = {\n  linear    = linear,\n  inQuad    = inQuad,    outQuad    = outQuad,    inOutQuad    = inOutQuad,    outInQuad    = outInQuad,\n  inCubic   = inCubic,   outCubic   = outCubic,   inOutCubic   = inOutCubic,   outInCubic   = outInCubic,\n  inQuart   = inQuart,   outQuart   = outQuart,   inOutQuart   = inOutQuart,   outInQuart   = outInQuart,\n  inQuint   = inQuint,   outQuint   = outQuint,   inOutQuint   = inOutQuint,   outInQuint   = outInQuint,\n  inSine    = inSine,    outSine    = outSine,    inOutSine    = inOutSine,    outInSine    = outInSine,\n  inExpo    = inExpo,    outExpo    = outExpo,    inOutExpo    = inOutExpo,    outInExpo    = outInExpo,\n  inCirc    = inCirc,    outCirc    = outCirc,    inOutCirc    = inOutCirc,    outInCirc    = outInCirc,\n  inElastic = inElastic, outElastic = outElastic, inOutElastic = inOutElastic, outInElastic = outInElastic,\n  inBack    = inBack,    outBack    = outBack,    inOutBack    = inOutBack,    outInBack    = outInBack,\n  inBounce  = inBounce,  outBounce  = outBounce,  inOutBounce  = inOutBounce,  outInBounce  = outInBounce\n}\n\n\n\n-- private stuff\n\nlocal function copyTables(destination, keysTable, valuesTable)\n  valuesTable = valuesTable or keysTable\n  local mt = getmetatable(keysTable)\n  if mt and getmetatable(destination) == nil then\n    setmetatable(destination, mt)\n  end\n  for k,v in pairs(keysTable) do\n    if type(v) == 'table' then\n      destination[k] = copyTables({}, v, valuesTable[k])\n    else\n      destination[k] = valuesTable[k]\n    end\n  end\n  return destination\nend\n\nlocal function checkSubjectAndTargetRecursively(subject, target, path)\n  path = path or {}\n  local newPath\n  for k,targetValue in pairs(target) do\n    newPath = copyTables({}, path)\n    table.insert(newPath, tostring(k))\n    if isnumber(targetValue) then\n      assert(isnumber(subject[k]), \"Parameter '\" .. table.concat(newPath,'/') .. \"' is missing from subject or isn't a number\")\n    elseif istable(targetValue) then\n      checkSubjectAndTargetRecursively(subject[k], targetValue, newPath)\n    else\n      assert(isnumber(targetValue), \"Parameter '\" .. table.concat(newPath,'/') .. \"' must be a number or table of numbers\")\n    end\n  end\nend\n\nlocal function checkNewParams(duration, subject, target, easing)\n  assert(isnumber(duration) and duration > 0, \"duration must be a positive number. Was \" .. tostring(duration))\n  assert(istable(target), \"target must be a table. Was \" .. tostring(target))\n  assert(isfunction(easing), \"easing must be a function. Was \" .. tostring(easing))\n  checkSubjectAndTargetRecursively(subject, target)\nend\n\nlocal function getEasingFunction(easing)\n  easing = easing or \"linear\"\n  if isstring(easing) then\n    local name = easing\n    easing = tween.easing[name]\n    if not isfunction(easing) then\n      error(\"The easing function name '\" .. name .. \"' is invalid\")\n    end\n  end\n  return easing\nend\n\nlocal function performEasingOnSubject(subject, target, initial, clock, duration, easing)\n  local t,b,c,d\n  for k,v in pairs(target) do\n    if istable(v) then\n      performEasingOnSubject(subject[k], v, initial[k], clock, duration, easing)\n    else\n      t,b,c,d = clock, initial[k], v - initial[k], duration\n      subject[k] = easing(t,b,c,d)\n    end\n  end\nend\n\nlocal function applyValues(subject, target)\n  for k, v in pairs(target) do\n    if (istable(v)) then\n      applyValues(subject[k], v)\n    else\n      subject[k] = v\n    end\n  end\nend\n\n-- Tween methods\n\nlocal Tween = {}\nlocal Tween_mt = {__index = Tween}\n\nfunction Tween:set(clock)\n  assert(isnumber(clock), \"clock must be a positive number or 0\")\n\n  self.initial = self.initial or copyTables({}, self.target, self.subject)\n  self.clock = clock\n\n  if self.clock <= 0 then\n\n    self.clock = 0\n    applyValues(self.subject, self.initial)\n\n  elseif self.clock >= self.duration then -- the tween has expired\n\n    self.clock = self.duration\n    applyValues(self.subject, self.target)\n\n  else\n\n    performEasingOnSubject(self.subject, self.target, self.initial, self.clock, self.duration, self.easing)\n\n  end\n\n  return self.clock >= self.duration\nend\n\nfunction Tween:reset()\n  return self:set(0)\nend\n\nfunction Tween:update(dt)\n  assert(isnumber(dt), \"dt must be a number\")\n  return self:set(self.clock + dt)\nend\n\n\n-- Public interface\n\nfunction tween.new(duration, subject, target, easing)\n  easing = getEasingFunction(easing)\n  checkNewParams(duration, subject, target, easing)\n  return setmetatable({\n    duration  = duration,\n    subject   = subject,\n    target    = target,\n    easing    = easing,\n    clock     = 0\n  }, Tween_mt)\nend\n\nix.tween = tween\n"
  },
  {
    "path": "gamemode/core/libs/thirdparty/sh_utf8.lua",
    "content": "-- $Id: utf8.lua 179 2009-04-03 18:10:03Z pasta $\n--\n-- Provides UTF-8 aware string functions implemented in pure lua:\n-- * string.utf8len(s)\n-- * string.utf8sub(s, i, j)\n-- * string.utf8reverse(s)\n--\n-- If utf8data.lua (containing the lower<->upper case mappings) is loaded, these\n-- additional functions are available:\n-- * string.utf8upper(s)\n-- * string.utf8lower(s)\n--\n-- All functions behave as their non UTF-8 aware counterparts with the exception\n-- that UTF-8 characters are used instead of bytes for all units.\n\n--[[\nCopyright (c) 2006-2007, Kyle Smith\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n\n    * Redistributions of source code must retain the above copyright notice,\n      this list of conditions and the following disclaimer.\n    * Redistributions in binary form must reproduce the above copyright\n      notice, this list of conditions and the following disclaimer in the\n      documentation and/or other materials provided with the distribution.\n    * Neither the name of the author nor the names of its contributors may be\n      used to endorse or promote products derived from this software without\n      specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\nAND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\nIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\nDISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE\nFOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\nDAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\nSERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\nCAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\nOR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n--]]\n\n-- ABNF from RFC 3629\n--\n-- UTF8-octets = *( UTF8-char )\n-- UTF8-char   = UTF8-1 / UTF8-2 / UTF8-3 / UTF8-4\n-- UTF8-1      = %x00-7F\n-- UTF8-2      = %xC2-DF UTF8-tail\n-- UTF8-3      = %xE0 %xA0-BF UTF8-tail / %xE1-EC 2( UTF8-tail ) /\n--               %xED %x80-9F UTF8-tail / %xEE-EF 2( UTF8-tail )\n-- UTF8-4      = %xF0 %x90-BF 2( UTF8-tail ) / %xF1-F3 3( UTF8-tail ) /\n--               %xF4 %x80-8F 2( UTF8-tail )\n-- UTF8-tail   = %x80-BF\n--\n\nix.util.Include(\"data/sh_utf8_casemap.lua\")\n\n-- returns the number of bytes used by the UTF-8 character at byte i in s\n-- also doubles as a UTF-8 character validator\nlocal function utf8charbytes (s, i)\n        -- argument defaults\n        i = i or 1\n\n        -- argument checking\n        if not isstring(s) then\n                error(\"bad argument #1 to 'utf8charbytes' (string expected, got \".. type(s).. \")\")\n        end\n        if not isnumber(i) then\n                error(\"bad argument #2 to 'utf8charbytes' (number expected, got \".. type(i).. \")\")\n        end\n\n        local c = s:byte(i)\n\n        -- determine bytes needed for character, based on RFC 3629\n        -- validate byte 1\n        if c > 0 and c <= 127 then\n                -- UTF8-1\n                return 1\n\n        elseif c >= 194 and c <= 223 then\n                -- UTF8-2\n                local c2 = s:byte(i + 1)\n\n                if not c2 then\n                        error(\"UTF-8 string terminated early\")\n                end\n\n                -- validate byte 2\n                if c2 < 128 or c2 > 191 then\n                        error(\"Invalid UTF-8 character\")\n                end\n\n                return 2\n\n        elseif c >= 224 and c <= 239 then\n                -- UTF8-3\n                local c2 = s:byte(i + 1)\n                local c3 = s:byte(i + 2)\n\n                if not c2 or not c3 then\n                        error(\"UTF-8 string terminated early\")\n                end\n\n                -- validate byte 2\n                if c == 224 and (c2 < 160 or c2 > 191) then\n                        error(\"Invalid UTF-8 character\")\n                elseif c == 237 and (c2 < 128 or c2 > 159) then\n                        error(\"Invalid UTF-8 character\")\n                elseif c2 < 128 or c2 > 191 then\n                        error(\"Invalid UTF-8 character\")\n                end\n\n                -- validate byte 3\n                if c3 < 128 or c3 > 191 then\n                        error(\"Invalid UTF-8 character\")\n                end\n\n                return 3\n\n        elseif c >= 240 and c <= 244 then\n                -- UTF8-4\n                local c2 = s:byte(i + 1)\n                local c3 = s:byte(i + 2)\n                local c4 = s:byte(i + 3)\n\n                if not c2 or not c3 or not c4 then\n                        error(\"UTF-8 string terminated early\")\n                end\n\n                -- validate byte 2\n                if c == 240 and (c2 < 144 or c2 > 191) then\n                        error(\"Invalid UTF-8 character\")\n                elseif c == 244 and (c2 < 128 or c2 > 143) then\n                        error(\"Invalid UTF-8 character\")\n                elseif c2 < 128 or c2 > 191 then\n                        error(\"Invalid UTF-8 character\")\n                end\n\n                -- validate byte 3\n                if c3 < 128 or c3 > 191 then\n                        error(\"Invalid UTF-8 character\")\n                end\n\n                -- validate byte 4\n                if c4 < 128 or c4 > 191 then\n                        error(\"Invalid UTF-8 character\")\n                end\n\n                return 4\n\n        else\n                error(\"Invalid UTF-8 character\")\n        end\nend\n\n\n-- returns the number of characters in a UTF-8 string\nlocal function utf8len (s)\n        -- argument checking\n        if not isstring(s) then\n                error(\"bad argument #1 to 'utf8len' (string expected, got \".. type(s).. \")\")\n        end\n\n        local pos = 1\n        local bytes = s:len()\n        local len = 0\n\n        while pos <= bytes do\n                len = len + 1\n                pos = pos + utf8charbytes(s, pos)\n        end\n\n        return len\nend\n\n-- install in the string library\nif not string.utf8bytes then\n        string.utf8bytes = utf8charbytes\nend\n\n-- install in the string library\nif not string.utf8len then\n        string.utf8len = utf8len\nend\n\n\n-- functions identically to string.sub except that i and j are UTF-8 characters\n-- instead of bytes\nlocal function utf8sub (s, i, j)\n        -- argument defaults\n        j = j or -1\n\n        -- argument checking\n        if not isstring(s) then\n                error(\"bad argument #1 to 'utf8sub' (string expected, got \".. type(s).. \")\")\n        end\n        if not isnumber(i) then\n                error(\"bad argument #2 to 'utf8sub' (number expected, got \".. type(i).. \")\")\n        end\n        if not isnumber(j) then\n                error(\"bad argument #3 to 'utf8sub' (number expected, got \".. type(j).. \")\")\n        end\n\n        local pos = 1\n        local bytes = s:len()\n        local len = 0\n\n        -- only set l if i or j is negative\n        local l = (i >= 0 and j >= 0) or s:utf8len()\n        local startChar = (i >= 0) and i or l + i + 1\n        local endChar   = (j >= 0) and j or l + j + 1\n\n        -- can't have start before end!\n        if startChar > endChar then\n                return \"\"\n        end\n\n        -- byte offsets to pass to string.sub\n        local startByte, endByte = 1, bytes\n\n        while pos <= bytes do\n                len = len + 1\n\n                if len == startChar then\n                        startByte = pos\n                end\n\n                pos = pos + utf8charbytes(s, pos)\n\n                if len == endChar then\n                        endByte = pos - 1\n                        break\n                end\n        end\n\n        return s:sub(startByte, endByte)\nend\n\n-- install in the string library\nif not string.utf8sub then\n        string.utf8sub = utf8sub\nend\n\n\n-- replace UTF-8 characters based on a mapping table\nlocal function utf8replace (s, mapping)\n        -- argument checking\n        if not isstring(s) then\n                error(\"bad argument #1 to 'utf8replace' (string expected, got \".. type(s).. \")\")\n        end\n        if not istable(mapping) then\n                error(\"bad argument #2 to 'utf8replace' (table expected, got \".. type(mapping).. \")\")\n        end\n\n        local pos = 1\n        local bytes = s:len()\n        local charbytes\n        local newstr = \"\"\n\n        while pos <= bytes do\n                charbytes = utf8charbytes(s, pos)\n                local c = s:sub(pos, pos + charbytes - 1)\n\n                newstr = newstr .. (mapping[c] or c)\n\n                pos = pos + charbytes\n        end\n\n        return newstr\nend\n\n\n-- identical to string.upper except it knows about unicode simple case conversions\nlocal function utf8upper (s)\n        return utf8replace(s, utf8_lc_uc)\nend\n\n-- install in the string library\nif not string.utf8upper and utf8_lc_uc then\n        string.utf8upper = utf8upper\nend\n\n\n-- identical to string.lower except it knows about unicode simple case conversions\nlocal function utf8lower (s)\n        return utf8replace(s, utf8_uc_lc)\nend\n\n-- install in the string library\nif not string.utf8lower and utf8_uc_lc then\n        string.utf8lower = utf8lower\nend\n\n\n-- identical to string.reverse except that it supports UTF-8\nlocal function utf8reverse (s)\n        -- argument checking\n        if not isstring(s) then\n                error(\"bad argument #1 to 'utf8reverse' (string expected, got \".. type(s).. \")\")\n        end\n\n        local bytes = s:len()\n        local pos = bytes\n        local charbytes\n        local newstr = \"\"\n\n        while pos > 0 do\n                c = s:byte(pos)\n                while c >= 128 and c <= 191 do\n                        pos = pos - 1\n                        c = s:byte(pos)\n                end\n\n                charbytes = utf8charbytes(s, pos)\n\n                newstr = newstr .. s:sub(pos, pos + charbytes - 1)\n\n                pos = pos - 1\n        end\n\n        return newstr\nend\n\n-- install in the string library\nif not string.utf8reverse then\n        string.utf8reverse = utf8reverse\nend\n"
  },
  {
    "path": "gamemode/core/libs/thirdparty/sh_yaml.lua",
    "content": "--[[\n  (The MIT License)\n\n  Copyright (c) 2017 Dominic Letz dominicletz@exosite.com\n\n  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\n  The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\n  THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n--]]\n\nlocal table_print_value\ntable_print_value = function(value, indent, done)\n  indent = indent or 0\n  done = done or {}\n  if istable(value) and not done [value] then\n    done [value] = true\n\n    local list = {}\n    for key in pairs (value) do\n      list[#list + 1] = key\n    end\n    table.sort(list, function(a, b) return tostring(a) < tostring(b) end)\n    local last = list[#list]\n\n    local rep = \"{\\n\"\n    local comma\n    for _, key in ipairs (list) do\n      if key == last then\n        comma = ''\n      else\n        comma = ','\n      end\n      local keyRep\n      if isnumber(key) then\n        keyRep = key\n      else\n        keyRep = string.format(\"%q\", tostring(key))\n      end\n      rep = rep .. string.format(\n        \"%s[%s] = %s%s\\n\",\n        string.rep(\" \", indent + 2),\n        keyRep,\n        table_print_value(value[key], indent + 2, done),\n        comma\n      )\n    end\n\n    rep = rep .. string.rep(\" \", indent) -- indent it\n    rep = rep .. \"}\"\n\n    done[value] = false\n    return rep\n  elseif isstring(value) then\n    return string.format(\"%q\", value)\n  else\n    return tostring(value)\n  end\nend\n\nlocal table_print = function(tt)\n  print('return '..table_print_value(tt))\nend\n\nlocal table_clone = function(t)\n  local clone = {}\n  for k,v in pairs(t) do\n    clone[k] = v\n  end\n  return clone\nend\n\nlocal string_trim = function(s, what)\n  what = what or \" \"\n  return s:gsub(\"^[\" .. what .. \"]*(.-)[\"..what..\"]*$\", \"%1\")\nend\n\nlocal push = function(stack, item)\n  stack[#stack + 1] = item\nend\n\nlocal pop = function(stack)\n  local item = stack[#stack]\n  stack[#stack] = nil\n  return item\nend\n\nlocal context = function (str)\n  if not isstring(str) then\n    return \"\"\n  end\n\n  str = str:sub(0,25):gsub(\"\\n\",\"\\\\n\"):gsub(\"\\\"\",\"\\\\\\\"\");\n  return \", near \\\"\" .. str .. \"\\\"\"\nend\n\nlocal Parser = {}\nfunction Parser.new (self, tokens)\n  self.tokens = tokens\n  self.parse_stack = {}\n  self.refs = {}\n  self.current = 0\n  return self\nend\n\nlocal exports = {version = \"1.2\"}\n\nlocal word = function(w) return \"^(\"..w..\")([%s$%c])\" end\n\nlocal tokens = {\n  {\"comment\",   \"^#[^\\n]*\"},\n  {\"indent\",    \"^\\n( *)\"},\n  {\"space\",     \"^ +\"},\n  {\"true\",      word(\"enabled\"),  const = true, value = true},\n  {\"true\",      word(\"true\"),     const = true, value = true},\n  {\"true\",      word(\"yes\"),      const = true, value = true},\n  {\"true\",      word(\"on\"),      const = true, value = true},\n  {\"false\",     word(\"disabled\"), const = true, value = false},\n  {\"false\",     word(\"false\"),    const = true, value = false},\n  {\"false\",     word(\"no\"),       const = true, value = false},\n  {\"false\",     word(\"off\"),      const = true, value = false},\n  {\"null\",      word(\"null\"),     const = true, value = nil},\n  {\"null\",      word(\"Null\"),     const = true, value = nil},\n  {\"null\",      word(\"NULL\"),     const = true, value = nil},\n  {\"null\",      word(\"~\"),        const = true, value = nil},\n  {\"id\",    \"^\\\"([^\\\"]-)\\\" *(:[%s%c])\"},\n  {\"id\",    \"^'([^']-)' *(:[%s%c])\"},\n  {\"string\",    \"^\\\"([^\\\"]-)\\\"\",  force_text = true},\n  {\"string\",    \"^'([^']-)'\",    force_text = true},\n  {\"timestamp\", \"^(%d%d%d%d)-(%d%d?)-(%d%d?)%s+(%d%d?):(%d%d):(%d%d)%s+(%-?%d%d?):(%d%d)\"},\n  {\"timestamp\", \"^(%d%d%d%d)-(%d%d?)-(%d%d?)%s+(%d%d?):(%d%d):(%d%d)%s+(%-?%d%d?)\"},\n  {\"timestamp\", \"^(%d%d%d%d)-(%d%d?)-(%d%d?)%s+(%d%d?):(%d%d):(%d%d)\"},\n  {\"timestamp\", \"^(%d%d%d%d)-(%d%d?)-(%d%d?)%s+(%d%d?):(%d%d)\"},\n  {\"timestamp\", \"^(%d%d%d%d)-(%d%d?)-(%d%d?)%s+(%d%d?)\"},\n  {\"timestamp\", \"^(%d%d%d%d)-(%d%d?)-(%d%d?)\"},\n  {\"doc\",       \"^%-%-%-[^%c]*\"},\n  {\",\",         \"^,\"},\n  {\"string\",    \"^%b{} *[^,%c]+\", noinline = true},\n  {\"{\",         \"^{\"},\n  {\"}\",         \"^}\"},\n  {\"string\",    \"^%b[] *[^,%c]+\", noinline = true},\n  {\"[\",         \"^%[\"},\n  {\"]\",         \"^%]\"},\n  {\"-\",         \"^%-\"},\n  {\":\",         \"^:\"},\n  {\"pipe\",      \"^(|)(%d*[+%-]?)\", sep = \"\\n\"},\n  {\"pipe\",      \"^(>)(%d*[+%-]?)\", sep = \" \"},\n  {\"id\",        \"^([%w][%w %-_]*)(:[%s%c])\"},\n  {\"string\",    \"^[^%c]+\", noinline = true},\n  {\"string\",    \"^[^,%c ]+\"}\n};\nexports.tokenize = function (str)\n  local token\n  local row = 0\n  local ignore\n  local indents = 0\n  local lastIndents\n  local stack = {}\n  local indentAmount = 0\n  local inline = false\n  str = str:gsub(\"\\r\\n\",\"\\010\")\n\n  while #str > 0 do\n    for i in ipairs(tokens) do\n      local captures = {}\n      if not inline or tokens[i].noinline == nil then\n        captures = {str:match(tokens[i][2])}\n      end\n\n      if #captures > 0 then\n        captures.input = str:sub(0, 25)\n        token = table_clone(tokens[i])\n        token[2] = captures\n        local str2 = str:gsub(tokens[i][2], \"\", 1)\n        token.raw = str:sub(1, #str - #str2)\n        str = str2\n\n        if token[1] == \"{\" or token[1] == \"[\" then\n          inline = true\n        elseif token.const then\n          -- Since word pattern contains last char we're re-adding it\n          str = token[2][2] .. str\n          token.raw = token.raw:sub(1, #token.raw - #token[2][2])\n        elseif token[1] == \"id\" then\n          -- Since id pattern contains last semi-colon we're re-adding it\n          str = token[2][2] .. str\n          token.raw = token.raw:sub(1, #token.raw - #token[2][2])\n          -- Trim\n          token[2][1] = string_trim(token[2][1])\n        elseif token[1] == \"string\" then\n          -- Finding numbers\n          local snip = token[2][1]\n          if not token.force_text then\n            if snip:match(\"^(%d+%.%d+)$\") or snip:match(\"^(%d+)$\") then\n              token[1] = \"number\"\n            end\n          end\n\n        elseif token[1] == \"comment\" then\n          ignore = true;\n        elseif token[1] == \"indent\" then\n          row = row + 1\n          inline = false\n          lastIndents = indents\n          if indentAmount == 0 then\n            indentAmount = #token[2][1]\n          end\n\n          if indentAmount ~= 0 then\n            indents = (#token[2][1] / indentAmount);\n          else\n            indents = 0\n          end\n\n          if indents == lastIndents then\n            ignore = true;\n          elseif indents > lastIndents + 2 then\n            error(\"SyntaxError: invalid indentation, got \" .. tostring(indents)\n              .. \" instead of \" .. tostring(lastIndents) .. context(token[2].input))\n          elseif indents > lastIndents + 1 then\n            push(stack, token)\n          elseif indents < lastIndents then\n            local input = token[2].input\n            token = {\"dedent\", {\"\", input = \"\"}}\n            token.input = input\n            while lastIndents > indents + 1 do\n              lastIndents = lastIndents - 1\n              push(stack, token)\n            end\n          end\n        end -- if token[1] == XXX\n        token.row = row\n        break\n      end -- if #captures > 0\n    end\n\n    if not ignore then\n      if token then\n        push(stack, token)\n        token = nil\n      else\n        error(\"SyntaxError \" .. context(str))\n      end\n    end\n\n    ignore = false;\n  end\n\n  return stack\nend\n\nParser.peek = function (self, offset)\n  offset = offset or 1\n  return self.tokens[offset + self.current]\nend\n\nParser.advance = function (self)\n  self.current = self.current + 1\n  return self.tokens[self.current]\nend\n\nParser.advanceValue = function (self)\n  return self:advance()[2][1]\nend\n\nParser.accept = function (self, type)\n  if self:peekType(type) then\n    return self:advance()\n  end\nend\n\nParser.expect = function (self, type, msg)\n  return self:accept(type) or\n    error(msg .. context(self:peek()[1].input))\nend\n\nParser.expectDedent = function (self, msg)\n  return self:accept(\"dedent\") or (self:peek() == nil) or\n    error(msg .. context(self:peek()[2].input))\nend\n\nParser.peekType = function (self, val, offset)\n  return self:peek(offset) and self:peek(offset)[1] == val\nend\n\nParser.ignore = function (self, items)\n  local advanced\n  repeat\n    advanced = false\n    for _,v in pairs(items) do\n      if self:peekType(v) then\n        self:advance()\n        advanced = true\n      end\n    end\n  until advanced == false\nend\n\nParser.ignoreSpace = function (self)\n  self:ignore{\"space\"}\nend\n\nParser.ignoreWhitespace = function (self)\n  self:ignore{\"space\", \"indent\", \"dedent\"}\nend\n\nParser.parse = function (self)\n\n  local ref = nil\n  if self:peekType(\"string\") and not self:peek().force_text then\n    local char = self:peek()[2][1]:sub(1,1)\n    if char == \"&\" then\n      ref = self:peek()[2][1]:sub(2)\n      self:advanceValue()\n      self:ignoreSpace()\n    elseif char == \"*\" then\n      ref = self:peek()[2][1]:sub(2)\n      return self.refs[ref]\n    end\n  end\n\n  local result\n  local c = {\n    indent = self:accept(\"indent\") and 1 or 0,\n    token = self:peek()\n  }\n  push(self.parse_stack, c)\n\n  if c.token[1] == \"doc\" then\n    result = self:parseDoc()\n  elseif c.token[1] == \"-\" then\n    result = self:parseList()\n  elseif c.token[1] == \"{\" then\n    result = self:parseInlineHash()\n  elseif c.token[1] == \"[\" then\n    result = self:parseInlineList()\n  elseif c.token[1] == \"id\" then\n    result = self:parseHash()\n  elseif c.token[1] == \"string\" then\n    result = self:parseString(\"\\n\")\n  elseif c.token[1] == \"timestamp\" then\n    result = self:parseTimestamp()\n  elseif c.token[1] == \"number\" then\n    result = tonumber(self:advanceValue())\n  elseif c.token[1] == \"pipe\" then\n    result = self:parsePipe()\n  elseif c.token.const == true then\n    self:advanceValue();\n    result = c.token.value\n  else\n    error(\"ParseError: unexpected token '\" .. c.token[1] .. \"'\" .. context(c.token.input))\n  end\n\n  pop(self.parse_stack)\n  while c.indent > 0 do\n    c.indent = c.indent - 1\n    local term = \"term \"..c.token[1]..\": '\"..c.token[2][1]..\"'\"\n    self:expectDedent(\"last \".. term ..\" is not properly dedented\")\n  end\n\n  if ref then\n    self.refs[ref] = result\n  end\n  return result\nend\n\nParser.parseDoc = function (self)\n  self:accept(\"doc\")\n  return self:parse()\nend\n\nParser.inline = function (self)\n  local current = self:peek(0)\n  if not current then\n    return {}, 0\n  end\n\n  local inline = {}\n  local i = 0\n\n  while self:peek(i) and not self:peekType(\"indent\", i) and current.row == self:peek(i).row do\n    inline[self:peek(i)[1]] = true\n    i = i - 1\n  end\n  return inline, -i\nend\n\nParser.isInline = function (self)\n  local _, i = self:inline()\n  return i > 0\nend\n\nParser.parent = function(self, level)\n  level = level or 1\n  return self.parse_stack[#self.parse_stack - level]\nend\n\nParser.parentType = function(self, type, level)\n  return self:parent(level) and self:parent(level).token[1] == type\nend\n\nParser.parseString = function (self)\n  if self:isInline() then\n    local result = self:advanceValue()\n\n    --[[\n      - a: this looks\n        flowing: but is\n        no: string\n    --]]\n    local types = self:inline()\n    if types[\"id\"] and types[\"-\"] then\n      if not self:peekType(\"indent\") or not self:peekType(\"indent\", 2) then\n        return result\n      end\n    end\n\n    --[[\n      a: 1\n      b: this is\n        a flowing string\n        example\n      c: 3\n    --]]\n    if self:peekType(\"indent\") then\n      self:expect(\"indent\", \"text block needs to start with indent\")\n      local addtl = self:accept(\"indent\")\n\n      result = result .. \"\\n\" .. self:parseTextBlock(\"\\n\")\n\n      self:expectDedent(\"text block ending dedent missing\")\n      if addtl then\n        self:expectDedent(\"text block ending dedent missing\")\n      end\n    end\n    return result\n  else\n    --[[\n      a: 1\n      b:\n        this is also\n        a flowing string\n        example\n      c: 3\n    --]]\n    return self:parseTextBlock(\"\\n\")\n  end\nend\n\nParser.parsePipe = function (self)\n  local pipe = self:expect(\"pipe\")\n  self:expect(\"indent\", \"text block needs to start with indent\")\n  local result = self:parseTextBlock(pipe.sep)\n  self:expectDedent(\"text block ending dedent missing\")\n  return result\nend\n\nParser.parseTextBlock = function (self, sep)\n  local token = self:advance()\n  local result = string_trim(token.raw, \"\\n\")\n  local indents = 0\n  while self:peek() ~= nil and ( indents > 0 or not self:peekType(\"dedent\") ) do\n    local newtoken = self:advance()\n    while token.row < newtoken.row do\n      result = result .. sep\n      token.row = token.row + 1\n    end\n    if newtoken[1] == \"indent\" then\n      indents = indents + 1\n    elseif newtoken[1] == \"dedent\" then\n      indents = indents - 1\n    else\n      result = result .. string_trim(newtoken.raw, \"\\n\")\n    end\n  end\n  return result\nend\n\nParser.parseHash = function (self, hash)\n  hash = hash or {}\n  local indents = 0\n\n  if self:isInline() then\n    local id = self:advanceValue()\n    self:expect(\":\", \"expected semi-colon after id\")\n    self:ignoreSpace()\n    if self:accept(\"indent\") then\n      indents = indents + 1\n      hash[id] = self:parse()\n    else\n      hash[id] = self:parse()\n      if self:accept(\"indent\") then\n        indents = indents + 1\n      end\n    end\n    self:ignoreSpace();\n  end\n\n  while self:peekType(\"id\") do\n    local id = self:advanceValue()\n    self:expect(\":\",\"expected semi-colon after id\")\n    self:ignoreSpace()\n    hash[id] = self:parse()\n    self:ignoreSpace();\n  end\n\n  while indents > 0 do\n    self:expectDedent(\"expected dedent\")\n    indents = indents - 1\n  end\n\n  return hash\nend\n\nParser.parseInlineHash = function (self)\n  local id\n  local hash = {}\n  local i = 0\n\n  self:accept(\"{\")\n  while not self:accept(\"}\") do\n    self:ignoreSpace()\n    if i > 0 then\n      self:expect(\",\",\"expected comma\")\n    end\n\n    self:ignoreWhitespace()\n    if self:peekType(\"id\") then\n      id = self:advanceValue()\n      if id then\n        self:expect(\":\",\"expected semi-colon after id\")\n        self:ignoreSpace()\n        hash[id] = self:parse()\n        self:ignoreWhitespace()\n      end\n    end\n\n    i = i + 1\n  end\n  return hash\nend\n\nParser.parseList = function (self)\n  local list = {}\n  while self:accept(\"-\") do\n    self:ignoreSpace()\n    list[#list + 1] = self:parse()\n\n    self:ignoreSpace()\n  end\n  return list\nend\n\nParser.parseInlineList = function (self)\n  local list = {}\n  local i = 0\n  self:accept(\"[\")\n  while not self:accept(\"]\") do\n    self:ignoreSpace()\n    if i > 0 then\n      self:expect(\",\",\"expected comma\")\n    end\n\n    self:ignoreSpace()\n    list[#list + 1] = self:parse()\n    self:ignoreSpace()\n    i = i + 1\n  end\n\n  return list\nend\n\nParser.parseTimestamp = function (self)\n  local capture = self:advance()[2]\n\n  return os.time{\n    year  = capture[1],\n    month = capture[2],\n    day   = capture[3],\n    hour  = capture[4] or 0,\n    min   = capture[5] or 0,\n    sec   = capture[6] or 0\n  }\nend\n\nexports.Eval = function (str)\n  return Parser:new(exports.tokenize(str)):parse()\nend\n\nexports.Read = function(file_name)\n  if file.Exists(file_name, 'GAME') then\n    local local_name = file_name:gsub('%.y([a]?)ml', '.local.y%1ml')\n\n    if file.Exists(local_name, 'GAME') then\n      file_name = local_name\n    end\n\n    return Parser:new(exports.tokenize(file.Read(file_name, 'GAME'))):parse()\n  end\nend\n\nexports.Dump = table_print\n\nix.yaml = exports"
  },
  {
    "path": "gamemode/core/libs/thirdparty/sv_mysql.lua",
    "content": "--[[\n\tmysql - 1.0.3\n\tA simple MySQL wrapper for Garry's Mod.\n\n\tAlexander Grist-Hucker\n\thttp://www.alexgrist.com\n\n\n\tThe MIT License (MIT)\n\n\tCopyright (c) 2014 Alex Grist-Hucker\n\n\tPermission is hereby granted, free of charge, to any person obtaining a copy\n\tof this software and associated documentation files (the \"Software\"), to deal\n\tin the Software without restriction, including without limitation the rights\n\tto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n\tcopies of the Software, and to permit persons to whom the Software is\n\tfurnished to do so, subject to the following conditions:\n\n\tThe above copyright notice and this permission notice shall be included in all\n\tcopies or substantial portions of the Software.\n\n\tTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n\tIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n\tFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n\tAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n\tLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n\tOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n\tSOFTWARE.\n--]]\n\nmysql = mysql or {\n\tmodule = \"sqlite\"\n}\n\nlocal QueueTable = {}\nlocal tostring = tostring\nlocal table = table\n\n--[[\n\tReplacement tables\n--]]\n\nlocal Replacements = {\n\tsqlite = {\n\t\tCreate = {\n\t\t\t{\"UNSIGNED \", \"\"},\n\t\t\t{\"NOT NULL AUTO_INCREMENT\", \"\"}, -- assuming primary key\n\t\t\t{\"AUTO_INCREMENT\", \"\"},\n\t\t\t{\"INT%(%d*%)\", \"INTEGER\"},\n\t\t\t{\"INT \", \"INTEGER\"}\n\t\t}\n\t}\n}\n\n--[[\n\tPhrases\n--]]\n\nlocal MODULE_NOT_EXIST = \"[mysql] The %s module does not exist!\\n\"\n\n--[[\n\tBegin Query Class.\n--]]\n\nlocal QUERY_CLASS = {}\nQUERY_CLASS.__index = QUERY_CLASS\n\nfunction QUERY_CLASS:New(tableName, queryType)\n\tlocal newObject = setmetatable({}, QUERY_CLASS)\n\t\tnewObject.queryType = queryType\n\t\tnewObject.tableName = tableName\n\t\tnewObject.selectList = {}\n\t\tnewObject.insertList = {}\n\t\tnewObject.updateList = {}\n\t\tnewObject.createList = {}\n\t\tnewObject.whereList = {}\n\t\tnewObject.orderByList = {}\n\treturn newObject\nend\n\nfunction QUERY_CLASS:Escape(text)\n\treturn mysql:Escape(tostring(text))\nend\n\nfunction QUERY_CLASS:ForTable(tableName)\n\tself.tableName = tableName\nend\n\nfunction QUERY_CLASS:Where(key, value)\n\tself:WhereEqual(key, value)\nend\n\nfunction QUERY_CLASS:WhereEqual(key, value)\n\tself.whereList[#self.whereList + 1] = \"`\"..key..\"` = '\"..self:Escape(value)..\"'\"\nend\n\nfunction QUERY_CLASS:WhereNotEqual(key, value)\n\tself.whereList[#self.whereList + 1] = \"`\"..key..\"` != '\"..self:Escape(value)..\"'\"\nend\n\nfunction QUERY_CLASS:WhereLike(key, value, format)\n\tformat = format or \"%%%s%%\"\n\tself.whereList[#self.whereList + 1] = \"`\"..key..\"` LIKE '\"..string.format(format, self:Escape(value))..\"'\"\nend\n\nfunction QUERY_CLASS:WhereNotLike(key, value, format)\n\tformat = format or \"%%%s%%\"\n\tself.whereList[#self.whereList + 1] = \"`\"..key..\"` NOT LIKE '\"..string.format(format, self:Escape(value))..\"'\"\nend\n\nfunction QUERY_CLASS:WhereGT(key, value)\n\tself.whereList[#self.whereList + 1] = \"`\"..key..\"` > '\"..self:Escape(value)..\"'\"\nend\n\nfunction QUERY_CLASS:WhereLT(key, value)\n\tself.whereList[#self.whereList + 1] = \"`\"..key..\"` < '\"..self:Escape(value)..\"'\"\nend\n\nfunction QUERY_CLASS:WhereGTE(key, value)\n\tself.whereList[#self.whereList + 1] = \"`\"..key..\"` >= '\"..self:Escape(value)..\"'\"\nend\n\nfunction QUERY_CLASS:WhereLTE(key, value)\n\tself.whereList[#self.whereList + 1] = \"`\"..key..\"` <= '\"..self:Escape(value)..\"'\"\nend\n\nfunction QUERY_CLASS:WhereIn(key, value)\n\tvalue = istable(value) and value or {value}\n\n\tlocal values = \"\"\n\tlocal bFirst = true\n\n\tfor k, v in pairs(value) do\n\t\tvalues = values .. (bFirst and \"\" or \", \") .. \"'\" .. self:Escape(v) .. \"'\"\n\t\tbFirst = false\n\tend\n\n\tself.whereList[#self.whereList + 1] = \"`\"..key..\"` IN (\"..values..\")\"\nend\n\nfunction QUERY_CLASS:OrderByDesc(key)\n\tself.orderByList[#self.orderByList + 1] = \"`\"..key..\"` DESC\"\nend\n\nfunction QUERY_CLASS:OrderByAsc(key)\n\tself.orderByList[#self.orderByList + 1] = \"`\"..key..\"` ASC\"\nend\n\nfunction QUERY_CLASS:Callback(queryCallback)\n\tself.callback = queryCallback\nend\n\nfunction QUERY_CLASS:Select(fieldName)\n\tself.selectList[#self.selectList + 1] = \"`\"..fieldName..\"`\"\nend\n\nfunction QUERY_CLASS:Insert(key, value)\n\tself.insertList[#self.insertList + 1] = {\"`\"..key..\"`\", \"'\"..self:Escape(value)..\"'\"}\nend\n\nfunction QUERY_CLASS:Update(key, value)\n\tself.updateList[#self.updateList + 1] = {\"`\"..key..\"`\", \"'\"..self:Escape(value)..\"'\"}\nend\n\nfunction QUERY_CLASS:Create(key, value)\n\tself.createList[#self.createList + 1] = {\"`\"..key..\"`\", value}\nend\n\nfunction QUERY_CLASS:Add(key, value)\n\tself.add = {\"`\"..key..\"`\", value}\nend\n\nfunction QUERY_CLASS:Drop(key)\n\tself.drop = \"`\"..key..\"`\"\nend\n\nfunction QUERY_CLASS:PrimaryKey(key)\n\tself.primaryKey = \"`\"..key..\"`\"\nend\n\nfunction QUERY_CLASS:Limit(value)\n\tself.limit = value\nend\n\nfunction QUERY_CLASS:Offset(value)\n\tself.offset = value\nend\n\nlocal function ApplyQueryReplacements(mode, query)\n\tif (!Replacements[mysql.module]) then\n\t\treturn query\n\tend\n\n\tlocal result = query\n\tlocal entries = Replacements[mysql.module][mode]\n\n\tfor i = 1, #entries do\n\t\tresult = string.gsub(result, entries[i][1], entries[i][2])\n\tend\n\n\treturn result\nend\n\nlocal function BuildSelectQuery(queryObj)\n\tlocal queryString = {\"SELECT\"}\n\n\tif (!istable(queryObj.selectList) or #queryObj.selectList == 0) then\n\t\tqueryString[#queryString + 1] = \" *\"\n\telse\n\t\tqueryString[#queryString + 1] = \" \"..table.concat(queryObj.selectList, \", \")\n\tend\n\n\tif (isstring(queryObj.tableName)) then\n\t\tqueryString[#queryString + 1] = \" FROM `\"..queryObj.tableName..\"` \"\n\telse\n\t\tErrorNoHalt(\"[mysql] No table name specified!\\n\")\n\t\treturn\n\tend\n\n\tif (istable(queryObj.whereList) and #queryObj.whereList > 0) then\n\t\tqueryString[#queryString + 1] = \" WHERE \"\n\t\tqueryString[#queryString + 1] = table.concat(queryObj.whereList, \" AND \")\n\tend\n\n\tif (istable(queryObj.orderByList) and #queryObj.orderByList > 0) then\n\t\tqueryString[#queryString + 1] = \" ORDER BY \"\n\t\tqueryString[#queryString + 1] = table.concat(queryObj.orderByList, \", \")\n\tend\n\n\tif (isnumber(queryObj.limit)) then\n\t\tqueryString[#queryString + 1] = \" LIMIT \"\n\t\tqueryString[#queryString + 1] = queryObj.limit\n\tend\n\n\tif (isnumber(queryObj.offset)) then\n\t\tqueryString[#queryString + 1] = \" OFFSET \"\n\t\tqueryString[#queryString + 1] = queryObj.offset\n\tend\n\n\treturn table.concat(queryString)\nend\n\nlocal function BuildInsertQuery(queryObj, bIgnore)\n\tlocal suffix = (bIgnore and (mysql.module == \"sqlite\" and \"INSERT OR IGNORE INTO\" or \"INSERT IGNORE INTO\") or \"INSERT INTO\")\n\tlocal queryString = {suffix}\n\tlocal keyList = {}\n\tlocal valueList = {}\n\n\tif (isstring(queryObj.tableName)) then\n\t\tqueryString[#queryString + 1] = \" `\"..queryObj.tableName..\"`\"\n\telse\n\t\tErrorNoHalt(\"[mysql] No table name specified!\\n\")\n\t\treturn\n\tend\n\n\tfor i = 1, #queryObj.insertList do\n\t\tkeyList[#keyList + 1] = queryObj.insertList[i][1]\n\t\tvalueList[#valueList + 1] = queryObj.insertList[i][2]\n\tend\n\n\tif (#keyList == 0) then\n\t\treturn\n\tend\n\n\tqueryString[#queryString + 1] = \" (\"..table.concat(keyList, \", \")..\")\"\n\tqueryString[#queryString + 1] = \" VALUES (\"..table.concat(valueList, \", \")..\")\"\n\n\treturn table.concat(queryString)\nend\n\nlocal function BuildUpdateQuery(queryObj)\n\tlocal queryString = {\"UPDATE\"}\n\n\tif (isstring(queryObj.tableName)) then\n\t\tqueryString[#queryString + 1] = \" `\"..queryObj.tableName..\"`\"\n\telse\n\t\tErrorNoHalt(\"[mysql] No table name specified!\\n\")\n\t\treturn\n\tend\n\n\tif (istable(queryObj.updateList) and #queryObj.updateList > 0) then\n\t\tlocal updateList = {}\n\n\t\tqueryString[#queryString + 1] = \" SET\"\n\n\t\tfor i = 1, #queryObj.updateList do\n\t\t\tupdateList[#updateList + 1] = queryObj.updateList[i][1]..\" = \"..queryObj.updateList[i][2]\n\t\tend\n\n\t\tqueryString[#queryString + 1] = \" \"..table.concat(updateList, \", \")\n\tend\n\n\tif (istable(queryObj.whereList) and #queryObj.whereList > 0) then\n\t\tqueryString[#queryString + 1] = \" WHERE \"\n\t\tqueryString[#queryString + 1] = table.concat(queryObj.whereList, \" AND \")\n\tend\n\n\tif (isnumber(queryObj.offset)) then\n\t\tqueryString[#queryString + 1] = \" OFFSET \"\n\t\tqueryString[#queryString + 1] = queryObj.offset\n\tend\n\n\treturn table.concat(queryString)\nend\n\nlocal function BuildDeleteQuery(queryObj)\n\tlocal queryString = {\"DELETE FROM\"}\n\n\tif (isstring(queryObj.tableName)) then\n\t\tqueryString[#queryString + 1] = \" `\"..queryObj.tableName..\"`\"\n\telse\n\t\tErrorNoHalt(\"[mysql] No table name specified!\\n\")\n\t\treturn\n\tend\n\n\tif (istable(queryObj.whereList) and #queryObj.whereList > 0) then\n\t\tqueryString[#queryString + 1] = \" WHERE \"\n\t\tqueryString[#queryString + 1] = table.concat(queryObj.whereList, \" AND \")\n\tend\n\n\tif (isnumber(queryObj.limit)) then\n\t\tqueryString[#queryString + 1] = \" LIMIT \"\n\t\tqueryString[#queryString + 1] = queryObj.limit\n\tend\n\n\treturn table.concat(queryString)\nend\n\nlocal function BuildDropQuery(queryObj)\n\tlocal queryString = {\"DROP TABLE\"}\n\n\tif (isstring(queryObj.tableName)) then\n\t\tqueryString[#queryString + 1] = \" `\"..queryObj.tableName..\"`\"\n\telse\n\t\tErrorNoHalt(\"[mysql] No table name specified!\\n\")\n\t\treturn\n\tend\n\n\treturn table.concat(queryString)\nend\n\nlocal function BuildTruncateQuery(queryObj)\n\tlocal queryString = {\"TRUNCATE TABLE\"}\n\n\tif (isstring(queryObj.tableName)) then\n\t\tqueryString[#queryString + 1] = \" `\"..queryObj.tableName..\"`\"\n\telse\n\t\tErrorNoHalt(\"[mysql] No table name specified!\\n\")\n\t\treturn\n\tend\n\n\treturn table.concat(queryString)\nend\n\nlocal function BuildCreateQuery(queryObj)\n\tlocal queryString = {\"CREATE TABLE IF NOT EXISTS\"}\n\n\tif (isstring(queryObj.tableName)) then\n\t\tqueryString[#queryString + 1] = \" `\"..queryObj.tableName..\"`\"\n\telse\n\t\tErrorNoHalt(\"[mysql] No table name specified!\\n\")\n\t\treturn\n\tend\n\n\tqueryString[#queryString + 1] = \" (\"\n\n\tif (istable(queryObj.createList) and #queryObj.createList > 0) then\n\t\tlocal createList = {}\n\n\t\tfor i = 1, #queryObj.createList do\n\t\t\tif (mysql.module == \"sqlite\") then\n\t\t\t\tcreateList[#createList + 1] = queryObj.createList[i][1]..\" \"..ApplyQueryReplacements(\"Create\", queryObj.createList[i][2])\n\t\t\telse\n\t\t\t\tcreateList[#createList + 1] = queryObj.createList[i][1]..\" \"..queryObj.createList[i][2]\n\t\t\tend\n\t\tend\n\n\t\tqueryString[#queryString + 1] = \" \"..table.concat(createList, \", \")\n\tend\n\n\tif (isstring(queryObj.primaryKey)) then\n\t\tqueryString[#queryString + 1] = \", PRIMARY KEY\"\n\t\tqueryString[#queryString + 1] = \" (\"..queryObj.primaryKey..\")\"\n\tend\n\n\tqueryString[#queryString + 1] = \" )\"\n\n\treturn table.concat(queryString)\nend\n\nlocal function BuildAlterQuery(queryObj)\n\tlocal queryString = {\"ALTER TABLE\"}\n\n\tif (isstring(queryObj.tableName)) then\n\t\tqueryString[#queryString + 1] = \" `\"..queryObj.tableName..\"`\"\n\telse\n\t\tErrorNoHalt(\"[mysql] No table name specified!\\n\")\n\t\treturn\n\tend\n\n\tif (istable(queryObj.add)) then\n\t\tqueryString[#queryString + 1] = \" ADD \"..queryObj.add[1]..\" \"..ApplyQueryReplacements(\"Create\", queryObj.add[2])\n\telseif (isstring(queryObj.drop)) then\n\t\tif (mysql.module == \"sqlite\") then\n\t\t\tErrorNoHalt(\"[mysql] Cannot drop columns in sqlite!\\n\")\n\t\t\treturn\n\t\tend\n\n\t\tqueryString[#queryString + 1] = \" DROP COLUMN \"..queryObj.drop\n\tend\n\n\treturn table.concat(queryString)\nend\n\nfunction QUERY_CLASS:Execute(bQueueQuery)\n\tlocal queryString = nil\n\tlocal queryType = string.lower(self.queryType)\n\n\tif (queryType == \"select\") then\n\t\tqueryString = BuildSelectQuery(self)\n\telseif (queryType == \"insert\") then\n\t\tqueryString = BuildInsertQuery(self)\n\telseif (queryType == \"insert ignore\") then\n\t\tqueryString = BuildInsertQuery(self, true)\n\telseif (queryType == \"update\") then\n\t\tqueryString = BuildUpdateQuery(self)\n\telseif (queryType == \"delete\") then\n\t\tqueryString = BuildDeleteQuery(self)\n\telseif (queryType == \"drop\") then\n\t\tqueryString = BuildDropQuery(self)\n\telseif (queryType == \"truncate\") then\n\t\tqueryString = BuildTruncateQuery(self)\n\telseif (queryType == \"create\") then\n\t\tqueryString = BuildCreateQuery(self)\n\telseif (queryType == \"alter\") then\n\t\tqueryString = BuildAlterQuery(self)\n\tend\n\n\tif (isstring(queryString)) then\n\t\tif (!bQueueQuery) then\n\t\t\treturn mysql:RawQuery(queryString, self.callback)\n\t\telse\n\t\t\treturn mysql:Queue(queryString, self.callback)\n\t\tend\n\tend\nend\n\n--[[\n\tEnd Query Class.\n--]]\n\nfunction mysql:Select(tableName)\n\treturn QUERY_CLASS:New(tableName, \"SELECT\")\nend\n\nfunction mysql:Insert(tableName)\n\treturn QUERY_CLASS:New(tableName, \"INSERT\")\nend\n\nfunction mysql:InsertIgnore(tableName)\n\treturn QUERY_CLASS:New(tableName, \"INSERT IGNORE\")\nend\n\nfunction mysql:Update(tableName)\n\treturn QUERY_CLASS:New(tableName, \"UPDATE\")\nend\n\nfunction mysql:Delete(tableName)\n\treturn QUERY_CLASS:New(tableName, \"DELETE\")\nend\n\nfunction mysql:Drop(tableName)\n\treturn QUERY_CLASS:New(tableName, \"DROP\")\nend\n\nfunction mysql:Truncate(tableName)\n\treturn QUERY_CLASS:New(tableName, \"TRUNCATE\")\nend\n\nfunction mysql:Create(tableName)\n\treturn QUERY_CLASS:New(tableName, \"CREATE\")\nend\n\nfunction mysql:Alter(tableName)\n\treturn QUERY_CLASS:New(tableName, \"ALTER\")\nend\n\nlocal UTF8MB4 = \"ALTER DATABASE %s CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci\"\n\n-- A function to connect to the MySQL database.\nfunction mysql:Connect(host, username, password, database, port, socket, flags)\n\tport = port or 3306\n\n\tif (self.module == \"mysqloo\") then\n\t\tif (!istable(mysqloo)) then\n\t\t\trequire(\"mysqloo\")\n\t\tend\n\n\t\tif (mysqloo) then\n\t\t\tif (self.connection and self.connection:ping()) then\n\t\t\t\treturn\n\t\t\tend\n\n\t\t\tlocal clientFlag = flags or 0\n\n\t\t\tif (!isstring(socket)) then\n\t\t\t\tself.connection = mysqloo.connect(host, username, password, database, port)\n\t\t\telse\n\t\t\t\tself.connection = mysqloo.connect(host, username, password, database, port, socket, clientFlag)\n\t\t\tend\n\n\t\t\tself.connection.onConnected = function(connection)\n\t\t        local success, error_message = connection:setCharacterSet(\"utf8mb4\")\n\n\t\t        if (!success) then\n\t\t\t\t\tErrorNoHalt(\"Failed to set MySQL encoding!\\n\")\n\t\t\t\t\tErrorNoHalt(error_message .. \"\\n\")\n\t\t\t\telse\n\t\t\t\t\tself:RawQuery(string.format(UTF8MB4, database))\n\t\t        end\n\n\t\t\t\tmysql:OnConnected()\n\t\t\tend\n\n\t\t\tself.connection.onConnectionFailed = function(database, errorText)\n\t\t\t\tmysql:OnConnectionFailed(errorText)\n\t\t\tend\n\n\t\t\tself.connection:connect()\n\n\t\t\ttimer.Create(\"mysql.KeepAlive\", 300, 0, function()\n\t\t\t\tself.connection:ping()\n\t\t\tend)\n\t\telse\n\t\t\tErrorNoHalt(string.format(MODULE_NOT_EXIST, self.module))\n\t\tend\n\telseif (self.module == \"sqlite\") then\n\t\ttimer.Simple(0, function()\n\t\t\tmysql:OnConnected()\n\t\tend)\n\tend\nend\n\n-- A function to query the MySQL database.\nfunction mysql:RawQuery(query, callback, flags, ...)\n\tif (self.module == \"mysqloo\") then\n\t\tlocal queryObj = self.connection:query(query)\n\n\t\tqueryObj:setOption(mysqloo.OPTION_NAMED_FIELDS)\n\n\t\tqueryObj.onSuccess = function(queryObj, result)\n\t\t\tif (callback) then\n\t\t\t\tlocal bStatus, value = pcall(callback, result, true, tonumber(queryObj:lastInsert()))\n\n\t\t\t\tif (!bStatus) then\n\t\t\t\t\terror(string.format(\"[mysql] MySQL Callback Error!\\n%s\\n\", value))\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\n\t\tqueryObj.onError = function(queryObj, errorText)\n\t\t\tErrorNoHalt(string.format(\"[mysql] MySQL Query Error!\\nQuery: %s\\n%s\\n\", query, errorText))\n\t\tend\n\n\t\tqueryObj:start()\n\telseif (self.module == \"sqlite\") then\n\t\tlocal result = sql.Query(query)\n\n\t\tif (result == false) then\n\t\t\terror(string.format(\"[mysql] SQL Query Error!\\nQuery: %s\\n%s\\n\", query, sql.LastError()))\n\t\telse\n\t\t\tif (callback) then\n\t\t\t\tlocal bStatus, value = pcall(callback, result, true, tonumber(sql.QueryValue(\"SELECT last_insert_rowid()\")))\n\n\t\t\t\tif (!bStatus) then\n\t\t\t\t\terror(string.format(\"[mysql] SQL Callback Error!\\n%s\\n\", value))\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\telse\n\t\tErrorNoHalt(string.format(\"[mysql] Unsupported module \\\"%s\\\"!\\n\", self.module))\n\tend\nend\n\n-- A function to add a query to the queue.\nfunction mysql:Queue(queryString, callback)\n\tif (isstring(queryString)) then\n\t\tQueueTable[#QueueTable + 1] = {queryString, callback}\n\tend\nend\n\n-- A function to escape a string for MySQL.\nfunction mysql:Escape(text)\n\tif (self.connection) then\n\t\tif (self.module == \"mysqloo\") then\n\t\t\treturn self.connection:escape(text)\n\t\tend\n\telse\n\t\treturn sql.SQLStr(text, true)\n\tend\nend\n\n-- A function to disconnect from the MySQL database.\nfunction mysql:Disconnect()\n\tif (self.connection) then\n\t\tif (self.module == \"mysqloo\") then\n\t\t\tself.connection:disconnect(true)\n\t\tend\n\tend\nend\n\nfunction mysql:Think()\n\tif (#QueueTable > 0) then\n\t\tif (istable(QueueTable[1])) then\n\t\t\tlocal queueObj = QueueTable[1]\n\t\t\tlocal queryString = queueObj[1]\n\t\t\tlocal callback = queueObj[2]\n\n\t\t\tif (isstring(queryString)) then\n\t\t\t\tself:RawQuery(queryString, callback)\n\t\t\tend\n\n\t\t\ttable.remove(QueueTable, 1)\n\t\tend\n\tend\nend\n\n-- A function to set the module that should be used.\nfunction mysql:SetModule(moduleName)\n\tself.module = moduleName\nend\n\n-- Called when the database connects sucessfully.\nfunction mysql:OnConnected()\n\tMsgC(Color(25, 235, 25), \"[mysql] Connected to the database!\\n\")\n\n\thook.Run(\"DatabaseConnected\")\nend\n\n-- Called when the database connection fails.\nfunction mysql:OnConnectionFailed(errorText)\n\tErrorNoHalt(string.format(\"[mysql] Unable to connect to the database!\\n%s\\n\", errorText))\n\n\thook.Run(\"DatabaseConnectionFailed\", errorText)\nend\n\n-- A function to check whether or not the module is connected to a database.\nfunction mysql:IsConnected()\n\treturn self.module == \"mysqloo\" and (self.connection and self.connection:ping()) or self.module == \"sqlite\"\nend\n\nreturn mysql\n"
  },
  {
    "path": "gamemode/core/meta/sh_character.lua",
    "content": "\n--[[--\nContains information about a player's current game state.\n\nCharacters are a fundamental object type in Helix. They are distinct from players, where players are the representation of a\nperson's existence in the server that owns a character, and their character is their currently selected persona. All the\ncharacters that a player owns will be loaded into memory once they connect to the server. Characters are saved during a regular\ninterval, and during specific events (e.g when the owning player switches away from one character to another).\n\nThey contain all information that is not persistent with the player; names, descriptions, model, currency, etc. For the most\npart, you'll want to keep all information stored on the character since it will probably be different or change if the\nplayer switches to another character. An easy way to do this is to use `ix.char.RegisterVar` to easily create accessor functions\nfor variables that automatically save to the character object.\n]]\n-- @classmod Character\n\nlocal CHAR = ix.meta.character or {}\nCHAR.__index = CHAR\nCHAR.id = CHAR.id or 0\nCHAR.vars = CHAR.vars or {}\n\n-- @todo not this\nif (!ix.db) then\n\tix.util.Include(\"../libs/sv_database.lua\")\nend\n\n--- Returns a string representation of this character\n-- @realm shared\n-- @treturn string String representation\n-- @usage print(ix.char.loaded[1])\n-- > \"character[1]\"\nfunction CHAR:__tostring()\n\treturn \"character[\"..(self.id or 0)..\"]\"\nend\n\n--- Returns true if this character is equal to another character. Internally, this checks character IDs.\n-- @realm shared\n-- @char other Character to compare to\n-- @treturn bool Whether or not this character is equal to the given character\n-- @usage print(ix.char.loaded[1] == ix.char.loaded[2])\n-- > false\nfunction CHAR:__eq(other)\n\treturn self:GetID() == other:GetID()\nend\n\n--- Returns this character's database ID. This is guaranteed to be unique.\n-- @realm shared\n-- @treturn number Unique ID of character\nfunction CHAR:GetID()\n\treturn self.id\nend\n\nif (SERVER) then\n\t--- Saves this character's info to the database.\n\t-- @realm server\n\t-- @func[opt=nil] callback Function to call when the save has completed.\n\t-- @usage ix.char.loaded[1]:Save(function()\n\t-- \tprint(\"done!\")\n\t-- end)\n\t-- > done! -- after a moment\n\tfunction CHAR:Save(callback)\n\t\t-- Do not save if the character is for a bot.\n\t\tif (self.isBot) then\n\t\t\treturn\n\t\tend\n\n\t\t-- Let plugins/schema determine if the character should be saved.\n\t\tlocal shouldSave = hook.Run(\"CharacterPreSave\", self)\n\n\t\tif (shouldSave != false) then\n\t\t\t-- Run a query to save the character to the database.\n\t\t\tlocal query = mysql:Update(\"ix_characters\")\n\t\t\t\t-- update all character vars\n\t\t\t\tfor k, v in pairs(ix.char.vars) do\n\t\t\t\t\tif (v.field and self.vars[k] != nil and !v.bSaveLoadInitialOnly) then\n\t\t\t\t\t\tlocal value = self.vars[k]\n\n\t\t\t\t\t\tquery:Update(v.field, istable(value) and util.TableToJSON(value) or tostring(value))\n\t\t\t\t\tend\n\t\t\t\tend\n\n\t\t\t\tquery:Where(\"id\", self:GetID())\n\t\t\t\tquery:Callback(function()\n\t\t\t\t\tif (callback) then\n\t\t\t\t\t\tcallback()\n\t\t\t\t\tend\n\n\t\t\t\t\thook.Run(\"CharacterPostSave\", self)\n\t\t\t\tend)\n\t\t\tquery:Execute()\n\t\tend\n\tend\n\n\t--- Networks this character's information to make the given player aware of this character's existence. If the receiver is\n\t-- not the owner of this character, it will only be sent a limited amount of data (as it does not need anything else).\n\t-- This is done automatically by the framework.\n\t-- @internal\n\t-- @realm server\n\t-- @player[opt=nil] receiver Player to send the information to. This will sync to all connected players if set to `nil`.\n\tfunction CHAR:Sync(receiver)\n\t\t-- Broadcast the character information if receiver is not set.\n\t\tif (receiver == nil) then\n\t\t\tfor _, v in player.Iterator() do\n\t\t\t\tself:Sync(v)\n\t\t\tend\n\t\t-- Send all character information if the receiver is the character's owner.\n\t\telseif (receiver == self.player) then\n\t\t\tlocal data = {}\n\n\t\t\tfor k, v in pairs(self.vars) do\n\t\t\t\tif (ix.char.vars[k] != nil and !ix.char.vars[k].bNoNetworking) then\n\t\t\t\t\tdata[k] = v\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tnet.Start(\"ixCharacterInfo\")\n\t\t\t\tnet.WriteTable(data)\n\t\t\t\tnet.WriteUInt(self:GetID(), 32)\n\t\t\t\tnet.WriteUInt(self.player:EntIndex(), 8)\n\t\t\tnet.Send(self.player)\n\t\telse\n\t\t\tlocal data = {}\n\n\t\t\tfor k, v in pairs(ix.char.vars) do\n\t\t\t\tif (!v.bNoNetworking and !v.isLocal) then\n\t\t\t\t\tdata[k] = self.vars[k]\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tnet.Start(\"ixCharacterInfo\")\n\t\t\t\tnet.WriteTable(data)\n\t\t\t\tnet.WriteUInt(self:GetID(), 32)\n\t\t\t\tnet.WriteUInt(self.player:EntIndex(), 8)\n\t\t\tnet.Send(receiver)\n\t\tend\n\tend\n\n\t-- Sets up the \"appearance\" related inforomation for the character.\n\t--- Applies the character's appearance and synchronizes information to the owning player.\n\t-- @realm server\n\t-- @internal\n\t-- @bool[opt] bNoNetworking Whether or not to sync the character info to other players\n\tfunction CHAR:Setup(bNoNetworking)\n\t\tlocal client = self:GetPlayer()\n\n\t\tif (IsValid(client)) then\n\t\t\t-- Set the faction, model, and character index for the player.\n\t\t\tlocal model = self:GetModel()\n\n\t\t\tclient:SetNetVar(\"char\", self:GetID())\n\t\t\tclient:SetTeam(self:GetFaction())\n\t\t\tclient:SetModel(istable(model) and model[1] or model)\n\n\t\t\t-- Apply saved body groups.\n\t\t\tfor k, v in pairs(self:GetData(\"groups\", {})) do\n\t\t\t\tclient:SetBodygroup(k, v)\n\t\t\tend\n\n\t\t\t-- Apply a saved skin.\n\t\t\tclient:SetSkin(self:GetData(\"skin\", 0))\n\n\t\t\t-- Synchronize the character if we should.\n\t\t\tif (!bNoNetworking) then\n\t\t\t\tif (client:IsBot()) then\n\t\t\t\t\ttimer.Simple(0.33, function()\n\t\t\t\t\t\tself:Sync()\n\t\t\t\t\tend)\n\t\t\t\telse\n\t\t\t\t\tself:Sync()\n\t\t\t\tend\n\n\t\t\t\tfor _, v in ipairs(self:GetInventory(true)) do\n\t\t\t\t\tif (istable(v)) then\n\t\t\t\t\t\tv:AddReceiver(client)\n\t\t\t\t\t\tv:Sync(client)\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tlocal id = self:GetID()\n\n\t\t\thook.Run(\"CharacterLoaded\", ix.char.loaded[id])\n\n\t\t\tnet.Start(\"ixCharacterLoaded\")\n\t\t\t\tnet.WriteUInt(id, 32)\n\t\t\tnet.Send(client)\n\n\t\t\tself.firstTimeLoaded = true\n\t\tend\n\tend\n\n\t--- Forces a player off their current character, and sends them to the character menu to select a character.\n\t-- @realm server\n\tfunction CHAR:Kick()\n\t\t-- Kill the player so they are not standing anywhere.\n\t\tlocal client = self:GetPlayer()\n\t\tclient:KillSilent()\n\n\t\tlocal steamID = client:SteamID64()\n\t\tlocal id = self:GetID()\n\t\tlocal isCurrentChar = self and self:GetID() == id\n\n\t\t-- Return the player to the character menu.\n\t\tif (self and self.steamID == steamID) then\n\t\t\tnet.Start(\"ixCharacterKick\")\n\t\t\t\tnet.WriteBool(isCurrentChar)\n\t\t\tnet.Send(client)\n\n\t\t\tif (isCurrentChar) then\n\t\t\t\tclient:SetNetVar(\"char\", nil)\n\t\t\t\tclient:Spawn()\n\t\t\tend\n\t\tend\n\tend\n\n\t--- Forces a player off their current character, and prevents them from using the character for the specified amount of time.\n\t-- @realm server\n\t-- @number[opt] time Amount of seconds to ban the character for. If left as `nil`, the character will be banned permanently\n\tfunction CHAR:Ban(time)\n\t\ttime = tonumber(time)\n\n\t\tif (time) then\n\t\t\t-- If time is provided, adjust it so it becomes the un-ban time.\n\t\t\ttime = os.time() + math.max(math.ceil(time), 60)\n\t\tend\n\n\t\t-- Mark the character as banned and kick the character back to menu.\n\t\tself:SetData(\"banned\", time or true)\n\t\tself:Kick()\n\tend\nend\n\n--- Returns the player that owns this character.\n-- @realm shared\n-- @treturn player Player that owns this character\nfunction CHAR:GetPlayer()\n\t-- Set the player from entity index.\n\tif (isnumber(self.player)) then\n\t\tlocal client = Entity(self.player)\n\n\t\tif (IsValid(client)) then\n\t\t\tself.player = client\n\n\t\t\treturn client\n\t\tend\n\t-- Return the player from cache.\n\telseif (IsValid(self.player)) then\n\t\treturn self.player\n\t-- Search for which player owns this character.\n\telseif (self.steamID) then\n\t\tlocal steamID = self.steamID\n\n\t\tfor _, v in player.Iterator() do\n\t\t\tif (v:SteamID64() == steamID) then\n\t\t\t\tself.player = v\n\n\t\t\t\treturn v\n\t\t\tend\n\t\tend\n\tend\nend\n\n-- Sets up a new character variable.\nfunction ix.char.RegisterVar(key, data)\n\t-- Store information for the variable.\n\tix.char.vars[key] = data\n\tdata.index = data.index or table.Count(ix.char.vars)\n\n\tlocal upperName = key:sub(1, 1):upper() .. key:sub(2)\n\n\tif (SERVER) then\n\t\tif (data.field) then\n\t\t\tix.db.AddToSchema(\"ix_characters\", data.field, data.fieldType or ix.type.string)\n\t\tend\n\n\t\t-- Provide functions to change the variable if allowed.\n\t\tif (!data.bNotModifiable) then\n\t\t\t-- Overwrite the set function if desired.\n\t\t\tif (data.OnSet) then\n\t\t\t\tCHAR[\"Set\"..upperName] = data.OnSet\n\t\t\t-- Have the set function only set on the server if no networking.\n\t\t\telseif (data.bNoNetworking) then\n\t\t\t\tCHAR[\"Set\"..upperName] = function(self, value)\n\t\t\t\t\tself.vars[key] = value\n\t\t\t\tend\n\t\t\t-- If the variable is a local one, only send the variable to the local player.\n\t\t\telseif (data.isLocal) then\n\t\t\t\tCHAR[\"Set\"..upperName] = function(self, value)\n\t\t\t\t\tlocal oldVar = self.vars[key]\n\t\t\t\t\tself.vars[key] = value\n\n\t\t\t\t\tnet.Start(\"ixCharacterVarChanged\")\n\t\t\t\t\t\tnet.WriteUInt(self:GetID(), 32)\n\t\t\t\t\t\tnet.WriteString(key)\n\t\t\t\t\t\tnet.WriteType(value)\n\t\t\t\t\tnet.Send(self.player)\n\n\t\t\t\t\thook.Run(\"CharacterVarChanged\", self, key, oldVar, value)\n\t\t\t\tend\n\t\t\t-- Otherwise network the variable to everyone.\n\t\t\telse\n\t\t\t\tCHAR[\"Set\"..upperName] = function(self, value)\n\t\t\t\t\tlocal oldVar = self.vars[key]\n\t\t\t\t\tself.vars[key] = value\n\n\t\t\t\t\tnet.Start(\"ixCharacterVarChanged\")\n\t\t\t\t\t\tnet.WriteUInt(self:GetID(), 32)\n\t\t\t\t\t\tnet.WriteString(key)\n\t\t\t\t\t\tnet.WriteType(value)\n\t\t\t\t\tnet.Broadcast()\n\n\t\t\t\t\thook.Run(\"CharacterVarChanged\", self, key, oldVar, value)\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend\n\n\t-- The get functions are shared.\n\t-- Overwrite the get function if desired.\n\tif (data.OnGet) then\n\t\tCHAR[\"Get\"..upperName] = data.OnGet\n\t-- Otherwise return the character variable or default if it does not exist.\n\telse\n\t\tCHAR[\"Get\"..upperName] = function(self, default)\n\t\t\tlocal value = self.vars[key]\n\n\t\t\tif (value != nil) then\n\t\t\t\treturn value\n\t\t\tend\n\n\t\t\tif (default == nil) then\n\t\t\t\treturn ix.char.vars[key] and (istable(ix.char.vars[key].default) and table.Copy(ix.char.vars[key].default)\n\t\t\t\t\tor ix.char.vars[key].default)\n\t\t\tend\n\n\t\t\treturn default\n\t\tend\n\tend\n\n\tlocal alias = data.alias\n\n\tif (alias) then\n\t\tif (istable(alias)) then\n\t\t\tfor _, v in ipairs(alias) do\n\t\t\t\tlocal aliasName = v:sub(1, 1):upper()..v:sub(2)\n\n\t\t\t\tCHAR[\"Get\"..aliasName] = CHAR[\"Get\"..upperName]\n\t\t\t\tCHAR[\"Set\"..aliasName] = CHAR[\"Set\"..upperName]\n\t\t\tend\n\t\telseif (isstring(alias)) then\n\t\t\tlocal aliasName = alias:sub(1, 1):upper()..alias:sub(2)\n\n\t\t\tCHAR[\"Get\"..aliasName] = CHAR[\"Get\"..upperName]\n\t\t\tCHAR[\"Set\"..aliasName] = CHAR[\"Set\"..upperName]\n\t\tend\n\tend\n\n\t-- Add the variable default to the character object.\n\tCHAR.vars[key] = data.default\nend\n\n-- Allows access to the character metatable using ix.meta.character\nix.meta.character = CHAR\n"
  },
  {
    "path": "gamemode/core/meta/sh_entity.lua",
    "content": "\n--[[--\nPhysical object in the game world.\n\nEntities are physical representations of objects in the game world. Helix extends the functionality of entities to interface\nbetween Helix's own classes, and to reduce boilerplate code.\n\nSee the [Garry's Mod Wiki](https://wiki.garrysmod.com/page/Category:Entity) for all other methods that the `Player` class has.\n]]\n-- @classmod Entity\n\nlocal meta = FindMetaTable(\"Entity\")\nlocal CHAIR_CACHE = {}\n\n-- Add chair models to the cache by checking if its vehicle category is a class.\nfor _, v in pairs(list.Get(\"Vehicles\")) do\n\tif (v.Category == \"Chairs\") then\n\t\tCHAIR_CACHE[v.Model] = true\n\tend\nend\n\n--- Returns `true` if this entity is a chair.\n-- @realm shared\n-- @treturn bool Whether or not this entity is a chair\nfunction meta:IsChair()\n\treturn CHAIR_CACHE[self:GetModel()]\nend\n\n--- Returns `true` if this entity is a door. Internally, this checks to see if the entity's class has `door` in its name.\n-- @realm shared\n-- @treturn bool Whether or not the entity is a door\nfunction meta:IsDoor()\n\tlocal class = self:GetClass()\n\n\treturn (class and class:find(\"door\") != nil)\nend\n\nif (SERVER) then\n\t--- Returns `true` if the given entity is a button or door and is locked.\n\t-- @realm server\n\t-- @treturn bool Whether or not this entity is locked; `false` if this entity cannot be locked at all\n\t-- (e.g not a button or door)\n\tfunction meta:IsLocked()\n\t\tif (self:IsVehicle()) then\n\t\t\treturn self:GetInternalVariable( \"VehicleLocked\" )\n\t\tend\n\n\t\treturn self:GetInternalVariable( \"m_bLocked\" )\n\tend\n\n\t--- Returns the neighbouring door entity for double doors.\n\t-- @realm shared\n\t-- @treturn[1] Entity This door's partner\n\t-- @treturn[2] nil If the door does not have a partner\n\tfunction meta:GetDoorPartner()\n\t\treturn self.ixPartner\n\tend\n\n\t--- Returns the entity that is blocking this door from opening.\n\t-- @realm server\n\t-- @treturn[1] Entity Entity that is blocking this door\n\t-- @treturn[2] nil If this entity is not a door, or there is no blocking entity\n\tfunction meta:GetBlocker()\n\t\treturn self:GetInternalVariable( \"pBlocker\" )\n\tend\n\n\t--- Blasts a door off its hinges. Internally, this hides the door entity, spawns a physics prop with the same model, and\n\t-- applies force to the prop.\n\t-- @realm server\n\t-- @vector velocity Velocity to apply to the door\n\t-- @number lifeTime How long to wait in seconds before the door is put back on its hinges\n\t-- @bool bIgnorePartner Whether or not to ignore the door's partner in the case of double doors\n\t-- @treturn[1] Entity The physics prop created for the door\n\t-- @treturn nil If the entity is not a door\n\tfunction meta:BlastDoor(velocity, lifeTime, bIgnorePartner)\n\t\tif (!self:IsDoor()) then\n\t\t\treturn\n\t\tend\n\n\t\tif (IsValid(self.ixDummy)) then\n\t\t\tself.ixDummy:Remove()\n\t\tend\n\n\t\tvelocity = velocity or VectorRand()*100\n\t\tlifeTime = lifeTime or 120\n\n\t\tlocal partner = self:GetDoorPartner()\n\n\t\tif (IsValid(partner) and !bIgnorePartner) then\n\t\t\tpartner:BlastDoor(velocity, lifeTime, true)\n\t\tend\n\n\t\tlocal color = self:GetColor()\n\n\t\tlocal dummy = ents.Create(\"prop_physics\")\n\t\tdummy:SetModel(self:GetModel())\n\t\tdummy:SetPos(self:GetPos())\n\t\tdummy:SetAngles(self:GetAngles())\n\t\tdummy:Spawn()\n\t\tdummy:SetColor(color)\n\t\tdummy:SetMaterial(self:GetMaterial())\n\t\tdummy:SetSkin(self:GetSkin() or 0)\n\t\tdummy:SetRenderMode(RENDERMODE_TRANSALPHA)\n\t\tdummy:CallOnRemove(\"restoreDoor\", function()\n\t\t\tif (IsValid(self)) then\n\t\t\t\tself:SetNotSolid(false)\n\t\t\t\tself:SetNoDraw(false)\n\t\t\t\tself:DrawShadow(true)\n\t\t\t\tself.ignoreUse = false\n\t\t\t\tself.ixIsMuted = false\n\n\t\t\t\tfor _, v in ents.Iterator() do\n\t\t\t\t\tif (v:GetParent() == self) then\n\t\t\t\t\t\tv:SetNotSolid(false)\n\t\t\t\t\t\tv:SetNoDraw(false)\n\n\t\t\t\t\t\tif (v.OnDoorRestored) then\n\t\t\t\t\t\t\tv:OnDoorRestored(self)\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\t\tend)\n\t\tdummy:SetOwner(self)\n\t\tdummy:SetCollisionGroup(COLLISION_GROUP_WEAPON)\n\n\t\tself:Fire(\"unlock\")\n\t\tself:Fire(\"open\")\n\t\tself:SetNotSolid(true)\n\t\tself:SetNoDraw(true)\n\t\tself:DrawShadow(false)\n\t\tself.ignoreUse = true\n\t\tself.ixDummy = dummy\n\t\tself.ixIsMuted = true\n\t\tself:DeleteOnRemove(dummy)\n\n\t\tfor _, v in ipairs(self:GetBodyGroups() or {}) do\n\t\t\tdummy:SetBodygroup(v.id, self:GetBodygroup(v.id))\n\t\tend\n\n\t\tfor _, v in ents.Iterator() do\n\t\t\tif (v:GetParent() == self) then\n\t\t\t\tv:SetNotSolid(true)\n\t\t\t\tv:SetNoDraw(true)\n\n\t\t\t\tif (v.OnDoorBlasted) then\n\t\t\t\t\tv:OnDoorBlasted(self)\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\n\t\tdummy:GetPhysicsObject():SetVelocity(velocity)\n\n\t\tlocal uniqueID = \"doorRestore\"..self:EntIndex()\n\t\tlocal uniqueID2 = \"doorOpener\"..self:EntIndex()\n\n\t\ttimer.Create(uniqueID2, 1, 0, function()\n\t\t\tif (IsValid(self) and IsValid(self.ixDummy)) then\n\t\t\t\tself:Fire(\"open\")\n\t\t\telse\n\t\t\t\ttimer.Remove(uniqueID2)\n\t\t\tend\n\t\tend)\n\n\t\ttimer.Create(uniqueID, lifeTime, 1, function()\n\t\t\tif (IsValid(self) and IsValid(dummy)) then\n\t\t\t\tuniqueID = \"dummyFade\"..dummy:EntIndex()\n\t\t\t\tlocal alpha = 255\n\n\t\t\t\ttimer.Create(uniqueID, 0.1, 255, function()\n\t\t\t\t\tif (IsValid(dummy)) then\n\t\t\t\t\t\talpha = alpha - 1\n\t\t\t\t\t\tdummy:SetColor(ColorAlpha(color, alpha))\n\n\t\t\t\t\t\tif (alpha <= 0) then\n\t\t\t\t\t\t\tdummy:Remove()\n\t\t\t\t\t\tend\n\t\t\t\t\telse\n\t\t\t\t\t\ttimer.Remove(uniqueID)\n\t\t\t\t\tend\n\t\t\t\tend)\n\t\t\tend\n\t\tend)\n\n\t\treturn dummy\n\tend\n\nelse\n\t-- Returns the door's slave entity.\n\tfunction meta:GetDoorPartner()\n\t\tlocal owner = self:GetOwner() or self.ixDoorOwner\n\n\t\tif (IsValid(owner) and owner:IsDoor()) then\n\t\t\treturn owner\n\t\tend\n\n\t\tfor _, v in ipairs(ents.FindByClass(\"prop_door_rotating\")) do\n\t\t\tif (v:GetOwner() == self) then\n\t\t\t\tself.ixDoorOwner = v\n\n\t\t\t\treturn v\n\t\t\tend\n\t\tend\n\tend\nend\n"
  },
  {
    "path": "gamemode/core/meta/sh_inventory.lua",
    "content": "\n--[[--\nHolds items within a grid layout.\n\nInventories are an object that contains `Item`s in a grid layout. Every `Character` will have exactly one inventory attached to\nit, which is the only inventory that is allowed to hold bags - any item that has its own inventory (i.e a suitcase). Inventories\ncan be owned by a character, or it can be individually interacted with as a standalone object. For example, the container plugin\nattaches inventories to props, allowing for items to be stored outside of any character inventories and remain \"in the world\".\n\n\nYou may be looking for the following common functions:\n\n`Add` Which adds an item to the inventory.\n\n`GetItems` Which gets all of the items inside the inventory.\n\n`GetItemByID` Which gets an item in the inventory by it's item ID.\n\n`GetItemAt` Which gets an item in the inventory by it's x and y\n\n`GetID` Which gets the inventory's ID.\n\n`HasItem` Which checks if the inventory has an item.\n]]\n-- @classmod Inventory\n\nlocal META = ix.meta.inventory or ix.middleclass(\"ix_inventory\")\n\nMETA.slots = META.slots or {}\nMETA.w = META.w or 4\nMETA.h = META.h or 4\nMETA.vars = META.vars or {}\nMETA.receivers = META.receivers or {}\n\n--- Returns a string representation of this inventory\n-- @realm shared\n-- @treturn string String representation\n-- @usage print(ix.item.inventories[1])\n-- > \"inventory[1]\"\nfunction META:__tostring()\n\treturn \"inventory[\"..(self.id or 0)..\"]\"\nend\n\n--- Initializes the inventory with the provided arguments.\n-- @realm shared\n-- @internal\n-- @number id The `Inventory`'s database ID.\n-- @number width The inventory's width.\n-- @number height The inventory's height.\nfunction META:Initialize(id, width, height)\n\tself.id = id\n\tself.w = width\n\tself.h = height\n\n\tself.slots = {}\n\tself.vars = {}\n\tself.receivers = {}\nend\n\n--- Returns this inventory's database ID. This is guaranteed to be unique.\n-- @realm shared\n-- @treturn number Unique ID of inventory\nfunction META:GetID()\n\treturn self.id or 0\nend\n\n--- Sets the grid size of this inventory.\n-- @realm shared\n-- @internal\n-- @number width New width of inventory\n-- @number height New height of inventory\nfunction META:SetSize(width, height)\n\tself.w = width\n\tself.h = height\nend\n\n--- Returns the grid size of this inventory.\n-- @realm shared\n-- @treturn number Width of inventory\n-- @treturn number Height of inventory\nfunction META:GetSize()\n\treturn self.w, self.h\nend\n\n-- this is pretty good to debug/develop function to use.\nfunction META:Print(printPos)\n\tfor k, v in pairs(self:GetItems()) do\n\t\tlocal str = k .. \": \" .. v.name\n\n\t\tif (printPos) then\n\t\t\tstr = str .. \" (\" .. v.gridX .. \", \" .. v.gridY .. \")\"\n\t\tend\n\n\t\tprint(str)\n\tend\nend\n\n\n--- Searches the inventory to find any stacked items.\n-- A common problem with developing, is that items will sometimes error out, or get corrupt.\n-- Sometimes, the server knows things you don't while developing live\n-- This function can be helpful for getting rid of those pesky errors.\n-- @realm shared\nfunction META:FindError()\n\tfor k, _ in self:Iter() do\n\t\tif (k.width == 1 and k.height == 1) then\n\t\t\tcontinue\n\t\tend\n\n\t\tprint(\"Finding error: \" .. k.name)\n\t\tprint(\"Item Position: \" .. k.gridX, k.gridY)\n\n\t\tfor x = k.gridX, k.gridX + k.width - 1 do\n\t\t\tfor y = k.gridY, k.gridY + k.height - 1 do\n\t\t\t\tlocal item = self.slots[x][y]\n\n\t\t\t\tif (item and item.id != k.id) then\n\t\t\t\t\tprint(\"Error Found: \".. item.name)\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend\nend\n\n--- Prints out the id, width, height, slots and each item in each slot of an `Inventory`, used for debugging.\n-- @realm shared\nfunction META:PrintAll()\n\tprint(\"------------------------\")\n\t\tprint(\"INVID\", self:GetID())\n\t\tprint(\"INVSIZE\", self:GetSize())\n\n\t\tif (self.slots) then\n\t\t\tfor x = 1, self.w do\n\t\t\t\tfor y = 1, self.h do\n\t\t\t\t\tlocal item = self.slots[x] and self.slots[x][y]\n\t\t\t\t\tif (item and item.id) then\n\t\t\t\t\t\tprint(item.name .. \"(\" .. item.id .. \")\", x, y)\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\n\t\tprint(\"INVVARS\")\n\t\tPrintTable(self.vars or {})\n\tprint(\"------------------------\")\nend\n\n--- Returns the player that owns this inventory.\n-- @realm shared\n-- @treturn[1] Player Owning player\n-- @treturn[2] nil If no connected player owns this inventory\nfunction META:GetOwner()\n\tfor _, v in player.Iterator() do\n\t\tif (v:GetCharacter() and v:GetCharacter().id == self.owner) then\n\t\t\treturn v\n\t\tend\n\tend\nend\n\n--- Sets the player that owns this inventory.\n-- @realm shared\n-- @player owner The player to take control over the inventory.\n-- @bool fullUpdate Whether or not to update the inventory immediately to the new owner.\nfunction META:SetOwner(owner, fullUpdate)\n\tif (type(owner) == \"Player\" and owner:GetNetVar(\"char\")) then\n\t\towner = owner:GetNetVar(\"char\")\n\telseif (!isnumber(owner)) then\n\t\treturn\n\tend\n\n\tif (SERVER) then\n\t\tif (fullUpdate) then\n\t\t\tfor _, v in player.Iterator() do\n\t\t\t\tif (v:GetNetVar(\"char\") == owner) then\n\t\t\t\t\tself:Sync(v, true)\n\n\t\t\t\t\tbreak\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\n\t\tlocal query = mysql:Update(\"ix_inventories\")\n\t\t\tquery:Update(\"character_id\", owner)\n\t\t\tquery:Where(\"inventory_id\", self:GetID())\n\t\tquery:Execute()\n\tend\n\n\tself.owner = owner\nend\n\n--- Checks whether a player has access to an inventory\n-- @realm shared\n-- @internal\n-- @player client Player to check access for\n-- @treturn bool Whether or not the player has access to the inventory\nfunction META:OnCheckAccess(client)\n\tlocal bAccess = false\n\n\tfor _, v in ipairs(self:GetReceivers()) do\n\t\tif (v == client) then\n\t\t\tbAccess = true\n\t\t\tbreak\n\t\tend\n\tend\n\n\treturn bAccess\nend\n\n--- Checks whether or not an `Item` can fit into the `Inventory` starting from `x` and `y`.\n-- Internally used by FindEmptySlot, in most cases you are better off using that.\n-- This function will search if all of the slots within `x + width` and `y + width` are empty,\n-- ignoring any space the `Item` itself already occupies.\n-- @realm shared\n-- @internal\n-- @number x The beginning x coordinate to search for.\n-- @number y The beginning y coordiate to search for.\n-- @number w The `Item`'s width.\n-- @number h The `Item`'s height.\n-- @item[opt=nil] item2 An `Item`, if any, to ignore when searching.\nfunction META:CanItemFit(x, y, w, h, item2)\n\tlocal canFit = true\n\n\tfor x2 = 0, w - 1 do\n\t\tfor y2 = 0, h - 1 do\n\t\t\tlocal item = (self.slots[x + x2] or {})[y + y2]\n\n\t\t\tif ((x + x2) > self.w or item) then\n\t\t\t\tif (item2) then\n\t\t\t\t\tif (item and item.id == item2.id) then\n\t\t\t\t\t\tcontinue\n\t\t\t\t\tend\n\t\t\t\tend\n\n\t\t\t\tcanFit = false\n\t\t\t\tbreak\n\t\t\tend\n\t\tend\n\n\t\tif (!canFit) then\n\t\t\tbreak\n\t\tend\n\tend\n\n\treturn canFit\nend\n\n\n--- Returns the amount of slots currently filled in the Inventory.\n-- @realm shared\n-- @treturn number The amount of slots currently filled.\nfunction META:GetFilledSlotCount()\n\tlocal count = 0\n\n\tfor x = 1, self.w do\n\t\tfor y = 1, self.h do\n\t\t\tif ((self.slots[x] or {})[y]) then\n\t\t\t\tcount = count + 1\n\t\t\tend\n\t\tend\n\tend\n\n\treturn count\nend\n\n--- Finds an empty slot of a specified width and height.\n-- In most cases, to check if an `Item` can actually fit in the `Inventory`,\n-- as if it can't, it will just return `nil`.\n--\n-- FindEmptySlot will loop through all the slots for you, as opposed to `CanItemFit`\n-- which you specify an `x` and `y` for.\n-- this will call CanItemFit anyway.\n-- If you need to check if an item will fit *exactly* at a position, you want CanItemFit instead.\n-- @realm shared\n-- @number w The width of the `Item` you are trying to fit.\n-- @number h The height of the `Item` you are trying to fit.\n-- @bool onlyMain Whether or not to search any bags connected to this `Inventory`\n-- @treturn[1] number x The `x` coordinate that the `Item` can fit into.\n-- @treturn[1] number y The `y` coordinate that the `Item` can fit into.\n-- @treturn[2] number x The `x` coordinate that the `Item` can fit into.\n-- @treturn[2] number y The `y` coordinate that the `Item` can fit into.\n-- @treturn[2] Inventory bagInv If the item was in a bag, it will return the inventory it was in.\n-- @see CanItemFit\nfunction META:FindEmptySlot(w, h, onlyMain)\n\tw = w or 1\n\th = h or 1\n\n\tif (w > self.w or h > self.h) then\n\t\treturn\n\tend\n\n\tfor y = 1, self.h - (h - 1) do\n\t\tfor x = 1, self.w - (w - 1) do\n\t\t\tif (self:CanItemFit(x, y, w, h)) then\n\t\t\t\treturn x, y\n\t\t\tend\n\t\tend\n\tend\n\n\tif (onlyMain != true) then\n\t\tlocal bags = self:GetBags()\n\n\t\tif (#bags > 0) then\n\t\t\tfor _, invID in ipairs(bags) do\n\t\t\t\tlocal bagInv = ix.item.inventories[invID]\n\n\t\t\t\tif (bagInv) then\n\t\t\t\t\tlocal x, y = bagInv:FindEmptySlot(w, h)\n\n\t\t\t\t\tif (x and y) then\n\t\t\t\t\t\treturn x, y, bagInv\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend\nend\n\n--- Returns the item that currently exists within `x` and `y` in the `Inventory`.\n-- Items that have a width or height greater than 0 occupy more than 1 x and y.\n-- @realm shared\n-- @number x The `x` coordindate to search in.\n-- @number y The `y` coordinate to search in.\n-- @treturn number x The `x` coordinate that the `Item` is located at.\n-- @treturn number y The `y` coordinate that the `Item` is located at.\nfunction META:GetItemAt(x, y)\n\tif (self.slots and self.slots[x]) then\n\t\treturn self.slots[x][y]\n\tend\nend\n\n--- Removes an item from the inventory.\n-- @realm shared\n-- @number id The item instance ID to remove\n-- @bool[opt=false] bNoReplication Whether or not the item's removal should not be replicated\n-- @bool[opt=false] bNoDelete Whether or not the item should not be fully deleted\n-- @bool[opt=false] bTransferring Whether or not the item is being transferred to another inventory\n-- @treturn number The X position that the item was removed from\n-- @treturn number The Y position that the item was removed from\nfunction META:Remove(id, bNoReplication, bNoDelete, bTransferring)\n\tlocal x2, y2\n\n\tfor x = 1, self.w do\n\t\tif (self.slots[x]) then\n\t\t\tfor y = 1, self.h do\n\t\t\t\tlocal item = self.slots[x][y]\n\n\t\t\t\tif (item and item.id == id) then\n\t\t\t\t\tself.slots[x][y] = nil\n\n\t\t\t\t\tx2 = x2 or x\n\t\t\t\t\ty2 = y2 or y\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend\n\n\tif (SERVER and !bNoReplication) then\n\t\tlocal receivers = self:GetReceivers()\n\n\t\tif (istable(receivers)) then\n\t\t\tnet.Start(\"ixInventoryRemove\")\n\t\t\t\tnet.WriteUInt(id, 32)\n\t\t\t\tnet.WriteUInt(self:GetID(), 32)\n\t\t\tnet.Send(receivers)\n\t\tend\n\n\t\t-- we aren't removing the item - we're transferring it to another inventory\n\t\tif (!bTransferring) then\n\t\t\thook.Run(\"InventoryItemRemoved\", self, ix.item.instances[id])\n\t\tend\n\n\t\tif (!bNoDelete) then\n\t\t\tlocal item = ix.item.instances[id]\n\n\t\t\tif (item and item.OnRemoved) then\n\t\t\t\titem:OnRemoved()\n\t\t\tend\n\n\t\t\tlocal query = mysql:Delete(\"ix_items\")\n\t\t\t\tquery:Where(\"item_id\", id)\n\t\t\tquery:Execute()\n\n\t\t\tix.item.instances[id] = nil\n\t\tend\n\tend\n\n\treturn x2, y2\nend\n\n--- Adds a player as a receiver on this `Inventory`\n-- Receivers are players who will be networked the items inside the inventory.\n--\n-- Calling this will *not* automatically sync it's current contents to the client.\n-- All future contents will be synced, but not anything that was not synced before this is called.\n--\n-- This function does not check the validity of `client`, therefore if `client` doesn't exist, it will error.\n-- @realm shared\n-- @player client The player to add as a receiver.\nfunction META:AddReceiver(client)\n\tself.receivers[client] = true\nend\n\n--- The opposite of `AddReceiver`.\n-- This function does not check the validity of `client`, therefore if `client` doesn't exist, it will error.\n-- @realm shared\n-- @player client The player to remove from the receiver list.\nfunction META:RemoveReceiver(client)\n\tself.receivers[client] = nil\nend\n\n--- Get all of the receivers this `Inventory` has.\n-- Receivers are players who will be networked the items inside the inventory.\n--\n-- This function will automatically sort out invalid players for you.\n-- @realm shared\n-- @treturn table result The players who are on the server and allowed to see this table.\nfunction META:GetReceivers()\n\tlocal result = {}\n\n\tif (self.receivers) then\n\t\tfor k, _ in pairs(self.receivers) do\n\t\t\tif (IsValid(k) and k:IsPlayer()) then\n\t\t\t\tresult[#result + 1] = k\n\t\t\tend\n\t\tend\n\tend\n\n\treturn result\nend\n\n--- Returns a count of a *specific* `Item`s in the `Inventory`\n-- @realm shared\n-- @string uniqueID The Unique ID of the item.\n-- @bool onlyMain Whether or not to exclude bags that are present from the search.\n-- @treturn number The amount of `Item`s this inventory has.\n-- @usage local curHighest, winner = 0, false\n-- for client, character in ix.util.GetCharacters() do\n--  local itemCount = character:GetInventory():GetItemCount('water', false)\n--  if itemCount > curHighest then\n--   curHighest = itemCount\n--   winner = character\n--  end\n-- end\n-- -- Finds the thirstiest character on the server and returns their Character ID or false if no character has water.\nfunction META:GetItemCount(uniqueID, onlyMain)\n\tlocal i = 0\n\n\tfor _, v in pairs(self:GetItems(onlyMain)) do\n\t\tif (v.uniqueID == uniqueID) then\n\t\t\ti = i + 1\n\t\tend\n\tend\n\n\treturn i\nend\n\n--- Returns a table of all `Item`s in the `Inventory` by their Unique ID.\n-- Not to be confused with `GetItemsByID` or `GetItemByID` which take in an Item Instance's ID instead.\n-- @realm shared\n-- @string uniqueID The Unique ID of the item.\n-- @bool onlyMain Whether or not to exclude bags that are present from the search.\n-- @treturn number The table of specified `Item`s this inventory has.\nfunction META:GetItemsByUniqueID(uniqueID, onlyMain)\n\tlocal items = {}\n\n\tfor _, v in pairs(self:GetItems(onlyMain)) do\n\t\tif (v.uniqueID == uniqueID) then\n\t\t\titems[#items + 1] = v\n\t\tend\n\tend\n\n\treturn items\nend\n\n--- Returns a table of `Item`s by their base.\n-- @realm shared\n-- @string baseID The base to search for.\n-- @bool bOnlyMain Whether or not to exclude bags that are present from the search.\nfunction META:GetItemsByBase(baseID, bOnlyMain)\n\tlocal items = {}\n\n\tfor _, v in pairs(self:GetItems(bOnlyMain)) do\n\t\tif (v.base == baseID) then\n\t\t\titems[#items + 1] = v\n\t\tend\n\tend\n\n\treturn items\nend\n\n--- Get an item by it's specific Database ID.\n-- @realm shared\n-- @number id The ID to search for.\n-- @bool onlyMain Whether or not to exclude bags that are present from the search.\n-- @treturn item The item if it exists.\nfunction META:GetItemByID(id, onlyMain)\n\tfor _, v in pairs(self:GetItems(onlyMain)) do\n\t\tif (v.id == id) then\n\t\t\treturn v\n\t\tend\n\tend\nend\n\n--- Get a table of `Item`s by their specific Database ID.\n-- It's important to note that while in 99% of cases,\n-- items will have a unique Database ID, developers or random GMod weirdness could\n-- cause a second item with the same ID to appear, even though, `ix.item.instances` will only store one of those.\n-- The inventory only stores a reference to the `ix.item.instance` ID, not the memory reference itself.\n-- @realm shared\n-- @number id The ID to search for.\n-- @bool onlyMain Whether or not to exclude bags that are present from the search.\n-- @treturn item The item if it exists.\nfunction META:GetItemsByID(id, onlyMain)\n\tlocal items = {}\n\n\tfor _, v in pairs(self:GetItems(onlyMain)) do\n\t\tif (v.id == id) then\n\t\t\titems[#items + 1] = v\n\t\tend\n\tend\n\n\treturn items\nend\n\n-- This function may pretty heavy.\n\n--- Returns a table of all the items that an `Inventory` has.\n-- @realm shared\n-- @bool onlyMain Whether or not to exclude bags from this search.\n-- @treturn table The items this `Inventory` has.\nfunction META:GetItems(onlyMain)\n\tlocal items = {}\n\n\tfor _, v in pairs(self.slots) do\n\t\tfor _, v2 in pairs(v) do\n\t\t\tif (istable(v2) and !items[v2.id]) then\n\t\t\t\titems[v2.id] = v2\n\n\t\t\t\tv2.data = v2.data or {}\n\t\t\t\tlocal isBag = (((v2.base == \"base_bags\") or v2.isBag) and v2.data.id)\n\n\t\t\t\tif (isBag and isBag != self:GetID() and onlyMain != true) then\n\t\t\t\t\tlocal bagInv = ix.item.inventories[isBag]\n\n\t\t\t\t\tif (bagInv) then\n\t\t\t\t\t\tlocal bagItems = bagInv:GetItems()\n\n\t\t\t\t\t\ttable.Merge(items, bagItems)\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend\n\n\treturn items\nend\n\n--- Returns an iterator that returns all contained items, a better way to iterate items than `pairs(inventory:GetItems())`\n-- @realm shared\n-- @treturn function iterator\nfunction META:Iter()\n\tlocal x, y, item = 1, 1\n\n\treturn function()\n\t\titem = nil\n\n\t\trepeat\n\t\t\tif (x > self.w) then\n\t\t\t\tx, y = 1, y + 1\n\t\t\t\tif (y > self.h) then return nil end\n\t\t\tend\n\n\t\t\titem = self.slots[x] and self.slots[x][y]\n\t\t\tx = x + 1\n\t\tuntil item and not isbool(item) -- skip reserved slots in which items have yet to be fully instantiated\n\n\t\tif (item) then\n\t\t\treturn item, x, y\n\t\tend\n\tend\nend\n\n-- This function may pretty heavy.\n--- Returns a table of all the items that an `Inventory` has.\n-- @realm shared\n-- @bool onlyMain Whether or not to exclude bags from this search.\n-- @treturn table The items this `Inventory` has.\nfunction META:GetBags()\n\tlocal invs = {}\n\tfor _, v in pairs(self.slots) do\n\t\tfor _, v2 in pairs(v) do\n\t\t\tif (istable(v2) and v2.data) then\n\t\t\t\tlocal isBag = (((v2.base == \"base_bags\") or v2.isBag) and v2.data.id)\n\n\t\t\t\tif (!table.HasValue(invs, isBag)) then\n\t\t\t\t\tif (isBag and isBag != self:GetID()) then\n\t\t\t\t\t\tinvs[#invs + 1] = isBag\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend\n\n\treturn invs\nend\n--- Returns the item with the given unique ID (e.g `\"handheld_radio\"`) if it exists in this inventory.\n-- This method checks both\n-- this inventory, and any bags that this inventory has inside of it.\n-- @realm shared\n-- @string targetID Unique ID of the item to look for\n-- @tab[opt] data Item data to check for\n-- @treturn[1] Item Item that belongs to this inventory with the given criteria\n-- @treturn[2] bool `false` if the item does not exist\n-- @see HasItems\n-- @see HasItemOfBase\n-- @usage local item = inventory:HasItem(\"handheld_radio\")\n--\n-- if (item) then\n-- \t-- do something with the item table\n-- end\nfunction META:HasItem(targetID, data)\n\tfor k, _ in self:Iter() do\n\t\tif (k.uniqueID == targetID) then\n\t\t\tif (data) then\n\t\t\t\tlocal itemData = k.data\n\t\t\t\tlocal bFound = true\n\n\t\t\t\tfor dataKey, dataVal in pairs(data) do\n\t\t\t\t\tif (itemData[dataKey] != dataVal) then\n\t\t\t\t\t\tbFound = false\n\t\t\t\t\t\tbreak\n\t\t\t\t\tend\n\t\t\t\tend\n\n\t\t\t\tif (!bFound) then\n\t\t\t\t\tcontinue\n\t\t\t\tend\n\t\t\tend\n\n\t\t\treturn k\n\t\tend\n\tend\n\n\treturn false\nend\n\n--- Checks whether or not the `Inventory` has a table of items.\n-- This function takes a table with **no** keys and runs in order of first item > last item,\n--this is due to the usage of the `#` operator in the function.\n--\n-- @realm shared\n-- @tab targetIDs A table of `Item` Unique ID's.\n-- @treturn[1] bool true Whether or not the `Inventory` has all of the items.\n-- @treturn[1] table targetIDs Your provided targetIDs table, but it will be empty.\n-- @treturn[2] bool false\n-- @treturn[2] table targetIDs Table consisting of the items the `Inventory` did **not** have.\n-- @usage local itemFilter = {'water', 'water_sparkling'}\n-- if not Entity(1):GetCharacter():GetInventory():HasItems(itemFilter) then return end\n-- -- Filters out if this player has both a water, and a sparkling water.\nfunction META:HasItems(targetIDs)\n\tlocal count = #targetIDs -- assuming array\n\ttargetIDs = table.Copy(targetIDs)\n\n\tfor item, _ in self:Iter() do\n\t\tfor k, targetID in ipairs(targetIDs) do\n\t\t\tif (item.uniqueID == targetID) then\n\t\t\t\ttable.remove(targetIDs, k)\n\t\t\t\tcount = count - 1\n\n\t\t\t\tbreak\n\t\t\tend\n\t\tend\n\tend\n\n\treturn count <= 0, targetIDs\nend\n\n--- Whether or not an `Inventory` has an item of a base, optionally with specified data.\n-- This function has an optional `data` argument, which will take a `table`.\n-- it will match if the data of the item is correct or not.\n--\n-- Items which are a base will automatically have base_ prefixed to their Unique ID, if you are having\n-- trouble finding your base, that is probably why.\n-- @realm shared\n-- @string baseID The Item Base's Unique ID.\n-- @tab[opt] data The Item's data to compare against.\n-- @treturn[1] item The first `Item` of `baseID` that is found and there is no `data` argument or `data` was matched.\n-- @treturn[2] false If no `Item`s of `baseID` is found or the `data` argument, if specified didn't match.\n-- @usage local bHasWeaponEquipped = Entity(1):GetCharacter():GetInventory():HasItemOfBase('base_weapons', {['equip'] = true})\n-- if bHasWeaponEquipped then\n--  Entity(1):Notify('One gun is fun, two guns is Woo-tastic.')\n-- end\n-- -- Notifies the player that they should get some more guns.\nfunction META:HasItemOfBase(baseID, data)\n\tfor k, _ in self:Iter() do\n\t\tif (k.base == baseID) then\n\t\t\tif (data) then\n\t\t\t\tlocal itemData = k.data\n\t\t\t\tlocal bFound = true\n\n\t\t\t\tfor dataKey, dataVal in pairs(data) do\n\t\t\t\t\tif (itemData[dataKey] != dataVal) then\n\t\t\t\t\t\tbFound = false\n\t\t\t\t\t\tbreak\n\t\t\t\t\tend\n\t\t\t\tend\n\n\t\t\t\tif (!bFound) then\n\t\t\t\t\tcontinue\n\t\t\t\tend\n\t\t\tend\n\n\t\t\treturn k\n\t\tend\n\tend\n\n\treturn false\nend\n\nif (SERVER) then\n\t--- Sends a specific slot to a character.\n\t-- This will *not* send all of the slots of the `Item` to the character, items can occupy multiple slots.\n\t--\n\t-- This will call `OnSendData` on the Item using all of the `Inventory`'s receivers.\n\t--\n\t-- This function should *not* be used to sync an entire inventory, if you need to do that, use `AddReceiver` and `Sync`.\n\t-- @realm server\n\t-- @internal\n\t-- @number x The Inventory x position to send.\n\t-- @number y The Inventory y position to send.\n\t-- @item[opt] item The item to send, if any.\n\t-- @see AddReceiver\n\t-- @see Sync\n\tfunction META:SendSlot(x, y, item)\n\t\tlocal receivers = self:GetReceivers()\n\t\tlocal sendData = item and item.data and !table.IsEmpty(item.data) and item.data or {}\n\n\t\tnet.Start(\"ixInventorySet\")\n\t\t\tnet.WriteUInt(self:GetID(), 32)\n\t\t\tnet.WriteUInt(x, 6)\n\t\t\tnet.WriteUInt(y, 6)\n\t\t\tnet.WriteString(item and item.uniqueID or \"\")\n\t\t\tnet.WriteUInt(item and item.id or 0, 32)\n\t\t\tnet.WriteUInt(self.owner or 0, 32)\n\t\t\tnet.WriteTable(sendData)\n\t\tnet.Send(receivers)\n\n\t\tif (item) then\n\t\t\tfor _, v in pairs(receivers) do\n\t\t\t\titem:Call(\"OnSendData\", v)\n\t\t\tend\n\t\tend\n\tend\n\n\t--- Sets whether  or not an `Inventory` should save.\n\t-- This will prevent an `Inventory` from updating in the Database, if the inventory is already saved,\n\t-- it will not be deleted when unloaded.\n\t-- @realm server\n\t-- @bool bNoSave Whether or not the Inventory should save.\n\tfunction META:SetShouldSave(bNoSave)\n\t\tself.noSave = bNoSave\n\tend\n\n\t--- Gets whether or not an `Inventory` should save.\n\t-- Inventories that are marked to not save will not update in the Database, if they inventory is already saved,\n\t-- it will not be deleted when unloaded.\n\t-- @realm server\n\t-- @treturn[1] bool Returns the field `noSave`.\n\t-- @treturn[2] bool Returns true if the field `noSave` is not registered to this inventory.\n\tfunction META:GetShouldSave()\n\t\treturn self.noSave or true\n\tend\n\n\t--- Add an item to the inventory.\n\t-- @realm server\n\t-- @param uniqueID The item unique ID (e.g `\"handheld_radio\"`) or instance ID (e.g `1024`) to add to the inventory\n\t-- @number[opt=1] quantity The quantity of the item to add\n\t-- @tab data Item data to add to the item\n\t-- @number[opt=nil] x The X position for the item\n\t-- @number[opt=nil] y The Y position for the item\n\t-- @bool[opt=false] noReplication Whether or not the item's addition should not be replicated\n\t-- @treturn[1] bool Whether the add was successful or not\n\t-- @treturn[1] string The error, if applicable\n\t-- @treturn[2] number The X position that the item was added to\n\t-- @treturn[2] number The Y position that the item was added to\n\t-- @treturn[2] number The inventory ID that the item was added to\n\tfunction META:Add(uniqueID, quantity, data, x, y, noReplication)\n\t\tquantity = quantity or 1\n\n\t\tif (quantity < 1) then\n\t\t\treturn false, \"noOwner\"\n\t\tend\n\n\t\tif (!isnumber(uniqueID) and quantity > 1) then\n\t\t\tfor _ = 1, quantity do\n\t\t\t\tlocal bSuccess, error = self:Add(uniqueID, 1, data)\n\n\t\t\t\tif (!bSuccess) then\n\t\t\t\t\treturn false, error\n\t\t\t\tend\n\t\t\tend\n\n\t\t\treturn true\n\t\tend\n\n\t\tlocal client = self.GetOwner and self:GetOwner() or nil\n\t\tlocal item = isnumber(uniqueID) and ix.item.instances[uniqueID] or ix.item.list[uniqueID]\n\t\tlocal targetInv = self\n\t\tlocal bagInv\n\n\t\tif (!item) then\n\t\t\treturn false, \"invalidItem\"\n\t\tend\n\n\t\tif (isnumber(uniqueID)) then\n\t\t\tlocal oldInvID = item.invID\n\n\t\t\tif (!x and !y) then\n\t\t\t\tx, y, bagInv = self:FindEmptySlot(item.width, item.height)\n\t\t\tend\n\n\t\t\tif (bagInv) then\n\t\t\t\ttargetInv = bagInv\n\t\t\tend\n\n\t\t\t-- we need to check for owner since the item instance already exists\n\t\t\tif (!item.bAllowMultiCharacterInteraction and IsValid(client) and client:GetCharacter() and\n\t\t\t\titem:GetPlayerID() == client:SteamID64() and item:GetCharacterID() != client:GetCharacter():GetID()) then\n\t\t\t\treturn false, \"itemOwned\"\n\t\t\tend\n\n\t\t\tif (hook.Run(\"CanTransferItem\", item, ix.item.inventories[0], targetInv) == false) then\n\t\t\t\treturn false, \"notAllowed\"\n\t\t\tend\n\n\t\t\tif (x and y) then\n\t\t\t\ttargetInv.slots[x] = targetInv.slots[x] or {}\n\t\t\t\ttargetInv.slots[x][y] = true\n\n\t\t\t\titem.gridX = x\n\t\t\t\titem.gridY = y\n\t\t\t\titem.invID = targetInv:GetID()\n\n\t\t\t\tfor x2 = 0, item.width - 1 do\n\t\t\t\t\tlocal index = x + x2\n\n\t\t\t\t\tfor y2 = 0, item.height - 1 do\n\t\t\t\t\t\ttargetInv.slots[index] = targetInv.slots[index] or {}\n\t\t\t\t\t\ttargetInv.slots[index][y + y2] = item\n\t\t\t\t\tend\n\t\t\t\tend\n\n\t\t\t\tif (!noReplication) then\n\t\t\t\t\ttargetInv:SendSlot(x, y, item)\n\t\t\t\tend\n\n\t\t\t\tif (!self.noSave) then\n\t\t\t\t\tlocal query = mysql:Update(\"ix_items\")\n\t\t\t\t\t\tquery:Update(\"inventory_id\", targetInv:GetID())\n\t\t\t\t\t\tquery:Update(\"x\", x)\n\t\t\t\t\t\tquery:Update(\"y\", y)\n\t\t\t\t\t\tquery:Where(\"item_id\", item.id)\n\t\t\t\t\tquery:Execute()\n\t\t\t\tend\n\n\t\t\t\thook.Run(\"InventoryItemAdded\", ix.item.inventories[oldInvID], targetInv, item)\n\n\t\t\t\treturn x, y, targetInv:GetID()\n\t\t\telse\n\t\t\t\treturn false, \"noFit\"\n\t\t\tend\n\t\telse\n\t\t\tif (!x and !y) then\n\t\t\t\tx, y, bagInv = self:FindEmptySlot(item.width, item.height)\n\t\t\tend\n\n\t\t\tif (bagInv) then\n\t\t\t\ttargetInv = bagInv\n\t\t\tend\n\n\t\t\tif (hook.Run(\"CanTransferItem\", item, ix.item.inventories[0], targetInv) == false) then\n\t\t\t\treturn false, \"notAllowed\"\n\t\t\tend\n\n\t\t\tif (x and y) then\n\t\t\t\tfor x2 = 0, item.width - 1 do\n\t\t\t\t\tlocal index = x + x2\n\n\t\t\t\t\tfor y2 = 0, item.height - 1 do\n\t\t\t\t\t\ttargetInv.slots[index] = targetInv.slots[index] or {}\n\t\t\t\t\t\ttargetInv.slots[index][y + y2] = true\n\t\t\t\t\tend\n\t\t\t\tend\n\n\t\t\t\tlocal characterID\n\t\t\t\tlocal playerID\n\n\t\t\t\tif (self.owner) then\n\t\t\t\t\tlocal character = ix.char.loaded[self.owner]\n\n\t\t\t\t\tif (character) then\n\t\t\t\t\t\tcharacterID = character.id\n\t\t\t\t\t\tplayerID = character.steamID\n\t\t\t\t\tend\n\t\t\t\tend\n\n\t\t\t\tix.item.Instance(targetInv:GetID(), uniqueID, data, x, y, function(newItem)\n\t\t\t\t\tnewItem.gridX = x\n\t\t\t\t\tnewItem.gridY = y\n\n\t\t\t\t\tfor x2 = 0, newItem.width - 1 do\n\t\t\t\t\t\tlocal index = x + x2\n\n\t\t\t\t\t\tfor y2 = 0, newItem.height - 1 do\n\t\t\t\t\t\t\ttargetInv.slots[index] = targetInv.slots[index] or {}\n\t\t\t\t\t\t\ttargetInv.slots[index][y + y2] = newItem\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\n\t\t\t\t\tif (!noReplication) then\n\t\t\t\t\t\ttargetInv:SendSlot(x, y, newItem)\n\t\t\t\t\tend\n\n\t\t\t\t\thook.Run(\"InventoryItemAdded\", nil, targetInv, newItem)\n\t\t\t\tend, characterID, playerID)\n\n\t\t\t\treturn x, y, targetInv:GetID()\n\t\t\telse\n\t\t\t\treturn false, \"noFit\"\n\t\t\tend\n\t\tend\n\tend\n\n\t--- Syncs the `Inventory` to the receiver.\n\t-- This will call Item.OnSendData on every item in the `Inventory`.\n\t-- @realm server\n\t-- @player receiver The player to\n\tfunction META:Sync(receiver)\n\t\tlocal slots = {}\n\n\t\tfor x, items in pairs(self.slots) do\n\t\t\tfor y, item in pairs(items) do\n\t\t\t\tif (istable(item) and item.gridX == x and item.gridY == y) then\n\t\t\t\t\tslots[#slots + 1] = {x, y, item.uniqueID, item.id, item.data}\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\n\t\tnet.Start(\"ixInventorySync\")\n\t\t\tnet.WriteTable(slots)\n\t\t\tnet.WriteUInt(self:GetID(), 32)\n\t\t\tnet.WriteUInt(self.w, 6)\n\t\t\tnet.WriteUInt(self.h, 6)\n\t\t\tnet.WriteType(self.owner)\n\t\t\tnet.WriteTable(self.vars or {})\n\t\tnet.Send(receiver)\n\n\t\tfor k, _ in self:Iter() do\n\t\t\tk:Call(\"OnSendData\", receiver)\n\t\tend\n\tend\nend\n\nix.meta.inventory = META\n"
  },
  {
    "path": "gamemode/core/meta/sh_item.lua",
    "content": "\n--[[--\nInteractable entities that can be held in inventories.\n\nItems are objects that are contained inside of an `Inventory`, or as standalone entities if they are dropped in the world. They\nusually have functionality that provides more gameplay aspects to the schema. For example, the zipties in the HL2 RP schema\nallow a player to tie up and search a player.\n\nFor an item to have an actual presence, they need to be instanced (usually with `ix.item.Instance`). Items describe the\nproperties, while instances are a clone of these properties that can have their own unique data (e.g an ID card will have the\nsame name but different numerical IDs). You can think of items as the class, while instances are objects of the `Item` class.\n\n## Creating item classes (`ItemStructure`)\nItem classes are defined in their own file inside of your schema or plugin's `items/` folder. In these item class files you\nspecify how instances of the item behave. This includes default values for basic things like the item's name and description,\nto more advanced things by overriding extra methods from an item base. See `ItemStructure` for information on how to define\na basic item class.\n\nItem classes in this folder are automatically loaded by Helix when the server starts up.\n\n## Item bases\nIf many items share the same functionality (i.e a can of soda and a bottle of water can both be consumed), then you might want\nto consider using an item base to reduce the amount of duplication for these items. Item bases are defined the same way as\nregular item classes, but they are placed in the `items/base/` folder in your schema or plugin. For example, a `consumables`\nbase would be in `items/base/sh_consumables.lua`.\n\nAny items that you want to use this base must be placed in a subfolder that has the name of the base you want that item to use.\nFor example, for a bottled water item to use the consumable base, it must be placed in `items/consumables/sh_bottled_water.lua`.\nThis also means that you cannot place items into subfolders as you wish, since the framework will try to use an item base that\ndoesn't exist.\n\nThe default item bases that come with Helix are:\n\n  - `ammo` - provides ammo to any items with the `weapons` base\n  - `bags` - holds an inventory that other items can be stored inside of\n  - `outfit` - changes the appearance of the player that wears it\n  - `pacoutfit` - changes the appearance of the player that wears it using PAC3\n  - `weapons` - makes any SWEP into an item that can be equipped\n\nThese item bases usually come with extra values and methods that you can define/override in order to change their functionality.\nYou should take a look at the source code for these bases to see their capabilities.\n\n## Item functions (`ItemFunctionStructure`)\nRequiring players to interact with items in order for them to do something is quite common. As such, there is already a built-in\nmechanism to allow players to right-click items and show a list of available options. Item functions are defined in your item\nclass file in the `ITEM.functions` table. See `ItemFunctionStructure` on how to define them.\n\nHelix comes with `drop`, `take`, and `combine` item functions by default that allows items to be dropped from a player's\ninventory, picked up from the world, and combining items together. These can be overridden by defining an item function\nin your item class file with the same name. See the `bags` base for example usage of the `combine` item function.\n\n## Item icons (`ItemIconStructure`)\nIcons for items sometimes don't line up quite right, in which case you can modify an item's `iconCam` value and line up the\nrendered model as needed. See `ItemIconStructure` for more details.\n]]\n-- @classmod Item\n\n--[[--\nAll item functions live inside of an item's `functions` table. An item function entry includes a few methods and fields you can\nuse to customize the functionality and appearance of the item function. An example item function is below:\n\n\t-- this item function's unique ID is \"MyFunction\"\n\tITEM.functions.MyFunction = {\n\t\tname = \"myFunctionPhrase\", -- uses the \"myFunctionPhrase\" language phrase when displaying in the UI\n\t\ttip = \"myFunctionDescription\", -- uses the \"myFunctionDescription\" language phrase when displaying in the UI\n\t\ticon = \"icon16/add.png\", -- path to the icon material\n\t\tOnRun = function(item)\n\t\t\tlocal client = item.player\n\t\t\tlocal entity = item.entity -- only set if this is function is being ran while the item is in the world\n\n\t\t\tif (IsValid(client)) then\n\t\t\t\tclient:ChatPrint(\"This is a test.\")\n\n\t\t\t\tif (IsValid(entity)) then\n\t\t\t\t\tclient:ChatPrint(entity:GetName())\n\t\t\t\tend\n\t\t\tend\n\n\t\t\t-- do not remove this item from the owning player's inventory\n\t\t\treturn false\n\t\tend,\n\t\tOnCanRun = function(item)\n\t\t\t-- only allow admins to run this item function\n\t\t\tlocal client = item.player\n\t\t\treturn IsValid(client) and client:IsAdmin()\n\t\tend\n\t}\n]]\n-- @table ItemFunctionStructure\n-- @realm shared\n-- @field[type=string,opt] name Language phrase to use when displaying this item function's name in the UI. If not specified,\n-- then it will use the unique ID of the item function\n-- @field[type=string,opt] tip Language phrase to use when displaying this item function's detailed description in the UI\n-- @field[type=string,opt] icon Path to the material to use when displaying this item function's icon\n-- @field[type=function] OnRun Function to call when the item function is ran. This function is **ONLY** ran on the server.\n--\n-- The only argument passed into this function is the instance of the item being called. The instance will have its `player`\n-- field set if the item function is being ran by a player (which it should be most of the time). It will also have its `entity`\n-- field set if the item function is being ran while the item is in the world, and not in a player's inventory.\n--\n-- The item will be removed after the item function is ran. If you want to prevent this behaviour, then you can return `false`\n-- in this function. See the example above.\n-- @field[type=function] OnCanRun Function to call when checking whether or not this item function can be ran. This function is\n-- ran **BOTH** on the client and server.\n--\n-- The arguments are the same as `OnCanRun`, and the `player` and `entity` fields will be set on the item instance accordingly.\n-- Returning `true` will allow the item function to be ran. Returning `false` will prevent it from running and additionally\n-- hide it from the UI. See the example above.\n-- @field[type=function,opt] OnClick This function is called when the player clicks on this item function's entry in the UI.\n-- This function is ran **ONLY** on the client, and is only ran if `OnCanRun` succeeds.\n--\n-- The same arguments from `OnCanRun` and `OnRun` apply to this function.\n\n--[[--\nChanging the way an item's icon is rendered is done by modifying the location and angle of the model, as well as the FOV of the\ncamera. You can tweak the values in code, or use the `ix_dev_icon` console command to visually position the model and camera. An\nexample entry for an item's icon is below:\n\n\tITEM.iconCam = {\n\t\tpos = Vector(0, 0, 60),\n\t\tang = Angle(90, 0, 0),\n\t\tfov = 45\n\t}\n\nNote that this will probably not work for your item's specific model, since every model has a different size, origin, etc. All\nitem icons need to be tweaked individually.\n]]\n-- @table ItemIconStructure\n-- @realm client\n-- @field[type=vector] pos Location of the model relative to the camera. +X is forward, +Z is up\n-- @field[type=angle] ang Angle of the model\n-- @field[type=number] fov FOV of the camera\n\n--[[--\nWhen creating an item class, the file will have a global table `ITEM` set that you use to define the item's values/methods. An\nexample item class is below:\n\n`items/sh_brick.lua`\n\tITEM.name = \"Brick\"\n\tITEM.description = \"A brick. Pretty self-explanatory. You can eat it but you'll probably lose some teeth.\"\n\tITEM.model = Model(\"models/props_debris/concrete_cynderblock001.mdl\")\n\tITEM.width = 1\n\tITEM.height = 1\n\tITEM.price = 25\n\nNote that the below list only includes the default fields available for *all* items, and not special ones defined in custom\nitem bases.\n]]\n-- @table ItemStructure\n-- @realm shared\n-- @field[type=string] name Display name of the item\n-- @field[type=string] description Detailed description of the item\n-- @field[type=string] model Model to use for the item's icon and when it's dropped in the world\n-- @field[type=number,opt=1] width Width of the item in grid cells\n-- @field[type=number,opt=1] height Height of the item in grid cells\n-- @field[type=number,opt=0] price How much money it costs to purchase this item in the business menu\n-- @field[type=string,opt] category Name of the category this item belongs to - mainly used for the business menu\n-- @field[type=boolean,opt=false] noBusiness Whether or not to disallow purchasing this item in the business menu\n-- @field[type=table,opt] factions List of factions allowed to purchase this item in the business menu\n-- @field[type=table,opt] classes List of character classes allowed to purchase this item in the business menu. Classes are\n-- checked after factions, so the character must also be in an allowed faction\n-- @field[type=string,opt] flag List of flags (as a string - e.g `\"a\"` or `\"abc\"`) allowed to purchase this item in the\n-- business menu. Flags are checked last, so the character must also be in an allowed faction and class\n-- @field[type=ItemIconStructure,opt] iconCam How to render this item's icon\n-- @field[type=table,opt] functions List of all item functions that this item has. See `ItemFunctionStructure` on how to define\n-- new item functions\n\nlocal ITEM = ix.meta.item or {}\nITEM.__index = ITEM\nITEM.name = \"Undefined\"\nITEM.description = ITEM.description or \"An item that is undefined.\"\nITEM.id = ITEM.id or 0\nITEM.uniqueID = \"undefined\"\n\n--- Returns a string representation of this item.\n-- @realm shared\n-- @treturn string String representation\n-- @usage print(ix.item.instances[1])\n-- > \"item[1]\"\nfunction ITEM:__tostring()\n\treturn \"item[\"..self.uniqueID..\"][\"..self.id..\"]\"\nend\n\n--- Returns true if this item is equal to another item. Internally, this checks item IDs.\n-- @realm shared\n-- @item other Item to compare to\n-- @treturn bool Whether or not this item is equal to the given item\n-- @usage print(ix.item.instances[1] == ix.item.instances[2])\n-- > false\nfunction ITEM:__eq(other)\n\treturn self:GetID() == other:GetID()\nend\n\n--- Returns this item's database ID. This is guaranteed to be unique.\n-- @realm shared\n-- @treturn number Unique ID of item\nfunction ITEM:GetID()\n\treturn self.id\nend\n\n--- Returns the name of the item.\n-- @realm shared\n-- @treturn string The name of the item\nfunction ITEM:GetName()\n\treturn (CLIENT and L(self.name) or self.name)\nend\n\n--- Returns the description of the item.\n-- @realm shared\n-- @treturn string The description of the item\nfunction ITEM:GetDescription()\n\tif (!self.description) then return \"ERROR\" end\n\n\treturn L(self.description or \"noDesc\")\nend\n\n--- Returns the model of the item.\n-- @realm shared\n-- @treturn string The model of the item\nfunction ITEM:GetModel()\n\treturn self.model\nend\n\n--- Returns the skin of the item.\n-- @realm shared\n-- @treturn number The skin of the item\nfunction ITEM:GetSkin()\n\treturn self.skin or 0\nend\n\nfunction ITEM:GetMaterial()\n\treturn nil\nend\n\n--- Returns the ID of the owning character, if one exists.\n-- @realm shared\n-- @treturn number The owning character's ID\nfunction ITEM:GetCharacterID()\n\treturn self.characterID\nend\n\n--- Returns the SteamID64 of the owning player, if one exists.\n-- @realm shared\n-- @treturn number The owning player's SteamID64\nfunction ITEM:GetPlayerID()\n\treturn self.playerID\nend\n\n--- A utility function which prints the item's details.\n-- @realm shared\n-- @bool[opt=false] detail Whether additional detail should be printed or not(Owner, X position, Y position)\nfunction ITEM:Print(detail)\n\tif (detail == true) then\n\t\tprint(Format(\"%s[%s]: >> [%s](%s,%s)\", self.uniqueID, self.id, self.owner, self.gridX, self.gridY))\n\telse\n\t\tprint(Format(\"%s[%s]\", self.uniqueID, self.id))\n\tend\nend\n\n--- A utility function printing the item's stored data.\n-- @realm shared\nfunction ITEM:PrintData()\n\tself:Print(true)\n\tprint(\"ITEM DATA:\")\n\tfor k, v in pairs(self.data) do\n\t\tprint(Format(\"[%s] = %s\", k, v))\n\tend\nend\n\n--- Calls one of the item's methods.\n-- @realm shared\n-- @string method The method to be called\n-- @player client The client to pass when calling the method, if applicable\n-- @entity entity The eneity to pass when calling the method, if applicable\n-- @param ... Arguments to pass to the method\n-- @return The values returned by the method\nfunction ITEM:Call(method, client, entity, ...)\n\tlocal oldPlayer, oldEntity = self.player, self.entity\n\n\tself.player = client or self.player\n\tself.entity = entity or self.entity\n\n\tif (isfunction(self[method])) then\n\t\tlocal results = {self[method](self, ...)}\n\n\t\tself.player = nil\n\t\tself.entity = nil\n\n\t\treturn unpack(results)\n\tend\n\n\tself.player = oldPlayer\n\tself.entity = oldEntity\nend\n\n--- Returns the player that owns this item.\n-- @realm shared\n-- @treturn player Player owning this item\nfunction ITEM:GetOwner()\n\tlocal inventory = ix.item.inventories[self.invID]\n\n\tif (inventory) then\n\t\treturn inventory.GetOwner and inventory:GetOwner()\n\tend\n\n\tlocal id = self:GetID()\n\n\tfor _, v in player.Iterator() do\n\t\tlocal character = v:GetCharacter()\n\n\t\tif (character and character:GetInventory() and character:GetInventory():GetItemByID(id)) then\n\t\t\treturn v\n\t\tend\n\tend\nend\n\n--- Sets a key within the item's data.\n-- @realm shared\n-- @string key The key to store the value within\n-- @param[opt=nil] value The value to store within the key\n-- @tab[opt=nil] receivers The players to replicate the data on\n-- @bool[opt=false] noSave Whether to disable saving the data on the database or not\n-- @bool[opt=false] noCheckEntity Whether to disable setting the data on the entity, if applicable\nfunction ITEM:SetData(key, value, receivers, noSave, noCheckEntity)\n\tself.data = self.data or {}\n\tself.data[key] = value\n\n\tif (SERVER) then\n\t\tif (!noCheckEntity) then\n\t\t\tlocal ent = self:GetEntity()\n\n\t\t\tif (IsValid(ent)) then\n\t\t\t\tlocal data = ent:GetNetVar(\"data\", {})\n\t\t\t\tdata[key] = value\n\n\t\t\t\tent:SetNetVar(\"data\", data)\n\t\t\tend\n\t\tend\n\tend\n\n\tlocal inventory = ix.item.inventories[self.invID]\n\n\tif (receivers != false) then\n\t\tlocal targets = receivers or (inventory and inventory.GetReceivers and inventory:GetReceivers()) or self:GetOwner()\n\t\tif (targets) then\n\t\t\tnet.Start(\"ixInventoryData\")\n\t\t\t\tnet.WriteUInt(self:GetID(), 32)\n\t\t\t\tnet.WriteString(key)\n\t\t\t\tnet.WriteType(value)\n\t\t\tnet.Send(targets)\n\t\tend\n\tend\n\n\tif (!noSave and ix.db) then\n\t\tlocal query = mysql:Update(\"ix_items\")\n\t\t\tquery:Update(\"data\", util.TableToJSON(self.data))\n\t\t\tquery:Where(\"item_id\", self:GetID())\n\t\tquery:Execute()\n\tend\nend\n\n--- Returns the value stored on a key within the item's data.\n-- @realm shared\n-- @string key The key in which the value is stored\n-- @param[opt=nil] default The value to return in case there is no value stored in the key\n-- @return The value stored within the key\nfunction ITEM:GetData(key, default)\n\tself.data = self.data or {}\n\n\tif (self.data) then\n\t\tif (key == true) then\n\t\t\treturn self.data\n\t\tend\n\n\t\tlocal value = self.data[key]\n\n\t\tif (value != nil) then\n\t\t\treturn value\n\t\telseif (IsValid(self.entity)) then\n\t\t\tlocal data = self.entity:GetNetVar(\"data\", {})\n\t\t\tvalue = data[key]\n\n\t\t\tif (value != nil) then\n\t\t\t\treturn value\n\t\t\tend\n\t\tend\n\telse\n\t\tself.data = {}\n\tend\n\n\tif (default != nil) then\n\t\treturn default\n\tend\n\n\treturn\nend\n\n--- Changes the function called on specific events for the item.\n-- @realm shared\n-- @string name The name of the hook\n-- @func func The function to call once the event occurs\nfunction ITEM:Hook(name, func)\n\tif (name) then\n\t\tself.hooks[name] = func\n\tend\nend\n\n--- Changes the function called after hooks for specific events for the item.\n-- @realm shared\n-- @string name The name of the hook\n-- @func func The function to call after the original hook was called\nfunction ITEM:PostHook(name, func)\n\tif (name) then\n\t\tself.postHooks[name] = func\n\tend\nend\n\n--- Removes the item.\n-- @realm shared\n-- @bool bNoReplication Whether or not the item's removal should not be replicated.\n-- @bool bNoDelete Whether or not the item should not be fully deleted\n-- @treturn bool Whether the item was successfully deleted or not\nfunction ITEM:Remove(bNoReplication, bNoDelete)\n\tlocal inv = ix.item.inventories[self.invID]\n\tlocal bFailed = false\n\n\tif (self.invID > 0 and inv) then\n\t\tfor x = self.gridX, self.gridX + (self.width - 1) do\n\t\t\tif (inv.slots[x]) then\n\t\t\t\tfor y = self.gridY, self.gridY + (self.height - 1) do\n\t\t\t\t\tlocal item = inv.slots[x][y]\n\n\t\t\t\t\tif (item and item.id == self.id) then\n\t\t\t\t\t\tinv.slots[x][y] = nil\n\t\t\t\t\telse\n\t\t\t\t\t\tbFailed = true\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\telse\n\t\t\t\tbFailed = true\n\t\t\tend\n\t\tend\n\n\t\tif (bFailed) then\n\t\t\tlocal items = {}\n\t\t\tfor _, v in pairs(ix.item.instances) do\n\t\t\t\tif (v.invID == self.invID and v.id != self.id) then\n\t\t\t\t\titems[#items + 1] = v\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tinv.slots = {}\n\t\t\tfor _, v in ipairs(items) do\n\t\t\t\tfor x = v.gridX, v.gridX + (v.width - 1) do\n\t\t\t\t\tfor y = v.gridY, v.gridY + (v.height - 1) do\n\t\t\t\t\t\tinv.slots[x] = inv.slots[x] or {}\n\t\t\t\t\t\tinv.slots[x][y] = v\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\telse\n\t\t-- @todo definition probably isn't needed\n\t\tinv = ix.item.inventories[self.invID]\n\n\t\tif (inv) then\n\t\t\tix.item.inventories[self.invID][self.id] = nil\n\t\tend\n\tend\n\n\tif (SERVER and !bNoReplication) then\n\t\tlocal entity = self:GetEntity()\n\n\t\tif (IsValid(entity)) then\n\t\t\tentity:Remove()\n\t\tend\n\n\t\tlocal receivers = inv.GetReceivers and inv:GetReceivers()\n\n\t\tif (self.invID != 0 and istable(receivers)) then\n\t\t\tif (bFailed) then\n\t\t\t\tinv:Sync(receivers)\n\t\t\telse\n\t\t\t\tnet.Start(\"ixInventoryRemove\")\n\t\t\t\t\tnet.WriteUInt(self.id, 32)\n\t\t\t\t\tnet.WriteUInt(self.invID, 32)\n\t\t\t\tnet.Send(receivers)\n\t\t\tend\n\t\tend\n\n\t\tif (!bNoDelete) then\n\t\t\tlocal item = ix.item.instances[self.id]\n\n\t\t\tif (inv and inv.owner) then\n\t\t\t\thook.Run(\"InventoryItemRemoved\", inv, item)\n\t\t\tend\n\n\t\t\tif (item and item.OnRemoved) then\n\t\t\t\titem:OnRemoved()\n\t\t\tend\n\n\t\t\tlocal query = mysql:Delete(\"ix_items\")\n\t\t\t\tquery:Where(\"item_id\", self.id)\n\t\t\tquery:Execute()\n\n\t\t\tix.item.instances[self.id] = nil\n\t\tend\n\tend\n\n\treturn true\nend\n\nif (SERVER) then\n\t--- Returns the item's entity.\n\t-- @realm server\n\t-- @treturn entity The entity of the item\n\tfunction ITEM:GetEntity()\n\t\tlocal id = self:GetID()\n\n\t\tfor _, v in ipairs(ents.FindByClass(\"ix_item\")) do\n\t\t\tif (v.ixItemID == id) then\n\t\t\t\treturn v\n\t\t\tend\n\t\tend\n\tend\n\n\t--- Spawn an item entity based off the item table.\n\t-- @realm server\n\t-- @param[type=vector] position The position in which the item's entity will be spawned\n\t-- @param[type=angle] angles The angles at which the item's entity will spawn\n\t-- @treturn entity The spawned entity\n\tfunction ITEM:Spawn(position, angles)\n\t\t-- Check if the item has been created before.\n\t\tif (ix.item.instances[self.id]) then\n\t\t\tlocal client\n\n\t\t\t-- Spawn the actual item entity.\n\t\t\tlocal entity = ents.Create(\"ix_item\")\n\t\t\tentity:Spawn()\n\t\t\tentity:SetAngles(angles or Angle(0, 0, 0))\n\t\t\tentity:SetItem(self.id)\n\n\t\t\t-- If the first argument is a player, then we will find a position to drop\n\t\t\t-- the item based off their aim.\n\t\t\tif (type(position) == \"Player\") then\n\t\t\t\tclient = position\n\t\t\t\tposition = position:GetItemDropPos(entity)\n\t\t\tend\n\n\t\t\tentity:SetPos(position)\n\n\t\t\tif (IsValid(client)) then\n\t\t\t\tentity.ixSteamID = client:SteamID()\n\t\t\t\tentity.ixCharID = client:GetCharacter():GetID()\n\t\t\t\tentity:SetNetVar(\"owner\", entity.ixCharID)\n\t\t\tend\n\n\t\t\thook.Run(\"OnItemSpawned\", entity)\n\t\t\treturn entity\n\t\tend\n\tend\n\n\t--- Transfers an item to a specific inventory.\n\t-- @realm server\n\t-- @number invID The inventory to transfer the item to\n\t-- @number x The X position to which the item should be transferred on the new inventory\n\t-- @number y The Y position to which the item should be transferred on the new inventory\n\t-- @player client The player to which the item is being transferred\n\t-- @bool noReplication Whether there should be no replication of the transferral\n\t-- @bool isLogical Whether or not an entity should spawn if the item is transferred to the world\n\t-- @treturn[1] bool Whether the transfer was successful or not\n\t-- @treturn[1] string The error, if applicable\n\tfunction ITEM:Transfer(invID, x, y, client, noReplication, isLogical)\n\t\tinvID = invID or 0\n\n\t\tif (self.invID == invID) then\n\t\t\treturn false, \"same inv\"\n\t\tend\n\n\t\tlocal inventory = ix.item.inventories[invID]\n\t\tlocal curInv = ix.item.inventories[self.invID or 0]\n\n\t\tif (curInv and !IsValid(client)) then\n\t\t\tclient = curInv.GetOwner and curInv:GetOwner() or nil\n\t\tend\n\n\t\t-- check if this item doesn't belong to another one of this player's characters\n\t\tlocal itemPlayerID = self:GetPlayerID()\n\t\tlocal itemCharacterID = self:GetCharacterID()\n\n\t\tif (!self.bAllowMultiCharacterInteraction and IsValid(client) and client:GetCharacter()) then\n\t\t\tlocal playerID = client:SteamID64()\n\t\t\tlocal characterID = client:GetCharacter():GetID()\n\n\t\t\tif (itemPlayerID and itemCharacterID) then\n\t\t\t\tif (itemPlayerID == playerID and itemCharacterID != characterID) then\n\t\t\t\t\treturn false, \"itemOwned\"\n\t\t\t\tend\n\t\t\telse\n\t\t\t\tself.characterID = characterID\n\t\t\t\tself.playerID = playerID\n\n\t\t\t\tlocal query = mysql:Update(\"ix_items\")\n\t\t\t\t\tquery:Update(\"character_id\", characterID)\n\t\t\t\t\tquery:Update(\"player_id\", playerID)\n\t\t\t\t\tquery:Where(\"item_id\", self:GetID())\n\t\t\t\tquery:Execute()\n\t\t\tend\n\t\tend\n\n\t\tif (hook.Run(\"CanTransferItem\", self, curInv, inventory) == false) then\n\t\t\treturn false, \"notAllowed\"\n\t\tend\n\n\t\tlocal authorized = false\n\n\t\tif (inventory and inventory.OnAuthorizeTransfer and inventory:OnAuthorizeTransfer(client, curInv, self)) then\n\t\t\tauthorized = true\n\t\tend\n\n\t\tif (!authorized and self.CanTransfer and self:CanTransfer(curInv, inventory) == false) then\n\t\t\treturn false, \"notAllowed\"\n\t\tend\n\n\t\tif (curInv) then\n\t\t\tif (invID and invID > 0 and inventory) then\n\t\t\t\tlocal targetInv = inventory\n\t\t\t\tlocal bagInv\n\n\t\t\t\tif (!x and !y) then\n\t\t\t\t\tx, y, bagInv = inventory:FindEmptySlot(self.width, self.height)\n\t\t\t\tend\n\n\t\t\t\tif (bagInv) then\n\t\t\t\t\ttargetInv = bagInv\n\t\t\t\tend\n\n\t\t\t\tif (!x or !y) then\n\t\t\t\t\treturn false, \"noFit\"\n\t\t\t\tend\n\n\t\t\t\tlocal prevID = self.invID\n\t\t\t\tlocal status, result = targetInv:Add(self.id, nil, nil, x, y, noReplication)\n\n\t\t\t\tif (status) then\n\t\t\t\t\tif (self.invID > 0 and prevID != 0) then\n\t\t\t\t\t\t-- we are transferring this item from one inventory to another\n\t\t\t\t\t\tcurInv:Remove(self.id, false, true, true)\n\n\t\t\t\t\t\tif (self.OnTransferred) then\n\t\t\t\t\t\t\tself:OnTransferred(curInv, inventory)\n\t\t\t\t\t\tend\n\n\t\t\t\t\t\thook.Run(\"OnItemTransferred\", self, curInv, inventory)\n\t\t\t\t\t\treturn true\n\t\t\t\t\telseif (self.invID > 0 and prevID == 0) then\n\t\t\t\t\t\t-- we are transferring this item from the world to an inventory\n\t\t\t\t\t\tix.item.inventories[0][self.id] = nil\n\n\t\t\t\t\t\tif (self.OnTransferred) then\n\t\t\t\t\t\t\tself:OnTransferred(curInv, inventory)\n\t\t\t\t\t\tend\n\n\t\t\t\t\t\thook.Run(\"OnItemTransferred\", self, curInv, inventory)\n\t\t\t\t\t\treturn true\n\t\t\t\t\tend\n\t\t\t\telse\n\t\t\t\t\treturn false, result\n\t\t\t\tend\n\t\t\telseif (IsValid(client)) then\n\t\t\t\t-- we are transferring this item from an inventory to the world\n\t\t\t\tself.invID = 0\n\t\t\t\tcurInv:Remove(self.id, false, true)\n\n\t\t\t\tlocal query = mysql:Update(\"ix_items\")\n\t\t\t\t\tquery:Update(\"inventory_id\", 0)\n\t\t\t\t\tquery:Where(\"item_id\", self.id)\n\t\t\t\tquery:Execute()\n\n\t\t\t\tinventory = ix.item.inventories[0]\n\t\t\t\tinventory[self:GetID()] = self\n\n\t\t\t\tif (self.OnTransferred) then\n\t\t\t\t\tself:OnTransferred(curInv, inventory)\n\t\t\t\tend\n\n\t\t\t\thook.Run(\"OnItemTransferred\", self, curInv, inventory)\n\n\t\t\t\tif (!isLogical) then\n\t\t\t\t\treturn self:Spawn(client)\n\t\t\t\tend\n\n\t\t\t\treturn true\n\t\t\telse\n\t\t\t\treturn false, \"noOwner\"\n\t\t\tend\n\t\telse\n\t\t\treturn false, \"invalidInventory\"\n\t\tend\n\tend\nend\n\nix.meta.item = ITEM\n"
  },
  {
    "path": "gamemode/core/meta/sh_player.lua",
    "content": "\n--[[--\nPhysical representation of connected player.\n\n`Player`s are a type of `Entity`. They are a physical representation of a `Character` - and can possess at most one `Character`\nobject at a time that you can interface with.\n\nSee the [Garry's Mod Wiki](https://wiki.garrysmod.com/page/Category:Player) for all other methods that the `Player` class has.\n]]\n-- @classmod Player\n\nlocal meta = FindMetaTable(\"Player\")\n\nif (SERVER) then\n\t--- Returns the amount of time the player has played on the server.\n\t-- @realm shared\n\t-- @treturn number Number of seconds the player has played on the server\n\tfunction meta:GetPlayTime()\n\t\treturn self.ixPlayTime + (RealTime() - (self.ixJoinTime or RealTime()))\n\tend\nelse\n\tix.playTime = ix.playTime or 0\n\n\tfunction meta:GetPlayTime()\n\t\treturn ix.playTime + (RealTime() - ix.joinTime or 0)\n\tend\nend\n\n--- Returns `true` if the player has their weapon raised.\n-- @realm shared\n-- @treturn bool Whether or not the player has their weapon raised\nfunction meta:IsWepRaised()\n\treturn self:GetNetVar(\"raised\", false)\nend\n\n--- Returns `true` if the player is restricted - that is to say that they are considered \"bound\" and cannot interact with\n-- objects normally (e.g hold weapons, use items, etc). An example of this would be a player in handcuffs.\n-- @realm shared\n-- @treturn bool Whether or not the player is restricted\nfunction meta:IsRestricted()\n\treturn self:GetNetVar(\"restricted\", false)\nend\n\n--- Returns `true` if the player is able to shoot their weapon.\n-- @realm shared\n-- @treturn bool Whether or not the player can shoot their weapon\nfunction meta:CanShootWeapon()\n\treturn self:GetNetVar(\"canShoot\", true)\nend\n\nlocal vectorLength2D = FindMetaTable(\"Vector\").Length2D\n\n--- Returns `true` if the player is running. Running in this case means that their current speed is greater than their\n-- regularly set walk speed.\n-- @realm shared\n-- @treturn bool Whether or not the player is running\nfunction meta:IsRunning()\n\treturn vectorLength2D(self:GetVelocity()) > (self:GetWalkSpeed() + 10)\nend\n\n--- Returns `true` if the player currently has a female model. This checks if the model has `female`, `alyx` or `mossman` in its\n-- name, or if the player's model class is `citizen_female`.\n-- @realm shared\n-- @treturn bool Whether or not the player has a female model\nfunction meta:IsFemale()\n\tlocal model = self:GetModel():lower()\n\n\treturn (model:find(\"female\") or model:find(\"alyx\") or model:find(\"mossman\")) != nil or\n\t\tix.anim.GetModelClass(model) == \"citizen_female\"\nend\n\n--- Whether or not this player is stuck and cannot move.\n-- @realm shared\n-- @treturn bool Whether or not this player is stuck\nfunction meta:IsStuck()\n\treturn util.TraceEntity({\n\t\tstart = self:GetPos(),\n\t\tendpos = self:GetPos(),\n\t\tfilter = self\n\t}, self).StartSolid\nend\n\n--- Returns a good position in front of the player for an entity to be placed. This is usually used for item entities.\n-- @realm shared\n-- @entity entity Entity to get a position for\n-- @treturn vector Best guess for a good drop position in front of the player\n-- @usage local position = client:GetItemDropPos(entity)\n-- entity:SetPos(position)\nfunction meta:GetItemDropPos(entity)\n\tlocal data = {}\n\tlocal trace\n\n\tdata.start = self:GetShootPos()\n\tdata.endpos = self:GetShootPos() + self:GetAimVector() * 86\n\tdata.filter = self\n\n\tif (IsValid(entity)) then\n\t\t-- use a hull trace if there's a valid entity to avoid collisions\n\t\tlocal mins, maxs = entity:GetRotatedAABB(entity:OBBMins(), entity:OBBMaxs())\n\n\t\tdata.mins = mins\n\t\tdata.maxs = maxs\n\t\tdata.filter = {entity, self}\n\t\ttrace = util.TraceHull(data)\n\telse\n\t\t-- trace along the normal for a few units so we can attempt to avoid a collision\n\t\ttrace = util.TraceLine(data)\n\n\t\tdata.start = trace.HitPos\n\t\tdata.endpos = data.start + trace.HitNormal * 48\n\t\ttrace = util.TraceLine(data)\n\tend\n\n\treturn trace.HitPos\nend\n\n--- Performs a time-delay action that requires this player to look at an entity. If this player looks away from the entity\n-- before the action timer completes, the action is cancelled. This is usually used in conjunction with `SetAction` to display\n-- progress to the player.\n-- @realm shared\n-- @entity entity that this player must look at\n-- @func callback Function to call when the timer completes\n-- @number time How much time in seconds this player must look at the entity for\n-- @func[opt=nil] onCancel Function to call when the timer has been cancelled\n-- @number[opt=96] distance Maximum distance a player can move away from the entity before the action is cancelled\n-- @see SetAction\n-- @usage client:SetAction(\"Searching...\", 4) -- for displaying the progress bar\n-- client:DoStaredAction(entity, function()\n-- \tprint(\"hello!\")\n-- end)\n-- -- prints \"hello!\" after looking at the entity for 4 seconds\nfunction meta:DoStaredAction(entity, callback, time, onCancel, distance)\n\tlocal uniqueID = \"ixStare\"..self:SteamID64()\n\tlocal data = {}\n\tdata.filter = self\n\n\ttimer.Create(uniqueID, 0.1, time / 0.1, function()\n\t\tif (IsValid(self) and IsValid(entity)) then\n\t\t\tdata.start = self:GetShootPos()\n\t\t\tdata.endpos = data.start + self:GetAimVector()*(distance or 96)\n\n\t\t\tif (util.TraceLine(data).Entity != entity) then\n\t\t\t\ttimer.Remove(uniqueID)\n\n\t\t\t\tif (onCancel) then\n\t\t\t\t\tonCancel()\n\t\t\t\tend\n\t\t\telseif (callback and timer.RepsLeft(uniqueID) == 0) then\n\t\t\t\tcallback()\n\t\t\tend\n\t\telse\n\t\t\ttimer.Remove(uniqueID)\n\n\t\t\tif (onCancel) then\n\t\t\t\tonCancel()\n\t\t\tend\n\t\tend\n\tend)\nend\n\n--- Resets all bodygroups this player's model has to their defaults (`0`).\n-- @realm shared\nfunction meta:ResetBodygroups()\n\tfor i = 0, (self:GetNumBodyGroups() - 1) do\n\t\tself:SetBodygroup(i, 0)\n\tend\nend\n\nif (SERVER) then\n\tutil.AddNetworkString(\"ixActionBar\")\n\tutil.AddNetworkString(\"ixActionBarReset\")\n\tutil.AddNetworkString(\"ixStringRequest\")\n\n\t--- Sets whether or not this player's current weapon is raised.\n\t-- @realm server\n\t-- @bool bState Whether or not the raise the weapon\n\t-- @entity[opt=GetActiveWeapon()] weapon Weapon to raise or lower. You should pass this argument if you already have a\n\t-- reference to this player's current weapon to avoid an expensive lookup for this player's current weapon.\n\tfunction meta:SetWepRaised(bState, weapon)\n\t\tweapon = weapon or self:GetActiveWeapon()\n\n\t\tif (IsValid(weapon)) then\n\t\t\tlocal bCanShoot = !bState and weapon.FireWhenLowered or bState\n\t\t\tself:SetNetVar(\"raised\", bState)\n\n\t\t\tif (bCanShoot) then\n\t\t\t\t-- delay shooting while the raise animation is playing\n\t\t\t\ttimer.Create(\"ixWeaponRaise\" .. self:SteamID64(), 1, 1, function()\n\t\t\t\t\tif (IsValid(self)) then\n\t\t\t\t\t\tself:SetNetVar(\"canShoot\", true)\n\t\t\t\t\tend\n\t\t\t\tend)\n\t\t\telse\n\t\t\t\ttimer.Remove(\"ixWeaponRaise\" .. self:SteamID64())\n\t\t\t\tself:SetNetVar(\"canShoot\", false)\n\t\t\tend\n\t\telse\n\t\t\ttimer.Remove(\"ixWeaponRaise\" .. self:SteamID64())\n\t\t\tself:SetNetVar(\"raised\", false)\n\t\t\tself:SetNetVar(\"canShoot\", false)\n\t\tend\n\tend\n\n\t--- Inverts this player's weapon raised state. You should use `SetWepRaised` instead of this if you already have a reference\n\t-- to this player's current weapon.\n\t-- @realm server\n\tfunction meta:ToggleWepRaised()\n\t\tlocal weapon = self:GetActiveWeapon()\n\n\t\tif (!IsValid(weapon) or\n\t\t\tweapon.IsAlwaysRaised or ALWAYS_RAISED[weapon:GetClass()] or\n\t\t\tweapon.IsAlwaysLowered or weapon.NeverRaised) then\n\t\t\treturn\n\t\tend\n\n\t\tself:SetWepRaised(!self:IsWepRaised(), weapon)\n\n\t\tif (self:IsWepRaised() and weapon.OnRaised) then\n\t\t\tweapon:OnRaised()\n\t\telseif (!self:IsWepRaised() and weapon.OnLowered) then\n\t\t\tweapon:OnLowered()\n\t\tend\n\tend\n\n\t--- Performs a delayed action that requires this player to hold use on an entity. This is displayed to this player as a\n\t-- closing ring over their crosshair.\n\t-- @realm server\n\t-- @number time How much time in seconds this player has to hold use for\n\t-- @entity entity Entity that this player must be looking at\n\t-- @func callback Function to run when the timer completes. It will be ran right away if `time` is `0`. Returning `false` in\n\t-- the callback will not mark this interaction as dirty if you're managing the interaction state manually.\n\tfunction meta:PerformInteraction(time, entity, callback)\n\t\tif (!IsValid(entity) or entity.ixInteractionDirty) then\n\t\t\treturn\n\t\tend\n\n\t\tif (time > 0) then\n\t\t\tself.ixInteractionTarget = entity\n\t\t\tself.ixInteractionCharacter = self:GetCharacter():GetID()\n\n\t\t\ttimer.Create(\"ixCharacterInteraction\" .. self:SteamID(), time, 1, function()\n\t\t\t\tif (IsValid(self) and IsValid(entity) and IsValid(self.ixInteractionTarget) and\n\t\t\t\t\tself.ixInteractionCharacter == self:GetCharacter():GetID()) then\n\t\t\t\t\tlocal useEntity = self:GetUseEntity()\n\n\t\t\t\t\tif (IsValid(useEntity) and useEntity == self.ixInteractionTarget and !useEntity.ixInteractionDirty) then\n\t\t\t\t\t\tif (callback(self) != false) then\n\t\t\t\t\t\t\tuseEntity.ixInteractionDirty = true\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend)\n\t\telse\n\t\t\tif (callback(self) != false) then\n\t\t\t\tentity.ixInteractionDirty = true\n\t\t\tend\n\t\tend\n\tend\n\n\t--- Displays a progress bar for this player that takes the given amount of time to complete.\n\t-- @realm server\n\t-- @string text Text to display above the progress bar\n\t-- @number[opt=5] time How much time in seconds to wait before the timer completes\n\t-- @func callback Function to run once the timer completes\n\t-- @number[opt=CurTime()] startTime Game time in seconds that the timer started. If you are using `time`, then you shouldn't\n\t-- use this argument\n\t-- @number[opt=startTime + time] finishTime Game time in seconds that the timer should complete at. If you are using `time`,\n\t-- then you shouldn't use this argument\n\tfunction meta:SetAction(text, time, callback, startTime, finishTime)\n\t\tif (time and time <= 0) then\n\t\t\tif (callback) then\n\t\t\t\tcallback(self)\n\t\t\tend\n\n\t\t\treturn\n\t\tend\n\n\t\t-- Default the time to five seconds.\n\t\ttime = time or 5\n\t\tstartTime = startTime or CurTime()\n\t\tfinishTime = finishTime or (startTime + time)\n\n\t\tif (text == false) then\n\t\t\ttimer.Remove(\"ixAct\"..self:SteamID64())\n\n\t\t\tnet.Start(\"ixActionBarReset\")\n\t\t\tnet.Send(self)\n\n\t\t\treturn\n\t\tend\n\n\t\tif (!text) then\n\t\t\tnet.Start(\"ixActionBarReset\")\n\t\t\tnet.Send(self)\n\t\telse\n\t\t\tnet.Start(\"ixActionBar\")\n\t\t\t\tnet.WriteFloat(startTime)\n\t\t\t\tnet.WriteFloat(finishTime)\n\t\t\t\tnet.WriteString(text)\n\t\t\tnet.Send(self)\n\t\tend\n\n\t\t-- If we have provided a callback, run it delayed.\n\t\tif (callback) then\n\t\t\t-- Create a timer that runs once with a delay.\n\t\t\ttimer.Create(\"ixAct\"..self:SteamID64(), time, 1, function()\n\t\t\t\t-- Call the callback if the player is still valid.\n\t\t\t\tif (IsValid(self)) then\n\t\t\t\t\tcallback(self)\n\t\t\t\tend\n\t\t\tend)\n\t\tend\n\tend\n\n\t--- Opens up a text box on this player's screen for input and returns the result. Remember to sanitize the user's input if\n\t-- it's needed!\n\t-- @realm server\n\t-- @string title Title to display on the panel\n\t-- @string subTitle Subtitle to display on the panel\n\t-- @func callback Function to run when this player enters their input. Callback is ran with the user's input string.\n\t-- @string[opt=nil] default Default value to put in the text box.\n\t-- @usage client:RequestString(\"Hello\", \"Please enter your name\", function(text)\n\t-- \tclient:ChatPrint(\"Hello, \" .. text)\n\t-- end)\n\t-- -- prints \"Hello, <text>\" in the player's chat\n\tfunction meta:RequestString(title, subTitle, callback, default)\n\t\tlocal time = math.floor(os.time())\n\n\t\tself.ixStrReqs = self.ixStrReqs or {}\n\t\tself.ixStrReqs[time] = callback\n\n\t\tnet.Start(\"ixStringRequest\")\n\t\t\tnet.WriteUInt(time, 32)\n\t\t\tnet.WriteString(title)\n\t\t\tnet.WriteString(subTitle)\n\t\t\tnet.WriteString(default or \"\")\n\t\tnet.Send(self)\n\tend\n\n\t--- Sets this player's restricted status.\n\t-- @realm server\n\t-- @bool bState Whether or not to restrict this player\n\t-- @bool bNoMessage Whether or not to suppress the restriction notification\n\tfunction meta:SetRestricted(bState, bNoMessage)\n\t\tif (bState) then\n\t\t\tself:SetNetVar(\"restricted\", true)\n\n\t\t\tif (bNoMessage) then\n\t\t\t\tself:SetLocalVar(\"restrictNoMsg\", true)\n\t\t\tend\n\n\t\t\tself.ixRestrictWeps = self.ixRestrictWeps or {}\n\n\t\t\tfor _, v in ipairs(self:GetWeapons()) do\n\t\t\t\tself.ixRestrictWeps[#self.ixRestrictWeps + 1] = {\n\t\t\t\t\tclass = v:GetClass(),\n\t\t\t\t\titem = v.ixItem,\n\t\t\t\t\tclip = v:Clip1()\n\t\t\t\t}\n\n\t\t\t\tv:Remove()\n\t\t\tend\n\n\t\t\thook.Run(\"OnPlayerRestricted\", self)\n\t\telse\n\t\t\tself:SetNetVar(\"restricted\")\n\n\t\t\tif (self:GetLocalVar(\"restrictNoMsg\")) then\n\t\t\t\tself:SetLocalVar(\"restrictNoMsg\")\n\t\t\tend\n\n\t\t\tif (self.ixRestrictWeps) then\n\t\t\t\tfor _, v in ipairs(self.ixRestrictWeps) do\n\t\t\t\t\tlocal weapon = self:Give(v.class, true)\n\n\t\t\t\t\tif (v.item) then\n\t\t\t\t\t\tweapon.ixItem = v.item\n\t\t\t\t\tend\n\n\t\t\t\t\tweapon:SetClip1(v.clip)\n\t\t\t\tend\n\n\t\t\t\tself.ixRestrictWeps = nil\n\t\t\tend\n\n\t\t\thook.Run(\"OnPlayerUnRestricted\", self)\n\t\tend\n\tend\n\n\t--- Creates a ragdoll entity of this player that will be synced with clients. This does **not** affect the player like\n\t-- `SetRagdolled` does.\n\t-- @realm server\n\t-- @bool[opt=false] bDontSetPlayer Whether or not to avoid setting the ragdoll's owning player\n\t-- @treturn entity Created ragdoll entity\n\tfunction meta:CreateServerRagdoll(bDontSetPlayer)\n\t\tlocal entity = ents.Create(\"prop_ragdoll\")\n\t\tentity:SetPos(self:GetPos())\n\t\tentity:SetAngles(self:EyeAngles())\n\t\tentity:SetModel(self:GetModel())\n\t\tentity:SetSkin(self:GetSkin())\n\n\t\tfor i = 0, (self:GetNumBodyGroups() - 1) do\n\t\t\tentity:SetBodygroup(i, self:GetBodygroup(i))\n\t\tend\n\n\t\tentity:Spawn()\n\n\t\tif (!bDontSetPlayer) then\n\t\t\tentity:SetNetVar(\"player\", self)\n\t\tend\n\n\t\tentity:SetCollisionGroup(COLLISION_GROUP_WEAPON)\n\t\tentity:Activate()\n\n\t\tlocal velocity = self:GetVelocity()\n\n\t\tfor i = 0, entity:GetPhysicsObjectCount() - 1 do\n\t\t\tlocal physObj = entity:GetPhysicsObjectNum(i)\n\n\t\t\tif (IsValid(physObj)) then\n\t\t\t\tphysObj:SetVelocity(velocity)\n\n\t\t\t\tlocal index = entity:TranslatePhysBoneToBone(i)\n\n\t\t\t\tif (index) then\n\t\t\t\t\tlocal position, angles = self:GetBonePosition(index)\n\n\t\t\t\t\tphysObj:SetPos(position)\n\t\t\t\t\tphysObj:SetAngles(angles)\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\n\t\treturn entity\n\tend\n\n\t--- Sets this player's ragdoll status.\n\t-- @realm server\n\t-- @bool bState Whether or not to ragdoll this player\n\t-- @number[opt=0] time How long this player should stay ragdolled for. Set to `0` if they should stay ragdolled until they\n\t-- get back up manually\n\t-- @number[opt=5] getUpGrace How much time in seconds to wait before the player is able to get back up manually. Set to\n\t-- the same number as `time` to disable getting up manually entirely\n\tfunction meta:SetRagdolled(bState, time, getUpGrace)\n\t\tif (!self:Alive()) then\n\t\t\treturn\n\t\tend\n\n\t\tgetUpGrace = getUpGrace or time or 5\n\n\t\tif (bState) then\n\t\t\tif (IsValid(self.ixRagdoll)) then\n\t\t\t\tself.ixRagdoll:Remove()\n\t\t\tend\n\n\t\t\tlocal entity = self:CreateServerRagdoll()\n\n\t\t\tentity:CallOnRemove(\"fixer\", function()\n\t\t\t\tif (IsValid(self)) then\n\t\t\t\t\tself:SetLocalVar(\"blur\", nil)\n\t\t\t\t\tself:SetLocalVar(\"ragdoll\", nil)\n\n\t\t\t\t\tif (!entity.ixNoReset) then\n\t\t\t\t\t\tself:SetPos(entity:GetPos())\n\t\t\t\t\tend\n\n\t\t\t\t\tself:SetNoDraw(false)\n\t\t\t\t\tself:SetNotSolid(false)\n\t\t\t\t\tself:SetMoveType(MOVETYPE_WALK)\n\t\t\t\t\tself:SetLocalVelocity(IsValid(entity) and entity.ixLastVelocity or vector_origin)\n\t\t\t\tend\n\n\t\t\t\tif (IsValid(self) and !entity.ixIgnoreDelete) then\n\t\t\t\t\tif (entity.ixWeapons) then\n\t\t\t\t\t\tfor _, v in ipairs(entity.ixWeapons) do\n\t\t\t\t\t\t\tif (v.class) then\n\t\t\t\t\t\t\t\tlocal weapon = self:Give(v.class, true)\n\n\t\t\t\t\t\t\t\tif (v.item) then\n\t\t\t\t\t\t\t\t\tweapon.ixItem = v.item\n\t\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\t\t\tself:SetAmmo(v.ammo, weapon:GetPrimaryAmmoType())\n\t\t\t\t\t\t\t\tweapon:SetClip1(v.clip)\n\t\t\t\t\t\t\telseif (v.item and v.invID == v.item.invID) then\n\t\t\t\t\t\t\t\tv.item:Equip(self, true, true)\n\t\t\t\t\t\t\t\tself:SetAmmo(v.ammo, self.carryWeapons[v.item.weaponCategory]:GetPrimaryAmmoType())\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\n\t\t\t\t\tif (entity.ixActiveWeapon) then\n\t\t\t\t\t\tif (self:HasWeapon(entity.ixActiveWeapon)) then\n\t\t\t\t\t\t\tself:SetActiveWeapon(self:GetWeapon(entity.ixActiveWeapon))\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tlocal weapons = self:GetWeapons()\n\t\t\t\t\t\t\tif (#weapons > 0) then\n\t\t\t\t\t\t\t\tself:SetActiveWeapon(weapons[1])\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\n\t\t\t\t\tif (self:IsStuck()) then\n\t\t\t\t\t\tentity:DropToFloor()\n\t\t\t\t\t\tself:SetPos(entity:GetPos() + Vector(0, 0, 16))\n\n\t\t\t\t\t\tlocal positions = ix.util.FindEmptySpace(self, {entity, self})\n\n\t\t\t\t\t\tfor _, v in ipairs(positions) do\n\t\t\t\t\t\t\tself:SetPos(v)\n\n\t\t\t\t\t\t\tif (!self:IsStuck()) then\n\t\t\t\t\t\t\t\treturn\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend)\n\n\t\t\tself:SetLocalVar(\"blur\", 25)\n\t\t\tself.ixRagdoll = entity\n\n\t\t\tentity.ixWeapons = {}\n\t\t\tentity.ixPlayer = self\n\n\t\t\tif (getUpGrace) then\n\t\t\t\tentity.ixGrace = CurTime() + getUpGrace\n\t\t\tend\n\n\t\t\tif (time and time > 0) then\n\t\t\t\tentity.ixStart = CurTime()\n\t\t\t\tentity.ixFinish = entity.ixStart + time\n\n\t\t\t\tself:SetAction(\"@wakingUp\", nil, nil, entity.ixStart, entity.ixFinish)\n\t\t\tend\n\n\t\t\tif (IsValid(self:GetActiveWeapon())) then\n\t\t\t\tentity.ixActiveWeapon = self:GetActiveWeapon():GetClass()\n\t\t\tend\n\n\t\t\tfor _, v in ipairs(self:GetWeapons()) do\n\t\t\t\tif (v.ixItem and v.ixItem.Equip and v.ixItem.Unequip) then\n\t\t\t\t\tentity.ixWeapons[#entity.ixWeapons + 1] = {\n\t\t\t\t\t\titem = v.ixItem,\n\t\t\t\t\t\tinvID = v.ixItem.invID,\n\t\t\t\t\t\tammo = self:GetAmmoCount(v:GetPrimaryAmmoType())\n\t\t\t\t\t}\n\t\t\t\t\tv.ixItem:Unequip(self, false)\n\t\t\t\telse\n\t\t\t\t\tlocal clip = v:Clip1()\n\t\t\t\t\tlocal reserve = self:GetAmmoCount(v:GetPrimaryAmmoType())\n\t\t\t\t\tentity.ixWeapons[#entity.ixWeapons + 1] = {\n\t\t\t\t\t\tclass = v:GetClass(),\n\t\t\t\t\t\titem = v.ixItem,\n\t\t\t\t\t\tclip = clip,\n\t\t\t\t\t\tammo = reserve\n\t\t\t\t\t}\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tself:GodDisable()\n\t\t\tself:StripWeapons()\n\t\t\tself:SetMoveType(MOVETYPE_OBSERVER)\n\t\t\tself:SetNoDraw(true)\n\t\t\tself:SetNotSolid(true)\n\n\t\t\tlocal uniqueID = \"ixUnRagdoll\" .. self:SteamID()\n\n\t\t\tif (time) then\n\t\t\t\ttimer.Create(uniqueID, 0.33, 0, function()\n\t\t\t\t\tif (IsValid(entity) and IsValid(self) and self.ixRagdoll == entity) then\n\t\t\t\t\t\tlocal velocity = entity:GetVelocity()\n\t\t\t\t\t\tentity.ixLastVelocity = velocity\n\n\t\t\t\t\t\tself:SetPos(entity:GetPos())\n\n\t\t\t\t\t\tif (velocity:Length2D() >= 8) then\n\t\t\t\t\t\t\tif (!entity.ixPausing) then\n\t\t\t\t\t\t\t\tself:SetAction()\n\t\t\t\t\t\t\t\tentity.ixPausing = true\n\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\t\treturn\n\t\t\t\t\t\telseif (entity.ixPausing) then\n\t\t\t\t\t\t\tself:SetAction(\"@wakingUp\", time)\n\t\t\t\t\t\t\tentity.ixPausing = false\n\t\t\t\t\t\tend\n\n\t\t\t\t\t\ttime = time - 0.33\n\n\t\t\t\t\t\tif (time <= 0) then\n\t\t\t\t\t\t\tentity:Remove()\n\t\t\t\t\t\tend\n\t\t\t\t\telse\n\t\t\t\t\t\ttimer.Remove(uniqueID)\n\t\t\t\t\tend\n\t\t\t\tend)\n\t\t\telse\n\t\t\t\ttimer.Create(uniqueID, 0.33, 0, function()\n\t\t\t\t\tif (IsValid(entity) and IsValid(self) and self.ixRagdoll == entity) then\n\t\t\t\t\t\tself:SetPos(entity:GetPos())\n\t\t\t\t\telse\n\t\t\t\t\t\ttimer.Remove(uniqueID)\n\t\t\t\t\tend\n\t\t\t\tend)\n\t\t\tend\n\n\t\t\tself:SetLocalVar(\"ragdoll\", entity:EntIndex())\n\t\t\thook.Run(\"OnCharacterFallover\", self, entity, true)\n\t\telseif (IsValid(self.ixRagdoll)) then\n\t\t\tself.ixRagdoll:Remove()\n\n\t\t\thook.Run(\"OnCharacterFallover\", self, nil, false)\n\t\tend\n\tend\nend\n"
  },
  {
    "path": "gamemode/core/meta/sh_tool.lua",
    "content": "\nlocal TOOL = ix.meta.tool or {}\n\n-- code replicated from gamemodes/sandbox/entities/weapons/gmod_tool/stool.lua\nfunction TOOL:Create()\n\tlocal object = {}\n\n\tsetmetatable(object, self)\n\tself.__index = self\n\n\tobject.Mode = nil\n\tobject.SWEP = nil\n\tobject.Owner = nil\n\tobject.ClientConVar = {}\n\tobject.ServerConVar = {}\n\tobject.Objects = {}\n\tobject.Stage = 0\n\tobject.Message = \"start\"\n\tobject.LastMessage = 0\n\tobject.AllowedCVar = 0\n\n\treturn object\nend\n\nfunction TOOL:CreateConVars()\n\tlocal mode = self:GetMode()\n\n\tif (CLIENT) then\n\t\tfor cvar, default in pairs(self.ClientConVar) do\n\t\t\tCreateClientConVar(mode .. \"_\" .. cvar, default, true, true)\n\t\tend\n\n\t\treturn\n\tend\n\n\t-- Note: I changed this from replicated because replicated convars don't work when they're created via Lua.\n\tif (SERVER) then\n\t\tself.AllowedCVar = CreateConVar(\"toolmode_allow_\" .. mode, 1, FCVAR_NOTIFY)\n\tend\nend\n\nfunction TOOL:GetServerInfo(property)\n\tlocal mode = self:GetMode()\n\treturn GetConVarString(mode .. \"_\" .. property)\nend\n\nfunction TOOL:BuildConVarList()\n\tlocal mode = self:GetMode()\n\tlocal convars = {}\n\n\tfor k, v in pairs(self.ClientConVar) do\n\t\tconvars[mode .. \"_\" .. k] = v\n\tend\n\n\treturn convars\nend\n\nfunction TOOL:GetClientInfo(property)\n\treturn self:GetOwner():GetInfo(self:GetMode() .. \"_\" .. property)\nend\n\nfunction TOOL:GetClientNumber(property, default)\n\treturn self:GetOwner():GetInfoNum(self:GetMode() .. \"_\" .. property, tonumber(default) or 0)\nend\n\nfunction TOOL:Allowed()\n\tif (CLIENT) then\n\t\treturn true\n\tend\n\n\treturn self.AllowedCVar:GetBool()\nend\n\n-- Now for all the TOOL redirects\nfunction TOOL:Init()\nend\n\nfunction TOOL:GetMode()\n\treturn self.Mode\nend\n\nfunction TOOL:GetSWEP()\n\treturn self.SWEP\nend\n\nfunction TOOL:GetOwner()\n\treturn self:GetSWEP().Owner or self.Owner\nend\n\nfunction TOOL:GetWeapon()\n\treturn self:GetSWEP().Weapon or self.Weapon\nend\n\nfunction TOOL:LeftClick()\n\treturn false\nend\n\nfunction TOOL:RightClick()\n\treturn false\nend\n\nfunction TOOL:Reload()\n\tself:ClearObjects()\nend\n\nfunction TOOL:Deploy()\n\tself:ReleaseGhostEntity()\n\treturn\nend\n\nfunction TOOL:Holster()\n\tself:ReleaseGhostEntity()\n\treturn\nend\n\nfunction TOOL:Think()\n\tself:ReleaseGhostEntity()\nend\n\n-- Checks the objects before any action is taken\n-- This is to make sure that the entities haven't been removed\nfunction TOOL:CheckObjects()\n\tfor _, v in pairs(self.Objects) do\n\t\tif (!v.Ent:IsWorld() and !v.Ent:IsValid()) then\n\t\t\tself:ClearObjects()\n\t\tend\n\tend\nend\n\nix.meta.tool = TOOL\n"
  },
  {
    "path": "gamemode/core/sh_commands.lua",
    "content": "\nix.command.Add(\"Roll\", {\n\tdescription = \"@cmdRoll\",\n\targuments = bit.bor(ix.type.number, ix.type.optional),\n\tOnRun = function(self, client, maximum)\n\t\tmaximum = math.Clamp(maximum or 100, 0, 1000000)\n\n\t\tlocal value = math.random(0, maximum)\n\n\t\tix.chat.Send(client, \"roll\", tostring(value), nil, nil, {\n\t\t\tmax = maximum\n\t\t})\n\n\t\tix.log.Add(client, \"roll\", value, maximum)\n\tend\n})\n\nix.command.Add(\"Event\", {\n\tdescription = \"@cmdEvent\",\n\targuments = ix.type.text,\n\tsuperAdminOnly = true,\n\tOnRun = function(self, client, text)\n\t\tix.chat.Send(client, \"event\", text)\n\tend\n})\n\nix.command.Add(\"PM\", {\n\tdescription = \"@cmdPM\",\n\targuments = {\n\t\tix.type.player,\n\t\tix.type.text\n\t},\n\tOnRun = function(self, client, target, message)\n\t\tlocal voiceMail = target:GetData(\"vm\")\n\n\t\tif (voiceMail and voiceMail:find(\"%S\")) then\n\t\t\treturn target:GetName()..\": \"..voiceMail\n\t\tend\n\n\t\tif ((client.ixNextPM or 0) < CurTime()) then\n\t\t\tix.chat.Send(client, \"pm\", message, false, {client, target}, {target = target})\n\n\t\t\tclient.ixNextPM = CurTime() + 0.5\n\t\t\ttarget.ixLastPM = client\n\t\tend\n\tend\n})\n\nix.command.Add(\"Reply\", {\n\tdescription = \"@cmdReply\",\n\targuments = ix.type.text,\n\tOnRun = function(self, client, message)\n\t\tlocal target = client.ixLastPM\n\n\t\tif (IsValid(target) and (client.ixNextPM or 0) < CurTime()) then\n\t\t\tix.chat.Send(client, \"pm\", message, false, {client, target}, {target = target})\n\t\t\tclient.ixNextPM = CurTime() + 0.5\n\t\tend\n\tend\n})\n\nix.command.Add(\"SetVoicemail\", {\n\tdescription = \"@cmdSetVoicemail\",\n\targuments = bit.bor(ix.type.text, ix.type.optional),\n\tOnRun = function(self, client, message)\n\t\tif (isstring(message) and message:find(\"%S\")) then\n\t\t\tclient:SetData(\"vm\", message:utf8sub(1, 240))\n\t\t\treturn \"@vmSet\"\n\t\telse\n\t\t\tclient:SetData(\"vm\")\n\t\t\treturn \"@vmRem\"\n\t\tend\n\tend\n})\n\nix.command.Add(\"CharGiveFlag\", {\n\tdescription = \"@cmdCharGiveFlag\",\n\tprivilege = \"Manage Character Flags\",\n\tsuperAdminOnly = true,\n\targuments = {\n\t\tix.type.character,\n\t\tbit.bor(ix.type.string, ix.type.optional)\n\t},\n\tOnRun = function(self, client, target, flags)\n\t\t-- show string request if no flags are specified\n\t\tif (!flags) then\n\t\t\tlocal available = \"\"\n\n\t\t\t-- sort and display flags the character already has\n\t\t\tfor k, _ in SortedPairs(ix.flag.list) do\n\t\t\t\tif (!target:HasFlags(k)) then\n\t\t\t\t\tavailable = available .. k\n\t\t\t\tend\n\t\t\tend\n\n\t\t\treturn client:RequestString(\"@flagGiveTitle\", \"@cmdCharGiveFlag\", function(text)\n\t\t\t\tix.command.Run(client, \"CharGiveFlag\", {target:GetName(), text})\n\t\t\tend, available)\n\t\tend\n\n\t\ttarget:GiveFlags(flags)\n\n\t\tfor _, v in player.Iterator() do\n\t\t\tif (self:OnCheckAccess(v) or v == target:GetPlayer()) then\n\t\t\t\tv:NotifyLocalized(\"flagGive\", client:GetName(), target:GetName(), flags)\n\t\t\tend\n\t\tend\n\tend\n})\n\nix.command.Add(\"CharTakeFlag\", {\n\tdescription = \"@cmdCharTakeFlag\",\n\tprivilege = \"Manage Character Flags\",\n\tsuperAdminOnly = true,\n\targuments = {\n\t\tix.type.character,\n\t\tbit.bor(ix.type.string, ix.type.optional)\n\t},\n\tOnRun = function(self, client, target, flags)\n\t\tif (!flags) then\n\t\t\treturn client:RequestString(\"@flagTakeTitle\", \"@cmdCharTakeFlag\", function(text)\n\t\t\t\tix.command.Run(client, \"CharTakeFlag\", {target:GetName(), text})\n\t\t\tend, target:GetFlags())\n\t\tend\n\n\t\ttarget:TakeFlags(flags)\n\n\t\tfor _, v in player.Iterator() do\n\t\t\tif (self:OnCheckAccess(v) or v == target:GetPlayer()) then\n\t\t\t\tv:NotifyLocalized(\"flagTake\", client:GetName(), flags, target:GetName())\n\t\t\tend\n\t\tend\n\tend\n})\n\nix.command.Add(\"ToggleRaise\", {\n\tdescription = \"@cmdToggleRaise\",\n\tOnRun = function(self, client, arguments)\n\t\tif (!timer.Exists(\"ixToggleRaise\" .. client:SteamID())) then\n\t\t\ttimer.Create(\"ixToggleRaise\" .. client:SteamID(), ix.config.Get(\"weaponRaiseTime\"), 1, function()\n\t\t\t\tclient:ToggleWepRaised()\n\t\t\tend)\n\t\tend\n\tend\n})\n\nix.command.Add(\"CharSetModel\", {\n\tdescription = \"@cmdCharSetModel\",\n\tsuperAdminOnly = true,\n\targuments = {\n\t\tix.type.character,\n\t\tix.type.string\n\t},\n\tOnRun = function(self, client, target, model)\n\t\ttarget:SetModel(model)\n\t\ttarget:GetPlayer():SetupHands()\n\n\t\tfor _, v in player.Iterator() do\n\t\t\tif (self:OnCheckAccess(v) or v == target:GetPlayer()) then\n\t\t\t\tv:NotifyLocalized(\"cChangeModel\", client:GetName(), target:GetName(), model)\n\t\t\tend\n\t\tend\n\tend\n})\n\nix.command.Add(\"CharSetSkin\", {\n\tdescription = \"@cmdCharSetSkin\",\n\tadminOnly = true,\n\targuments = {\n\t\tix.type.character,\n\t\tbit.bor(ix.type.number, ix.type.optional)\n\t},\n\tOnRun = function(self, client, target, skin)\n\t\ttarget:SetData(\"skin\", skin)\n\t\ttarget:GetPlayer():SetSkin(skin or 0)\n\n\t\tfor _, v in player.Iterator() do\n\t\t\tif (self:OnCheckAccess(v) or v == target:GetPlayer()) then\n\t\t\t\tv:NotifyLocalized(\"cChangeSkin\", client:GetName(), target:GetName(), skin or 0)\n\t\t\tend\n\t\tend\n\tend\n})\n\nix.command.Add(\"CharSetBodygroup\", {\n\tdescription = \"@cmdCharSetBodygroup\",\n\tadminOnly = true,\n\targuments = {\n\t\tix.type.character,\n\t\tix.type.string,\n\t\tbit.bor(ix.type.number, ix.type.optional)\n\t},\n\tOnRun = function(self, client, target, bodygroup, value)\n\t\tlocal index = target:GetPlayer():FindBodygroupByName(bodygroup)\n\n\t\tif (index > -1) then\n\t\t\tif (value and value < 1) then\n\t\t\t\tvalue = nil\n\t\t\tend\n\n\t\t\tlocal groups = target:GetData(\"groups\", {})\n\t\t\t\tgroups[index] = value\n\t\t\ttarget:SetData(\"groups\", groups)\n\t\t\ttarget:GetPlayer():SetBodygroup(index, value or 0)\n\n\t\t\tix.util.NotifyLocalized(\"cChangeGroups\", nil, client:GetName(), target:GetName(), bodygroup, value or 0)\n\t\telse\n\t\t\treturn \"@invalidArg\", 2\n\t\tend\n\tend\n})\n\nix.command.Add(\"CharSetAttribute\", {\n\tdescription = \"@cmdCharSetAttribute\",\n\tprivilege = \"Manage Character Attributes\",\n\tadminOnly = true,\n\targuments = {\n\t\tix.type.character,\n\t\tix.type.string,\n\t\tix.type.number\n\t},\n\tOnRun = function(self, client, target, attributeName, level)\n\t\tfor k, v in pairs(ix.attributes.list) do\n\t\t\tif (ix.util.StringMatches(L(v.name, client), attributeName) or ix.util.StringMatches(k, attributeName)) then\n\t\t\t\ttarget:SetAttrib(k, math.abs(level))\n\t\t\t\treturn \"@attributeSet\", target:GetName(), L(v.name, client), math.abs(level)\n\t\t\tend\n\t\tend\n\n\t\treturn \"@attributeNotFound\"\n\tend\n})\n\nix.command.Add(\"CharAddAttribute\", {\n\tdescription = \"@cmdCharAddAttribute\",\n\tprivilege = \"Manage Character Attributes\",\n\tadminOnly = true,\n\targuments = {\n\t\tix.type.character,\n\t\tix.type.string,\n\t\tix.type.number\n\t},\n\tOnRun = function(self, client, target, attributeName, level)\n\t\tfor k, v in pairs(ix.attributes.list) do\n\t\t\tif (ix.util.StringMatches(L(v.name, client), attributeName) or ix.util.StringMatches(k, attributeName)) then\n\t\t\t\ttarget:UpdateAttrib(k, math.abs(level))\n\t\t\t\treturn \"@attributeUpdate\", target:GetName(), L(v.name, client), math.abs(level)\n\t\t\tend\n\t\tend\n\n\t\treturn \"@attributeNotFound\"\n\tend\n})\n\nix.command.Add(\"CharSetName\", {\n\tdescription = \"@cmdCharSetName\",\n\tadminOnly = true,\n\targuments = {\n\t\tix.type.character,\n\t\tbit.bor(ix.type.text, ix.type.optional)\n\t},\n\tOnRun = function(self, client, target, newName)\n\t\t-- display string request panel if no name was specified\n\t\tif (newName:len() == 0) then\n\t\t\treturn client:RequestString(\"@chgName\", \"@chgNameDesc\", function(text)\n\t\t\t\tix.command.Run(client, \"CharSetName\", {target:GetName(), text})\n\t\t\tend, target:GetName())\n\t\tend\n\n\t\tfor _, v in player.Iterator() do\n\t\t\tif (self:OnCheckAccess(v) or v == target:GetPlayer()) then\n\t\t\t\tv:NotifyLocalized(\"cChangeName\", client:GetName(), target:GetName(), newName)\n\t\t\tend\n\t\tend\n\n\t\ttarget:SetName(newName:gsub(\"#\", \"#​\"))\n\tend\n})\n\nix.command.Add(\"CharGiveItem\", {\n\tdescription = \"@cmdCharGiveItem\",\n\tsuperAdminOnly = true,\n\targuments = {\n\t\tix.type.character,\n\t\tix.type.string,\n\t\tbit.bor(ix.type.number, ix.type.optional)\n\t},\n\tOnRun = function(self, client, target, item, amount)\n\t\tlocal uniqueID = item:lower()\n\n\t\tif (!ix.item.list[uniqueID]) then\n\t\t\tfor k, v in SortedPairs(ix.item.list) do\n\t\t\t\tif (ix.util.StringMatches(v.name, uniqueID)) then\n\t\t\t\t\tuniqueID = k\n\n\t\t\t\t\tbreak\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\n\t\tamount = amount or 1\n\t\tlocal bSuccess, error = target:GetInventory():Add(uniqueID, amount)\n\n\t\tif (bSuccess) then\n\t\t\ttarget:GetPlayer():NotifyLocalized(\"itemCreated\")\n\n\t\t\tif (target != client:GetCharacter()) then\n\t\t\t\treturn \"@itemCreated\"\n\t\t\tend\n\t\telse\n\t\t\treturn \"@\" .. tostring(error)\n\t\tend\n\tend\n})\n\nix.command.Add(\"CharKick\", {\n\tdescription = \"@cmdCharKick\",\n\tadminOnly = true,\n\targuments = ix.type.character,\n\tOnRun = function(self, client, target)\n\t\ttarget:Save(function()\n\t\t\ttarget:Kick()\n\t\tend)\n\n\t\tfor _, v in player.Iterator() do\n\t\t\tif (self:OnCheckAccess(v) or v == target:GetPlayer()) then\n\t\t\t\tv:NotifyLocalized(\"charKick\", client:GetName(), target:GetName())\n\t\t\tend\n\t\tend\n\tend\n})\n\nix.command.Add(\"CharBan\", {\n\tdescription = \"@cmdCharBan\",\n\tprivilege = \"Ban Character\",\n\targuments = {\n\t\tix.type.character,\n\t\tbit.bor(ix.type.number, ix.type.optional)\n\t},\n\tadminOnly = true,\n\tOnRun = function(self, client, target, minutes)\n\t\tif (minutes) then\n\t\t\tminutes = minutes * 60\n\t\tend\n\n\t\ttarget:Ban(minutes)\n\t\ttarget:Save()\n\n\t\tfor _, v in player.Iterator() do\n\t\t\tif (self:OnCheckAccess(v) or v == target:GetPlayer()) then\n\t\t\t\tv:NotifyLocalized(\"charBan\", client:GetName(), target:GetName())\n\t\t\tend\n\t\tend\n\tend\n})\n\nix.command.Add(\"CharUnban\", {\n\tdescription = \"@cmdCharUnban\",\n\tprivilege = \"Ban Character\",\n\targuments = ix.type.text,\n\tadminOnly = true,\n\tOnRun = function(self, client, name)\n\t\tif ((client.ixNextSearch or 0) >= CurTime()) then\n\t\t\treturn L(\"charSearching\", client)\n\t\tend\n\n\t\tfor _, v in pairs(ix.char.loaded) do\n\t\t\tif (ix.util.StringMatches(v:GetName(), name)) then\n\t\t\t\tif (v:GetData(\"banned\")) then\n\t\t\t\t\tv:SetData(\"banned\")\n\t\t\t\telse\n\t\t\t\t\treturn \"@charNotBanned\"\n\t\t\t\tend\n\n\t\t\t\tfor _, v2 in player.Iterator() do\n\t\t\t\t\tif (self:OnCheckAccess(v2) or v2 == v:GetPlayer()) then\n\t\t\t\t\t\tv2:NotifyLocalized(\"charUnBan\", client:GetName(), v:GetName())\n\t\t\t\t\tend\n\t\t\t\tend\n\n\t\t\t\treturn\n\t\t\tend\n\t\tend\n\n\t\tclient.ixNextSearch = CurTime() + 15\n\n\t\tlocal query = mysql:Select(\"ix_characters\")\n\t\t\tquery:Select(\"id\")\n\t\t\tquery:Select(\"name\")\n\t\t\tquery:Select(\"data\")\n\t\t\tquery:WhereLike(\"name\", name)\n\t\t\tquery:Limit(1)\n\t\t\tquery:Callback(function(result)\n\t\t\t\tif (istable(result) and #result > 0) then\n\t\t\t\t\tlocal characterID = tonumber(result[1].id)\n\t\t\t\t\tlocal data = util.JSONToTable(result[1].data or \"[]\")\n\t\t\t\t\tname = result[1].name\n\n\t\t\t\t\tclient.ixNextSearch = 0\n\n\t\t\t\t\tif (!data.banned) then\n\t\t\t\t\t\treturn client:NotifyLocalized(\"charNotBanned\")\n\t\t\t\t\tend\n\n\t\t\t\t\tdata.banned = nil\n\n\t\t\t\t\tlocal updateQuery = mysql:Update(\"ix_characters\")\n\t\t\t\t\t\tupdateQuery:Update(\"data\", util.TableToJSON(data))\n\t\t\t\t\t\tupdateQuery:Where(\"id\", characterID)\n\t\t\t\t\tupdateQuery:Execute()\n\n\t\t\t\t\tfor _, v in player.Iterator() do\n\t\t\t\t\t\tif (self:OnCheckAccess(v)) then\n\t\t\t\t\t\t\tv:NotifyLocalized(\"charUnBan\", client:GetName(), name)\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend)\n\t\tquery:Execute()\n\tend\n})\n\ndo\n\thook.Add(\"InitializedConfig\", \"ixMoneyCommands\", function()\n\t\tlocal MONEY_NAME = string.gsub(ix.util.ExpandCamelCase(ix.currency.plural), \"%s\", \"\")\n\n\t\tix.command.Add(\"Give\" .. MONEY_NAME, {\n\t\t\talias = {\"GiveMoney\"},\n\t\t\tdescription = \"@cmdGiveMoney\",\n\t\t\targuments = ix.type.number,\n\t\t\tOnRun = function(self, client, amount)\n\t\t\t\tamount = math.floor(amount)\n\n\t\t\t\tif (amount <= 0) then\n\t\t\t\t\treturn L(\"invalidArg\", client, 1)\n\t\t\t\tend\n\n\t\t\t\tlocal data = {}\n\t\t\t\t\tdata.start = client:GetShootPos()\n\t\t\t\t\tdata.endpos = data.start + client:GetAimVector() * 96\n\t\t\t\t\tdata.filter = client\n\t\t\t\tlocal target = util.TraceLine(data).Entity\n\n\t\t\t\tif (IsValid(target) and target:IsPlayer() and target:GetCharacter()) then\n\t\t\t\t\tif (!client:GetCharacter():HasMoney(amount)) then\n\t\t\t\t\t\treturn\n\t\t\t\t\tend\n\n\t\t\t\t\ttarget:GetCharacter():GiveMoney(amount)\n\t\t\t\t\tclient:GetCharacter():TakeMoney(amount)\n\n\t\t\t\t\ttarget:NotifyLocalized(\"moneyTaken\", ix.currency.Get(amount))\n\t\t\t\t\tclient:NotifyLocalized(\"moneyGiven\", ix.currency.Get(amount))\n\t\t\t\tend\n\t\t\tend\n\t\t})\n\n\t\tix.command.Add(\"CharSet\" .. MONEY_NAME, {\n\t\t\talias = {\"CharSetMoney\"},\n\t\t\tdescription = \"@cmdCharSetMoney\",\n\t\t\tsuperAdminOnly = true,\n\t\t\targuments = {\n\t\t\t\tix.type.character,\n\t\t\t\tix.type.number\n\t\t\t},\n\t\t\tOnRun = function(self, client, target, amount)\n\t\t\t\tamount = math.Round(amount)\n\n\t\t\t\tif (amount <= 0) then\n\t\t\t\t\treturn \"@invalidArg\", 2\n\t\t\t\tend\n\n\t\t\t\ttarget:SetMoney(amount)\n\t\t\t\tclient:NotifyLocalized(\"setMoney\", target:GetName(), ix.currency.Get(amount))\n\t\t\tend\n\t\t})\n\n\t\tix.command.Add(\"Drop\" .. MONEY_NAME, {\n\t\t\talias = {\"DropMoney\"},\n\t\t\tdescription = \"@cmdDropMoney\",\n\t\t\targuments = ix.type.number,\n\t\t\tOnRun = function(self, client, amount)\n\t\t\t\tamount = math.Round(amount)\n\n\t\t\t\tlocal minDropAmount = ix.config.Get(\"minMoneyDropAmount\", 1)\n\n\t\t\t\tif (amount < minDropAmount) then\n\t\t\t\t\treturn \"@belowMinMoneyDrop\", minDropAmount\n\t\t\t\tend\n\n\t\t\t\tif (!client:GetCharacter():HasMoney(amount)) then\n\t\t\t\t\treturn \"@insufficientMoney\"\n\t\t\t\tend\n\n\t\t\t\tclient:GetCharacter():TakeMoney(amount)\n\n\t\t\t\tlocal money = ix.currency.Spawn(client, amount)\n\t\t\t\tmoney.ixCharID = client:GetCharacter():GetID()\n\t\t\t\tmoney.ixSteamID = client:SteamID()\n\t\t\tend\n\t\t})\n\tend)\nend\n\nix.command.Add(\"PlyWhitelist\", {\n\tdescription = \"@cmdPlyWhitelist\",\n\tprivilege = \"Manage Character Whitelist\",\n\tsuperAdminOnly = true,\n\targuments = {\n\t\tix.type.player,\n\t\tix.type.text\n\t},\n\tOnRun = function(self, client, target, name)\n\t\tif (name == \"\") then\n\t\t\treturn \"@invalidArg\", 2\n\t\tend\n\n\t\tlocal faction = ix.faction.teams[name]\n\n\t\tif (!faction) then\n\t\t\tfor _, v in ipairs(ix.faction.indices) do\n\t\t\t\tif (ix.util.StringMatches(L(v.name, client), name) or ix.util.StringMatches(v.uniqueID, name)) then\n\t\t\t\t\tfaction = v\n\n\t\t\t\t\tbreak\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\n\t\tif (faction) then\n\t\t\tif (target:SetWhitelisted(faction.index, true)) then\n\t\t\t\tfor _, v in player.Iterator() do\n\t\t\t\t\tif (self:OnCheckAccess(v) or v == target) then\n\t\t\t\t\t\tv:NotifyLocalized(\"whitelist\", client:GetName(), target:GetName(), L(faction.name, v))\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\t\telse\n\t\t\treturn \"@invalidFaction\"\n\t\tend\n\tend\n})\n\nix.command.Add(\"CharGetUp\", {\n\tdescription = \"@cmdCharGetUp\",\n\tOnRun = function(self, client, arguments)\n\t\tlocal entity = client.ixRagdoll\n\n\t\tif (IsValid(entity) and entity.ixGrace and entity.ixGrace < CurTime() and\n\t\t\tentity:GetVelocity():Length2D() < 8 and !entity.ixWakingUp) then\n\t\t\tentity.ixWakingUp = true\n\t\t\tentity:CallOnRemove(\"CharGetUp\", function()\n\t\t\t\tclient:SetAction()\n\t\t\tend)\n\n\t\t\tclient:SetAction(\"@gettingUp\", 5, function()\n\t\t\t\tif (!IsValid(entity)) then\n\t\t\t\t\treturn\n\t\t\t\tend\n\n\t\t\t\thook.Run(\"OnCharacterGetup\", client, entity)\n\t\t\t\tentity:Remove()\n\t\t\tend)\n\t\tend\n\tend\n})\n\nix.command.Add(\"PlyUnwhitelist\", {\n\tdescription = \"@cmdPlyUnwhitelist\",\n\tprivilege = \"Manage Character Whitelist\",\n\tsuperAdminOnly = true,\n\targuments = {\n\t\tix.type.string,\n\t\tix.type.text\n\t},\n\tOnRun = function(self, client, target, name)\n\t\tlocal faction = ix.faction.teams[name]\n\n\t\tif (!faction) then\n\t\t\tfor _, v in ipairs(ix.faction.indices) do\n\t\t\t\tif (ix.util.StringMatches(L(v.name, client), name) or ix.util.StringMatches(v.uniqueID, name)) then\n\t\t\t\t\tfaction = v\n\n\t\t\t\t\tbreak\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\n\t\tif (faction) then\n\t\t\tlocal targetPlayer = ix.util.FindPlayer(target)\n\n\t\t\tif (IsValid(targetPlayer) and targetPlayer:SetWhitelisted(faction.index, false)) then\n\t\t\t\tfor _, v in player.Iterator() do\n\t\t\t\t\tif (self:OnCheckAccess(v) or v == targetPlayer) then\n\t\t\t\t\t\tv:NotifyLocalized(\"unwhitelist\", client:GetName(), targetPlayer:GetName(), L(faction.name, v))\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\telse\n\t\t\t\tlocal steamID64 = util.SteamIDTo64(target)\n\t\t\t\tlocal query = mysql:Select(\"ix_players\")\n\t\t\t\t\tquery:Select(\"data\")\n\t\t\t\t\tquery:Where(\"steamid\", steamID64)\n\t\t\t\t\tquery:Limit(1)\n\t\t\t\t\tquery:Callback(function(result)\n\t\t\t\t\t\tif (istable(result) and #result > 0) then\n\t\t\t\t\t\t\tlocal data = util.JSONToTable(result[1].data or \"[]\")\n\t\t\t\t\t\t\tlocal whitelists = data.whitelists and data.whitelists[Schema.folder]\n\n\t\t\t\t\t\t\tif (!whitelists or !whitelists[faction.uniqueID]) then\n\t\t\t\t\t\t\t\treturn\n\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\t\twhitelists[faction.uniqueID] = nil\n\n\t\t\t\t\t\t\tlocal updateQuery = mysql:Update(\"ix_players\")\n\t\t\t\t\t\t\t\tupdateQuery:Update(\"data\", util.TableToJSON(data))\n\t\t\t\t\t\t\t\tupdateQuery:Where(\"steamid\", steamID64)\n\t\t\t\t\t\t\tupdateQuery:Execute()\n\n\t\t\t\t\t\t\tfor _, v in player.Iterator() do\n\t\t\t\t\t\t\t\tif (self:OnCheckAccess(v)) then\n\t\t\t\t\t\t\t\t\tv:NotifyLocalized(\"unwhitelist\", client:GetName(), target, L(faction.name, v))\n\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\tend\n\t\t\t\t\tend)\n\t\t\t\tquery:Execute()\n\t\t\tend\n\t\telse\n\t\t\treturn \"@invalidFaction\"\n\t\tend\n\tend\n})\n\nix.command.Add(\"CharFallOver\", {\n\tdescription = \"@cmdCharFallOver\",\n\targuments = bit.bor(ix.type.number, ix.type.optional),\n\tOnRun = function(self, client, time)\n\t\tif (!client:Alive() or client:GetMoveType() == MOVETYPE_NOCLIP) then\n\t\t\treturn \"@notNow\"\n\t\tend\n\n\t\tif (time and time > 0) then\n\t\t\ttime = math.Clamp(time, 1, 60)\n\t\tend\n\n\t\tif (!IsValid(client.ixRagdoll)) then\n\t\t\tclient:SetRagdolled(true, time)\n\t\tend\n\tend\n})\n\nix.command.Add(\"BecomeClass\", {\n\tdescription = \"@cmdBecomeClass\",\n\targuments = ix.type.text,\n\tOnRun = function(self, client, class)\n\t\tlocal character = client:GetCharacter()\n\n\t\tif (character) then\n\t\t\tlocal num = isnumber(tonumber(class)) and tonumber(class) or -1\n\n\t\t\tif (ix.class.list[num]) then\n\t\t\t\tlocal v = ix.class.list[num]\n\n\t\t\t\tif (character:JoinClass(num)) then\n\t\t\t\t\treturn \"@becomeClass\", L(v.name, client)\n\t\t\t\telse\n\t\t\t\t\treturn \"@becomeClassFail\", L(v.name, client)\n\t\t\t\tend\n\t\t\telse\n\t\t\t\tfor k, v in ipairs(ix.class.list) do\n\t\t\t\t\tif (ix.util.StringMatches(v.uniqueID, class) or ix.util.StringMatches(L(v.name, client), class)) then\n\t\t\t\t\t\tif (character:JoinClass(k)) then\n\t\t\t\t\t\t\treturn \"@becomeClass\", L(v.name, client)\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\treturn \"@becomeClassFail\", L(v.name, client)\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\n\t\t\treturn \"@invalid\", L(\"class\", client)\n\t\telse\n\t\t\treturn \"@illegalAccess\"\n\t\tend\n\tend\n})\n\nix.command.Add(\"CharDesc\", {\n\tdescription = \"@cmdCharDesc\",\n\targuments = bit.bor(ix.type.text, ix.type.optional),\n\tOnRun = function(self, client, description)\n\t\tif (!description:find(\"%S\")) then\n\t\t\treturn client:RequestString(\"@cmdCharDescTitle\", \"@cmdCharDescDescription\", function(text)\n\t\t\t\tix.command.Run(client, \"CharDesc\", {text})\n\t\t\tend, client:GetCharacter():GetDescription())\n\t\tend\n\n\t\tlocal info = ix.char.vars.description\n\t\tlocal result, fault, count = info:OnValidate(description)\n\n\t\tif (result == false) then\n\t\t\treturn \"@\" .. fault, count\n\t\tend\n\n\t\tclient:GetCharacter():SetDescription(description)\n\t\treturn \"@descChanged\"\n\tend\n})\n\nix.command.Add(\"PlyTransfer\", {\n\tdescription = \"@cmdPlyTransfer\",\n\tadminOnly = true,\n\targuments = {\n\t\tix.type.character,\n\t\tix.type.text\n\t},\n\tOnRun = function(self, client, target, name)\n\t\tlocal faction = ix.faction.teams[name]\n\n\t\tif (!faction) then\n\t\t\tfor _, v in pairs(ix.faction.indices) do\n\t\t\t\tif (ix.util.StringMatches(L(v.name, client), name)) then\n\t\t\t\t\tfaction = v\n\n\t\t\t\t\tbreak\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\n\t\tif (faction) then\n\t\t\tlocal bHasWhitelist = target:GetPlayer():HasWhitelist(faction.index)\n\n\t\t\tif (bHasWhitelist) then\n\t\t\t\ttarget.vars.faction = faction.uniqueID\n\t\t\t\ttarget:SetFaction(faction.index)\n\n\t\t\t\tif (faction.OnTransferred) then\n\t\t\t\t\tfaction:OnTransferred(target)\n\t\t\t\tend\n\n\t\t\t\tfor _, v in player.Iterator() do\n\t\t\t\t\tif (self:OnCheckAccess(v) or v == target:GetPlayer()) then\n\t\t\t\t\t\tv:NotifyLocalized(\"cChangeFaction\", client:GetName(), target:GetName(), L(faction.name, v))\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\telse\n\t\t\t\treturn \"@charNotWhitelisted\", target:GetName(), L(faction.name, client)\n\t\t\tend\n\t\telse\n\t\t\treturn \"@invalidFaction\"\n\t\tend\n\tend\n})\n\nix.command.Add(\"CharSetClass\", {\n\tdescription = \"@cmdCharSetClass\",\n\tadminOnly = true,\n\targuments = {\n\t\tix.type.character,\n\t\tix.type.text\n\t},\n\tOnRun = function(self, client, target, class)\n\t\tlocal classTable\n\n\t\tfor _, v in ipairs(ix.class.list) do\n\t\t\tif (ix.util.StringMatches(v.uniqueID, class) or ix.util.StringMatches(v.name, class)) then\n\t\t\t\tclassTable = v\n\t\t\tend\n\t\tend\n\n\t\tif (classTable) then\n\t\t\tlocal oldClass = target:GetClass()\n\t\t\tlocal targetPlayer = target:GetPlayer()\n\n\t\t\tif (targetPlayer:Team() == classTable.faction) then\n\t\t\t\ttarget:SetClass(classTable.index)\n\t\t\t\thook.Run(\"PlayerJoinedClass\", targetPlayer, classTable.index, oldClass)\n\n\t\t\t\ttargetPlayer:NotifyLocalized(\"becomeClass\", L(classTable.name, targetPlayer))\n\n\t\t\t\t-- only send second notification if the character isn't setting their own class\n\t\t\t\tif (client != targetPlayer) then\n\t\t\t\t\treturn \"@setClass\", target:GetName(), L(classTable.name, client)\n\t\t\t\tend\n\t\t\telse\n\t\t\t\treturn \"@invalidClassFaction\"\n\t\t\tend\n\t\telse\n\t\t\treturn \"@invalidClass\"\n\t\tend\n\tend\n})\n\nix.command.Add(\"MapRestart\", {\n\tdescription = \"@cmdMapRestart\",\n\tadminOnly = true,\n\targuments = bit.bor(ix.type.number, ix.type.optional),\n\tOnRun = function(self, client, delay)\n\t\tdelay = delay or 10\n\t\tix.util.NotifyLocalized(\"mapRestarting\", nil, delay)\n\n\t\ttimer.Simple(delay, function()\n\t\t\tRunConsoleCommand(\"changelevel\", game.GetMap())\n\t\tend)\n\tend\n})\n"
  },
  {
    "path": "gamemode/core/sh_config.lua",
    "content": "\n--- Helper library for creating/setting config options.\n-- @module ix.config\n\nix.config = ix.config or {}\nix.config.stored = ix.config.stored or {}\n\nif (SERVER) then\n\tutil.AddNetworkString(\"ixConfigList\")\n\tutil.AddNetworkString(\"ixConfigSet\")\n\tutil.AddNetworkString(\"ixConfigRequestUnloadedList\")\n\tutil.AddNetworkString(\"ixConfigUnloadedList\")\n\tutil.AddNetworkString(\"ixConfigPluginToggle\")\n\n\tix.config.server = ix.yaml.Read(\"gamemodes/helix/helix.yml\") or {}\nend\n\nCAMI.RegisterPrivilege({\n\tName = \"Helix - Manage Config\",\n\tMinAccess = \"superadmin\"\n})\n\n--- Creates a config option with the given information.\n-- @realm shared\n-- @string key Unique ID of the config\n-- @param value Default value that this config will have\n-- @string description Description of the config\n-- @func[opt=nil] callback Function to call when config is changed\n-- @tab[opt=nil] data Additional settings for this config option\n-- @bool[opt=false] bNoNetworking Whether or not to prevent networking the config\n-- @bool[opt=false] bSchemaOnly Whether or not the config is for the schema only\nfunction ix.config.Add(key, value, description, callback, data, bNoNetworking, bSchemaOnly)\n\tdata = istable(data) and data or {}\n\n\tlocal oldConfig = ix.config.stored[key]\n\tlocal type = data.type or ix.util.GetTypeFromValue(value)\n\n\tif (!type) then\n\t\tErrorNoHalt(\"attempted to add config with invalid type\\n\")\n\t\treturn\n\tend\n\n\tlocal default = value\n\tdata.type = nil\n\n\t-- using explicit nil comparisons so we don't get caught by a config's value being `false`\n\tif (oldConfig != nil) then\n\t\tif (oldConfig.value != nil) then\n\t\t\tvalue = oldConfig.value\n\t\tend\n\n\t\tif (oldConfig.default != nil) then\n\t\t\tdefault = oldConfig.default\n\t\tend\n\tend\n\n\tix.config.stored[key] = {\n\t\ttype = type,\n\t\tdata = data,\n\t\tvalue = value,\n\t\tdefault = default,\n\t\tdescription = description,\n\t\tbNoNetworking = bNoNetworking,\n\t\tglobal = !bSchemaOnly,\n\t\tcallback = callback,\n\t\thidden = data.hidden or nil\n\t}\nend\n\n--- Sets the default value for a config option.\n-- @realm shared\n-- @string key Unique ID of the config\n-- @param value Default value for the config option\nfunction ix.config.SetDefault(key, value)\n\tlocal config = ix.config.stored[key]\n\n\tif (config) then\n\t\tconfig.default = value\n\telse\n\t\t-- set up dummy config if we're setting default of config that doesn't exist yet (i.e schema setting framework default)\n\t\tix.config.stored[key] = {\n\t\t\tvalue = value,\n\t\t\tdefault = value\n\t\t}\n\tend\nend\n\nfunction ix.config.ForceSet(key, value, noSave)\n\tlocal config = ix.config.stored[key]\n\n\tif (config) then\n\t\tconfig.value = value\n\tend\n\n\tif (noSave) then\n\t\tix.config.Save()\n\tend\nend\n\n--- Sets the value of a config option.\n-- @realm shared\n-- @string key Unique ID of the config\n-- @param value New value to assign to the config\nfunction ix.config.Set(key, value)\n\tlocal config = ix.config.stored[key]\n\n\tif (config) then\n\t\tlocal oldValue = value\n\t\tconfig.value = value\n\n\t\tif (SERVER) then\n\t\t\tif (!config.bNoNetworking) then\n\t\t\t\tnet.Start(\"ixConfigSet\")\n\t\t\t\t\tnet.WriteString(key)\n\t\t\t\t\tnet.WriteType(value)\n\t\t\t\tnet.Broadcast()\n\t\t\tend\n\n\t\t\tif (config.callback) then\n\t\t\t\tconfig.callback(oldValue, value)\n\t\t\tend\n\n\t\t\tix.config.Save()\n\t\tend\n\tend\nend\n\n--- Retrieves a value of a config option. If it is not set, it'll return the default that you've specified.\n-- @realm shared\n-- @string key Unique ID of the config\n-- @param default Default value to return if the config is not set\n-- @return Value associated with the key, or the default that was given if it doesn't exist\nfunction ix.config.Get(key, default)\n\tlocal config = ix.config.stored[key]\n\n\t-- ensure we aren't accessing a dummy value\n\tif (config and config.type) then\n\t\tif (config.value != nil) then\n\t\t\treturn config.value\n\t\telseif (config.default != nil) then\n\t\t\treturn config.default\n\t\tend\n\tend\n\n\treturn default\nend\n\n--- Loads all saved config options from disk.\n-- @realm shared\n-- @internal\nfunction ix.config.Load()\n\tif (SERVER) then\n\t\tlocal globals = ix.data.Get(\"config\", nil, true, true)\n\t\tlocal data = ix.data.Get(\"config\", nil, false, true)\n\n\t\tif (globals) then\n\t\t\tfor k, v in pairs(globals) do\n\t\t\t\tix.config.stored[k] = ix.config.stored[k] or {}\n\t\t\t\tix.config.stored[k].value = v\n\t\t\tend\n\t\tend\n\n\t\tif (data) then\n\t\t\tfor k, v in pairs(data) do\n\t\t\t\tix.config.stored[k] = ix.config.stored[k] or {}\n\t\t\t\tix.config.stored[k].value = v\n\t\t\tend\n\t\tend\n\tend\n\n\tix.util.Include(\"helix/gamemode/config/sh_config.lua\")\n\n\tif (SERVER or !IX_RELOADED) then\n\t\thook.Run(\"InitializedConfig\")\n\tend\nend\n\nif (SERVER) then\n\tfunction ix.config.GetChangedValues()\n\t\tlocal data = {}\n\n\t\tfor k, v in pairs(ix.config.stored) do\n\t\t\tif (v.default != v.value) then\n\t\t\t\tdata[k] = v.value\n\t\t\tend\n\t\tend\n\n\t\treturn data\n\tend\n\n\tfunction ix.config.Send(client)\n\t\tnet.Start(\"ixConfigList\")\n\t\t\tnet.WriteTable(ix.config.GetChangedValues())\n\t\tnet.Send(client)\n\tend\n\n\t--- Saves all config options to disk.\n\t-- @realm server\n\t-- @internal\n\tfunction ix.config.Save()\n\t\tlocal globals = {}\n\t\tlocal data = {}\n\n\t\tfor k, v in pairs(ix.config.GetChangedValues()) do\n\t\t\tif (ix.config.stored[k].global) then\n\t\t\t\tglobals[k] = v\n\t\t\telse\n\t\t\t\tdata[k] = v\n\t\t\tend\n\t\tend\n\n\t\t-- Global and schema data set respectively.\n\t\tix.data.Set(\"config\", globals, true, true)\n\t\tix.data.Set(\"config\", data, false, true)\n\tend\n\n\tnet.Receive(\"ixConfigSet\", function(length, client)\n\t\tlocal key = net.ReadString()\n\t\tlocal value = net.ReadType()\n\n\t\tif (CAMI.PlayerHasAccess(client, \"Helix - Manage Config\", nil) and\n\t\t\ttype(ix.config.stored[key].default) == type(value)) then\n\t\t\tix.config.Set(key, value)\n\n\t\t\tif (ix.util.IsColor(value)) then\n\t\t\t\tvalue = string.format(\"[%d, %d, %d]\", value.r, value.g, value.b)\n\t\t\telseif (istable(value)) then\n\t\t\t\tlocal value2 = \"[\"\n\t\t\t\tlocal count = table.Count(value)\n\t\t\t\tlocal i = 1\n\n\t\t\t\tfor _, v in SortedPairs(value) do\n\t\t\t\t\tvalue2 = value2 .. v .. (i == count and \"]\" or \", \")\n\t\t\t\t\ti = i + 1\n\t\t\t\tend\n\n\t\t\t\tvalue = value2\n\t\t\telseif (isstring(value)) then\n\t\t\t\tvalue = string.format(\"\\\"%s\\\"\", tostring(value))\n\t\t\telseif (isbool(value)) then\n\t\t\t\tvalue = string.format(\"[%s]\", tostring(value))\n\t\t\tend\n\n\t\t\tix.util.NotifyLocalized(\"cfgSet\", nil, client:Name(), key, tostring(value))\n\t\t\tix.log.Add(client, \"cfgSet\", key, value)\n\t\tend\n\tend)\n\n\tnet.Receive(\"ixConfigRequestUnloadedList\", function(length, client)\n\t\tif (!CAMI.PlayerHasAccess(client, \"Helix - Manage Config\", nil)) then\n\t\t\treturn\n\t\tend\n\n\t\tnet.Start(\"ixConfigUnloadedList\")\n\t\t\tnet.WriteTable(ix.plugin.unloaded)\n\t\tnet.Send(client)\n\tend)\n\n\tnet.Receive(\"ixConfigPluginToggle\", function(length, client)\n\t\tif (!CAMI.PlayerHasAccess(client, \"Helix - Manage Config\", nil)) then\n\t\t\treturn\n\t\tend\n\n\t\tlocal uniqueID = net.ReadString()\n\t\tlocal bUnloaded = !!ix.plugin.unloaded[uniqueID]\n\t\tlocal bShouldEnable = net.ReadBool()\n\n\t\tif ((bShouldEnable and bUnloaded) or (!bShouldEnable and !bUnloaded)) then\n\t\t\tix.plugin.SetUnloaded(uniqueID, !bShouldEnable) -- flip bool since we're setting unloaded, not enabled\n\n\t\t\tix.util.NotifyLocalized(bShouldEnable and \"pluginLoaded\" or \"pluginUnloaded\", nil, client:GetName(), uniqueID)\n\t\t\tix.log.Add(client, bShouldEnable and \"pluginLoaded\" or \"pluginUnloaded\", uniqueID)\n\n\t\t\tnet.Start(\"ixConfigPluginToggle\")\n\t\t\t\tnet.WriteString(uniqueID)\n\t\t\t\tnet.WriteBool(bShouldEnable)\n\t\t\tnet.Broadcast()\n\t\tend\n\tend)\nelse\n\tnet.Receive(\"ixConfigList\", function()\n\t\tlocal data = net.ReadTable()\n\n\t\tfor k, v in pairs(data) do\n\t\t\tif (ix.config.stored[k]) then\n\t\t\t\tix.config.stored[k].value = v\n\t\t\tend\n\t\tend\n\n\t\thook.Run(\"InitializedConfig\", data)\n\tend)\n\n\tnet.Receive(\"ixConfigSet\", function()\n\t\tlocal key = net.ReadString()\n\t\tlocal value = net.ReadType()\n\t\tlocal config = ix.config.stored[key]\n\n\t\tif (config) then\n\t\t\tif (config.callback) then\n\t\t\t\tconfig.callback(config.value, value)\n\t\t\tend\n\n\t\t\tconfig.value = value\n\n\t\t\tlocal properties = ix.gui.properties\n\n\t\t\tif (IsValid(properties)) then\n\t\t\t\tlocal row = properties:GetCategory(L(config.data and config.data.category or \"misc\")):GetRow(key)\n\n\t\t\t\tif (IsValid(row)) then\n\t\t\t\t\tif (istable(value) and value.r and value.g and value.b) then\n\t\t\t\t\t\tvalue = Vector(value.r / 255, value.g / 255, value.b / 255)\n\t\t\t\t\tend\n\n\t\t\t\t\trow:SetValue(value)\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend)\n\n\tnet.Receive(\"ixConfigUnloadedList\", function()\n\t\tix.plugin.unloaded = net.ReadTable()\n\t\tix.gui.bReceivedUnloadedPlugins = true\n\n\t\tif (IsValid(ix.gui.pluginManager)) then\n\t\t\tix.gui.pluginManager:UpdateUnloaded()\n\t\tend\n\tend)\n\n\tnet.Receive(\"ixConfigPluginToggle\", function()\n\t\tlocal uniqueID = net.ReadString()\n\t\tlocal bEnabled = net.ReadBool()\n\n\t\tif (bEnabled) then\n\t\t\tix.plugin.unloaded[uniqueID] = false\n\t\telse\n\t\t\tix.plugin.unloaded[uniqueID] = true\n\t\tend\n\n\t\tif (IsValid(ix.gui.pluginManager)) then\n\t\t\tix.gui.pluginManager:UpdatePlugin(uniqueID, bEnabled)\n\t\tend\n\tend)\n\n\thook.Add(\"CreateMenuButtons\", \"ixConfig\", function(tabs)\n\t\tif (!CAMI.PlayerHasAccess(LocalPlayer(), \"Helix - Manage Config\", nil)) then\n\t\t\treturn\n\t\tend\n\n\t\ttabs[\"config\"] = {\n\t\t\tCreate = function(info, container)\n\t\t\t\tcontainer.panel = container:Add(\"ixConfigManager\")\n\t\t\tend,\n\n\t\t\tOnSelected = function(info, container)\n\t\t\t\tcontainer.panel.searchEntry:RequestFocus()\n\t\t\tend,\n\n\t\t\tSections = {\n\t\t\t\tplugins = {\n\t\t\t\t\tCreate = function(info, container)\n\t\t\t\t\t\tix.gui.pluginManager = container:Add(\"ixPluginManager\")\n\t\t\t\t\tend,\n\n\t\t\t\t\tOnSelected = function(info, container)\n\t\t\t\t\t\tix.gui.pluginManager.searchEntry:RequestFocus()\n\t\t\t\t\tend\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\tend)\nend\n"
  },
  {
    "path": "gamemode/core/sh_data.lua",
    "content": "\n--- Helper library for reading/writing files to the data folder.\n-- @module ix.data\n\nix.data = ix.data or {}\nix.data.stored = ix.data.stored or {}\n\n-- Create a folder to store data in.\nfile.CreateDir(\"helix\")\n\n--- Populates a file in the `data/helix` folder with some serialized data.\n-- @realm shared\n-- @string key Name of the file to save\n-- @param value Some sort of data to save\n-- @bool[opt=false] bGlobal Whether or not to write directly to the `data/helix` folder, or the `data/helix/schema` folder,\n-- where `schema` is the name of the current schema.\n-- @bool[opt=false] bIgnoreMap Whether or not to ignore the map and save in the schema folder, rather than\n-- `data/helix/schema/map`, where `map` is the name of the current map.\nfunction ix.data.Set(key, value, bGlobal, bIgnoreMap)\n\t-- Get the base path to write to.\n\tlocal path = \"helix/\" .. (bGlobal and \"\" or Schema.folder .. \"/\") .. (bIgnoreMap and \"\" or game.GetMap() .. \"/\")\n\n\t-- Create the schema folder if the data is not global.\n\tif (!bGlobal) then\n\t\tfile.CreateDir(\"helix/\" .. Schema.folder .. \"/\")\n\tend\n\n\t-- If we're not ignoring the map, create a folder for the map.\n\tfile.CreateDir(path)\n\t-- Write the data using JSON encoding.\n\tfile.Write(path .. key .. \".txt\", util.TableToJSON({value}))\n\n\t-- Cache the data value here.\n\tix.data.stored[key] = value\n\n\treturn path\nend\n\n--- Retrieves the contents of a saved file in the `data/helix` folder.\n-- @realm shared\n-- @string key Name of the file to load\n-- @param default Value to return if the file could not be loaded successfully\n-- @bool[opt=false] bGlobal Whether or not the data is in the `data/helix` folder, or the `data/helix/schema` folder,\n-- where `schema` is the name of the current schema.\n-- @bool[opt=false] bIgnoreMap Whether or not to ignore the map and load from the schema folder, rather than\n-- `data/helix/schema/map`, where `map` is the name of the current map.\n-- @bool[opt=false] bRefresh Whether or not to skip the cache and forcefully load from disk.\n-- @return Value associated with the key, or the default that was given if it doesn't exists\nfunction ix.data.Get(key, default, bGlobal, bIgnoreMap, bRefresh)\n\t-- If it exists in the cache, return the cached value so it is faster.\n\tif (!bRefresh) then\n\t\tlocal stored = ix.data.stored[key]\n\n\t\tif (stored != nil) then\n\t\t\treturn stored\n\t\tend\n\tend\n\n\t-- Get the path to read from.\n\tlocal path = \"helix/\" .. (bGlobal and \"\" or Schema.folder .. \"/\") .. (bIgnoreMap and \"\" or game.GetMap() .. \"/\")\n\t-- Read the data from a local file.\n\tlocal contents = file.Read(path .. key .. \".txt\", \"DATA\")\n\n\tif (contents and contents != \"\") then\n\t\tlocal status, decoded = pcall(util.JSONToTable, contents)\n\n\t\tif (status and decoded) then\n\t\t\tlocal value = decoded[1]\n\n\t\t\tif (value != nil) then\n\t\t\t\treturn value\n\t\t\tend\n\t\tend\n\n\t\t-- Backwards compatibility.\n\t\t-- This may be removed in the future.\n\t\tstatus, decoded = pcall(pon.decode, contents)\n\n\t\tif (status and decoded) then\n\t\t\tlocal value = decoded[1]\n\n\t\t\tif (value != nil) then\n\t\t\t\treturn value\n\t\t\tend\n\t\tend\n\tend\n\n\treturn default\nend\n\n--- Deletes the contents of a saved file in the `data/helix` folder.\n-- @realm shared\n-- @string key Name of the file to delete\n-- @bool[opt=false] bGlobal Whether or not the data is in the `data/helix` folder, or the `data/helix/schema` folder,\n-- where `schema` is the name of the current schema.\n-- @bool[opt=false] bIgnoreMap Whether or not to ignore the map and delete from the schema folder, rather than\n-- `data/helix/schema/map`, where `map` is the name of the current map.\n-- @treturn bool Whether or not the deletion has succeeded\nfunction ix.data.Delete(key, bGlobal, bIgnoreMap)\n\t-- Get the path to read from.\n\tlocal path = \"helix/\" .. (bGlobal and \"\" or Schema.folder .. \"/\") .. (bIgnoreMap and \"\" or game.GetMap() .. \"/\")\n\t-- Read the data from a local file.\n\tlocal contents = file.Read(path .. key .. \".txt\", \"DATA\")\n\n\tif (contents and contents != \"\") then\n\t\tfile.Delete(path .. key .. \".txt\")\n\t\tix.data.stored[key] = nil\n\t\treturn true\n\tend\n\n\treturn false\nend\n\nif (SERVER) then\n\ttimer.Create(\"ixSaveData\", 600, 0, function()\n\t\thook.Run(\"SaveData\")\n\tend)\nend\n"
  },
  {
    "path": "gamemode/core/sh_util.lua",
    "content": "\n--- Various useful helper functions.\n-- @module ix.util\n\nix.type = ix.type or {\n\t[2] = \"string\",\n\t[4] = \"text\",\n\t[8] = \"number\",\n\t[16] = \"player\",\n\t[32] = \"steamid\",\n\t[64] = \"character\",\n\t[128] = \"bool\",\n\t[1024] = \"color\",\n\t[2048] = \"vector\",\n\n\tstring = 2,\n\ttext = 4,\n\tnumber = 8,\n\tplayer = 16,\n\tsteamid = 32,\n\tcharacter = 64,\n\tbool = 128,\n\tcolor = 1024,\n\tvector = 2048,\n\n\toptional = 256,\n\tarray = 512\n}\n\nix.blurRenderQueue = {}\n\n--- Includes a lua file based on the prefix of the file. This will automatically call `include` and `AddCSLuaFile` based on the\n-- current realm. This function should always be called shared to ensure that the client will receive the file from the server.\n-- @realm shared\n-- @string fileName Path of the Lua file to include. The path is relative to the file that is currently running this function\n-- @string[opt] realm Realm that this file should be included in. You should usually ignore this since it\n-- will be automatically be chosen based on the `SERVER` and `CLIENT` globals. This value should either be `\"server\"` or\n-- `\"client\"` if it is filled in manually\nfunction ix.util.Include(fileName, realm)\n\tif (!fileName) then\n\t\terror(\"[Helix] No file name specified for including.\")\n\tend\n\n\t-- Only include server-side if we're on the server.\n\tif ((realm == \"server\" or fileName:find(\"sv_\")) and SERVER) then\n\t\treturn include(fileName)\n\t-- Shared is included by both server and client.\n\telseif (realm == \"shared\" or fileName:find(\"shared.lua\") or fileName:find(\"sh_\")) then\n\t\tif (SERVER) then\n\t\t\t-- Send the file to the client if shared so they can run it.\n\t\t\tAddCSLuaFile(fileName)\n\t\tend\n\n\t\treturn include(fileName)\n\t-- File is sent to client, included on client.\n\telseif (realm == \"client\" or fileName:find(\"cl_\")) then\n\t\tif (SERVER) then\n\t\t\tAddCSLuaFile(fileName)\n\t\telse\n\t\t\treturn include(fileName)\n\t\tend\n\tend\nend\n\n--- Includes multiple files in a directory.\n-- @realm shared\n-- @string directory Directory to include files from\n-- @bool[opt] bFromLua Whether or not to search from the base `lua/` folder, instead of contextually basing from `schema/`\n-- or `gamemode/`\n-- @see ix.util.Include\nfunction ix.util.IncludeDir(directory, bFromLua)\n\t-- By default, we include relatively to Helix.\n\tlocal baseDir = \"helix\"\n\n\t-- If we're in a schema, include relative to the schema.\n\tif (Schema and Schema.folder and Schema.loading) then\n\t\tbaseDir = Schema.folder..\"/schema/\"\n\telse\n\t\tbaseDir = baseDir..\"/gamemode/\"\n\tend\n\n\t-- Find all of the files within the directory.\n\tfor _, v in ipairs(file.Find((bFromLua and \"\" or baseDir)..directory..\"/*.lua\", \"LUA\")) do\n\t\t-- Include the file from the prefix.\n\t\tix.util.Include(directory..\"/\"..v)\n\tend\nend\n\n--- Removes the realm prefix from a file name. The returned string will be unchanged if there is no prefix found.\n-- @realm shared\n-- @string name String to strip prefix from\n-- @treturn string String stripped of prefix\n-- @usage print(ix.util.StripRealmPrefix(\"sv_init.lua\"))\n-- > init.lua\nfunction ix.util.StripRealmPrefix(name)\n\tlocal prefix = name:sub(1, 3)\n\n\treturn (prefix == \"sh_\" or prefix == \"sv_\" or prefix == \"cl_\") and name:sub(4) or name\nend\n\n--- Returns `true` if the given input is a color table. This is necessary since the engine `IsColor` function only checks for\n-- color metatables - which are not used for regular Lua color types.\n-- @realm shared\n-- @param input Input to check\n-- @treturn bool Whether or not the input is a color\nfunction ix.util.IsColor(input)\n\treturn istable(input) and\n\t\tisnumber(input.a) and isnumber(input.g) and isnumber(input.b) and (input.a and isnumber(input.a) or input.a == nil)\nend\n\n--- Returns a dimmed version of the given color by the given scale.\n-- @realm shared\n-- @color color Color to dim\n-- @number multiplier What to multiply the red, green, and blue values by\n-- @number[opt=255] alpha Alpha to use in dimmed color\n-- @treturn color Dimmed color\n-- @usage print(ix.util.DimColor(Color(100, 100, 100, 255), 0.5))\n-- > 50 50 50 255\nfunction ix.util.DimColor(color, multiplier, alpha)\n\treturn Color(color.r * multiplier, color.g * multiplier, color.b * multiplier, alpha or 255)\nend\n\n--- Sanitizes an input value with the given type. This function ensures that a valid type is always returned. If a valid value\n-- could not be found, it will return the default value for the type. This only works for simple types - e.g it does not work\n-- for player, character, or Steam ID types.\n-- @realm shared\n-- @ixtype type Type to check for\n-- @param input Value to sanitize\n-- @return Sanitized value\n-- @see ix.type\n-- @usage print(ix.util.SanitizeType(ix.type.number, \"123\"))\n-- > 123\n-- print(ix.util.SanitizeType(ix.type.bool, 1))\n-- > true\nfunction ix.util.SanitizeType(type, input)\n\tif (type == ix.type.string) then\n\t\treturn tostring(input)\n\telseif (type == ix.type.text) then\n\t\treturn tostring(input)\n\telseif (type == ix.type.number) then\n\t\treturn tonumber(input or 0) or 0\n\telseif (type == ix.type.bool) then\n\t\treturn tobool(input)\n\telseif (type == ix.type.color) then\n\t\treturn istable(input) and\n\t\t\tColor(tonumber(input.r) or 255, tonumber(input.g) or 255, tonumber(input.b) or 255, tonumber(input.a) or 255) or\n\t\t\tcolor_white\n\telseif (type == ix.type.vector) then\n\t\treturn isvector(input) and input or vector_origin\n\telseif (type == ix.type.array) then\n\t\treturn input\n\telse\n\t\terror(\"attempted to sanitize \" .. (ix.type[type] and (\"invalid type \" .. ix.type[type]) or \"unknown type \" .. type))\n\tend\nend\n\ndo\n\tlocal typeMap = {\n\t\tstring = ix.type.string,\n\t\tnumber = ix.type.number,\n\t\tPlayer = ix.type.player,\n\t\tboolean = ix.type.bool,\n\t\tVector = ix.type.vector\n\t}\n\n\tlocal tableMap = {\n\t\t[ix.type.character] = function(value)\n\t\t\treturn getmetatable(value) == ix.meta.character\n\t\tend,\n\n\t\t[ix.type.color] = function(value)\n\t\t\treturn ix.util.IsColor(value)\n\t\tend,\n\n\t\t[ix.type.steamid] = function(value)\n\t\t\treturn isstring(value) and (value:match(\"STEAM_(%d+):(%d+):(%d+)\")) != nil\n\t\tend\n\t}\n\n\t--- Returns the `ix.type` of the given value.\n\t-- @realm shared\n\t-- @param value Value to get the type of\n\t-- @treturn ix.type Type of value\n\t-- @see ix.type\n\t-- @usage print(ix.util.GetTypeFromValue(\"hello\"))\n\t-- > 2 -- i.e the value of ix.type.string\n\tfunction ix.util.GetTypeFromValue(value)\n\t\tlocal result = typeMap[type(value)]\n\n\t\tif (result) then\n\t\t\treturn result\n\t\tend\n\n\t\tif (istable(value)) then\n\t\t\tfor k, v in pairs(tableMap) do\n\t\t\t\tif (v(value)) then\n\t\t\t\t\treturn k\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend\nend\n\nfunction ix.util.Bind(self, callback)\n\treturn function(_, ...)\n\t\treturn callback(self, ...)\n\tend\nend\n\n-- Returns the address:port of the server.\nfunction ix.util.GetAddress()\n\treturn game.GetIPAddress()\nend\n\n--- Returns a cached copy of the given material, or creates and caches one if it doesn't exist. This is a quick helper function\n-- if you aren't locally storing a `Material()` call.\n-- @realm shared\n-- @string materialPath Path to the material\n-- @treturn[1] material The cached material\n-- @treturn[2] nil If the material doesn't exist in the filesystem\nfunction ix.util.GetMaterial(materialPath)\n\t-- Cache the material.\n\tix.util.cachedMaterials = ix.util.cachedMaterials or {}\n\tix.util.cachedMaterials[materialPath] = ix.util.cachedMaterials[materialPath] or Material(materialPath)\n\n\treturn ix.util.cachedMaterials[materialPath]\nend\n\n--- Attempts to find a player by matching their name or Steam ID.\n-- @realm shared\n-- @string identifier Search query\n-- @bool[opt=false] bAllowPatterns Whether or not to accept Lua patterns in `identifier`\n-- @treturn player Player that matches the given search query - this will be `nil` if a player could not be found\nfunction ix.util.FindPlayer(identifier, bAllowPatterns)\n\tif (#identifier == 0) then return end\n\n\tif (string.find(identifier, \"STEAM_(%d+):(%d+):(%d+)\")) then\n\t\treturn player.GetBySteamID(identifier)\n\tend\n\n\tif (!bAllowPatterns) then\n\t\tidentifier = string.PatternSafe(identifier)\n\tend\n\n\tfor _, v in player.Iterator() do\n\t\tif (ix.util.StringMatches(v:Name(), identifier)) then\n\t\t\treturn v\n\t\tend\n\tend\nend\n\n--- Checks to see if two strings are equivalent using a fuzzy manner. Both strings will be lowered, and will return `true` if\n-- the strings are identical, or if `b` is a substring of `a`.\n-- @realm shared\n-- @string a First string to check\n-- @string b Second string to check\n-- @treturn bool Whether or not the strings are equivalent\nfunction ix.util.StringMatches(a, b)\n\tif (a and b) then\n\t\tlocal a2, b2 = a:utf8lower(), b:utf8lower()\n\n\t\t-- Check if the actual letters match.\n\t\tif (a == b) then return true end\n\t\tif (a2 == b2) then return true end\n\n\t\t-- Be less strict and search.\n\t\tif (a:find(b)) then return true end\n\t\tif (a2:find(b2)) then return true end\n\tend\n\n\treturn false\nend\n\n--- Returns a string that has the named arguments in the format string replaced with the given arguments.\n-- @realm shared\n-- @string format Format string\n-- @tparam tab|... Arguments to pass to the formatted string. If passed a table, it will use that table as the lookup table for\n-- the named arguments. If passed multiple arguments, it will replace the arguments in the string in order.\n-- @usage print(ix.util.FormatStringNamed(\"Hi, my name is {name}.\", {name = \"Bobby\"}))\n-- > Hi, my name is Bobby.\n-- @usage print(ix.util.FormatStringNamed(\"Hi, my name is {name}.\", \"Bobby\"))\n-- > Hi, my name is Bobby.\nfunction ix.util.FormatStringNamed(format, ...)\n\tlocal arguments = {...}\n\tlocal bArray = false -- Whether or not the input has numerical indices or named ones\n\tlocal input\n\n\t-- If the first argument is a table, we can assumed it's going to specify which\n\t-- keys to fill out. Otherwise we'll fill in specified arguments in order.\n\tif (istable(arguments[1])) then\n\t\tinput = arguments[1]\n\telse\n\t\tinput = arguments\n\t\tbArray = true\n\tend\n\n\tlocal i = 0\n\tlocal result = format:gsub(\"{(%w-)}\", function(word)\n\t\ti = i + 1\n\t\treturn tostring((bArray and input[i] or input[word]) or word)\n\tend)\n\n\treturn result\nend\n\ndo\n\tlocal upperMap = {\n\t\t[\"ooc\"] = true,\n\t\t[\"looc\"] = true,\n\t\t[\"afk\"] = true,\n\t\t[\"url\"] = true\n\t}\n\t--- Returns a string that is the given input with spaces in between each CamelCase word. This function will ignore any words\n\t-- that do not begin with a capital letter. The words `ooc`, `looc`, `afk`, and `url` will be automatically transformed\n\t-- into uppercase text. This will not capitalize non-ASCII letters due to limitations with Lua's pattern matching.\n\t-- @realm shared\n\t-- @string input String to expand\n\t-- @bool[opt=false] bNoUpperFirst Whether or not to avoid capitalizing the first character. This is useful for lowerCamelCase\n\t-- @treturn string Expanded CamelCase string\n\t-- @usage print(ix.util.ExpandCamelCase(\"HelloWorld\"))\n\t-- > Hello World\n\tfunction ix.util.ExpandCamelCase(input, bNoUpperFirst)\n\t\tinput = bNoUpperFirst and input or input:utf8sub(1, 1):utf8upper() .. input:utf8sub(2)\n\n\t\t-- extra parentheses to select first return value of gsub\n\t\treturn string.TrimRight((input:gsub(\"%u%l+\", function(word)\n\t\t\tif (upperMap[word:utf8lower()]) then\n\t\t\t\tword = word:utf8upper()\n\t\t\tend\n\n\t\t\treturn word .. \" \"\n\t\tend)))\n\tend\nend\n\nfunction ix.util.GridVector(vec, gridSize)\n\tif (gridSize <= 0) then\n\t\tgridSize = 1\n\tend\n\n\tfor i = 1, 3 do\n\t\tvec[i] = vec[i] / gridSize\n\t\tvec[i] = math.Round(vec[i])\n\t\tvec[i] = vec[i] * gridSize\n\tend\n\n\treturn vec\nend\n\ndo\n\tlocal i\n\tlocal value\n\tlocal character\n\n\tlocal function iterator(table)\n\t\trepeat\n\t\t\ti = i + 1\n\t\t\tvalue = table[i]\n\t\t\tcharacter = value and value:GetCharacter()\n\t\tuntil character or value == nil\n\n\t\treturn value, character\n\tend\n\n\t--- Returns an iterator for characters. The resulting key/values will be a player and their corresponding characters. This\n\t-- iterator skips over any players that do not have a valid character loaded.\n\t-- @realm shared\n\t-- @treturn Iterator\n\t-- @usage for client, character in ix.util.GetCharacters() do\n\t-- \tprint(client, character)\n\t-- end\n\t-- > Player [1][Bot01]    character[1]\n\t-- > Player [2][Bot02]    character[2]\n\t-- -- etc.\n\tfunction ix.util.GetCharacters()\n\t\ti = 0\n\t\treturn iterator, player.GetAll()\n\tend\nend\n\nif (CLIENT) then\n\tlocal blur = ix.util.GetMaterial(\"pp/blurscreen\")\n\tlocal surface = surface\n\n\t--- Blurs the content underneath the given panel. This will fall back to a simple darkened rectangle if the player has\n\t-- blurring disabled.\n\t-- @realm client\n\t-- @tparam panel panel Panel to draw the blur for\n\t-- @number[opt=5] amount Intensity of the blur. This should be kept between 0 and 10 for performance reasons\n\t-- @number[opt=0.2] passes Quality of the blur. This should be kept as default\n\t-- @number[opt=255] alpha Opacity of the blur\n\t-- @usage function PANEL:Paint(width, height)\n\t-- \tix.util.DrawBlur(self)\n\t-- end\n\tfunction ix.util.DrawBlur(panel, amount, passes, alpha)\n\t\tamount = amount or 5\n\n\t\tif (ix.option.Get(\"cheapBlur\", false)) then\n\t\t\tsurface.SetDrawColor(50, 50, 50, alpha or (amount * 20))\n\t\t\tsurface.DrawRect(0, 0, panel:GetWide(), panel:GetTall())\n\t\telse\n\t\t\tsurface.SetMaterial(blur)\n\t\t\tsurface.SetDrawColor(255, 255, 255, alpha or 255)\n\n\t\t\tlocal x, y = panel:LocalToScreen(0, 0)\n\n\t\t\tfor i = -(passes or 0.2), 1, 0.2 do\n\t\t\t\t-- Do things to the blur material to make it blurry.\n\t\t\t\tblur:SetFloat(\"$blur\", i * amount)\n\t\t\t\tblur:Recompute()\n\n\t\t\t\t-- Draw the blur material over the screen.\n\t\t\t\trender.UpdateScreenEffectTexture()\n\t\t\t\tsurface.DrawTexturedRect(x * -1, y * -1, ScrW(), ScrH())\n\t\t\tend\n\t\tend\n\tend\n\n\t--- Draws a blurred rectangle with the given position and bounds. This shouldn't be used for panels, see `ix.util.DrawBlur`\n\t-- instead.\n\t-- @realm client\n\t-- @number x X-position of the rectangle\n\t-- @number y Y-position of the rectangle\n\t-- @number width Width of the rectangle\n\t-- @number height Height of the rectangle\n\t-- @number[opt=5] amount Intensity of the blur. This should be kept between 0 and 10 for performance reasons\n\t-- @number[opt=0.2] passes Quality of the blur. This should be kept as default\n\t-- @number[opt=255] alpha Opacity of the blur\n\t-- @usage hook.Add(\"HUDPaint\", \"MyHUDPaint\", function()\n\t-- \tix.util.DrawBlurAt(0, 0, ScrW(), ScrH())\n\t-- end)\n\tfunction ix.util.DrawBlurAt(x, y, width, height, amount, passes, alpha)\n\t\tamount = amount or 5\n\n\t\tif (ix.option.Get(\"cheapBlur\", false)) then\n\t\t\tsurface.SetDrawColor(30, 30, 30, amount * 20)\n\t\t\tsurface.DrawRect(x, y, width, height)\n\t\telse\n\t\t\tsurface.SetMaterial(blur)\n\t\t\tsurface.SetDrawColor(255, 255, 255, alpha or 255)\n\n\t\t\tlocal scrW, scrH = ScrW(), ScrH()\n\t\t\tlocal x2, y2 = x / scrW, y / scrH\n\t\t\tlocal w2, h2 = (x + width) / scrW, (y + height) / scrH\n\n\t\t\tfor i = -(passes or 0.2), 1, 0.2 do\n\t\t\t\tblur:SetFloat(\"$blur\", i * amount)\n\t\t\t\tblur:Recompute()\n\n\t\t\t\trender.UpdateScreenEffectTexture()\n\t\t\t\tsurface.DrawTexturedRectUV(x, y, width, height, x2, y2, w2, h2)\n\t\t\tend\n\t\tend\n\tend\n\n\t--- Pushes a 3D2D blur to be rendered in the world. The draw function will be called next frame in the\n\t-- `PostDrawOpaqueRenderables` hook.\n\t-- @realm client\n\t-- @func drawFunc Function to call when it needs to be drawn\n\tfunction ix.util.PushBlur(drawFunc)\n\t\tix.blurRenderQueue[#ix.blurRenderQueue + 1] = drawFunc\n\tend\n\n\t--- Draws some text with a shadow.\n\t-- @realm client\n\t-- @string text Text to draw\n\t-- @number x X-position of the text\n\t-- @number y Y-position of the text\n\t-- @color color Color of the text to draw\n\t-- @number[opt=TEXT_ALIGN_LEFT] alignX Horizontal alignment of the text, using one of the `TEXT_ALIGN_*` constants\n\t-- @number[opt=TEXT_ALIGN_LEFT] alignY Vertical alignment of the text, using one of the `TEXT_ALIGN_*` constants\n\t-- @string[opt=\"ixGenericFont\"] font Font to use for the text\n\t-- @number[opt=color.a * 0.575] alpha Alpha of the shadow\n\tfunction ix.util.DrawText(text, x, y, color, alignX, alignY, font, alpha)\n\t\tcolor = color or color_white\n\n\t\treturn draw.TextShadow({\n\t\t\ttext = text,\n\t\t\tfont = font or \"ixGenericFont\",\n\t\t\tpos = {x, y},\n\t\t\tcolor = color,\n\t\t\txalign = alignX or TEXT_ALIGN_LEFT,\n\t\t\tyalign = alignY or TEXT_ALIGN_LEFT\n\t\t}, 1, alpha or (color.a * 0.575))\n\tend\n\n\t--- Wraps text so it does not pass a certain width. This function will try and break lines between words if it can,\n\t-- otherwise it will break a word if it's too long.\n\t-- @realm client\n\t-- @string text Text to wrap\n\t-- @number maxWidth Maximum allowed width in pixels\n\t-- @string[opt=\"ixChatFont\"] font Font to use for the text\n\tfunction ix.util.WrapText(text, maxWidth, font)\n\t\tfont = font or \"ixChatFont\"\n\t\tsurface.SetFont(font)\n\n\t\tlocal words = string.Explode(\"%s\", text, true)\n\t\tlocal lines = {}\n\t\tlocal line = \"\"\n\t\tlocal lineWidth = 0 -- luacheck: ignore 231\n\n\t\t-- we don't need to calculate wrapping if we're under the max width\n\t\tif (surface.GetTextSize(text) <= maxWidth) then\n\t\t\treturn {text}\n\t\tend\n\n\t\tfor i = 1, #words do\n\t\t\tlocal word = words[i]\n\t\t\tlocal wordWidth = surface.GetTextSize(word)\n\n\t\t\t-- this word is very long so we have to split it by character\n\t\t\tif (wordWidth > maxWidth) then\n\t\t\t\tlocal newWidth\n\n\t\t\t\tfor i2 = 1, word:utf8len() do\n\t\t\t\t\tlocal character = word[i2]\n\t\t\t\t\tnewWidth = surface.GetTextSize(line .. character)\n\n\t\t\t\t\t-- if current line + next character is too wide, we'll shove the next character onto the next line\n\t\t\t\t\tif (newWidth > maxWidth) then\n\t\t\t\t\t\tlines[#lines + 1] = line\n\t\t\t\t\t\tline = \"\"\n\t\t\t\t\tend\n\n\t\t\t\t\tline = line .. character\n\t\t\t\tend\n\n\t\t\t\tlineWidth = newWidth\n\t\t\t\tcontinue\n\t\t\tend\n\n\t\t\tlocal space = (i == 1) and \"\" or \" \"\n\t\t\tlocal newLine = line .. space .. word\n\t\t\tlocal newWidth = surface.GetTextSize(newLine)\n\n\t\t\tif (newWidth > maxWidth) then\n\t\t\t\t-- adding this word will bring us over the max width\n\t\t\t\tlines[#lines + 1] = line\n\n\t\t\t\tline = word\n\t\t\t\tlineWidth = wordWidth\n\t\t\telse\n\t\t\t\t-- otherwise we tack on the new word and continue\n\t\t\t\tline = newLine\n\t\t\t\tlineWidth = newWidth\n\t\t\tend\n\t\tend\n\n\t\tif (line != \"\") then\n\t\t\tlines[#lines + 1] = line\n\t\tend\n\n\t\treturn lines\n\tend\n\n\tlocal cos, sin, abs, rad1, log, pow = math.cos, math.sin, math.abs, math.rad, math.log, math.pow\n\n\t-- arc drawing functions\n\t-- by bobbleheadbob\n\t-- https://facepunch.com/showthread.php?t=1558060\n\tfunction ix.util.DrawArc(cx, cy, radius, thickness, startang, endang, roughness, color)\n\t\tsurface.SetDrawColor(color)\n\t\tix.util.DrawPrecachedArc(ix.util.PrecacheArc(cx, cy, radius, thickness, startang, endang, roughness))\n\tend\n\n\tfunction ix.util.DrawPrecachedArc(arc) -- Draw a premade arc.\n\t\tfor _, v in ipairs(arc) do\n\t\t\tsurface.DrawPoly(v)\n\t\tend\n\tend\n\n\tfunction ix.util.PrecacheArc(cx, cy, radius, thickness, startang, endang, roughness)\n\t\tlocal quadarc = {}\n\n\t\t-- Correct start/end ang\n\t\tstartang = startang or 0\n\t\tendang = endang or 0\n\n\t\t-- Define step\n\t\t-- roughness = roughness or 1\n\t\tlocal diff = abs(startang - endang)\n\t\tlocal smoothness = log(diff, 2) / 2\n\t\tlocal step = diff / (pow(2, smoothness))\n\n\t\tif startang > endang then\n\t\t\tstep = abs(step) * -1\n\t\tend\n\n\t\t-- Create the inner circle's points.\n\t\tlocal inner = {}\n\t\tlocal outer = {}\n\t\tlocal ct = 1\n\t\tlocal r = radius - thickness\n\n\t\tfor deg = startang, endang, step do\n\t\t\tlocal rad = rad1(deg)\n\t\t\tlocal cosrad, sinrad = cos(rad), sin(rad) --calculate sin, cos\n\n\t\t\tlocal ox, oy = cx + (cosrad * r), cy + (-sinrad * r) --apply to inner distance\n\t\t\tinner[ct] = {\n\t\t\t\tx = ox,\n\t\t\t\ty = oy,\n\t\t\t\tu = (ox - cx) / radius + .5,\n\t\t\t\tv = (oy - cy) / radius + .5\n\t\t\t}\n\n\t\t\tlocal ox2, oy2 = cx + (cosrad * radius), cy + (-sinrad * radius) --apply to outer distance\n\t\t\touter[ct] = {\n\t\t\t\tx = ox2,\n\t\t\t\ty = oy2,\n\t\t\t\tu = (ox2 - cx) / radius + .5,\n\t\t\t\tv = (oy2 - cy) / radius + .5\n\t\t\t}\n\n\t\t\tct = ct + 1\n\t\tend\n\n\t\t-- QUAD the points.\n\t\tfor tri = 1, ct do\n\t\t\tlocal p1, p2, p3, p4\n\t\t\tlocal t = tri + 1\n\t\t\tp1 = outer[tri]\n\t\t\tp2 = outer[t]\n\t\t\tp3 = inner[t]\n\t\t\tp4 = inner[tri]\n\n\t\t\tquadarc[tri] = {p1, p2, p3, p4}\n\t\tend\n\n\t\t-- Return a table of triangles to draw.\n\t\treturn quadarc\n\tend\n\n\t--- Resets all stencil values to known good (i.e defaults)\n\t-- @realm client\n\tfunction ix.util.ResetStencilValues()\n\t\trender.SetStencilWriteMask(0xFF)\n\t\trender.SetStencilTestMask(0xFF)\n\t\trender.SetStencilReferenceValue(0)\n\t\trender.SetStencilCompareFunction(STENCIL_ALWAYS)\n\t\trender.SetStencilPassOperation(STENCIL_KEEP)\n\t\trender.SetStencilFailOperation(STENCIL_KEEP)\n\t\trender.SetStencilZFailOperation(STENCIL_KEEP)\n\t\trender.ClearStencil()\n\tend\n\n\t-- luacheck: globals derma\n\t-- Alternative to SkinHook that allows you to pass more arguments to skin methods\n\tfunction derma.SkinFunc(name, panel, a, b, c, d, e, f, g)\n\t\tlocal skin = (ispanel(panel) and IsValid(panel)) and panel:GetSkin() or derma.GetDefaultSkin()\n\n\t\tif (!skin) then\n\t\t\treturn\n\t\tend\n\n\t\tlocal func = skin[name]\n\n\t\tif (!func) then\n\t\t\treturn\n\t\tend\n\n\t\treturn func(skin, panel, a, b, c, d, e, f, g)\n\tend\n\n\t-- Alternative to Color that retrieves from the SKIN.Colours table\n\tfunction derma.GetColor(name, panel, default)\n\t\tdefault = default or ix.config.Get(\"color\")\n\n\t\tlocal skin = panel:GetSkin()\n\n\t\tif (!skin) then\n\t\t\treturn default\n\t\tend\n\n\t\treturn skin.Colours[name] or default\n\tend\n\n\n\thook.Add(\"OnScreenSizeChanged\", \"ix.OnScreenSizeChanged\", function(oldWidth, oldHeight)\n\t\thook.Run(\"ScreenResolutionChanged\", oldWidth, oldHeight)\n\tend)\nend\n\n-- Vector extension, courtesy of code_gs\ndo\n\tlocal VECTOR = FindMetaTable(\"Vector\")\n\tlocal CrossProduct = VECTOR.Cross\n\tlocal right = Vector(0, -1, 0)\n\n\tfunction VECTOR:Right(vUp)\n\t\tif (self[1] == 0 and self[2] == 0) then\n\t\t\treturn right\n\t\tend\n\n\t\tif (vUp == nil) then\n\t\t\tvUp = vector_up\n\t\tend\n\n\t\tlocal vRet = CrossProduct(self, vUp)\n\t\tvRet:Normalize()\n\n\t\treturn vRet\n\tend\n\n\tfunction VECTOR:Up(vUp)\n\t\tif (self[1] == 0 and self[2] == 0) then return Vector(-self[3], 0, 0) end\n\n\t\tif (vUp == nil) then\n\t\t\tvUp = vector_up\n\t\tend\n\n\t\tlocal vRet = CrossProduct(self, vUp)\n\t\tvRet = CrossProduct(vRet, self)\n\t\tvRet:Normalize()\n\n\t\treturn vRet\n\tend\nend\n\n-- luacheck: globals FCAP_IMPULSE_USE FCAP_CONTINUOUS_USE FCAP_ONOFF_USE\n-- luacheck: globals FCAP_DIRECTIONAL_USE FCAP_USE_ONGROUND FCAP_USE_IN_RADIUS\nFCAP_IMPULSE_USE = 0x00000010\nFCAP_CONTINUOUS_USE = 0x00000020\nFCAP_ONOFF_USE = 0x00000040\nFCAP_DIRECTIONAL_USE = 0x00000080\nFCAP_USE_ONGROUND = 0x00000100\nFCAP_USE_IN_RADIUS = 0x00000200\n\nfunction ix.util.IsUseableEntity(entity, requiredCaps)\n\tif (IsValid(entity)) then\n\t\tlocal caps = entity:ObjectCaps()\n\n\t\tif (bit.band(caps, bit.bor(FCAP_IMPULSE_USE, FCAP_CONTINUOUS_USE, FCAP_ONOFF_USE, FCAP_DIRECTIONAL_USE))) then\n\t\t\tif (bit.band(caps, requiredCaps) == requiredCaps) then\n\t\t\t\treturn true\n\t\t\tend\n\t\tend\n\tend\nend\n\ndo\n\tlocal function IntervalDistance(x, x0, x1)\n\t\t-- swap so x0 < x1\n\t\tif (x0 > x1) then\n\t\t\tlocal tmp = x0\n\n\t\t\tx0 = x1\n\t\t\tx1 = tmp\n\t\tend\n\n\t\tif (x < x0) then\n\t\t\treturn x0-x\n\t\telseif (x > x1) then\n\t\t\treturn x - x1\n\t\tend\n\n\t\treturn 0\n\tend\n\n\tlocal NUM_TANGENTS = 8\n\tlocal tangents = {0, 1, 0.57735026919, 0.3639702342, 0.267949192431, 0.1763269807, -0.1763269807, -0.267949192431}\n\tlocal traceMin = Vector(-16, -16, -16)\n\tlocal traceMax = Vector(16, 16, 16)\n\n\tfunction ix.util.FindUseEntity(player, origin, forward)\n\t\tlocal tr\n\t\tlocal up = forward:Up()\n\t\t-- Search for objects in a sphere (tests for entities that are not solid, yet still useable)\n\t\tlocal searchCenter = origin\n\n\t\t-- NOTE: Some debris objects are useable too, so hit those as well\n\t\t-- A button, etc. can be made out of clip brushes, make sure it's +useable via a traceline, too.\n\t\tlocal useableContents = bit.bor(MASK_SOLID, CONTENTS_DEBRIS, CONTENTS_PLAYERCLIP)\n\n\t\t-- UNDONE: Might be faster to just fold this range into the sphere query\n\t\tlocal pObject\n\n\t\tlocal nearestDist = 1e37\n\t\t-- try the hit entity if there is one, or the ground entity if there isn't.\n\t\tlocal pNearest = NULL\n\n\t\tfor i = 1, NUM_TANGENTS do\n\t\t\tif (i == 0) then\n\t\t\t\ttr = util.TraceLine({\n\t\t\t\t\tstart = searchCenter,\n\t\t\t\t\tendpos = searchCenter + forward * 1024,\n\t\t\t\t\tmask = useableContents,\n\t\t\t\t\tfilter = player\n\t\t\t\t})\n\n\t\t\t\ttr.EndPos = searchCenter + forward * 1024\n\t\t\telse\n\t\t\t\tlocal down = forward - tangents[i] * up\n\t\t\t\tdown:Normalize()\n\n\t\t\t\ttr = util.TraceHull({\n\t\t\t\t\tstart = searchCenter,\n\t\t\t\t\tendpos = searchCenter + down * 72,\n\t\t\t\t\tmins = traceMin,\n\t\t\t\t\tmaxs = traceMax,\n\t\t\t\t\tmask = useableContents,\n\t\t\t\t\tfilter = player\n\t\t\t\t})\n\n\t\t\t\ttr.EndPos = searchCenter + down * 72\n\t\t\tend\n\n\t\t\tpObject = tr.Entity\n\n\t\t\tlocal bUsable = ix.util.IsUseableEntity(pObject, 0)\n\n\t\t\twhile (IsValid(pObject) and !bUsable and pObject:GetMoveParent()) do\n\t\t\t\tpObject = pObject:GetMoveParent()\n\t\t\t\tbUsable = ix.util.IsUseableEntity(pObject, 0)\n\t\t\tend\n\n\t\t\tif (bUsable) then\n\t\t\t\tlocal delta = tr.EndPos - tr.StartPos\n\t\t\t\tlocal centerZ = origin.z - player:WorldSpaceCenter().z\n\t\t\t\tdelta.z = IntervalDistance(tr.EndPos.z, centerZ - player:OBBMins().z, centerZ + player:OBBMaxs().z)\n\t\t\t\tlocal dist = delta:Length()\n\n\t\t\t\tif (dist < 80) then\n\t\t\t\t\tpNearest = pObject\n\n\t\t\t\t\t-- if this is directly under the cursor just return it now\n\t\t\t\t\tif (i == 0) then\n\t\t\t\t\t\treturn pObject\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\n\t\t-- check ground entity first\n\t\t-- if you've got a useable ground entity, then shrink the cone of this search to 45 degrees\n\t\t-- otherwise, search out in a 90 degree cone (hemisphere)\n\t\tif (IsValid(player:GetGroundEntity()) and ix.util.IsUseableEntity(player:GetGroundEntity(), FCAP_USE_ONGROUND)) then\n\t\t\tpNearest = player:GetGroundEntity()\n\t\tend\n\n\t\tif (IsValid(pNearest)) then\n\t\t\t-- estimate nearest object by distance from the view vector\n\t\t\tlocal point = pNearest:NearestPoint(searchCenter)\n\t\t\tnearestDist = util.DistanceToLine(searchCenter, forward, point)\n\t\tend\n\n\t\tfor _, v in ipairs(ents.FindInSphere(searchCenter, 80)) do\n\t\t\tif (!ix.util.IsUseableEntity(v, FCAP_USE_IN_RADIUS)) then\n\t\t\t\tcontinue\n\t\t\tend\n\n\t\t\t-- see if it's more roughly in front of the player than previous guess\n\t\t\tlocal point = v:NearestPoint(searchCenter)\n\n\t\t\tlocal dir = point - searchCenter\n\t\t\tdir:Normalize()\n\t\t\tlocal dot = dir:Dot(forward)\n\n\t\t\t-- Need to be looking at the object more or less\n\t\t\tif (dot < 0.8) then\n\t\t\t\tcontinue\n\t\t\tend\n\n\t\t\tlocal dist = util.DistanceToLine(searchCenter, forward, point)\n\n\t\t\tif (dist < nearestDist) then\n\t\t\t\t-- Since this has purely been a radius search to this point, we now\n\t\t\t\t-- make sure the object isn't behind glass or a grate.\n\t\t\t\tlocal trCheckOccluded = {}\n\n\t\t\t\tutil.TraceLine({\n\t\t\t\t\tstart = searchCenter,\n\t\t\t\t\tendpos = point,\n\t\t\t\t\tmask = useableContents,\n\t\t\t\t\tfilter = player,\n\t\t\t\t\toutput = trCheckOccluded\n\t\t\t\t})\n\n\t\t\t\tif (trCheckOccluded.fraction == 1.0 or trCheckOccluded.Entity == v) then\n\t\t\t\t\tpNearest = v\n\t\t\t\t\tnearestDist = dist\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\n\t\treturn pNearest\n\tend\nend\n\nALWAYS_RAISED = {}\nALWAYS_RAISED[\"weapon_physgun\"] = true\nALWAYS_RAISED[\"gmod_tool\"] = true\nALWAYS_RAISED[\"ix_poshelper\"] = true\n\nfunction ix.util.FindEmptySpace(entity, filter, spacing, size, height, tolerance)\n\tspacing = spacing or 32\n\tsize = size or 3\n\theight = height or 36\n\ttolerance = tolerance or 5\n\n\tlocal position = entity:GetPos()\n\tlocal mins, maxs = Vector(-spacing * 0.5, -spacing * 0.5, 0), Vector(spacing * 0.5, spacing * 0.5, height)\n\tlocal output = {}\n\n\tfor x = -size, size do\n\t\tfor y = -size, size do\n\t\t\tlocal origin = position + Vector(x * spacing, y * spacing, 0)\n\n\t\t\tlocal data = {}\n\t\t\t\tdata.start = origin + mins + Vector(0, 0, tolerance)\n\t\t\t\tdata.endpos = origin + maxs\n\t\t\t\tdata.filter = filter or entity\n\t\t\tlocal trace = util.TraceLine(data)\n\n\t\t\tdata.start = origin + Vector(-maxs.x, -maxs.y, tolerance)\n\t\t\tdata.endpos = origin + Vector(mins.x, mins.y, height)\n\n\t\t\tlocal trace2 = util.TraceLine(data)\n\n\t\t\tif (trace.StartSolid or trace.Hit or trace2.StartSolid or trace2.Hit or !util.IsInWorld(origin)) then\n\t\t\t\tcontinue\n\t\t\tend\n\n\t\t\toutput[#output + 1] = origin\n\t\tend\n\tend\n\n\ttable.sort(output, function(a, b)\n\t\treturn a:DistToSqr(position) < b:DistToSqr(position)\n\tend)\n\n\treturn output\nend\n\n-- Time related stuff.\ndo\n\t--- Gets the current time in the UTC time-zone.\n\t-- @realm shared\n\t-- @treturn number Current time in UTC\n\tfunction ix.util.GetUTCTime()\n\t\tlocal date = os.date(\"!*t\")\n\t\tlocal localDate = os.date(\"*t\")\n\t\tlocalDate.isdst = false\n\n\t\treturn os.difftime(os.time(date), os.time(localDate))\n\tend\n\n\t-- Setup for time strings.\n\tlocal TIME_UNITS = {}\n\tTIME_UNITS[\"s\"] = 1\t\t\t\t\t\t-- Seconds\n\tTIME_UNITS[\"m\"] = 60\t\t\t\t\t-- Minutes\n\tTIME_UNITS[\"h\"] = 3600\t\t\t\t\t-- Hours\n\tTIME_UNITS[\"d\"] = TIME_UNITS[\"h\"] * 24\t-- Days\n\tTIME_UNITS[\"w\"] = TIME_UNITS[\"d\"] * 7\t-- Weeks\n\tTIME_UNITS[\"mo\"] = TIME_UNITS[\"d\"] * 30\t-- Months\n\tTIME_UNITS[\"y\"] = TIME_UNITS[\"d\"] * 365\t-- Years\n\n\t--- Gets the amount of seconds from a given formatted string. If no time units are specified, it is assumed minutes.\n\t-- The valid values are as follows:\n\t--\n\t-- - `s` - Seconds\n\t-- - `m` - Minutes\n\t-- - `h` - Hours\n\t-- - `d` - Days\n\t-- - `w` - Weeks\n\t-- - `mo` - Months\n\t-- - `y` - Years\n\t-- @realm shared\n\t-- @string text Text to interpret a length of time from\n\t-- @treturn[1] number Amount of seconds from the length interpreted from the given string\n\t-- @treturn[2] 0 If the given string does not have a valid time\n\t-- @usage print(ix.util.GetStringTime(\"5y2d7w\"))\n\t-- > 162086400 -- 5 years, 2 days, 7 weeks\n\tfunction ix.util.GetStringTime(text)\n\t\tlocal minutes = tonumber(text)\n\n\t\tif (minutes) then\n\t\t\treturn math.abs(minutes * 60)\n\t\tend\n\n\t\tlocal time = 0\n\n\t\tfor amount, unit in text:lower():gmatch(\"(%d+)(%a+)\") do\n\t\t\tamount = tonumber(amount)\n\n\t\t\tif (amount and TIME_UNITS[unit]) then\n\t\t\t\ttime = time + math.abs(amount * TIME_UNITS[unit])\n\t\t\tend\n\t\tend\n\n\t\treturn time\n\tend\nend\n\n--[[\n\tCredit to TFA for figuring this mess out.\n\tOriginal: https://steamcommunity.com/sharedfiles/filedetails/?id=903541818\n]]\n\nif (system.IsLinux()) then\n\tlocal cache = {}\n\n\t-- Helper Functions\n\tlocal function GetSoundPath(path, gamedir)\n\t\tif (!gamedir) then\n\t\t\tpath = \"sound/\" .. path\n\t\t\tgamedir = \"GAME\"\n\t\tend\n\n\t\treturn path, gamedir\n\tend\n\n\tlocal function f_IsWAV(f)\n\t\tf:Seek(8)\n\n\t\treturn f:Read(4) == \"WAVE\"\n\tend\n\n\t-- WAV functions\n\tlocal function f_SampleDepth(f)\n\t\tf:Seek(34)\n\t\tlocal bytes = {}\n\n\t\tfor i = 1, 2 do\n\t\t\tbytes[i] = f:ReadByte(1)\n\t\tend\n\n\t\tlocal num = bit.lshift(bytes[2], 8) + bit.lshift(bytes[1], 0)\n\n\t\treturn num\n\tend\n\n\tlocal function f_SampleRate(f)\n\t\tf:Seek(24)\n\t\tlocal bytes = {}\n\n\t\tfor i = 1, 4 do\n\t\t\tbytes[i] = f:ReadByte(1)\n\t\tend\n\n\t\tlocal num = bit.lshift(bytes[4], 24) + bit.lshift(bytes[3], 16) + bit.lshift(bytes[2], 8) + bit.lshift(bytes[1], 0)\n\n\t\treturn num\n\tend\n\n\tlocal function f_Channels(f)\n\t\tf:Seek(22)\n\t\tlocal bytes = {}\n\n\t\tfor i = 1, 2 do\n\t\t\tbytes[i] = f:ReadByte(1)\n\t\tend\n\n\t\tlocal num = bit.lshift(bytes[2], 8) + bit.lshift(bytes[1], 0)\n\n\t\treturn num\n\tend\n\n\tlocal function f_Duration(f)\n\t\treturn (f:Size() - 44) / (f_SampleDepth(f) / 8 * f_SampleRate(f) * f_Channels(f))\n\tend\n\n\tixSoundDuration = ixSoundDuration or SoundDuration -- luacheck: globals ixSoundDuration\n\n\tfunction SoundDuration(str) -- luacheck: globals SoundDuration\n\t\tlocal path, gamedir = GetSoundPath(str)\n\t\tlocal f = file.Open(path, \"rb\", gamedir)\n\n\t\tif (!f) then return 0 end --Return nil on invalid files\n\n\t\tlocal ret\n\n\t\tif (cache[str]) then\n\t\t\tret = cache[str]\n\t\telseif (f_IsWAV(f)) then\n\t\t\tret = f_Duration(f)\n\t\telse\n\t\t\tret = ixSoundDuration(str)\n\t\tend\n\n\t\tf:Close()\n\n\t\treturn ret\n\tend\nend\n\nlocal ADJUST_SOUND = SoundDuration(\"npc/metropolice/pain1.wav\") > 0 and \"\" or \"../../hl2/sound/\"\n\n--- Emits sounds one after the other from an entity.\n-- @realm shared\n-- @entity entity Entity to play sounds from\n-- @tab sounds Sound paths to play\n-- @number delay[opt=0] How long to wait before starting to play the sounds\n-- @number spacing[opt=0.1] How long to wait between playing each sound\n-- @number volume[opt=75] The sound level of each sound\n-- @number pitch[opt=100] Pitch percentage of each sound\n-- @treturn number How long the entire sequence of sounds will take to play\nfunction ix.util.EmitQueuedSounds(entity, sounds, delay, spacing, volume, pitch)\n\t-- Let there be a delay before any sound is played.\n\tdelay = delay or 0\n\tspacing = spacing or 0.1\n\n\t-- Loop through all of the sounds.\n\tfor _, v in ipairs(sounds) do\n\t\tlocal postSet, preSet = 0, 0\n\n\t\t-- Determine if this sound has special time offsets.\n\t\tif (istable(v)) then\n\t\t\tpostSet, preSet = v[2] or 0, v[3] or 0\n\t\t\tv = v[1]\n\t\tend\n\n\t\t-- Get the length of the sound.\n\t\tlocal length = SoundDuration(ADJUST_SOUND..v)\n\t\t-- If the sound has a pause before it is played, add it here.\n\t\tdelay = delay + preSet\n\n\t\t-- Have the sound play in the future.\n\t\ttimer.Simple(delay, function()\n\t\t\t-- Check if the entity still exists and play the sound.\n\t\t\tif (IsValid(entity)) then\n\t\t\t\tentity:EmitSound(v, volume, pitch)\n\t\t\tend\n\t\tend)\n\n\t\t-- Add the delay for the next sound.\n\t\tdelay = delay + length + postSet + spacing\n\tend\n\n\t-- Return how long it took for the whole thing.\n\treturn delay\nend\n\n--- Merges the contents of the second table with the content in the first one. The destination table will be modified.\n--- If element is table but not metatable object, value's elements will be changed only.\n-- @realm shared\n-- @tab destination The table you want the source table to merge with\n-- @tab source The table you want to merge with the destination table\n-- @return table\nfunction ix.util.MetatableSafeTableMerge(destination, source)\n\tfor k, v in pairs(source) do\n\t\tif (istable(v) and istable(destination[k]) and getmetatable(v) == nil) then\n\t\t\t-- don't overwrite one table with another\n\t\t\t-- instead merge them recurisvely\n\t\t\tix.util.MetatableSafeTableMerge(destination[k], v);\n\t\telse\n\t\t\tdestination[ k ] = v;\n\t\tend\n\tend\n\treturn destination;\nend\n\nix.util.Include(\"helix/gamemode/core/meta/sh_entity.lua\")\nix.util.Include(\"helix/gamemode/core/meta/sh_player.lua\")\n"
  },
  {
    "path": "gamemode/init.lua",
    "content": "\n-- Include Helix content.\nresource.AddWorkshop(\"1267236756\")\n\n-- Include features from the Sandbox gamemode.\nDeriveGamemode(\"sandbox\")\n-- Define a global shared table to store Helix information.\nix = ix or {util = {}, meta = {}}\n\n-- Send the following files to players.\nAddCSLuaFile(\"cl_init.lua\")\nAddCSLuaFile(\"core/sh_util.lua\")\nAddCSLuaFile(\"core/sh_data.lua\")\nAddCSLuaFile(\"shared.lua\")\n\n-- Include utility functions, data storage functions, and then shared.lua\ninclude(\"core/sh_util.lua\")\ninclude(\"core/sh_data.lua\")\ninclude(\"shared.lua\")\n\n-- Resources that are required for players to download are here.\nresource.AddFile(\"materials/helix/gui/vignette.png\")\nresource.AddFile(\"resource/fonts/fontello.ttf\")\nresource.AddFile(\"sound/helix/intro.mp3\")\nresource.AddFile(\"sound/helix/ui/press.wav\")\nresource.AddFile(\"sound/helix/ui/rollover.wav\")\nresource.AddFile(\"sound/helix/ui/whoosh1.wav\")\nresource.AddFile(\"sound/helix/ui/whoosh2.wav\")\nresource.AddFile(\"sound/helix/ui/whoosh3.wav\")\nresource.AddFile(\"sound/helix/ui/whoosh4.wav\")\nresource.AddFile(\"sound/helix/ui/whoosh5.wav\")\nresource.AddFile(\"sound/helix/ui/whoosh6.wav\")\n\ncvars.AddChangeCallback(\"sbox_persist\", function(name, old, new)\n\t-- A timer in case someone tries to rapily change the convar, such as addons with \"live typing\" or whatever\n\ttimer.Create(\"sbox_persist_change_timer\", 1, 1, function()\n\t\thook.Run(\"PersistenceSave\", old)\n\n\t\tif (new == \"\") then\n\t\t\treturn\n\t\tend\n\n\t\thook.Run(\"PersistenceLoad\", new)\n\tend)\nend, \"sbox_persist_load\")\n"
  },
  {
    "path": "gamemode/items/ammo/sh_357ammo.txt",
    "content": "ITEM.name = \".357 Ammo\"\nITEM.model = \"models/items/357ammo.mdl\"\nITEM.ammo = \"357\" -- type of the ammo\nITEM.ammoAmount = 12 -- amount of the ammo\nITEM.description = \"A Box that contains %s of .357 Ammo\"\nITEM.price = 10\n"
  },
  {
    "path": "gamemode/items/ammo/sh_ar2ammo.txt",
    "content": "ITEM.name = \"AR2 Cartridge\"\nITEM.model = \"models/Items/combine_rifle_cartridge01.mdl\"\nITEM.ammo = \"ar2\" -- type of the ammo\nITEM.ammoAmount = 30 -- amount of the ammo\nITEM.description = \"A Cartridge that contains %s of AR2 Ammo\"\n"
  },
  {
    "path": "gamemode/items/ammo/sh_crossbowammo.txt",
    "content": "ITEM.name = \"Crossbow Bolts\"\nITEM.model = \"models/Items/CrossbowRounds.mdl\"\nITEM.ammo = \"XBowRounds\" -- type of the ammo\nITEM.ammoAmount = 5 -- amount of the ammo\nITEM.description = \"A Bundle of %s Crossbow Bolts\"\n"
  },
  {
    "path": "gamemode/items/ammo/sh_pistolammo.txt",
    "content": "ITEM.name = \"Pistol Ammo\"\nITEM.model = \"models/items/357ammo.mdl\"\nITEM.ammo = \"pistol\" -- type of the ammo\nITEM.ammoAmount = 30 -- amount of the ammo\nITEM.description = \"A Box that contains %s of Pistol Ammo\"\n"
  },
  {
    "path": "gamemode/items/ammo/sh_rocketammo.txt",
    "content": "ITEM.name = \"A Rocket\"\nITEM.model = \"models/weapons/w_missile_closed.mdl\"\nITEM.ammo = \"rpg_round\" -- type of the ammo\nITEM.ammoAmount = 1 -- amount of the ammo\nITEM.width = 2\nITEM.description = \"A Package of %s Rockets\"\nITEM.iconCam = {\n\tang\t= Angle(-0.70499622821808, 268.25439453125, 0),\n\tfov\t= 12.085652091515,\n\tpos\t= Vector(7, 200, -2)\n}\n"
  },
  {
    "path": "gamemode/items/ammo/sh_shotgunammo.txt",
    "content": "ITEM.name = \"Shotgun Shells\"\nITEM.model = \"models/Items/BoxBuckshot.mdl\"\nITEM.ammo = \"buckshot\" -- type of the ammo\nITEM.ammoAmount = 15 -- amount of the ammo\nITEM.description = \"A Box of %s Shotgun Shells\"\n"
  },
  {
    "path": "gamemode/items/ammo/sh_smg1ammo.txt",
    "content": "ITEM.name = \"Sub Machine Gun Ammo\"\nITEM.model = \"models/Items/BoxSRounds.mdl\"\nITEM.ammo = \"smg1\" -- type of the ammo\nITEM.ammoAmount = 45 -- amount of the ammo\nITEM.description = \"A Box that contains %s of SMG Ammo\"\n"
  },
  {
    "path": "gamemode/items/bags/sh_large.txt",
    "content": "ITEM.name = \"Big Bag\"\nITEM.description = \"A big bag.\"\nITEM.invWidth = 6\nITEM.invHeight = 4"
  },
  {
    "path": "gamemode/items/bags/sh_small.txt",
    "content": "ITEM.name = \"Small Bag\"\nITEM.description = \"A small bag.\""
  },
  {
    "path": "gamemode/items/base/sh_ammo.lua",
    "content": "\nITEM.name = \"Ammo Base\"\nITEM.model = \"models/Items/BoxSRounds.mdl\"\nITEM.width = 1\nITEM.height = 1\nITEM.ammo = \"pistol\" -- type of the ammo\nITEM.ammoAmount = 30 -- amount of the ammo\nITEM.description = \"A Box that contains %s of Pistol Ammo\"\nITEM.category = \"Ammunition\"\nITEM.useSound = \"items/ammo_pickup.wav\"\n\nfunction ITEM:GetDescription()\n\tlocal rounds = self:GetData(\"rounds\", self.ammoAmount)\n\treturn Format(self.description, rounds)\nend\n\nif (CLIENT) then\n\tfunction ITEM:PaintOver(item, w, h)\n\t\tdraw.SimpleText(\n\t\t\titem:GetData(\"rounds\", item.ammoAmount), \"DermaDefault\", w - 5, h - 5,\n\t\t\tcolor_white, TEXT_ALIGN_RIGHT, TEXT_ALIGN_BOTTOM, 1, color_black\n\t\t)\n\tend\nend\n\n-- On player uneqipped the item, Removes a weapon from the player and keep the ammo in the item.\nITEM.functions.use = {\n\tname = \"Load\",\n\ttip = \"useTip\",\n\ticon = \"icon16/add.png\",\n\tOnRun = function(item)\n\t\tlocal rounds = item:GetData(\"rounds\", item.ammoAmount)\n\n\t\titem.player:GiveAmmo(rounds, item.ammo)\n\t\titem.player:EmitSound(item.useSound, 110)\n\n\t\treturn true\n\tend,\n}\n\n-- Called after the item is registered into the item tables.\nfunction ITEM:OnRegistered()\n\tif (ix.ammo) then\n\t\tix.ammo.Register(self.ammo)\n\tend\nend\n"
  },
  {
    "path": "gamemode/items/base/sh_bags.lua",
    "content": "\nif (SERVER) then\n\tutil.AddNetworkString(\"ixBagDrop\")\nend\n\nITEM.name = \"Bag\"\nITEM.description = \"A bag to hold items.\"\nITEM.model = \"models/props_c17/suitcase001a.mdl\"\nITEM.category = \"Storage\"\nITEM.width = 2\nITEM.height = 2\nITEM.invWidth = 4\nITEM.invHeight = 2\nITEM.isBag = true\nITEM.functions.View = {\n\ticon = \"icon16/briefcase.png\",\n\tOnClick = function(item)\n\t\tlocal index = item:GetData(\"id\", \"\")\n\n\t\tif (index) then\n\t\t\tlocal panel = ix.gui[\"inv\"..index]\n\t\t\tlocal inventory = ix.item.inventories[index]\n\t\t\tlocal parent = IsValid(ix.gui.menuInventoryContainer) and ix.gui.menuInventoryContainer or ix.gui.openedStorage\n\n\t\t\tif (IsValid(panel)) then\n\t\t\t\tpanel:Remove()\n\t\t\tend\n\n\t\t\tif (inventory and inventory.slots) then\n\t\t\t\tpanel = vgui.Create(\"ixInventory\", IsValid(parent) and parent or nil)\n\t\t\t\tpanel:SetInventory(inventory)\n\t\t\t\tpanel:ShowCloseButton(true)\n\t\t\t\tpanel:SetTitle(item.GetName and item:GetName() or L(item.name))\n\n\t\t\t\tif (parent != ix.gui.menuInventoryContainer) then\n\t\t\t\t\tpanel:Center()\n\n\t\t\t\t\tif (parent == ix.gui.openedStorage) then\n\t\t\t\t\t\tpanel:MakePopup()\n\t\t\t\t\tend\n\t\t\t\telse\n\t\t\t\t\tpanel:MoveToFront()\n\t\t\t\tend\n\n\t\t\t\tix.gui[\"inv\"..index] = panel\n\t\t\telse\n\t\t\t\tErrorNoHalt(\"[Helix] Attempt to view an uninitialized inventory '\"..index..\"'\\n\")\n\t\t\tend\n\t\tend\n\n\t\treturn false\n\tend,\n\tOnCanRun = function(item)\n\t\treturn !IsValid(item.entity) and item:GetData(\"id\") and !IsValid(ix.gui[\"inv\" .. item:GetData(\"id\", \"\")])\n\tend\n}\nITEM.functions.combine = {\n\tOnRun = function(item, data)\n\t\tix.item.instances[data[1]]:Transfer(item:GetData(\"id\"), nil, nil, item.player)\n\n\t\treturn false\n\tend,\n\tOnCanRun = function(item, data)\n\t\tlocal index = item:GetData(\"id\", \"\")\n\n\t\tif (index) then\n\t\t\tlocal inventory = ix.item.inventories[index]\n\n\t\t\tif (inventory) then\n\t\t\t\treturn true\n\t\t\tend\n\t\tend\n\n\t\treturn false\n\tend\n}\n\nif (CLIENT) then\n\tfunction ITEM:PaintOver(item, width, height)\n\t\tlocal panel = ix.gui[\"inv\" .. item:GetData(\"id\", \"\")]\n\n\t\tif (!IsValid(panel)) then\n\t\t\treturn\n\t\tend\n\n\t\tif (vgui.GetHoveredPanel() == self) then\n\t\t\tpanel:SetHighlighted(true)\n\t\telse\n\t\t\tpanel:SetHighlighted(false)\n\t\tend\n\tend\nend\n\n-- Called when a new instance of this item has been made.\nfunction ITEM:OnInstanced(invID, x, y)\n\tlocal inventory = ix.item.inventories[invID]\n\n\tix.inventory.New(inventory and inventory.owner or 0, self.uniqueID, function(inv)\n\t\tlocal client = inv:GetOwner()\n\n\t\tinv.vars.isBag = self.uniqueID\n\t\tself:SetData(\"id\", inv:GetID())\n\n\t\tif (IsValid(client)) then\n\t\t\tinv:AddReceiver(client)\n\t\tend\n\tend)\nend\n\nfunction ITEM:GetInventory()\n\tlocal index = self:GetData(\"id\")\n\n\tif (index) then\n\t\treturn ix.item.inventories[index]\n\tend\nend\n\nITEM.GetInv = ITEM.GetInventory\n\n-- Called when the item first appears for a client.\nfunction ITEM:OnSendData()\n\tlocal index = self:GetData(\"id\")\n\n\tif (index) then\n\t\tlocal inventory = ix.item.inventories[index]\n\n\t\tif (inventory) then\n\t\t\tinventory.vars.isBag = self.uniqueID\n\t\t\tinventory:Sync(self.player)\n\t\t\tinventory:AddReceiver(self.player)\n\t\telse\n\t\t\tlocal owner = self.player:GetCharacter():GetID()\n\n\t\t\tix.inventory.Restore(self:GetData(\"id\"), self.invWidth, self.invHeight, function(inv)\n\t\t\t\tinv.vars.isBag = self.uniqueID\n\t\t\t\tinv:SetOwner(owner, true)\n\n\t\t\t\tif (!inv.owner) then\n\t\t\t\t\treturn\n\t\t\t\tend\n\n\t\t\t\tfor client, character in ix.util.GetCharacters() do\n\t\t\t\t\tif (character:GetID() == inv.owner) then\n\t\t\t\t\t\tinv:AddReceiver(client)\n\t\t\t\t\t\tbreak\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend)\n\t\tend\n\telse\n\t\tix.inventory.New(self.player:GetCharacter():GetID(), self.uniqueID, function(inv)\n\t\t\tself:SetData(\"id\", inv:GetID())\n\t\tend)\n\tend\nend\n\nITEM.postHooks.drop = function(item, result)\n\tlocal index = item:GetData(\"id\")\n\n\tlocal query = mysql:Update(\"ix_inventories\")\n\t\tquery:Update(\"character_id\", 0)\n\t\tquery:Where(\"inventory_id\", index)\n\tquery:Execute()\n\n\tnet.Start(\"ixBagDrop\")\n\t\tnet.WriteUInt(index, 32)\n\tnet.Send(item.player)\nend\n\nif (CLIENT) then\n\tnet.Receive(\"ixBagDrop\", function()\n\t\tlocal index = net.ReadUInt(32)\n\t\tlocal panel = ix.gui[\"inv\"..index]\n\n\t\tif (panel and panel:IsVisible()) then\n\t\t\tpanel:Close()\n\t\tend\n\tend)\nend\n\n-- Called before the item is permanently deleted.\nfunction ITEM:OnRemoved()\n\tlocal index = self:GetData(\"id\")\n\n\tif (index) then\n\t\tlocal query = mysql:Delete(\"ix_items\")\n\t\t\tquery:Where(\"inventory_id\", index)\n\t\tquery:Execute()\n\n\t\tquery = mysql:Delete(\"ix_inventories\")\n\t\t\tquery:Where(\"inventory_id\", index)\n\t\tquery:Execute()\n\tend\nend\n\n-- Called when the item should tell whether or not it can be transfered between inventories.\nfunction ITEM:CanTransfer(oldInventory, newInventory)\n\tlocal index = self:GetData(\"id\")\n\n\tif (newInventory) then\n\t\tif (newInventory.vars and newInventory.vars.isBag) then\n\t\t\treturn false\n\t\tend\n\n\t\tlocal index2 = newInventory:GetID()\n\n\t\tif (index == index2) then\n\t\t\treturn false\n\t\tend\n\n\t\tfor k, _ in self:GetInventory():Iter() do\n\t\t\tif (k:GetData(\"id\") == index2) then\n\t\t\t\treturn false\n\t\t\tend\n\t\tend\n\tend\n\n\treturn !newInventory or newInventory:GetID() != oldInventory:GetID() or newInventory.vars.isBag\nend\n\nfunction ITEM:OnTransferred(curInv, inventory)\n\tlocal bagInventory = self:GetInventory()\n\n\tif (isfunction(curInv.GetOwner)) then\n\t\tlocal owner = curInv:GetOwner()\n\n\t\tif (IsValid(owner)) then\n\t\t\tbagInventory:RemoveReceiver(owner)\n\t\tend\n\tend\n\n\tif (isfunction(inventory.GetOwner)) then\n\t\tlocal owner = inventory:GetOwner()\n\n\t\tif (IsValid(owner)) then\n\t\t\tbagInventory:AddReceiver(owner)\n\t\t\tbagInventory:SetOwner(owner)\n\t\tend\n\telse\n\t\t-- it's not in a valid inventory so nobody owns this bag\n\t\tbagInventory:SetOwner(nil)\n\tend\nend\n\n-- Called after the item is registered into the item tables.\nfunction ITEM:OnRegistered()\n\tix.inventory.Register(self.uniqueID, self.invWidth, self.invHeight, true)\nend\n"
  },
  {
    "path": "gamemode/items/base/sh_outfit.lua",
    "content": "\nITEM.name = \"Outfit\"\nITEM.description = \"A Outfit Base.\"\nITEM.category = \"Outfit\"\nITEM.model = \"models/Gibs/HGIBS.mdl\"\nITEM.width = 1\nITEM.height = 1\nITEM.outfitCategory = \"model\"\nITEM.pacData = {}\n\n--[[\n-- This will change a player's skin after changing the model. Keep in mind it starts at 0.\nITEM.newSkin = 1\n-- This will change a certain part of the model.\nITEM.replacements = {\"group01\", \"group02\"}\n-- This will change the player's model completely.\nITEM.replacements = \"models/manhack.mdl\"\n-- This will have multiple replacements.\nITEM.replacements = {\n\t{\"male\", \"female\"},\n\t{\"group01\", \"group02\"}\n}\n\n-- This will apply body groups.\nITEM.bodyGroups = {\n\t[\"blade\"] = 1,\n\t[\"bladeblur\"] = 1\n}\n]]--\n\n-- Inventory drawing\nif (CLIENT) then\n\tfunction ITEM:PaintOver(item, w, h)\n\t\tif (item:GetData(\"equip\")) then\n\t\t\tsurface.SetDrawColor(110, 255, 110, 100)\n\t\t\tsurface.DrawRect(w - 14, h - 14, 8, 8)\n\t\tend\n\tend\nend\n\nfunction ITEM:AddOutfit(client)\n\tlocal character = client:GetCharacter()\n\n\tself:SetData(\"equip\", true)\n\n\tlocal groups = character:GetData(\"groups\", {})\n\n\t-- remove original bodygroups\n\tif (!table.IsEmpty(groups)) then\n\t\tcharacter:SetData(\"oldGroups\" .. self.outfitCategory, groups)\n\t\tcharacter:SetData(\"groups\", {})\n\n\t\tclient:ResetBodygroups()\n\tend\n\n\tif (isfunction(self.OnGetReplacement)) then\n\t\tcharacter:SetData(\"oldModel\" .. self.outfitCategory,\n\t\t\tcharacter:GetData(\"oldModel\" .. self.outfitCategory, self.player:GetModel()))\n\t\tcharacter:SetModel(self:OnGetReplacement())\n\telseif (self.replacement or self.replacements) then\n\t\tcharacter:SetData(\"oldModel\" .. self.outfitCategory,\n\t\t\tcharacter:GetData(\"oldModel\" .. self.outfitCategory, self.player:GetModel()))\n\n\t\tif (istable(self.replacements)) then\n\t\t\tif (#self.replacements == 2 and isstring(self.replacements[1])) then\n\t\t\t\tcharacter:SetModel(self.player:GetModel():gsub(self.replacements[1], self.replacements[2]))\n\t\t\telse\n\t\t\t\tfor _, v in ipairs(self.replacements) do\n\t\t\t\t\tcharacter:SetModel(self.player:GetModel():gsub(v[1], v[2]))\n\t\t\t\tend\n\t\t\tend\n\t\telse\n\t\t\tcharacter:SetModel(self.replacement or self.replacements)\n\t\tend\n\tend\n\n\tif (self.newSkin) then\n\t\tcharacter:SetData(\"oldSkin\" .. self.outfitCategory, self.player:GetSkin())\n\t\tself.player:SetSkin(self.newSkin)\n\tend\n\n\t-- get outfit saved bodygroups\n\tgroups = self:GetData(\"groups\", {})\n\n\t-- restore bodygroups saved to the item\n\tif (!table.IsEmpty(groups) and self:ShouldRestoreBodygroups()) then\n\t\tfor k, v in pairs(groups) do\n\t\t\tclient:SetBodygroup(k, v)\n\t\tend\n\t-- apply default item bodygroups if none are saved\n\telseif (istable(self.bodyGroups)) then\n\t\tfor k, v in pairs(self.bodyGroups) do\n\t\t\tlocal index = client:FindBodygroupByName(k)\n\n\t\t\tif (index > -1) then\n\t\t\t\tclient:SetBodygroup(index, v)\n\t\t\tend\n\t\tend\n\tend\n\n\tlocal materials  = self:GetData(\"submaterial\", {})\n\n\tif (!table.IsEmpty(materials) and self:ShouldRestoreSubMaterials()) then\n\t\tfor k, v in pairs(materials) do\n\t\t\tif (!isnumber(k) or !isstring(v)) then\n\t\t\t\tcontinue\n\t\t\tend\n\n\t\t\tclient:SetSubMaterial(k - 1, v)\n\t\tend\n\tend\n\n\tif (istable(self.attribBoosts)) then\n\t\tfor k, v in pairs(self.attribBoosts) do\n\t\t\tcharacter:AddBoost(self.uniqueID, k, v)\n\t\tend\n\tend\n\n\tself:GetOwner():SetupHands()\n\tself:OnEquipped()\nend\n\nlocal function ResetSubMaterials(client)\n\tfor k, _ in ipairs(client:GetMaterials()) do\n\t\tif (client:GetSubMaterial(k - 1) != \"\") then\n\t\t\tclient:SetSubMaterial(k - 1)\n\t\tend\n\tend\nend\n\nfunction ITEM:RemoveOutfit(client)\n\tlocal character = client:GetCharacter()\n\n\tself:SetData(\"equip\", false)\n\n\tlocal materials = {}\n\n\tfor k, _ in ipairs(client:GetMaterials()) do\n\t\tif (client:GetSubMaterial(k - 1) != \"\") then\n\t\t\tmaterials[k] = client:GetSubMaterial(k - 1)\n\t\tend\n\tend\n\n\t-- save outfit submaterials\n\tif (!table.IsEmpty(materials)) then\n\t\tself:SetData(\"submaterial\", materials)\n\tend\n\n\t-- remove outfit submaterials\n\tResetSubMaterials(client)\n\n\tlocal groups = {}\n\n\tfor i = 0, (client:GetNumBodyGroups() - 1) do\n\t\tlocal bodygroup = client:GetBodygroup(i)\n\n\t\tif (bodygroup > 0) then\n\t\t\tgroups[i] = bodygroup\n\t\tend\n\tend\n\n\t-- save outfit bodygroups\n\tif (!table.IsEmpty(groups)) then\n\t\tself:SetData(\"groups\", groups)\n\tend\n\n\t-- remove outfit bodygroups\n\tclient:ResetBodygroups()\n\n\t-- restore the original player model\n\tif (character:GetData(\"oldModel\" .. self.outfitCategory)) then\n\t\tcharacter:SetModel(character:GetData(\"oldModel\" .. self.outfitCategory))\n\t\tcharacter:SetData(\"oldModel\" .. self.outfitCategory, nil)\n\tend\n\n\t-- restore the original player model skin\n\tif (self.newSkin) then\n\t\tif (character:GetData(\"oldSkin\" .. self.outfitCategory)) then\n\t\t\tclient:SetSkin(character:GetData(\"oldSkin\" .. self.outfitCategory))\n\t\t\tcharacter:SetData(\"oldSkin\" .. self.outfitCategory, nil)\n\t\telse\n\t\t\tclient:SetSkin(0)\n\t\tend\n\tend\n\n\t-- get character original bodygroups\n\tgroups = character:GetData(\"oldGroups\" .. self.outfitCategory, {})\n\n\t-- restore original bodygroups\n\tif (!table.IsEmpty(groups)) then\n\t\tfor k, v in pairs(groups) do\n\t\t\tclient:SetBodygroup(k, v)\n\t\tend\n\n\t\tcharacter:SetData(\"groups\", character:GetData(\"oldGroups\" .. self.outfitCategory, {}))\n\t\tcharacter:SetData(\"oldGroups\" .. self.outfitCategory, nil)\n\tend\n\n\tif (istable(self.attribBoosts)) then\n\t\tfor k, _ in pairs(self.attribBoosts) do\n\t\t\tcharacter:RemoveBoost(self.uniqueID, k)\n\t\tend\n\tend\n\n\tfor k, _ in pairs(self:GetData(\"outfitAttachments\", {})) do\n\t\tself:RemoveAttachment(k, client)\n\tend\n\n\tself:GetOwner():SetupHands()\n\tself:OnUnequipped()\nend\n\n-- makes another outfit depend on this outfit in terms of requiring this item to be equipped in order to equip the attachment\n-- also unequips the attachment if this item is dropped\nfunction ITEM:AddAttachment(id)\n\tlocal attachments = self:GetData(\"outfitAttachments\", {})\n\tattachments[id] = true\n\n\tself:SetData(\"outfitAttachments\", attachments)\nend\n\nfunction ITEM:RemoveAttachment(id, client)\n\tlocal item = ix.item.instances[id]\n\tlocal attachments = self:GetData(\"outfitAttachments\", {})\n\n\tif (item and attachments[id]) then\n\t\titem:OnDetached(client)\n\tend\n\n\tattachments[id] = nil\n\tself:SetData(\"outfitAttachments\", attachments)\nend\n\nITEM:Hook(\"drop\", function(item)\n\tif (item:GetData(\"equip\")) then\n\t\tlocal character = ix.char.loaded[item.owner]\n\t\tlocal client = character and character:GetPlayer() or item:GetOwner()\n\n\t\titem.player = client\n\t\titem:RemoveOutfit(item:GetOwner())\n\tend\nend)\n\nITEM.functions.EquipUn = { -- sorry, for name order.\n\tname = \"unequip\",\n\ttip = \"unequipTip\",\n\ticon = \"icon16/cross.png\",\n\tOnRun = function(item)\n\t\titem:RemoveOutfit(item.player)\n\t\treturn false\n\tend,\n\tOnCanRun = function(item)\n\t\tlocal client = item.player\n\n\t\treturn !IsValid(item.entity) and IsValid(client) and item:GetData(\"equip\") == true and\n\t\t\thook.Run(\"CanPlayerUnequipItem\", client, item) != false\n\tend\n}\n\nITEM.functions.Equip = {\n\tname = \"equip\",\n\ttip = \"equipTip\",\n\ticon = \"icon16/tick.png\",\n\tOnRun = function(item)\n\t\tlocal client = item.player\n\t\tlocal char = client:GetCharacter()\n\n\t\tfor k, _ in char:GetInventory():Iter() do\n\t\t\tif (k.id != item.id) then\n\t\t\t\tlocal itemTable = ix.item.instances[k.id]\n\n\t\t\t\tif (itemTable.pacData and k.outfitCategory == item.outfitCategory and itemTable:GetData(\"equip\")) then\n\t\t\t\t\tclient:NotifyLocalized(item.equippedNotify or \"outfitAlreadyEquipped\")\n\t\t\t\t\treturn false\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\n\t\titem:AddOutfit(item.player)\n\t\treturn false\n\tend,\n\tOnCanRun = function(item)\n\t\tlocal client = item.player\n\n\t\treturn !IsValid(item.entity) and IsValid(client) and item:GetData(\"equip\") != true and item:CanEquipOutfit() and\n\t\t\thook.Run(\"CanPlayerEquipItem\", client, item) != false\n\tend\n}\n\nfunction ITEM:CanTransfer(oldInventory, newInventory)\n\tif (newInventory and self:GetData(\"equip\")) then\n\t\treturn false\n\tend\n\n\treturn true\nend\n\nfunction ITEM:OnRemoved()\n\tif (self.invID != 0 and self:GetData(\"equip\")) then\n\t\tself.player = self:GetOwner()\n\t\t\tself:RemoveOutfit(self.player)\n\t\tself.player = nil\n\tend\nend\n\nfunction ITEM:OnEquipped()\nend\n\nfunction ITEM:OnUnequipped()\nend\n\nfunction ITEM:CanEquipOutfit()\n\treturn true\nend\n\nfunction ITEM:ShouldRestoreBodygroups()\n\treturn true\nend\n\nfunction ITEM:ShouldRestoreSubMaterials()\n\treturn true\nend\n"
  },
  {
    "path": "gamemode/items/base/sh_pacoutfit.lua",
    "content": "\nITEM.name = \"PAC Outfit\"\nITEM.description = \"A PAC Outfit Base.\"\nITEM.category = \"Outfit\"\nITEM.model = \"models/Gibs/HGIBS.mdl\"\nITEM.width = 1\nITEM.height = 1\nITEM.outfitCategory = \"hat\"\nITEM.pacData = {}\n\n--[[\nITEM.pacData = {\n\t[1] = {\n\t\t[\"children\"] = {\n\t\t\t[1] = {\n\t\t\t\t[\"children\"] = {\n\t\t\t\t},\n\t\t\t\t[\"self\"] = {\n\t\t\t\t\t[\"Angles\"] = Angle(12.919322967529, 6.5696062847564e-006, -1.0949343050015e-005),\n\t\t\t\t\t[\"Position\"] = Vector(-2.099609375, 0.019973754882813, 1.0180969238281),\n\t\t\t\t\t[\"UniqueID\"] = \"4249811628\",\n\t\t\t\t\t[\"Size\"] = 1.25,\n\t\t\t\t\t[\"Bone\"] = \"eyes\",\n\t\t\t\t\t[\"Model\"] = \"models/Gibs/HGIBS.mdl\",\n\t\t\t\t\t[\"ClassName\"] = \"model\",\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t[\"self\"] = {\n\t\t\t[\"ClassName\"] = \"group\",\n\t\t\t[\"UniqueID\"] = \"907159817\",\n\t\t\t[\"EditorExpand\"] = true,\n\t\t},\n\t},\n}\n\n-- This will change a player's skin after changing the model. Keep in mind it starts at 0.\nITEM.newSkin = 1\n-- This will change a certain part of the model.\nITEM.replacements = {\"group01\", \"group02\"}\n-- This will change the player's model completely.\nITEM.replacements = \"models/manhack.mdl\"\n-- This will have multiple replacements.\nITEM.replacements = {\n\t{\"male\", \"female\"},\n\t{\"group01\", \"group02\"}\n}\n\n-- This will apply body groups.\nITEM.bodyGroups = {\n\t[\"blade\"] = 1,\n\t[\"bladeblur\"] = 1\n}\n\n--]]\n\n-- Inventory drawing\nif (CLIENT) then\n\t-- Draw camo if it is available.\n\tfunction ITEM:PaintOver(item, w, h)\n\t\tif (item:GetData(\"equip\")) then\n\t\t\tsurface.SetDrawColor(110, 255, 110, 100)\n\t\t\tsurface.DrawRect(w - 14, h - 14, 8, 8)\n\t\tend\n\tend\nend\n\nfunction ITEM:RemovePart(client)\n\tlocal char = client:GetCharacter()\n\n\tself:SetData(\"equip\", false)\n\tclient:RemovePart(self.uniqueID)\n\n\tif (self.attribBoosts) then\n\t\tfor k, _ in pairs(self.attribBoosts) do\n\t\t\tchar:RemoveBoost(self.uniqueID, k)\n\t\tend\n\tend\n\n\tself:OnUnequipped()\nend\n\n-- On item is dropped, Remove a weapon from the player and keep the ammo in the item.\nITEM:Hook(\"drop\", function(item)\n\tif (item:GetData(\"equip\")) then\n\t\titem:RemovePart(item:GetOwner())\n\tend\nend)\n\n-- On player uneqipped the item, Removes a weapon from the player and keep the ammo in the item.\nITEM.functions.EquipUn = { -- sorry, for name order.\n\tname = \"unequip\",\n\ttip = \"unequipTip\",\n\ticon = \"icon16/cross.png\",\n\tOnRun = function(item)\n\t\titem:RemovePart(item.player)\n\n\t\treturn false\n\tend,\n\tOnCanRun = function(item)\n\t\tlocal client = item.player\n\n\t\treturn !IsValid(item.entity) and IsValid(client) and item:GetData(\"equip\") == true and\n\t\t\thook.Run(\"CanPlayerUnequipItem\", client, item) != false\n\tend\n}\n\n-- On player eqipped the item, Gives a weapon to player and load the ammo data from the item.\nITEM.functions.Equip = {\n\tname = \"equip\",\n\ttip = \"equipTip\",\n\ticon = \"icon16/tick.png\",\n\tOnRun = function(item)\n\t\tlocal char = item.player:GetCharacter()\n\n\t\tfor k, _ in char:GetInventory():Iter() do\n\t\t\tif (k.id != item.id) then\n\t\t\t\tlocal itemTable = ix.item.instances[k.id]\n\n\t\t\t\tif (itemTable.pacData and k.outfitCategory == item.outfitCategory and itemTable:GetData(\"equip\")) then\n\t\t\t\t\titem.player:NotifyLocalized(item.equippedNotify or \"outfitAlreadyEquipped\")\n\n\t\t\t\t\treturn false\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\n\t\titem:SetData(\"equip\", true)\n\t\titem.player:AddPart(item.uniqueID, item)\n\n\t\tif (item.attribBoosts) then\n\t\t\tfor k, v in pairs(item.attribBoosts) do\n\t\t\t\tchar:AddBoost(item.uniqueID, k, v)\n\t\t\tend\n\t\tend\n\n\t\titem:OnEquipped()\n\t\treturn false\n\tend,\n\tOnCanRun = function(item)\n\t\tlocal client = item.player\n\n\t\treturn !IsValid(item.entity) and IsValid(client) and item:GetData(\"equip\") != true and\n\t\t\thook.Run(\"CanPlayerEquipItem\", client, item) != false\n\tend\n}\n\nfunction ITEM:CanTransfer(oldInventory, newInventory)\n\tif (newInventory and self:GetData(\"equip\")) then\n\t\treturn false\n\tend\n\n\treturn true\nend\n\nfunction ITEM:OnRemoved()\n\tlocal inventory = ix.item.inventories[self.invID]\n\tlocal owner = inventory.GetOwner and inventory:GetOwner()\n\n\tif (IsValid(owner) and owner:IsPlayer()) then\n\t\tif (self:GetData(\"equip\")) then\n\t\t\tself:RemovePart(owner)\n\t\tend\n\tend\nend\n\nfunction ITEM:OnEquipped()\nend\n\nfunction ITEM:OnUnequipped()\nend\n"
  },
  {
    "path": "gamemode/items/base/sh_weapons.lua",
    "content": "\nITEM.name = \"Weapon\"\nITEM.description = \"A Weapon.\"\nITEM.category = \"Weapons\"\nITEM.model = \"models/weapons/w_pistol.mdl\"\nITEM.class = \"weapon_pistol\"\nITEM.width = 2\nITEM.height = 2\nITEM.isWeapon = true\nITEM.isGrenade = false\nITEM.weaponCategory = \"sidearm\"\nITEM.useSound = \"items/ammo_pickup.wav\"\n\n-- Inventory drawing\nif (CLIENT) then\n\tfunction ITEM:PaintOver(item, w, h)\n\t\tif (item:GetData(\"equip\")) then\n\t\t\tsurface.SetDrawColor(110, 255, 110, 100)\n\t\t\tsurface.DrawRect(w - 14, h - 14, 8, 8)\n\t\tend\n\tend\n\n\tfunction ITEM:PopulateTooltip(tooltip)\n\t\tif (self:GetData(\"equip\")) then\n\t\t\tlocal name = tooltip:GetRow(\"name\")\n\t\t\tname:SetBackgroundColor(derma.GetColor(\"Success\", tooltip))\n\t\tend\n\tend\nend\n\n-- On item is dropped, Remove a weapon from the player and keep the ammo in the item.\nITEM:Hook(\"drop\", function(item)\n\tlocal inventory = ix.item.inventories[item.invID]\n\n\tif (!inventory) then\n\t\treturn\n\tend\n\n\t-- the item could have been dropped by someone else (i.e someone searching this player), so we find the real owner\n\tlocal owner\n\n\tfor client, character in ix.util.GetCharacters() do\n\t\tif (character:GetID() == inventory.owner) then\n\t\t\towner = client\n\t\t\tbreak\n\t\tend\n\tend\n\n\tif (!IsValid(owner)) then\n\t\treturn\n\tend\n\n\tif (item:GetData(\"equip\")) then\n\t\titem:SetData(\"equip\", nil)\n\n\t\towner.carryWeapons = owner.carryWeapons or {}\n\n\t\tlocal weapon = owner.carryWeapons[item.weaponCategory]\n\n\t\tif (!IsValid(weapon)) then\n\t\t\tweapon = owner:GetWeapon(item.class)\n\t\tend\n\n\t\tif (IsValid(weapon)) then\n\t\t\titem:SetData(\"ammo\", weapon:Clip1())\n\n\t\t\towner:StripWeapon(item.class)\n\t\t\towner.carryWeapons[item.weaponCategory] = nil\n\t\t\towner:EmitSound(item.useSound, 80)\n\t\tend\n\n\t\titem:RemovePAC(owner)\n\tend\nend)\n\n-- On player uneqipped the item, Removes a weapon from the player and keep the ammo in the item.\nITEM.functions.EquipUn = { -- sorry, for name order.\n\tname = \"unequip\",\n\ttip = \"unequipTip\",\n\ticon = \"icon16/cross.png\",\n\tOnRun = function(item)\n\t\titem:Unequip(item.player, true)\n\t\treturn false\n\tend,\n\tOnCanRun = function(item)\n\t\tlocal client = item.player\n\n\t\treturn !IsValid(item.entity) and IsValid(client) and item:GetData(\"equip\") == true and\n\t\t\thook.Run(\"CanPlayerUnequipItem\", client, item) != false\n\tend\n}\n\n-- On player eqipped the item, Gives a weapon to player and load the ammo data from the item.\nITEM.functions.Equip = {\n\tname = \"equip\",\n\ttip = \"equipTip\",\n\ticon = \"icon16/tick.png\",\n\tOnRun = function(item)\n\t\titem:Equip(item.player)\n\t\treturn false\n\tend,\n\tOnCanRun = function(item)\n\t\tlocal client = item.player\n\n\t\treturn !IsValid(item.entity) and IsValid(client) and item:GetData(\"equip\") != true and\n\t\t\thook.Run(\"CanPlayerEquipItem\", client, item) != false\n\tend\n}\n\nfunction ITEM:WearPAC(client)\n\tif (ix.pac and self.pacData) then\n\t\tclient:AddPart(self.uniqueID, self)\n\tend\nend\n\nfunction ITEM:RemovePAC(client)\n\tif (ix.pac and self.pacData) then\n\t\tclient:RemovePart(self.uniqueID)\n\tend\nend\n\nfunction ITEM:Equip(client, bNoSelect, bNoSound)\n\tclient.carryWeapons = client.carryWeapons or {}\n\n\tfor k, _ in client:GetCharacter():GetInventory():Iter() do\n\t\tif (k.id != self.id) then\n\t\t\tlocal itemTable = ix.item.instances[k.id]\n\n\t\t\tif (!itemTable) then\n\t\t\t\tclient:NotifyLocalized(\"tellAdmin\", \"wid!xt\")\n\n\t\t\t\treturn false\n\t\t\telse\n\t\t\t\tif (itemTable.isWeapon and client.carryWeapons[self.weaponCategory] and itemTable:GetData(\"equip\")) then\n\t\t\t\t\tclient:NotifyLocalized(\"weaponSlotFilled\", self.weaponCategory)\n\n\t\t\t\t\treturn false\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend\n\n\tif (client:HasWeapon(self.class)) then\n\t\tclient:StripWeapon(self.class)\n\tend\n\n\tlocal weapon = client:Give(self.class, !self.isGrenade)\n\n\tif (IsValid(weapon)) then\n\t\tlocal ammoType = weapon:GetPrimaryAmmoType()\n\n\t\tclient.carryWeapons[self.weaponCategory] = weapon\n\n\t\tif (!bNoSelect) then\n\t\t\tclient:SelectWeapon(weapon:GetClass())\n\t\tend\n\n\t\tif (!bNoSound) then\n\t\t\tclient:EmitSound(self.useSound, 80)\n\t\tend\n\n\t\t-- Remove default given ammo.\n\t\tif (client:GetAmmoCount(ammoType) == weapon:Clip1() and self:GetData(\"ammo\", 0) == 0) then\n\t\t\tclient:RemoveAmmo(weapon:Clip1(), ammoType)\n\t\tend\n\n\t\t-- assume that a weapon with -1 clip1 and clip2 would be a throwable (i.e hl2 grenade)\n\t\t-- TODO: figure out if this interferes with any other weapons\n\t\tif (weapon:GetMaxClip1() == -1 and weapon:GetMaxClip2() == -1 and client:GetAmmoCount(ammoType) == 0) then\n\t\t\tclient:SetAmmo(1, ammoType)\n\t\tend\n\n\t\tself:SetData(\"equip\", true)\n\n\t\tif (self.isGrenade) then\n\t\t\tweapon:SetClip1(1)\n\t\t\tclient:SetAmmo(0, ammoType)\n\t\telse\n\t\t\tweapon:SetClip1(self:GetData(\"ammo\", 0))\n\t\tend\n\n\t\tweapon.ixItem = self\n\n\t\tif (self.OnEquipWeapon) then\n\t\t\tself:OnEquipWeapon(client, weapon)\n\t\tend\n\telse\n\t\tprint(Format(\"[Helix] Cannot equip weapon - %s does not exist!\", self.class))\n\tend\nend\n\nfunction ITEM:Unequip(client, bPlaySound, bRemoveItem)\n\tclient.carryWeapons = client.carryWeapons or {}\n\n\tlocal weapon = client.carryWeapons[self.weaponCategory]\n\n\tif (!IsValid(weapon)) then\n\t\tweapon = client:GetWeapon(self.class)\n\tend\n\n\tif (IsValid(weapon)) then\n\t\tweapon.ixItem = nil\n\n\t\tself:SetData(\"ammo\", weapon:Clip1())\n\t\tclient:StripWeapon(self.class)\n\telse\n\t\tprint(Format(\"[Helix] Cannot unequip weapon - %s does not exist!\", self.class))\n\tend\n\n\tif (bPlaySound) then\n\t\tclient:EmitSound(self.useSound, 80)\n\tend\n\n\tclient.carryWeapons[self.weaponCategory] = nil\n\tself:SetData(\"equip\", nil)\n\tself:RemovePAC(client)\n\n\tif (self.OnUnequipWeapon) then\n\t\tself:OnUnequipWeapon(client, weapon)\n\tend\n\n\tif (bRemoveItem) then\n\t\tself:Remove()\n\tend\nend\n\nfunction ITEM:CanTransfer(oldInventory, newInventory)\n\tif (newInventory and self:GetData(\"equip\")) then\n\t\tlocal owner = self:GetOwner()\n\n\t\tif (IsValid(owner)) then\n\t\t\towner:NotifyLocalized(\"equippedWeapon\")\n\t\tend\n\n\t\treturn false\n\tend\n\n\treturn true\nend\n\nfunction ITEM:OnLoadout()\n\tif (self:GetData(\"equip\")) then\n\t\tlocal client = self.player\n\t\tclient.carryWeapons = client.carryWeapons or {}\n\n\t\tlocal weapon = client:Give(self.class, true)\n\n\t\tif (IsValid(weapon)) then\n\t\t\tclient:RemoveAmmo(weapon:Clip1(), weapon:GetPrimaryAmmoType())\n\t\t\tclient.carryWeapons[self.weaponCategory] = weapon\n\n\t\t\tweapon.ixItem = self\n\t\t\tweapon:SetClip1(self:GetData(\"ammo\", 0))\n\n\t\t\tif (self.OnEquipWeapon) then\n\t\t\t\tself:OnEquipWeapon(client, weapon)\n\t\t\tend\n\t\telse\n\t\t\tprint(Format(\"[Helix] Cannot give weapon - %s does not exist!\", self.class))\n\t\tend\n\tend\nend\n\nfunction ITEM:OnSave()\n\tlocal weapon = self.player:GetWeapon(self.class)\n\n\tif (IsValid(weapon) and weapon.ixItem == self and self:GetData(\"equip\")) then\n\t\tself:SetData(\"ammo\", weapon:Clip1())\n\tend\nend\n\nfunction ITEM:OnRemoved()\n\tlocal inventory = ix.item.inventories[self.invID]\n\tlocal owner = inventory.GetOwner and inventory:GetOwner()\n\n\tif (IsValid(owner) and owner:IsPlayer()) then\n\t\tlocal weapon = owner:GetWeapon(self.class)\n\n\t\tif (IsValid(weapon)) then\n\t\t\tweapon:Remove()\n\t\tend\n\n\t\tself:RemovePAC(owner)\n\tend\nend\n\nhook.Add(\"PlayerDeath\", \"ixStripClip\", function(client)\n\tclient.carryWeapons = {}\n\n\tfor k, _ in client:GetCharacter():GetInventory():Iter() do\n\t\tif (k.isWeapon and k:GetData(\"equip\")) then\n\t\t\tk:SetData(\"ammo\", nil)\n\t\t\tk:SetData(\"equip\", nil)\n\n\t\t\tif (k.pacData) then\n\t\t\t\tk:RemovePAC(client)\n\t\t\tend\n\t\tend\n\tend\nend)\n\nhook.Add(\"EntityRemoved\", \"ixRemoveGrenade\", function(entity)\n\t-- hack to remove hl2 grenades after they've all been thrown\n\tif (entity:GetClass() == \"weapon_frag\") then\n\t\tlocal client = entity:GetOwner()\n\n\t\tif (IsValid(client) and client:IsPlayer() and client:GetCharacter()) then\n\t\t\tlocal ammoName = game.GetAmmoName(entity:GetPrimaryAmmoType())\n\n\t\t\tif (isstring(ammoName) and ammoName:lower() == \"grenade\" and client:GetAmmoCount(ammoName) < 1\n\t\t\tand entity.ixItem and entity.ixItem.Unequip) then\n\t\t\t\tentity.ixItem:Unequip(client, false, true)\n\t\t\tend\n\t\tend\n\tend\nend)\n"
  },
  {
    "path": "gamemode/items/pacoutfit/sh_skullmask.txt",
    "content": "ITEM.name = \"Skull Mask\"\nITEM.description = \"It's a skull mask.\"\nITEM.model = \"models/Gibs/HGIBS.mdl\"\nITEM.width = 1\nITEM.height = 1\nITEM.outfitCategory = \"hat\"\nITEM.pacData = {\n\t[1] = {\n\t\t[\"children\"] = {\n\t\t\t[1] = {\n\t\t\t\t[\"children\"] = {\n\t\t\t\t},\n\t\t\t\t[\"self\"] = {\n\t\t\t\t\t[\"Angles\"] = Angle(12.919322967529, 6.5696062847564e-006, -1.0949343050015e-005),\n\t\t\t\t\t[\"Position\"] = Vector(-2.099609375, 0.019973754882813, 1.3180969238281),\n\t\t\t\t\t[\"UniqueID\"] = \"4249811628\",\n\t\t\t\t\t[\"Size\"] = 1.25,\n\t\t\t\t\t[\"Bone\"] = \"eyes\",\n\t\t\t\t\t[\"Model\"] = \"models/Gibs/HGIBS.mdl\",\n\t\t\t\t\t[\"ClassName\"] = \"model\",\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t[\"self\"] = {\n\t\t\t[\"ClassName\"] = \"group\",\n\t\t\t[\"UniqueID\"] = \"907159817\",\n\t\t\t[\"EditorExpand\"] = true,\n\t\t},\n\t},\n}"
  },
  {
    "path": "gamemode/items/sh_defaultitem.txt",
    "content": "ITEM.name = \"Test Item\"\nITEM.description = \"A test item!\"\nITEM.model = \"models/props_c17/oildrum001.mdl\""
  },
  {
    "path": "gamemode/items/weapons/sh_357.txt",
    "content": "ITEM.name = \"357\"\nITEM.description = \"A sidearm utilising .357 Caliber ammunition.\"\nITEM.model = \"models/weapons/w_357.mdl\"\nITEM.class = \"weapon_357\"\nITEM.weaponCategory = \"sidearm\"\nITEM.width = 2\nITEM.height = 1\nITEM.iconCam = {\n\tang\t= Angle(-17.581502914429, 250.7974395752, 0),\n\tfov\t= 5.412494001838,\n\tpos\t= Vector(57.109928131104, 181.7945098877, -60.738327026367)\n}\n"
  },
  {
    "path": "gamemode/items/weapons/sh_ar2.txt",
    "content": "ITEM.name = \"AR2\"\nITEM.description = \"A Weapon.\"\nITEM.model = \"models/weapons/w_IRifle.mdl\"\nITEM.class = \"weapon_ar2\"\nITEM.weaponCategory = \"primary\"\nITEM.width = 4\nITEM.height = 2\nITEM.iconCam = {\n\tang\t= Angle(-0.70499622821808, 268.25439453125, 0),\n\tfov\t= 12.085652091515,\n\tpos\t= Vector(0, 200, 0)\n}"
  },
  {
    "path": "gamemode/items/weapons/sh_crowbar.txt",
    "content": "ITEM.name = \"Crowbar\"\nITEM.description = \"A slightly rusty looking crowbar.\"\nITEM.model = \"models/weapons/w_crowbar.mdl\"\nITEM.class = \"weapon_crowbar\"\nITEM.weaponCategory = \"melee\"\nITEM.width = 2\nITEM.height = 1\nITEM.iconCam = {\n\tang\t= Angle(-0.23955784738064, 270.44906616211, 0),\n\tfov\t= 10.780103254469,\n\tpos\t= Vector(0, 200, 0)\n}\n"
  },
  {
    "path": "gamemode/items/weapons/sh_pistol.txt",
    "content": "ITEM.name = \"9MM Pistol\"\nITEM.description = \"A sidearm utilising 9mm Ammunition.\"\nITEM.model = \"models/weapons/w_pistol.mdl\"\nITEM.class = \"weapon_pistol\"\nITEM.weaponCategory = \"sidearm\"\nITEM.width = 2\nITEM.height = 1\nITEM.iconCam = {\n\tang\t= Angle(0.33879372477531, 270.15808105469, 0),\n\tfov\t= 5.0470897275697,\n\tpos\t= Vector(0, 200, -1)\n}\n"
  },
  {
    "path": "gamemode/items/weapons/sh_smg1.txt",
    "content": "ITEM.name = \"Sub Machine Gun\"\nITEM.description = \"A Weapon.\"\nITEM.model = \"models/weapons/w_smg1.mdl\"\nITEM.class = \"weapon_smg1\"\nITEM.weaponCategory = \"primary\"\nITEM.width = 3\nITEM.height = 2\nITEM.iconCam = {\n\tang\t= Angle(-0.020070368424058, 270.40155029297, 0),\n\tfov\t= 7.2253324508038,\n\tpos\t= Vector(0, 200, -1)\n}"
  },
  {
    "path": "gamemode/languages/sh_dutch.lua",
    "content": "\n-- DUTCH TRANSLATION - http://steamcommunity.com/profiles/76561198084653538/\nNAME = \"Nederlands\"\n\nLANGUAGE = {\n\tloading = \"Laden\",\n\tdbError = \"Database verbinding gefaald\",\n\tunknown = \"Onbekend\",\n\tnoDesc = \"Geen beschrijving beschikbaar\",\n\tcreate = \"Creëer\",\n\tcreateTip = \"Maak een nieuw karakter aan om mee te spelen.\",\n\tload = \"Laad\",\n\tloadTip = \"Kies een eerder gemaakte karakter om mee te spelen.\",\n\tleave = \"Verlaat\",\n\tleaveTip = \"Verlaat de huidige server.\",\n\t[\"return\"] = \"Terug\",\n\treturnTip = \"Ga terug naar het vorige menu.\",\n\tname = \"Naam\",\n\tdescription = \"Beschrijving\",\n\tmodel = \"Model\",\n\tattributes = \"Attributen\",\n\tcharCreateTip = \"Vul de velden hieronder in en druk op 'Voltooi' om je karakter te maken.\",\n\tinvalid = \"Je hebt een ongeldige %s voorzien\",\n\tdescMinLen = \"Je beschrijving moet ten minste  %d tekens zijn.\",\n\tplayer = \"Speler\",\n\tfinish = \"Voltooi\",\n\tfinishTip = \"Voltooi het maken van jouw karakter.\",\n\tneedModel = \"Je moet een geldig 'Model' kiezen.\",\n\tcreating = \"Je karakter wordt gemaakt...\",\n\tunknownError = \"Er is een onbekende fout opgetreden\",\n\tdelConfirm = \"Weet je zeker dat je %s wilt verwijderen?\",\n\tno = \"Nee\",\n\tyes = \"Ja\",\n\titemInfo = \"Naam: %s\\nBeschrijving: %s\",\n\tcloud_no_repo = \"Deze opslaagplaats is niet geldig.\",\n\tcloud_no_plugin = \"Deze plugin is niet geldig.\",\n\tinv = \"Inventaris\",\n\tplugins = \"Plugins\",\n\tauthor = \"Auteur\",\n\tversion = \"Versie\",\n\tcharacters = \"Karakters\",\n\tbusiness = \"Bedrijf\",\n\tsettings = \"Instellingen\",\n\tconfig = \"Config\",\n\tchat = \"Chat\",\n\tappearance = \"Uiterlijk\",\n\tmisc = \"Gemengd\",\n\toocDelay = \"Je moet %s seconde(n) wachten voordat je weer in OOC kan praten.\",\n\tloocDelay = \"Je moet %s seconde(n) wachten voordat je weer in LOOC kan praten.\",\n\tusingChar = \"Je bent dit karakter al aan het gebruiken.\",\n\titemNoExist = \"Sorry, het item dat je hebt opgevraagd bestaat niet.\",\n\tcmdNoExist = \"Sorry, dat commando bestaat niet.\",\n\tplyNoExist = \"Sorry, Er is geen speler gevonden met die naam.\",\n\tcfgSet = \"%s heeft \\\"%s\\\" gezet tot %s.\",\n\tdrop = \"Vallen\",\n\tdropTip = \"Dit laat deze item(s) vallen uit je inventaris.\",\n\ttake = \"Nemen\",\n\ttakeTip = \"Dit laat deze item(s) oppakken en het in je inventaris doen.\",\n\tdTitle = \"Unowned Door\",\n\tdTitleOwned = \"Purchased Door\",\n\tdIsNotOwnable = \"Deze deur kan niet gekocht worden.\",\n\tdIsOwnable = \"Je kan deze deur kopen door op 'F2' te drukken.\",\n\tdMadeUnownable = \"Je hebt deze deur niet verkoopbaar gemaakt.\",\n\tdMadeOwnable = \"Je hebt deze deur verkoopbaar gemaakt.\",\n\tdNotAllowedToOwn = \"Je mag deze deur niet bezitten.\",\n\tdSetDisabled = \"Je hebt deze deur uitgeschakeld.\",\n\tdSetNotDisabled = \"Je hebt deze deur ingeschakeld.\",\n\tdSetParentDoor = \"Je hebt deze deur als je 'Parent' deur gezet. \",\n\tdCanNotSetAsChild = \"Je kan deze 'Parent' deur niet als een 'Child' deur zetten.\",\n\tdAddChildDoor = \"Je hebt deze deur als een 'Child' toegevoegd.\",\n\tdRemoveChildren = \"Je hebt alle 'Childs' van deze deur verwijderd.\",\n\tdRemoveChildDoor = \"Je hebt de 'Child' status van deze deur verwijderd.\",\n\tdNoParentDoor = \"Je hebt nog geen 'Parent' deur ingeschakeld.\",\n\tdOwnedBy = \"%s is de eigenaar van deze deur.\",\n\tdConfigName = \"Deuren\",\n\tdSetFaction = \"Deze deur behoort nu toe aan de %s 'faction'.\",\n\tdRemoveFaction = \"Deze deur behoort niet meer toe aan een 'faction'.\",\n\tdNotValid = \"Je kijkt niet naar een geldige deur.\",\n\tcanNotAfford = \"Je kunt het je niet veroorloven om dit te kopen.\",\n\tdPurchased = \"Je hebt deze deur gekocht voor %s.\",\n\tdSold = \"Je hebt deze deur verkocht voor %s.\",\n\tnotOwner = \"Je bent niet de eigenaar van dit.\",\n\tinvalidArg = \"Je hebt een ongeldige waarde voor argument #%s.\",\n\tflagGive = \"%s heeft %s '%s' flags gegeven.\",\n\tflagTake = \"%s heeft '%s' flags van %s genomen.\",\n\tflagNoMatch = \"Je moet (de) \\\"%s\\\" Flag(s) hebben om dit te doen\",\n\ttextAdded = \"Je hebt een tekst toegevoegd.\",\n\ttextRemoved = \"Je hebt de %s tekst(en) verwijderd.\",\n\tmoneyTaken = \"Je hebt %s gevonden.\",\n\tbusinessPurchase = \"Je hebt %s gekocht voor %s.\",\n\tbusinessSell = \"Je verkocht %s voor %s.\",\n\tcChangeModel = \"%s veranderde %s's model naar %s.\",\n\tcChangeName = \"%s veranderde %s's naam naar %s.\",\n\tplayerCharBelonging = \"Dit object behoort aan je andere karakter toe.\",\n\tinvalidFaction = \"Je hebt een ongeldige 'faction' voorzien\",\n\tspawnAdd = \"Je hebt een spawn toegevoegd voor de %s 'faction'.\",\n\tspawnDeleted = \"Je hebt (een) %s spawn punt(en) verwijderd.\",\n\tsomeone = \"Iemand\",\n\trgnLookingAt = \"Toestaan dat de persoon waar je naar kijkt je herkent.\",\n\trgnWhisper = \"Toestaan dat degene(n) in het fluister gebied je herkent.\",\n\trgnTalk = \"Toestaan dat de personen in het praat gebied je herkennen.\",\n\trgnYell = \"Toestaan dat de personen in het schreeuw gebied je herkennen.\",\n\ticFormat = \"%s zegt \\\"%s\\\"\",\n\trollFormat = \"%s heeft %s uitgerold.\",\n\twFormat = \"%s fluistert \\\"%s\\\"\",\n\tyFormat = \"%s schreeuwt \\\"%s\\\"\",\n\tsbOptions = \"Klik om opties over %s te zien.\",\n\tspawnAdded = \"Je hebt een spawn toegevoegd voor %s.\",\n\twhitelist = \"%s heeft %s gewhitelist voor de %s 'faction'.\",\n\tunwhitelist = \"%s heeft %s geblacklist van de %s 'faction'.\",\n\tgettingUp = \"Je staat nu op...\",\n\twakingUp = \"Je krijgt je bewustzijn terug...\",\n\tWeapons = \"Wapens\",\n\tcheckout = \"Ga naar checkout (%s)\",\n\tpurchase = \"Koop\",\n\tpurchasing = \"Aan het kopen...\",\n\tsuccess = \"Succes\",\n\tbuyFailed = \"De koop is gefaald!\",\n\tbuyGood = \"Succesvol gekocht!\",\n\tshipment = \"Lading\",\n\tshipmentDesc = \"Deze lading behoort toe aan %s.\",\n\tclass = \"Class\",\n\tclasses = \"Classes\",\n\tillegalAccess = \"Illegale toegang\",\n\tbecomeClassFail = \"Het is niet gelukt om %s te worden.\",\n\tbecomeClass = \"Je bent %s geworden.\",\n\tattributeSet = \"You set %s's %s to %s.\",\n\tattributeUpdate = \"You added %s's %s by %s.\",\n\tnoFit = \"Deze item(s) passen niet in je inventaris\",\n\thelp = \"Hulp\",\n\tcommands = \"Commando's\",\n\thelpDefault = \"Selecteer een categorie\",\n\tdoorSettings = \"Deur Instellingen\",\n\tsell = \"Verkoop\",\n\taccess = \"Toegang\",\n\tlocking = \"Bezig deze 'Entity' opslot te zetten ...\",\n\tunlocking = \"Bezig deze 'Entity' van het slot te zetten ...\",\n\tmodelNoSeq = \"Je model ondersteunt deze 'Act' niet.\",\n\tnotNow = \"Je kan dat nu niet doen.\",\n\tfaceWall = \"Je moet de muur aankijken om dit te doen.\",\n\tfaceWallBack = \"Je moet met je rug tegen de muur staan om dit te doen\",\n\tdescChanged = \"Je hebt je karakter's beschrijving veranderd.\",\n\tcharMoney = \"Je hebt momenteel %s.\",\n\tcharFaction = \"Je bent een lid van de %s 'faction'.\",\n\tcharClass = \"Je bent %s van de 'faction'.\",\n\tnoOwner = \"De eigenaar is niet geldig.\",\n\tnotAllowed = \"Dit is niet toegestaan.\",\n\tinvalidIndex = \"De Item Index is Ongeldig.\",\n\tinvalidItem = \"De Item Object is Ongeldig.\",\n\tinvalidInventory = \"Het inventaris object is ongeldig.\",\n\thome = \"Home\",\n\tcharKick = \"%s kickde karakter %s.\",\n\tcharBan = \"%s heeft karakter %s verbannen.\",\n\tcharBanned = \"Deze karakter is verbannen.\",\n\tsetMoney = \"Je hebt %s's geld tot %s gezet.\"\n}\n"
  },
  {
    "path": "gamemode/languages/sh_english.lua",
    "content": "\nNAME = \"English\"\n\nLANGUAGE = {\n\thelix = \"Helix\",\n\n\tintroTextOne = \"fist industries presents\",\n\tintroTextTwo = \"in collaboration with %s\",\n\tintroContinue = \"press space to continue\",\n\n\thelpIdle = \"Select a category\",\n\thelpCommands = \"Command parameters with <arrows> are required, while [brackets] are optional.\",\n\thelpFlags = \"Flags with a green background are accessible by this character.\",\n\n\tcreditSpecial = \"Special Thanks\",\n\tcreditLeadDeveloper = \"Lead Developer\",\n\tcreditUIDesigner = \"UI Designer\",\n\tcreditManager = \"Project Manager\",\n\tcreditTester = \"Lead Tester\",\n\n\tchatTyping = \"Typing...\",\n\tchatTalking = \"Talking...\",\n\tchatYelling = \"Yelling...\",\n\tchatWhispering = \"Whispering...\",\n\tchatPerforming = \"Performing...\",\n\tchatNewTab = \"New tab...\",\n\tchatReset = \"Reset position\",\n\tchatResetTabs = \"Reset tabs\",\n\tchatCustomize = \"Customize...\",\n\tchatCloseTab = \"Close Tab\",\n\tchatTabName = \"Tab Name\",\n\tchatNewTabTitle = \"New Tab\",\n\tchatAllowedClasses = \"Allowed Chat Classes\",\n\tchatTabExists = \"A chat tab with that name already exists!\",\n\tchatMarkRead = \"Mark all as read\",\n\n\tcommunity = \"Community\",\n\tcheckAll = \"Check All\",\n\tuncheckAll = \"Uncheck All\",\n\tcolor = \"Color\",\n\ttype = \"Type\",\n\tdisplay = \"Display\",\n\tloading = \"Loading\",\n\tdbError = \"Database connection failed\",\n\tunknown = \"Unknown\",\n\tnoDesc = \"No description available\",\n\tcreate = \"Create\",\n\tupdate = \"Update\",\n\tload = \"Load character\",\n\tloadTitle = \"Load a Character\",\n\tleave = \"Leave server\",\n\tleaveTip = \"Leave the current server.\",\n\t[\"return\"] = \"Return\",\n\treturnTip = \"Return to the previous menu.\",\n\tproceed = \"Proceed\",\n\tfaction = \"Faction\",\n\tskills = \"Skills\",\n\tchoose = \"Choose\",\n\tchooseFaction = \"Choose a Faction\",\n\tchooseDescription = \"Define your Narrative\",\n\tchooseSkills = \"Hone your Skills\",\n\tname = \"Name\",\n\tdescription = \"Description\",\n\tmodel = \"Model\",\n\tattributes = \"Attributes\",\n\tattribPointsLeft = \"Points left\",\n\tcharInfo = \"Character Information\",\n\tcharCreated = \"You have successfully created your character.\",\n\tcharCreateTip = \"Fill in the fields below and press 'Finish' to create your character.\",\n\tinvalid = \"You have provided an invalid %s!\",\n\tnameMinLen = \"Your name must be at least %d characters!\",\n\tnameMaxLen = \"Your name must not be more than %d characters!\",\n\tdescMinLen = \"Your description must be at least %d characters!\",\n\tmaxCharacters = \"You cannot create any more characters!\",\n\tplayer = \"Player\",\n\tfinish = \"Finish\",\n\tfinishTip = \"Finish creating the character.\",\n\tneedModel = \"You need to choose a valid model!\",\n\tcreating = \"Your character is being created...\",\n\tunknownError = \"An unknown error has occured!\",\n\tareYouSure = \"Are you sure?\",\n\tdelete = \"Delete\",\n\tdeleteConfirm = \"This character will be irrevocably removed!\",\n\tdeleteComplete = \"%s has been deleted.\",\n\tno = \"No\",\n\tyes = \"Yes\",\n\tclose = \"Close\",\n\tsave = \"Save\",\n\titemInfo = \"Name: %s\\nDescription: %s\",\n\titemCreated = \"Item(s) successfully created.\",\n\tcloud_no_repo = \"The repository provided is not valid!\",\n\tcloud_no_plugin = \"The plugin provided is not valid!\",\n\tinv = \"Inventory\",\n\tplugins = \"Plugins\",\n\tpluginLoaded = \"%s has enabled the \\\"%s\\\" plugin to load next map change.\",\n\tpluginUnloaded = \"%s has disabled the \\\"%s\\\" plugin from loading next map change.\",\n\tloadedPlugins = \"Loaded Plugins\",\n\tunloadedPlugins = \"Unloaded Plugins\",\n\ton = \"On\",\n\toff = \"Off\",\n\tauthor = \"Author\",\n\tversion = \"Version\",\n\tcharacters = \"Characters\",\n\tbusiness = \"Business\",\n\tsettings = \"Settings\",\n\tconfig = \"Config\",\n\tchat = \"Chat\",\n\tappearance = \"Appearance\",\n\tmisc = \"Miscellaneous\",\n\toocDelay = \"You must wait %s more seconds before using OOC again!\",\n\tloocDelay = \"You must wait %s more seconds before using LOOC again!\",\n\tusingChar = \"You are already using this character.\",\n\tnotAllowed = \"You are not allowed to do this!\",\n\titemNoExist = \"The item that you requested does not exist!\",\n\tcmdNoExist = \"That command does not exist!\",\n\tcharNoExist = \"A matching character could not be found!\",\n\tplyNoExist = \"A matching player could not be found!\",\n\tcfgSet = \"%s has set \\\"%s\\\" to %s.\",\n\tdrop = \"Drop\",\n\tdropTip = \"Drops this item from your inventory.\",\n\ttake = \"Take\",\n\ttakeTip = \"Take this item and place it in your inventory.\",\n\tdTitle = \"Unowned Door\",\n\tdTitleOwned = \"Purchased Door\",\n\tdIsNotOwnable = \"This door is unownable.\",\n\tdIsOwnable = \"You can purchase this door by pressing F2.\",\n\tdMadeUnownable = \"You have made this door unownable.\",\n\tdMadeOwnable = \"You have made this door ownable.\",\n\tdNotAllowedToOwn = \"You are not allowed to own this door!\",\n\tdSetDisabled = \"You have made this door disabled.\",\n\tdSetNotDisabled = \"You have made this door no longer disabled.\",\n\tdSetHidden = \"You have made this door hidden.\",\n\tdSetNotHidden = \"You have made this door no longer hidden.\",\n\tdSetParentDoor = \"You have set this door as your parent door.\",\n\tdCanNotSetAsChild = \"You can not set the parent door as a child!\",\n\tdAddChildDoor = \"You have added a this door as a child.\",\n\tdRemoveChildren = \"You have removed all of the children for this door.\",\n\tdRemoveChildDoor = \"You have removed this door from being a child.\",\n\tdNoParentDoor = \"You do not have a parent door set.\",\n\tdOwnedBy = \"This door is owned by %s.\",\n\tdConfigName = \"Doors\",\n\tdSetFaction = \"This door now belongs to the %s faction.\",\n\tdRemoveFaction = \"This door no longer belongs to any faction.\",\n\tdNotValid = \"You are not looking at a valid door!\",\n\tcanNotAfford = \"You can not afford to purchase this!\",\n\tdPurchased = \"You have purchased this door for %s.\",\n\tdSold = \"You have sold this door for %s.\",\n\tnotOwner = \"You are not the owner of this!\",\n\tinvalidArg = \"You have provided an invalid value for argument #%s!\",\n\tinvalidFaction = \"The faction you provided could not be found!\",\n\tflagGive = \"%s has given %s '%s' flags.\",\n\tflagGiveTitle = \"Give Flags\",\n\tflagTake = \"%s has taken '%s' flags from %s.\",\n\tflagTakeTitle = \"Take Flags\",\n\tflagNoMatch = \"You must have \\\"%s\\\" flag(s) to do this action!\",\n\tpanelAdded = \"You have added a panel.\",\n\tpanelRemoved = \"You have removed %d panel(s).\",\n\ttextAdded = \"You have added a text.\",\n\ttextRemoved = \"You have removed %s text(s).\",\n\tmoneyTaken = \"You were given %s.\",\n\tmoneyGiven = \"You have given %s.\",\n\tinsufficientMoney = \"You do not have enough to do this!\",\n\tbelowMinMoneyDrop = \"You cannot drop less than %s.\",\n\tbusinessPurchase = \"You purchased %s for %s.\",\n\tbusinessSell = \"You sold %s for %s.\",\n\tbusinessTooFast = \"Please wait before purchasing another item!\",\n\tcChangeModel = \"%s changed %s's model to %s.\",\n\tcChangeName = \"%s changed %s's name to %s.\",\n\tcChangeSkin = \"%s changed %s's skin to %s.\",\n\tcChangeGroups = \"%s changed %s's \\\"%s\\\" bodygroup to %s.\",\n\tcChangeFaction = \"%s has transferred %s to the %s faction.\",\n\tplayerCharBelonging = \"This object is your other character's belonging!\",\n\tspawnAdd = \"You have added a spawn for the %s.\",\n\tspawnDeleted = \"You have removed %s spawn point(s).\",\n\tsomeone = \"Someone\",\n\trgnLookingAt = \"Allow the person you are looking at to recognize you.\",\n\trgnWhisper = \"Allow those in a whispering range to recognize you.\",\n\trgnTalk = \"Allow those in a talking range to recognize you.\",\n\trgnYell = \"Allow those in a yelling range to recognize you.\",\n\ticFormat = \"%s says \\\"%s\\\"\",\n\trollFormat = \"%s has rolled %s out of %s.\",\n\twFormat = \"%s whispers \\\"%s\\\"\",\n\tyFormat = \"%s yells \\\"%s\\\"\",\n\tsbOptions = \"Click to see options for %s.\",\n\tspawnAdded = \"You added spawn for %s.\",\n\twhitelist = \"%s has whitelisted %s for the %s faction.\",\n\tunwhitelist = \"%s has unwhitelisted %s from the %s faction.\",\n\tnoWhitelist = \"You do not have the whitelist for this character!\",\n\tcharNotWhitelisted = \"%s is not whitelisted for the %s faction.\",\n\tgettingUp = \"You are now getting up...\",\n\twakingUp = \"You are regaining consciousness...\",\n\tWeapons = \"Weapons\",\n\tcheckout = \"Go to Checkout (%s)\",\n\tpurchase = \"Purchase\",\n\tpurchasing = \"Purchasing...\",\n\tsuccess = \"Success\",\n\tbuyFailed = \"Purchase failed!\",\n\tbuyGood = \"Purchase successful!\",\n\tshipment = \"Shipment\",\n\tshipmentDesc = \"This shipment belongs to %s.\",\n\tclass = \"Class\",\n\tclasses = \"Classes\",\n\tillegalAccess = \"Illegal Access.\",\n\tbecomeClassFail = \"You cannot become a %s!\",\n\tbecomeClass = \"You have become a %s.\",\n\tsetClass = \"You have set %s's class to %s.\",\n\tattributeSet = \"You set %s's %s to %s.\",\n\tattributeNotFound = \"You have specified an invalid attribute!\",\n\tattributeUpdate = \"You added %s's %s by %s.\",\n\tnoFit = \"You do not have enough space for this item!\",\n\titemOwned = \"You cannot interact with an item that you own on a different character!\",\n\thelp = \"Help\",\n\tcommands = \"Commands\",\n\tdoorSettings = \"Door Settings\",\n\tsell = \"Sell\",\n\taccess = \"Access\",\n\tlocking = \"Locking this entity...\",\n\tunlocking = \"Unlocking this entity...\",\n\tmodelNoSeq = \"Your model does not support this act!\",\n\tnotNow = \"You are not allowed to do this right now!\",\n\tfaceWall = \"You must be facing the wall to do this!\",\n\tfaceWallBack = \"Your back must be facing the wall to do this!\",\n\tdescChanged = \"You have changed your character's description.\",\n\tnoOwner = \"The owner is invalid!\",\n\tinvalidItem = \"You have specified an invalid item!\",\n\tinvalidInventory = \"You have specified an invalid inventory!\",\n\thome = \"Home\",\n\tcharKick = \"%s kicked char %s.\",\n\tcharBan = \"%s banned the character %s.\",\n\tcharBanned = \"This character is banned.\",\n\tcharBannedTemp = \"This character is temporarily banned.\",\n\tplayerConnected = \"%s has connected to the server.\",\n\tplayerDisconnected = \"%s has disconnected from the server.\",\n\tsetMoney = \"You have set %s's money to %s.\",\n\titemPriceInfo = \"You can purchase this item for %s.\\nYou can sell this item for %s\",\n\tfree = \"Free\",\n\tvendorNoSellItems = \"There are no items to sell.\",\n\tvendorNoBuyItems = \"There are no items to purchase.\",\n\tvendorSettings = \"Vendor Settings\",\n\tvendorUseMoney = \"Vendor should use money?\",\n\tvendorNoBubble = \"Hide vendor bubble?\",\n\tcategory = \"Category\",\n\tmode = \"Mode\",\n\tprice = \"Price\",\n\tstock = \"Stock\",\n\tnone = \"None\",\n\tvendorBoth = \"Buy and Sell\",\n\tvendorBuy = \"Buy Only\",\n\tvendorSell = \"Sell Only\",\n\tmaxStock = \"Max Stock\",\n\tvendorFaction = \"Faction Editor\",\n\tbuy = \"Purchase\",\n\tvendorWelcome = \"Welcome to my store, what can I get you today?\",\n\tvendorBye = \"Come again soon!\",\n\tcharSearching = \"You are already searching another character!\",\n\tcharUnBan = \"%s has unbanned the character %s.\",\n\tcharNotBanned = \"This character is not banned!\",\n\tquickSettings = \"Quick Settings\",\n\tvmSet = \"You have set your voicemail.\",\n\tvmRem = \"You have removed your voicemail.\",\n\tnoPerm = \"You are not allowed to do this!\",\n\tyoureDead = \"You are Dead\",\n\tinjMajor = \"Seems critically injured\",\n\tinjLittle = \"Seems injured\",\n\tchgName = \"Change Name\",\n\tchgNameDesc = \"Enter the character's new name below.\",\n\tweaponSlotFilled = \"You cannot use another %s weapon!\",\n\tequippedBag = \"You cannot move a bag that has an equipped item!\",\n\tequippedWeapon = \"You cannot move a weapon that is currently equipped!\",\n\tnestedBags = \"You cannot put an inventory inside of a storage inventory!\",\n\toutfitAlreadyEquipped = \"You're already wearing this kind of outfit!\",\n\tuseTip = \"Uses the item.\",\n\tequip = \"Equip\",\n\tequipTip = \"Equips the item.\",\n\tunequip = \"Unequip\",\n\tunequipTip = \"Unequips the item.\",\n\tconsumables = \"Consumables\",\n\tplyNotValid = \"You are not looking at a valid player!\",\n\trestricted = \"You have been restrained.\",\n\tsalary = \"You have received %s from your salary.\",\n\tnoRecog = \"You do not recognize this person.\",\n\tcurTime = \"The current time is %s.\",\n\tvendorEditor = \"Vendor Editor\",\n\tedit = \"Edit\",\n\tdisable = \"Disable\",\n\tvendorPriceReq = \"Enter the new price for this item.\",\n\tvendorEditCurStock = \"Edit Current Stock\",\n\tvendorStockReq = \"Enter the Maximum amount of Stock the item should have.\",\n\tvendorStockCurReq = \"Enter how many items are available for purchase from the maximum stock.\",\n\tyou = \"You\",\n\tvendorSellScale = \"Sell price scale\",\n\tvendorNoTrade = \"You are not able to trade with this vendor!\",\n\tvendorNoMoney = \"This vendor can not afford that item!\",\n\tvendorNoStock = \"This vendor does not have that item in stock!\",\n\tvendorMaxStock = \"This vendor has full stock of that item!\",\n\tcontentTitle = \"Helix Content Missing\",\n\tcontentWarning = \"You do not have the Helix content mounted. This may result in certain features missing.\\nWould you like to open the Workshop page for the Helix content?\",\n\tflags = \"Flags\",\n\tmapRestarting = \"The map will restart in %d seconds!\",\n\tchooseTip = \"Choose this character to play with.\",\n\tdeleteTip = \"Delete this character.\",\n\tstorageInUse = \"Someone else is using this!\",\n\tstorageSearching = \"Searching...\",\n\tcontainer = \"Container\",\n\tcontainerPassword = \"You have set this container's password to %s.\",\n\tcontainerPasswordRemove = \"You have removed this container's password.\",\n\tcontainerPasswordWrite = \"Enter the password.\",\n\tcontainerName = \"You have set this container's name to %s.\",\n\tcontainerNameWrite = \"Enter the name.\",\n\tcontainerNameRemove = \"You have removed this container's name.\",\n\tcontainerInvalid = \"You need to be looking at a container to do this!\",\n\twrongPassword = \"You have entered an incorrect password!\",\n\tpasswordAttemptLimit = \"You have made too many incorrect password attempts!\",\n\trespawning = \"Respawning...\",\n\ttellAdmin = \"Inform a staff member of this error: %s\",\n\tsyntax = \"Syntax: %s\",\n\n\tmapAdd = \"You have added a map scene.\",\n\tmapDel = \"You have removed %d map scene(s).\",\n\tmapRepeat = \"Now add the secondary point.\",\n\n\tscoreboard = \"Scoreboard\",\n\tping = \"Ping: %d\",\n\tviewProfile = \"View Steam Profile\",\n\tcopySteamID = \"Copy Steam ID\",\n\n\tmoney = \"Money\",\n\tmoneyLeft = \"Your Money: \",\n\tcurrentMoney = \"Money Left: \",\n\n\tinvalidClass = \"That is not a valid class!\",\n\tinvalidClassFaction = \"That is not a valid class for that faction!\",\n\n\tmiscellaneous = \"Miscellaneous\",\n\tgeneral = \"General\",\n\tobserver = \"Observer\",\n\tperformance = \"Performance\",\n\tthirdperson = \"Third Person\",\n\tdate = \"Date\",\n\tinteraction = \"Interaction\",\n\tserver = \"Server\",\n\n\tresetDefault = \"Reset to default\",\n\tresetDefaultDescription = \"This will reset \\\"%s\\\" to its default value of \\\"%s\\\".\",\n\toptOpenBags = \"Open bags with inventory\",\n\toptdOpenBags = \"Automatically views all the bags in your inventory when the menu is opened.\",\n\toptShowIntro = \"Show intro on join\",\n\toptdShowIntro = \"Shows the Helix introduction the next time you join. This is always disabled after you've watched it.\",\n\toptCheapBlur = \"Disable blurring\",\n\toptdCheapBlur = \"Replaces UI blurring with simple dimming.\",\n\toptObserverTeleportBack = \"Return to previous location\",\n\toptdObserverTeleportBack = \"Returns you to the location that you entered observer mode.\",\n\toptObserverESP = \"Show admin ESP\",\n\toptdObserverESP = \"Shows the names and locations of each player in the server.\",\n\topt24hourTime = \"Use 24-hour time\",\n\toptd24hourTime = \"Show timestamps in 24-hour time, rather than 12-hour time (AM/PM).\",\n\toptChatNotices = \"Show notices in chat\",\n\toptdChatNotices = \"Puts all notices that appear in the top-right into the chat instead.\",\n\toptChatTimestamps = \"Show timestamps in chat\",\n\toptdChatTimestamps = \"Prepends the time to each message in the chatbox.\",\n\toptAlwaysShowBars = \"Always show info bars\",\n\toptdAlwaysShowBars = \"Draws the information bars in the top-left, regardless if it should be hidden or not.\",\n\toptAltLower = \"Hide hands when lowered\", -- @todo remove me\n\toptdAltLower = \"Hides your hands when they are by your side.\", -- @todo remove me\n\toptThirdpersonEnabled = \"Enable third person\",\n\toptdThirdpersonEnabled = \"Moves the camera behind you. This can also be enabled with the \\\"ix_togglethirdperson\\\" console command.\",\n\toptThirdpersonClassic = \"Enable classic third person camera\",\n\toptdThirdpersonClassic = \"Moves your character's view with your mouse.\",\n\toptThirdpersonVertical = \"Camera height\",\n\toptdThirdpersonVertical = \"How high up the camera should be.\",\n\toptThirdpersonHorizontal = \"Camera offset\",\n\toptdThirdpersonHorizontal = \"How far left or right the camera should be.\",\n\toptThirdpersonDistance = \"Camera distance\",\n\toptdThirdpersonDistance = \"How far away the camera should be.\",\n\toptThirdpersonCrouchOffset = \"Camera crouch height\",\n\toptdThirdpersonCrouchOffset = \"How high up the camera should be while crouched.\",\n\toptDisableAnimations = \"Disable animations\",\n\toptdDisableAnimations = \"Stops animations from playing to provide instant transitions.\",\n\toptAnimationScale = \"Animation scale\",\n\toptdAnimationScale = \"How much faster or slower the animations should play.\",\n\toptLanguage = \"Language\",\n\toptdLanguage = \"The language shown in the Helix UI.\",\n\toptMinimalTooltips = \"Minimal HUD tooltips\",\n\toptdMinimalTooltips = \"Changes the HUD tooltip style to take up less space.\",\n\toptNoticeDuration = \"Notice duration\",\n\toptdNoticeDuration = \"How long to show notices for (in seconds).\",\n\toptNoticeMax = \"Maximum notices\",\n\toptdNoticeMax = \"The amount of notices shown before excess previous ones are removed.\",\n\toptChatFontScale = \"Chat font scale\",\n\toptdChatFontScale = \"How much bigger or smaller the chat font should be.\",\n\toptChatOutline = \"Outline chat text\",\n\toptdChatOutline = \"Draws an outline around the chat text, rather than a drop shadow. Enable this if you are having trouble reading text.\",\n\toptEscCloseMenu = \"Escape returns to game\",\n\toptdEscCloseMenu = \"Whether the escape menu should close itself when you're using it to close the in-game menu.\",\n\n\tcmdRoll = \"Rolls a number between 0 and the specified number.\",\n\tcmdPM = \"Sends a private message to someone.\",\n\tcmdReply = \"Sends a private message to the last person you received a message from.\",\n\tcmdSetVoicemail = \"Sets or removes the auto-reply message when someone sends you a private message.\",\n\tcmdCharGiveFlag = \"Gives the specified flag(s) to someone.\",\n\tcmdCharTakeFlag = \"Removes the specified flag(s) from someone if they have it.\",\n\tcmdToggleRaise = \"Raises or lowers the weapon you're holding.\",\n\tcmdCharSetModel = \"Sets a person's character model.\",\n\tcmdCharSetSkin = \"Sets the skin for a person's character model.\",\n\tcmdCharSetBodygroup = \"Sets the bodygroup for a person's character model.\",\n\tcmdCharSetAttribute = \"Sets the level of the specified attribute for someone.\",\n\tcmdCharAddAttribute = \"Adds a level to the specified attribute for someone.\",\n\tcmdCharSetName = \"Changes someone's name to the specified name.\",\n\tcmdCharGiveItem = \"Gives the specified item to someone.\",\n\tcmdCharKick = \"Forcefully makes someone log off of their character.\",\n\tcmdCharBan = \"Prohibits someone from logging into the server with their current character.\",\n\tcmdCharUnban = \"Allows a prohibited character the ability to be used again.\",\n\tcmdGiveMoney = \"Gives a specified amount of money to the person you are looking at.\",\n\tcmdCharSetMoney = \"Changes the total amount of money of someone to the amount specified.\",\n\tcmdDropMoney = \"Drops the specified amount of money in a little box in front of you.\",\n\tcmdPlyWhitelist = \"Allows someone to create a character in the specified faction.\",\n\tcmdCharGetUp = \"Attempts to stand back up after having fallen over.\",\n\tcmdPlyUnwhitelist = \"Disallows someone to create a character in the specified faction.\",\n\tcmdCharFallOver = \"Makes your knees weak and fall over.\",\n\tcmdBecomeClass = \"Attempts to become part of the specified class in your current faction.\",\n\tcmdCharDesc = \"Sets your physical description.\",\n\tcmdCharDescTitle = \"Physical Description\",\n\tcmdCharDescDescription = \"Enter your character's physical description.\",\n\tcmdPlyTransfer = \"Transfers someone to the specified faction.\",\n\tcmdCharSetClass = \"Forcefully makes someone become part of the specified class in their current faction.\",\n\tcmdMapRestart = \"Restarts the map after the specified amount of time.\",\n\tcmdPanelAdd = \"Places a web panel in the world.\",\n\tcmdPanelRemove = \"Removes a web panel that you are looking at.\",\n\tcmdTextAdd = \"Places a block of text in the world.\",\n\tcmdTextRemove = \"Removes blocks of text that you are looking at.\",\n\tcmdMapSceneAdd = \"Adds a cinematic camera viewpoint that is shown in the character menu.\",\n\tcmdMapSceneRemove = \"Removes a camera viewpoint that is shown in the character menu.\",\n\tcmdSpawnAdd = \"Adds a spawn point for the specified faction.\",\n\tcmdSpawnRemove = \"Removes any spawn points you are looking at.\",\n\tcmdAct = \"Performs the %s animation.\",\n\tcmdContainerSetPassword = \"Sets the password of the container you're looking at.\",\n\tcmdDoorSell = \"Sells the door you are looking at.\",\n\tcmdDoorBuy = \"Buys the door you are looking at.\",\n\tcmdDoorSetUnownable = \"Makes the door you are looking at unownable.\",\n\tcmdDoorSetOwnable = \"Makes the door you are looking at ownable.\",\n\tcmdDoorSetFaction = \"Makes the door you are looking at owned by the specified faction.\",\n\tcmdDoorSetDisabled = \"Disallows any commands to be ran on the door you are looking at.\",\n\tcmdDoorSetTitle = \"Sets the title of the door you are looking at.\",\n\tcmdDoorSetParent = \"Sets the parent of a pair of doors.\",\n\tcmdDoorSetChild = \"Sets the child of a pair of doors.\",\n\tcmdDoorRemoveChild = \"Removes the child from a pair of doors.\",\n\tcmdDoorSetHidden = \"Hides the description of the door you are looking at, but still allows it to be ownable.\",\n\tcmdDoorSetClass = \"Makes the door you are looking at owned by the specified class.\",\n\tcmdMe = \"Perform a physical action.\",\n\tcmdIt = \"Make something around you perform an action.\",\n\tcmdW = \"Whisper something to the people near you.\",\n\tcmdY = \"Yell something to the people around you.\",\n\tcmdEvent = \"Make something perform an action that everyone can see.\",\n\tcmdOOC = \"Sends a message in global out-of-character chat.\",\n\tcmdLOOC = \"Sends a message in local out-of-character chat.\",\n\n\ticonEditorAlignBest = \"Best Guess\",\n\ticonEditorWidth = \"Width\",\n\ticonEditorHeight = \"Height\",\n\ticonEditorCopy = \"Copy to Clipboard\",\n\ticonEditorCopied = \"Copied Item Model Data to Clipboard.\",\n\ticonEditorAlignFront = \"Align from Front\",\n\ticonEditorAlignAbove = \"Align from Above\",\n\ticonEditorAlignRight = \"Align from Right\",\n\ticonEditorAlignCenter = \"Align from Center\"\n}\n"
  },
  {
    "path": "gamemode/languages/sh_french.lua",
    "content": "\n-- FRENCH TRANSLATION\n-- http://steamcommunity.com/id/sadness81\n-- https://steamcommunity.com/id/Azphal\n\nNAME = \"Français\"\n\nLANGUAGE = {\n\thelix = \"Helix\",\n\n\tintroTextOne = \"fist industrie vous presente\",\n\tintroTextTwo = \"en collaboration avec %s\",\n\tintroContinue = \"Appuyez sur ESPACE pour continuer\",\n\n\thelpIdle = \"Selectionner une catégorie\",\n\thelpCommands = \"Les paramètres de commande avec les <flèches> sont obligatoires, tandis que les [crochets] sont facultatifs.\",\n\thelpFlags = \"Les drapeaux sur fond vert sont accessibles à ce personnage.\",\n\n\tcreditSpecial = \"Remerciement spécial\",\n\tcreditLeadDeveloper = \"Développeur principal\",\n\tcreditUIDesigner = \"Concepteur d'interface utilisateur\",\n\tcreditManager = \"Chef de projet\",\n\tcreditTester = \"Testeur en chef\",\n\n\tchatTyping = \"Écrit...\",\n\tchatTalking = \"Parle...\",\n\tchatYelling = \"Hurle...\",\n\tchatWhispering = \"Chuchote...\",\n\tchatPerforming = \"Fait une action...\",\n\tchatNewTab = \"Nouvel onglet...\",\n\tchatReset = \"Réinitialise les discussions\",\n\tchatResetTabs = \"Réinitialiser les onglets\",\n\tchatCustomize = \"Personnalise...\",\n\tchatCloseTab = \"Ferme les onglets\",\n\tchatTabName = \"Nom de l'onglet\",\n\tchatAllowedClasses = \"Types de chat acceptés\",\n\tchatTabExists = \"Un onglet de discussion portant ce nom existe déjà !\",\n\tchatMarkRead = \"Tout marquer comme lu\",\n\n\tcommunity = \"Communauté\",\n\tcheckAll = \"Cocher tout\",\n\tuncheckAll = \"Décocher tout\",\n\tcolor = \"Couleur\",\n\ttype = \"Type\",\n\tdisplay = \"Afficher\",\n\tloading = \"Chargement\",\n\tdbError = \"La connexion à la base de données a échouée\",\n\tunknown = \"Inconnu\",\n\tnoDesc = \"Aucune description disponible\",\n\tcreate = \"Créer\",\n\tupdate = \"Mettre à jour\",\n\tload = \"Charger le personnage\",\n\tloadTitle = \"Charger un personnage\",\n\tleave = \"Quitter le serveur\",\n\tleaveTip = \"Quitter le serveur actuel.\",\n\t[\"return\"] = \"Revenir\",\n\treturnTip = \"Retourner au menu précédent..\",\n\tproceed = \"Procéder\",\n\tfaction = \"Faction\",\n\tskills = \"Compétences\",\n\tchoose = \"Choisir\",\n\tchooseFaction = \"Choisir une faction\",\n\tchooseDescription = \"Définissez votre récit\",\n\tchooseSkills = \"Affûtez vos compétences\",\n\tname = \"Nom\",\n\tdescription = \"Description\",\n\tmodel = \"Modèle\",\n\tattributes = \"Attribut\",\n\tattribPointsLeft = \"Points restant\",\n\tcharInfo = \"Information du personnage\",\n\tcharCreated = \"Vous avez créé votre personnage avec succès.\",\n\tcharCreateTip = \"Remplissez les champs ci-dessous et appuyez sur 'Terminée' pour créer votre personnage.\",\n\tinvalid = \"Vous avez fourni un invalide %s !\",\n\tnameMinLen = \"Votre nom doit contenir au moins %d caractères !\",\n\tnameMaxLen = \"Votre nom ne doit pas dépasser %d caractères !\",\n\tdescMinLen = \"Votre description doit contenir au moins %d caractères !\",\n\tmaxCharacters = \"Vous ne pouvez pas créer plus de personnage !\",\n\tplayer = \"Joueur\",\n\tfinish = \"Terminée\",\n\tfinishTip = \"Terminez la création du personnage.\",\n\tneedModel = \"Vous devez choisir un modèle valide !\",\n\tcreating = \"Votre personnage est en train d'être créé...\",\n\tunknownError = \"Une erreur inconnue s'est produite !\",\n\tareYouSure = \"Êtes-vous sûr ?\",\n\tdelete = \"Supprimer\",\n\tdeleteConfirm = \"Ce personnage sera irrévocablement supprimé !\",\n\tdeleteComplete = \"%s a été supprimé.\",\n\tno = \"Non\",\n\tyes = \"Oui\",\n\tclose = \"Fermer\",\n\tsave = \"Sauvegarder\",\n\titemInfo = \"Nom: %s\\nDescription: %s\",\n\titemCreated = \"Élément(s) créé(s) avec succès.\",\n\tcloud_no_repo = \"Le référentiel fourni n'est pas valide !\",\n\tcloud_no_plugin = \"Le plugin fourni n'est pas valide !\",\n\tinv = \"Inventaire\",\n\tplugins = \"Plugins\",\n\tauthor = \"Auteur\",\n\tversion = \"Version\",\n\tcharacters = \"Personnages\",\n\tbusiness = \"Marché\",\n\tsettings = \"Options\",\n\tconfig = \"Configuration\",\n\tchat = \"Chat\",\n\tappearance = \"Apparence\",\n\tmisc = \"Divers\",\n\toocDelay = \"Vous devez attendre %s seconde(s) avant de réutiliser le OOC !\",\n\tloocDelay = \"Vous devez attendre %s seconde(s) avant de réutiliser le LOOC !\",\n\tusingChar = \"Vous utilisez déjà ce personnage.\",\n\tnotAllowed = \"Vous n'êtes pas autorisé à le faire !\",\n\titemNoExist = \"L'article que vous avez demandé n'existe pas !\",\n\tcmdNoExist = \"Cette commande n'existe pas !\",\n\tcharNoExist = \"Un personnage correspondant n'a pas pu être trouvé !\",\n\tplyNoExist = \"Impossible de trouver un joueur correspondant !\",\n\tcfgSet = \"%s a mis \\\"%s\\\" à %s.\",\n\tdrop = \"Lâcher\",\n\tdropTip = \"Lâcher cet objet de votre inventaire.\",\n\ttake = \"Prendre\",\n\ttakeTip = \"Prendre cet objet et le placer dans votre inventaire.\",\n\tdTitle = \"Porte sans propriétaire\",\n\tdTitleOwned = \"Porte achetée\",\n\tdIsNotOwnable = \"Cette porte ne peut être achetée.\",\n\tdIsOwnable = \"Vous pouvez acheter cette porte en appuyant sur F2.\",\n\tdMadeUnownable = \"Vous avez rendu cette porte inachetable.\",\n\tdMadeOwnable = \"Vous avez rendu cette porte achetable.\",\n\tdNotAllowedToOwn = \"Vous n'êtes pas autorisé à posséder cette porte !\",\n\tdSetDisabled = \"Vous avez désactivé cette porte.\",\n\tdSetNotDisabled = \"Vous avez réactivé cette porte.\",\n\tdSetHidden = \"Vous avez caché cette porte.\",\n\tdSetNotHidden = \"Vous avez montré cette porte.\",\n\tdSetParentDoor = \"Vous avez défini cette porte comme porte parent.\",\n\tdCanNotSetAsChild = \"Vous ne pouvez pas définir une porte héritière alors qu'elle est parente !\",\n\tdAddChildDoor = \"Vous avez défini cette porte héritère.\",\n\tdRemoveChildren = \"Vous avez enlevé tout les héritiers de cette porte.\",\n\tdRemoveChildDoor = \"Vous avez enlevé cette porte de son status hértier.\",\n\tdNoParentDoor = \"Aucune porte parent existe.\",\n\tdOwnedBy = \"Cette porte appartient à %s.\",\n\tdConfigName = \"Portes\",\n\tdSetFaction = \"Cette porte appartient désormais à la faction %s.\",\n\tdRemoveFaction = \"Cette porte n'appartient plus à aucune faction.\",\n\tdNotValid = \"Vous ne regardez pas une porte valide !\",\n\tcanNotAfford = \"Vous ne pouvez pas vous permettre d'acheter ceci !\",\n\tdPurchased = \"Vous avez acheté cette porte pour %s.\",\n\tdSold = \"Vous avez vendu cette porte pour %s.\",\n\tnotOwner = \"Vous n'êtes pas le propriétaire de cela !\",\n\tinvalidArg = \"Vous avez fourni une valeur non valide pour l'argument #%s !\",\n\tinvalidFaction = \"La faction que vous avez fournie est introuvable !\",\n\tflagGive = \"%s a donné %s '%s' flags.\",\n\tflagGiveTitle = \"Donner Flags\",\n\tflagTake = \"%s a pris les '%s' flags de %s.\",\n\tflagTakeTitle = \"Prend les flags\",\n\tflagNoMatch = \"Vous devez avoir \\\"%s\\\" flag(s) pour faire cette action !\",\n\ttextAdded = \"Vous avez ajouté un texte.\",\n\ttextRemoved = \"Vous avez supprimé le texte de %s.\",\n\tmoneyTaken = \"Vous avez reçu %s.\",\n\tmoneyGiven = \"Vous avez donné %s.\",\n\tinsufficientMoney = \"Tu n'as pas assez pour faire ça !\",\n\tbusinessPurchase = \"Vous avez acheté %s pour %s.\",\n\tbusinessSell = \"Vous avez vendu %s pour %s.\",\n\tbusinessTooFast = \"Veuillez patienter avant d'acheter un autre article !\",\n\tcChangeModel = \"%s a changé le model de %s en %s.\",\n\tcChangeName = \"%s a changé le nom de %s en %s.\",\n\tcChangeSkin = \"%s a changé le skin de %s en %s.\",\n\tcChangeGroups = \"%s a changé le Bodygroup de %s \\\"%s\\\" en %s.\",\n\tcChangeFaction = \"%s a transféré %s dans la faction %s.\",\n\tplayerCharBelonging = \"Cet objet appartient à votre autre personnage !\",\n\tspawnAdd = \"Vous avez ajouté un spawn pour le %s.\",\n\tspawnDeleted = \"Vous avez supprimé le point d'apparition de %s.\",\n\tsomeone = \"Quelqu'un\",\n\trgnLookingAt = \"Permettre à la personne que vous regardez de vous reconnaître.\",\n\trgnWhisper = \"Permettre à ceux qui entendent vos chuchotements de vous reconnaître.\",\n\trgnTalk = \"Permettre à ceux qui entendent vos conversations de vous reconnaître.\",\n\trgnYell = \"Permettre à ceux qui entendent vos cris de vous reconnaître.\",\n\ticFormat = \"%s dit \\\"%s\\\"\",\n\trollFormat = \"%s fait un roll de %s.\",\n\twFormat = \"%s chuchote \\\"%s\\\"\",\n\tyFormat = \"%s hurle \\\"%s\\\"\",\n\tsbOptions = \"Cliquez pour voir les options pour %s.\",\n\tspawnAdded = \"Vous avez ajouté un spawn pour %s.\",\n\twhitelist = \"%s a mis dans la whitelist %s de la faction %s.\",\n\tunwhitelist = \"%s a enlevé de la whitelist %s de la faction %s.\",\n\tnoWhitelist = \"vous n'avez pas la whitelist pour ce personnage !\",\n\tgettingUp = \"Vous êtes maintenant relevé...\",\n\twakingUp = \"Vous reprenez conscience...\",\n\tWeapons = \"Arme\",\n\tcheckout = \"Aller au Checkpoint (%s)\",\n\tpurchase = \"Achat\",\n\tpurchasing = \"Achat...\",\n\tsuccess = \"Succès\",\n\tbuyFailed = \"Achat raté !\",\n\tbuyGood = \"Achat réussi !\",\n\tshipment = \"Expédition\",\n\tshipmentDesc = \"Cet envoi appartient à %s.\",\n\tclass = \"Classe\",\n\tclasses = \"Classes\",\n\tillegalAccess = \"Accès illégal.\",\n\tbecomeClassFail = \"Vous ne pouvez pas devenir un %s!\",\n\tbecomeClass = \"Vous êtes devenu un %s.\",\n\tsetClass = \"Vous avez défini la classe de %s en %s.\",\n\tattributeSet = \"Vous avez définis %s's %s en %s.\",\n\tattributeNotFound = \"Vous avez spécifié un attribut non valide !\",\n\tattributeUpdate = \"Vous avez ajouté %s's %s par %s.\",\n\tnoFit = \"Vous n'avez pas assez d'espace pour cet article !\",\n\titemOwned = \"Vous ne pouvez pas interagir avec un objet que vous possédez sur un personnage différent !\",\n\thelp = \"Aide\",\n\tcommands = \"Commande\",\n\tdoorSettings = \"Réglages de la porte\",\n\tsell = \"Vendre\",\n\taccess = \"Accès\",\n\tlocking = \"VERROUILLAGE DE L'ENTITÉ...\",\n\tunlocking = \"DÉVERROUILLAGE DE L'ENTITÉ...\",\n\tmodelNoSeq = \"Votre modèle ne supporte pas cet action.\",\n\tnotNow = \"Vous n'êtes pas autorisé à le faire ceci maintenant.\",\n\tfaceWall = \"Vous devez être face au mur pour faire ceci.\",\n\tfaceWallBack = \"Votre dos doit faire face au mur pour faire ceci.\",\n\tdescChanged = \"Vous avez changé la description de votre personnage.\",\n\tnoOwner = \"Le propriétaire est invalide !\",\n\tinvalidItem = \"Vous avez spécifié un élément non valide !\",\n\tinvalidInventory = \"Vous avez spécifié un inventaire non valide !\",\n\thome = \"Maison\",\n\tcharKick = \"%s a exclu le personnage %s.\",\n\tcharBan = \"%s a banni le personnage %s.\",\n\tcharBanned = \"Ce personnage est banni.\",\n\tplayerConnected = \"%s s'est connecté au serveur.\",\n\tplayerDisconnected = \"%s s'est déconnecté du serveur.\",\n\tsetMoney = \"Vous avez défini l'argent de %s à %s..\",\n\titemPriceInfo = \"Vous pouvez acheter cet article pour %s.\\nVous pouvez vendre cet article pour %s\",\n\tfree = \"Gratuit\",\n\tvendorNoSellItems = \"Il n'y a pas d'objets à vendre.\",\n\tvendorNoBuyItems = \"Il n'y a pas d'objets à acheter.\",\n\tvendorSettings = \"Paramètres du vendeur\",\n\tvendorUseMoney = \"Le vendeur doit utiliser de l'argent ?\",\n\tvendorNoBubble = \"Masquer la bulle du vendeur ?\",\n\tmode = \"Mode\",\n\tprice = \"Prix\",\n\tstock = \"Stock\",\n\tnone = \"Aucun\",\n\tvendorBoth = \"Acheter et vendre\",\n\tvendorBuy = \"Achat seulement\",\n\tvendorSell = \"Vente seulement\",\n\tmaxStock = \"Stock max\",\n\tvendorFaction = \"Éditeur de faction\",\n\tbuy = \"Achat\",\n\tvendorWelcome = \"Bienvenue dans mon magasin, que puis-je vous offrir aujourd'hui ?\",\n\tvendorBye = \"Reviens bientôt !\",\n\tcharSearching = \"Vous recherchez déjà un autre personnage !\",\n\tcharUnBan = \"%s a débanni le personnage %s.\",\n\tcharNotBanned = \"Ce personnage n'est pas banni!\",\n\tquickSettings = \"Réglages rapides\",\n\tvmSet = \"Vous avez configuré votre messagerie vocale.\",\n\tvmRem = \"Vous avez supprimé votre messagerie vocale.\",\n\tnoPerm = \"Vous n'êtes pas autorisé à faire ceci!\",\n\tyoureDead = \"Tu es mort\",\n\tinjMajor = \"Semble gravement blessé\",\n\tinjLittle = \"Semble blessé\",\n\tchgName = \"Changer de nom\",\n\tchgNameDesc = \"Entrez le nouveau nom du personnage ci-dessous.\",\n\tweaponSlotFilled = \"Vous ne pouvez pas utiliser une autre arme %s!\",\n\tequippedBag = \"Vous ne pouvez pas déplacer un sac contenant un objet équipé !\",\n\tequippedWeapon = \"Vous ne pouvez pas déplacer une arme actuellement équipée !\",\n\tnestedBags = \"Vous ne pouvez pas mettre un inventaire à l'intérieur d'un inventaire de stockage !\",\n\toutfitAlreadyEquipped = \"Tu portes déjà ce genre de tenue !\",\n\tuseTip = \"Utiliser l'objet.\",\n\tequip = \"Équiper\",\n\tequipTip = \"Équiper l'objet.\",\n\tunequip = \"Déséquiper\",\n\tunequipTip = \"Déséquiper l'objet.\",\n\tconsumables = \"Consommables\",\n\tplyNotValid = \"Vous ne regardez pas un joueur valide !\",\n\trestricted = \"Vous avez été restreint.\",\n\tsalary = \"Vous avez reçu %s de votre salaire.\",\n\tnoRecog = \"Vous ne reconnaissez pas cette personne.\",\n\tcurTime = \"L'heure actuelle est %s.\",\n\tvendorEditor = \"Éditeur du vendeur\",\n\tedit = \"Edit\",\n\tdisable = \"Désactiver\",\n\tvendorPriceReq = \"Entrez le nouveau prix pour cet article.\",\n\tvendorEditCurStock = \"Modifier le stock actuel\",\n\tyou = \"Vous\",\n\tvendorSellScale = \"Échelle de prix\",\n\tvendorNoTrade = \"Vous ne pouvez pas échanger avec ce marchand !\",\n\tvendorNoMoney = \"Ce marchand ne peut pas se acheter cet article !\",\n\tvendorNoStock = \"Ce marchand n'a pas cet article en stock !\",\n\tcontentTitle = \"Contenu Helix manquant\",\n\tcontentWarning = \"Le contenu d'Helix n'est pas monté. Certaines fonctionnalités peuvent être manquantes.\\nSouhaitez-vous ouvrir la page du Workshop pour le contenu Helix ?\",\n\tflags = \"Flags\",\n\tmapRestarting = \"La carte va redémarrer dans %d secondes !\",\n\tchooseTip = \"Choisissez ce personnage jouer avec.\",\n\tdeleteTip = \"Supprimer ce personnage.\",\n\tstorageInUse = \"Quelqu'un d'autre utilise ceci !\",\n\tstorageSearching = \"Recherche...\",\n\tcontainer = \"Conteneur\",\n\tcontainerPassword = \"Vous avez défini le mot de passe de ce conteneur en %s.\",\n\tcontainerPasswordRemove = \"Vous avez supprimé le mot de passe de ce conteneur.\",\n\tcontainerPasswordWrite = \"Entrer le mot de passe.\",\n\tcontainerName = \"Vous avez défini le nom de ce conteneur en %s.\",\n\tcontainerNameWrite = \"Entrez le nom.\",\n\tcontainerNameRemove = \"Vous avez supprimé le nom de ce conteneur.\",\n\tcontainerInvalid = \"Vous devez regarder un conteneur pour faire ceci !\",\n\twrongPassword = \"Vous avez entré un mot de passe incorrect !\",\n\trespawning = \"Réapparition...\",\n\n\tscoreboard = \"Tableau des joueurs\",\n\tping = \"Ping: %d\",\n\tviewProfile = \"Voir le profil Steam\",\n\tcopySteamID = \"Copier le Steam ID\",\n\n\tmoney = \"Argent\",\n\tmoneyLeft = \"Votre argent: \",\n\tcurrentMoney = \"Argent restant: \",\n\n\tinvalidClass = \"Ceci n'est pas une classe valide !\",\n\tinvalidClassFaction = \"Il ne s'agit pas d'une classe valide pour cette faction !\",\n\n\tmiscellaneous = \"Divers\",\n\tgeneral = \"General\",\n\tobserver = \"Observateur\",\n\tperformance = \"Performance\",\n\tthirdperson = \"Troisième personne\",\n\tdate = \"Date\",\n\tinteraction = \"Interaction\",\n\tserver = \"Serveur\",\n\n\tresetDefault = \"Réinitialiser aux valeurs par défaut\",\n\tresetDefaultDescription = \"Ceci réinitialisera \\\"%s\\\" à sa valeur par défaut de \\\"%s\\\".\",\n\toptOpenBags = \"Sacs ouverts avec inventaire\",\n\toptdOpenBags = \"Visualise automatiquement tous les sacs de votre inventaire lorsque le menu est ouvert.\",\n\toptShowIntro = \"Afficher l'intro au démarrage\",\n\toptdShowIntro = \"Affiche l'introduction de Helix la prochaine fois que vous vous inscrivez. Ceci est toujours désactivé après l'avoir regardé.\",\n\toptCheapBlur = \"Désactiver le flou\",\n\toptdCheapBlur = \"Remplace le flou de l'interface utilisateur par une simple gradation.\",\n\toptObserverTeleportBack = \"Retour à l'emplacement précédent\",\n\toptdObserverTeleportBack = \"Vous ramène à l'emplacement où vous êtes entré en mode observateur.\",\n\toptObserverESP = \"Montrer l'ESP admin\",\n\toptdObserverESP = \"Affiche les noms et les emplacements de chaque joueur sur le serveur.\",\n\topt24hourTime = \"Utilisez le temps de 24 heures\",\n\toptd24hourTime = \"Afficher les horodatages sur 24 heures au lieu de 12 heures (AM / PM).\",\n\toptChatNotices = \"Afficher les avis dans le chat\",\n\toptdChatNotices = \"Met à la place tous les avis qui apparaissent en haut à droite dans le chat.\",\n\toptChatTimestamps = \"Afficher les horodatages dans le chat\",\n\toptdChatTimestamps = \"Prépose l'heure à chaque message dans la chatbox.\",\n\toptAlwaysShowBars = \"Toujours afficher les barres d'informations\",\n\toptdAlwaysShowBars = \"Affiche les barres d’information en haut à gauche, qu’elles soient masquées ou non.\",\n\toptAltLower = \"Masquer les mains une fois abaissé\", -- @todo remove me\n\toptdAltLower = \"Cacher les mains quand elles sont sur les côtés.\", -- @todo remove me\n\toptThirdpersonEnabled = \"Activer la troisième personne\",\n\toptdThirdpersonEnabled = \"Déplace la caméra derrière toi. Ceci peut également être activé dans la console avec la commande \\\"ix_togglethirdperson\\\"\",\n\toptThirdpersonClassic = \"Activer la caméra classique à la troisième personne\",\n\toptdThirdpersonClassic = \"Déplace la vue de ton personnage avec ta souris.\",\n\toptThirdpersonVertical = \"Troisième personne hauteur\",\n\toptdThirdpersonVertical = \"À quelle hauteur la caméra devrait être.\",\n\toptThirdpersonHorizontal = \"Troisième personne horizontale\",\n\toptdThirdpersonHorizontal = \"Quelle distance gauche ou droite la caméra doit être.\",\n\toptThirdpersonDistance = \"Troisième personne distance\",\n\toptdThirdpersonDistance = \"À quelle distance la caméra doit être.\",\n\toptDisableAnimations = \"Désactiver les animations\",\n\toptdDisableAnimations = \"Arrête la lecture des animations pour fournir des transitions instantanées.\",\n\toptAnimationScale = \"Échelle d'animation\",\n\toptdAnimationScale = \"A quelle vitesse les animations doivent être faites.\",\n\toptLanguage = \"Langue\",\n\toptdLanguage = \"La langue affichée dans l'interface utilisateur Helix.\",\n\toptNoticeDuration = \"Durée de la notification\",\n\toptdNoticeDuration = \"Combien de temps afficher les notifications pour (en secondes).\",\n\toptNoticeMax = \"Notification maximum\",\n\toptdNoticeMax = \"Le nombre de notification affichés avant que les précédentes soient supprimés.\",\n\toptChatFontScale = \"Échelle de polices de discussion\",\n\toptdChatFontScale = \"A quelle taille doit être la police de discussion.\",\n\toptChatOutline = \"Décrire le texte du chat\",\n\toptdChatOutline = \"Affiche un contour autour du texte de la discussion plutôt qu’une ombre. Activez cette option si vous rencontrez des difficultés pour lire du texte.\",\n\n\tcmdRoll = \"Rolls un nombre entre 0 et le nombre spécifié\",\n\tcmdPM = \"Envoie un message privé à quelqu'un.\",\n\tcmdReply = \"Envoie un message privé à la dernière personne à qui vous avez envoyé un message.\",\n\tcmdSetVoicemail = \"Définit ou supprime le message de réponse automatique lorsque quelqu'un vous envoie un message privé.\",\n\tcmdCharGiveFlag = \"Donne le(s) flag(s) spécifié(s) à quelqu'un.\",\n\tcmdCharTakeFlag = \"Supprime le flag spécifié de la personnage si elle le possède.\",\n\tcmdToggleRaise = \"Lève ou baisse l'arme que vous tenez.\",\n\tcmdCharSetModel = \"Définit le modèle de personnage d'une personne.\",\n\tcmdCharSetSkin = \"Définit l'apparence du modèle de personnage d'une personne.\",\n\tcmdCharSetBodygroup = \"Définit le Bodygroup pour le modèle de personnage d'une personne.\",\n\tcmdCharSetAttribute = \"Définit le niveau de l'attribut spécifié pour quelqu'un.\",\n\tcmdCharAddAttribute = \"Ajoute un niveau à l'attribut spécifié pour quelqu'un.\",\n\tcmdCharSetName = \"Remplace le nom de quelqu'un par le nom spécifié.\",\n\tcmdCharGiveItem = \"Donne l'élément spécifié à quelqu'une.\",\n\tcmdCharKick = \"Oblige quelqu'un à se déconnecter de son personnage.\",\n\tcmdCharBan = \"Interdit à une personne de jouer son personnage actuel.\",\n\tcmdCharUnban = \"Permet à un personnage banni d'être utiliser à nouveau.\",\n\tcmdGiveMoney = \"Donne une somme d'argent précise à la personne que vous regardez.\",\n\tcmdCharSetMoney = \"Modifie le montant total d'argent d'une personne en fonction du montant spécifié.\",\n\tcmdDropMoney = \"Dépose le montant d'argent spécifié dans une petite boîte devant vous.\",\n\tcmdPlyWhitelist = \"Permet à quelqu'un de créer un personnage dans la faction spécifiée.\",\n\tcmdCharGetUp = \"Tentatives de se relever après être tombé.\",\n\tcmdPlyUnwhitelist = \"Interdit à quelqu'un de créer un personnage dans la faction spécifiée.\",\n\tcmdCharFallOver = \"Rends vos genoux faibles et vous fait tomber.\",\n\tcmdBecomeClass = \"Tente de faire partie de la classe spécifiée dans votre faction actuelle.\",\n\tcmdCharDesc = \"Définir votre description physique\",\n\tcmdCharDescTitle = \"Description physique\",\n\tcmdCharDescDescription = \"Entrez la description physique de votre personnage.\",\n\tcmdPlyTransfer = \"Transfère quelqu'un à la faction spécifiée.\",\n\tcmdCharSetClass = \"Force quelqu'un à faire partie d'une classe dans sa faction.\",\n\tcmdMapRestart = \"Redémarre la carte après le délai spécifié.\",\n\tcmdPanelAdd = \"Place un panneau Web dans le monde.\",\n\tcmdPanelRemove = \"Supprime un panneau Web que vous regardez.\",\n\tcmdTextAdd = \"Place un bloc de texte dans le monde.\",\n\tcmdTextRemove = \"Supprime les blocs de texte que vous regardez.\",\n\tcmdMapSceneAdd = \"Ajoute un point de vue de caméra cinématique affiché dans le menu du personnage..\",\n\tcmdMapSceneRemove = \"Supprime un point de vue de la caméra qui est affiché dans le menu du personnage.\",\n\tcmdSpawnAdd = \"Ajoute un point d'apparition pour la faction spécifiée.\",\n\tcmdSpawnRemove = \"Supprime les points d'apparition que vous regardez.\",\n\tcmdAct = \"Effectuer l'animation %s.\",\n\tcmdContainerSetPassword = \"Définit le mot de passe du conteneur que vous consultez.\",\n\tcmdDoorSell = \"Vend la porte que vous regardez.\",\n\tcmdDoorBuy = \"Achète la porte que vous regardez.\",\n\tcmdDoorSetUnownable = \"Rend la porte que vous regardez inachetable.\",\n\tcmdDoorSetOwnable = \"Rend la porte que vous regardez achetable.\",\n\tcmdDoorSetFaction = \"Attribut la porte que vous regardez à la faction spécifiée.\",\n\tcmdDoorSetDisabled = \"N'autorise aucune commande sur la porte que vous regardez.\",\n\tcmdDoorSetTitle = \"Définit le titre de la porte que vous regardez.\",\n\tcmdDoorSetParent = \"Définit le parent d'une paire de portes.\",\n\tcmdDoorSetChild = \"Définit l'héritié d'une paire de portes.\",\n\tcmdDoorRemoveChild = \"Enlève l'héritié d'une paire de portes.\",\n\tcmdDoorSetHidden = \"Cache la description de la porte que vous regardez, mais lui permet quand même d'être achetable.\",\n\tcmdDoorSetClass = \"Attribut la porte que vous regardez à la classe spécifiée.\",\n\tcmdMe = \"Effectuer une action physique.\",\n\tcmdIt = \"Décrire une action/situation en tant que narrateur.\",\n\tcmdW = \"Chuchoter quelque chose au gens près de vous.\",\n\tcmdY = \"Crier quelque chose au gens autour de vous.\",\n\tcmdEvent = \"Décrire une action que tout le monde peut voir.\",\n\tcmdOOC = \"Envoie un message dans le chat général en dehors du Rôleplay.\",\n\tcmdLOOC = \"Envoie un message dans le chat local en dehors du Rôleplay.\"\n}\n"
  },
  {
    "path": "gamemode/languages/sh_german.lua",
    "content": "\n-- GERMAN TRANSLATION\n\nNAME = \"Deutsch\"\n\nLANGUAGE = {\n    helix = \"Helix\",\n\n    introTextOne = \"fist industries präsentiert\",\n    introTextTwo = \"in Zusammenarbeit mit %s\",\n    introContinue = \"Drücke Leertaste um fortzufahren\",\n\n    helpIdle = \"Wähle eine Kategorie\",\n    helpCommands = \"Kommandoparameter mit <Pfeilen> sind Voraussetung, während [Klammern] optional sind.\",\n    helpFlags = \"Dieser Charakter kann auf Flags mit einem grünen Hintergrund zugreifen.\",\n\n    creditSpecial = \"Vielen Dank\",\n    creditLeadDeveloper = \"Lead Developer\",\n    creditUIDesigner = \"UI Designer\",\n    creditManager = \"Project Manager\",\n    creditTester = \"Lead Tester\",\n\n    chatTyping = \"Am Schreiben...\",\n    chatTalking = \"Am Reden...\",\n    chatYelling = \"Am Schreien...\",\n    chatWhispering = \"Am Flüstern...\",\n    chatPerforming = \"Am Ausführen...\",\n    chatNewTab = \"Neuer Tab...\",\n    chatReset = \"Position zurücksetzen\",\n    chatResetTabs = \"Tabs zurücksetzen\",\n    chatCustomize = \"Individualisieren...\",\n    chatCloseTab = \"Tab schließen\",\n    chatTabName = \"Tab Name\",\n    chatNewTabTitle = \"Neuer Tab\",\n    chatAllowedClasses = \"Erlaubte Chatklassen\",\n    chatTabExists = \"Ein Chat-Tab mit diesem Namen exisitert bereits!\",\n    chatMarkRead = \"Alles als gelesen markieren.\",\n\n    community = \"Community\",\n    checkAll = \"Alles auswählen\",\n    uncheckAll = \"Auswahl aufheben\",\n    color = \"Farbe\",\n    type = \"Typ\",\n    display = \"Anzeige\",\n    loading = \"Am Laden\",\n    dbError = \"Datenbankverbindung fehlgeschlagen\",\n    unknown = \"Unbekannt\",\n    noDesc = \"Keine Beschreibung verfügbar\",\n    create = \"Erstellen\",\n    update = \"Update\",\n    load = \"Charakter laden\",\n    loadTitle = \"Lade einen Charakter\",\n    leave = \"Server verlassen\",\n    leaveTip = \"Verlasse den aktuellen Server\",\n    [\"return\"] = \"Zurück\",\n    returnTip = \"Gehe zum vorherigen Menü zurück\",\n    proceed = \"Weiter\",\n    faction = \"Fraktion\",\n    skills = \"Fähigkeiten\",\n    choose = \"Auswählen\",\n    chooseFaction = \"Wähle eine Fraktion\",\n    chooseDescription = \"Definiere deine Geschichte\",\n    chooseSkills = \"Verfeinere deine Fähigkeiten\",\n    name = \"Name\",\n    description = \"Beschreibung\",\n    model = \"Model\",\n    attributes = \"Attribute\",\n    attribPointsLeft = \"Verbleibende Punkte\",\n    charInfo = \"Charakter Informationen\",\n    charCreated = \"Charakter erfolgreich erstellt.\",\n    charCreateTip = \"Vervollständige alle Felder und klicke Anschließend auf 'Bestätigen' um deinen Charakter zu erstellen.\",\n    invalid = \"Falscher %s!\",\n    nameMinLen = \"Dein Name muss mindestens %d Zeichen enthalten!\",\n    nameMaxLen = \"Deine Name darf maximal %d Zeichen enthalten!\",\n    descMinLen = \"Deine Beschreibung muss mindestens %d Zeichen enthalten!\",\n    maxCharacters = \"Du kannst keine weiteren Charakter erstellen!\",\n    player = \"Spieler\",\n    finish = \"Bestätigen\",\n    finishTip = \"Beende die Charaktererstellung\",\n    needModel = \"Du musst ein gültiges Model auswählen!\",\n    creating = \"Dein Charakter wird erstellt...\",\n    unknownError = \"Ein unbekannter Fehler ist aufgetreten!\",\n    areYouSure = \"Bist du dir sicher?\",\n    delete = \"Löschen\",\n    deleteConfirm = \"Dieser Charakter kann nicht wiederhergestellt werden!\",\n    deleteComplete = \"%s wurde gelöscht.\",\n    no = \"Nein\",\n    yes = \"Ja\",\n    close = \"Schließen\",\n    save = \"Speichern\",\n    itemInfo = \"Name: %s\\nBeschreibung: %s\",\n    itemCreated = \"Item(s) erfolgreich erstellt.\",\n    cloud_no_repo = \"Das gewählte Lager ist nicht gültig!\",\n    cloud_no_plugin = \"Das gewählte Plugin ist nicht gültig!\",\n    inv = \"Inventar\",\n    plugins = \"Plugins\",\n    pluginLoaded = \"%s hat Plugin \\\"%s\\\" für das Laden eines Mapchange aktiviert.\",\n    pluginUnloaded = \"%s hat Plugin \\\"%s\\\" für das Laden eines Mapchange deaktivier.\",\n    loadedPlugins = \"Geladene Plugins\",\n    unloadedPlugins = \"Nicht-Geladene Plugins\",\n    on = \"Aktiviert\",\n    off = \"Deaktiviert\",\n    author = \"Author\",\n    version = \"Version\",\n    characters = \"Charaktere\",\n    business = \"Geschäft\",\n    settings = \"Einstellungen\",\n    config = \"Konfiguration\",\n    chat = \"Chat\",\n    appearance = \"Aussehen\",\n    misc = \"Sonstiges\",\n    oocDelay = \"Du musst %s Sekunden warten bevor du OOC erneut benutzt!\",\n    loocDelay = \"Du musst %s Sekunden warten bevor du LOOC erneut benutzt!\",\n    usingChar = \"Du benutzt diesen Charakter schon.\",\n    notAllowed = \"Das darfst du nicht tun!\",\n    itemNoExist = \"Das angefragte Item existiert nicht!\",\n    cmdNoExist = \"Dieser Befehl existiert nicht!\",\n    charNoExist = \"Ein passendes Zeichen wurde nicht gefunden!\",\n    plyNoExist = \"Ein passendes Spieler wurde nicht gefunden!\",\n    cfgSet = \"%s hat \\\"%s\\\" zu %s gesetzt.\",\n    drop = \"Fallenlassen\",\n    dropTip = \"Lasse diese Item fallen.\",\n    take = \"Nehmen\",\n    takeTip = \"Nehme dieses Item und packe es in dein Inventar.\",\n    dTitle = \"Unbekannte Tür\",\n    dTitleOwned = \"Gekaufte Tür\",\n    dIsNotOwnable = \"Tür nicht verfügbar\",\n    dIsOwnable = \"Du kannst diese Tür mit F2 kaufen.\",\n    dMadeUnownable = \"Diese Tür ist nun nicht mehr verfügbar.\",\n    dMadeOwnable = \"Diese Tür ist nun wieder verfügbar.\",\n    dNotAllowedToOwn = \"Du darfst diese Tür nicht besitzen!\",\n    dSetDisabled = \"Diese Tür ist nun deaktiviert.\",\n    dSetNotDisabled = \"Diese Tür ist nun wieder aktiviert.\",\n    dSetHidden = \"Diese Tür ist nun versteckt.\",\n    dSetNotHidden = \"Diese Tür ist nun sichtbar.\",\n    dSetParentDoor = \"Du hast diese Tür als deine Parent Tür gesetzt\",\n    dCanNotSetAsChild = \"Du kannst diese Parent-Tür nicht als Child setzen.\",\n    dAddChildDoor = \"Du hast diese Tür als Child gesetzt.\",\n    dRemoveChildren = \"Du hast alle Children dieser Tür entfernt.\",\n    dRemoveChildDoor = \"Diese Tür ist kein Child mehr.\",\n    dNoParentDoor = \"Du hast keine Parent-Tür ausgewählt.\",\n    dOwnedBy = \"%s besitzt diese Tür bereits.\",\n    dConfigName = \"Türen\",\n    dSetFaction = \"Fraktion %s besitzt diese Tür.\",\n    dRemoveFaction = \"Diese Tür gehört nun keine Fraktion mehr.\",\n    dNotValid = \"Du gucks auf keine Tür!\",\n    canNotAfford = \"Du hast nicht genug Geld!\",\n    dPurchased = \"Tür wurde für %s von dir gekauft.\",\n    dSold = \"Du hast diese Tür für %s verkauft.\",\n    notOwner = \"Du bist nicht der Besitzer!\",\n    invalidArg = \"Ungültiger Wert für Argument #%s!\",\n    invalidFaction = \"Fraktion konnte nicht gefunden werden!\",\n    flagGive = \"%s hat %s '%s' Berechtigungen gegeben.\",\n    flagGiveTitle = \"Berechtigungen geben\",\n    flagTake = \"%s hat '%s' Berechtigungen von %s genommen.\",\n    flagTakeTitle = \"Berechtigungen nehmen\",\n    flagNoMatch = \"Du benötigt \\\"%s\\\" Berechtigung(en) um diese Aktion auszuführen!\",\n    panelAdded = \"Feld hinzugefügt.\",\n    panelRemoved = \"Du hast %d Feld(er) entfernt.\",\n    textAdded = \"Du hast einen Text hinzugefügt.\",\n    textRemoved = \"Du hast %s Text(e) gelöscht.\",\n    moneyTaken = \"Dir wurde %s gegeben.\",\n    moneyGiven = \"Du hast %s gegeben.\",\n    insufficientMoney = \"Du hast nicht genug Geld!\",\n    businessPurchase = \"Du hast %s für %s gekauft.\",\n    businessSell = \"Du hast %s für %s verkauft.\",\n    businessTooFast = \"Bitte warte bevor du ein weitere Item kaufst!\",\n    cChangeModel = \"%s hat %s's Model zu %s geändert.\",\n    cChangeName = \"%s hat %s's Name zu %s geändert.\",\n    cChangeSkin = \"%s hat %s's Skin zu %s geändert.\",\n    cChangeGroups = \"%s hat %s's \\\"%s\\\" Bodygroup zu %s geändert.\",\n    cChangeFaction = \"%s hat %s zu Fraktion %s gewechselt.\",\n    playerCharBelonging = \"Dieser Gegenstand gehört deinem anderen Charakter!\",\n    spawnAdd = \"Du hast einen Spawn für %s hinzugefügt.\",\n    spawnDeleted = \"Du hast %s Spawnpoint(s) entfernt.\",\n    someone = \"Jemand\",\n    rgnLookingAt = \"Erlaube allen in Sichtreichweite dich zu erkennen\",\n    rgnWhisper = \"Erlaube allen in Flüsterreichweite dich zu erkennen.\",\n    rgnTalk = \"Erlaube allen in Sprachreichweite dich zu erkennen\",\n    rgnYell = \"Erlaube allen in Rufreichweite dich zu erkennen\",\n    icFormat = \"%s sagt \\\"%s\\\"\",\n    rollFormat = \"%s hat eine %s von %s gewürfelt.\",\n    wFormat = \"%s flüstert \\\"%s\\\"\",\n    yFormat = \"%s ruft \\\"%s\\\"\",\n    sbOptions = \"Auswählen um Optionen für %s zu sehen.\",\n    spawnAdded = \"Du hast einen Spawn für %s hinzugefügt.\",\n    whitelist = \"%s hat %s auf die Whitelist der Fraktion %s gesetzt.\",\n    unwhitelist = \"%s hat %s von der Whitelist der Fraktion %s entfernt.\",\n    noWhitelist = \"Du hast keine Whitelist für diesen Charakter!\",\n    charNotWhitelisted = \"%s ist nicht auf der Whitelist der Fraktion %s.\",\n    gettingUp = \"Du stehst auf...\",\n    wakingUp = \"Du bist wieder bei Bewusstsein...\",\n    Weapons = \"Waffen\",\n    checkout = \"Guck dir (%s) an\",\n    purchase = \"Kaufen\",\n    purchasing = \"Kaufprozess im Gange...\",\n    success = \"Erfolgreich\",\n    buyFailed = \"Kauf fehlgeschlagen!\",\n    buyGood = \"Kauf erfolgreich!\",\n    shipment = \"Lieferung\",\n    shipmentDesc = \"Diese Lieferung gehört %s.\",\n    class = \"Klasse\",\n    classes = \"Klassen\",\n    illegalAccess = \"Unerlaubter Zugriff.\",\n    becomeClassFail = \"Du kannst kein %s werden!\",\n    becomeClass = \"Du bist nun %s.\",\n    setClass = \"Du hast %s's Klasse zu %s gesetzt.\",\n    attributeSet = \"Du hast %s's %s zu %s gesetzt.\",\n    attributeNotFound = \"Du hast ein ungültiges Attribut angegeben!\",\n    attributeUpdate = \"Du hast %s's %s %s hinzugefügt.\",\n    noFit = \"Du hast kein Platz für dieses Item.\",\n    itemOwned = \"Du kannst nicht mit einem Item interagieren das ein anderer deiner Charakter besitzt!\",\n    help = \"Hilfe\",\n    commands = \"Commands\",\n    doorSettings = \"Türeinstellungen\",\n    sell = \"Verkaufen\",\n    access = \"Zugriff\",\n    locking = \"Entity am zuschließen...\",\n    unlocking = \"Entity am aufschließen...\",\n    modelNoSeq = \"Dein Model unterstütz diese Aktion nicht!\",\n    notNow = \"Das kannst du nicht tun!\",\n    faceWall = \"Du musst mit dem Gesicht zu einer Wand stehen!\",\n    faceWallBack = \"Du musst mit dem Rücken zu einer Wand stehen!\",\n    descChanged = \"Du hast deine Charakterbeschreibung geändert.\",\n    noOwner = \"Ungültiger Besitzer!\",\n    invalidItem = \"Ungültiges Item ausgewählt!\",\n    invalidInventory = \"Ungültiges Inventar ausgewählt!\",\n    home = \"Start\",\n    charKick = \"%s hat Charakter %s gekickt.\",\n    charBan = \"%s hat Charakter %s gebannt.\",\n    charBanned = \"Dieser Charakter ist gebannt.\",\n    charBannedTemp = \"Dieser Charakter ist temporär gebannt.\",\n    playerConnected = \"%s hat den Server betreten.\",\n    playerDisconnected = \"%s hat den Server verlassen.\",\n    setMoney = \"Du hast %s's Geld auf %s gesetzt.\",\n    itemPriceInfo = \"Du kannst das Item für %s kaufen.\\nDu kannst das Item für %s verkaufen.\",\n    free = \"Kostenlos\",\n    vendorNoSellItems = \"Es gibt keine Items zu verkaufen.\",\n    vendorNoBuyItems = \"Es gibt keine Items zu kaufen.\",\n    vendorSettings = \"Verkäufereinstellungen\",\n    vendorUseMoney = \"Soll der Verkäufer Geld benutzen?\",\n    vendorNoBubble = \"Blase verstecken?\",\n    category = \"Kategorie\",\n    mode = \"Modus\",\n    price = \"Preis\",\n    stock = \"Bestand\",\n    none = \"Keine\",\n    vendorBoth = \"Kauf und Verkauf\",\n    vendorBuy = \"Nur Kauf\",\n    vendorSell = \"Nur Verkauf\",\n    maxStock = \"Höchster Bestand\",\n    vendorFaction = \"Fraktionseditor\",\n    buy = \"Kaufen\",\n    vendorWelcome = \"Wilkommen in meinem Laden, wie kann ich behilflich sein?\",\n    vendorBye = \"Auf Wiedersehen!\",\n    charSearching = \"Du suchst bereits einen anderen Charakter!\",\n    charUnBan = \"%s hat Charakter %s entbannt.\",\n    charNotBanned = \"Dieser Charakter ist nicht gebannt!\",\n    quickSettings = \"Schnelleinstellungen\",\n    vmSet = \"Du hast deine Sprachmitteilung gesetzt.\",\n    vmRem = \"Du hast deiner Sprachmitteilung entfernt.\",\n    noPerm = \"Das ist dir nicht gestattet!\",\n    youreDead = \"Du bist tot\",\n    injMajor = \"Wirkt schwer verletzt\",\n    injLittle = \"Wirkt leicht verletzt\",\n    chgName = \"Namen ändern\",\n    chgNameDesc = \"Gebe den Charakternamen unten ein.\",\n    weaponSlotFilled = \"Du kannst keine weitere %s Waffe benutzen!\",\n    equippedBag = \"Du kannst keine Tasche mit einen ausgerüsteten Item bewegen!\",\n    equippedWeapon = \"Du kannst keine ausgerüstete Waffe bewegen!\",\n    nestedBags = \"Du kannst kein Inventar in ein anderes Inventar legen!\",\n    outfitAlreadyEquipped = \"Du trägst dieses Outfit bereits!\",\n    useTip = \"Benutze das Item.\",\n    equipTip = \"Rüste das Item aus.\",\n    unequipTip = \"Lege das Item ab.\",\n    consumables = \"Verbrauchsgegenstand\",\n    plyNotValid = \"Du schaust nicht auf einen gültigen Spieler!\",\n    restricted = \"Du wurdest festgenommen.\",\n    salary = \"Du hast %s Lohn bekommen.\",\n    noRecog = \"Du erkennst diese Person nicht.\",\n    curTime = \"Zeit: %s\",\n    vendorEditor = \"Verkäufer Einstellungen\",\n    edit = \"Bearbeiten\",\n    disable = \"Deaktivieren\",\n    vendorPriceReq = \"Neuen Preis eingeben.\",\n    vendorEditCurStock = \"Bestand bearbeiten\",\n    vendorStockReq = \"Maximalen Bestand für diese Item eingeben.\",\n    vendorStockCurReq = \"Gebe ein wie viele Items benötigt werden um vom Bestand kaufen zu dürfen.\",\n    you = \"Du\",\n    vendorSellScale = \"Verkaufspreis Faktor\",\n    vendorNoTrade = \"Du kannst mit diesem Verkäufer nicht tauschen!\",\n    vendorNoMoney = \"Dieser Verkäufer kann sich diese Item nicht leiten!\",\n    vendorNoStock = \"Dieser Verkäufer hat das Item nicht mehr!\",\n    vendorMaxStock = \"Dieser Verkäufer hat einen vollen Bestand dieses Items!\",\n    contentTitle = \"Helix Content fehlt!\",\n    contentWarning = \"Du hast die Helix-Inhalte nicht installiert. Dies kann dazu führen, dass bestimmte Funktionen fehlen.\\nMöchtest du die Workshop-Seite für die Helix-Inhalte öffnen?\",\n    flags = \"Berechtigungen\",\n    mapRestarting = \"Die Map wird in %d Sekunden neustarten!\",\n    chooseTip = \"Wähle einen Charakter zum spielen.\",\n    deleteTip = \"Lösche diesen Charakter.\",\n    storageInUse = \"Gegenstand in Nutzung!\",\n    storageSearching = \"Suche...\",\n    container = \"Behälter\",\n    containerPassword = \"Passwort auf %s gesetzt.\",\n    containerPasswordRemove = \"Du hast das Passwort entfernt.\",\n    containerPasswordWrite = \"Passwort eingeben.\",\n    containerName = \"Namen des Behälters auf %s gesetzt.\",\n    containerNameWrite = \"Namen eingeben.\",\n    containerNameRemove = \"Name des Behälters entfernt.\",\n    containerInvalid = \"Du musst einen Behälter anschauen!\",\n    wrongPassword = \"Falsches Passwort!\",\n    respawning = \"Wiederbeleben...\",\n    tellAdmin = \"Informiere ein Teammitglied über diese Fehler: %s\",\n\n    mapAdd = \"Du hast eine Mapszene hinzugefügt.\",\n    mapDel = \"du hast %d Mapszene(n) entfernt.\",\n    mapRepeat = \"Füge nun den zweiten Punkt hinzu.\",\n\n    scoreboard = \"Spieleübersicht\",\n    ping = \"Ping: %d\",\n    viewProfile = \"Steamprofil anschauen\",\n    copySteamID = \"Kopiere Steam ID\",\n\n    money = \"Credits\",\n    moneyLeft = \"Deine Credits: \",\n    currentMoney = \"Verbleibende Credits: \",\n\n    invalidClass = \"Das ist keine gültige Klasse!\",\n    invalidClassFaction = \"Das ist keine gültige Klasse für deine Fraktion!\",\n\n    miscellaneous = \"Sonstiges\",\n    general = \"Allgemein\",\n    observer = \"Beobachter\",\n    performance = \"Leistung\",\n    thirdperson = \"Third Person\",\n    date = \"Datum\",\n    interaction = \"Interaktion\",\n    server = \"Server\",\n\n    resetDefault = \"Standarteinstellung wiederherstellen\",\n    resetDefaultDescription = \"\\\"%s\\\" wird auf den Standartwert \\\"%s\\\" zurückgesetzt.\",\n    optOpenBags = \"Öffne Tasche mit Inventar\",\n    optdOpenBags = \"Öffnet alle Taschen in deinem Inventar automatisch\",\n    optShowIntro = \"Intro beim Verbinden anzeigen\",\n    optdShowIntro = \"Zeigt die Helix Einführen beim nächsten Verbinden. Wird nach dem ersten Schauen automatisch deaktiviert.\",\n    optCheapBlur = \"Deaktiviere Blur\",\n    optdCheapBlur = \"Ersetzt UI Blur mit einfachem Verdunkeln.\",\n    optObserverTeleportBack = \"Kehre zur vorherigen Position zurück.\",\n    optdObserverTeleportBack = \"Kehre zum Ort zurück an dem du in den Beobachtungsmodus gegangen bist.\",\n    optObserverESP = \"Zeige Admin ESP\",\n    optdObserverESP = \"Zeige Namen und Position aller Spieler auf dem Server.\",\n    opt24hourTime = \"Nutze 24-Stundenformat\",\n    optd24hourTime = \"Zeige Zeitstempel in 24-Stundenformat, statt 12-Stundenformat (AM/PM).\",\n    optChatNotices = \"Zeige Hinweise im Chat\",\n    optdChatNotices = \"Zeige alle Hinweise Rechts-Oben stattdessen im Chat an.\",\n    optChatTimestamps = \"Zeige Zeitstempel im Chat.\",\n    optdChatTimestamps = \"Zeigt bei jeder Nachricht im Chat die Zeit an.\",\n    optAlwaysShowBars = \"Zeige Statusbalken dauerhaft.\",\n    optdAlwaysShowBars = \"Zeige Statusbalken Links-Oben dauerhaft.\",\n    optAltLower = \"Verstecke Hände wenn runtergenommen.\", -- @todo remove me\n    optdAltLower = \"Versteckt Händer wenn sie an deinem Körper herunterhängen.\", -- @todo remove me\n    optThirdpersonEnabled = \"Aktiviere Third-Person\",\n    optdThirdpersonEnabled = \"Bewegt die Kamera hinter dich. Kann auch mit dem \\\"ix_togglethirdperson\\\" Console-Command umgeschaltet werden.\",\n    optThirdpersonClassic = \"Aktiviere die klassische Third-Person Kamera\",\n    optdThirdpersonClassic = \"Bewegt die Sicht deines Charakters mit der Maus.\",\n    optThirdpersonVertical = \"Kamera Höhe\",\n    optdThirdpersonVertical = \"Wie hoch die Kamera sein soll.\",\n    optThirdpersonHorizontal = \"Kamera Offset\",\n    optdThirdpersonHorizontal = \"Wie weit links oder rechts die Kamera sein soll.\",\n    optThirdpersonDistance = \"Kamera Entfernung\",\n    optdThirdpersonDistance = \"Wie weit entfernt die Kamera sein soll.\",\n    optThirdpersonCrouchOffset = \"Camera Höhe beim Kriechen\",\n    optdThirdpersonCrouchOffset = \"Wie hoch die Kamerahöhe beim Kriechen sein soll.\",\n    optDisableAnimations = \"Deaktiviere Animationen\",\n    optdDisableAnimations = \"Verhindert Animationen für einen sofortigen Übergang.\",\n    optAnimationScale = \"Animation Scale\",\n    optdAnimationScale = \"Wie schnell Animationen laufen sollen.\",\n    optLanguage = \"Sprache\",\n    optdLanguage = \"Die Sprache der Helix UI.\",\n    optMinimalTooltips = \"Minimale HUD Tips\",\n    optdMinimalTooltips = \"Ändert die HUD Tips um weniger auffällig zu sein.\",\n    optNoticeDuration = \"Hinweiszeit\",\n    optdNoticeDuration = \"Wie lange Hinweise angezeigt werden sollen (in Sekunden).\",\n    optNoticeMax = \"Maximale Hinweise\",\n    optdNoticeMax = \"Die maximal gleichzeitig angezeigten Hinweise.\",\n    optChatFontScale = \"Chat Schiftgröße\",\n    optdChatFontScale = \"Die Größe der Schirft im Chat.\",\n    optChatOutline = \"Chat Schirftrand\",\n    optdChatOutline = \"Größe des Randes um die Schirft im Text.\",\n\n    cmdRoll = \"Würfelt eine Zahl zwischen 0 und der angegebenen Zahl.\",\n    cmdPM = \"Sendet eine Privatnachricht.\",\n    cmdReply = \"Sendet eine private Antwort zur letzten Person die dich angeschrieben hat.\",\n    cmdSetVoicemail = \"Setzt oder entfernt die Automatische Nachricht wenn dir jemand eine Privatnachricht sendet.\",\n    cmdCharGiveFlag = \"Gibt einer Person die angegebenen Berechtigungen.\",\n    cmdCharTakeFlag = \"Entfernt einer Person die angegebenen Berechtigungen.\",\n    cmdToggleRaise = \"Hebt oder Senkt die Waffe die du hälst.\",\n    cmdCharSetModel = \"Setzt das Model einer Person.\",\n    cmdCharSetSkin = \"Setzt den Skin des Models einer Person.\",\n    cmdCharSetBodygroup = \"Setzt die Bodygroup des Models einer Person.\",\n    cmdCharSetAttribute = \"Setzt das Level des angegebenen Attributs einer Person.\",\n    cmdCharAddAttribute = \"Fügt ein Level zum angegebenen Attirbuts einer Person hinzu.\",\n    cmdCharSetName = \"Ändert den Namen einer Person.\",\n    cmdCharGiveItem = \"Gibt das angegebenen Item einer Person.\",\n    cmdCharKick = \"Wirft jemanden aus seinem Charakter heraus.\",\n    cmdCharBan = \"Bannt einen bestimmten Charakter.\",\n    cmdCharUnban = \"Entbannt einen bestimmten Charakter.\",\n    cmdGiveMoney = \"Gibt dem Spieler den du anschaust eine angegebene Menge Geld.\",\n    cmdCharSetMoney = \"Setzt das Geld eines Spielers.\",\n    cmdDropMoney = \"Lässt Geld in einer Box vor dir fallen.\",\n    cmdPlyWhitelist = \"Whitelistet eine Person zu einer Fraktion.\",\n    cmdCharGetUp = \"Lässt deine Charakter versuchen wieder aufzustehen.\",\n    cmdPlyUnwhitelist = \"Entwhitelisted einen Charakter von eine Fraktion.\",\n    cmdCharFallOver = \"Lässt dich ohnmächtig werden.\",\n    cmdBecomeClass = \"Versucht die gegebene Klasse in deiner Fraktion zu werden.\",\n    cmdCharDesc = \"Setzt eine Beschreibung deine Körpers.\",\n    cmdCharDescTitle = \"Körperbeschreibung\",\n    cmdCharDescDescription = \"Geben deine Körperbeschreibung ein.\",\n    cmdPlyTransfer = \"Transferiert einen Spieler zu gegebenen Position.\",\n    cmdCharSetClass = \"Setzt jemanden zu einer Klasse.\",\n    cmdMapRestart = \"Führt nach der angegebenen Zeit einen Maprestart aus.\",\n    cmdPanelAdd = \"Platziert eine Webpanel.\",\n    cmdPanelRemove = \"Entfernt das Webpanel auf das du schaust.\",\n    cmdTextAdd = \"Platziert einen Text in die Welt.\",\n    cmdTextRemove = \"Entfernt den Text auf den du schaust.\",\n    cmdMapSceneAdd = \"Platziert eine Kamera für eine Mapszene.\",\n    cmdMapSceneRemove = \"Entfernt eine Kamera für eine Mapszene.\",\n    cmdSpawnAdd = \"Fügt einen Spawnpoint für die Fraktion hinzu.\",\n    cmdSpawnRemove = \"Entfernt alle Spawnpoints in deinem Sichtfeld.\",\n    cmdAct = \"Führt die %s Animation aus.\",\n    cmdContainerSetPassword = \"Setzt das Passwort des Behälters auf den du schaust.\",\n    cmdDoorSell = \"Verkauft die tür auf die du schaust.\",\n    cmdDoorBuy = \"Kauft die tür auf die du schaust.\",\n    cmdDoorSetUnownable = \"Macht die Tür auf die du schaust nicht-besitzbar.\",\n    cmdDoorSetOwnable = \"Macht die Tür auf die du schaust wieder beitzbar.\",\n    cmdDoorSetFaction = \"Setzt die Tür auf die du schaust als Besitzt einer Fraktion\",\n    cmdDoorSetDisabled = \"Deaktiviert die Tür auf die du schaust.\",\n    cmdDoorSetTitle = \"Setzt den Namen der Tür auf die du schaust.\",\n    cmdDoorSetParent = \"Setzt den Parent der Tür auf die du schaust.\",\n    cmdDoorSetChild = \"Setzt das Child ein Paar von Türen.\",\n    cmdDoorRemoveChild = \"Entfernt das Child ein Paar von Türen.\",\n    cmdDoorSetHidden = \"Versteckt die Beschreibung der Tür auf die du schaust.\",\n    cmdDoorSetClass = \"Setzt die Tür auf die du schaust als Besitz einer bestimmten Klasse.\",\n    cmdMe = \"Führe eine lokale Aktion aus.\",\n    cmdIt = \"Führe eine lokale Aktion für ein Objekt in deiner Nähe aus.\",\n    cmdW = \"Flüstere den Personen in deiner Nähe etwas.\",\n    cmdY = \"Rufe den Personen um dich etwas zu.\",\n    cmdEvent = \"Führe eine globale Aktion aus.\",\n    cmdOOC = \"Sendet eine Nachricht in den globalen Out-of-Character Chat.\",\n    cmdLOOC = \"Sendet eine Nachricht in den lokalen Out-of-Character Chat.\",\n\n}\n"
  },
  {
    "path": "gamemode/languages/sh_korean.lua",
    "content": "\nNAME = \"한국어\"\n\nLANGUAGE = {\n\tloading = \"불러오는 중\",\n\tdbError = \"DB 서버 연결 실패\",\n\tunknown = \"알 수 없음\",\n\tnoDesc = \"정보가 존재하지 않습니다.\",\n\tcreate = \"생성\",\n\tcreateTip = \"새로운 캐릭터를 생성합니다.\",\n\tload = \"계속\",\n\tloadTip = \"플레이할 캐릭터를 불러옵니다.\",\n\tleave = \"종료\",\n\tleaveTip = \"서버에서 퇴장합니다.\",\n\t[\"return\"] = \"뒤로\",\n\treturnTip = \"이전 메뉴로 돌아갑니다.\",\n\tname = \"이름\",\n\tdescription = \"정보\",\n\tmodel = \"외관\",\n\tattributes = \"능력\",\n\tcharCreateTip = \"빈칸들을 채우고 아래 '완료' 버튼을 눌러 캐릭터를 생성하십시오.\",\n\tinvalid = \"다음 정보가 존재하지 않습니다: %s\",\n\tdescMinLen = \"정보는 적어도 %d 자 이상이어야 합니다.\",\n\tplayer = \"플레이어\",\n\tfinish = \"완료\",\n\tfinishTip = \"캐릭터 생성을 완료합니다.\",\n\tneedModel = \"올바른 외관을 선택하여야 합니다.\",\n\tcreating = \"캐릭터를 생성중입니다...\",\n\tunknownError = \"오류가 발생하였습니다.\",\n\tdelConfirm = \"%s 영구히 완전히 삭제합니까?\",\n\tno = \"아니오\",\n\tyes = \"예\",\n\titemInfo = \"이름: %s\\n정보: %s\",\n\tcloud_no_repo = \"클라우드 경로가 존재하지 않습니다.\",\n\tcloud_no_plugin = \"클라우드 추가 기능이 존재하지 않습니다.\",\n\tinv = \"인벤토리\",\n\tplugins = \"추가 기능\",\n\tauthor = \"제작자\",\n\tversion = \"버전\",\n\tcharacters = \"캐릭터\",\n\tbusiness = \"사업\",\n\tsettings = \"설정\",\n\tconfig = \"설정\",\n\tchat = \"대화\",\n\tappearance = \"외관\",\n\tmisc = \"기타\",\n\toocDelay = \"%s 초를 더 기다려야 OOC 대화가 가능합니다.\",\n\tloocDelay = \"%s 초를 더 기다려야 LOOC 대화가 가능합니다.\",\n\tusingChar = \"이미 이 캐릭터로 서버에서 플레이하고 있습니다.\",\n\tnotAllowed = \"당신은 이것을 할 권한이 없습니다.\",\n\titemNoExist = \"당신이 요청한 아이템은 존재하지 않습니다.\",\n\tcmdNoExist = \"당신이 요청한 명령은 존재하지 않습니다.\",\n\tplyNoExist = \"그 이름을 가진 플레이어를 검색할 수 없습니다.\",\n\tcfgSet = \"%s 님이 \\\"%s\\\" 를 %s 으로 설정하였습니다.\",\n\tdrop = \"버리기\",\n\tdropTip = \"아이템을 소지품에서 제외시킵니다.\",\n\ttake = \"가지기\",\n\ttakeTip = \"아이템을 소지품에 추가시킵니다.\",\n\tdTitle = \"소유되지 않은 문\",\n\tdTitleOwned = \"소유된 문\",\n\tdIsNotOwnable = \"이 문은 소유할 수 없습니다.\",\n\tdIsOwnable = \"F2를 눌러서 이 문을 소유할 수 있습니다.\",\n\tdMadeUnownable = \"당신은 이 문을 소유할 수 없도록 설정했습니다.\",\n\tdMadeOwnable = \"당신은 이 문을을 소유할 수 있도록 설정했습니다.\",\n\tdNotAllowedToOwn = \"이 문을 소유하도록 허가되지 않았습니다.\",\n\tdSetDisabled = \"당신은 이 문의 기능을 껐습니다.\",\n\tdSetNotDisabled = \"당신은 이 문의 기능을 다시 켰습니다.\",\n\tdSetHidden = \"당신은 이 문을 숨겼습니다.\",\n\tdSetNotHidden = \"당신은 이 문을 숨김 해제했습니다.\",\n\tdSetParentDoor = \"당신은 이 문을 상위 개체로 설정하였습니다.\",\n\tdCanNotSetAsChild = \"당신은 이 문을 하위 개체로 설정할 수 없습니다.\",\n\tdAddChildDoor = \"당신은 이 문을 하위 개체로 설정하였습니다.\",\n\tdRemoveChildren = \"당신은 이 문에 할당된 모든 하위 개체를 삭제했습니다.\",\n\tdRemoveChildDoor = \"당신은 이 문을 하위 개체에서 삭제했습니다.\",\n\tdNoParentDoor = \"상위 개체인 문이 없습니다.\",\n\tdOwnedBy = \"이 문은 %s 님의 소유입니다.\",\n\tdConfigName = \"문\",\n\tdSetFaction = \"이 문은 이제 %s 단체에 속하게 됩니다.\",\n\tdRemoveFaction = \"이 문은 이제 어느 단체에도 속하지 않습니다.\",\n\tdNotValid = \"유효한 문을 바라보고 있어야 합니다.\",\n\tcanNotAfford = \"이 문을 구매할 충분한 자금을 가지고 있지 않습니다.\",\n\tdPurchased = \"이 문을 %s으로 구매했습니다.\",\n\tdSold = \"당신은 이 문을 %s으로 판매했습니다.\",\n\tnotOwner = \"당신은 이 문을 소유하고 있지 않습니다.\",\n\tinvalidArg = \"#%s 번째 명령 변수에 올바른 값을 입력해야 합니다.\",\n\tinvalidFaction = \"제시된 이름으로 된 단체를 찾을 수 없습니다.\",\n\tflagGive = \"%s 님이  %s 님에게 '%s' 권한을 주었습니다.\",\n\tflagGiveTitle = \"권한 주기\",\n\tflagGiveDesc = \"이 권한들을 플레이어에게 줍니다.\",\n\tflagTake = \"%s 님이 '%s' 권한을 %s 님으로 부터 받았습니다.\",\n\tflagTakeTitle = \"플래그 가지기.\",\n\tflagTakeDesc = \"이 권한들을 플레이어에게서 뺏습니다.\",\n\tflagNoMatch = \"이 행동은 \\\"%s\\\" 권한을 필요로 합니다.\",\n\ttextAdded = \"텍스트를 추가하였습니다.\",\n\ttextRemoved = \"%s개의 택스트를 삭제하였습니다.\",\n\tmoneyTaken = \"%s 발견.\",\n\tbusinessPurchase = \"당신은 %s 을/를 %s에 구매하였습니다.\",\n\tbusinessSell = \"당신은 %s 을/를 %s에 판매했습니다.\",\n\tcChangeModel = \"%s님이 %s님의 외관을 교체했습니다: %s.\",\n\tcChangeName = \"%s님이 %s님의 이름을 교체했습니다: %s.\",\n\tcChangeSkin = \"%s 가 %s's 의 스킨을 %s 로 바꾸었습니다.\",\n\tcChangeGroups = \"%s 가 %s 의 \\\"%s\\\" 바디그룹을 %s 로 바꾸었습니다.\",\n\tcChangeFaction = \"%s 는 %s 를 %s 팩션으로 이동시켰습니다.\",\n\tplayerCharBelonging = \"이 물건은 당신의 다른 캐릭터의 물건입니다.\",\n\tspawnAdd = \"%s 개의 시작지점을 추가하였습니다.\",\n\tspawnDeleted = \"%s개의 시작지점을 삭제하였습니다.\",\n\tsomeone = \"누군가\",\n\trgnLookingAt = \"당신이 보고 있는 사람이 당신을 인식하도록 선언.\",\n\trgnWhisper = \"귓속말 거리에 있는 사람을 당신을 인식하도록 선언\",\n\trgnTalk = \"일반 대화 거리에 있는 사람을 당신을 인식하도록 선언\",\n\trgnYell = \"외침 대화 거리에 있는 사람을 당신을 인식하도록 선언\",\n\ticFormat = \"%s: \\\"%s\\\"\",\n\trollFormat = \"%s님이 주사위를 굴렸습니다: %s.\",\n\twFormat = \"%s(귓속말): \\\"%s\\\"\",\n\tyFormat = \"%s(외침): \\\"%s\\\"\",\n\tsbOptions = \"%s님에 대한 선택지를 보려면 클릭하십시오.\",\n\tspawnAdded = \"%s 단체를 위한 시작 지점이 추가되었습니다.\",\n\twhitelist = \"%s님이 %s님을 %s 단체에 들어가도록 허가했습니다.\",\n\tunwhitelist = \"%s님이 %s님을 %s 단체에 들어가는 것을 금지했습니다.\",\n\tgettingUp = \"몸을 일으키는 중입니다...\",\n\twakingUp = \"정신을 차리는 중입니다...\",\n\tWeapons = \"무기\",\n\tcheckout = \"물건 결제 (%s)\",\n\tpurchase = \"구매\",\n\tpurchasing = \"결제 진행중...\",\n\tsuccess = \"성공\",\n\tbuyFailed = \"결제 실패.\",\n\tbuyGood = \"결제가 완료되었습니다!\",\n\tshipment = \"소유물\",\n\tshipmentDesc = \"이 소유물은 %s님의 명의로 되어있습니다.\",\n\tclass = \"직업\",\n\tclasses = \"직업\",\n\tillegalAccess = \"잘못된 접근입니다.\",\n\tbecomeClassFail = \"%s이/가 되는 것에 실패했습니다.\",\n\tbecomeClass = \"%s이/가 되었습니다.\",\n\tattributeSet = \"당신은 %s님의 %s을/를 %s로 설정하였습니다.\",\n\tattributeUpdate = \"당신은 %s님의 %s을/를 %s만큼 추가하였습니다.\",\n\tnoFit = \"소지품 공간이 부족합니다.\",\n\thelp = \"도움말\",\n\tcommands = \"명령어\",\n\thelpDefault = \"목차 선택\",\n\tdoorSettings = \"문 설정\",\n\tsell = \"판매\",\n\taccess = \"접근\",\n\tlocking = \"이 물건을 잠그는 중입니다...\",\n\tunlocking = \"이 물건을 여는 중입니다...\",\n\tmodelNoSeq = \"당신의 외관은 이 행동을 지원하지 않습니다.\",\n\tnotNow = \"당신은 아직 이 행동을 할 수 없습니다.\",\n\tfaceWall = \"이 행동을 위해선 벽을 바라보고 있어야 합니다.\",\n\tfaceWallBack = \"이 행동을 위해선 벽을 등지고 있어야 합니다.\",\n\tdescChanged = \"당신의 캐릭터의 정보를 변경했습니다.\",\n\tcharMoney = \"당신의 소지금은 %s 입니다.\",\n\tcharFaction = \"당신은 %s 단체에 소속되어 있습니다.\",\n\tcharClass = \"당신은 이 단체의 %s 입니다.\",\n\tnoOwner = \"소유자가 존재하지 않습니다.\",\n\tinvalidIndex = \"아이템의 구분 번호가 올바르지 않습니다.\",\n\tinvalidItem = \"아이템 객체 참조가 잘못되었습니다.\",\n\tinvalidInventory = \"소지품 객체 참조가 잘못되었습니다.\",\n\thome = \"초기\",\n\tcharKick = \"%s님이 %s님의 캐릭터를 추방하였습니다.\",\n\tcharBan = \"%s님이 %s님의 캐릭터를 영구히 추방하였습니다.\",\n\tcharBanned = \"이 캐릭터는 사용이 금지되었습니다.\",\n\tsetMoney = \"당신은 %s님의 돈을 %s으로 설정하였습니다.\",\n\titemPriceInfo = \"이 아이템을 %s에 구매가 가능합니다.\\n이 아이템을 %s에 탄매가 가능합니다\",\n\tfree = \"무료\",\n\tvendorNoSellItems = \"판매할 아이템이 없습니다.\",\n\tvendorNoBuyItems = \"구매할 아이템이 없습니다.\",\n\tvendorSettings = \"상인 설정\",\n\tvendorUseMoney = \"상인에 제한된 돈\",\n\tvendorNoBubble = \"말풍선 보이기\",\n\tmode = \"상태\",\n\tprice = \"가격\",\n\tstock = \"재고\",\n\tnone = \"없음\",\n\tvendorBoth = \"판매와 구매\",\n\tvendorBuy = \"구매 전용\",\n\tvendorSell = \"판매 전용\",\n\tmaxStock = \"최대 재고\",\n\tvendorFaction = \"팩션 에디터\",\n\tbuy = \"구매\",\n\tvendorWelcome = \"어서오세요. 무엇을 찾으십니까?\",\n\tvendorBye = \"다음에 또 오세요!\",\n\tcharSearching = \"이미 캐릭터를 수색하고 있습니다.\",\n\tcharUnBan = \"%s 님이 다음 캐릭터를 금지 해제했습니다: %s.\",\n\tcharNotBanned = \"이 캐릭터는 금지되지 않았습니다.\",\n\tcontainerPassword = \"이 보관함의 암호를 %s 으로 설정하였습니다.\",\n\tcontainerPasswordRemove = \"이 보관함의 암호를 삭제했습니다.\",\n\tcontainerPasswordWrite = \"암호를 입력해 주십시오.\",\n\twrongPassword = \"암호가 다릅니다.\",\n\tcheapBlur = \"블러 효과 사용 (FPS 향상)\",\n\tquickSettings = \"빠른 설정\",\n\tvmSet = \"개인 귓속말을 설정했습니다.\",\n\tvmRem = \"개인 귓속말을 삭제했습니다.\",\n\taltLower = \"주먹 미사용시 숨김\",\n\tnoPerm = \"이 행위를 할 권한이 없습니다.\",\n\tyoureDead = \"당신은 죽었습니다\",\n\tinjMajor = \"중상을 입음.\",\n\tinjLittle = \"부상을 입음.\",\n\ttoggleESP = \"어드민 월핵 사용\",\n\tchgName = \"이름 변경\",\n\tchgNameDesc = \"아래에 캐릭터의 새로운 이름을 입력하세요.\",\n\tthirdpersonToggle = \"3인칭 사용\",\n\tthirdpersonClassic = \"클래식 3인칭 사용\",\n\tequippedBag = \"가방 내부에 사용중인 아이템이 있습니다.\",\n\tuseTip = \"이 아이템을 사용합니다.\",\n\tequipTip = \"이 아이템을 착용합니다.\",\n\tunequipTip = \"이 아이템을 착용해제합니다.\",\n\tconsumables = \"소모품\",\n\tplyNotValid = \"당신은 잘못된 플레이어를 보고있습니다.\",\n\trestricted = \"당신은 저지되었습니다.\",\n\tviewProfile = \"스팀 프로필 보기\",\n\tsalary = \"당신은 월급으로 부터 &s 만큼의 돈이 들어왔습니다.\",\n\tnoRecog = \"당신은 이 사람을 인식하지 않았습니다.\",\n\tcurTime = \"지금 시각은 %s.\",\n\tvendorEditor = \"상인 수정\",\n\tedit = \"수정\",\n\tdisable = \"해제\",\n\tvendorPriceReq = \"이 물품의 새로운 가격을 적으십시오.\",\n\tvendorEditCurStock = \"현재 재고 수정\",\n\tyou = \"당신\",\n\tvendorSellScale = \"판매 가격 규모\",\n\tvendorNoTrade = \"당신은 이 상인과 거래 할수없습니다.\",\n\tvendorNoMoney = \"이 상인은 해당 물품을 사 들일수 없습니다.\",\n\tvendorNoStock = \"이 상인은 해당 물품의 재고가 없습니다.\",\n\tcontentTitle = \"Helix 콘텐츠 없음.\",\n\tcontentWarning = \"당신은 Helix 콘텐츠가 적용되어있지 않습니다. 특정 기능이 누락될 수 있습니다.\\nHelix 콘텐츠를 적용해야 합니다.\",\n\tflags = \"플래그\"\n}\n"
  },
  {
    "path": "gamemode/languages/sh_norwegian.lua",
    "content": "\nNAME = \"Norwegian\"\n\nLANGUAGE = {\n\tloading = \"Laster\",\n\tdbError = \"Database tilkobling feilet\",\n\tunknown = \"Ukjent\",\n\tnoDesc = \"Ingen beskrivelse tilgjengelig\",\n\tcreate = \"Lag\",\n\tcreateTip = \"Lag en ny karakter til å spille med.\",\n\tload = \"Last\",\n\tloadTip = \"Velg en tidligere brukt karakter til å spille med.\",\n\tleave = \"Forlat\",\n\tleaveTip = \"Forlat den nåværende serveren.\",\n\t[\"return\"] = \"Tilbake\",\n\treturnTip = \"Tilbake til den forrige menyen.\",\n\tname = \"Navn\",\n\tdescription = \"Beskrivelse\",\n\tmodel = \"Modell\",\n\tattributes = \"Attributter\",\n\tcharCreateTip = \"Fyll inn feltene nedenfor og trykk på 'Fullfør' for å skape din karakter.\",\n\tinvalid = \"Du har gitt et ugyldig %s\",\n\tdescMinLen = \"Din beskrivelse må være minst %d bokstav(er).\",\n\tplayer = \"Spiller\",\n\tfinish = \"Fullfør\",\n\tfinishTip = \"Fullfør med å lage karakteren.\",\n\tneedModel = \"Du må velge en gyldig modell\",\n\tcreating = \"Din karakter blir skapt...\",\n\tunknownError = \"Det har oppstått en ukjent feil\",\n\tdelConfirm = \"Er du sikker på at du vil PERMANENT slette %s?\",\n\tno = \"Nei\",\n\tyes = \"Ja\",\n\titemInfo = \"Navn: %s\\nDescription: %s\",\n\tcloud_no_repo = \"The repository provided is not valid.\",\n\tcloud_no_plugin = \"The plugin provided is not valid.\",\n\tinv = \"Inventar\",\n\tplugins = \"Tillegg\",\n\tauthor = \"Forfatter\",\n\tversion = \"Versjon\",\n\tcharacters = \"Karakterer\",\n\tbusiness = \"Handel\",\n\tsettings = \"Innstillinger\",\n\tconfig = \"Konfigurasjon\",\n\tchat = \"Chat\",\n\tappearance = \"Utseende\",\n\tmisc = \"Diverse\",\n\toocDelay = \"Du må vente %s sekund(er) med å bruke OOC igjen.\",\n\tloocDelay = \"Du må vente %s sekund(er) med å bruke LOOC igjen.\",\n\tusingChar = \"Du bruker allerede denne karakteren.\",\n\titemNoExist = \"Beklager, det elementet du forespurte finnes ikke.\",\n\tcmdNoExist = \"Beklager, den kommandoen ekisterer ikke.\",\n\tplyNoExist = \"Beklager, en matchende spiller ble ikke funnet.\",\n\tcfgSet = \"%s har satt \\\"%s\\\" til %s.\",\n\tdrop = \"Frigjør\",\n\tdropTip = \"Frigjør dette elementet fra ditt inventar.\",\n\ttake = \"Ta\",\n\ttakeTip = \"Ta dette elementet og putt det i inventaret ditt.\",\n\tdTitle = \"Ueid Dør\",\n\tdTitleOwned = \"Kjøpt Dør\",\n\tdIsNotOwnable = \"Denne døren er ikke mulig å kjøpe.\",\n\tdIsOwnable = \"Du kan kjøpe denne døren med å trykke på F2.\",\n\tdMadeUnownable = \"Du har gjordt denne døren umulig å kjøpe.\",\n\tdMadeOwnable = \"Du har gjordt denne døren mulig å kjøpe.\",\n\tdNotAllowedToOwn = \"Du er ikke tillatt å eie denne døra.\",\n\tdSetDisabled = \"Du har deaktivert denne døren.\",\n\tdSetNotDisabled = \"Du har gjordt denne døren ikke lenger deaktivert.\",\n\tdSetHidden = \"Du har gjordt denne døren gjemt.\",\n\tdSetNotHidden = \"Du har gjordt at døren ikke er gjemt lenger.\",\n\tdSetParentDoor = \"Du har gjort denne døren, hoveddøren.\",\n\tdCanNotSetAsChild = \"Du kan ikke sette hoveddøren som sekundær døren.\",\n\tdAddChildDoor = \"Du har lagt til en sekundær dør.\",\n\tdRemoveChildren = \"Du har fjernet alle sekundære dører fra denne døren.\",\n\tdRemoveChildDoor = \"Du har fjernet denne døren fra å være en sekundær dør.\",\n\tdNoParentDoor = \"Du har ikke en hoveddør satt.\",\n\tdOwnedBy = \"Denne døren er eid av %s.\",\n\tdConfigName = \"Dører\",\n\tdSetFaction = \"Denne døren tilhører %s gruppen.\",\n\tdRemoveFaction = \"Denne døren tilhører ikke en gruppe lenger.\",\n\tdNotValid = \"Du ser ikke på en gyldig dør.\",\n\tcanNotAfford = \"Du har ikke råd til å kjøpe dette.\",\n\tdPurchased = \"Du har kjøpt denne døren for %s.\",\n\tdSold = \"Du har solgt denne døren for %s.\",\n\tnotOwner = \"Du er ikke eieren av dette.\",\n\tinvalidArg = \"Du har gitt en ugyldig verdi for argumentet #%s.\",\n\tflagGive = \"%s har gitt %s '%s' flaggene.\",\n\tflagGiveTitle = \"Gi Flagg\",\n\tflagGiveDesc = \"Gi de følgene flaggene til en spiller.\",\n\tflagTake = \"%s har tatt '%s' flaggene fra %s.\",\n\tflagTakeTitle = \"Ta Flagg\",\n\tflagTakeDesc = \"Fjern de følgene flaggene\t til en spiller.\",\n\tflagNoMatch = \"Du må ha \\\"%s\\\" Flagg for å gjøre denne handlingen.\",\n\ttextAdded = \"Du har lagt til en tekst.\",\n\ttextRemoved = \"Du har fjernet %s tekst(er).\",\n\tmoneyTaken = \"Du har funnet %s.\",\n\tbusinessPurchase = \"Du har kjøpt %s for %s.\",\n\tbusinessSell = \"Du har solgt %s for %s.\",\n\tcChangeModel = \"%s endret %s's modell til %s.\",\n\tcChangeName = \"%s endret %s's navn til %s.\",\n\tcChangeSkin = \"%s endret %s's skin til %s.\",\n\tcChangeGroups = \"%s endret %s's \\\"%s\\\" kroppsgruppe to %s.\",\n\tcChangeFaction = \"%s har overført %s til %s gruppen.\",\n\tplayerCharBelonging = \"Dette objektet tilhører en av dine andre karakterer.\",\n\tinvalidFaction = \"Du har gitt en ugyldig gruppe.\",\n\tspawnAdd = \"Du har lagt til spawnen for %s.\",\n\tspawnDeleted = \"Du har fjernet %s spawn punkt(er).\",\n\tsomeone = \"Noen\",\n\trgnLookingAt = \"Tillat personen du ser på å gjenkjenne deg.\",\n\trgnWhisper = \"Tillat de innen hviske radius å gjenkjenne deg.\",\n\trgnTalk = \"Tillat de innen prate radius å gjenkjenne deg.\",\n\trgnYell = \"Tillat de innen rope radius å gjenkjenne deg.\",\n\ticFormat = \"%s sier \\\"%s\\\"\",\n\trollFormat = \"%s har rullet %s.\",\n\twFormat = \"%s hvisker \\\"%s\\\"\",\n\tyFormat = \"%s roper \\\"%s\\\"\",\n\tsbOptions = \"Klikk for å se instillingene for %s.\",\n\tspawnAdded = \"Du har lagt til spawnen for %s.\",\n\twhitelist = \"%s har hvitelistet %s for %s gruppen.\",\n\tunwhitelist = \"%s har fjernet %s fra hvitelisten til %s gruppen.\",\n\tgettingUp = \"Du reiser deg opp...\",\n\twakingUp = \"Du er kommer til bevissthet...\",\n\tWeapons = \"Våpen\",\n\tcheckout = \"Gå til kassen (%s)\",\n\tpurchase = \"Kjøp\",\n\tpurchasing = \"Kjøp = er...\",\n\tsuccess = \"Suksess\",\n\tbuyFailed = \"Kjøpet mislyktes.\",\n\tbuyGood = \"Kjøp vellykket!\",\n\tshipment = \"Forsendelsen\",\n\tshipmentDesc = \"Denne forsendelsen tilhører %s.\",\n\tclass = \"Klasse\",\n\tclasses = \"Klasser\",\n\tillegalAccess = \"Ulovlig Tilgang.\",\n\tbecomeClassFail = \"Klarte ikke å bli %s.\",\n\tbecomeClass = \"Du har bltt %s.\",\n\tattributeSet = \"Du har satt %s's %s til %s.\",\n\tattributeUpdate = \"Du har lagt til %s's %s av %s.\",\n\tnoFit = \"Dette elementet kan ikke passe i inventaret ditt.\",\n\thelp = \"Hjelp\",\n\tcommands = \"Kommandoer\",\n\thelpDefault = \"Velg et katagori\",\n\tdoorSettings = \"Dør innstillinger\",\n\tsell = \"Selg\",\n\taccess = \"Tilgang\",\n\tlocking = \"Låser denne enheten...\",\n\tunlocking = \"Låser opp denne enheten...\",\n\tmodelNoSeq = \"Din modell støtter ikke denne handlingen.\",\n\tnotNow = \"Du er ikke tillatt.\",\n\tfaceWall = \"Du må stå mot veggen for å gjøre dette.\",\n\tfaceWallBack = \"Din rygg må stå mot veggen for å gjøre dette.\",\n\tdescChanged = \"Du har endret din karakters beskrivelse.\",\n\tcharMoney = \"Du har akkurat nå %s.\",\n\tcharFaction = \"Du er et medlem av denne %s gruppen.\",\n\tcharClass = \"Du er %s i gruppen.\",\n\tnoOwner = \"Eieren er ugyldig.\",\n\tnotAllowed = \"Denne handlingen er ikke tillatt.\",\n\tinvalidIndex = \"Elementet's Index er ugyldig.\",\n\tinvalidItem = \"Element Objektet er ugyldig.\",\n\tinvalidInventory = \"Inventar objektet\ter ugyldig.\",\n\thome = \"Hjem\",\n\tcharKick = \"%s sparket karakteren %s.\",\n\tcharBan = \"%s utestengte karakteren %s.\",\n\tcharBanned = \"Denne karakteren er utestengt.\",\n\tsetMoney = \"Du har satt %s's penger til %s.\",\n\titemPriceInfo = \"Du kan kjøpe dette elementet for %s.\\nDu kan kjøpe dette elementet for %s\",\n\tfree = \"Gratis\",\n\tvendorNoSellItems = \"Det er ingen elementer for å selle.\",\n\tvendorNoBuyItems = \"Det er ingen elementer til å kjøpe.\",\n\tvendorSettings = \"Leverandør Innstillinger\",\n\tvendorUseMoney = \"Leverandør skal bruke penger?\",\n\tvendorNoBubble = \"Gjem leverandør bobblen?\",\n\tmode = \"Modus\",\n\tprice = \"Pris\",\n\tstock = \"På lager\",\n\tnone = \"Ingen\",\n\tvendorBoth = \"Kjøp og selg\",\n\tvendorBuy = \"Kun Kjøp\",\n\tvendorSell = \"Kun Selg\",\n\tmaxStock = \"Maks på lager\",\n\tvendorFaction = \"Gruppe endrer\",\n\tbuy = \"Kjøp\",\n\tvendorWelcome = \"Velkommen til butikken min, hva kan jeg gi deg i dag?\",\n\tvendorBye = \"Kom tilbake snart!\",\n\tcharSearching = \"Du søker allerede etter en annen karakter, vennligst vent.\",\n\tcharUnBan = \"%s har fjernet %s fra karakter utestengt listen.\",\n\tcharNotBanned = \"Denne karakteren er ikke utestengt.\",\n\tcontainerPassword = \"Du har satt dette lagerets passord til %s.\",\n\tcontainerPasswordRemove = \"Du har fjernet dette lagerets passord.\",\n\tcontainerPasswordWrite = \"Skriv inn passordet.\",\n\twrongPassword = \"Du har skrivd inn feil passord.\",\n\tcheapBlur = \"Deaktiver uklarhet? (Bedre FPS)\",\n\tquickSettings = \"Hurtiginnstillinger\",\n\tvmSet = \"Du har satt ditt mobilsvar.\",\n\tvmRem = \"Du har fjernet ditt mobilsvar.\",\n\taltLower = \"Gjemme hendene når senket?\",\n\tnoPerm = \"Du er ikke tillatt til å gjøre dette.\",\n\tyoureDead = \"Du er død.\",\n\tinjMajor = \"Ser kritisk skadd ut.\",\n\tinjLittle = \"Ser skadd ut.\",\n\ttoggleESP = \"Veksle Admin ESP\",\n\tchgName = \"Endre navn\",\n\tchgNameDesc = \"Skriv in karakterens nye navn under.\",\n\tthirdpersonToggle = \"Veksle Tredje-Person\",\n\tthirdpersonClassic = \"Bruk klassisk Tredje-Person\",\n\tequippedBag = \"Posen at du flyttet har utstyrt element.\",\n\tuseTip = \"Bruk dette elementet.\",\n\tequipTip = \"Ta på dette elementet.\",\n\tunequipTip = \"Ta av dette elementet.\",\n\tconsumables = \"Forbruksvarer\",\n\tplyNotValid = \"Du ser ikke på en gyldig spiller.\",\n\trestricted = \"Du har blitt tilbakeholdne.\",\n\tviewProfile = \"Se Steam Profil\",\n\tsalary = \"Du har motatt %s fra lønnen din.\",\n\tnoRecog = \"Du kjenner ikke denne personen.\",\n\tcurTime = \"Tiden er %s.\",\n\tvendorEditor = \"Leverandør Redigering\",\n\tedit = \"Rediger\",\n\tdisable = \"Deaktiver\",\n\tvendorPriceReq = \"Skriv in den nye prisen for dette elementet.\",\n\tvendorEditCurStock = \"Rediger nåværende lager\",\n\tyou = \"Du\",\n\tvendorSellScale = \"Utsalgspris skala\",\n\tvendorNoTrade = \"Du er ikke i stand til å handle med denne leverandøren.\",\n\tvendorNoMoney = \"Denne leverandøren ikke har råd til dette elementet.\",\n\tvendorNoStock = \"Denne leverandøren har ikke at varen på lager.\",\n\tcontentTitle = \"Helix Innhold Mangler\",\n\tcontentWarning = \"Du har ikke Helix innhold montert. Dette kan føre til enkelte funksjoner mangler. \\nVil du åpne Workshop side for Helix innhold?\",\n\tflags = \"Flagg\"\n}\n"
  },
  {
    "path": "gamemode/languages/sh_polish.lua",
    "content": "-- Autorzy: zgredinzyyy (Poprawki + Brakujące rzeczy) || Veran120, Michał, Lechu2375 https://github.com/lechu2375/helix-polishlocalization/blob/master/sh_polish.lua\n\nNAME = \"Polski\"\n\nLANGUAGE = {\n\thelix = \"Helix\",\n\n\tintroTextOne = \"fist industries prezentuje\",\n\tintroTextTwo = \"w współpracy z %s\",\n\tintroContinue = \"wciśnij spację by kontynuować\",\n\n\thelpIdle = \"Wybierz kategorię\",\n\thelpCommands = \"Komendy z parametrami w <strzałkach> są wymagane, a w [nawiasach kwadratowych] są opcjonalne\",\n\thelpFlags = \"Flagi z zielonym tłem są dostępne przez tą postać.\",\n\n\tcreditSpecial = \"Podziękowania dla\",\n\tcreditLeadDeveloper = \"Główny Developer\",\n\tcreditUIDesigner = \"UI Designer\",\n\tcreditManager = \"Project Manager\",\n\tcreditTester = \"Lead Tester\",\n\n\tchatTyping = \"Pisze...\",\n\tchatTalking = \"Mówi...\",\n\tchatYelling = \"Krzyczy...\",\n\tchatWhispering = \"Szepcze...\",\n\tchatPerforming = \"Wykonuje...\",\n\tchatNewTab = \"Nowa karta\",\n\tchatReset = \"Zresetuj pozycję\",\n\tchatResetTabs = \"Resetuj karty\",\n\tchatCustomize = \"Personalizuj...\",\n\tchatCloseTab = \"Zamknij kartę\",\n\tchatTabName = \"Nazwa karty\",\n\tchatAllowedClasses = \"Dostępne Klasy czatów\",\n\tchatTabExists = \"Karta z taką nazwą już instnieje!\",\n\tchatMarkRead = \"Odznacz wszystko jako przeczytane\",\n\n\tcommunity = \"Community\",\n\tcheckAll = \"Zaznacz wszystko\",\n\tuncheckAll = \"Odznacz wszystko\",\n\tcolor = \"Kolor\",\n\ttype = \"Typ\",\n\tdisplay = \"Wyświetlanie\",\n\tloading = \"Ładowanie\",\n\tdbError = \"Połączenie z bazą danych nie powiodło się\",\n\tunknown = \"Nieznane\",\n\tnoDesc = \"Opis niedostępny\",\n\tcreate = \"Stwórz\",\n\tupdate = \"Zaktualizuj\",\n\tload = \"Załaduj postać\",\n\tloadTitle = \"Załaduj swoją postać\",\n\tleave = \"Opuść serwer\",\n\tleaveTip = \"Opuść ten serwer.\",\n\t[\"return\"] = \"Powrót\",\n\treturnTip = \"Powróć do poprzedniego menu.\",\n\tproceed = \"Kontynuuj\",\n\tfaction = \"Frakcja\",\n\tskills = \"Umiejętności\",\n\tchoose = \"Wybierz\",\n\tchooseFaction = \"Wybierz Frakcje\",\n\tchooseDescription = \"Zdefiniuj swoją postać\",\n\tchooseSkills = \"Dostosuj swoje umiejętności\",\n\tname = \"Imię i Nazwisko\",\n\tdescription = \"Opis\",\n\tmodel = \"Model\",\n\tattributes = \"Atrybuty\",\n\tattribPointsLeft = \"Pozostałe punkty\",\n\tcharInfo = \"Informacje o postaci\",\n\tcharCreated = \"Udało ci się stworzyć swoją postać.\",\n\tcharCreateTip = \"Wypełnij pola poniżej i klinij 'Zakończ' aby stworzyć swoją postać.\",\n\tinvalid = \"Podałeś niewłaściwe(ą) %s\",\n\tnameMinLen = \"Twoje imię musi zawierać conajmniej %d znaków!\",\n\tnameMaxLen = \"Twoje imię nie może posiadać więcej niż %d znaków!\",\n\tdescMinLen = \"Twoje opis musi zawierać conajmniej %d znaków!\",\n\tmaxCharacters = \"Nie możesz utworzyć więcej postaci!\",\n\tplayer = \"Gracz\",\n\tfinish = \"Zakończ\",\n\tfinishTip = \"Ukończ tworzenie postaci.\",\n\tneedModel = \"Musisz wybrać poprawny model!\",\n\tcreating = \"Twoja postać jest aktualnie tworzona...\",\n\tunknownError = \"Wystąpił nieznany błąd!\",\n\tareYouSure = \"jesteś pewien?\",\n\tdelete = \"Usuń\",\n\tdeleteConfirm = \"Ta postać będzie bezpowrotnie usunięta!\",\n\tdeleteComplete = \"%s został(a) usunięty(a).\",\n\tno = \"Nie\",\n\tyes = \"Tak\",\n\tclose = \"Zamknij\",\n\tsave = \"Zapisz\",\n\titemInfo = \"Imię Nazwisko: %s\\nOpis: %s\",\n\titemCreated = \"Przedmiot(y) pomyślnie utworzony.\",\n\tcloud_no_repo = \"Repozytorium, które zostało podane jest nieprawidłowe.\",\n\tcloud_no_plugin = \"Podany plugin jest nieprawidłowy.\",\n\tinv = \"Ekwipunek\",\n\tplugins = \"Pluginy\",\n\tpluginLoaded = \"%s włączył \\\"%s\\\" plugin na następne załadowanie mapy.\",\n\tpluginUnloaded = \"%s wyłączył \\\"%s\\\" plugin z następnego załadowania mapy.\",\n\tloadedPlugins = \"Załadowane Pluginy\",\n\tunloadedPlugins = \"Niezaładowane Pluginy\",\n\ton = \"On\",\n\toff = \"Off\",\n\tauthor = \"Autor\",\n\tversion = \"Wersja\",\n\tcharacters = \"Postacie\",\n\tbusiness = \"Biznes\",\n\tsettings = \"Opcje\",\n\tconfig = \"Konfiguracja\",\n\tchat = \"Czat\",\n\tappearance = \"Wygląd\",\n\tmisc = \"Różne\",\n\toocDelay = \"Musisz poczekać %s sekund przed ponownym użyciem OOC.\",\n\tloocDelay = \"Musisz poczekać %s sekund przed ponownym użyciem LOOC.\",\n\tusingChar = \"Aktualnie używasz tej postaci.\",\n\tnotAllowed = \"Przepraszamy, nie masz uprawnień do zrobienia tego.\",\n\titemNoExist = \"Przedmiot o który prosiłeś nie istnieje.\",\n\tcmdNoExist = \"Taka komenda nie istnieje.\",\n\tcharNoExist = \"Nie znaleziono pasującej postaci.\",\n\tplyNoExist = \"Nie znaleziono pasującego gracza.\",\n\tcfgSet = \"%s ustawił \\\"%s\\\" na %s.\",\n\tdrop = \"Wyrzuć\",\n\tdropTip = \"Upuszcza ten przedmiot z twojego ekwipunku.\",\n\ttake = \"Podnieś\",\n\ttakeTip = \"Weź ten przedmiot i umieść go w swoim ekwipunku.\",\n\tdTitle = \"Drzwi do kupienia\",\n\tdTitleOwned = \"Wykupione Drzwi\",\n\tdIsNotOwnable = \"Tych drzwi nie można kupić.\",\n\tdIsOwnable = \"Możesz kupić te drzwi naciskając F2.\",\n\tdMadeUnownable = \"Uczyniłeś te drzwi niemożliwymi do kupienia.\",\n\tdMadeOwnable = \"Uczyniłeś te drzwi możliwymi do kupienia.\",\n\tdNotAllowedToOwn = \"Nie możesz kupić tych drzwi.\",\n\tdSetDisabled = \"Wyłączyłeś te drzwi z użytku.\",\n\tdSetNotDisabled = \"Ponownie można używać tych drzwi.\",\n\tdSetHidden = \"Schowałeś te drzwi.\",\n\tdSetNotHidden = \"Usunąłeś te drzwi z ukrytych.\",\n\tdSetParentDoor = \"Uczyniłeś te drzwi swoimi drzwiami nadrzędnymi.\",\n\tdCanNotSetAsChild = \"Nie możesz ustawi aby drzwi nadrzędne były drzwiami podrzędnymi.\",\n\tdAddChildDoor = \"You have added a this door as a child.\",\n\tdRemoveChildren = \"Usunąłeś wszystkie drzwi podrzędne należące do tych drzwi.\",\n\tdRemoveChildDoor = \"Te drzwi już nie są drzwiami podrzędnymi.\",\n\tdNoParentDoor = \"Nie masz ustawionych drzwi nadrzędnych.\",\n\tdOwnedBy = \"Te drzwi należą do %s.\",\n\tdConfigName = \"Drzwi\",\n\tdSetFaction = \"Te drzwi należą teraz do frakcji %s.\",\n\tdRemoveFaction = \"Te drzwi już nie należą do żadnej frakcji.\",\n\tdNotValid = \"Nie patrzysz na prawidłowe drzwi.\",\n\tcanNotAfford = \"Nie stać Cię na kupienie tego.\",\n\tdPurchased = \"Kupiłeś te drzwi za %s.\",\n\tdSold = \"Sprzedałeś te drzwi za %s.\",\n\tnotOwner = \"Nie jesteś właścicielem tego.\",\n\tinvalidArg = \"Podałeś niepoprawną wartość dla argumentu #%s.\",\n\tinvalidFaction = \"Frakcja, którą podałeś nie została znaleziona!\",\n\tflagGive = \"%s dał %s następujące flagi: '%s'.\",\n\tflagGiveTitle = \"Daj Flagi\",\n\tflagTake = \"%s zabrał od %s następujące flagi: '%s'.\",\n\tflagTakeTitle = \"Zabierz Flagi\",\n\tflagNoMatch = \"Musisz posiadać flagę(i) \\\"%s\\\" aby wykonać tą czynność.\",\n\tpanelAdded = \"Dodałeś nowy panel.\",\n\tpanelRemoved = \"Usunąłęś %d panel(e)\",\n\ttextAdded = \"Dodałeś tekst.\",\n\ttextRemoved = \"Usunąłeś %s tekst(y).\",\n\tmoneyTaken = \"Znalazłeś %s.\",\n\tmoneyGiven = \"Otrzymałeś %s.\",\n\tinsufficientMoney = \"Nie posiadasz tyle środków!\",\n\tbusinessPurchase = \"Kupiłeś %s za %s.\",\n\tbusinessSell = \"Sprzedałeś %s za %s.\",\n\tbusinessTooFast = \"Zaczekaj przed następnym kupnem!\",\n\tcChangeModel = \"%s zmienił model gracza %s na %s.\",\n\tcChangeName = \"%s zmienił imię gracza %s na %s.\",\n\tcChangeSkin = \"%s zmienił model %s na %s.\",\n\tcChangeGroups = \"%s zmienił bodygroupy %s \\\"%s\\\" na %s.\",\n\tcChangeFaction = \"%s przeniósł %s do frakcji %s.\",\n\tplayerCharBelonging = \"Ten przedmiot należy do innej postaci należącej do Ciebie.\",\n\tspawnAdd = \"Dodałeś punkt odradzania dla %s.\",\n\tspawnDeleted = \"Usunąłeś %s punkt(y) odradzania się.\",\n\tsomeone = \"Ktoś\",\n\trgnLookingAt = \"Pozwól osobie na którą patrzysz, aby Cię rozpoznawała.\",\n\trgnWhisper = \"Pozwól tym, którzy są w zasięgu Twoich szeptów, aby Cię rozpoznawali.\",\n\trgnTalk = \"Pozwól tym, którzy są w zasięgu normalnych rozmów, aby Cię rozpoznawali.\",\n\trgnYell = \"Pozwól tym, którzy są w zasięgu Twoich krzyków, aby Cię rozpoznawali.\",\n\ticFormat = \"%s mówi: \\\"%s\\\"\",\n\trollFormat = \"%s wylosował %s.\",\n\twFormat = \"%s szepcze: \\\"%s\\\"\",\n\tyFormat = \"%s krzyczy: \\\"%s\\\"\",\n\tsbOptions = \"Kliknij aby zobaczyć opcje dla %s.\",\n\tspawnAdded = \"Dodałeś punkt odradzania dla %s.\",\n\twhitelist = \"%s dodał %s na whitelistę frakcji %s.\",\n\tunwhitelist = \"%s usunął %s z whitelisty frakcji %s.\",\n\tnoWhitelist = \"Nie masz dostępu do whitelisty na tą postać!\",\n\tgettingUp = \"Podnosisz się...\",\n\twakingUp = \"Wraca Ci świadomość...\",\n\tWeapons = \"Broń\",\n\tcheckout = \"Idź do kasy (%s)\",\n\tpurchase = \"Kup\",\n\tpurchasing = \"Kupuję...\",\n\tsuccess = \"Sukces\",\n\tbuyFailed = \"Zakupy nie powiodły się.\",\n\tbuyGood = \"Zakupy udane!\",\n\tshipment = \"Dostawa\",\n\tshipmentDesc = \"Ta dostawa należy do %s.\",\n\tclass = \"Klasa\",\n\tclasses = \"Klasy\",\n\tillegalAccess = \"Nielegalny Dostęp.\",\n\tbecomeClassFail = \"Nie udało Ci się zostać %s.\",\n\tbecomeClass = \"Zostałeś %s.\",\n\tsetClass = \"Ustawiłeś klasę %s na %s.\",\n\tattributeSet = \"Ustawiłeś %s %s na %s.\",\n\tattributeNotFound = \"You have specified an invalid attribute!\",\n\tattributeUpdate = \"Podwyższyłeś %s %s o %s.\",\n\tnoFit = \"Nieposiadasz wystarczająco miejsca, aby zmieścić ten przedmiot!\",\n\titemOwned = \"Nie możesz wchodzić w interakcje z przedmiotami, które posiadasz jako inna postać!\",\n\thelp = \"Pomoc\",\n\tcommands = \"Komendy\",\n\tdoorSettings = \"Ustawienia Drzwi\",\n\tsell = \"Sprzedaj\",\n\taccess = \"Dostęp\",\n\tlocking = \"Blokowanie tego przedmiotu...\",\n\tunlocking = \"Odblokowywanie tego przedmiotu...\",\n\tmodelNoSeq = \"Twój model nie obsługuje tej animacji.\",\n\tnotNow = \"Nie możesz tego aktualnie zrobić.\",\n\tfaceWall = \"Musisz patrzeć na ścianę aby to wykonać.\",\n\tfaceWallBack = \"Musisz stać tyłem do ściany aby to wykonać.\",\n\tdescChanged = \"Zmieniłeś rysopis swojej postaci.\",\n\tnoOwner = \"Nieprawidłowy właściciel.\",\n\tinvalidItem = \"Wskazałeś nieprawidłowy przedmiot!\",\n\tinvalidInventory = \"Wskazałeś nieprawidłowy ekwipunek!\",\n\thome = \"Strona Główna\",\n\tcharKick = \"%s wyrzucił %s.\",\n\tcharBan = \"%s zablokował postać %s.\",\n\tcharBanned = \"Ta postać jest zablokowana.\",\n\tcharBannedTemp = \"Ta postać jest tymczasowo zablokowana.\",\n\tplayerConnected = \"%s połączył się z serwerem.\",\n\tplayerDisconnected = \"%s wyszedł z serwera.\",\n\tsetMoney = \"Ustawiłeś ilość pieniędzy %s na %s.\",\n\titemPriceInfo = \"Możesz kupić ten przedmiot za %s.\\nMożesz sprzedać ten przedmiot za %s\",\n\tfree = \"Darmowe\",\n\tvendorNoSellItems = \"Nie ma przedmiotów do sprzedania.\",\n\tvendorNoBuyItems = \"Nie ma przedmiotów do kupienia.\",\n\tvendorSettings = \"Ustawienia sprzedawców\",\n\tvendorUseMoney = \"Czy sprzedawcy powinni używać pieniędzy?\",\n\tvendorNoBubble = \"Ukryć dymek sprzedawcy?\",\n\tmode = \"Tryb\",\n\tprice = \"Cena\",\n\tstock = \"Zasób\",\n\tnone = \"Nic\",\n\tvendorBoth = \"Kupowanie i Sprzedawanie\",\n\tvendorBuy = \"Tylko kupowanie\",\n\tvendorSell = \"Tylko sprzedawanie\",\n\tmaxStock = \"Maksymalny zasób\",\n\tvendorFaction = \"Edytor frakcji\",\n\tbuy = \"Kup\",\n\tvendorWelcome = \"Witaj w moim sklepie, czy mogę Ci coś podać?\",\n\tvendorBye = \"Przyjdź niedługo z powrotem!\",\n\tcharSearching = \"Aktualnie szukasz już innej postaci, proszę poczekać.\",\n\tcharUnBan = \"%s odblokował postać %s.\",\n\tcharNotBanned = \"Ta postać nie jest zablokowana.\",\n\tquickSettings = \"Szybkie Ustawienia\",\n\tvmSet = \"Ustawiłeś swoją pocztę głosową.\",\n\tvmRem = \"Usunąłęś swoją pocztę głosową.\",\n\tnoPerm = \"Nie możesz tego zrobić!\",\n\tyoureDead = \"Jesteś martwy\",\n\tinjMajor = \"Widoczne krytyczne obrażenia.\",\n\tinjLittle = \"Widoczne obrażenia.\",\n\tchgName = \"Zmień Imię i Nazwisko.\",\n\tchgNameDesc = \"Wprowadź nowę imię i nazwisko postaci poniżej.\",\n\tweaponSlotFilled = \"Nie możesz użyć kolejnej broni typu %s!\",\n\tequippedBag = \"Nie możesz przemieszczać torby z wyekwipowanym przedmiotem!\",\n\tequippedWeapon = \"Nie możesz przemieszczać aktualnie wyekwipowanej broni!\",\n\tnestedBags = \"Nie możesz wrzucić torby do torby!\",\n\toutfitAlreadyEquipped = \"Już nosisz ubranie tego typu!\",\n\tuseTip = \"Używa przedmiotu.\",\n\tequipTip = \"Zakłada przedmiot.\",\n\tunequipTip = \"Zdejmuje przedmiot.\",\n\tconsumables = \"Towary konsumpcyjne.\",\n\tplyNotValid = \"Nie patrzysz na prawidłowego gracza.\",\n\trestricted = \"Zostałeś związany.\",\n\tsalary = \"Otrzymałeś wynagrodzenie w wysokości %s\",\n\tnoRecog = \"Nie poznajesz tej osoby.\",\n\tcurTime = \"Aktualny czas to %s.\",\n\tvendorEditor = \"Edytor Sprzedawcy\",\n\tedit = \"Edytuj\",\n\tdisable = \"Wyłącz\",\n\tvendorPriceReq = \"Wprowadź nową cenę dla tego produktu.\",\n\tvendorEditCurStock = \"Edytuj aktualny zapas\",\n\tvendorStockReq = \"Wprowadź ile produktów powinno się znajdować maksymalnie w zasobie\",\n\tvendorStockCurReq = \"Wprowadź ile przedmiotów jest dostępnych do sprzedarzy z całego zasobu.\",\n\tyou = \"Ty\",\n\tvendorSellScale = \"Skala ceny sprzedaży\",\n\tvendorNoTrade = \"Nie możesz dokonać wymiany z tym sprzedawcą!\",\n\tvendorNoMoney = \"Sprzedawce nie stać na ten przedmiot!\",\n\tvendorNoStock = \"Sprzedawca nie ma tego produktu aktualnie w asortymencie!\",\n\tcontentTitle = \"Nie znaleziono zawartości dla trybu Helix\",\n\tcontentWarning = \"Zawartość dla trybu Helix nie został wgrana. Rezultatem tego może być brak części funkcji.\\nCzy chciałbyś otworzyć stronę warsztatu z daną zawartością?\",\n\tflags = \"Flagi\",\n\tmapRestarting = \"Restart mapy za %d sekund!\",\n\tchooseTip = \"Wybierz postać do gry.\",\n\tdeleteTip = \"Usuń tą postać.\",\n\tstorageInUse = \"Ktoś inny używa tego teraz!\",\n\tstorageSearching = \"Przeszukiwanie...\",\n\tcontainer = \"Pojemnik\",\n\tcontainerPassword = \"Ustawiłeś hasło tego pojemnika na %s.\",\n\tcontainerPasswordRemove = \"Usunąłeś hasło z tego pojemnika.\",\n\tcontainerPasswordWrite = \"Wprowadź hasło.\",\n\tcontainerName = \"Ustawiłeś nazwę tego pojemnika na %s.\",\n\tcontainerNameWrite = \"Wprowadź nazwę.\",\n\tcontainerNameRemove = \"Usunąłeś nazwę z tego pojemnika.\",\n\tcontainerInvalid = \"Musisz patrzeć na pojemnik, aby tego użyć!\",\n\twrongPassword = \"Wprowadziłeś złe hasło!\",\n\trespawning = \"Odradzanie...\",\n\ttellAdmin = \"Powiadom administrację o tym błędzie: %s\",\n\n\tmapAdd = \"Dodałeś nową scenerie mapy.\",\n\tmapDel = \"Usunąłęś %d scenerie mapy.\",\n\tmapRepeat = \"Teraz dodaj punkt drugorzędny\",\n\n\tscoreboard = \"Tabela\",\n\tping = \"Ping: %d\",\n\tviewProfile = \"Obejrzyj profil Steam.\",\n\tcopySteamID = \"Skopiuj Steam ID\",\n\n\tmoney = \"Pieniądze\",\n\tmoneyLeft = \"Twoje Pieniądze: \",\n\tcurrentMoney = \"Obecna ilość pieniędzy: \",\n\n\tinvalidClass = \"To nie jest odpowiednia klasa!\",\n\tinvalidClassFaction = \"To nie jest poprawna klasa dla tej frakcji!\",\n\n\tmiscellaneous = \"Różne\",\n\tgeneral = \"Generalne\",\n\tobserver = \"Obserwator\",\n\tperformance = \"Wydajnośc\",\n\tthirdperson = \"Trzecia osoba\",\n\tdate = \"Data\",\n\tinteraction = \"Interakcja\",\n\tserver = \"Serwer\",\n\n\tresetDefault = \"Ustaw domyślnie\",\n\tresetDefaultDescription = \"To zresetuje \\\"%s\\\" do swojej domyślej wartości \\\"%s\\\".\",\n\toptOpenBags = \"Otwórz torbe z ekwipunkiem\",\n\toptdOpenBags = \"Automatycznie pokazuje wszystkie torby w twoim ekwipunku gdy menu jest otwarte.\",\n\toptShowIntro = \"Pokaż intro przy wchodzeniu na serwer\",\n\toptdShowIntro = \"Pokazuje wstęp do Helixa następnym razem gdy będziesz wchodzić. Ta opcja jest zawsze wyłączona po tym gdy obejrzałeś wstęp.\",\n\toptCheapBlur = \"Wyłącz rozmazanie\",\n\toptdCheapBlur = \"Zastępuje rozmazanie interfejsu z zwykłym przyciemnieniem.\",\n\toptObserverTeleportBack = \"Przywraca cię do poprzedniej lokalizacji\",\n\toptdObserverTeleportBack = \"Przywraca cię do lokalizacji w której włączyłeś tryb obserwatora.\",\n\toptObserverESP = \"Pokaż ESP administracyjne\",\n\toptdObserverESP = \"Pokazuje nazwę i lokalizację każdego gracza na serwerze.\",\n\topt24hourTime = \"Używaj czasu 24-godzinnego\",\n\toptd24hourTime = \"Pokazuj znacznik czasu w formacie 24-godzinnym, zamiast 12-godzinnego (AM/PM).\",\n\toptChatNotices = \"Pokazuj uwagi/ogłoszenia na czacie\",\n\toptdChatNotices = \"Przenosi wszystkie uwagi/ogłoszenia wyskakujące w prawym górnym rogu do czatu.\",\n\toptChatTimestamps = \"Show timestamps in chat\",\n\toptdChatTimestamps = \"Pokazuje godzinę wysłania przy każdej wiadomości na czacie.\",\n\toptAlwaysShowBars = \"Zawsze pokazuj tabelkę z informacjami\",\n\toptdAlwaysShowBars = \"Tworzy tabelkę z informacjami w lewym górnym rogu, bez znaczenia czy ma być ukryte czy nie.\",\n\toptAltLower = \"Ukryj ręce gdy są opuszczone.\",\n\toptdAltLower = \"Ukrywa ręce, gdy są opuszczone.\",\n\toptThirdpersonEnabled = \"Włącz trzecią osobe\",\n\toptdThirdpersonEnabled = \"Przenosi kamerę za ciebie. Również może być włączone w konsoli za pomocą \\\"ix_togglethirdperson\\\" \",\n\toptThirdpersonClassic = \"Włącz klasyczną trzecią osobe\",\n\toptdThirdpersonClassic = \"Moves your character's view with your mouse.\",\n\toptThirdpersonVertical = \"Wysokość kamery\",\n\toptdThirdpersonVertical = \"Jak wysoko powinna być kamera.\",\n\toptThirdpersonHorizontal = \"Wyrównanie kamery\",\n\toptdThirdpersonHorizontal = \"Jak bardzo na lewo lub prawo powinna być kamera.\",\n\toptThirdpersonDistance = \"Odległość kamery\",\n\toptdThirdpersonDistance = \"Jak oddalona powinna być kamera.\",\n\toptThirdpersonCrouchOffset = \"Kamera na wysokości kucania\",\n\toptdThirdpersonCrouchOffset = \"Jak wysoko powinna być kamera podczas kucania.\",\n\toptDisableAnimations = \"Wyłącz animacje\",\n\toptdDisableAnimations = \"Zatrzymuje animację by zapewnić natychmiastowe przejście.\",\n\toptAnimationScale = \"Skala animacji\",\n\toptdAnimationScale = \"Jak bardziej szybko lub długo powinny być animacje.\",\n\toptLanguage = \"Język\",\n\toptdLanguage = \"Język interfejsu.\",\n\toptMinimalTooltips = \"Minimalne powiadomienia z HUD'u\",\n\toptdMinimalTooltips = \"Changes the HUD tooltip style to take up less space.\",\n\toptNoticeDuration = \"Długość powiadomienia\",\n\toptdNoticeDuration = \"Jak długo powinny wyświetlać się uwagi/ogłoszenia (w sekundach).\",\n\toptNoticeMax = \"Maksimum powiadomień\",\n\toptdNoticeMax = \"Ilość powiadomień zanim nadmiar zostanie usunięty.\",\n\toptChatFontScale = \"Rozmiar czcionki\",\n\toptdChatFontScale = \"Skalowanie czcionki na czacie.\",\n\toptChatOutline = \"Obrysuj tekst w czacie\",\n\toptdChatOutline = \" Obramowuje tekst czatu. Włącz to, jeśli masz problemy z czytaniem czatu.\",\n\n\tcmdRoll = \"Losuje liczbę pomiędzy 0 a wyznaczoną liczbą.\",\n\tcmdPM = \"Wysyła prywatną wiadomośc do kogoś.\",\n\tcmdReply = \"Wysyła prywatną wiadomośc do ostatniej osoby, od której otrzymałeś wiadomość.\",\n\tcmdSetVoicemail = \"Ustawia lub usuwa automatyczną odpowiedź gdy ktoś wysyła ci prywatną wiadomość.\",\n\tcmdCharGiveFlag = \"Daje wyznaczoną flagę(i) do danej osoby.\",\n\tcmdCharTakeFlag = \"Usuwa wyznaczoną flagę(i) osobie jeśli ją ma.\",\n\tcmdToggleRaise = \"Podnosi albo opuscza broń, którą trzymasz.\",\n\tcmdCharSetModel = \"Ustawia model postaci.\",\n\tcmdCharSetSkin = \"Ustawia skórkę dla danej postaci.\",\n\tcmdCharSetBodygroup = \"Ustawia bodygroupy dla danego modelu.\",\n\tcmdCharSetAttribute = \"Ustawia poziom danego atrybutu dla osoby.\",\n\tcmdCharAddAttribute = \"Dodaje poziom do danego atrybutu.\",\n\tcmdCharSetName = \"Zmienia nazwę na wyznaczoną nazwę.\",\n\tcmdCharGiveItem = \"Daje wyznaczony przedmiot osobie.\",\n\tcmdCharKick = \"Zmusza osobę do wyjścia z jej postaci.\",\n\tcmdCharBan = \"Zabrania osobie wchodzenia na danej postaci.\",\n\tcmdCharUnban = \"Unban na zbanowaną postać.\",\n\tcmdGiveMoney = \"Daje wyznaczoną ilość pieniędzy osobie, na którą patrzysz\",\n\tcmdCharSetMoney = \"Zmienia ilośc pieniędzy do wyznaczonej ilośći.\",\n\tcmdDropMoney = \"Wyrzuca wyznaczoną ilość pieniędzy przed tobą.\",\n\tcmdPlyWhitelist = \"Pozwala osobie na stworzenie postaci w wyznaczonej frakcji.\",\n\tcmdCharGetUp = \"Sprróbuj wstać po upadku.\",\n\tcmdPlyUnwhitelist = \"Usuwa osobie whiteliste z danej frakcji.\",\n\tcmdCharFallOver = \"Walisz salto na plecy\",\n\tcmdBecomeClass = \"Zostań daną klasą w obecnej frakcji.\",\n\tcmdCharDesc = \"Ustaw opis zewnętrzny postaci.\",\n\tcmdCharDescTitle = \"Opis zewnętrzny\",\n\tcmdCharDescDescription = \"Napisz opis zewnętrzny twojej postaci.\",\n\tcmdPlyTransfer = \"Przenosi osobę do wyznaczonej frakcji.\",\n\tcmdCharSetClass = \"Zmusza osobę do zostania wyznaczoną klasą w obecnej frakcji.\",\n\tcmdMapRestart = \"Restartuje mape po wyznaczonej ilości czasu.\",\n\tcmdPanelAdd = \"Dodaje Web Panel.\",\n\tcmdPanelRemove = \"Usuwa Web Panel na który patrzysz.\",\n\tcmdTextAdd = \"Dodaje napis.\",\n\tcmdTextRemove = \"Usuwa napis na który patrzysz.\",\n\tcmdMapSceneAdd = \"Dodaje punkt widokowy widoczny w menu postaci.\",\n\tcmdMapSceneRemove = \"Usuwa punkt widokowy widoczny w menu postaci.\",\n\tcmdSpawnAdd = \"Dodaje punkt odrodzenia danej frakcji.\",\n\tcmdSpawnRemove = \"Usuwa punkt odrodzenia na który patrzysz.\",\n\tcmdAct = \"Wykonuje animację %s .\",\n\tcmdContainerSetPassword = \"Ustawia hasło kontenera na który patrzysz.\",\n\tcmdDoorSell = \"Sprzedaje drzwi na które patrzysz.\",\n\tcmdDoorBuy = \"Kupuje drzwi na które patrzysz.\",\n\tcmdDoorSetUnownable = \"Ustawia drzwi na które patrzysz na niemożliwe do posiadania.\",\n\tcmdDoorSetOwnable = \"Ustawia drzwi na które patrzysz na możliwe do posiadania.\",\n\tcmdDoorSetFaction = \"Ustawia drzwi na które patrzysz na posiadane przed daną frakcję.\",\n\tcmdDoorSetDisabled = \"Zabrania wykonywania komend na drzwi na które patrzysz.\",\n\tcmdDoorSetTitle = \"Ustawia opis drzwi na które patrzysz.\",\n\tcmdDoorSetParent = \"Ustawia właściciela danych drzwi.\",\n\tcmdDoorSetChild = \"Ustawia podwłaścicieli danych drzwi.\",\n\tcmdDoorRemoveChild = \"Usuwa podwłaściciela danych drzwi.\",\n\tcmdDoorSetHidden = \"Ukrywa opis drzwi na które patrzysz. Wciąż są możliwe do posiadania.\",\n\tcmdDoorSetClass = \"Ustawia drzwi na które patrzysz na posiadane przez daną klasę.\",\n\tcmdMe = \"Wykonaj akcję fizyczną.\",\n\tcmdIt = \"Wykonaj akcję twojego otoczenia.\",\n\tcmdW = \"Szeptaj.\",\n\tcmdY = \"Krzycz.\",\n\tcmdEvent = \"Wykonuje akcję, którą każdy widzi.\",\n\tcmdOOC = \"Wysyła wiadomość na czacie out-of-character.\",\n\tcmdLOOC = \"Wysyła wiadomość na lokalnym czacie out-of-character.\"\n}\n"
  },
  {
    "path": "gamemode/languages/sh_portuguese.lua",
    "content": "NAME = \"Portuguese\"\n\nLANGUAGE = {\n\thelix = \"Helix\",\n\n\tintroTextOne = \"fist industries apresenta\",\n\tintroTextTwo = \"em colaboração com %s\",\n\tintroContinue = \"pressione espaço para continuar\",\n\n\thelpIdle = \"Selecione uma categoria\",\n\thelpCommands = \"Parametros de comando são <flechas> são requiridos, enquanto [colchetes] são opcionais.\",\n\thelpFlags = \"As flags com um fundo verde são acessíveis por este personagem.\",\n\n\tcreditSpecial = \"Agradecimentos especiais\",\n\tcreditLeadDeveloper = \"Desenvolvedor Principal\",\n\tcreditUIDesigner = \"Designer de interface do usuário\",\n\tcreditManager = \"Gestor de projeto\",\n\tcreditTester = \"Testador principal\",\n\n\tchatTyping = \"Escrevendo...\",\n\tchatTalking = \"Falando...\",\n\tchatYelling = \"Gritando...\",\n\tchatWhispering = \"Cochichando...\",\n\tchatPerforming = \"Atuando...\",\n\tchatNewTab = \"Nova aba...\",\n\tchatReset = \"Redefinir posição\",\n\tchatResetTabs = \"Redefinir abas\",\n\tchatCustomize = \"Customizar...\",\n\tchatCloseTab = \"Fechar aba\",\n\tchatTabName = \"Nome da aba\",\n\tchatAllowedClasses = \"Classe de bate-papo permitidas\",\n\tchatTabExists = \"Uma aba de chat com esse nome já existe!\",\n\tchatMarkRead = \"Marcar tudo como lido\",\n\n\tcommunity = \"Comunidade\",\n\tcheckAll = \"Selecionar tudo\",\n\tuncheckAll = \"Deselecionar tudo\",\n\tcolor = \"Cor\",\n\ttype = \"Tipo\",\n\tdisplay = \"Visualização\",\n\tloading = \"Carregando\",\n\tdbError = \"Falha na conexão com o banco de dados\",\n\tunknown = \"Desconhecido\",\n\tnoDesc = \"Sem descrição disponível\",\n\tcreate = \"Criar\",\n\tupdate = \"Atualizar\",\n\tload = \"Carregar personagem\",\n\tloadTitle = \"Carregar um personagem\",\n\tleave = \"Sair\",\n\tleaveTip = \"Sair deste servidor.\",\n\t[\"return\"] = \"Retornar\",\n\treturnTip = \"Retornar para o menu anterior.\",\n\tproceed = \"Prosseguir\",\n\tfaction = \"Facção\",\n\tskills = \"Habilidades\",\n\tchoose = \"Escolher\",\n\tchooseFaction = \"Escolha uma facção\",\n\tchooseDescription = \"Descreva a aparência do seu personagem\",\n\tchooseSkills = \"Ajuste suas habilidades\",\n\tname = \"Nome\",\n\tdescription = \"Descrição\",\n\tmodel = \"Modelo\",\n\tattributes = \"Atributos\",\n\tattribPointsLeft = \"Pontos restantes\",\n\tcharInfo = \"Character Information\",\n\tcharCreated = \"Você criou seu personagem com sucesso.\",\n\tcharCreateTip = \"Preencha os campos abaixo e pressione 'Finalizar' para criar o seu personagem.\",\n\tinvalid = \"Você providênciou um %s inválido!\",\n\tnameMinLen = \"Seu nome deve conter ao menos %d caracteres!\",\n\tnameMaxLen = \"Seu nome não pode ser maior que %d caracteres!\",\n\tdescMinLen = \"Sua descrição precisa conter ao menos %d caracteres!\",\n\tmaxCharacters = \"Você não pode criar mais personagens!\",\n\tplayer = \"Jogador\",\n\tfinish = \"Finalizar\",\n\tfinishTip = \"Finalizar a criação do personagem.\",\n\tneedModel = \"Você precisa escolher um modelo válido!\",\n\tcreating = \"O seu personagem está sendo criado...\",\n\tunknownError = \"Um erro desconhecido ocorreu!\",\n\tareYouSure = \"Você tem certeza?\",\n\tdelete = \"Deletar\",\n\tdeleteConfirm = \"Este personagem será irrevogavelmente removido!\",\n\tdeleteComplete = \"%s foi deletado.\",\n\tno = \"Não\",\n\tyes = \"Sim\",\n\tclose = \"Fechar\",\n\tsave = \"Salvar\",\n\titemInfo = \"Nome: %s\\nDescrição: %s\",\n\titemCreated = \"Item(ns) criado com sucesso.\",\n\tcloud_no_repo = \"O repositório fornecido não é válido!\",\n\tcloud_no_plugin = \"O plugin providênciado não é válido!\",\n\tinv = \"Inventário\",\n\tplugins = \"Plugins\",\n\tauthor = \"Autor\",\n\tversion = \"Versão\",\n\tcharacters = \"Personagens\",\n\tbusiness = \"Négocios\",\n\tsettings = \"Opções\",\n\tconfig = \"Configuração\",\n\tchat = \"Chat\",\n\tappearance = \"Aparência\",\n\tmisc = \"Diversos\",\n\toocDelay = \"Você precisa aguardar %s mais segundos antes de usar o OOC novamente!\",\n\tloocDelay = \"Você precisa aguardar  %s mais segundos antes de usar o LOOC novamente!\",\n\tusingChar = \"Você já está utilizando este personagem.\",\n\tnotAllowed = \"Você não tem permissão para fazer isso!\",\n\titemNoExist = \"O item que você solicitou não existe!\",\n\tcmdNoExist = \"Esse comando não existe!\",\n\tcharNoExist = \"Um personagem correspondente não foi encontrado!\",\n\tplyNoExist = \"Um jogador correspondente não foi encontrado!\",\n\tcfgSet = \"%s definiu \\\"%s\\\" para %s.\",\n\tdrop = \"Soltar\",\n\tdropTip = \"Solta esse item do seu inventário.\",\n\ttake = \"Pegar\",\n\ttakeTip = \"Pega este item e coloca no seu inventário.\",\n\tdTitle = \"Porta sem proprietário\",\n\tdTitleOwned = \"Porta Comprada\",\n\tdIsNotOwnable = \"Esta porta não pode ser comprada.\",\n\tdIsOwnable = \"Você pode comprar esta porta pressionando F2.\",\n\tdMadeUnownable = \"Você tornou esta porta 'não comprável'.\",\n\tdMadeOwnable = \"Você tornou esta porta 'comprável'.\",\n\tdNotAllowedToOwn = \"Você não tem permissão para possuir esta porta!\",\n\tdSetDisabled = \"Você tornou esta porta 'desativada'.\",\n\tdSetNotDisabled = \"Você tornou esta porta 'ativada'.\",\n\tdSetHidden = \"Você tornou esta porta oculta.\",\n\tdSetNotHidden = \"Você tornou esta porta não mais oculta.\",\n\tdSetParentDoor = \"Você definiu esta porta como sua porta mãe.\",\n\tdCanNotSetAsChild = \"Você não pode definir a porta dos pais sendo criança!\",\n\tdAddChildDoor = \"Você adicionou essa porta como criança.\",\n\tdRemoveChildren = \"Você removeu todas as crianças desta porta.\",\n\tdRemoveChildDoor = \"Você removeu esta porta de ser uma criança.\",\n\tdNoParentDoor = \"Você não tem uma porta mãe estabelecida.\",\n\tdOwnedBy = \"Esta porta é de propriedade de %s.\",\n\tdConfigName = \"Portas\",\n\tdSetFaction = \"Esta porta agora pertence a facção %s.\",\n\tdRemoveFaction = \"Esta porta não pertence mais a nenhuma facção.\",\n\tdNotValid = \"Você não está olhando para nenhuma porta válida!\",\n\tcanNotAfford = \"Você não pode pagar por isto!\",\n\tdPurchased = \"Você comprou essa porta por %s.\",\n\tdSold = \"Você vendeu esta porta por %s.\",\n\tnotOwner = \"Você não é o dono disso!\",\n\tinvalidArg = \"Você forneceu um valor inválido para o argumento #%s!\",\n\tinvalidFaction = \"A facção que você forneceu não pôde ser encontrada!\",\n\tflagGive = \"%s atribuiu a %s as flags '%s'.\",\n\tflagGiveTitle = \"Dar Flags\",\n\tflagTake = \"%s revogou as flags '%s' de %s.\",\n\tflagTakeTitle = \"Revogar Flags\",\n\tflagNoMatch = \"Você precisa possuir as flags \\\"%s\\\" para realizar esta ação!\",\n\ttextAdded = \"Você adicionou um texto.\",\n\ttextRemoved = \"Você removeu o(s) texto(s) %s .\",\n\tmoneyTaken = \"Você recebeu %s.\",\n\tmoneyGiven = \"Você deu %s.\",\n\tinsufficientMoney = \"Você não tem o suficiente para fazer isso!\",\n\tbusinessPurchase = \"Você comprou %s por %s.\",\n\tbusinessSell = \"Você vendeu %s por %s.\",\n\tbusinessTooFast = \"Por favor espere antes de comprar outro item!\",\n\tcChangeModel = \"%s mudou o modelo de %s para %s.\",\n\tcChangeName = \"%s mudou o nome de %s para %s.\",\n\tcChangeSkin = \"%s mudou a 'skin' de %s para %s.\",\n\tcChangeGroups = \"%s mudou o 'bodygroup' \\\"%s\\\" de %s para %s.\",\n\tcChangeFaction = \"%s mudou %s para a facção %s.\",\n\tplayerCharBelonging = \"Este objeto pertençe ao seu outro personagem!\",\n\tspawnAdd = \"Você adiconou um ponto de ressurgimento para %s.\",\n\tspawnDeleted = \"Você removeu %s ponto(s) de ressurgimento.\",\n\tsomeone = \"Alguém\",\n\trgnLookingAt = \"Permite que a pessoa que você está olhando reconheça você.\",\n\trgnWhisper = \"Permita que te reconheça aqueles que te escutam sussurar.\",\n\trgnTalk = \"Permite que aqueles que te escutam falando reconheça você.\",\n\trgnYell = \"Permite que aqueles que te escutam gritando rechoneça você.\",\n\ticFormat = \"%s disse \\\"%s\\\"\",\n\trollFormat = \"%s jogou os dados e tirou %s.\",\n\twFormat = \"%s susurra \\\"%s\\\"\",\n\tyFormat = \"%s grita \\\"%s\\\"\",\n\tsbOptions = \"Clique para ver opções de %s.\",\n\tspawnAdded = \"Você adicionou um ponto de ressurgimento para %s.\",\n\twhitelist = \"%s adicionou %s à facção %s.\",\n\tunwhitelist = \"%s retirou %s da facção %s.\",\n\tnoWhitelist = \"Você não tem permissão para usar este personagem!\",\n\tgettingUp = \"Você está se levantando...\",\n\twakingUp = \"Você está retomando consciência...\",\n\tWeapons = \"Armas\",\n\tcheckout = \"Ir ao Cheque (%s)\",\n\tpurchase = \"Comprar\",\n\tpurchasing = \"Comprando...\",\n\tsuccess = \"Sucesso\",\n\tbuyFailed = \"Falha na compra!\",\n\tbuyGood = \"Compra sucesiva!\",\n\tshipment = \"Carga\",\n\tshipmentDesc = \"Esta carga pertence à %s.\",\n\tclass = \"Classe\",\n\tclasses = \"Classes\",\n\tillegalAccess = \"Acesso Ilegal.\",\n\tbecomeClassFail = \"Você não pode se tornar um(a) %s!\",\n\tbecomeClass = \"Você se tornou em um(a) %s.\",\n\tsetClass = \"Você mudou a classe de %s para %s.\",\n\tattributeSet = \"Estabeleceu o atributo de %s de %s à %s.\",\n\tattributeNotFound = \"Você especificou um atributo inválido!\",\n\tattributeUpdate = \"You added %s's %s by %s.\",\n\tnoFit = \"Você não tem espaço o suficiente para este item!\",\n\titemOwned = \"You cannot interact with an item that you own on a different character!\",\n\thelp = \"Ajuda\",\n\tcommands = \"Comandos\",\n\tdoorSettings = \"Propiedades da Porta\",\n\tsell = \"Vender\",\n\taccess = \"Acessar\",\n\tlocking = \"Trancando esta entidade...\",\n\tunlocking = \"Destrancando esta entidade...\",\n\tmodelNoSeq = \"Seu modelo não suporta esta ação!\",\n\tnotNow = \"Você não tem permissão para fazer isto agora!\",\n\tfaceWall = \"Você precisa encarar uma parede para fazer isto!\",\n\tfaceWallBack = \"Você precisa estar contra a parede para fazer isto!\",\n\tdescChanged = \"Você mudou a descrição do seu personagem.\",\n\tnoOwner = \"O dono é inválido!\",\n\tinvalidItem = \"Você especificou um item inválido!\",\n\tinvalidInventory = \"Você especificou um inventário inválido!\",\n\thome = \"Início\",\n\tcharKick = \"%s expulsou o personagem %s.\",\n\tcharBan = \"%s baniu o personagem %s.\",\n\tcharBanned = \"Este personagem está banido.\",\n\tplayerConnected = \"%s conectou-se ao servidor.\",\n\tplayerDisconnected = \"%s desconectou-se do servidor.\",\n\tsetMoney = \"Você ajustou o dinheiro de %s para %s.\",\n\titemPriceInfo = \"Você pode comprar este item por %s.\\nVocê pode vender este item por %s\",\n\tfree = \"Grátis\",\n\tvendorNoSellItems = \"Não há items para vender.\",\n\tvendorNoBuyItems = \"Não há items para comprar.\",\n\tvendorSettings = \"Propiedades do Fornecedor\",\n\tvendorUseMoney = \"Fornecedor deve usar dinheiro?\",\n\tvendorNoBubble = \"Esconder o balão do vendedor?\",\n\tmode = \"Modo\",\n\tprice = \"Preço\",\n\tstock = \"Estoque\",\n\tnone = \"Nenhum\",\n\tvendorBoth = \"Comprar e Vender\",\n\tvendorBuy = \"Apenas Comprar\",\n\tvendorSell = \"Apenas Vender\",\n\tmaxStock = \"Estoque Máximo\",\n\tvendorFaction = \"Editar Facção\",\n\tbuy = \"Comprar\",\n\tvendorWelcome = \"Bem-vindo, o que posso lhe oferecer hoje?\",\n\tvendorBye = \"Até mais!\",\n\tcharSearching = \"Você já está procurando um outro personagem!\",\n\tcharUnBan = \"%s desbaniu o personagem %s.\",\n\tcharNotBanned = \"Este personagem não está banido!\",\n\tquickSettings = \"Configurações Rápidas\",\n\tvmSet = \"Você ajustou seu correio de voz.\",\n\tvmRem = \"Você removeu seu correio de voz.\",\n\tnoPerm = \"Você não é permitido a fazer isto!\",\n\tyoureDead = \"Você está Morto.\",\n\tinjMajor = \"Parece críticamente lesionado\",\n\tinjLittle = \"Parece lesionado\",\n\tchgName = \"Mudar Nome\",\n\tchgNameDesc = \"Insira o nome do personagem abaixo.\",\n\tweaponSlotFilled = \"Você não pode usar outra arma de %s!\",\n\tequippedBag = \"Você não pode mover uma bolsa que tem um item equipado!\",\n\tequippedWeapon = \"Você não pode mover uma arma que está sendo equipada!\",\n\tnestedBags = \"Você não pode colocar um inventário dentro de outro!\",\n\toutfitAlreadyEquipped = \"Você já está vestindo este tipo de roupa!\",\n\tuseTip = \"Usa este item.\",\n\tequip = \"Equipar\",\n\tequipTip = \"Equipa o item..\",\n\tunequip = \"Desequipar\",\n\tunequipTip = \"Desequipa o item.\",\n\tconsumables = \"Consumíveis\",\n\tplyNotValid = \"Você não está olhando para um jogador válido!\",\n\trestricted = \"Você foi preso.\",\n\tsalary = \"Você recebeu %s do seu salário.\",\n\tnoRecog = \"Você não reconhece esta pessoa.\",\n\tcurTime = \"A hora é %s.\",\n\tvendorEditor = \"Editar Fornecedor\",\n\tedit = \"Editar\",\n\tdisable = \"Desativar\",\n\tvendorPriceReq = \"Insira um novo preço para este item.\",\n\tvendorEditCurStock = \"Edite o Estoque Atual\",\n\tvendorStockReq = \"Insira a quantidade Máxima de Estoque o item deve ter.\",\n\tvendorStockCurReq = \"Insira quantos itens estão disponíveis para compra do estoque máximo.\",\n\tyou = \"Você\",\n\tvendorSellScale = \"Escala do preço de venda\",\n\tvendorNoTrade = \"Você não consegue trocar com este fornecedor!\",\n\tvendorNoMoney = \"Este fornecedor não consegue pagar este item!\",\n\tvendorNoStock = \"Este fornecedor não possui esse item no estoque!\",\n\tcontentTitle = \"Faltando Conteúdo do Helix\",\n\tcontentWarning = \"Você não tem o conteúdo do Helix instalado. Isto pode resultar na falta de alguns aspectos.\\nVocê gostaria de abrir a página da Workshop para baixar o conteúdo?\",\n\tflags = \"Flags\",\n\tmapRestarting = \"O mapa irá reiniciar em %d segundos!\",\n\tchooseTip = \"Escolha este personagem para jogar.\",\n\tdeleteTip = \"Delete este personagem.\",\n\tstorageInUse = \"Alguém já está usando isto!\",\n\tstorageSearching = \"Procurando...\",\n\tcontainer = \"Container\",\n\tcontainerPassword = \"Você ajustou a senha deste container para %s.\",\n\tcontainerPasswordRemove = \"Você retirou a senha deste container.\",\n\tcontainerPasswordWrite = \"Insira a senha.\",\n\tcontainerName = \"Você mudou o nome deste container para %s.\",\n\tcontainerNameWrite = \"Insira o nome.\",\n\tcontainerNameRemove = \"Você removeu o nome deste container.\",\n\tcontainerInvalid = \"Você precisa estar olhando para um container para fazer isto!\",\n\twrongPassword = \"Você inseriu uma senha errada!\",\n\trespawning = \"Ressurgindo...\",\n\ttellAdmin = \"Informe um membro da staff sobre este erro: %s\",\n\n\tscoreboard = \"Scoreboard\",\n\tping = \"Ping: %d\",\n\tviewProfile = \"Ver Perfil da Steam\",\n\tcopySteamID = \"Copiar Steam ID\",\n\n\tmoney = \"Dinheiro\",\n\tmoneyLeft = \"Seu Dinheiro: \",\n\tcurrentMoney = \"Dinheiro Restante: \",\n\n\tinvalidClass = \"Essa não é uma classe válida!\",\n\tinvalidClassFaction = \"Essa não é uma classe válida para esta facção!\",\n\n\tmiscellaneous = \"Diversos\",\n\tgeneral = \"Geral\",\n\tobserver = \"Observador\",\n\tperformance = \"Performance\",\n\tthirdperson = \"Terceira Pessoa\",\n\tdate = \"Data\",\n\tinteraction = \"Interação\",\n\tserver = \"Servidor\",\n\n\tresetDefault = \"Reiniciar para o padrão\",\n\tresetDefaultDescription = \"Isto irá reiniciar \\\"%s\\\" ao seu valor padrão de \\\"%s\\\".\",\n\toptOpenBags = \"Abrir bolsa com inventário\",\n\toptdOpenBags = \"Automáticamente vê todas as bolsas em seu inventário quando o menu é aberto.\",\n\toptShowIntro = \"Mostrar intro ao entrar\",\n\toptdShowIntro = \"Mostra a introdução do Helix na próxima vez que entrar. Isto é sempre desativado depois que já assiste.\",\n\toptCheapBlur = \"Desativar embaçamento\",\n\toptdCheapBlur = \"Substitui o embaçamento da UI com uma ofuscação simples.\",\n\toptObserverTeleportBack = \"Retornar ao último local\",\n\toptdObserverTeleportBack = \"Retorna você ao local que você usou o modo observador..\",\n\toptObserverESP = \"Mostrar admin ESP\",\n\toptdObserverESP = \"Mostra o nome e o local de cada jogador no servidor.\",\n\topt24hourTime = \"Usar horário de 24 horas.\",\n\toptd24hourTime = \"Mostra horários em formato de 24 horas, ao em vez de 12 (AM/PM).\",\n\toptChatNotices = \"Mostrar noticias no chat\",\n\toptdChatNotices = \"Coloca todas as notícias que aparecem no topo da tela no chat.\",\n\toptChatTimestamps = \"Mostrar horários no chat\",\n\toptdChatTimestamps = \"Acopla o horário à cada mensagem enviada no chat.\",\n\toptAlwaysShowBars = \"Sempre mostrar barras de informação\",\n\toptdAlwaysShowBars = \"Mostra as informações de barra no topo esquerdo da tela, mesmo se estiverem escondidas.\",\n\toptAltLower = \"Esconder mãos quando abaixadas\", -- @todo remove me\n\toptdAltLower = \"Esconde suas mãos quando elas estão ao lado.\", -- @todo remove me\n\toptThirdpersonEnabled = \"Ativar terceira pessoa\",\n\toptdThirdpersonEnabled = \"Move a câmera para atrás de você. Também pode ser ativo com o comando \\\"ix_togglethirdperson\\\".\",\n\toptThirdpersonClassic = \"Ativar terceira pessoa clássico\",\n\toptdThirdpersonClassic = \"Move a visão do seu personagem com o mouse.\",\n\toptThirdpersonVertical = \"Altura da câmera\",\n\toptdThirdpersonVertical = \"O quão alto a câmera deve estar.\",\n\toptThirdpersonHorizontal = \"Posição da câmera\",\n\toptdThirdpersonHorizontal = \"O quão para a direita ou esquerda a câmera deve estar.\",\n\toptThirdpersonDistance = \"Distância da câmera\",\n\toptdThirdpersonDistance = \"O quão longe a câmera deve estar.\",\n\toptThirdpersonCrouchOffset = \"Altura da câmera agachada.\",\n\toptdThirdpersonCrouchOffset = \"O quão alto a câmera deve estar enquanto agachado.\",\n\toptDisableAnimations = \"Desativar animações\",\n\toptdDisableAnimations = \"Desativa as animações do menu do Helix para transições instantâneas..\",\n\toptAnimationScale = \"Escala da Animação\",\n\toptdAnimationScale = \"O quão rápido ou devagar as animações devem estar.\",\n\toptLanguage = \"Língua\",\n\toptdLanguage = \"A língua mostrada na UI do Helix.\",\n\toptMinimalTooltips = \"Dicas minimalistas no HUD\",\n\toptdMinimalTooltips = \"Muda o estilo de dicas no HUD para tomar menos espaço.\",\n\toptNoticeDuration = \"Duração de noticia\",\n\toptdNoticeDuration = \"Por quanto tempo mostrar noticias (em segundos).\",\n\toptNoticeMax = \"Máximo de noticias\",\n\toptdNoticeMax = \"O número máximo de noticias até que noticias passadas sejam removidas.\",\n\toptChatFontScale = \"Tamanho da fonte do Chat\",\n\toptdChatFontScale = \"O quão grande ou pequeno a fonte deve ser.\",\n\toptChatOutline = \"Borda no texto do Chat\",\n\toptdChatOutline = \"Desenha uma borda ao redor do texto mostrado no Chat. Ative caso não consiga ver o texto.\",\n\n\tcmdRoll = \"Rola um número entre 0 e o número especificado.\",\n\tcmdPM = \"Manda uma mensagem privada para alguém.\",\n\tcmdReply = \"Manda uma mensagem privada para a última pessoa que lhe mandou uma mensagem.\",\n\tcmdSetVoicemail = \"Ajuste ou remova a mensagem de resposta automática quando alguém lhe manda uma mensagem.\",\n\tcmdCharGiveFlag = \"Dá a(s) flag(s) especificadas para alguém.\",\n\tcmdCharTakeFlag = \"Remove a(s) flag(s) especificadas de alguém caso tenham.\",\n\tcmdToggleRaise = \"Levanta ou abaixa a arma que está segurando.\",\n\tcmdCharSetModel = \"Ajusta o modelo do personagem da pessoa.\",\n\tcmdCharSetSkin = \"Ajusta a skin para o modelo do personagem da pessoa.\",\n\tcmdCharSetBodygroup = \"Ajusta o bodygroup para o modelo do personagem da pessoa.\",\n\tcmdCharSetAttribute = \"Ajusta o nível do atributo especificado para a pessoa.\",\n\tcmdCharAddAttribute = \"Adiciona um nível ao atributo especificado para a pessoa.\",\n\tcmdCharSetName = \"Muda o nome de alguém para o nome especificado.\",\n\tcmdCharGiveItem = \"Dá o item especificado para alguém.\",\n\tcmdCharKick = \"Faz alguém sair do personagem que está jogando forçadamente.\",\n\tcmdCharBan = \"Proibe alguém de entrar no servidor com o personagem atual.\",\n\tcmdCharUnban = \"Permite que um personagem que foi banido possa ser usado novamente.\",\n\tcmdGiveMoney = \"Dá uma quantia de dinheiro especificada á pessoa que está olhando.\",\n\tcmdCharSetMoney = \"Muda a quantia total de dinheiro de alguém para a quantia especificada.\",\n\tcmdDropMoney = \"Deixa cair a quantia especificada de dinheiro em uma caixinha à sua frente.\",\n\tcmdPlyWhitelist = \"Permite que alguém crie um personagem na facção especificada.\",\n\tcmdCharGetUp = \"Tenta se levantar após ter caído.\",\n\tcmdPlyUnwhitelist = \"Tira a permissão de alguém para criar um personagem na facção especificada.\",\n\tcmdCharFallOver = \"Faz você cair.\",\n\tcmdBecomeClass = \"Tenta se tornar parte da classe especificada na sua facção.\",\n\tcmdCharDesc = \"Ajusta a tua descrição física.\",\n\tcmdCharDescTitle = \"Descrição Física\",\n\tcmdCharDescDescription = \"Insira a descrição física de teu personagem.\",\n\tcmdPlyTransfer = \"Transfere alguém para a facção designada.\",\n\tcmdCharSetClass = \"Força alguém a se tornar a classe especificada na facção da pessoa.\",\n\tcmdMapRestart = \"Reinicia o mapa após um certo intervalo de tempo.\",\n\tcmdPanelAdd = \"Coloca um painel da Web no mundo.\",\n\tcmdPanelRemove = \"Remove o painel que está a olhar.\",\n\tcmdTextAdd = \"Adiciona um bloco de texto no mundo.\",\n\tcmdTextRemove = \"Remove o bloco de texto que está a olhar.\",\n\tcmdMapSceneAdd = \"Adiciona uma câmera cinemática que pode ser vista do menu.\",\n\tcmdMapSceneRemove = \"Removes a camera viewpoint that is shown in the character menu.\",\n\tcmdSpawnAdd = \"Adiciona um ponto de ressurgimento para a facção especificada.\",\n\tcmdSpawnRemove = \"Remove qualquer ponto de ressurgimento que esteja a olhar.\",\n\tcmdAct = \"Faz a animação de %s\",\n\tcmdContainerSetPassword = \"Ajuste a senha do container que está a olhar.\",\n\tcmdDoorSell = \"Venda a porta que está a olhar.\",\n\tcmdDoorBuy = \"Compra a porta que está a olhar.\",\n\tcmdDoorSetUnownable = \"Faz a porta que está a olhar incomprável.\",\n\tcmdDoorSetOwnable = \"Faz a porta que está a olhar comprável.\",\n\tcmdDoorSetFaction = \"Designa a porta que está a olhar à uma facção especificada.\",\n\tcmdDoorSetDisabled = \"Faz com que seja impossível realizar comandos na porta que está a olhar.\",\n\tcmdDoorSetTitle = \"Coloca um título na porta que está a olhar.\",\n\tcmdDoorSetParent = \"Define o parentesco de um par de portas.\",\n\tcmdDoorSetChild = \"Define o herdeiro de um par de portas.\",\n\tcmdDoorRemoveChild = \"Remove o herdeiro de um par de portas.\",\n\tcmdDoorSetHidden = \"Esconde a descrição da porta que está a olhar, mas ainda permite que alguém a compre.\",\n\tcmdDoorSetClass = \"Permite que a porta que está a olhar seja propiedade da classe especificada.\",\n\tcmdMe = \"Faça uma ação física.\",\n\tcmdIt = \"Faça algo ao seu redor realizar uma ação.\",\n\tcmdW = \"Susurre algo para as pessoas próximas a você.\",\n\tcmdY = \"Grite algo para as pessoas ao seu redor.\",\n\tcmdEvent = \"Faça algo realizar uma ação que todos podem ver.\",\n\tcmdOOC = \"Mande uma mensagem para o chat fora-de-personagem global.\",\n\tcmdLOOC = \"Mande uma mensagem para o chat dentro-de-personagem local.\"\n}\n"
  },
  {
    "path": "gamemode/languages/sh_russian.lua",
    "content": "--Maybe this thing have some trouble with translate\nNAME = \"Русский\"\n\nLANGUAGE = {\n\thelix = \"Helix\",\n\n\tintroTextOne = \"Fist Industries представляет\",\n\tintroTextTwo = \"В сотрудничестве с %s\",\n\tintroContinue = \"Чтобы продолжить, нажмите Пробел\",\n\n\thelpIdle = \"Выберите категорию\",\n\thelpCommands = \"Параметры команды отмеченные <стрелками> обязательны, тогда как [скобки] опциональны.\",\n\thelpFlags = \"Флаги с зеленым фоном обозначают доступные этому персонажу флаги.\",\n\n\tcreditSpecial = \"Особая благодарность\",\n\tcreditLeadDeveloper = \"Ведущий разработчик\",\n\tcreditUIDesigner = \"Дизайнер интерфейса\",\n\tcreditManager = \"Менеджер проекта\",\n\tcreditTester = \"Ведущий тестировщик\",\n\n\tchatTyping = \"Пишет...\",\n\tchatTalking = \"Говорит...\",\n\tchatYelling = \"Кричит...\",\n\tchatWhispering = \"Шепчет...\",\n\tchatPerforming = \"Выполняет действие...\",\n\tchatNewTab = \"Новая вкладка...\",\n\tchatReset = \"Сбросить позицию\",\n\tchatResetTabs = \"Сбросить позицию вкладок\",\n\tchatCustomize = \"Настроить...\",\n\tchatCloseTab = \"Закрыть вкладку\",\n\tchatTabName = \"Имя вкладки\",\n\tchatNewTabTitle = \"Новая Вкладка\",\n\tchatAllowedClasses = \"Разрешенные классы чата\",\n\tchatTabExists = \"Вкладка чата с таким именем уже существует!\",\n\tchatMarkRead = \"Отметить как прочитанные\",\n\n\tcommunity = \"Сообщество\",\n\tcheckAll = \"Отметить все\",\n\tuncheckAll = \"Убрать все\",\n\tcolor = \"Цвет\",\n\ttype = \"Тип\",\n\tdisplay = \"Изображение\",\n\tloading = \"Загрузка\",\n\tdbError = \"Сбой подключения к базе данных\",\n\tunknown = \"Неизвестный\",\n\tnoDesc = \"Нет описания\",\n\tcreate = \"Создать\",\n\tupdate = \"Обновить\",\n\tload = \"Загрузить персонажа\",\n\tloadTitle = \"Загрузить персонажа.\",\n\tleave = \"Выйти с сервера\",\n\tleaveTip = \"Покинуть сервер.\",\n\t[\"return\"] = \"Вернуться\",\n\treturnTip = \"Вернуться в предыдущее меню.\",\n\tproceed = \"Продолжить\",\n\tfaction = \"Фракция\",\n\tskills = \"Навыки\",\n\tchoose = \"Выбрать\",\n\tchooseFaction = \"Выберите фракцию\",\n\tchooseDescription = \"Опишите свою внешность\",\n\tchooseSkills = \"Выберите ваши навыки\",\n\tname = \"Имя\",\n\tdescription = \"Описание\",\n\tmodel = \"Модель\",\n\tattributes = \"Атрибуты\",\n\tattribPointsLeft = \"Очки\",\n\tEndurance = \"Выносливость\",\n\tStamina = \"Быстроходность\",\n\tStrength = \"Сила\",\n\tcharInfo = \"Информация о персонаже\",\n\tcharCreated = \"Персонаж успешно создан.\",\n\tcharCreateTip = \"Заполните поля ниже и нажмите 'Создать' чтобы создать персонажа.\",\n\tinvalid = \"Вы ввели недействительный %s!\",\n\tnameMinLen = \"Имя не должно быть короче %d символов!\",\n\tnameMaxLen = \"Имя не должно быть длиннее %d символов!\",\n\tdescMinLen = \"Описание не должно быть короче %d символов!\",\n\tmaxCharacters = \"Достигнут лимит созданных персонажей!\",\n\tplayer = \"Игрок\",\n\tfinish = \"Создать\",\n\tfinishTip = \"Закончить создание персонажа.\",\n\tneedModel = \"Выберите одну из моделей!\",\n\tcreating = \"Персонаж создается...\",\n\tunknownError = \"Произошла неизвестная ошибка!\",\n\tareYouSure = \"Вы уверены?\",\n\tdelete = \"Удалить\",\n\tdeleteConfirm = \"Этот персонаж будет безвозвратно удален!\",\n\tdeleteComplete = \"%s был удален.\",\n\tno = \"Нет\",\n\tyes = \"Да\",\n\tclose = \"Закрыть\",\n\tsave = \"Сохранить\",\n\titemInfo = \"Имя: %s\\nОписание: %s\",\n\titemCreated = \"Предмет(ы) успешно создан(ы).\",\n\tcloud_no_repo = \"Данный репозиторий недействителен!\",\n\tcloud_no_plugin = \"Данный плагин недействителен!\",\n\tinv = \"Инвентарь\",\n\tplugins = \"Плагины\",\n\tauthor = \"Автор\",\n\tversion = \"Версия\",\n\tcharacters = \"Меню персонажей\",\n\tbusiness = \"Бизнес\",\n\tsettings = \"Настройки\",\n\tconfig = \"Конфигурация\",\n\tchat = \"Чат\",\n\tappearance = \"Описание\",\n\tmisc = \"Остальное\",\n\toocDelay = \"Подождите %s секунд чтобы снова написать в OOC чат!\",\n\tloocDelay = \"Подождите %s секунд чтобы снова написать в LOOC чат!\",\n\tusingChar = \"Вы уже используете этого персонажа.\",\n\tnotAllowed = \"Эта команда вам недоступна!\",\n\titemNoExist = \"Запрошенный предмет не существует!\",\n\tcmdNoExist = \"Такой команды не существует!\",\n\tcharNoExist = \"Такой персонаж не найден!\",\n\tplyNoExist = \"Такой игрок не найден!\",\n\tcfgSet = \"%s установил с \\\"%s\\\" на %s.\",\n\tdrop = \"Выбросить\",\n\tdropTip = \"Выбросить предмет из инвентаря.\",\n\ttake = \"Взять\",\n\ttakeTip = \"Взять этот предмет в инвентарь.\",\n\tdTitle = \"Дверь\",\n\tdTitleOwned = \"Купленная дверь\",\n\tdIsNotOwnable = \"Эта дверь не имеет владельца.\",\n\tdIsOwnable = \"Вы можете приобрести эту дверь, нажав F2.\",\n\tdMadeUnownable = \"Вы сделали эту дверь непокупаемой.\",\n\tdMadeOwnable = \"Вы сделали эту дверь приобретаемой.\",\n\tdNotAllowedToOwn = \"Вы не можете владеть этой дверью!\",\n\tdSetDisabled = \"Вы заблокировали данную дверь.\",\n\tdSetNotDisabled = \"Вы разблокировали данную дверь.\",\n\tdSetHidden = \"Вы скрыли описание данной двери.\",\n\tdSetNotHidden = \"Вы раскрыли описание данной двери.\",\n\tdSetParentDoor = \"Вы сделали эту дверь родительской.\",\n\tdCanNotSetAsChild = \"Вы не можете сделать родительскую дверь дочерней!\",\n\tdAddChildDoor = \"Вы сделали эту дверь дочерней.\",\n\tdRemoveChildren = \"Вы удалили у этой двери ее дочерние двери.\",\n\tdRemoveChildDoor = \"Вы удалили дочернюю связь у этой двери.\",\n\tdNoParentDoor = \"У вас нет родительской двери для связи.\",\n\tdOwnedBy = \"Эта дверь принадлежит  %s.\",\n\tdConfigName = \"Двери\",\n\tdSetFaction = \"Эта дверь теперь принадлежит фракции %s.\",\n\tdRemoveFaction = \"Эта дверь больше не принадлежит ни одной фракции.\",\n\tdNotValid = \"Вы не смотрите на валидную дверь!\",\n\tcanNotAfford = \"Вы не можете позволить это себе!\",\n\tdPurchased = \"Вы купили эту дверь за %s.\",\n\tdSold = \"Вы продали эту дверь за %s.\",\n\tnotOwner = \"Вы не владелец этой двери!\",\n\tinvalidArg = \"Указано недопустимое значение для аргумента #%s!\",\n\tinvalidFaction = \"Указанная фракция не найдена!\",\n\tflagGive = \"%s выдал игроку %s флаги'%s'.\",\n\tflagGiveTitle = \"Выдать флаги\",\n\tflagTake = \"%s забрал у игрока '%s' флаги %s.\",\n\tflagTakeTitle = \"Забрать флаги\",\n\tflagNoMatch = \"Вы должны иметь \\\"%s\\\" флаш(и) чтобы сделать это!\",\n\ttextAdded = \"Текст добавлен.\",\n\ttextRemoved = \"Вы удалили %s текст(ов).\",\n\tmoneyTaken = \"Вы получили %s.\",\n\tmoneyGiven = \"Вы отдали %s.\",\n\tinsufficientMoney = \"У вас не хватает денег, чтобы сделать это!\",\n\tbusinessPurchase = \"Вы купили %s за %s.\",\n\tbusinessSell = \"Вы продали %s за %s.\",\n\tbusinessTooFast = \"Подождите, прежде чем покупать другой товар!\",\n\tcChangeModel = \"%s сменил %s модель на %s.\",\n\tcChangeName = \"%s сменил %s имя на %s.\",\n\tcChangeSkin = \"%s сменил %s скин на %s.\",\n\tcChangeGroups = \"%s сменил %s \\\"%s\\\" на бодигруппу %s.\",\n\tcChangeFaction = \"%s перенес персонажа %s во фракцию %s \",\n\tplayerCharBelonging = \"Этот предмет принадлежит другому вашему персонажу!\",\n\tspawnAdd = \"Вы добавили спаун для фракции%s.\",\n\tspawnDeleted = \"Вы удалили %s спаун(ов).\",\n\tsomeone = \"Кто-то\",\n\trgnLookingAt = \"Представиться человеку, на которого вы смотрите.\",\n\trgnWhisper = \"Представиться людям в радиусе шепота.\",\n\trgnTalk = \"Представиться людям в радиусе разговора.\",\n\trgnYell = \"Представиться людям в радиусе крика.\",\n\ticFormat = \"%s говорит \\\"%s\\\"\",\n\trollFormat = \"%s получил число %s из %s.\",\n\twFormat = \"%s шепчет \\\"%s\\\"\",\n\tyFormat = \"%s кричит \\\"%s\\\"\",\n\tsbOptions = \"Нажмите, чтобы посмотреть настройки %s.\",\n\tspawnAdded = \"Вы добавили спаун для %s.\",\n\twhitelist = \"%s выдал вайтлист %s для фракции %s.\",\n\tunwhitelist = \"%s забрал вайтлист у %s фракции %s.\",\n\tnoWhitelist = \"У вас нет вайтлиста для этого персонажа!\",\n\tgettingUp = \"Вы поднимаетесь...\",\n\twakingUp = \"Вы приходите в себя...\",\n\tAmmunition = \"Боеприпасы\",\n\tStorage = \"Вместилища\",\n\tOutfit = \"Одежда\",\n\tWeapons = \"Оружие\",\n\tcheckout = \"Оформить заказ (%s)\",\n\tpurchase = \"Купить\",\n\tpurchasing = \"Покупка...\",\n\tsuccess = \"Успешно\",\n\tbuyFailed = \"Покупка не выполнена!\",\n\tbuyGood = \"Покупка выполнена!\",\n\tshipment = \"Товар\",\n\tshipmentDesc = \"Этот товар принадлежит %s.\",\n\tclass = \"Класс\",\n\tclasses = \"Классы\",\n\tillegalAccess = \"Незаконный доступ.\",\n\tbecomeClassFail = \"Вы не можете выбрать класс %s!\",\n\tbecomeClass = \"Вы выбрали класс %s.\",\n\tsetClass = \"Вы установили игроку %s класс %s.\",\n\tattributeSet = \"Вы сменили игроку %s %s на %s.\",\n\tattributeNotFound = \"Вы указали неверный атрибут!\",\n\tattributeUpdate = \"Вы добавили игроку %s %s, %s единицы.\",\n\tnoFit = \"Недостаточно места для этого предмета!\",\n\titemOwned = \"Вы не можете взаимодействовать с предметом, который принадлежит другому вашему персонажу!\",\n\thelp = \"Помощь\",\n\tcommands = \"Команды\",\n\tdoorSettings = \"Настройки двери\",\n\tsell = \"Продать\",\n\taccess = \"Доступ\",\n\tlocking = \"Обьект блокируется...\",\n\tunlocking = \"Обьект разблокируется...\",\n\tmodelNoSeq = \"Ваша модель не поддерживает эту анимацию.\",\n\tnotNow = \"Вы не можете сделать это сейчас.\",\n\tfaceWall = \"Прислонитесь лицом к стене, чтобы выполнить эту анимацию.\",\n\tfaceWallBack = \"Прислонитесь спиной к стене, чтобы выполнить эту анимацию.\",\n\tdescChanged = \"Описание вашего персонажа успешно изменено.\",\n\tnoOwner = \"Неверный владелец!\",\n\tinvalidItem = \"Вы указали неверный предмет!\",\n\tinvalidInventory = \"Вы указали неверный инвентарь!\",\n\thome = \"Домой\",\n\tcharKick = \"%s кикнул персонажа %s.\",\n\tcharBan = \"%s забанил персонажа %s.\",\n\tcharBanned = \"Этот персонаж забанен.\",\n\tcharBannedTemp = \"Этот персонаж временно забанен.\",\n\tplayerConnected = \"%s подключился к серверу.\",\n\tplayerDisconnected = \"%s отключился от сервера.\",\n\tsetMoney = \"Вы установили игроку %s баланс %s единиц.\",\n\titemPriceInfo = \"Вы можете приобрести этот предмет за %s.\\nВы можете продать этот предмет за %s\",\n\tfree = \"Бесплатно\",\n\tvendorNoSellItems = \"У торговца нету предметов для продажи.\",\n\tvendorNoBuyItems = \"Нету предметов для покупки.\",\n\tvendorSettings = \"Настройки торговца\",\n\tvendorUseMoney = \"Торговец использует деньги?\",\n\tvendorNoBubble = \"Убрать пузырек над головой?\",\n\tmode = \"Режим\",\n\tprice = \"Цена\",\n\tstock = \"Кол-во\",\n\tnone = \"Нет\",\n\tvendorBoth = \"Покупать и продавать\",\n\tvendorBuy = \"Только покупать\",\n\tvendorSell = \"Только продавать\",\n\tmaxStock = \"Максимальное кол-во\",\n\tvendorFaction = \"Доступ фракций\",\n\tbuy = \"Купить\",\n\tvendorWelcome = \"Приветствую вас в моем магазине. Желаете что-то купить?\",\n\tvendorBye = \"Приходите еще!\",\n\tcharSearching = \"Вы уже ищите другого персонажа!\",\n\tcharUnBan = \"%s разбанил персонажа %s.\",\n\tcharNotBanned = \"Этот персонаж не забанен!\",\n\tquickSettings = \"Быстрые настройки\",\n\tvmSet = \"Вы настроили свою голосовую почту.\",\n\tvmRem = \"Вы удалили свою голосовую почту.\",\n\tnoPerm = \"У вас нет прав чтобы это сделать!\",\n\tyoureDead = \"Вы погибли\",\n\tinjMajor = \"Очень сильно ранен\",\n\tinjLittle = \"Слегка ранен\",\n\tchgName = \"Сменить имя\",\n\tchgNameDesc = \"Введите новое имя персонажа.\",\n\tweaponSlotFilled = \"Слот %s занять другим оружием!\",\n\tequippedBag = \"Вы не можете переместить сумку с предметами внутри!\",\n\tequippedWeapon = \"Вы не можете манипулировать экипированным оружием!\",\n\tnestedBags = \"Вы не можете положить сумку с вещами в хранилище!\",\n\toutfitAlreadyEquipped = \"Вы уже носите этот элемент экипировки!\",\n\tLoad = \"Зарядить\",\n\tuseTip = \"Использовать этот предмет.\",\n\tView = \"Просмотреть\",\n\tEquip = \"Экипировать\",\n\tequipTip = \"Экипировать этот предмет.\",\n\tUnequip = \"Стянуть\",\n\tunequipTip = \"Стянуть этот предмет.\",\n\tconsumables = \"Расходные материалы\",\n\tplyNotValid = \"Вы должны смотреть на существующего игрока!\",\n\trestricted = \"Вы были связаны.\",\n\tsalary = \"Вы получили свою зарплату в размере %s.\",\n\tnoRecog = \"Вы не знаете этого персонажа.\",\n\tcurTime = \"Время: %s.\",\n\tvendorEditor = \"Редактор торговца\",\n\tedit = \"Изменить макс. кол-во\",\n\tdisable = \"Отключить\",\n\tvendorPriceReq = \"Введите новую цену для этого предмета.\",\n\tvendorEditCurStock = \"Изменить настоящее кол-во\",\n\tvendorStockReq = \"Введите максимальное количество предметов.\",\n\tvendorStockCurReq = \"Введите доступное количество предметов для покупки.\",\n\tyou = \"Персонаж\",\n\tvendorSellScale = \"Множитель продажи\",\n\tvendorNoTrade = \"Вы не можете торговать с этим торговцем!\",\n\tvendorNoMoney = \"У торговца недостаточно денег для покупки!\",\n\tvendorNoStock = \"У торговца нету этого товара!\",\n\tcontentTitle = \"Отсутствует контент Helix\",\n\tcontentWarning = \"У вас нет контента фреймворка Helix. Могут отсутствовать некоторые функции.\\nХотите открыть страницу контента в Steam Workshop?\",\n\tflags = \"Флаги\",\n\tmapRestarting = \"Рестарт карты произойдет через %d секунд!\",\n\tchooseTip = \"Выберите персонажа, чтобы начать играть.\",\n\tdeleteTip = \"Удалить этого персонажа.\",\n\tstorageInUse = \"Кто-то уже использует это!\",\n\tstorageSearching = \"Открываю...\",\n\tcontainer = \"Контейнер\",\n\tcontainerPassword = \"На контейнер установлен замок с паролем %s.\",\n\tcontainerPasswordRemove = \"Пароль с этого контейнера был убран.\",\n\tcontainerPasswordWrite = \"Введите пароль.\",\n\tcontainerName = \"Имя контейнера установлено %s.\",\n\tcontainerNameWrite = \"Введите имя.\",\n\tcontainerNameRemove = \"Имя контейнера убрано.\",\n\tcontainerInvalid = \"Вы должны смотреть на контейнер, чтобы это выполнить это!\",\n\twrongPassword = \"Неверный пароль!\",\n\trespawning = \"Респавнимся...\",\n\ttellAdmin = \"Сообщите администрации эту ошибку: %s\",\n\n\tmapAdd = \"Вы добавили сцену карты.\",\n\tmapDel = \"Вы удалили сцену(ы) %d.\",\n\tmapRepeat = \"Теперь добавьте вторую точку.\",\n\n\tscoreboard = \"Игроки\",\n\tping = \"Пинг: %d\",\n\tviewProfile = \"Открыть профиль Steam\",\n\tcopySteamID = \"Скопировать Steam ID\",\n\n\tmoney = \"Наличные\",\n\tmoneyLeft = \"Ваши наличные: \",\n\tcurrentMoney = \"Баланс: \",\n\n\tinvalidClass = \"Недопустимый класс!\",\n\tinvalidClassFaction = \"Недопустимый класс или фракция!\",\n\n\tmiscellaneous = \"Разное\",\n\tgeneral = \"Общие\",\n\tobserver = \"ESP и наблюдение\",\n\tperformance = \"Производительность\",\n\tthirdperson = \"Третье лицо\",\n\tdate = \"Дата\",\n\tinteraction = \"Взаимодействие\",\n\tserver = \"Сервер\",\n\n\tresetDefault = \"По умолчанию\",\n\tresetDefaultDescription = \"Это сбросит значение \\\"%s\\\" до стандартного значения \\\"%s\\\".\",\n\toptOpenBags = \"Открывать сумки в инвентаре \",\n\toptdOpenBags = \"При открытии меню, автоматически открывает все сумки в вашем инвентаре.\",\n\toptShowIntro = \"Интро при подключении\",\n\toptdShowIntro = \"Показывает интро фреймворка при следующем подключении. Автоматически отключается после просмотра.\",\n\toptCheapBlur = \"Отключить блюр\",\n\toptdCheapBlur = \"Заменяет блюр в интерфейсе на простое затемнение.\",\n\toptObserverTeleportBack = \"Возвращение на место\",\n\toptdObserverTeleportBack = \"Возвращает вас к месту, в котором вы вошли в режим наблюдения.\",\n\toptObserverESP = \"Админ ESP\",\n\toptdObserverESP = \"Показывает имена и местоположение каждого игрока на сервере.\",\n\topt24hourTime = \"24-часовой формат времени\",\n\toptd24hourTime = \"Показывать время в 24-х часовом формате, вместо 12 часового (AM/PM).\",\n\toptChatNotices = \"Уведомления в чате\",\n\toptdChatNotices = \"Выводит все уведомления которые появляются в правом верхнем углу в текстовый чат.\",\n\toptChatTimestamps = \"Время в чате\",\n\toptdChatTimestamps = \"Отмечает время отправки кажого сообщения в чате.\",\n\toptAlwaysShowBars = \"Всегда показывать информационную панель\",\n\toptdAlwaysShowBars = \"Отображает состояние персонажа в левом верхнем углу на постоянной основе.\",\n\toptAltLower = \"Опуская, скрывать руки\", -- @todo remove me\n\toptdAltLower = \"Скрывать руки, при опускани оных.\", -- @todo remove me\n\toptThirdpersonEnabled = \"|Третье лицо\",\n\toptdThirdpersonEnabled = \"Перемещение камеры позади вас. Также переключается консольной командой \\'ix_togglethirdperson\\'\",\n\toptThirdpersonClassic = \"|Классическая камера\",\n\toptdThirdpersonClassic = \"Управлять взглядом вашего персонажа с помощью мыши.\",\n\toptThirdpersonVertical = \"Высота камеры\",\n\toptdThirdpersonVertical = \"Высота расположения камеры от 3 лица.\",\n\toptThirdpersonHorizontal = \"Камера по горизонтали\",\n\toptdThirdpersonHorizontal = \"Расположение камеры влево или вправо от персонажа.\",\n\toptThirdpersonDistance = \"Расстояние камеры\",\n\toptdThirdpersonDistance = \"Как далеко располагается камера от персонажа.\",\n\toptThirdpersonCrouchOffset = \"Высота камеры при присяде\",\n\toptdThirdpersonCrouchOffset = \"Высота расположения камеры от 3 лица.\",\n\toptDisableAnimations = \"Отключить анимации интерфейса\",\n\toptdDisableAnimations = \"Отключает анимации интерфейса, для резкого перехода между окнами.\",\n\toptAnimationScale = \"Скорость анимаций\",\n\toptdAnimationScale = \"Насколько быстрее или медленнее проигрывается анимация.\",\n\toptLanguage = \"Язык\",\n\toptdLanguage = \"Язык интерфейса Helix.\",\n\toptMinimalTooltips = \"Минималистичные подсказки\",\n\toptdMinimalTooltips = \"Изменяет стиль показа подсказок, чтобы занимать меньше места.\",\n\toptNoticeDuration = \"Длительность уведомления\",\n\toptdNoticeDuration = \"Как долго показываются уведомления (в секундах).\",\n\toptNoticeMax = \"Максимальное кол-во уведомлений\",\n\toptdNoticeMax = \"Количество уведомлений, показываемых одновременно.\",\n\toptChatFontScale = \"Шрифт чата\",\n\toptdChatFontScale = \"Насколько больше или меньше шрифт чата должен быть.\",\n\toptChatOutline = \"Контур в чате\",\n\toptdChatOutline = \"Рисует контур вокруг текста в чате. Включите, если у вас проблемы с чтением чата.\",\n\n\tcmdRoll = \"Случайное число от 0 до указанного значения.\",\n\tcmdPM = \"Отправить личное сообщение выбранному персонажу.\",\n\tcmdReply = \"Отправить личное сообщение последнему человеку, от которого вы получили сообщение.\",\n\tcmdSetVoicemail = \"Установить или убрать сообщение автоответчика, при отправке вам сообщения.\",\n\tcmdCharGiveFlag = \"Дать указанному игроку флаг(и)\",\n\tcmdCharTakeFlag = \"Забрать указанный(е) флаг(и) у игрока, если такие имеются.\",\n\tcmdToggleRaise = \"Поднять или опустить оружие, которое вы удерживаете.\",\n\tcmdCharSetModel = \"Установить выбранную модель игроку.\",\n\tcmdCharSetSkin = \"Установить выбранный скин для модели игрока.\",\n\tcmdCharSetBodygroup = \"Установить бодигруппу для модели игрока.\",\n\tcmdCharSetAttribute = \"Установить уровень выбранного атрибута у игрока.\",\n\tcmdCharAddAttribute = \"Добавить к уровню выбранное количество очков.\",\n\tcmdCharSetName = \"Изменить имя пользователя на заданное.\",\n\tcmdCharGiveItem = \"Дать заданный предмет игроку.\",\n\tcmdCharKick = \"Кикнуть выбранного персонажа.\",\n\tcmdCharBan = \"Ограничить игроку доступ к выбранному персонажу.\",\n\tcmdCharUnban = \"Снимает ограничение на игру за выбранного персонажа\",\n\tcmdGiveMoney = \"Передать сумму денег персонажу, на которого вы смотрите.\",\n\tcmdCharSetMoney = \"Изменить сумму денег у персонажа на указанную.\",\n\tcmdDropMoney = \"Положить определенную сумму денег в хранилище на которое вы смотрите.\",\n\tcmdPlyWhitelist = \"Выдать доступ игроку к созданию персонажа в указанной фракции\",\n\tcmdCharGetUp = \"Попытаться встать после падения.\",\n\tcmdPlyUnwhitelist = \"Забрать доступ у игрока к созданию персонажа в указанной фракции.\",\n\tcmdCharFallOver = \"Расслабиться и упасть на землю.\",\n\tcmdBecomeClass = \"Взять выбранный класс в вашей текущей фракции.\",\n\tcmdCharDesc = \"Установить физическое описание вашего персонажа.\",\n\tcmdCharDescTitle = \"Физическое описание\",\n\tcmdCharDescDescription = \"Введите физическое описание персонажа.\",\n\tcmdPlyTransfer = \"Перевести выбранного персонажа в указанную фракцию.\",\n\tcmdCharSetClass = \"Сменить выбранному игроку указанный класс в его фракции.\",\n\tcmdMapRestart = \"Перезапустить карту через указанное время.\",\n\tcmdPanelAdd = \"Создать веб изображение там, куда направлен взгляд.\",\n\tcmdPanelRemove = \"Удалить веб изображение, на которое вы смотрите.\",\n\tcmdTextAdd = \"Создать блок текста в мире.\",\n\tcmdTextRemove = \"Удалить блоки текста, на которые вы смотрите.\",\n\tcmdMapSceneAdd = \"Добавить кинематографичную камеру, в меню выбора персонажа.\",\n\tcmdMapSceneRemove = \"Убрать кинематографичную камеру, в меню выбора персонажа.\",\n\tcmdSpawnAdd = \"Добавить точку спавна для указанной фракции.\",\n\tcmdSpawnRemove = \"Удалить все точки спавна, на которые вы смотрите.\",\n\tcmdAct = \"Выполнить анимацию %s.\",\n\tcmdContainerSetPassword = \"Установить пароль для контейнера, на который вы смотрите.\",\n\tcmdDoorSell = \"Продать дверь, на которую вы смотрите.\",\n\tcmdDoorBuy = \"Купить дверь, на которую вы смотрите.\",\n\tcmdDoorSetUnownable = \"Сделать дверь, на которую вы смотрите, недоступной для покупки.\",\n\tcmdDoorSetOwnable = \"Сделать дверь, на которую вы смотрите, доступной для приобретения.\",\n\tcmdDoorSetFaction = \"Установить двери, на которую вы смотрите, принадлежность к фракции.\",\n\tcmdDoorSetDisabled = \"Отключить возможность взаимодействия с дверью, на которую вы смотрите.\",\n\tcmdDoorSetTitle = \"Установить заголовок двери, на которую вы смотрите.\",\n\tcmdDoorSetParent = \"Выбрать родительскую дверь для дочерних дверей.\",\n\tcmdDoorSetChild = \"Выбрать дочерние двери для родительской двери.\",\n\tcmdDoorRemoveChild = \"Убрать у двери дочернюю зависимость.\",\n\tcmdDoorSetHidden = \"Убирает описание двери на которую вы смотрите, но оставляет возможность владения.\",\n\tcmdDoorSetClass = \"Установить двери, на которую вы смотрите, принадлежность к классу во фракции.\",\n\tcmdMe = \"Выполнить физическое действие.\",\n\tcmdIt = \"Совершить какое-либо действие вокруг себя.\",\n\tcmdW = \"Прошептать что-то людям, стоящим рядом.\",\n\tcmdY = \"Крикнуть что-то людям, стоящим в большом радиусе.\",\n\tcmdEvent = \"Выполнить действие, которое увидят все игроки.\",\n\tcmdOOC = \"Отправить сообщение в глобальный out-of-character чат .\",\n\tcmdLOOC = \"Отправить сообщение в локальный out-of-character чат.\",\n\n\tInstructions = \"Информация\"\n}\n"
  },
  {
    "path": "gamemode/languages/sh_spanish.lua",
    "content": "-- SPANISH TRANSLATION\n-- Cuboxis (http://steamcommunity.com/id/Cuboxis)\n-- Geferon (https://steamcommunity.com/id/GEFERON)\n-- Whitehole (https://steamcommunity.com/id/whitehole)\n-- Carlos Bes\n\nNAME = \"Español\"\n\nLANGUAGE = {\n\thelix = \"Helix\",\n\n\tintroTextOne = \"fist industries presenta\",\n\tintroTextTwo = \"en colaboración con %s\",\n\tintroContinue = \"pulsa espacio para continuar\",\n\n\thelpIdle = \"Selecciona una categoría\",\n\thelpCommands = \"Los parámetros de comando rodeados de <flechas> son obligatorios, mientras que los rodeados de [corchetes] son opcionales.\",\n\thelpFlags = \"Las Flags con el fondo verde son accesibles por este personaje.\",\n\n\tcreditSpecial = \"Muchas Gracias\",\n\tcreditLeadDeveloper = \"Desarrollador Principal\",\n\tcreditUIDesigner = \"Diseñador de la Interfaz de Usuario\",\n\tcreditManager = \"Jefe de Proyecto\",\n\tcreditTester = \"Tester Principal\",\n\n\tchatTyping = \"Escribiendo...\",\n\tchatTalking = \"Hablando...\",\n\tchatYelling = \"Gritando...\",\n\tchatWhispering = \"Susurrando...\",\n\tchatPerforming = \"Actuando...\",\n\tchatNewTab = \"Nueva pestaña...\",\n\tchatReset = \"Restablecer posición\",\n\tchatResetTabs = \"Restablecer pestañas\",\n\tchatCustomize = \"Personalizar...\",\n\tchatCloseTab = \"Cerrar Pestaña\",\n\tchatTabName = \"Nombre de Pestaña\",\n\tchatNewTabTitle = \"Nueva Pestaña\",\n\tchatAllowedClasses = \"Categorías de chat permitidas\",\n\tchatTabExists = \"¡Ya existe una pestaña de chat con ese nombre!\",\n\tchatMarkRead = \"Marcar todo como leído\",\n\n\tcommunity = \"Comunidad\",\n\tcheckAll = \"Seleccionar todo\",\n\tuncheckAll = \"Deseleccionar todo\",\n\tcolor = \"Color\",\n\ttype = \"Tipo\",\n\tdisplay = \"Visualización\",\n\tloading = \"Cargando\",\n\tdbError = \"Fallo de conexión con la Base de Datos\",\n\tunknown = \"Desconocido\",\n\tnoDesc = \"Descripción no disponible\",\n\tcreate = \"Crear\",\n\tupdate = \"Actualizar\",\n\tload = \"Cargar personaje\",\n\tloadTitle = \"Cargar un Personaje\",\n\tleave = \"Abandonar\",\n\tleaveTip = \"Salir del servidor.\",\n\t[\"return\"] = \"Volver\",\n\treturnTip = \"Vuelve al menú anterior.\",\n\tproceed = \"Continuar\",\n\tfaction = \"Facción\",\n\tskills = \"Habilidades\",\n\tchoose = \"Escoger\",\n\tchooseFaction = \"Escoge una Facción\",\n\tchooseDescription = \"Describe la apariencia de tu personaje\",\n\tchooseSkills = \"Ajusta tus habilidades\",\n\tname = \"Nombre\",\n\tdescription = \"Descripción\",\n\tmodel = \"Modelo\",\n\tattributes = \"Atributos\",\n\tattribPointsLeft = \"Puntos restantes\",\n\tcharInfo = \"Información del personaje\",\n\tcharCreated = \"Has creado tu personaje correctamente.\",\n\tcharCreateTip = \"Completa todos los campos obligatorios y haz clic en 'Finalizar' para crear a tu personaje.\",\n\tinvalid = \"Has introducido un(a) %s inválido/a.\",\n\tnameMinLen = \"Tu nombre debe de tener al menos %d caracteres.\",\n\tnameMaxLen = \"Tu nombre no puede tener mas de %d caracteres.\",\n\tdescMinLen = \"Tu descripción debe tener al menos %d caracteres.\",\n\tmaxCharacters = \"¡No puedes crear mas personajes!\",\n\tplayer = \"Jugador\",\n\tfinish = \"Finalizar\",\n\tfinishTip = \"Finaliza la creación de personaje.\",\n\tneedModel = \"Necesitas escoger un modelo válido.\",\n\tcreating = \"Tu personaje está siendo creado...\",\n\tunknownError = \"Ha ocurrido un error desconocido\",\n\tareYouSure = \"¿Estás seguro?\",\n\tdelete = \"Borrar\",\n\tdeleteConfirm = \"¡Este personaje será eliminado PERMANENTEMENTE!\",\n\tdeleteComplete = \"%s ha sido eliminado.\",\n\tno = \"No\",\n\tyes = \"Sí\",\n\tclose = \"Cerrar\",\n\tsave = \"Guardar\",\n\titemInfo = \"Nombre: %s\\nDescripción: %s\",\n\titemCreated = \"Objeto(s) creado(s) correctamente.\",\n\tcloud_no_repo = \"El repositorio introducido no es válido.\",\n\tcloud_no_plugin = \"El plugin introducido no es válido.\",\n\tinv = \"Inventario\",\n\tplugins = \"Plugins\",\n\tpluginLoaded = \"%s ha habilitado el plugin \\\"%s\\\" y se habilitará con el siguiente cambio de mapa.\",\n\tpluginUnloaded = \"%s ha deshabilitado el plugin \\\"%s\\\" y se deshabilitará con el siguiente cambio de mapa.\",\n\tloadedPlugins = \"Plugins Habilitados\",\n\tunloadedPlugins = \"Plugins Deshabilitados\",\n\ton = \"On\",\n\toff = \"Off\",\n\tauthor = \"Autor\",\n\tversion = \"Versión\",\n\tcharacters = \"Personajes\",\n\tbusiness = \"Negocios\",\n\tsettings = \"Opciones\",\n\tconfig = \"Configuración\",\n\tchat = \"Chat\",\n\tappearance = \"Apariencia\",\n\tmisc = \"Misceláneo\",\n\toocDelay = \"Debes esperar %s segundo(s) antes de usar de nuevo el chat OOC.\",\n\tloocDelay = \"Debes esperar %s segundo(s) antes de usar de nuevo el chat LOOC.\",\n\tusingChar = \"Ya estás usando éste personaje.\",\n\tnotAllowed = \"Lo siento, no tienes permitido hacer esto.\",\n\titemNoExist = \"Lo siento, el objeto especificado no existe.\",\n\tcmdNoExist = \"Lo siento, este comando no existe.\",\n\tcharNoExist = \"Lo siento, el personaje especificado no ha podido ser encontrado.\",\n\tplyNoExist = \"Lo siento, el jugador correspondiente no ha podido ser encontrado.\",\n\tcfgSet = \"%s ha establecido \\\"%s\\\" a %s.\",\n\tdrop = \"Tirar\",\n\tdropTip = \"Suelta el objeto de tu inventario.\",\n\ttake = \"Coger\",\n\ttakeTip = \"Pone este objeto en tu inventario.\",\n\tdTitle = \"Puerta sin propietario\",\n\tdTitleOwned = \"Puerta comprada\",\n\tdIsNotOwnable = \"Esta puerta no puede tener propietario.\",\n\tdIsOwnable = \"Puedes comprar esta puerta pulsando F2.\",\n\tdMadeUnownable = \"Has hecho que esta puerta no pueda tener propietario.\",\n\tdMadeOwnable = \"Has hecho que esta puerta pueda tener propietario.\",\n\tdNotAllowedToOwn = \"No tienes permiso para hacer esta puerta de tu propiedad.\",\n\tdSetDisabled = \"Has deshabilitado esta puerta.\",\n\tdSetNotDisabled = \"Has habilitado esta puerta.\",\n\tdSetHidden = \"Has hecho que esta puerta esté oculta.\",\n\tdSetNotHidden = \"Has hecho que esta puerta no esté oculta.\",\n\tdSetParentDoor = \"Has establecido esta puerta como puerta primaria.\",\n\tdCanNotSetAsChild = \"No puedes establecer la puerta primaria como puerta secundaria.\",\n\tdAddChildDoor = \"Has añadido esta puerta como puerta secundaria.\",\n\tdRemoveChildren = \"Has eliminado todas las puertas secundarias de esta puerta.\",\n\tdRemoveChildDoor = \"Has eliminado esta puerta como puerta secundaria.\",\n\tdNoParentDoor = \"No tienes seleccionada una puerta primaria.\",\n\tdOwnedBy = \"Esta puerta es propiedad de %s.\",\n\tdConfigName = \"Puertas\",\n\tdSetFaction = \"Esta puerta ahora pertenece a %s.\",\n\tdRemoveFaction = \"Esta puerta ya no pertenece a ninguna facción.\",\n\tdNotValid = \"No estás mirando a una puerta válida.\",\n\tcanNotAfford = \"No puedes permitirte comprar esto.\",\n\tdPurchased = \"Has comprado esta puerta por %s.\",\n\tdSold = \"Has vendido esta puerta por %s.\",\n\tnotOwner = \"Esto no te pertenece.\",\n\tinvalidArg = \"El valor introducido para el argumento #%s es inválido.\",\n\tinvalidFaction = \"La facción que has introducido no se ha podido encontrar.\",\n\tflagGive = \"%s ha dado a %s las flags '%s'.\",\n\tflagGiveTitle = \"Dar Flags\",\n\tflagTake = \"%s ha quitado las flags '%s' de %s.\",\n\tflagTakeTitle = \"Quitar Flags\",\n\tflagNoMatch = \"Debes tener la(s) flag(s) \\\"%s\\\" para hacer esta acción.\",\n\tpanelAdded = \"Has añadido un panel.\",\n\tpanelRemoved = \"Has borrado %d panel(es).\",\n\ttextAdded = \"Has añadido un texto.\",\n\ttextRemoved = \"Has borrado %s texto(s).\",\n\tmoneyTaken = \"Has recibido %s.\",\n\tmoneyGiven = \"Has dado %s.\",\n\tinsufficientMoney = \"No puedes permitirte esto con tu dinero.\",\n\tbelowMinMoneyDrop = \"No puedes soltar menos de %s.\",\n\tbusinessPurchase = \"Has comprado %s por %s.\",\n\tbusinessSell = \"Has vendido %s por %s.\",\n\tbusinessTooFast = \"Por favor, espera antes de comprar otro objeto.\",\n\tcChangeModel = \"%s ha cambiado el modelo de %s a %s.\",\n\tcChangeName = \"%s ha cambiado el nombre de %s a %s.\",\n\tcChangeSkin = \"%s ha cambiado el 'skin' de %s a %s.\",\n\tcChangeGroups = \"%s ha cambiado el 'bodygroup' \\\"%s\\\" de %s a %s.\",\n\tcChangeFaction = \"%s ha transferido a %s a la facción %s.\",\n\tplayerCharBelonging = \"Este objeto ya pertenece a otro de tus personajes.\",\n\tspawnAdd = \"Has añadido un punto de reaparición para %s.\",\n\tspawnDeleted = \"Has eliminado un punto de reaparición de %s.\",\n\tsomeone = \"Alguien\",\n\trgnLookingAt = \"Permite que te reconozca la persona a la que estás mirando.\",\n\trgnWhisper = \"Permite que te reconozcan aquellos que te escuchen susurrar.\",\n\trgnTalk = \"Permite que te reconozcan aquellos que te escuchen hablar.\",\n\trgnYell = \"Permite que te reconozcan aquellos que te escuchen gritar.\",\n\ticFormat = \"%s dice \\\"%s\\\"\",\n\trollFormat = \"%s ha tirado los dados y ha sacado un %s.\",\n\twFormat = \"%s susurra \\\"%s\\\"\",\n\tyFormat = \"%s grita \\\"%s\\\"\",\n\tsbOptions = \"Haz clic para ver las opciones de %s.\",\n\tspawnAdded = \"Has añadido punto de aparición de %s.\",\n\twhitelist = \"%s ha añadido a %s en la lista blanca de la facción %s.\",\n\tunwhitelist = \"%s ha eliminado a %s de la lista blanca de la facción %s.\",\n\tnoWhitelist = \"No tienes la lista blanca para la facción de este personaje.\",\n\tcharNotWhitelisted = \"%s no está en la lista blanca para la facción %s.\",\n\tgettingUp = \"Te estás levantando...\",\n\twakingUp = \"Estás recuperando la consciencia...\",\n\tWeapons = \"Armas\",\n\tcheckout = \"Ir a la cesta (%s)\",\n\tpurchase = \"Comprar\",\n\tpurchasing = \"Comprando...\",\n\tsuccess = \"Éxito.\",\n\tbuyFailed = \"Compra fallida.\",\n\tbuyGood = \"Compra exitosa!\",\n\tshipment = \"Envío\",\n\tshipmentDesc = \"Este envío pertenece a %s.\",\n\tclass = \"Clase\",\n\tclasses = \"Clases\",\n\tillegalAccess = \"Acceso ilegal.\",\n\tbecomeClassFail = \"Fallo en convertirse en %s.\",\n\tbecomeClass = \"Te has convertido en %s.\",\n\tsetClass = \"Has establecido la clase de %s a %s.\",\n\tattributeSet = \"Has establecido el nivel de %s de %s a %s puntos.\",\n\tattributeNotFound = \"Has especificado un atributo inválido.\",\n\tattributeUpdate = \"Has añadido al nivel de %s de %s %s puntos.\",\n\tnoFit = \"Este objeto no cabe en tu inventario.\",\n\titemOwned = \"No puedes interactuar con un objeto de tus otros personajes.\",\n\thelp = \"Ayuda\",\n\tcommands = \"Comandos\",\n\tdoorSettings = \"Configuración de puertas\",\n\tsell = \"Vender\",\n\taccess = \"Acceso\",\n\tlocking = \"Bloqueando esta entidad...\",\n\tunlocking = \"Desbloqueando esta entidad...\",\n\tmodelNoSeq = \"Tu modelo no es compatible con este acto.\",\n\tnotNow = \"No tienes permiso para hacer esto ahora.\",\n\tfaceWall = \"Debes estar mirando una pared para hacer esto.\",\n\tfaceWallBack = \"Tu espalda tiene que estar mirando una pared para hacer esto.\",\n\tdescChanged = \"Has cambiado la descripción de tu personaje.\",\n\tnoOwner = \"El propietario no es válido.\",\n\tinvalidItem = \"El objeto no es válido.\",\n\tinvalidInventory = \"El inventario no es válido.\",\n\thome = \"Inicio\",\n\tcharKick = \"%s ha echado a %s.\",\n\tcharBan = \"%s ha bloqueado el personaje %s.\",\n\tcharBanned = \"Este personaje está bloqueado.\",\n\tcharBannedTemp = \"Este personaje está temporalmente bloqueado.\",\n\tplayerConnected = \"%s se ha conectado al servidor.\",\n\tplayerDisconnected = \"%s se ha desconectado del servidor.\",\n\tsetMoney = \"Has establecido el dinero de %s a %s.\",\n\titemPriceInfo = \"Puedes comprar este objeto por %s.\\nPuedes vender este objeto por %s.\",\n\tfree = \"Gratis\",\n\tvendorNoSellItems = \"No hay objetos para vender.\",\n\tvendorNoBuyItems = \"No hay objetos para comprar.\",\n\tvendorSettings = \"Configuración del Vendedor\",\n\tvendorUseMoney = \"¿El vendedor debe usar dinero?\",\n\tvendorNoBubble = \"¿Esconder burbuja del vendedor?\",\n\tcategory = \"Categoría\",\n\tmode = \"Modo\",\n\tprice = \"Precio\",\n\tstock = \"Existencias\",\n\tnone = \"Nada\",\n\tvendorBoth = \"Comprar y vender\",\n\tvendorBuy = \"Sólo comprar\",\n\tvendorSell = \"Sólo vender\",\n\tmaxStock = \"Existencias máximas\",\n\tvendorFaction = \"Editor de facción\",\n\tbuy = \"Comprar\",\n\tvendorWelcome = \"Bienvenido/a a mi tienda, ¿Qué puedo hacer por usted?\",\n\tvendorBye = \"¡Vuelva pronto!\",\n\tcharSearching = \"Ya te encuentras buscando a otro personaje, por favor espera.\",\n\tcharUnBan = \"%s ha desbloqueado el personaje %s.\",\n\tcharNotBanned = \"Este personaje no está bloqueado.\",\n\tquickSettings = \"Configuración rápida\",\n\tvmSet = \"Has establecido tu buzón de voz.\",\n\tvmRem = \"Has eliminado tu buzón de voz.\",\n\tnoPerm = \"No tienes permiso para hacer esto.\",\n\tyoureDead = \"Estás muerto.\",\n\tinjMajor = \"Parece que está gravemente herido.\",\n\tinjLittle = \"Parece que está herido.\",\n\tchgName = \"Cambiar nombre\",\n\tchgNameDesc = \"Introduce abajo el nuevo nombre del personaje.\",\n\tweaponSlotFilled = \"No puedes usar otra arma %s!\",\n\tequippedBag = \"La bolsa que has movido tiene un objeto que te has equipado.\",\n\tequippedWeapon = \"¡No puedes mover un arma que tienes equipada actualmente!\",\n\tnestedBags = \"¡No puedes poner un inventario dentro de un inventario de almacenamiento!\",\n\toutfitAlreadyEquipped = \"¡Ya estas usando este tipo de ropa!\",\n\tuseTip = \"Usa el objeto.\",\n\tequip = \"Equipar\",\n\tequipTip = \"Equipa el objeto.\",\n\tunequip = \"Desequipar\",\n\tunequipTip = \"Desequipa el objeto.\",\n\tconsumables = \"Consumibles\",\n\tplyNotValid = \"No estás mirando a un jugador válido.\",\n\trestricted = \"Has sido atado.\",\n\tsalary = \"Has recibido %s de tu salario.\",\n\tnoRecog = \"No reconoces a esta persona.\",\n\tcurTime = \"La hora actual es %s.\",\n\tvendorEditor = \"Editor de vendedor\",\n\tedit = \"Editar\",\n\tdisable = \"Deshabilitar\",\n\tvendorPriceReq = \"Introduce el nuevo precio del artículo.\",\n\tvendorEditCurStock = \"Editar las existencias actuales\",\n\tvendorStockReq = \"Introduce la cantidad máxima de existencias que debe tener el artículo.\",\n\tvendorStockCurReq = \"Introduce cuantos artículos de las existencias máximas estan disponibles para comprar.\",\n\tyou = \"Tú\",\n\tvendorSellScale = \"Escala de precio de venta\",\n\tvendorNoTrade = \"¡No puedes intercambiar con este vendedor!\",\n\tvendorNoMoney = \"Este vendedor no puede permitirse comprar ese artículo.\",\n\tvendorNoStock = \"Este vendedor no tiene ese artículo en existencia.\",\n\tvendorMaxStock = \"¡Este vendedor ya tiene las existencias máximas de ese artículo!\",\n\tcontentTitle = \"Falta el Contenido de Helix\",\n\tcontentWarning = \"No tienes el contenido de Helix montado. Puede que falten ciertas características.\\n¿Quieres abrir la página de Workshop del contenido de Helix?\",\n\tflags = \"Flags\",\n\tmapRestarting = \"¡El mapa se reiniciará en %d segundos!\",\n\tchooseTip = \"Escoge este personaje para jugar.\",\n\tdeleteTip = \"Eliminar este personaje.\",\n\tstorageInUse = \"¡Alguien ya está usando esto!\",\n\tstorageSearching = \"Buscando...\",\n\tcontainer = \"Contenedor\",\n\tcontainerPassword = \"Has establecido la contraseña de este contenedor a %s.\",\n\tcontainerPasswordRemove = \"Has eliminado la contraseña de este contenedor.\",\n\tcontainerPasswordWrite = \"Introduce la contraseña.\",\n\tcontainerName = \"Has cambiado el nombre de este contenedor a %s.\",\n\tcontainerNameWrite = \"Introduce el nombre.\",\n\tcontainerNameRemove = \"Has borrado el nombre de este contenedor.\",\n\tcontainerInvalid = \"¡Necesitas estar mirando a un contenedor para hacer esto!\",\n\tpasswordAttemptLimit = \"¡Demasiados intentos fallidos de contraseña!\",\n\twrongPassword = \"Has introducido una contraseña errónea.\",\n\trespawning = \"Reapareciendo...\",\n\tsyntax = \"Formato: %s\",\n\ttellAdmin = \"Informa a un miembro del staff de este error: %s\",\n\n\tmapAdd = \"Has añadido una escena de mapa.\",\n\tmapDel = \"Has borrado %d escena(s) de mapa.\",\n\tmapRepeat = \"Ahora añade el segundo punto.\",\n\n\tscoreboard = \"Marcador\",\n\tping = \"Ping: %d\",\n\tviewProfile = \"Ver el perfil de Steam.\",\n\tcopySteamID = \"Copiar Steam ID\",\n\n\tmoney = \"Dinero\",\n\tmoneyLeft = \"Dinero restante: \",\n\tcurrentMoney = \"Dinero actual: \",\n\n\tinvalidClass = \"¡Esa no es una clase válida!\",\n\tinvalidClassFaction = \"¡Esa no es una clase válida para la facción!\",\n\n\tmiscellaneous = \"Misceláneo\",\n\tgeneral = \"General\",\n\tobserver = \"Observador\",\n\tperformance = \"Rendimiento\",\n\tthirdperson = \"Tercera Persona\",\n\tdate = \"Fecha\",\n\tinteraction = \"Interacción\",\n\tserver = \"Servidor\",\n\n\tresetDefault = \"Restablecer a valores predeterminados\",\n\tresetDefaultDescription = \"Esto restablecerá \\\"%s\\\" a su valor predeterminado de \\\"%s\\\".\",\n\toptOpenBags = \"Abrir objetos con inventario\",\n\toptdOpenBags = \"Abrir automáticamente todos los objetos con inventario cuando el menú es abierto.\",\n\toptShowIntro = \"Mostrar la intro al entrar\",\n\toptdShowIntro = \"Muestra la introducción de Helix la siguiente vez que entres. Esta opción siempre es deshabilitada una vez la has visto.\",\n\toptCheapBlur = \"Deshabilitar difuminación\",\n\toptdCheapBlur = \"Remplaza la difuminación de la interfaz con un oscurecido simple.\",\n\toptObserverTeleportBack = \"Volver a la posición anterior\",\n\toptdObserverTeleportBack = \"Te hace volver a la posición en la cual te encontrabas antes de entrar en modo observador.\",\n\toptObserverESP = \"Mostrar admin ESP\",\n\toptdObserverESP = \"Muestra los nombres y las localizaciones de cada jugador en el servidor.\",\n\topt24hourTime = \"Usar formato de 24 horas\",\n\toptd24hourTime = \"Muestra las marcas de tiempo en formato 24 horas, en vez de usar el formato de 12 horas (AM/PM).\",\n\toptChatNotices = \"Mostrar avisos en el chat\",\n\toptdChatNotices = \"Pone todos los avisos que aparecen en la esquina superior derecha, en el chat.\",\n\toptChatTimestamps = \"Mostrar marcas de tiempo en el chat\",\n\toptdChatTimestamps = \"Prepone una marca temporal a cada mensaje en el chat.\",\n\toptAlwaysShowBars = \"Mostrar siempre las barras de información\",\n\toptdAlwaysShowBars = \"Siempre muestra las barras de información en la esquina superior izquierda, sin importar si deberían de mostrarse o no.\",\n\toptAltLower = \"Ocultar las manos al estar bajadas\", -- @todo remove me\n\toptdAltLower = \"Oculta tus manos cuando están bajadas.\", -- @todo remove me\n\toptThirdpersonEnabled = \"Activar Tercera Persona\",\n\toptdThirdpersonEnabled = \"Pone la cámara detrás de ti. Esto también puede ser activado con el comando de consola \\\"ix_togglethirdperson\\\".\",\n\toptThirdpersonClassic = \"Activar el estilo clásico de tercera persona\",\n\toptdThirdpersonClassic = \"Mueve la vista de tu personaje con tu ratón.\",\n\toptThirdpersonVertical = \"Altura de Tercera Persona\",\n\toptdThirdpersonVertical = \"Como de alto debería de estar la cámara de tercera persona.\",\n\toptThirdpersonHorizontal = \"Horizontal de Tercera Persona\",\n\toptdThirdpersonHorizontal = \"Como de lejos a la izquierda o la derecha debería de estar la cámara.\",\n\toptThirdpersonDistance = \"Distancia de Tercera Persona\",\n\toptdThirdpersonDistance = \"Como de lejos debería de estar la cámara.\",\n\toptThirdpersonCrouchOffset = \"Altura de la cámara al agacharse\",\n\toptdThirdpersonCrouchOffset = \"A que altura debe de estar la cámara cuando se está agachado.\",\n\toptDisableAnimations = \"Desactivar animaciones\",\n\toptdDisableAnimations = \"Elimina las animaciones de la interfaz de usuario, haciendo que las transiciones sean instantáneas.\",\n\toptAnimationScale = \"Escala de animación\",\n\toptdAnimationScale = \"Velocidad de reproducción de las animaciones de la interfaz de usuario.\",\n\toptLanguage = \"Idioma\",\n\toptdLanguage = \"El idioma mostrado en la interfaz de usuario de Helix.\",\n\toptMinimalTooltips = \"Información del HUD minimalista\",\n\toptdMinimalTooltips = \"Cambia el estilo de la información del HUD para que ocupe menos espacio.\",\n\toptNoticeDuration = \"Duración de Avisos\",\n\toptdNoticeDuration = \"Cuanto tiempo duran los avisos (en segundos).\",\n\toptNoticeMax = \"Máximo de Avisos\",\n\toptdNoticeMax = \"La cantidad de avisos mostrados antes de que sean eliminados los anteriores.\",\n\toptChatFontScale = \"Tamaño de fuente del chat\",\n\toptdChatFontScale = \"Modifica el tamaño de la fuente del chat.\",\n\toptChatOutline = \"Contorno en el texto de chat\",\n\toptdChatOutline = \"Dibuja un contorno alrededor del texto del chat, en vez de una sombra paralela. Activa esto si tienes problemas leyendo el texto.\",\n\toptEscCloseMenu = \"Tecla ESC vuelve al juego\",\n\toptdEscCloseMenu = \"Al pulsar la tecla ESC, se cierra el menú de Helix y también se oculta automáticamente el menú de pausa de Garry's Mod. Es decir, vuelves directamente a controlar tu personaje.\",\n\n\tcmdRoll = \"Tira un numero entre 0 y el número especificado.\",\n\tcmdPM = \"Envía un mensaje privado a alguien.\",\n\tcmdReply = \"Responde a la ultima persona de la cual recibiste un mensaje privado.\",\n\tcmdSetVoicemail = \"Establece o elimina el mensaje de respuesta automática cuando alguien te envía un mensaje privado.\",\n\tcmdCharGiveFlag = \"Da la(s) flag(s) especificadas a alguien.\",\n\tcmdCharTakeFlag = \"Elimina la(s) flag(s) especificada(s) de alguien si las tienen.\",\n\tcmdToggleRaise = \"Levanta o baja el arma que estas que estas sosteniendo.\",\n\tcmdCharSetModel = \"Establece el modelo del personaje de una persona.\",\n\tcmdCharSetSkin = \"Establece la 'skin' del modelo de un personaje.\",\n\tcmdCharSetBodygroup = \"Establece el 'bodygroup' especificado del modelo de un personaje.\",\n\tcmdCharSetAttribute = \"Establece el nivel del atributo especificado para alguien.\",\n\tcmdCharAddAttribute = \"Añade un nivel al atributo especificado para alguien.\",\n\tcmdCharSetName = \"Cambia el nombre de un personaje al nombre especificado.\",\n\tcmdCharGiveItem = \"Da el objeto especificado a alguien.\",\n\tcmdCharKick = \"Saca al jugador de su personaje, devolviéndolo a la pantalla de selección.\",\n\tcmdCharBan = \"Bloquea a un jugador el uso del personaje especificado.\",\n\tcmdCharUnban = \"Desbloquea un personaje para que el jugador pueda usarlo otra vez.\",\n\tcmdGiveMoney = \"Da una cantidad específica de dinero a la persona a la que estas mirando.\",\n\tcmdCharSetMoney = \"Cambia la cantidad total de dinero de alguien a la cantidad especificada.\",\n\tcmdDropMoney = \"Tira una cantidad específica de dinero en una pequeña caja en frente de ti.\",\n\tcmdPlyWhitelist = \"Permite el acceso de alguien a la facción especificada (lista blanca).\",\n\tcmdCharGetUp = \"Intenta levantarte después de haber caído.\",\n\tcmdPlyUnwhitelist = \"Elimina el acceso de alguien a la facción especificada.\",\n\tcmdCharFallOver = \"Hace que tus rodillas se debiliten y te caigas.\",\n\tcmdBecomeClass = \"Intenta formar parte de la clase especificada en tu facción actual.\",\n\tcmdCharDesc = \"Establece tu descripción física.\",\n\tcmdCharDescTitle = \"Descripción Física\",\n\tcmdCharDescDescription = \"Introduce la descripción física de tu personaje.\",\n\tcmdPlyTransfer = \"Transfiere a alguien a la facción especificada.\",\n\tcmdCharSetClass = \"Forzar a alguien formar parte de la clase especificada de su facción.\",\n\tcmdMapRestart = \"Reinicia el mapa después de la cantidad de tiempo especificada.\",\n\tcmdPanelAdd = \"Pone un panel web en el mundo.\",\n\tcmdPanelRemove = \"Elimina el panel web al que estas mirando.\",\n\tcmdTextAdd = \"Pone un bloque de texto en el mundo.\",\n\tcmdTextRemove = \"Elimina bloques de texto de donde estas mirando.\",\n\tcmdMapSceneAdd = \"Añade un punto de cámara cinemática la cual es mostrada en el menú de selección de personaje.\",\n\tcmdMapSceneRemove = \"Elimina un punto de cámara que es mostrado en el menú de selección de personaje.\",\n\tcmdSpawnAdd = \"Añade un punto de aparición para la facción especificada.\",\n\tcmdSpawnRemove = \"Elimina cualquier punto de aparición a los cuales estés mirando.\",\n\tcmdAct = \"Realiza la animación %s.\",\n\tcmdContainerSetPassword = \"Establece la contraseña para el contenedor que estas mirando.\",\n\tcmdDoorSell = \"Vende la puerta a la que estas mirando.\",\n\tcmdDoorBuy = \"Compra la puerta a la que estas mirando.\",\n\tcmdDoorSetUnownable = \"Hace que la puerta a la que estas mirando no pueda tener dueño.\",\n\tcmdDoorSetOwnable = \"Hace que la puerta a la que estas mirando pueda tener dueño.\",\n\tcmdDoorSetFaction = \"Hace que la puerta a la que estas mirando pertenezca a una facción.\",\n\tcmdDoorSetDisabled = \"No permite que ningún comando sea ejecutado en la puerta a la cual estas mirando.\",\n\tcmdDoorSetTitle = \"Establece el titulo de la puerta a la cual estas mirando.\",\n\tcmdDoorSetParent = \"Selecciona la puerta como madre de un grupo de puertas.\",\n\tcmdDoorSetChild = \"Establece la puerta como hija de un grupo de puertas.\",\n\tcmdDoorRemoveChild = \"Elimina la puerta como hija del grupo de puertas seleccionado.\",\n\tcmdDoorSetHidden = \"Oculta la descripción de la puerta a la cual estas mirando, pero sigue permitiendo que pueda tener dueño.\",\n\tcmdDoorSetClass = \"Hace que la puerta a la que estas mirando pertenezca la clase especificada de una facción.\",\n\tcmdMe = \"Describe una acción de tu personaje en tercera persona.\",\n\tcmdIt = \"Describe un evento a tu alrededor.\",\n\tcmdW = \"Susurra algo a las personas a tu alrededor.\",\n\tcmdY = \"Grita algo a las personas a tu alrededor.\",\n\tcmdEvent = \"Describe un evento que todos pueden ver en el servidor.\",\n\tcmdOOC = \"Envía un mensaje en el chat (global) fuera de personaje.\",\n\tcmdLOOC = \"Envía un mensaje en el chat (local) fuera de personaje.\",\n\n\ticonEditorAlignBest = \"Alineación óptima\",\n\ticonEditorWidth = \"Ancho\",\n\ticonEditorHeight = \"Altura\",\n\ticonEditorCopy = \"Copiar al portapapeles\",\n\ticonEditorCopied = \"Datos del modelo del objeto copiados al portapapeles.\",\n\ticonEditorAlignFront = \"Alinear desde el frente\",\n\ticonEditorAlignAbove = \"Alinear desde arriba\",\n\ticonEditorAlignRight = \"Alinear desde la derecha\",\n\ticonEditorAlignCenter = \"Alinear desde el centro\"\n}\n"
  },
  {
    "path": "gamemode/shared.lua",
    "content": "\r\n--- Top-level library containing all Helix libraries. A large majority of the framework is split into respective libraries that\r\n-- reside within `ix`.\r\n-- @module ix\r\n\r\n--- A table of variable types that are used throughout the framework. It represents types as a table with the keys being the\r\n-- name of the type, and the values being some number value. **You should never directly use these number values!** Using the\r\n-- values from this table will ensure backwards compatibility if the values in this table change.\r\n--\r\n-- This table also contains the numerical values of the types as keys. This means that if you need to check if a type exists, or\r\n-- if you need to get the name of a type, you can do a table lookup with a numerical value. Note that special types are not\r\n-- included since they are not real types that can be compared with.\r\n-- @table ix.type\r\n-- @realm shared\r\n-- @field string A regular string. In the case of `ix.command.Add`, this represents one word.\r\n-- @field text A regular string. In the case of `ix.command.Add`, this represents all words concatenated into a string.\r\n-- @field number Any number.\r\n-- @field player Any player that matches the given query string in `ix.util.FindPlayer`.\r\n-- @field steamid A string that matches the Steam ID format of `STEAM_X:X:XXXXXXXX`.\r\n-- @field character Any player's character that matches the given query string in `ix.util.FindPlayer`.\r\n-- @field bool A string representation of a bool - `false` and `0` will return `false`, anything else will return `true`.\r\n-- @field color A color represented by its red/green/blue/alpha values.\r\n-- @field vector A 3D vector represented by its x/y/z values.\r\n-- @field optional This is a special type that can be bitwise OR'd with any other type to make it optional. Currently only\r\n-- supported in `ix.command.Add`.\r\n-- @field array This is a special type that can be bitwise OR'd with any other type to make it an array of that type. Currently\r\n-- only supported in `ix.option.Add`.\r\n-- @see ix.command.Add\r\n-- @see ix.option.Add\r\n-- @usage -- checking if type exists\r\n-- print(ix.type[2] != nil)\r\n-- > true\r\n--\r\n-- -- getting name of type\r\n-- print(ix.type[ix.type.string])\r\n-- > \"string\"\r\nix.type = ix.type or {}\r\n\r\n-- Define gamemode information.\r\nGM.Name = \"Helix\"\r\nGM.Author = \"nebulous.cloud\"\r\nGM.Website = \"https://nebulous.cloud\"\r\nGM.Version = \"β\"\r\n\r\ndo\r\n\t-- luacheck: globals player_manager\r\n\tplayer_manager.ixTranslateModel = player_manager.ixTranslateModel or player_manager.TranslateToPlayerModelName\r\n\r\n\tfunction player_manager.TranslateToPlayerModelName(model)\r\n\t\tmodel = model:lower():gsub(\"\\\\\", \"/\")\r\n\t\tlocal result = player_manager.ixTranslateModel(model)\r\n\r\n\t\tif (result == \"kleiner\" and !model:find(\"kleiner\")) then\r\n\t\t\tlocal model2 = model:gsub(\"models/\", \"models/player/\")\r\n\t\t\tresult = player_manager.ixTranslateModel(model2)\r\n\r\n\t\t\tif (result != \"kleiner\") then\r\n\t\t\t\treturn result\r\n\t\t\tend\r\n\r\n\t\t\tmodel2 = model:gsub(\"models/humans\", \"models/player\")\r\n\t\t\tresult = player_manager.ixTranslateModel(model2)\r\n\r\n\t\t\tif (result != \"kleiner\") then\r\n\t\t\t\treturn result\r\n\t\t\tend\r\n\r\n\t\t\tmodel2 = model:gsub(\"models/zombie/\", \"models/player/zombie_\")\r\n\t\t\tresult = player_manager.ixTranslateModel(model2)\r\n\r\n\t\t\tif (result != \"kleiner\") then\r\n\t\t\t\treturn result\r\n\t\t\tend\r\n\t\tend\r\n\r\n\t\treturn result\r\n\tend\r\nend\r\n\r\n-- Include core framework files.\r\nix.util.Include(\"core/cl_skin.lua\")\r\nix.util.IncludeDir(\"core/libs/thirdparty\")\r\nix.util.Include(\"core/sh_config.lua\")\r\nix.util.IncludeDir(\"core/libs\")\r\nix.util.IncludeDir(\"core/derma\")\r\nix.util.IncludeDir(\"core/hooks\")\r\n\r\n-- Include language and default base items.\r\nix.lang.LoadFromDir(\"helix/gamemode/languages\")\r\nix.item.LoadFromDir(\"helix/gamemode/items\")\r\n\r\n-- Called after the gamemode has loaded.\r\nfunction GM:Initialize()\r\n\t-- Load all of the Helix plugins.\r\n\tix.plugin.Initialize()\r\n\t-- Restore client options\r\n\tix.option.Load()\r\n\t-- Restore the configurations from earlier if applicable.\r\n\tix.config.Load()\r\nend\r\n\r\n-- luacheck: globals IX_RELOADED\r\nIX_RELOADED = false\r\n\r\n-- Called when a file has been modified.\r\nfunction GM:OnReloaded()\r\n\t-- Reload the default fonts.\r\n\tif (CLIENT) then\r\n\t\thook.Run(\"LoadFonts\", ix.config.Get(\"font\"), ix.config.Get(\"genericFont\"))\r\n\r\n\t\t-- Reload the scoreboard.\r\n\t\tif (IsValid(ix.gui.scoreboard)) then\r\n\t\t\tix.gui.scoreboard:Remove()\r\n\t\tend\r\n\telse\r\n\t\t-- Auto-reload support for faction pay timers.\r\n\t\tfor index, faction in ipairs(ix.faction.indices) do\r\n\t\t\tfor _, v in ipairs(team.GetPlayers(index)) do\r\n\t\t\t\tif (faction.pay and faction.pay > 0) then\r\n\t\t\t\t\ttimer.Adjust(\"ixSalary\"..v:SteamID64(), faction.payTime or 300, 0)\r\n\t\t\t\telse\r\n\t\t\t\t\ttimer.Remove(\"ixSalary\"..v:SteamID64())\r\n\t\t\t\tend\r\n\t\t\tend\r\n\t\tend\r\n\tend\r\n\r\n\tif (!IX_RELOADED) then\r\n\t\tIX_RELOADED = true\r\n\r\n\t\t-- Load all of the Helix plugins.\r\n\t\tix.plugin.Initialize()\r\n\t\t-- Restore the configurations from earlier if applicable.\r\n\t\tix.config.Load()\r\n\t\t-- Restore client options\r\n\t\tix.option.Load()\r\n\tend\r\nend\r\n\r\n-- Include default Helix chat commands.\r\nix.util.Include(\"core/sh_commands.lua\")\r\n\r\nif (SERVER and game.IsDedicated()) then\r\n\tconcommand.Remove(\"gm_save\")\r\n\r\n\tconcommand.Add(\"gm_save\", function(client, command, arguments) end)\r\n\tconcommand.Add(\"gmod_admin_cleanup\", function(client, command, arguments) end)\r\nend\r\n\r\n-- add entries for c_viewmodels that aren't set by default\r\nplayer_manager.AddValidModel(\"group02male01\", \"models/humans/group02/male_01.mdl\")\r\nplayer_manager.AddValidHands(\"group02male01\", \"models/weapons/c_arms_citizen.mdl\", 1, \"0000000\")\r\nplayer_manager.AddValidModel(\"group02male03\", \"models/humans/group02/male_03.mdl\")\r\nplayer_manager.AddValidHands(\"group02male03\", \"models/weapons/c_arms_citizen.mdl\", 1, \"0000000\")\r\nplayer_manager.AddValidModel(\"group01female07\", \"models/player/group01/female_07.mdl\")\r\nplayer_manager.AddValidHands(\"group01female07\", \"models/weapons/c_arms_citizen.mdl\", 1, \"0000000\")\r\nplayer_manager.AddValidModel(\"group02female03\", \"models/player/group01/female_03.mdl\")\r\nplayer_manager.AddValidHands(\"group02female03\", \"models/weapons/c_arms_citizen.mdl\", 1, \"0000000\")\r\n"
  },
  {
    "path": "helix.example.yml",
    "content": "\ndatabase:\n  adapter: \"sqlite\"\n  hostname: \"example.com\"\n  username: \"example\"\n  password: \"example\"\n  database: \"helix\"\n  port: 3306\n"
  },
  {
    "path": "helix.txt",
    "content": "\"helix\"\n{\n\t\"base\"\t\t\"sandbox\"\n\t\"title\"\t\t\"Helix\"\n\t\"author\"\t\"nebulous.cloud\"\n}\n"
  },
  {
    "path": "plugins/3dpanel.lua",
    "content": "\nlocal PLUGIN = PLUGIN\n\nPLUGIN.name = \"3D Panels\"\nPLUGIN.author = \"Chessnut\"\nPLUGIN.description = \"Adds web panels that can be placed on the map.\"\n\n-- List of available panel dislays.\nPLUGIN.list = PLUGIN.list or {}\n\nif (SERVER) then\n\tutil.AddNetworkString(\"ixPanelList\")\n\tutil.AddNetworkString(\"ixPanelAdd\")\n\tutil.AddNetworkString(\"ixPanelRemove\")\n\n\t-- Called when the player is sending client info.\n\tfunction PLUGIN:PlayerInitialSpawn(client)\n\t\t-- Send the list of panel displays.\n\t\ttimer.Simple(1, function()\n\t\t\tif (IsValid(client)) then\n\t\t\t\tlocal json = util.TableToJSON(self.list)\n\t\t\t\tlocal compressed = util.Compress(json)\n\t\t\t\tlocal length = compressed:len()\n\n\t\t\t\tnet.Start(\"ixPanelList\")\n\t\t\t\t\tnet.WriteUInt(length, 32)\n\t\t\t\t\tnet.WriteData(compressed, length)\n\t\t\t\tnet.Send(client)\n\t\t\tend\n\t\tend)\n\tend\n\n\t-- Adds a panel to the list, sends it to the players, and saves data.\n\tfunction PLUGIN:AddPanel(position, angles, url, scale, brightness)\n\t\tscale = math.Clamp((scale or 1) * 0.1, 0.001, 5)\n\t\tbrightness = math.Clamp(math.Round((brightness or 100) * 2.55), 1, 255)\n\n\t\t-- Find an ID for this panel within the list.\n\t\tlocal index = #self.list + 1\n\n\t\t-- Add the panel to the list so it can be sent and saved.\n\t\tself.list[index] = {position, angles, nil, nil, scale, url, nil, brightness}\n\n\t\t-- Send the panel information to the players.\n\t\tnet.Start(\"ixPanelAdd\")\n\t\t\tnet.WriteUInt(index, 32)\n\t\t\tnet.WriteVector(position)\n\t\t\tnet.WriteAngle(angles)\n\t\t\tnet.WriteFloat(scale)\n\t\t\tnet.WriteString(url)\n\t\t\tnet.WriteUInt(brightness, 8)\n\t\tnet.Broadcast()\n\n\t\t-- Save the plugin data.\n\t\tself:SavePanels()\n\tend\n\n\t-- Removes a panel that are within the radius of a position.\n\tfunction PLUGIN:RemovePanel(position, radius)\n\t\t-- Default the radius to 100.\n\t\tradius = radius or 100\n\n\t\tlocal panelsDeleted = {}\n\n\t\t-- Loop through all of the panels.\n\t\tfor k, v in pairs(self.list) do\n\t\t\tif (k == 0) then\n\t\t\t\tcontinue\n\t\t\tend\n\n\t\t\t-- Check if the distance from our specified position to the panel is less than the radius.\n\t\t\tif (v[1]:Distance(position) <= radius) then\n\t\t\t\tpanelsDeleted[#panelsDeleted + 1] = k\n\t\t\tend\n\t\tend\n\n\t\t-- Save the plugin data if we actually changed anything.\n\t\tif (#panelsDeleted > 0) then\n\t\t\t-- Invert index table to delete from highest -> lowest\n\t\t\tpanelsDeleted = table.Reverse(panelsDeleted)\n\n\t\t\tfor _, v in ipairs(panelsDeleted) do\n\t\t\t\t-- Remove the panel from the list of panels.\n\t\t\t\ttable.remove(self.list, v)\n\n\t\t\t\t-- Tell the players to stop showing the panel.\n\t\t\t\tnet.Start(\"ixPanelRemove\")\n\t\t\t\t\tnet.WriteUInt(v, 32)\n\t\t\t\tnet.Broadcast()\n\t\t\tend\n\n\t\t\tself:SavePanels()\n\t\tend\n\n\t\t-- Return the number of deleted panels.\n\t\treturn #panelsDeleted\n\tend\n\n\t-- Called after entities have been loaded on the map.\n\tfunction PLUGIN:LoadData()\n\t\tself.list = self:GetData() or {}\n\n\t\t-- Formats table to sequential to support legacy panels.\n\t\tself.list = table.ClearKeys(self.list)\n\tend\n\n\t-- Called when the plugin needs to save information.\n\tfunction PLUGIN:SavePanels()\n\t\tself:SetData(self.list)\n\tend\nelse\n\t-- Pre-define the zero index in client before the net receives\n\tPLUGIN.list[0] = PLUGIN.list[0] or 0\n\n\t-- Holds the current cached material and filename.\n\tlocal cachedPreview = {}\n\n\tlocal function CacheMaterial(index)\n\t\tif (index < 1) then\n\t\t\treturn\n\t\tend\n\n\t\tlocal info = PLUGIN.list[index]\n\t\tlocal exploded = string.Explode(\"/\", info[6])\n\t\tlocal filename = exploded[#exploded]\n\t\tlocal path = \"helix/\"..Schema.folder..\"/\"..PLUGIN.uniqueID..\"/\"\n\n\t\tif (file.Exists(path..filename, \"DATA\")) then\n\t\t\tlocal material = Material(\"../data/\"..path..filename, \"noclamp smooth\")\n\n\t\t\tif (!material:IsError()) then\n\t\t\t\tinfo[7] = material\n\n\t\t\t\t-- Set width and height\n\t\t\t\tinfo[3] = material:GetInt(\"$realwidth\")\n\t\t\t\tinfo[4] = material:GetInt(\"$realheight\")\n\t\t\tend\n\t\telse\n\t\t\tfile.CreateDir(path)\n\n\t\t\thttp.Fetch(info[6], function(body)\n\t\t\t\tfile.Write(path..filename, body)\n\n\t\t\t\tlocal material = Material(\"../data/\"..path..filename, \"noclamp smooth\")\n\n\t\t\t\tif (!material:IsError()) then\n\t\t\t\t\tinfo[7] = material\n\n\t\t\t\t\t-- Set width and height\n\t\t\t\t\tinfo[3] = material:GetInt(\"$realwidth\")\n\t\t\t\t\tinfo[4] = material:GetInt(\"$realheight\")\n\t\t\t\tend\n\t\t\tend)\n\t\tend\n\tend\n\n\tlocal function UpdateCachedPreview(url)\n\t\tlocal path = \"helix/\"..Schema.folder..\"/\"..PLUGIN.uniqueID..\"/\"\n\n\t\t-- Gets the file name\n\t\tlocal exploded = string.Explode(\"/\", url)\n\t\tlocal filename = exploded[#exploded]\n\n\t\tif (file.Exists(path..filename, \"DATA\")) then\n\t\t\tlocal preview = Material(\"../data/\"..path..filename, \"noclamp smooth\")\n\n\t\t\t-- Update the cached preview if success\n\t\t\tif (!preview:IsError()) then\n\t\t\t\tcachedPreview = {url, preview}\n\t\t\telse\n\t\t\t\tcachedPreview = {}\n\t\t\tend\n\t\telse\n\t\t\tfile.CreateDir(path)\n\n\t\t\thttp.Fetch(url, function(body)\n\t\t\t\tfile.Write(path..filename, body)\n\n\t\t\t\tlocal preview = Material(\"../data/\"..path..filename, \"noclamp smooth\")\n\n\t\t\t\t-- Update the cached preview if success\n\t\t\t\tif (!preview:IsError()) then\n\t\t\t\t\tcachedPreview = {url, preview}\n\t\t\t\telse\n\t\t\t\t\tcachedPreview = {}\n\t\t\t\tend\n\t\t\tend)\n\t\tend\n\tend\n\n\t-- Receives new panel objects that need to be drawn.\n\tnet.Receive(\"ixPanelAdd\", function()\n\t\tlocal index = net.ReadUInt(32)\n\t\tlocal position = net.ReadVector()\n\t\tlocal angles = net.ReadAngle()\n\t\tlocal scale = net.ReadFloat()\n\t\tlocal url = net.ReadString()\n\t\tlocal brightness = net.ReadUInt(8)\n\n\t\tif (url != \"\") then\n\t\t\tPLUGIN.list[index] = {position, angles, nil, nil, scale, url, nil, brightness}\n\n\t\t\tCacheMaterial(index)\n\n\t\t\tPLUGIN.list[0] = #PLUGIN.list\n\t\tend\n\tend)\n\n\tnet.Receive(\"ixPanelRemove\", function()\n\t\tlocal index = net.ReadUInt(32)\n\n\t\ttable.remove(PLUGIN.list, index)\n\n\t\tPLUGIN.list[0] = #PLUGIN.list\n\tend)\n\n\t-- Receives a full update on ALL panels.\n\tnet.Receive(\"ixPanelList\", function()\n\t\tlocal length = net.ReadUInt(32)\n\t\tlocal data = net.ReadData(length)\n\t\tlocal uncompressed = util.Decompress(data)\n\n\t\tif (!uncompressed) then\n\t\t\tErrorNoHalt(\"[Helix] Unable to decompress panel data!\\n\")\n\t\t\treturn\n\t\tend\n\n\t\t-- Set the list of panels to the ones provided by the server.\n\t\tPLUGIN.list = util.JSONToTable(uncompressed)\n\n\t\t-- Will be saved, but refresh just to make sure.\n\t\tPLUGIN.list[0] = #PLUGIN.list\n\n\t\tlocal CacheQueue  = {}\n\n\t\t-- Loop through the list of panels.\n\t\tfor k, _ in pairs(PLUGIN.list) do\n\t\t\tif (k == 0) then\n\t\t\t\tcontinue\n\t\t\tend\n\n\t\t\tCacheQueue[#CacheQueue + 1] = k\n\t\tend\n\n\t\tif (#CacheQueue == 0) then\n\t\t\treturn\n\t\tend\n\n\t\ttimer.Create(\"ixCache3DPanels\", 1, #CacheQueue, function()\n\t\t\tif (#CacheQueue > 0) then\n\t\t\t\tCacheMaterial(CacheQueue[1])\n\n\t\t\t\ttable.remove(CacheQueue, 1)\n\t\t\telse\n\t\t\t\ttimer.Remove(\"ixCache3DPanels\")\n\t\t\tend\n\t\tend)\n\tend)\n\n\t-- Called after all translucent objects are drawn.\n\tfunction PLUGIN:PostDrawTranslucentRenderables(bDrawingDepth, bDrawingSkybox)\n\t\tif (bDrawingDepth or bDrawingSkybox) then\n\t\t\treturn\n\t\tend\n\n\t\t-- Panel preview\n\t\tif (ix.chat.currentCommand == \"paneladd\") then\n\t\t\tself:PreviewPanel()\n\t\tend\n\n\t\t-- Store the position of the player to be more optimized.\n\t\tlocal ourPosition = LocalPlayer():GetPos()\n\n\t\tlocal panel = self.list\n\n\t\tfor i = 1, panel[0] do\n\t\t\tlocal position = panel[i][1]\n\t\t\tlocal image = panel[i][7]\n\n\t\t\t-- Older panels do not have a brightness index\n\t\t\tlocal brightness = panel[i][8] or 255\n\n\t\t\tif (panel[i][7] and ourPosition:DistToSqr(position) <= 4194304) then\n\t\t\t\tcam.Start3D2D(position, panel[i][2], panel[i][5] or 0.1)\n\t\t\t\t\trender.PushFilterMin(TEXFILTER.ANISOTROPIC)\n\t\t\t\t\trender.PushFilterMag(TEXFILTER.ANISOTROPIC)\n\t\t\t\t\t\tsurface.SetDrawColor(brightness, brightness, brightness)\n\t\t\t\t\t\tsurface.SetMaterial(image)\n\t\t\t\t\t\tsurface.DrawTexturedRect(0, 0, panel[i][3] or image:Width(), panel[i][4] or image:Height())\n\t\t\t\t\trender.PopFilterMag()\n\t\t\t\t\trender.PopFilterMin()\n\t\t\t\tcam.End3D2D()\n\t\t\tend\n\t\tend\n\tend\n\n\tfunction PLUGIN:ChatTextChanged(text)\n\t\tif (ix.chat.currentCommand == \"paneladd\") then\n\t\t\t-- Allow time for ix.chat.currentArguments to update\n\t\t\ttimer.Simple(0, function()\n\t\t\t\tlocal arguments = ix.chat.currentArguments\n\n\t\t\t\tif (!arguments[1]) then\n\t\t\t\t\treturn\n\t\t\t\tend\n\n\t\t\t\tUpdateCachedPreview(arguments[1])\n\t\t\tend)\n\t\tend\n\tend\n\n\tfunction PLUGIN:PreviewPanel()\n\t\tlocal arguments = ix.chat.currentArguments\n\n\t\t-- if there's no URL, then no preview.\n\t\tif (!arguments[1]) then\n\t\t\treturn\n\t\tend\n\n\t\t-- If the material is valid, preview the panel\n\t\tif (cachedPreview[2] and !cachedPreview[2]:IsError()) then\n\t\t\tlocal trace = LocalPlayer():GetEyeTrace()\n\t\t\tlocal angles = trace.HitNormal:Angle()\n\t\t\tangles:RotateAroundAxis(angles:Up(), 90)\n\t\t\tangles:RotateAroundAxis(angles:Forward(), 90)\n\t\t\tlocal position = (trace.HitPos + angles:Up() * 0.1)\n\t\t\tlocal ourPosition = LocalPlayer():GetPos()\n\n\t\t\t-- validate argument types\n\t\t\tlocal scale = math.Clamp((tonumber(arguments[2]) or 1) * 0.1, 0.001, 5)\n\t\t\tlocal brightness = math.Clamp(math.Round((tonumber(arguments[3]) or 100) * 2.55), 1, 255)\n\n\t\t\t-- Attempt to collect the dimensions from the Material\n\t\t\tlocal width, height = cachedPreview[2]:GetInt(\"$realwidth\"), cachedPreview[2]:GetInt(\"$realheight\")\n\n\t\t\tif (ourPosition:DistToSqr(position) <= 4194304) then\n\t\t\t\tcam.Start3D2D(position, angles, scale or 0.1)\n\t\t\t\t\trender.PushFilterMin(TEXFILTER.ANISOTROPIC)\n\t\t\t\t\trender.PushFilterMag(TEXFILTER.ANISOTROPIC)\n\t\t\t\t\t\tsurface.SetDrawColor(brightness, brightness, brightness)\n\t\t\t\t\t\tsurface.SetMaterial(cachedPreview[2])\n\t\t\t\t\t\tsurface.DrawTexturedRect(0, 0, width or cachedPreview[2]:Width(), height or cachedPreview[2]:Height())\n\t\t\t\t\trender.PopFilterMag()\n\t\t\t\t\trender.PopFilterMin()\n\t\t\t\tcam.End3D2D()\n\t\t\tend\n\t\tend\n\tend\nend\n\nix.command.Add(\"PanelAdd\", {\n\tdescription = \"@cmdPanelAdd\",\n\tprivilege = \"Manage Panels\",\n\tadminOnly = true,\n\targuments = {\n\t\tix.type.string,\n\t\tbit.bor(ix.type.number, ix.type.optional),\n\t\tbit.bor(ix.type.number, ix.type.optional)\n\t},\n\tOnRun = function(self, client, url, scale, brightness)\n\t\t-- Get the position and angles of the panel.\n\t\tlocal trace = client:GetEyeTrace()\n\t\tlocal position = trace.HitPos\n\t\tlocal angles = trace.HitNormal:Angle()\n\t\tangles:RotateAroundAxis(angles:Up(), 90)\n\t\tangles:RotateAroundAxis(angles:Forward(), 90)\n\n\t\t-- Add the panel.\n\t\tPLUGIN:AddPanel(position + angles:Up() * 0.1, angles, url, scale, brightness)\n\t\treturn \"@panelAdded\"\n\tend\n})\n\nix.command.Add(\"PanelRemove\", {\n\tdescription = \"@cmdPanelRemove\",\n\tprivilege = \"Manage Panels\",\n\tadminOnly = true,\n\targuments = bit.bor(ix.type.number, ix.type.optional),\n\tOnRun = function(self, client, radius)\n\t\t-- Get the origin to remove panel.\n\t\tlocal trace = client:GetEyeTrace()\n\t\tlocal position = trace.HitPos\n\t\t-- Remove the panel(s) and get the amount removed.\n\t\tlocal amount = PLUGIN:RemovePanel(position, radius)\n\n\t\treturn \"@panelRemoved\", amount\n\tend\n})\n"
  },
  {
    "path": "plugins/3dtext.lua",
    "content": "\nlocal PLUGIN = PLUGIN\n\nPLUGIN.name = \"3D Text\"\nPLUGIN.author = \"Chessnut\"\nPLUGIN.description = \"Adds text that can be placed on the map.\"\n\n-- List of available text panels\nPLUGIN.list = PLUGIN.list or {}\n\nif (SERVER) then\n\tutil.AddNetworkString(\"ixTextList\")\n\tutil.AddNetworkString(\"ixTextAdd\")\n\tutil.AddNetworkString(\"ixTextRemove\")\n\n\tix.log.AddType(\"undo3dText\", function(client)\n\t\treturn string.format(\"%s has removed their last 3D text.\", client:GetName())\n\tend)\n\n\t-- Called when the player is sending client info.\n\tfunction PLUGIN:PlayerInitialSpawn(client)\n\t\ttimer.Simple(1, function()\n\t\t\tif (IsValid(client)) then\n\t\t\t\tlocal json = util.TableToJSON(self.list)\n\t\t\t\tlocal compressed = util.Compress(json)\n\t\t\t\tlocal length = compressed:len()\n\n\t\t\t\tnet.Start(\"ixTextList\")\n\t\t\t\t\tnet.WriteUInt(length, 32)\n\t\t\t\t\tnet.WriteData(compressed, length)\n\t\t\t\tnet.Send(client)\n\t\t\tend\n\t\tend)\n\tend\n\n\t-- Adds a text to the list, sends it to the players, and saves data.\n\tfunction PLUGIN:AddText(position, angles, text, scale)\n\t\tlocal index = #self.list + 1\n\t\tscale = math.Clamp((scale or 1) * 0.1, 0.001, 5)\n\n\t\tself.list[index] = {position, angles, text, scale}\n\n\t\tnet.Start(\"ixTextAdd\")\n\t\t\tnet.WriteUInt(index, 32)\n\t\t\tnet.WriteVector(position)\n\t\t\tnet.WriteAngle(angles)\n\t\t\tnet.WriteString(text)\n\t\t\tnet.WriteFloat(scale)\n\t\tnet.Broadcast()\n\n\t\tself:SaveText()\n\t\treturn index\n\tend\n\n\t-- Removes a text that are within the radius of a position.\n\tfunction PLUGIN:RemoveText(position, radius)\n\t\tradius = radius or 100\n\n\t\tlocal textDeleted = {}\n\n\t\tfor k, v in pairs(self.list) do\n\t\t\tif (k == 0) then\n\t\t\t\tcontinue\n\t\t\tend\n\n\t\t\tif (v[1]:Distance(position) <= radius) then\n\t\t\t\ttextDeleted[#textDeleted + 1] = k\n\t\t\tend\n\t\tend\n\n\t\tif (#textDeleted > 0) then\n\t\t\t-- Invert index table to delete from highest -> lowest\n\t\t\ttextDeleted = table.Reverse(textDeleted)\n\n\t\t\tfor _, v in ipairs(textDeleted) do\n\t\t\t\ttable.remove(self.list, v)\n\n\t\t\t\tnet.Start(\"ixTextRemove\")\n\t\t\t\t\tnet.WriteUInt(v, 32)\n\t\t\t\tnet.Broadcast()\n\t\t\tend\n\n\t\t\tself:SaveText()\n\t\tend\n\n\t\treturn #textDeleted\n\tend\n\n\tfunction PLUGIN:RemoveTextByID(id)\n\t\tlocal info = self.list[id]\n\n\t\tif (!info) then\n\t\t\treturn false\n\t\tend\n\n\t\tnet.Start(\"ixTextRemove\")\n\t\t\tnet.WriteUInt(id, 32)\n\t\tnet.Broadcast()\n\n\t\ttable.remove(self.list, id)\n\t\treturn true\n\tend\n\n\t-- Called after entities have been loaded on the map.\n\tfunction PLUGIN:LoadData()\n\t\tself.list = self:GetData() or {}\n\n\t\t-- Formats table to sequential to support legacy panels.\n\t\tself.list = table.ClearKeys(self.list)\n\tend\n\n\t-- Called when the plugin needs to save information.\n\tfunction PLUGIN:SaveText()\n\t\tself:SetData(self.list)\n\tend\nelse\n\t-- Pre-define the zero index in client before the net receives\n\tPLUGIN.list[0] = PLUGIN.list[0] or 0\n\n\tlanguage.Add(\"Undone_ix3dText\", \"Removed 3D Text\")\n\n\tfunction PLUGIN:GenerateMarkup(text)\n\t\tlocal object = ix.markup.Parse(\"<font=ix3D2DFont>\"..text:gsub(\"\\\\n\", \"\\n\"))\n\n\t\tobject.onDrawText = function(surfaceText, font, x, y, color, alignX, alignY, alpha)\n\t\t\t-- shadow\n\t\t\tsurface.SetTextPos(x + 1, y + 1)\n\t\t\tsurface.SetTextColor(0, 0, 0, alpha)\n\t\t\tsurface.SetFont(font)\n\t\t\tsurface.DrawText(surfaceText)\n\n\t\t\tsurface.SetTextPos(x, y)\n\t\t\tsurface.SetTextColor(color.r or 255, color.g or 255, color.b or 255, alpha)\n\t\t\tsurface.SetFont(font)\n\t\t\tsurface.DrawText(surfaceText)\n\t\tend\n\n\t\treturn object\n\tend\n\n\t-- Receives new text objects that need to be drawn.\n\tnet.Receive(\"ixTextAdd\", function()\n\t\tlocal index = net.ReadUInt(32)\n\t\tlocal position = net.ReadVector()\n\t\tlocal angles = net.ReadAngle()\n\t\tlocal text = net.ReadString()\n\t\tlocal scale = net.ReadFloat()\n\n\t\tif (text != \"\") then\n\t\t\tPLUGIN.list[index] = {\n\t\t\t\tposition,\n\t\t\t\tangles,\n\t\t\t\tPLUGIN:GenerateMarkup(text),\n\t\t\t\tscale\n\t\t\t}\n\n\t\t\tPLUGIN.list[0] = #PLUGIN.list\n\t\tend\n\tend)\n\n\tnet.Receive(\"ixTextRemove\", function()\n\t\tlocal index = net.ReadUInt(32)\n\n\t\ttable.remove(PLUGIN.list, index)\n\n\t\tPLUGIN.list[0] = #PLUGIN.list\n\tend)\n\n\t-- Receives a full update on ALL texts.\n\tnet.Receive(\"ixTextList\", function()\n\t\tlocal length = net.ReadUInt(32)\n\t\tlocal data = net.ReadData(length)\n\t\tlocal uncompressed = util.Decompress(data)\n\n\t\tif (!uncompressed) then\n\t\t\tErrorNoHalt(\"[Helix] Unable to decompress text data!\\n\")\n\t\t\treturn\n\t\tend\n\n\t\tPLUGIN.list = util.JSONToTable(uncompressed)\n\n\t\t-- Will be saved, but refresh just to make sure.\n\t\tPLUGIN.list[0] = #PLUGIN.list\n\n\t\tfor k, v in pairs(PLUGIN.list) do\n\t\t\tif (k == 0) then\n\t\t\t\tcontinue\n\t\t\tend\n\n\t\t\tlocal object = ix.markup.Parse(\"<font=ix3D2DFont>\"..v[3]:gsub(\"\\\\n\", \"\\n\"))\n\n\t\t\tobject.onDrawText = function(text, font, x, y, color, alignX, alignY, alpha)\n\t\t\t\tdraw.TextShadow({\n\t\t\t\t\tpos = {x, y},\n\t\t\t\t\tcolor = ColorAlpha(color, alpha),\n\t\t\t\t\ttext = text,\n\t\t\t\t\txalign = 0,\n\t\t\t\t\tyalign = alignY,\n\t\t\t\t\tfont = font\n\t\t\t\t}, 1, alpha)\n\t\t\tend\n\n\t\t\tv[3] = object\n\t\tend\n\tend)\n\n\tfunction PLUGIN:StartChat()\n\t\tself.preview = nil\n\tend\n\n\tfunction PLUGIN:FinishChat()\n\t\tself.preview = nil\n\tend\n\n\tfunction PLUGIN:HUDPaint()\n\t\tif (ix.chat.currentCommand != \"textremove\") then\n\t\t\treturn\n\t\tend\n\n\t\tlocal radius = tonumber(ix.chat.currentArguments[1]) or 100\n\n\t\tsurface.SetDrawColor(200, 30, 30)\n\t\tsurface.SetTextColor(200, 30, 30)\n\t\tsurface.SetFont(\"ixMenuButtonFont\")\n\n\t\tlocal i = 0\n\n\t\tfor k, v in pairs(self.list) do\n\t\t\tif (k == 0) then\n\t\t\t\tcontinue\n\t\t\tend\n\n\t\t\tif (v[1]:Distance(LocalPlayer():GetEyeTraceNoCursor().HitPos) <= radius) then\n\t\t\t\tlocal screen = v[1]:ToScreen()\n\t\t\t\tsurface.DrawLine(\n\t\t\t\t\tScrW() * 0.5,\n\t\t\t\t\tScrH() * 0.5,\n\t\t\t\t\tmath.Clamp(screen.x, 0, ScrW()),\n\t\t\t\t\tmath.Clamp(screen.y, 0, ScrH())\n\t\t\t\t)\n\n\t\t\t\ti = i + 1\n\t\t\tend\n\t\tend\n\n\t\tif (i > 0) then\n\t\t\tlocal textWidth, textHeight = surface.GetTextSize(i)\n\t\t\tsurface.SetTextPos(ScrW() * 0.5 - textWidth * 0.5, ScrH() * 0.5 + textHeight + 8)\n\t\t\tsurface.DrawText(i)\n\t\tend\n\tend\n\n\tfunction PLUGIN:PostDrawTranslucentRenderables(bDrawingDepth, bDrawingSkybox)\n\t\tif (bDrawingDepth or bDrawingSkybox) then\n\t\t\treturn\n\t\tend\n\n\t\t-- preview for textadd command\n\t\tif (ix.chat.currentCommand == \"textadd\") then\n\t\t\tlocal arguments = ix.chat.currentArguments\n\t\t\tlocal text = tostring(arguments[1] or \"\")\n\t\t\tlocal scale = math.Clamp((tonumber(arguments[2]) or 1) * 0.1, 0.001, 5)\n\t\t\tlocal trace = LocalPlayer():GetEyeTraceNoCursor()\n\t\t\tlocal position = trace.HitPos\n\t\t\tlocal angles = trace.HitNormal:Angle()\n\t\t\tlocal markup\n\n\t\t\tangles:RotateAroundAxis(angles:Up(), 90)\n\t\t\tangles:RotateAroundAxis(angles:Forward(), 90)\n\n\t\t\t-- markup will error with invalid fonts\n\t\t\tpcall(function()\n\t\t\t\tmarkup = PLUGIN:GenerateMarkup(text)\n\t\t\tend)\n\n\t\t\tif (markup) then\n\t\t\t\tcam.Start3D2D(position, angles, scale)\n\t\t\t\t\tmarkup:draw(0, 0, 1, 1, 255)\n\t\t\t\tcam.End3D2D()\n\t\t\tend\n\t\tend\n\n\t\tlocal position = LocalPlayer():GetPos()\n\t\tlocal texts = self.list\n\n\t\tfor i = 1, texts[0] do\n\t\t\tlocal distance = texts[i][1]:DistToSqr(position)\n\n\t\t\tif (distance > 1048576) then\n\t\t\t\tcontinue\n\t\t\tend\n\n\t\t\tcam.Start3D2D(texts[i][1], texts[i][2], texts[i][4] or 0.1)\n\t\t\t\tlocal alpha = (1 - ((distance - 65536) / 768432)) * 255\n\t\t\t\ttexts[i][3]:draw(0, 0, 1, 1, alpha)\n\t\t\tcam.End3D2D()\n\t\tend\n\tend\nend\n\nix.command.Add(\"TextAdd\", {\n\tdescription = \"@cmdTextAdd\",\n\tadminOnly = true,\n\targuments = {\n\t\tix.type.string,\n\t\tbit.bor(ix.type.number, ix.type.optional)\n\t},\n\tOnRun = function(self, client, text, scale)\n\t\tlocal trace = client:GetEyeTrace()\n\t\tlocal position = trace.HitPos\n\t\tlocal angles = trace.HitNormal:Angle()\n\t\tangles:RotateAroundAxis(angles:Up(), 90)\n\t\tangles:RotateAroundAxis(angles:Forward(), 90)\n\n\t\tlocal index = PLUGIN:AddText(position + angles:Up() * 0.1, angles, text, scale)\n\n\t\tundo.Create(\"ix3dText\")\n\t\t\tundo.SetPlayer(client)\n\t\t\tundo.AddFunction(function()\n\t\t\t\tif (PLUGIN:RemoveTextByID(index)) then\n\t\t\t\t\tix.log.Add(client, \"undo3dText\")\n\t\t\t\tend\n\t\t\tend)\n\t\tundo.Finish()\n\n\t\treturn \"@textAdded\"\n\tend\n})\n\nix.command.Add(\"TextRemove\", {\n\tdescription = \"@cmdTextRemove\",\n\tadminOnly = true,\n\targuments = bit.bor(ix.type.number, ix.type.optional),\n\tOnRun = function(self, client, radius)\n\t\tlocal trace = client:GetEyeTrace()\n\t\tlocal position = trace.HitPos + trace.HitNormal * 2\n\t\tlocal amount = PLUGIN:RemoveText(position, radius)\n\n\t\treturn \"@textRemoved\", amount\n\tend\n})\n"
  },
  {
    "path": "plugins/act/cl_hooks.lua",
    "content": "\nlocal animationTime = 2\n\nlocal PLUGIN = PLUGIN\nPLUGIN.cameraFraction = 0\n\nlocal function GetHeadBone(client)\n\tlocal head\n\n\tfor i = 1, client:GetBoneCount() do\n\t\tlocal name = client:GetBoneName(i)\n\n\t\tif (string.find(name:lower(), \"head\")) then\n\t\t\thead = i\n\t\t\tbreak\n\t\tend\n\tend\n\n\treturn head\nend\n\nfunction PLUGIN:PlayerBindPress(client, bind, bPressed)\n\tif (!client:GetNetVar(\"actEnterAngle\")) then\n\t\treturn\n\tend\n\n\tif (bind:find(\"+jump\") and bPressed) then\n\t\tix.command.Send(\"ExitAct\")\n\t\treturn true\n\tend\nend\n\nfunction PLUGIN:ShouldDrawLocalPlayer(client)\n\tif (client:GetNetVar(\"actEnterAngle\") and self.cameraFraction > 0.25) then\n\t\treturn true\n\telseif (self.cameraFraction > 0.25) then\n\t\treturn true\n\tend\nend\n\nlocal forwardOffset = 16\nlocal backwardOffset = -32\nlocal heightOffset = Vector(0, 0, 20)\nlocal idleHeightOffset = Vector(0, 0, 6)\nlocal traceMin = Vector(-4, -4, -4)\nlocal traceMax = Vector(4, 4, 4)\n\nfunction PLUGIN:CalcView(client, origin)\n\tlocal enterAngle = client:GetNetVar(\"actEnterAngle\")\n\tlocal fraction = self.cameraFraction\n\tlocal offset = self.bIdle and forwardOffset or backwardOffset\n\tlocal height = self.bIdle and idleHeightOffset or heightOffset\n\n\tif (!enterAngle) then\n\t\tif (fraction > 0) then\n\t\t\tlocal view = {\n\t\t\t\torigin = LerpVector(fraction, origin, origin + self.forward * offset + height)\n\t\t\t}\n\n\t\t\tif (self.cameraTween) then\n\t\t\t\tself.cameraTween:update(FrameTime())\n\t\t\tend\n\n\t\t\treturn view\n\t\tend\n\n\t\treturn\n\tend\n\n\tlocal view = {}\n\tlocal forward = enterAngle:Forward()\n\tlocal head = GetHeadBone(client)\n\n\tlocal bFirstPerson = true\n\n\tif (ix.option.Get(\"thirdpersonEnabled\", false)) then\n\t\tlocal originPosition = head and client:GetBonePosition(head) or client:GetPos()\n\n\t\t-- check if the camera will hit something\n\t\tlocal data = util.TraceHull({\n\t\t\tstart = originPosition,\n\t\t\tendpos = originPosition - client:EyeAngles():Forward() * 48,\n\t\t\tmins = traceMin * 0.75,\n\t\t\tmaxs = traceMax * 0.75,\n\t\t\tfilter = client\n\t\t})\n\n\t\tbFirstPerson = data.Hit\n\n\t\tif (!bFirstPerson) then\n\t\t\tview.origin = data.HitPos\n\t\tend\n\tend\n\n\tif (bFirstPerson) then\n\t\tif (head) then\n\t\t\tlocal position = client:GetBonePosition(head) + forward * offset + height\n\t\t\tlocal data = {\n\t\t\t\tstart = (client:GetBonePosition(head) or Vector(0, 0, 64)) + forward * 8,\n\t\t\t\tendpos = position + forward * offset,\n\t\t\t\tmins = traceMin,\n\t\t\t\tmaxs = traceMax,\n\t\t\t\tfilter = client\n\t\t\t}\n\n\t\t\tdata = util.TraceHull(data)\n\n\t\t\tif (data.Hit) then\n\t\t\t\tview.origin = data.HitPos\n\t\t\telse\n\t\t\t\tview.origin = position\n\t\t\tend\n\t\telse\n\t\t\tview.origin = origin + forward * forwardOffset + height\n\t\tend\n\tend\n\n\tview.origin = LerpVector(fraction, origin, view.origin)\n\n\tif (self.cameraTween) then\n\t\tself.cameraTween:update(FrameTime())\n\tend\n\n\treturn view\nend\n\nnet.Receive(\"ixActEnter\", function()\n\tPLUGIN.bIdle = net.ReadBool()\n\tPLUGIN.forward = LocalPlayer():GetNetVar(\"actEnterAngle\"):Forward()\n\tPLUGIN.cameraTween = ix.tween.new(animationTime, PLUGIN, {\n\t\tcameraFraction = 1\n\t}, \"outQuint\")\nend)\n\nnet.Receive(\"ixActLeave\", function()\n\tPLUGIN.cameraTween = ix.tween.new(animationTime * 0.5, PLUGIN, {\n\t\tcameraFraction = 0\n\t}, \"outQuint\")\nend)\n"
  },
  {
    "path": "plugins/act/sh_definitions.lua",
    "content": "\nlocal function FacingWall(client)\n\tlocal data = {}\n\tdata.start = client:EyePos()\n\tdata.endpos = data.start + client:GetForward() * 20\n\tdata.filter = client\n\n\tif (!util.TraceLine(data).Hit) then\n\t\treturn \"@faceWall\"\n\tend\nend\n\nlocal function FacingWallBack(client)\n\tlocal data = {}\n\tdata.start = client:LocalToWorld(client:OBBCenter())\n\tdata.endpos = data.start - client:GetForward() * 20\n\tdata.filter = client\n\n\tif (!util.TraceLine(data).Hit) then\n\t\treturn \"@faceWallBack\"\n\tend\nend\n\nfunction PLUGIN:SetupActs()\n\t-- sit\n\tix.act.Register(\"Sit\", {\"citizen_male\", \"citizen_female\"}, {\n\t\tstart = {\"idle_to_sit_ground\", \"idle_to_sit_chair\"},\n\t\tsequence = {\"sit_ground\", \"sit_chair\"},\n\t\tfinish = {\n\t\t\t{\"sit_ground_to_idle\", duration = 2.1},\n\t\t\t\"\"\n\t\t},\n\t\tuntimed = true,\n\t\tidle = true\n\t})\n\n\tix.act.Register(\"SitWall\", {\"citizen_male\", \"citizen_female\"}, {\n\t\tsequence = {\n\t\t\t{\"plazaidle4\", check = FacingWallBack},\n\t\t\t{\"injured1\", check = FacingWallBack, offset = function(client)\n\t\t\t\treturn client:GetForward() * 14\n\t\t\tend}\n\t\t},\n\t\tuntimed = true,\n\t\tidle = true\n\t})\n\n\tix.act.Register(\"Sit\", \"vortigaunt\", {\n\t\tsequence = \"chess_wait\",\n\t\tuntimed = true,\n\t\tidle = true\n\t})\n\n\t-- stand\n\tix.act.Register(\"Stand\", \"citizen_male\", {\n\t\tsequence = {\"lineidle01\", \"lineidle02\", \"lineidle03\", \"lineidle04\"},\n\t\tuntimed = true,\n\t\tidle = true\n\t})\n\n\tix.act.Register(\"Stand\", \"citizen_female\", {\n\t\tsequence = {\"lineidle01\", \"lineidle02\", \"lineidle03\"},\n\t\tuntimed = true,\n\t\tidle = true\n\t})\n\n\tix.act.Register(\"Stand\", \"metrocop\", {\n\t\tsequence = \"plazathreat2\"\n\t})\n\n\t-- cheer\n\tix.act.Register(\"Cheer\", \"citizen_male\", {\n\t\tsequence = {{\"cheer1\", duration = 1.6}, \"cheer2\", \"wave_smg1\"}\n\t})\n\n\tix.act.Register(\"Cheer\", \"citizen_female\", {\n\t\tsequence = {\"cheer1\", \"wave_smg1\"}\n\t})\n\n\t-- lean\n\tix.act.Register(\"Lean\", {\"citizen_male\", \"citizen_female\"}, {\n\t\tstart = {\"idle_to_lean_back\", \"\", \"\"},\n\t\tsequence = {\n\t\t\t{\"lean_back\", check = FacingWallBack},\n\t\t\t{\"plazaidle1\", check = FacingWallBack},\n\t\t\t{\"plazaidle2\", check = FacingWallBack}\n\t\t},\n\t\tuntimed = true,\n\t\tidle = true\n\t})\n\n\tix.act.Register(\"Lean\", {\"metrocop\"}, {\n\t\tsequence = {{\"idle_baton\", check = FacingWallBack}, \"busyidle2\"},\n\t\tuntimed = true,\n\t\tidle = true\n\t})\n\n\t-- injured\n\tix.act.Register(\"Injured\", \"citizen_male\", {\n\t\tsequence = {\"d1_town05_wounded_idle_1\", \"d1_town05_wounded_idle_2\", \"d1_town05_winston_down\"},\n\t\tuntimed = true,\n\t\tidle = true\n\t})\n\n\tix.act.Register(\"Injured\", \"citizen_female\", {\n\t\tsequence = \"d1_town05_wounded_idle_1\",\n\t\tuntimed = true,\n\t\tidle = true\n\t})\n\n\t-- arrest\n\tix.act.Register(\"ArrestWall\", \"citizen_male\", {\n\t\tsequence = {\n\t\t\t{\"apcarrestidle\",\n\t\t\tcheck = FacingWall,\n\t\t\toffset = function(client)\n\t\t\t\treturn -client:GetForward() * 23\n\t\t\tend},\n\t\t\t\"spreadwallidle\"\n\t\t},\n\t\tuntimed = true\n\t})\n\n\tix.act.Register(\"Arrest\", \"citizen_male\", {\n\t\tsequence = \"arrestidle\",\n\t\tuntimed = true\n\t})\n\n\t-- threat\n\tix.act.Register(\"Threat\", \"metrocop\", {\n\t\tsequence = \"plazathreat1\",\n\t})\n\n\t-- deny\n\tix.act.Register(\"Deny\", \"metrocop\", {\n\t\tsequence = \"harassfront2\",\n\t})\n\n\t-- motion\n\tix.act.Register(\"Motion\", \"metrocop\", {\n\t\tsequence = {\"motionleft\", \"motionright\", \"luggagewarn\"}\n\t})\n\n\t-- wave\n\tix.act.Register(\"Wave\", {\"citizen_male\", \"citizen_female\"}, {\n\t\tsequence = {{\"wave\", duration = 2.75}, {\"wave_close\", duration = 1.75}}\n\t})\n\n\t-- pant\n\tix.act.Register(\"Pant\", {\"citizen_male\", \"citizen_female\"}, {\n\t\tstart = {\"d2_coast03_postbattle_idle02_entry\", \"d2_coast03_postbattle_idle01_entry\"},\n\t\tsequence = {\"d2_coast03_postbattle_idle02\", {\"d2_coast03_postbattle_idle01\", check = FacingWall}},\n\t\tuntimed = true\n\t})\n\n\t-- window\n\tix.act.Register(\"Window\", \"citizen_male\", {\n\t\tsequence = \"d1_t03_tenements_look_out_window_idle\",\n\t\tuntimed = true\n\t})\n\n\tix.act.Register(\"Window\", \"citizen_female\", {\n\t\tsequence = \"d1_t03_lookoutwindow\",\n\t\tuntimed = true\n\t})\nend\n"
  },
  {
    "path": "plugins/act/sh_plugin.lua",
    "content": "\n--[[--\nProvides players the ability to perform animations.\n\n]]\n-- @module ix.act\n\nlocal PLUGIN = PLUGIN\n\nPLUGIN.name = \"Player Acts\"\nPLUGIN.description = \"Adds animations that can be performed by certain models.\"\nPLUGIN.author = \"`impulse\"\n\nix.act = ix.act or {}\nix.act.stored = ix.act.stored or {}\n\nCAMI.RegisterPrivilege({\n\tName = \"Helix - Player Acts\",\n\tMinAccess = \"user\"\n})\n\n--- Registers a sequence as a performable animation.\n-- @realm shared\n-- @string name Name of the animation (in CamelCase)\n-- @string modelClass Model class to add this animation to\n-- @tab data An `ActInfoStructure` table describing the animation\nfunction ix.act.Register(name, modelClass, data)\n\tix.act.stored[name] = ix.act.stored[name] or {} -- might be adding onto an existing act\n\n\tif (!data.sequence) then\n\t\treturn ErrorNoHalt(string.format(\n\t\t\t\"Act '%s' for '%s' tried to register without a provided sequence\\n\", name, modelClass\n\t\t))\n\tend\n\n\tif (!istable(data.sequence)) then\n\t\tdata.sequence = {data.sequence}\n\tend\n\n\tif (data.start and istable(data.start) and #data.start != #data.sequence) then\n\t\treturn ErrorNoHalt(string.format(\n\t\t\t\"Act '%s' tried to register without matching number of enter sequences\\n\", name\n\t\t))\n\tend\n\n\tif (data.finish and istable(data.finish) and #data.finish != #data.sequence) then\n\t\treturn ErrorNoHalt(string.format(\n\t\t\t\"Act '%s' tried to register without matching number of exit sequences\\n\", name\n\t\t))\n\tend\n\n\tif (istable(modelClass)) then\n\t\tfor _, v in ipairs(modelClass) do\n\t\t\tix.act.stored[name][v] = data\n\t\tend\n\telse\n\t\tix.act.stored[name][modelClass] = data\n\tend\nend\n\n--- Removes a sequence from being performable if it has been previously registered.\n-- @realm shared\n-- @string name Name of the animation\nfunction ix.act.Remove(name)\n\tix.act.stored[name] = nil\n\tix.command.list[\"Act\" .. name] = nil\nend\n\nix.util.Include(\"sh_definitions.lua\")\nix.util.Include(\"sv_hooks.lua\")\nix.util.Include(\"cl_hooks.lua\")\n\nfunction PLUGIN:InitializedPlugins()\n\thook.Run(\"SetupActs\")\n\thook.Run(\"PostSetupActs\")\nend\n\nfunction PLUGIN:ExitAct(client)\n\tclient.ixUntimedSequence = nil\n\tclient:SetNetVar(\"actEnterAngle\")\n\n\tnet.Start(\"ixActLeave\")\n\tnet.Send(client)\nend\n\nfunction PLUGIN:PostSetupActs()\n\t-- create chat commands for all stored acts\n\tfor act, classes in pairs(ix.act.stored) do\n\t\tlocal variants = 1\n\t\tlocal COMMAND = {\n\t\t\tprivilege = \"Player Acts\"\n\t\t}\n\n\t\t-- check if this act has any variants (i.e /ActSit 2)\n\t\tfor _, v in pairs(classes) do\n\t\t\tif (#v.sequence > 1) then\n\t\t\t\tvariants = math.max(variants, #v.sequence)\n\t\t\tend\n\t\tend\n\n\t\t-- setup command arguments if there are variants for this act\n\t\tif (variants > 1) then\n\t\t\tCOMMAND.arguments = bit.bor(ix.type.number, ix.type.optional)\n\t\t\tCOMMAND.argumentNames = {\"variant (1-\" .. variants .. \")\"}\n\t\tend\n\n\t\tCOMMAND.GetDescription = function(command)\n\t\t\treturn L(\"cmdAct\", act)\n\t\tend\n\n\t\tlocal privilege = \"Helix - \" .. COMMAND.privilege\n\n\t\t-- we'll perform a model class check in OnCheckAccess to prevent the command from showing up on the client at all\n\t\tCOMMAND.OnCheckAccess = function(command, client)\n\t\t\tlocal bHasAccess, _ = CAMI.PlayerHasAccess(client, privilege, nil)\n\n\t\t\tif (!bHasAccess) then\n\t\t\t\treturn false\n\t\t\tend\n\n\t\t\tlocal modelClass = ix.anim.GetModelClass(client:GetModel())\n\n\t\t\tif (!classes[modelClass]) then\n\t\t\t\treturn false, \"modelNoSeq\"\n\t\t\tend\n\n\t\t\treturn true\n\t\tend\n\n\t\tCOMMAND.OnRun = function(command, client, variant)\n\t\t\tvariant = math.Clamp(tonumber(variant) or 1, 1, variants)\n\n\t\t\tif (client:GetNetVar(\"actEnterAngle\")) then\n\t\t\t\treturn \"@notNow\"\n\t\t\tend\n\n\t\t\tlocal modelClass = ix.anim.GetModelClass(client:GetModel())\n\t\t\tlocal bCanEnter, error = PLUGIN:CanPlayerEnterAct(client, modelClass, variant, classes)\n\n\t\t\tif (!bCanEnter) then\n\t\t\t\treturn error\n\t\t\tend\n\n\t\t\tlocal data = classes[modelClass]\n\t\t\tlocal mainSequence = data.sequence[variant]\n\t\t\tlocal mainDuration\n\n\t\t\t-- check if the main sequence has any extra info\n\t\t\tif (istable(mainSequence)) then\n\t\t\t\t-- any validity checks to perform (i.e facing a wall)\n\t\t\t\tif (mainSequence.check) then\n\t\t\t\t\tlocal result = mainSequence.check(client)\n\n\t\t\t\t\tif (result) then\n\t\t\t\t\t\treturn result\n\t\t\t\t\tend\n\t\t\t\tend\n\n\t\t\t\t-- position offset\n\t\t\t\tif (mainSequence.offset) then\n\t\t\t\t\tclient.ixOldPosition = client:GetPos()\n\t\t\t\t\tclient:SetPos(client:GetPos() + mainSequence.offset(client))\n\t\t\t\tend\n\n\t\t\t\tmainDuration = mainSequence.duration\n\t\t\t\tmainSequence = mainSequence[1]\n\t\t\tend\n\n\t\t\tlocal startSequence = data.start and data.start[variant] or \"\"\n\t\t\tlocal startDuration\n\n\t\t\tif (istable(startSequence)) then\n\t\t\t\tstartDuration = startSequence.duration\n\t\t\t\tstartSequence = startSequence[1]\n\t\t\tend\n\n\t\t\tclient:SetNetVar(\"actEnterAngle\", client:GetAngles())\n\n\t\t\tclient:ForceSequence(startSequence, function()\n\t\t\t\t-- we've finished the start sequence\n\t\t\t\tclient.ixUntimedSequence = data.untimed -- client can exit after the start sequence finishes playing\n\n\t\t\t\tlocal duration = client:ForceSequence(mainSequence, function()\n\t\t\t\t\t-- we've stopped playing the main sequence (either duration expired or user cancelled the act)\n\t\t\t\t\tif (data.finish) then\n\t\t\t\t\t\tlocal finishSequence = data.finish[variant]\n\t\t\t\t\t\tlocal finishDuration\n\n\t\t\t\t\t\tif (istable(finishSequence)) then\n\t\t\t\t\t\t\tfinishDuration = finishSequence.duration\n\t\t\t\t\t\t\tfinishSequence = finishSequence[1]\n\t\t\t\t\t\tend\n\n\t\t\t\t\t\tclient:ForceSequence(finishSequence, function()\n\t\t\t\t\t\t\t-- client has finished the end sequence and is no longer playing any animations\n\t\t\t\t\t\t\tself:ExitAct(client)\n\t\t\t\t\t\tend, finishDuration)\n\t\t\t\t\telse\n\t\t\t\t\t\t-- there's no end sequence so we can exit right away\n\t\t\t\t\t\tself:ExitAct(client)\n\t\t\t\t\tend\n\t\t\t\tend, data.untimed and 0 or (mainDuration or nil))\n\n\t\t\t\tif (!duration) then\n\t\t\t\t\t-- the model doesn't support this variant\n\t\t\t\t\tself:ExitAct(client)\n\t\t\t\t\tclient:NotifyLocalized(\"modelNoSeq\")\n\n\t\t\t\t\treturn\n\t\t\t\tend\n\t\t\tend, startDuration, nil)\n\n\t\t\tnet.Start(\"ixActEnter\")\n\t\t\t\tnet.WriteBool(data.idle or false)\n\t\t\tnet.Send(client)\n\n\t\t\tclient.ixNextAct = CurTime() + 4\n\t\tend\n\n\t\tix.command.Add(\"Act\" .. act, COMMAND)\n\tend\n\n\t-- setup exit act command\n\tlocal COMMAND = {\n\t\tprivilege = \"Player Acts\",\n\t\tOnRun = function(command, client)\n\t\t\tif (client.ixUntimedSequence) then\n\t\t\t\tclient:LeaveSequence()\n\t\t\tend\n\t\tend\n\t}\n\n\tif (CLIENT) then\n\t\t-- hide this command from the command list\n\t\tCOMMAND.OnCheckAccess = function(client)\n\t\t\treturn false\n\t\tend\n\tend\n\n\tix.command.Add(\"ExitAct\", COMMAND)\nend\n\nfunction PLUGIN:UpdateAnimation(client, moveData)\n\tlocal angle = client:GetNetVar(\"actEnterAngle\")\n\n\tif (angle) then\n\t\tclient:SetRenderAngles(angle)\n\tend\nend\n\ndo\n\tlocal keyBlacklist = IN_ATTACK + IN_ATTACK2\n\n\tfunction PLUGIN:StartCommand(client, command)\n\t\tif (client:GetNetVar(\"actEnterAngle\")) then\n\t\t\tcommand:RemoveKey(keyBlacklist)\n\t\tend\n\tend\nend\n"
  },
  {
    "path": "plugins/act/sv_hooks.lua",
    "content": "\nlocal PLUGIN = PLUGIN\n\nutil.AddNetworkString(\"ixActEnter\")\nutil.AddNetworkString(\"ixActLeave\")\n\nfunction PLUGIN:CanPlayerEnterAct(client, modelClass, variant, act)\n\tif (!client:Alive() or client:GetLocalVar(\"ragdoll\") or client:WaterLevel() > 0 or !client:IsOnGround()) then\n\t\treturn false, L(\"notNow\", client)\n\tend\n\n\t-- check if player's model class has an entry in this act table\n\tmodelClass = modelClass or ix.anim.GetModelClass(client:GetModel())\n\tlocal data = act[modelClass]\n\n\tif (!data) then\n\t\treturn false, L(\"modelNoSeq\", client)\n\tend\n\n\t-- some models don't support certain variants\n\tlocal sequence = data.sequence[variant]\n\n\tif (!sequence) then\n\t\treturn false, L(\"modelNoSeq\", client)\n\tend\n\n\treturn true\nend\n\nfunction PLUGIN:PlayerDeath(client)\n\tif (client.ixUntimedSequence) then\n\t\tclient:SetNetVar(\"actEnterAngle\")\n\t\tclient:LeaveSequence()\n\t\tclient.ixUntimedSequence = nil\n\tend\nend\n\nfunction PLUGIN:PlayerSpawn(client)\n\tif (client.ixUntimedSequence) then\n\t\tclient:SetNetVar(\"actEnterAngle\")\n\t\tclient:LeaveSequence()\n\t\tclient.ixUntimedSequence = nil\n\tend\nend\n\nfunction PLUGIN:OnCharacterFallover(client)\n\tif (client.ixUntimedSequence) then\n\t\tclient:SetNetVar(\"actEnterAngle\")\n\t\tclient:LeaveSequence()\n\t\tclient.ixUntimedSequence = nil\n\tend\nend\n"
  },
  {
    "path": "plugins/ammosave.lua",
    "content": "\nlocal PLUGIN = PLUGIN\n\nPLUGIN.name = \"Ammo Saver\"\nPLUGIN.author = \"Black Tea\"\nPLUGIN.description = \"Saves the ammo of a character.\"\nPLUGIN.ammoList = {}\n\nix.ammo = ix.ammo or {}\n\nfunction ix.ammo.Register(name)\n\tname = name:lower()\n\n\tif (!table.HasValue(PLUGIN.ammoList, name)) then\n\t\tPLUGIN.ammoList[#PLUGIN.ammoList + 1] = name\n\tend\nend\n\n-- Register Default HL2 Ammunition.\nix.ammo.Register(\"ar2\")\nix.ammo.Register(\"pistol\")\nix.ammo.Register(\"357\")\nix.ammo.Register(\"smg1\")\nix.ammo.Register(\"xbowbolt\")\nix.ammo.Register(\"buckshot\")\nix.ammo.Register(\"rpg_round\")\nix.ammo.Register(\"smg1_grenade\")\nix.ammo.Register(\"grenade\")\nix.ammo.Register(\"ar2altfire\")\nix.ammo.Register(\"slam\")\n\n-- Register Cut HL2 Ammunition.\nix.ammo.Register(\"alyxgun\")\nix.ammo.Register(\"sniperround\")\nix.ammo.Register(\"sniperpenetratedround\")\nix.ammo.Register(\"thumper\")\nix.ammo.Register(\"gravity\")\nix.ammo.Register(\"battery\")\nix.ammo.Register(\"gaussenergy\")\nix.ammo.Register(\"combinecannon\")\nix.ammo.Register(\"airboatgun\")\nix.ammo.Register(\"striderminigun\")\nix.ammo.Register(\"helicoptergun\")\n\n-- Called right before the character has its information save.\nfunction PLUGIN:CharacterPreSave(character)\n\t-- Get the player from the character.\n\tlocal client = character:GetPlayer()\n\n\t-- Check to see if we can get the player's ammo.\n\tif (IsValid(client)) then\n\t\tlocal ammoTable = {}\n\n\t\tfor _, v in ipairs(self.ammoList) do\n\t\t\tlocal ammo = client:GetAmmoCount(v)\n\n\t\t\tif (ammo > 0) then\n\t\t\t\tammoTable[v] = ammo\n\t\t\tend\n\t\tend\n\n\t\tcharacter:SetData(\"ammo\", ammoTable)\n\tend\nend\n\n-- Called after the player's loadout has been set.\nfunction PLUGIN:PlayerLoadedCharacter(client)\n\ttimer.Simple(0.25, function()\n\t\tif (!IsValid(client)) then\n\t\t\treturn\n\t\tend\n\n\t\t-- Get the saved ammo table from the character data.\n\t\tlocal character = client:GetCharacter()\n\n\t\tif (!character) then\n\t\t\treturn\n\t\tend\n\n\t\tlocal ammoTable = character:GetData(\"ammo\")\n\n\t\t-- Check if the ammotable is exists.\n\t\tif (ammoTable) then\n\t\t\tfor k, v in pairs(ammoTable) do\n\t\t\t\tclient:SetAmmo(v, tostring(k))\n\t\t\tend\n\t\tend\n\tend)\nend\n"
  },
  {
    "path": "plugins/area/cl_hooks.lua",
    "content": "\nlocal PLUGIN = PLUGIN\n\nlocal function DrawTextBackground(x, y, text, font, backgroundColor, padding)\n\tfont = font or \"ixSubTitleFont\"\n\tpadding = padding or 8\n\tbackgroundColor = backgroundColor or Color(88, 88, 88, 255)\n\n\tsurface.SetFont(font)\n\tlocal textWidth, textHeight = surface.GetTextSize(text)\n\tlocal width, height = textWidth + padding * 2, textHeight + padding * 2\n\n\tix.util.DrawBlurAt(x, y, width, height)\n\tsurface.SetDrawColor(0, 0, 0, 40)\n\tsurface.DrawRect(x, y, width, height)\n\n\tderma.SkinFunc(\"DrawImportantBackground\", x, y, width, height, backgroundColor)\n\n\tsurface.SetTextColor(color_white)\n\tsurface.SetTextPos(x + padding, y + padding)\n\tsurface.DrawText(text)\n\n\treturn height\nend\n\nfunction PLUGIN:InitPostEntity()\n\thook.Run(\"SetupAreaProperties\")\nend\n\nfunction PLUGIN:ChatboxCreated()\n\tif (IsValid(self.panel)) then\n\t\tself.panel:Remove()\n\tend\n\n\tself.panel = vgui.Create(\"ixArea\")\nend\n\nfunction PLUGIN:ChatboxPositionChanged(x, y, width, height)\n\tif (!IsValid(self.panel)) then\n\t\treturn\n\tend\n\n\tself.panel:SetSize(width, y)\n\tself.panel:SetPos(32, 0)\nend\n\nfunction PLUGIN:ShouldDrawCrosshair()\n\tif (ix.area.bEditing) then\n\t\treturn true\n\tend\nend\n\nfunction PLUGIN:PlayerBindPress(client, bind, bPressed)\n\tif (!ix.area.bEditing) then\n\t\treturn\n\tend\n\n\tif ((bind:find(\"invnext\") or bind:find(\"invprev\")) and bPressed) then\n\t\treturn true\n\telseif (bind:find(\"attack2\") and bPressed) then\n\t\tself:EditRightClick()\n\t\treturn true\n\telseif (bind:find(\"attack\") and bPressed) then\n\t\tself:EditClick()\n\t\treturn true\n\telseif (bind:find(\"reload\") and bPressed) then\n\t\tself:EditReload()\n\t\treturn true\n\tend\nend\n\nfunction PLUGIN:HUDPaint()\n\tif (!ix.area.bEditing) then\n\t\treturn\n\tend\n\n\tlocal id = LocalPlayer():GetArea()\n\tlocal area = ix.area.stored[id]\n\tlocal height = ScrH()\n\n\tlocal y = 64\n\ty = y + DrawTextBackground(64, y, L(\"areaEditMode\"), nil, ix.config.Get(\"color\"))\n\n\tif (!self.editStart) then\n\t\ty = y + DrawTextBackground(64, y, L(\"areaEditTip\"), \"ixSmallTitleFont\")\n\t\tDrawTextBackground(64, y, L(\"areaRemoveTip\"), \"ixSmallTitleFont\")\n\telse\n\t\tDrawTextBackground(64, y, L(\"areaFinishTip\"), \"ixSmallTitleFont\")\n\tend\n\n\tif (area) then\n\t\tDrawTextBackground(64, height - 64 - ScreenScale(12), id, \"ixSmallTitleFont\", area.properties.color)\n\tend\nend\n\nfunction PLUGIN:PostDrawTranslucentRenderables(bDepth, bSkybox)\n\tif (bSkybox or !ix.area.bEditing) then\n\t\treturn\n\tend\n\n\t-- draw all areas\n\tfor k, v in pairs(ix.area.stored) do\n\t\tlocal center, min, max = self:GetLocalAreaPosition(v.startPosition, v.endPosition)\n\t\tlocal color = ColorAlpha(v.properties.color or ix.config.Get(\"color\"), 255)\n\n\t\trender.DrawWireframeBox(center, angle_zero, min, max, color)\n\n\t\tcam.Start2D()\n\t\t\tlocal centerScreen = center:ToScreen()\n\t\t\tlocal _, textHeight = draw.SimpleText(\n\t\t\t\tk, \"BudgetLabel\", centerScreen.x, centerScreen.y, color, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)\n\n\t\t\tif (v.type != \"area\") then\n\t\t\t\tdraw.SimpleText(\n\t\t\t\t\t\"(\" .. L(v.type) .. \")\", \"BudgetLabel\",\n\t\t\t\t\tcenterScreen.x, centerScreen.y + textHeight, color, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER\n\t\t\t\t)\n\t\t\tend\n\t\tcam.End2D()\n\tend\n\n\t-- draw currently edited area\n\tif (self.editStart) then\n\t\tlocal center, min, max = self:GetLocalAreaPosition(self.editStart, self:GetPlayerAreaTrace().HitPos)\n\t\tlocal color = Color(255, 255, 255, 25 + (1 + math.sin(SysTime() * 6)) * 115)\n\n\t\trender.DrawWireframeBox(center, angle_zero, min, max, color)\n\n\t\tcam.Start2D()\n\t\t\tlocal centerScreen = center:ToScreen()\n\n\t\t\tdraw.SimpleText(L(\"areaNew\"), \"BudgetLabel\",\n\t\t\t\tcenterScreen.x, centerScreen.y, color_white, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)\n\t\tcam.End2D()\n\tend\nend\n\nfunction PLUGIN:EditRightClick()\n\tif (self.editStart) then\n\t\tself.editStart = nil\n\telse\n\t\tself:StopEditing()\n\tend\nend\n\nfunction PLUGIN:EditClick()\n\tif (!self.editStart) then\n\t\tself.editStart = LocalPlayer():GetEyeTraceNoCursor().HitPos\n\telseif (self.editStart and !self.editProperties) then\n\t\tself.editProperties = true\n\n\t\tlocal panel = vgui.Create(\"ixAreaEdit\")\n\t\tpanel:MakePopup()\n\tend\nend\n\nfunction PLUGIN:EditReload()\n\tif (self.editStart) then\n\t\treturn\n\tend\n\n\tlocal id = LocalPlayer():GetArea()\n\tlocal area = ix.area.stored[id]\n\n\tif (!area) then\n\t\treturn\n\tend\n\n\tDerma_Query(L(\"areaDeleteConfirm\", id), L(\"areaDelete\"),\n\t\tL(\"no\"), nil,\n\t\tL(\"yes\"), function()\n\t\t\tnet.Start(\"ixAreaRemove\")\n\t\t\t\tnet.WriteString(id)\n\t\t\tnet.SendToServer()\n\t\tend\n\t)\nend\n\nfunction PLUGIN:ShouldDisplayArea(id)\n\tif (ix.area.bEditing) then\n\t\treturn false\n\tend\nend\n\nfunction PLUGIN:OnAreaChanged(oldID, newID)\n\tlocal client = LocalPlayer()\n\tclient.ixArea = newID\n\n\tlocal area = ix.area.stored[newID]\n\n\tif (!area) then\n\t\tclient.ixInArea = false\n\t\treturn\n\tend\n\n\tclient.ixInArea = true\n\n\tif (hook.Run(\"ShouldDisplayArea\", newID) == false or !area.properties.display) then\n\t\treturn\n\tend\n\n\tlocal format = newID .. (ix.option.Get(\"24hourTime\", false) and \", %H:%M.\" or \", %I:%M %p.\")\n\tformat = ix.date.GetFormatted(format)\n\n\tself.panel:AddEntry(format, area.properties.color)\nend\n\nnet.Receive(\"ixAreaEditStart\", function()\n\tPLUGIN:StartEditing()\nend)\n\nnet.Receive(\"ixAreaEditEnd\", function()\n\tPLUGIN:StopEditing()\nend)\n\nnet.Receive(\"ixAreaAdd\", function()\n\tlocal name = net.ReadString()\n\tlocal type = net.ReadString()\n\tlocal startPosition, endPosition = net.ReadVector(), net.ReadVector()\n\tlocal properties = net.ReadTable()\n\n\tif (name != \"\") then\n\t\tix.area.stored[name] = {\n\t\t\ttype = type,\n\t\t\tstartPosition = startPosition,\n\t\t\tendPosition = endPosition,\n\t\t\tproperties = properties\n\t\t}\n\tend\nend)\n\nnet.Receive(\"ixAreaRemove\", function()\n\tlocal name = net.ReadString()\n\n\tif (ix.area.stored[name]) then\n\t\tix.area.stored[name] = nil\n\tend\nend)\n\nnet.Receive(\"ixAreaSync\", function()\n\tlocal length = net.ReadUInt(32)\n\tlocal data = net.ReadData(length)\n\tlocal uncompressed = util.Decompress(data)\n\n\tif (!uncompressed) then\n\t\tErrorNoHalt(\"[Helix] Unable to decompress area data!\\n\")\n\t\treturn\n\tend\n\n\t-- Set the list of texts to the ones provided by the server.\n\tix.area.stored = util.JSONToTable(uncompressed)\nend)\n\nnet.Receive(\"ixAreaChanged\", function()\n\tlocal oldID, newID = net.ReadString(), net.ReadString()\n\n\thook.Run(\"OnAreaChanged\", oldID, newID)\nend)\n"
  },
  {
    "path": "plugins/area/cl_plugin.lua",
    "content": "\nlocal PLUGIN = PLUGIN\n\nfunction PLUGIN:GetPlayerAreaTrace()\n\tlocal client = LocalPlayer()\n\n\treturn util.TraceLine({\n\t\tstart = client:GetShootPos(),\n\t\tendpos = client:GetShootPos() + client:GetForward() * 96,\n\t\tfilter = client\n\t})\nend\n\nfunction PLUGIN:StartEditing()\n\tix.area.bEditing = true\n\tself.editStart = nil\n\tself.editProperties = nil\nend\n\nfunction PLUGIN:StopEditing()\n\tix.area.bEditing = false\n\n\tif (IsValid(ix.gui.areaEdit)) then\n\t\tix.gui.areaEdit:Remove()\n\tend\nend\n"
  },
  {
    "path": "plugins/area/derma/cl_area.lua",
    "content": "\n-- area entry\nDEFINE_BASECLASS(\"Panel\")\nlocal PANEL = {}\n\nAccessorFunc(PANEL, \"text\", \"Text\", FORCE_STRING)\nAccessorFunc(PANEL, \"backgroundColor\", \"BackgroundColor\")\nAccessorFunc(PANEL, \"tickSound\", \"TickSound\", FORCE_STRING)\nAccessorFunc(PANEL, \"tickSoundRange\", \"TickSoundRange\")\nAccessorFunc(PANEL, \"backgroundAlpha\", \"BackgroundAlpha\", FORCE_NUMBER)\nAccessorFunc(PANEL, \"expireTime\", \"ExpireTime\", FORCE_NUMBER)\nAccessorFunc(PANEL, \"animationTime\", \"AnimationTime\", FORCE_NUMBER)\n\nfunction PANEL:Init()\n\tself:DockPadding(4, 4, 4, 4)\n\tself:SetSize(self:GetParent():GetWide(), 0)\n\n\tself.label = self:Add(\"DLabel\")\n\tself.label:Dock(FILL)\n\tself.label:SetFont(\"ixMediumLightFont\")\n\tself.label:SetTextColor(color_white)\n\tself.label:SetExpensiveShadow(1, color_black)\n\tself.label:SetText(\"Area\")\n\n\tself.text = \"\"\n\tself.tickSound = \"ui/buttonrollover.wav\"\n\tself.tickSoundRange = {190, 200}\n\tself.backgroundAlpha = 255\n\tself.expireTime = 8\n\tself.animationTime = 2\n\n\tself.character = 1\n\tself.createTime = RealTime()\n\tself.currentAlpha = 255\n\tself.currentHeight = 0\n\tself.nextThink = RealTime()\nend\n\nfunction PANEL:Show()\n\tself:CreateAnimation(0.5, {\n\t\tindex = -1,\n\t\ttarget = {currentHeight = self.label:GetTall() + 8},\n\t\teasing = \"outQuint\",\n\n\t\tThink = function(animation, panel)\n\t\t\tpanel:SetTall(panel.currentHeight)\n\t\tend\n\t})\nend\n\nfunction PANEL:SetFont(font)\n\tself.label:SetFont(font)\nend\n\nfunction PANEL:SetText(text)\n\tif (text:sub(1, 1) == \"@\") then\n\t\ttext = L(text:sub(2))\n\tend\n\n\tself.label:SetText(text)\n\tself.text = text\n\tself.character = 1\nend\n\nfunction PANEL:Think()\n\tlocal time = RealTime()\n\n\tif (time >= self.nextThink) then\n\t\tif (self.character < self.text:utf8len()) then\n\t\t\tself.character = self.character + 1\n\t\t\tself.label:SetText(string.utf8sub(self.text, 1, self.character))\n\n\t\t\tLocalPlayer():EmitSound(self.tickSound, 100, math.random(self.tickSoundRange[1], self.tickSoundRange[2]))\n\t\tend\n\n\t\tif (time >= self.createTime + self.expireTime and !self.bRemoving) then\n\t\t\tself:Remove()\n\t\tend\n\n\t\tself.nextThink = time + 0.05\n\tend\nend\n\nfunction PANEL:SizeToContents()\n\tself:SetWide(self:GetParent():GetWide())\n\n\tself.label:SetWide(self:GetWide())\n\tself.label:SizeToContentsY()\nend\n\nfunction PANEL:Paint(width, height)\n\tself.backgroundAlpha = math.max(self.backgroundAlpha - 200 * FrameTime(), 0)\n\n\tderma.SkinFunc(\"PaintAreaEntry\", self, width, height)\nend\n\nfunction PANEL:Remove()\n\tif (self.bRemoving) then\n\t\treturn\n\tend\n\n\tself:CreateAnimation(self.animationTime, {\n\t\ttarget = {currentAlpha = 0},\n\n\t\tThink = function(animation, panel)\n\t\t\tpanel:SetAlpha(panel.currentAlpha)\n\t\tend,\n\n\t\tOnComplete = function(animation, panel)\n\t\t\tpanel:CreateAnimation(0.5, {\n\t\t\t\tindex = -1,\n\t\t\t\ttarget = {currentHeight = 0},\n\t\t\t\teasing = \"outQuint\",\n\n\t\t\t\tThink = function(_, sizePanel)\n\t\t\t\t\tsizePanel:SetTall(sizePanel.currentHeight)\n\t\t\t\tend,\n\n\t\t\t\tOnComplete = function(_, sizePanel)\n\t\t\t\t\tsizePanel:OnRemove()\n\t\t\t\t\tBaseClass.Remove(sizePanel)\n\t\t\t\tend\n\t\t\t})\n\t\tend\n\t})\n\n\tself.bRemoving = true\nend\n\nfunction PANEL:OnRemove()\nend\n\nvgui.Register(\"ixAreaEntry\", PANEL, \"Panel\")\n\n-- main panel\nPANEL = {}\n\nfunction PANEL:Init()\n\tlocal chatWidth, _ = chat.GetChatBoxSize()\n\tlocal _, chatY = chat.GetChatBoxPos()\n\n\tself:SetSize(chatWidth, chatY)\n\tself:SetPos(32, 0)\n\tself:ParentToHUD()\n\n\tself.entries = {}\n\tix.gui.area = self\nend\n\nfunction PANEL:AddEntry(entry, color)\n\tcolor = color or ix.config.Get(\"color\")\n\n\tlocal id = #self.entries + 1\n\tlocal panel = entry\n\n\tif (isstring(entry)) then\n\t\tpanel = self:Add(\"ixAreaEntry\")\n\t\tpanel:SetText(entry)\n\tend\n\n\tpanel:SetBackgroundColor(color)\n\tpanel:SizeToContents()\n\tpanel:Dock(BOTTOM)\n\tpanel:Show()\n\tpanel.OnRemove = function()\n\t\tfor k, v in pairs(self.entries) do\n\t\t\tif (v == panel) then\n\t\t\t\ttable.remove(self.entries, k)\n\t\t\t\tbreak\n\t\t\tend\n\t\tend\n\tend\n\n\tself.entries[id] = panel\n\treturn id\nend\n\nfunction PANEL:GetEntries()\n\treturn self.entries\nend\n\nvgui.Register(\"ixArea\", PANEL, \"Panel\")\n"
  },
  {
    "path": "plugins/area/derma/cl_areaedit.lua",
    "content": "\nlocal PLUGIN = PLUGIN\nlocal PANEL = {}\n\nfunction PANEL:Init()\n\tif (IsValid(ix.gui.areaEdit)) then\n\t\tix.gui.areaEdit:Remove()\n\tend\n\n\tix.gui.areaEdit = self\n\tself.list = {}\n\tself.properties = {}\n\n\tself:SetDeleteOnClose(true)\n\tself:SetSizable(true)\n\tself:SetTitle(L(\"areaNew\"))\n\n\t-- scroll panel\n\tself.canvas = self:Add(\"DScrollPanel\")\n\tself.canvas:Dock(FILL)\n\n\t-- name entry\n\tself.nameEntry = vgui.Create(\"ixTextEntry\")\n\tself.nameEntry:SetFont(\"ixMediumLightFont\")\n\tself.nameEntry:SetText(L(\"areaNew\"))\n\n\tlocal listRow = self.canvas:Add(\"ixListRow\")\n\tlistRow:SetList(self.list)\n\tlistRow:SetLabelText(L(\"name\"))\n\tlistRow:SetRightPanel(self.nameEntry)\n\tlistRow:Dock(TOP)\n\tlistRow:SizeToContents()\n\n\t-- type entry\n\tself.typeEntry = self.canvas:Add(\"DComboBox\")\n\tself.typeEntry:Dock(RIGHT)\n\tself.typeEntry:SetFont(\"ixMediumLightFont\")\n\tself.typeEntry:SetTextColor(color_black)\n\tself.typeEntry.OnSelect = function(panel)\n\t\tpanel:SizeToContents()\n\t\tpanel:SetWide(panel:GetWide() + 12) -- padding for arrow (nice)\n\tend\n\n\tfor id, name in pairs(ix.area.types) do\n\t\tself.typeEntry:AddChoice(L(name), id, id == \"area\")\n\tend\n\n\tlistRow = self.canvas:Add(\"ixListRow\")\n\tlistRow:SetList(self.list)\n\tlistRow:SetLabelText(L(\"type\"))\n\tlistRow:SetRightPanel(self.typeEntry)\n\tlistRow:Dock(TOP)\n\tlistRow:SizeToContents()\n\n\t-- properties\n\tfor k, v in pairs(ix.area.properties) do\n\t\tlocal panel\n\n\t\tif (v.type == ix.type.string or v.type == ix.type.number) then\n\t\t\tpanel = vgui.Create(\"ixTextEntry\")\n\t\t\tpanel:SetFont(\"ixMenuButtonFont\")\n\t\t\tpanel:SetText(tostring(v.default))\n\n\t\t\tif (v.type == ix.type.number) then\n\t\t\t\tpanel.realGetValue = panel.GetValue\n\t\t\t\tpanel.GetValue = function()\n\t\t\t\t\treturn tonumber(panel:realGetValue()) or v.default\n\t\t\t\tend\n\t\t\tend\n\t\telseif (v.type == ix.type.bool) then\n\t\t\tpanel = vgui.Create(\"ixCheckBox\")\n\t\t\tpanel:SetChecked(v.default, true)\n\t\t\tpanel:SetFont(\"ixMediumLightFont\")\n\t\telseif (v.type == ix.type.color) then\n\t\t\tpanel = vgui.Create(\"DButton\")\n\t\t\tpanel.value = v.default\n\t\t\tpanel:SetText(\"\")\n\t\t\tpanel:SetSize(64, 64)\n\n\t\t\tpanel.picker = vgui.Create(\"DColorCombo\")\n\t\t\tpanel.picker:SetColor(panel.value)\n\t\t\tpanel.picker:SetVisible(false)\n\t\t\tpanel.picker.OnValueChanged = function(_, newColor)\n\t\t\t\tpanel.value = newColor\n\t\t\tend\n\n\t\t\tpanel.Paint = function(_, width, height)\n\t\t\t\tsurface.SetDrawColor(0, 0, 0, 255)\n\t\t\t\tsurface.DrawOutlinedRect(0, 0, width, height)\n\n\t\t\t\tsurface.SetDrawColor(panel.value)\n\t\t\t\tsurface.DrawRect(4, 4, width - 8, height - 8)\n\t\t\tend\n\n\t\t\tpanel.DoClick = function()\n\t\t\t\tif (!panel.picker:IsVisible()) then\n\t\t\t\t\tlocal x, y = panel:LocalToScreen(0, 0)\n\n\t\t\t\t\tpanel.picker:SetPos(x, y + 32)\n\t\t\t\t\tpanel.picker:SetColor(panel.value)\n\t\t\t\t\tpanel.picker:SetVisible(true)\n\t\t\t\t\tpanel.picker:MakePopup()\n\t\t\t\telse\n\t\t\t\t\tpanel.picker:SetVisible(false)\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tpanel.OnRemove = function()\n\t\t\t\tpanel.picker:Remove()\n\t\t\tend\n\n\t\t\tpanel.GetValue = function()\n\t\t\t\treturn panel.picker:GetColor()\n\t\t\tend\n\t\tend\n\n\t\tif (IsValid(panel)) then\n\t\t\tlocal row = self.canvas:Add(\"ixListRow\")\n\t\t\trow:SetList(self.list)\n\t\t\trow:SetLabelText(L(k))\n\t\t\trow:SetRightPanel(panel)\n\t\t\trow:Dock(TOP)\n\t\t\trow:SizeToContents()\n\t\tend\n\n\t\tself.properties[k] = function()\n\t\t\treturn panel:GetValue()\n\t\tend\n\tend\n\n\t-- save button\n\tself.saveButton = self:Add(\"DButton\")\n\tself.saveButton:SetText(L(\"save\"))\n\tself.saveButton:SizeToContents()\n\tself.saveButton:Dock(BOTTOM)\n\tself.saveButton.DoClick = function()\n\t\tself:Submit()\n\tend\n\n\tself:SizeToContents()\n\tself:SetPos(64, 0)\n\tself:CenterVertical()\nend\n\nfunction PANEL:SizeToContents()\n\tlocal width = 600\n\tlocal height = 37\n\n\tfor _, v in ipairs(self.canvas:GetCanvas():GetChildren()) do\n\t\twidth = math.max(width, v:GetLabelWidth())\n\t\theight = height + v:GetTall()\n\tend\n\n\tself:SetWide(width + 200)\n\tself:SetTall(height + self.saveButton:GetTall() + 50)\nend\n\nfunction PANEL:Submit()\n\tlocal name = self.nameEntry:GetValue()\n\n\tif (ix.area.stored[name]) then\n\t\tix.util.NotifyLocalized(\"areaAlreadyExists\")\n\t\treturn\n\tend\n\n\tlocal properties = {}\n\n\tfor k, v in pairs(self.properties) do\n\t\tproperties[k] = v()\n\tend\n\n\tlocal _, type = self.typeEntry:GetSelected()\n\n\tnet.Start(\"ixAreaAdd\")\n\t\tnet.WriteString(name)\n\t\tnet.WriteString(type)\n\t\tnet.WriteVector(PLUGIN.editStart)\n\t\tnet.WriteVector(PLUGIN:GetPlayerAreaTrace().HitPos)\n\t\tnet.WriteTable(properties)\n\tnet.SendToServer()\n\n\tPLUGIN.editStart = nil\n\tself:Remove()\nend\n\nfunction PANEL:OnRemove()\n\tPLUGIN.editProperties = nil\nend\n\nvgui.Register(\"ixAreaEdit\", PANEL, \"DFrame\")\n\nif (IsValid(ix.gui.areaEdit)) then\n\tix.gui.areaEdit:Remove()\nend"
  },
  {
    "path": "plugins/area/languages/sh_english.lua",
    "content": "LANGUAGE = {\n\tarea = \"Area\",\n\tareas = \"Areas\",\n\tareaEditMode = \"Area Edit Mode\",\n\tareaNew = \"New Area\",\n\tareaAlreadyExists = \"An area with this name already exists!\",\n\tareaDoesntExist = \"An area with that name doesn't exist!\",\n\tareaInvalidType = \"You have specified an invalid area type!\",\n\tareaEditTip = \"Click to start creating an area. Right click to exit.\",\n\tareaFinishTip = \"Click again to finish drawing the area. Right click to go back.\",\n\tareaRemoveTip = \"Press reload to remove the area you're currently in.\",\n\tareaDeleteConfirm = \"Are you sure you want to delete the area \\\"%s\\\"?\",\n\tareaDelete = \"Delete Area\",\n\n\tcmdAreaEdit = \"Enters area edit mode.\"\n}"
  },
  {
    "path": "plugins/area/languages/sh_russian.lua",
    "content": "LANGUAGE = {\n\tarea = \"Зона\",\n\tareas = \"Зоны\",\n\tareaEditMode = \"Редактирование зоны\",\n\tareaNew = \"Новая зона\",\n\tareaAlreadyExists = \"Зона с таким названием уже существует!\",\n\tareaDoesntExist = \"Зона с таким названием не существует!\",\n\tareaInvalidType = \"Вы указали неверный тип зоны!\",\n\tareaEditTip = \"Нажмите, чтобы начать создание зоны. Щелкните правой кнопкой мыши, чтобы выйти.\",\n\tareaFinishTip = \"Нажмите еще раз, чтобы закончить создание зоны. Щелкните правой кнопкой мыши, чтобы вернуться.\",\n\tareaRemoveTip = \"Нажмите перезарядку, чтобы удалить зону, в которой вы находитесь.\",\n\tareaDeleteConfirm = \"Вы уверены, что хотите удалить зону \\\"%s\\\"?\",\n\tareaDelete = \"Удалить зону\",\n\n\tcmdAreaEdit = \"Вход в режим редактирования зоны.\"\n}"
  },
  {
    "path": "plugins/area/sh_plugin.lua",
    "content": "\nlocal PLUGIN = PLUGIN\n\nPLUGIN.name = \"Areas\"\nPLUGIN.author = \"`impulse\"\nPLUGIN.description = \"Provides customizable area definitions.\"\n\nix.area = ix.area or {}\nix.area.types = ix.area.types or {}\nix.area.properties = ix.area.properties or {}\nix.area.stored = ix.area.stored or {}\n\nix.config.Add(\"areaTickTime\", 1, \"How many seconds between each time a character's current area is calculated.\",\n\tfunction(oldValue, newValue)\n\t\tif (SERVER) then\n\t\t\ttimer.Adjust(\"ixAreaThink\", newValue)\n\t\tend\n\tend,\n\t{\n\t\tdata = {min = 0.1, max = 4},\n\t\tcategory = \"areas\"\n\t}\n)\n\nfunction ix.area.AddProperty(name, type, default, data)\n\tix.area.properties[name] = {\n\t\ttype = type,\n\t\tdefault = default,\n\t\tdata = data or {}\n\t}\nend\n\nfunction ix.area.AddType(type, name)\n\tname = name or type\n\n\t-- only store localized strings on the client\n\tix.area.types[type] = CLIENT and name or true\nend\n\nfunction PLUGIN:SetupAreaProperties()\n\tix.area.AddType(\"area\")\n\n\tix.area.AddProperty(\"color\", ix.type.color, ix.config.Get(\"color\"))\n\tix.area.AddProperty(\"display\", ix.type.bool, true)\nend\n\nix.util.Include(\"sv_plugin.lua\")\nix.util.Include(\"cl_plugin.lua\")\nix.util.Include(\"sv_hooks.lua\")\nix.util.Include(\"cl_hooks.lua\")\n\n-- return world center, local min, and local max from world start/end positions\nfunction PLUGIN:GetLocalAreaPosition(startPosition, endPosition)\n\tlocal center = LerpVector(0.5, startPosition, endPosition)\n\tlocal min = WorldToLocal(startPosition, angle_zero, center, angle_zero)\n\tlocal max = WorldToLocal(endPosition, angle_zero, center, angle_zero)\n\n\treturn center, min, max\nend\n\ndo\n\tlocal COMMAND = {}\n\tCOMMAND.description = \"@cmdAreaEdit\"\n\tCOMMAND.adminOnly = true\n\n\tfunction COMMAND:OnRun(client)\n\t\tclient:SetWepRaised(false)\n\n\t\tnet.Start(\"ixAreaEditStart\")\n\t\tnet.Send(client)\n\tend\n\n\tix.command.Add(\"AreaEdit\", COMMAND)\nend\n\ndo\n\tlocal PLAYER = FindMetaTable(\"Player\")\n\n\t-- returns the current area the player is in, or the last valid one if the player is not in an area\n\tfunction PLAYER:GetArea()\n\t\treturn self.ixArea\n\tend\n\n\t-- returns true if the player is in any area, this does not use the last valid area like GetArea does\n\tfunction PLAYER:IsInArea()\n\t\treturn self.ixInArea\n\tend\nend\n"
  },
  {
    "path": "plugins/area/sv_hooks.lua",
    "content": "\nfunction PLUGIN:LoadData()\n\thook.Run(\"SetupAreaProperties\")\n\tix.area.stored = self:GetData() or {}\n\n\ttimer.Create(\"ixAreaThink\", ix.config.Get(\"areaTickTime\"), 0, function()\n\t\tself:AreaThink()\n\tend)\nend\n\nfunction PLUGIN:SaveData()\n\tself:SetData(ix.area.stored)\nend\n\nfunction PLUGIN:PlayerInitialSpawn(client)\n\ttimer.Simple(1, function()\n\t\tif (IsValid(client)) then\n\t\t\tlocal json = util.TableToJSON(ix.area.stored)\n\t\t\tlocal compressed = util.Compress(json)\n\t\t\tlocal length = compressed:len()\n\n\t\t\tnet.Start(\"ixAreaSync\")\n\t\t\t\tnet.WriteUInt(length, 32)\n\t\t\t\tnet.WriteData(compressed, length)\n\t\t\tnet.Send(client)\n\t\tend\n\tend)\nend\n\nfunction PLUGIN:PlayerLoadedCharacter(client)\n\tclient.ixArea = \"\"\n\tclient.ixInArea = nil\nend\n\nfunction PLUGIN:PlayerSpawn(client)\n\tclient.ixArea = \"\"\n\tclient.ixInArea = nil\nend\n\nfunction PLUGIN:AreaThink()\n\tfor _, client in player.Iterator() do\n\t\tlocal character = client:GetCharacter()\n\n\t\tif (!client:Alive() or !character) then\n\t\t\tcontinue\n\t\tend\n\n\t\tlocal overlappingBoxes = {}\n\t\tlocal position = client:GetPos() + client:OBBCenter()\n\n\t\tfor id, info in pairs(ix.area.stored) do\n\t\t\tif (position:WithinAABox(info.startPosition, info.endPosition)) then\n\t\t\t\toverlappingBoxes[#overlappingBoxes + 1] = id\n\t\t\tend\n\t\tend\n\n\t\tif (#overlappingBoxes > 0) then\n\t\t\tlocal oldID = client:GetArea()\n\t\t\tlocal id = overlappingBoxes[1]\n\n\t\t\tif (oldID != id) then\n\t\t\t\thook.Run(\"OnPlayerAreaChanged\", client, client.ixArea, id)\n\t\t\t\tclient.ixArea = id\n\t\t\tend\n\n\t\t\tclient.ixInArea = true\n\t\telse\n\t\t\tclient.ixInArea = false\n\t\tend\n\tend\nend\n\nfunction PLUGIN:OnPlayerAreaChanged(client, oldID, newID)\n\tnet.Start(\"ixAreaChanged\")\n\t\tnet.WriteString(oldID)\n\t\tnet.WriteString(newID)\n\tnet.Send(client)\nend\n\nnet.Receive(\"ixAreaAdd\", function(length, client)\n\tif (!client:Alive() or !CAMI.PlayerHasAccess(client, \"Helix - AreaEdit\", nil)) then\n\t\treturn\n\tend\n\n\tlocal id = net.ReadString()\n\tlocal type = net.ReadString()\n\tlocal startPosition, endPosition = net.ReadVector(), net.ReadVector()\n\tlocal properties = net.ReadTable()\n\n\tif (!ix.area.types[type]) then\n\t\tclient:NotifyLocalized(\"areaInvalidType\")\n\t\treturn\n\tend\n\n\tif (ix.area.stored[id]) then\n\t\tclient:NotifyLocalized(\"areaAlreadyExists\")\n\t\treturn\n\tend\n\n\tfor k, v in pairs(properties) do\n\t\tif (!isstring(k) or !ix.area.properties[k]) then\n\t\t\tcontinue\n\t\tend\n\n\t\tproperties[k] = ix.util.SanitizeType(ix.area.properties[k].type, v)\n\tend\n\n\tix.area.Create(id, type, startPosition, endPosition, nil, properties)\n\tix.log.Add(client, \"areaAdd\", id)\nend)\n\nnet.Receive(\"ixAreaRemove\", function(length, client)\n\tif (!client:Alive() or !CAMI.PlayerHasAccess(client, \"Helix - AreaEdit\", nil)) then\n\t\treturn\n\tend\n\n\tlocal id = net.ReadString()\n\n\tif (!ix.area.stored[id]) then\n\t\tclient:NotifyLocalized(\"areaDoesntExist\")\n\t\treturn\n\tend\n\n\tix.area.Remove(id)\n\tix.log.Add(client, \"areaRemove\", id)\nend)\n"
  },
  {
    "path": "plugins/area/sv_plugin.lua",
    "content": "\nutil.AddNetworkString(\"ixAreaSync\")\nutil.AddNetworkString(\"ixAreaAdd\")\nutil.AddNetworkString(\"ixAreaRemove\")\nutil.AddNetworkString(\"ixAreaChanged\")\n\nutil.AddNetworkString(\"ixAreaEditStart\")\nutil.AddNetworkString(\"ixAreaEditEnd\")\n\nix.log.AddType(\"areaAdd\", function(client, name)\n\treturn string.format(\"%s has added area \\\"%s\\\".\", client:Name(), tostring(name))\nend)\n\nix.log.AddType(\"areaRemove\", function(client, name)\n\treturn string.format(\"%s has removed area \\\"%s\\\".\", client:Name(), tostring(name))\nend)\n\nlocal function SortVector(first, second)\n\treturn Vector(math.min(first.x, second.x), math.min(first.y, second.y), math.min(first.z, second.z)),\n\t\tVector(math.max(first.x, second.x), math.max(first.y, second.y), math.max(first.z, second.z))\nend\n\nfunction ix.area.Create(name, type, startPosition, endPosition, bNoReplicate, properties)\n\tlocal min, max = SortVector(startPosition, endPosition)\n\n\tix.area.stored[name] = {\n\t\ttype = type or \"area\",\n\t\tstartPosition = min,\n\t\tendPosition = max,\n\t\tbNoReplicate = bNoReplicate,\n\t\tproperties = properties\n\t}\n\n\t-- network to clients if needed\n\tif (!bNoReplicate) then\n\t\tnet.Start(\"ixAreaAdd\")\n\t\t\tnet.WriteString(name)\n\t\t\tnet.WriteString(type)\n\t\t\tnet.WriteVector(startPosition)\n\t\t\tnet.WriteVector(endPosition)\n\t\t\tnet.WriteTable(properties)\n\t\tnet.Broadcast()\n\tend\nend\n\nfunction ix.area.Remove(name, bNoReplicate)\n\tix.area.stored[name] = nil\n\n\t-- network to clients if needed\n\tif (!bNoReplicate) then\n\t\tnet.Start(\"ixAreaRemove\")\n\t\t\tnet.WriteString(name)\n\t\tnet.Broadcast()\n\tend\nend\n"
  },
  {
    "path": "plugins/chatbox/derma/cl_chatbox.lua",
    "content": "\nlocal PLUGIN = PLUGIN\n\nlocal animationTime = 0.5\nlocal chatBorder = 32\nlocal sizingBorder = 20\nlocal maxChatEntries = 100\n\n-- called when a markup object should paint its text\nlocal function PaintMarkupOverride(text, font, x, y, color, alignX, alignY, alpha)\n\talpha = alpha or 255\n\n\tif (ix.option.Get(\"chatOutline\", false)) then\n\t\t-- outlined background for even more visibility\n\t\tdraw.SimpleTextOutlined(text, font, x, y, ColorAlpha(color, alpha), alignX, alignY, 1, Color(0, 0, 0, alpha))\n\telse\n\t\t-- background for easier reading\n\t\tsurface.SetTextPos(x + 1, y + 1)\n\t\tsurface.SetTextColor(0, 0, 0, alpha)\n\t\tsurface.SetFont(font)\n\t\tsurface.DrawText(text)\n\n\t\tsurface.SetTextPos(x, y)\n\t\tsurface.SetTextColor(color.r, color.g, color.b, alpha)\n\t\tsurface.SetFont(font)\n\t\tsurface.DrawText(text)\n\tend\nend\n\n-- chat message\nlocal PANEL = {}\n\nAccessorFunc(PANEL, \"fadeDelay\", \"FadeDelay\", FORCE_NUMBER)\nAccessorFunc(PANEL, \"fadeDuration\", \"FadeDuration\", FORCE_NUMBER)\n\nfunction PANEL:Init()\n\tself.text = \"\"\n\tself.alpha = 255\n\tself.fadeDelay = 15\n\tself.fadeDuration = 5\nend\n\nfunction PANEL:SetMarkup(text)\n\tself.text = text\n\n\tself.markup = ix.markup.Parse(self.text, self:GetWide())\n\tself.markup.onDrawText = PaintMarkupOverride\n\n\tself:SetTall(self.markup:GetHeight())\n\n\ttimer.Simple(self.fadeDelay, function()\n\t\tif (!IsValid(self)) then\n\t\t\treturn\n\t\tend\n\n\t\tself:CreateAnimation(self.fadeDuration, {\n\t\t\tindex = 3,\n\t\t\ttarget = {alpha = 0}\n\t\t})\n\tend)\nend\n\nfunction PANEL:PerformLayout(width, height)\n\tif ((IsValid(ix.gui.chat) and ix.gui.chat.bSizing) or width == self.markup:GetWidth()) then\n\t\treturn\n\tend\n\n\tself.markup = ix.markup.Parse(self.text, width)\n\tself.markup.onDrawText = PaintMarkupOverride\n\n\tself:SetTall(self.markup:GetHeight())\nend\n\nfunction PANEL:Paint(width, height)\n\tlocal newAlpha\n\n\t-- we'll want to hide the chat while some important menus are open\n\tif (IsValid(ix.gui.characterMenu)) then\n\t\tnewAlpha = math.min(255 - ix.gui.characterMenu.currentAlpha, self.alpha)\n\telseif (IsValid(ix.gui.menu)) then\n\t\tnewAlpha = math.min(255 - ix.gui.menu.currentAlpha, self.alpha)\n\telseif (ix.gui.chat:GetActive()) then\n\t\tnewAlpha = math.max(ix.gui.chat.alpha, self.alpha)\n\telse\n\t\tnewAlpha = self.alpha\n\tend\n\n\tif (newAlpha < 1) then\n\t\treturn\n\tend\n\n\tself.markup:draw(0, 0, nil, nil, newAlpha)\nend\n\nvgui.Register(\"ixChatMessage\", PANEL, \"Panel\")\n\n-- chatbox tab button\nPANEL = {}\n\nAccessorFunc(PANEL, \"bActive\", \"Active\", FORCE_BOOL)\nAccessorFunc(PANEL, \"bUnread\", \"Unread\", FORCE_BOOL)\n\nfunction PANEL:Init()\n\tself:SetFont(\"ixChatFont\")\n\tself:SetContentAlignment(5)\n\n\tself.unreadAlpha = 0\nend\n\nfunction PANEL:SetUnread(bValue)\n\tself.bUnread = bValue\n\n\tself:CreateAnimation(animationTime, {\n\t\tindex = 4,\n\t\ttarget = {unreadAlpha = bValue and 1 or 0},\n\t\teasing = \"outQuint\"\n\t})\nend\n\nfunction PANEL:SizeToContents()\n\tlocal width, height = self:GetContentSize()\n\tself:SetSize(width + 12, height + 6)\nend\n\nfunction PANEL:Paint(width, height)\n\tderma.SkinFunc(\"PaintChatboxTabButton\", self, width, height)\nend\n\nvgui.Register(\"ixChatboxTabButton\", PANEL, \"DButton\")\n\n-- chatbox tab panel\n-- holds all tab buttons and corresponding history panels\nPANEL = {}\n\nfunction PANEL:Init()\n\t-- holds all tab buttons\n\tself.buttons = self:Add(\"Panel\")\n\tself.buttons:Dock(TOP)\n\tself.buttons:DockPadding(1, 1, 0, 0)\n\tself.buttons.OnMousePressed = ix.util.Bind(ix.gui.chat, ix.gui.chat.OnMousePressed) -- we want mouse events to fall through\n\tself.buttons.OnMouseReleased = ix.util.Bind(ix.gui.chat, ix.gui.chat.OnMouseReleased)\n\tself.buttons.Paint = function(_, width, height)\n\t\tderma.SkinFunc(\"PaintChatboxTabs\", self, width, height)\n\tend\n\n\tself.tabs = {}\nend\n\nfunction PANEL:GetTabs()\n\treturn self.tabs\nend\n\nfunction PANEL:AddTab(id, filter)\n\tlocal button = self.buttons:Add(\"ixChatboxTabButton\")\n\tbutton:Dock(LEFT)\n\tbutton:SetText(id) -- display name is also the ID\n\tbutton:SetActive(false)\n\tbutton:SetMouseInputEnabled(true)\n\tbutton:SizeToContents()\n\n\tbutton.DoClick = function(this)\n\t\tself:SetActiveTab(this:GetText())\n\tend\n\n\tlocal panel = self:Add(\"ixChatboxHistory\")\n\tpanel:SetButton(button)\n\tpanel:SetID(id)\n\tpanel:Dock(FILL)\n\tpanel:SetVisible(false)\n\tpanel:SetFilter(filter or {})\n\n\tbutton.DoRightClick = function(this)\n\t\tix.gui.chat:OnTabRightClick(this, panel, panel:GetID())\n\tend\n\n\tself.tabs[id] = panel\n\treturn panel\nend\n\nfunction PANEL:RemoveTab(id)\n\tlocal tab = self.tabs[id]\n\n\tif (!tab) then\n\t\treturn\n\tend\n\n\ttab:GetButton():Remove()\n\ttab:Remove()\n\n\tself.tabs[id] = nil\n\n\t-- add default tab if we don't have any tabs left\n\tif (table.IsEmpty(self.tabs)) then\n\t\tself:AddTab(L(\"chat\"), {})\n\t\tself:SetActiveTab(L(\"chat\"))\n\telseif (id == self:GetActiveTabID()) then\n\t\t-- set a different active tab if we've removed a tab that is currently active\n\t\tself:SetActiveTab(next(self.tabs))\n\tend\nend\n\nfunction PANEL:RenameTab(id, newID)\n\tlocal tab = self.tabs[id]\n\n\tif (!tab) then\n\t\treturn\n\tend\n\n\ttab:GetButton():SetText(newID)\n\ttab:GetButton():SizeToContents()\n\n\ttab:SetID(newID)\n\n\tself.tabs[id] = nil\n\tself.tabs[newID] = tab\n\n\tif (id == self:GetActiveTabID()) then\n\t\tself:SetActiveTab(newID)\n\tend\nend\n\nfunction PANEL:SetActiveTab(id)\n\tlocal tab = self.tabs[id]\n\n\tif (!tab) then\n\t\terror(\"attempted to set non-existent active tab\")\n\tend\n\n\tfor _, v in ipairs(self.buttons:GetChildren()) do\n\t\tv:SetActive(v:GetText() == id)\n\tend\n\n\tfor _, v in pairs(self.tabs) do\n\t\tv:SetVisible(v:GetID() == id)\n\tend\n\n\ttab:GetButton():SetUnread(false)\n\n\tself.activeTab = id\n\tself:OnTabChanged(tab)\nend\n\nfunction PANEL:GetActiveTabID()\n\treturn self.activeTab\nend\n\nfunction PANEL:GetActiveTab()\n\treturn self.tabs[self.activeTab]\nend\n\n-- called when the active tab is changed\n-- `panel` is the corresponding history panel\nfunction PANEL:OnTabChanged(panel)\nend\n\nvgui.Register(\"ixChatboxTabs\", PANEL, \"EditablePanel\")\n\n-- chatbox history panel\n-- holds individual messages in a scrollable panel\nPANEL = {}\n\nAccessorFunc(PANEL, \"filter\", \"Filter\") -- blacklist of message classes\nAccessorFunc(PANEL, \"id\", \"ID\", FORCE_STRING)\nAccessorFunc(PANEL, \"button\", \"Button\") -- button panel that this panel corresponds to\n\nfunction PANEL:Init()\n\tself:DockMargin(4, 2, 4, 4) -- smaller top margin to help blend tab button/history panel transition\n\tself:SetPaintedManually(true)\n\n\tlocal bar = self:GetVBar()\n\tbar:SetWide(0)\n\n\tself.entries = {}\n\tself.filter = {}\nend\n\nDEFINE_BASECLASS(\"Panel\") -- DScrollPanel doesn't have SetVisible member\nfunction PANEL:SetVisible(bState)\n\tself:GetCanvas():SetVisible(bState)\n\tBaseClass.SetVisible(self, bState)\nend\n\nDEFINE_BASECLASS(\"DScrollPanel\")\nfunction PANEL:PerformLayoutInternal()\n\tlocal bar = self:GetVBar()\n\tlocal bScroll = !ix.gui.chat:GetActive() or bar.Scroll == bar.CanvasSize -- only scroll when we're not at the bottom/inactive\n\n\tBaseClass.PerformLayoutInternal(self)\n\n\tif (bScroll) then\n\t\tself:ScrollToBottom()\n\tend\nend\n\nfunction PANEL:ScrollToBottom()\n\tlocal bar = self:GetVBar()\n\tbar:SetScroll(bar.CanvasSize)\nend\n\n-- adds a line of text as described by its elements\nfunction PANEL:AddLine(elements, bShouldScroll)\n\t-- table.concat is faster than regular string concatenation where there are lots of strings to concatenate\n\tlocal buffer = {\n\t\t\"<font=ixChatFont>\"\n\t}\n\n\tif (ix.option.Get(\"chatTimestamps\", false)) then\n\t\tbuffer[#buffer + 1] = \"<color=150,150,150>(\"\n\n\t\tif (ix.option.Get(\"24hourTime\", false)) then\n\t\t\tbuffer[#buffer + 1] = os.date(\"%H:%M\")\n\t\telse\n\t\t\tbuffer[#buffer + 1] = os.date(\"%I:%M %p\")\n\t\tend\n\n\t\tbuffer[#buffer + 1] = \") \"\n\tend\n\n\tif (CHAT_CLASS) then\n\t\tbuffer[#buffer + 1] = \"<font=\"\n\t\tbuffer[#buffer + 1] = CHAT_CLASS.font or \"ixChatFont\"\n\t\tbuffer[#buffer + 1] = \">\"\n\tend\n\n\tfor _, v in ipairs(elements) do\n\t\tif (type(v) == \"IMaterial\") then\n\t\t\tlocal texture = v:GetName()\n\n\t\t\tif (texture) then\n\t\t\t\tbuffer[#buffer + 1] = string.format(\"<img=%s,%dx%d> \", texture, v:Width(), v:Height())\n\t\t\tend\n\t\telseif (istable(v) and v.r and v.g and v.b) then\n\t\t\tbuffer[#buffer + 1] = string.format(\"<color=%d,%d,%d>\", v.r, v.g, v.b)\n\t\telseif (type(v) == \"Player\") then\n\t\t\tlocal color = team.GetColor(v:Team())\n\n\t\t\tbuffer[#buffer + 1] = string.format(\"<color=%d,%d,%d>%s\", color.r, color.g, color.b,\n\t\t\t\tv:GetName():gsub(\"<\", \"&lt;\"):gsub(\">\", \"&gt;\"))\n\t\telse\n\t\t\tbuffer[#buffer + 1] = tostring(v):gsub(\"<\", \"&lt;\"):gsub(\">\", \"&gt;\"):gsub(\"%b**\", function(value)\n\t\t\t\tlocal inner = value:utf8sub(2, -2)\n\n\t\t\t\tif (inner:find(\"%S\")) then\n\t\t\t\t\treturn \"<font=ixChatFontItalics>\" .. value:utf8sub(2, -2) .. \"</font>\"\n\t\t\t\tend\n\t\t\tend)\n\t\tend\n\tend\n\n\tlocal panel = self:Add(\"ixChatMessage\")\n\tpanel:Dock(TOP)\n\tpanel:InvalidateParent(true)\n\tpanel:SetMarkup(table.concat(buffer))\n\n\tif (#self.entries >= maxChatEntries) then\n\t\tlocal oldPanel = table.remove(self.entries, 1)\n\n\t\tif (IsValid(oldPanel)) then\n\t\t\toldPanel:Remove()\n\t\tend\n\tend\n\n\tself.entries[#self.entries + 1] = panel\n\treturn panel\nend\n\nvgui.Register(\"ixChatboxHistory\", PANEL, \"DScrollPanel\")\n\nPANEL = {}\nDEFINE_BASECLASS(\"DTextEntry\")\n\nfunction PANEL:Init()\n\tself:SetFont(\"ixChatFont\")\n\tself:SetUpdateOnType(true)\n\tself:SetHistoryEnabled(true)\n\n\tself.History = ix.chat.history\n\tself.m_bLoseFocusOnClickAway = false\nend\n\nfunction PANEL:SetFont(font)\n\tBaseClass.SetFont(self, font)\n\n\tsurface.SetFont(font)\n\tlocal _, height = surface.GetTextSize(\"W@\")\n\n\tself:SetTall(height + 8)\nend\n\nfunction PANEL:AllowInput(newCharacter)\n\tlocal text = self:GetText()\n\tlocal maxLength = ix.config.Get(\"chatMax\")\n\n\t-- we can't check for the proper length using utf-8 since AllowInput is called for single bytes instead of full characters\n\tif (string.len(text .. newCharacter) > maxLength) then\n\t\tsurface.PlaySound(\"common/talk.wav\")\n\t\treturn true\n\tend\nend\n\nfunction PANEL:Think()\n\tlocal text = self:GetText()\n\tlocal maxLength = ix.config.Get(\"chatMax\", 256)\n\n\tif (text:utf8len() > maxLength) then\n\t\tlocal newText = text:utf8sub(0, maxLength)\n\n\t\tself:SetText(newText)\n\t\tself:SetCaretPos(newText:utf8len())\n\tend\nend\n\nfunction PANEL:Paint(width, height)\n\tderma.SkinFunc(\"PaintChatboxEntry\", self, width, height)\nend\n\nvgui.Register(\"ixChatboxEntry\", PANEL, \"DTextEntry\")\n\n-- chatbox additional command info panel\nPANEL = {}\n\nAccessorFunc(PANEL, \"text\", \"Text\", FORCE_STRING)\nAccessorFunc(PANEL, \"padding\", \"Padding\", FORCE_NUMBER)\nAccessorFunc(PANEL, \"backgroundColor\", \"BackgroundColor\")\nAccessorFunc(PANEL, \"textColor\", \"TextColor\")\n\nfunction PANEL:Init()\n\tself.text = \"\"\n\tself.padding = 4\n\tself.currentWidth = 0\n\tself.currentMargin = 0\n\tself.backgroundColor = ix.config.Get(\"color\")\n\tself.textColor = color_white\n\n\tself:SetWide(0)\n\tself:DockMargin(0, 0, 0, 0)\nend\n\nfunction PANEL:SetText(text)\n\tself:SetVisible(true)\n\n\tif (!isstring(text) or text == \"\") then\n\t\tself:CreateAnimation(animationTime, {\n\t\t\tindex = 9,\n\t\t\teasing = \"outQuint\",\n\t\t\ttarget = {\n\t\t\t\tcurrentWidth = 0,\n\t\t\t\tcurrentMargin = 0\n\t\t\t},\n\n\t\t\tThink = function(animation, panel)\n\t\t\t\tpanel:SetWide(panel.currentWidth)\n\t\t\t\tpanel:DockMargin(0, 0, panel.currentMargin, 0)\n\t\t\tend,\n\n\t\t\tOnComplete = function(animation, panel)\n\t\t\t\tpanel:SetVisible(false)\n\t\t\t\tself.text = \"\"\n\t\t\tend\n\t\t})\n\telse\n\t\ttext = tostring(text)\n\n\t\tsurface.SetFont(\"ixChatFont\")\n\t\tlocal textWidth = surface.GetTextSize(text)\n\n\t\tself:CreateAnimation(animationTime, {\n\t\t\tindex = 9,\n\t\t\teasing = \"outQuint\",\n\t\t\ttarget = {\n\t\t\t\tcurrentWidth = textWidth + self.padding * 2,\n\t\t\t\tcurrentMargin = 4\n\t\t\t},\n\n\t\t\tThink = function(animation, panel)\n\t\t\t\tpanel:SetWide(panel.currentWidth)\n\t\t\t\tpanel:DockMargin(0, 0, panel.currentMargin, 0)\n\t\t\tend,\n\t\t})\n\n\t\tself.text = text\n\tend\nend\n\nfunction PANEL:Paint(width, height)\n\tderma.SkinFunc(\"DrawChatboxPrefixBox\", self, width, height)\n\n\tsurface.SetFont(\"ixChatFont\")\n\tlocal textWidth, textHeight = surface.GetTextSize(self.text)\n\n\tsurface.SetTextColor(self.textColor)\n\tsurface.SetTextPos(width * 0.5 - textWidth * 0.5, height * 0.5 - textHeight * 0.5)\n\tsurface.DrawText(self.text)\nend\n\nvgui.Register(\"ixChatboxPrefix\", PANEL, \"Panel\")\n\n-- chatbox command preview panel\nPANEL = {}\nDEFINE_BASECLASS(\"Panel\")\n\nAccessorFunc(PANEL, \"targetHeight\", \"TargetHeight\", FORCE_NUMBER)\nAccessorFunc(PANEL, \"command\", \"Command\", FORCE_STRING)\n\nfunction PANEL:Init()\n\tself:SetTall(0)\n\tself:SetVisible(false, true)\n\n\tself.height = 0\n\tself.targetHeight = 16\n\tself.margin = 0\n\n\tself.command = \"\"\nend\n\nfunction PANEL:SetCommand(command)\n\t-- if we're setting it to an empty command, then we'll hold the reference to the old command table to render it for the\n\t-- fade out animation\n\tif (command == \"\") then\n\t\tself.command = \"\"\n\t\tix.chat.currentCommand = \"\"\n\n\t\treturn\n\tend\n\n\tlocal commandTable = ix.command.list[command]\n\n\tif (!commandTable) then\n\t\treturn\n\tend\n\n\tself.command = command\n\tself.commandTable = commandTable\n\tself.arguments = {}\n\n\tix.chat.currentCommand = command:lower()\nend\n\nfunction PANEL:UpdateArguments(text)\n\tif (self.command == \"\") then\n\t\tix.chat.currentArguments = {}\n\t\treturn\n\tend\n\n\tlocal commandName = text:match(\"(/(%w+)%s)\") or self.command -- we could be using a chat class prefix and not a proper command\n\tlocal givenArguments = ix.command.ExtractArgs(text:utf8sub(commandName:utf8len()))\n\tlocal commandArguments = self.commandTable.arguments or {}\n\tlocal arguments = {}\n\n\t-- we want to concat any text types so they show up as one argument at the end of the list, this is so the argument\n\t-- highlighting is accurate since ExtractArgs will not account because it has no type context\n\tfor k, v in ipairs(givenArguments) do\n\t\tif (k == #commandArguments) then\n\t\t\targuments[#arguments + 1] = table.concat(givenArguments, \" \", k)\n\t\t\tbreak\n\t\tend\n\n\t\targuments[#arguments + 1] = v\n\tend\n\n\tself.arguments = arguments\n\tix.chat.currentArguments = table.Copy(arguments)\nend\n\n-- returns the target SetVisible value\nfunction PANEL:IsOpen()\n\treturn self.bOpen\nend\n\nfunction PANEL:SetVisible(bValue, bForce)\n\tif (bForce) then\n\t\tBaseClass.SetVisible(self, bValue)\n\t\treturn\n\tend\n\n\tBaseClass.SetVisible(self, true) -- make sure this panel is visible during animation\n\tself.bOpen = bValue\n\n\tself:CreateAnimation(animationTime * 0.5, {\n\t\tindex = 5,\n\t\ttarget = {\n\t\t\theight = bValue and self.targetHeight or 0,\n\t\t\tmargin = bValue and 4 or 0\n\t\t},\n\t\teasing = \"outQuint\",\n\n\t\tThink = function(animation, panel)\n\t\t\tpanel:SetTall(math.ceil(panel.height))\n\t\t\tpanel:DockMargin(4, 0, 4, math.ceil(panel.margin))\n\t\tend,\n\n\t\tOnComplete = function(animation, panel)\n\t\t\tBaseClass.SetVisible(panel, bValue)\n\t\tend\n\t})\nend\n\nfunction PANEL:Paint(width, height)\n\tlocal command = self.commandTable\n\n\tif (!command) then\n\t\treturn\n\tend\n\n\tlocal color = ix.config.Get(\"color\")\n\tsurface.SetFont(\"ixChatFont\")\n\n\t-- command name\n\tlocal x = derma.SkinFunc(\"DrawChatboxPreviewBox\", 0, 0, \"/\" .. command.name) + 6\n\n\t-- command arguments\n\tif (istable(command.arguments)) then\n\t\tfor k, v in ipairs(command.arguments) do\n\t\t\tlocal bOptional = bit.band(v, ix.type.optional) > 0\n\t\t\tlocal type = bOptional and bit.bxor(v, ix.type.optional) or v\n\n\t\t\tx = x + derma.SkinFunc(\n\t\t\t\t\"DrawChatboxPreviewBox\", x, 0,\n\t\t\t\t-- draw text in format of <name: type> or [name: type] if it's optional\n\t\t\t\tstring.format(bOptional and \"[%s: %s]\" or \"<%s: %s>\", command.argumentNames[k], ix.type[type]),\n\t\t\t\t-- fill in the color for arguments that are before the one the user is currently typing, otherwise draw a faded\n\t\t\t\t-- color instead (optional arguments will not have any background color unless it's been filled out by user)\n\t\t\t\t(k <= #self.arguments) and color or (bOptional and Color(0, 0, 0, 66) or ColorAlpha(color, 100))\n\t\t\t) + 6\n\t\tend\n\tend\nend\n\nvgui.Register(\"ixChatboxPreview\", PANEL, \"Panel\")\n\n-- chatbox autocomplete panel\n-- holds and displays similar commands based on the textentry\nPANEL = {}\nDEFINE_BASECLASS(\"Panel\")\n\nAccessorFunc(PANEL, \"maxEntries\", \"MaxEntries\", FORCE_NUMBER)\n\nfunction PANEL:Init()\n\tself:SetVisible(false, true)\n\tself:SetMouseInputEnabled(true)\n\n\tself.maxEntries = 20\n\tself.currentAlpha = 0\n\n\tself.commandIndex = 0 -- currently selected entry in command list\n\tself.commands = {}\n\tself.commandPanels = {}\nend\n\nfunction PANEL:GetCommands()\n\treturn self.commands\nend\n\nfunction PANEL:IsOpen()\n\treturn self.bOpen\nend\n\nfunction PANEL:SetVisible(bValue, bForce)\n\tif (bForce) then\n\t\tBaseClass.SetVisible(self, bValue)\n\t\treturn\n\tend\n\n\tBaseClass.SetVisible(self, true) -- make sure this panel is visible during animation\n\tself.bOpen = bValue\n\n\tself:CreateAnimation(animationTime, {\n\t\tindex = 6,\n\t\ttarget = {\n\t\t\tcurrentAlpha = bValue and 255 or 0\n\t\t},\n\t\teasing = \"outQuint\",\n\n\t\tThink = function(animation, panel)\n\t\t\tpanel:SetAlpha(math.ceil(panel.currentAlpha))\n\t\tend,\n\n\t\tOnComplete = function(animation, panel)\n\t\t\tBaseClass.SetVisible(panel, bValue)\n\n\t\t\tif (!bValue) then\n\t\t\t\tself.commands = {}\n\t\t\tend\n\t\tend\n\t})\nend\n\nfunction PANEL:Update(text)\n\tlocal commands = ix.command.FindAll(text, true, true, true)\n\n\tself.commandIndex = 0 -- reset the command index because the command list could be different\n\tself.commands = {}\n\n\tfor _, v in ipairs(self.commandPanels) do\n\t\tv:Remove()\n\tend\n\n\tself.commandPanels = {}\n\n\t-- manually loop over the found commands so we can ignore commands the user doesn't have access to\n\tlocal i = 1\n\tlocal bSelected -- just to make sure we don't reset it during the loop for whatever reason\n\n\tfor _, v in ipairs(commands) do\n\t\t-- @todo chat classes aren't checked since they're done through the class's OnCanSay callback\n\t\tif (v.OnCheckAccess and !v:OnCheckAccess(LocalPlayer())) then\n\t\t\tcontinue\n\t\tend\n\n\t\tlocal panel = self:Add(\"ixChatboxAutocompleteEntry\")\n\t\tpanel:SetCommand(v)\n\n\t\tif (!bSelected and text:utf8lower():utf8sub(1, v.uniqueID:utf8len()) == v.uniqueID) then\n\t\t\tpanel:SetHighlighted(true)\n\n\t\t\tself.commandIndex = i\n\t\t\tbSelected = true\n\t\tend\n\n\t\tself.commandPanels[i] = panel\n\t\tself.commands[i] = v\n\n\t\tif (i == self.maxEntries) then\n\t\t\tbreak\n\t\tend\n\n\t\ti = i + 1\n\tend\nend\n\n-- selects the next entry in the autocomplete if possible and returns the text that should replace the textentry\nfunction PANEL:SelectNext()\n\t-- wrap back to beginning if we're past the end\n\tif (self.commandIndex == #self.commands) then\n\t\tself.commandIndex = 1\n\telse\n\t\tself.commandIndex = self.commandIndex + 1\n\tend\n\n\tfor k, v in ipairs(self.commandPanels) do\n\t\tif (k == self.commandIndex) then\n\t\t\tv:SetHighlighted(true)\n\t\t\tself:ScrollToChild(v)\n\t\telse\n\t\t\tv:SetHighlighted(false)\n\t\tend\n\tend\n\n\treturn \"/\" .. self.commands[self.commandIndex].uniqueID\nend\n\nfunction PANEL:Paint(width, height)\n\tix.util.DrawBlur(self)\n\n\tsurface.SetDrawColor(0, 0, 0, 200)\n\tsurface.DrawRect(0, 0, width, height)\nend\n\nvgui.Register(\"ixChatboxAutocomplete\", PANEL, \"DScrollPanel\")\n\n-- autocomplete entry\nPANEL = {}\n\nAccessorFunc(PANEL, \"bSelected\", \"Highlighted\", FORCE_BOOL)\n\nfunction PANEL:Init()\n\tself:Dock(TOP)\n\n\tself.name = self:Add(\"DLabel\")\n\tself.name:Dock(TOP)\n\tself.name:DockMargin(4, 4, 0, 0)\n\tself.name:SetContentAlignment(4)\n\tself.name:SetFont(\"ixChatFont\")\n\tself.name:SetTextColor(ix.config.Get(\"color\"))\n\tself.name:SetExpensiveShadow(1, color_black)\n\n\tself.description = self:Add(\"DLabel\")\n\tself.description:Dock(BOTTOM)\n\tself.description:DockMargin(4, 4, 0, 4)\n\tself.description:SetContentAlignment(4)\n\tself.description:SetFont(\"ixChatFont\")\n\tself.description:SetTextColor(color_white)\n\tself.description:SetExpensiveShadow(1, color_black)\n\n\tself.highlightAlpha = 0\nend\n\nfunction PANEL:SetHighlighted(bValue)\n\tself:CreateAnimation(animationTime * 2, {\n\t\tindex = 7,\n\t\ttarget = {highlightAlpha = bValue and 1 or 0},\n\t\teasing = \"outQuint\"\n\t})\n\n\tself.bHighlighted = true\nend\n\nfunction PANEL:SetCommand(command)\n\tlocal description = command:GetDescription()\n\n\tself.name:SetText(\"/\" .. command.name)\n\n\tif (description and description != \"\") then\n\t\tself.description:SetText(command:GetDescription())\n\telse\n\t\tself.description:SetVisible(false)\n\tend\n\n\tself:SizeToContents()\n\tself.command = command\nend\n\nfunction PANEL:SizeToContents()\n\tlocal bDescriptionVisible = self.description:IsVisible()\n\tlocal _, height = self.name:GetContentSize()\n\n\tself.name:SetTall(height)\n\n\tif (bDescriptionVisible) then\n\t\t_, height = self.description:GetContentSize()\n\t\tself.description:SetTall(height)\n\telse\n\t\tself.description:SetTall(0)\n\tend\n\n\tself:SetTall(self.name:GetTall() + self.description:GetTall() + (bDescriptionVisible and 12 or 8))\nend\n\nfunction PANEL:Paint(width, height)\n\tderma.SkinFunc(\"PaintChatboxAutocompleteEntry\", self, width, height)\nend\n\nvgui.Register(\"ixChatboxAutocompleteEntry\", PANEL, \"Panel\")\n\n-- main chatbox panel\n-- this contains the text entry, tab sheets, and callbacks for other panel events\nPANEL = {}\n\nAccessorFunc(PANEL, \"bActive\", \"Active\", FORCE_BOOL)\n\nfunction PANEL:Init()\n\tix.gui.chat = self\n\n\tself:SetSize(self:GetDefaultSize())\n\tself:SetPos(self:GetDefaultPosition())\n\n\tlocal entryPanel = self:Add(\"Panel\")\n\tentryPanel:SetZPos(1)\n\tentryPanel:Dock(BOTTOM)\n\tentryPanel:DockMargin(4, 0, 4, 4)\n\n\tself.entry = entryPanel:Add(\"ixChatboxEntry\")\n\tself.entry:Dock(FILL)\n\tself.entry.OnValueChange = ix.util.Bind(self, self.OnTextChanged)\n\tself.entry.OnKeyCodeTyped = ix.util.Bind(self, self.OnKeyCodeTyped)\n\tself.entry.OnEnter = ix.util.Bind(self, self.OnMessageSent)\n\n\tself.prefix = entryPanel:Add(\"ixChatboxPrefix\")\n\tself.prefix:Dock(LEFT)\n\n\tself.preview = self:Add(\"ixChatboxPreview\")\n\tself.preview:SetZPos(2) -- ensure the preview is docked above the text entry\n\tself.preview:Dock(BOTTOM)\n\tself.preview:SetTargetHeight(self.entry:GetTall())\n\n\tself.tabs = self:Add(\"ixChatboxTabs\")\n\tself.tabs:Dock(FILL)\n\tself.tabs.OnTabChanged = ix.util.Bind(self, self.OnTabChanged)\n\n\tself.autocomplete = self.tabs:Add(\"ixChatboxAutocomplete\")\n\tself.autocomplete:Dock(FILL)\n\tself.autocomplete:DockMargin(4, 3, 4, 4) -- top margin is 3 to account for tab 1px border\n\tself.autocomplete:SetZPos(3)\n\n\tself.alpha = 0\n\tself:SetActive(false)\n\n\t-- luacheck: globals chat\n\tchat.GetChatBoxPos = function()\n\t\treturn self:GetPos()\n\tend\n\n\tchat.GetChatBoxSize = function()\n\t\treturn self:GetSize()\n\tend\nend\n\nfunction PANEL:GetDefaultSize()\n\treturn ScrW() * 0.4, ScrH() * 0.375\nend\n\nfunction PANEL:GetDefaultPosition()\n\treturn chatBorder, ScrH() - self:GetTall() - chatBorder\nend\n\nDEFINE_BASECLASS(\"Panel\")\nfunction PANEL:SetAlpha(amount, duration)\n\tself:CreateAnimation(duration or animationTime, {\n\t\tindex = 1,\n\t\ttarget = {alpha = amount},\n\t\teasing = \"outQuint\",\n\n\t\tThink = function(animation, panel)\n\t\t\tBaseClass.SetAlpha(panel, panel.alpha)\n\t\tend\n\t})\nend\n\nfunction PANEL:SizingInBounds()\n\tlocal screenX, screenY = self:LocalToScreen(0, 0)\n\tlocal mouseX, mouseY = gui.MousePos()\n\n\treturn mouseX > screenX + self:GetWide() - sizingBorder and mouseY > screenY + self:GetTall() - sizingBorder\nend\n\nfunction PANEL:DraggingInBounds()\n\tlocal _, screenY = self:LocalToScreen(0, 0)\n\tlocal mouseY = gui.MouseY()\n\n\treturn mouseY > screenY and mouseY < screenY + self.tabs.buttons:GetTall()\nend\n\nfunction PANEL:SetActive(bActive)\n\tif (bActive) then\n\t\tself:SetAlpha(255)\n\t\tself:MakePopup()\n\t\tself.entry:RequestFocus()\n\n\t\tinput.SetCursorPos(self:LocalToScreen(-1, -1))\n\n\t\thook.Run(\"StartChat\")\n\t\tself.prefix:SetText(hook.Run(\"GetChatPrefixInfo\", \"\"))\n\telse\n\t\t-- make sure we aren't still sizing/dragging anything\n\t\tif (self.bSizing or self.DragOffset) then\n\t\t\tself:OnMouseReleased(MOUSE_LEFT)\n\t\tend\n\n\t\tself:SetAlpha(0)\n\t\tself:SetMouseInputEnabled(false)\n\t\tself:SetKeyboardInputEnabled(false)\n\n\t\tself.autocomplete:SetVisible(false)\n\t\tself.preview:SetVisible(false)\n\t\tself.entry:SetText(\"\")\n\t\tself.preview:SetCommand(\"\")\n\t\tself.prefix:SetText(hook.Run(\"GetChatPrefixInfo\", \"\"))\n\n\t\tCloseDermaMenus()\n\t\tgui.EnableScreenClicker(false)\n\n\t\thook.Run(\"FinishChat\")\n\tend\n\n\tlocal tab = self.tabs:GetActiveTab()\n\n\tif (tab) then\n\t\t-- we'll scroll to bottom even if we're opening since the SetVisible for the textentry will shift things a bit\n\t\ttab:ScrollToBottom()\n\tend\n\n\tself.bActive = tobool(bActive)\nend\n\nfunction PANEL:SetupTabs(tabs)\n\tif (!tabs or table.IsEmpty(tabs)) then\n\t\tself.tabs:AddTab(L(\"chat\"), {})\n\t\tself.tabs:SetActiveTab(L(\"chat\"))\n\n\t\treturn\n\tend\n\n\tfor id, filter in pairs(tabs) do\n\t\tself.tabs:AddTab(id, filter)\n\tend\n\n\tself.tabs:SetActiveTab(next(tabs))\nend\n\nfunction PANEL:SetupPosition(info)\n\tlocal x, y, width, height\n\n\tif (!istable(info)) then\n\t\tx, y = self:GetDefaultPosition()\n\t\twidth, height = self:GetDefaultSize()\n\telse\n\t\t-- screen size may have changed so we'll need to clamp the values\n\t\twidth = math.Clamp(info[3], 32, ScrW() - chatBorder * 2)\n\t\theight = math.Clamp(info[4], 32, ScrH() - chatBorder * 2)\n\t\tx = math.Clamp(info[1], 0, ScrW() - width)\n\t\ty = math.Clamp(info[2], 0, ScrH() - height)\n\tend\n\n\tself:SetSize(width, height)\n\tself:SetPos(x, y)\n\n\tPLUGIN:SavePosition()\nend\n\nfunction PANEL:OnMousePressed(key)\n\tif (key == MOUSE_RIGHT) then\n\t\tlocal menu = DermaMenu()\n\t\t\tmenu:AddOption(L(\"chatNewTab\"), function()\n\t\t\t\tif (IsValid(ix.gui.chatTabCustomize)) then\n\t\t\t\t\tix.gui.chatTabCustomize:Remove()\n\t\t\t\tend\n\n\t\t\t\tlocal panel = vgui.Create(\"ixChatboxTabCustomize\")\n\t\t\t\tpanel.OnTabCreated = ix.util.Bind(self, self.OnTabCreated)\n\t\t\tend)\n\n\t\t\tmenu:AddOption(L(\"chatMarkRead\"), function()\n\t\t\t\tfor _, v in pairs(self.tabs:GetTabs()) do\n\t\t\t\t\tv:GetButton():SetUnread(false)\n\t\t\t\tend\n\t\t\tend)\n\n\t\t\tmenu:AddSpacer()\n\n\t\t\tmenu:AddOption(L(\"chatReset\"), function()\n\t\t\t\tlocal x, y = self:GetDefaultPosition()\n\t\t\t\tlocal width, height = self:GetDefaultSize()\n\n\t\t\t\tself:SetSize(width, height)\n\t\t\t\tself:SetPos(x, y)\n\n\t\t\t\tix.option.Set(\"chatPosition\", \"\")\n\t\t\t\thook.Run(\"ChatboxPositionChanged\", x, y, width, height)\n\t\t\tend)\n\n\t\t\tmenu:AddOption(L(\"chatResetTabs\"), function()\n\t\t\t\tfor id, _ in pairs(self.tabs:GetTabs()) do\n\t\t\t\t\tself.tabs:RemoveTab(id)\n\t\t\t\tend\n\n\t\t\t\tix.option.Set(\"chatTabs\", \"\")\n\t\t\tend)\n\t\tmenu:Open()\n\t\tmenu:MakePopup()\n\n\t\treturn\n\tend\n\n\tif (key != MOUSE_LEFT) then\n\t\treturn\n\tend\n\n\t-- capture the mouse if we're in bounds for sizing this panel\n\tif (self:SizingInBounds()) then\n\t\tself.bSizing = true\n\t\tself:MouseCapture(true)\n\telseif (self:DraggingInBounds()) then\n\t\tlocal mouseX, mouseY = self:ScreenToLocal(gui.MousePos())\n\n\t\t-- mouse offset relative to the panel\n\t\tself.DragOffset = {mouseX, mouseY}\n\t\tself:MouseCapture(true)\n\tend\nend\n\nfunction PANEL:OnMouseReleased()\n\tself:MouseCapture(false)\n\tself:SetCursor(\"arrow\")\n\n\t-- save new position/size if we were dragging/resizing\n\tif (self.bSizing or self.DragOffset) then\n\t\tPLUGIN:SavePosition()\n\n\t\tself.bSizing = nil\n\t\tself.DragOffset = nil\n\n\t\t-- resize chat messages to fit new width\n\t\tself:InvalidateChildren(true)\n\n\t\tlocal x, y = self:GetPos()\n\t\tlocal width, height = self:GetSize()\n\n\t\thook.Run(\"ChatboxPositionChanged\", x, y, width, height)\n\tend\nend\n\nfunction PANEL:Think()\n\tif (!self.bActive) then\n\t\treturn\n\tend\n\n\tlocal mouseX = math.Clamp(gui.MouseX(), 0, ScrW())\n\tlocal mouseY = math.Clamp(gui.MouseY(), 0, ScrH())\n\n\tif (self.bSizing) then\n\t\tlocal x, y = self:GetPos()\n\t\tlocal width = math.Clamp(mouseX - x, chatBorder, ScrW() - chatBorder * 2)\n\t\tlocal height = math.Clamp(mouseY - y, chatBorder, ScrH() - chatBorder * 2)\n\n\t\tself:SetSize(width, height)\n\t\tself:SetCursor(\"sizenwse\")\n\telseif (self.DragOffset) then\n\t\tlocal x = math.Clamp(mouseX - self.DragOffset[1], 0, ScrW() - self:GetWide())\n\t\tlocal y = math.Clamp(mouseY - self.DragOffset[2], 0, ScrH() - self:GetTall())\n\n\t\tself:SetPos(x, y)\n\telseif (self:SizingInBounds()) then\n\t\tself:SetCursor(\"sizenwse\")\n\telseif (self:DraggingInBounds()) then\n\t\t-- we have to set the cursor on the list panel since that's the actual hovered panel\n\t\tself.tabs.buttons:SetCursor(\"sizeall\")\n\telse\n\t\tself:SetCursor(\"arrow\")\n\tend\nend\n\nfunction PANEL:Paint(width, height)\n\tlocal tab = self.tabs:GetActiveTab()\n\tlocal alpha = self:GetAlpha()\n\n\tderma.SkinFunc(\"PaintChatboxBackground\", self, width, height)\n\n\tif (tab) then\n\t\t-- manually paint active tab since messages handle their own alpha lifetime\n\t\tsurface.SetAlphaMultiplier(1)\n\t\t\ttab:PaintManual()\n\t\tsurface.SetAlphaMultiplier(alpha / 255)\n\tend\n\n\tif (alpha > 0) then\n\t\thook.Run(\"PostChatboxDraw\", width, height, self:GetAlpha())\n\tend\nend\n\n-- get the command of the current chat class in the textentry if possible\nfunction PANEL:GetTextEntryChatClass(text)\n\ttext = text or self.entry:GetText()\n\n\tlocal chatType = ix.chat.Parse(LocalPlayer(), text, true)\n\n\tif (chatType and chatType != \"ic\") then\n\t\t-- OOC is the only one with two slashes as its prefix, so we'll make a special case for it here\n\t\tif (chatType == \"ooc\") then\n\t\t\treturn \"ooc\"\n\t\tend\n\n\t\tlocal class = ix.chat.classes[chatType]\n\n\t\tif (istable(class.prefix)) then\n\t\t\tfor _, v in ipairs(class.prefix) do\n\t\t\t\tif (v:utf8sub(1, 1) == \"/\") then\n\t\t\t\t\treturn v:utf8sub(2):utf8lower()\n\t\t\t\tend\n\t\t\tend\n\t\telseif (class.prefix:utf8sub(1, 1) == \"/\") then\n\t\t\treturn class.prefix:utf8sub(2):utf8lower()\n\t\tend\n\tend\nend\n\n-- chatbox panel hooks\n-- called when the textentry value changes\nfunction PANEL:OnTextChanged(text)\n\thook.Run(\"ChatTextChanged\", text)\n\n\tlocal preview = self.preview\n\tlocal autocomplete = self.autocomplete\n\tlocal chatClassCommand = self:GetTextEntryChatClass(text)\n\n\tself.prefix:SetText(hook.Run(\"GetChatPrefixInfo\", text))\n\n\tif (chatClassCommand) then\n\t\tpreview:SetCommand(chatClassCommand)\n\t\tpreview:SetVisible(true)\n\t\tpreview:UpdateArguments(text)\n\n\t\tautocomplete:SetVisible(false)\n\t\treturn\n\tend\n\n\tlocal start, _, command = text:find(\"(/(%w+)%s)\")\n\tcommand = ix.command.list[tostring(command):utf8sub(2, tostring(command):utf8len() - 1):utf8lower()]\n\n\t-- update preview if we've found a command\n\tif (start == 1 and command) then\n\t\tpreview:SetCommand(command.uniqueID)\n\t\tpreview:SetVisible(true)\n\t\tpreview:UpdateArguments(text)\n\n\t\t-- we don't need the autocomplete because we have a command already typed out\n\t\tautocomplete:SetVisible(false)\n\t\treturn\n\t-- if there's a slash then we're probably going to be (or are currently) typing out a command\n\telseif (text:utf8sub(1, 1) == \"/\") then\n\t\tcommand = text:match(\"(/(%w+))\") or \"/\"\n\n\t\tpreview:SetVisible(false) -- we don't have a valid command yet\n\t\tautocomplete:Update(command:utf8sub(2))\n\t\tautocomplete:SetVisible(true)\n\n\t\treturn\n\tend\n\n\tif (preview:GetCommand() != \"\") then\n\t\tpreview:SetCommand(\"\")\n\t\tpreview:SetVisible(false)\n\tend\n\n\tif (autocomplete:IsVisible()) then\n\t\tautocomplete:SetVisible(false)\n\tend\nend\n\nDEFINE_BASECLASS(\"DTextEntry\")\nfunction PANEL:OnKeyCodeTyped(key)\n\tif (key == KEY_TAB) then\n\t\tif (self.autocomplete:IsOpen() and #self.autocomplete:GetCommands() > 0) then\n\t\t\tlocal newText = self.autocomplete:SelectNext()\n\n\t\t\tself.entry:SetText(newText)\n\t\t\tself.entry:SetCaretPos(newText:utf8len())\n\t\tend\n\n\t\treturn true\n\tend\n\n\treturn BaseClass.OnKeyCodeTyped(self.entry, key)\nend\n\n-- called when player types something and presses enter in the textentry\nfunction PANEL:OnMessageSent()\n\tlocal text = self.entry:GetText()\n\n\tif (text:find(\"%S\")) then\n\t\tlocal lastEntry = ix.chat.history[#ix.chat.history]\n\n\t\t-- only add line to textentry history if it isn't the same message\n\t\tif (lastEntry != text) then\n\t\t\tif (#ix.chat.history >= 20) then\n\t\t\t\ttable.remove(ix.chat.history, 1)\n\t\t\tend\n\n\t\t\tix.chat.history[#ix.chat.history + 1] = text\n\t\tend\n\n\t\tnet.Start(\"ixChatMessage\")\n\t\t\tnet.WriteString(text)\n\t\tnet.SendToServer()\n\tend\n\n\tself:SetActive(false) -- textentry is set to \"\" in SetActive\nend\n\n-- called when the player changes the currently active tab\nfunction PANEL:OnTabChanged(panel)\n\tpanel:InvalidateLayout(true)\n\tpanel:ScrollToBottom()\nend\n\n-- called when the player creates a new tab\nfunction PANEL:OnTabCreated(id, filter)\n\tself.tabs:AddTab(id, filter)\n\tPLUGIN:SaveTabs()\nend\n\n-- called when the player updates a tab's filter\nfunction PANEL:OnTabUpdated(id, filter, newID)\n\tlocal tab = self.tabs:GetTabs()[id]\n\n\tif (!tab) then\n\t\treturn\n\tend\n\n\ttab:SetFilter(filter)\n\tself.tabs:RenameTab(id, newID)\n\n\tPLUGIN:SaveTabs()\nend\n\n-- called when a tab's button was right-clicked\nfunction PANEL:OnTabRightClick(button, tab, id)\n\tlocal menu = DermaMenu()\n\t\tmenu:AddOption(L(\"chatCustomize\"), function()\n\t\t\tif (IsValid(ix.gui.chatTabCustomize)) then\n\t\t\t\tix.gui.chatTabCustomize:Remove()\n\t\t\tend\n\n\t\t\tlocal panel = vgui.Create(\"ixChatboxTabCustomize\")\n\t\t\tpanel:PopulateFromTab(id, tab:GetFilter())\n\t\t\tpanel.OnTabUpdated = ix.util.Bind(self, self.OnTabUpdated)\n\t\tend)\n\n\t\tmenu:AddSpacer()\n\n\t\tmenu:AddOption(L(\"chatCloseTab\"), function()\n\t\t\tself.tabs:RemoveTab(id)\n\t\t\tPLUGIN:SaveTabs()\n\t\tend)\n\tmenu:Open()\n\tmenu:MakePopup() -- HACK: mouse input doesn't work when created immediately after opening chatbox\nend\n\n-- called when a message needs to be added to applicable tabs\nfunction PANEL:AddMessage(...)\n\tlocal class = CHAT_CLASS and CHAT_CLASS.uniqueID or \"notice\"\n\tlocal activeTab = self.tabs:GetActiveTab()\n\n\t-- track whether or not the message was filtered out in the active tab\n\tlocal bShown = false\n\n\tif (activeTab and !activeTab:GetFilter()[class]) then\n\t\tactiveTab:AddLine({...}, true)\n\t\tbShown = true\n\tend\n\n\tfor _, v in pairs(self.tabs:GetTabs()) do\n\t\tif (v:GetID() == activeTab:GetID()) then\n\t\t\tcontinue -- we already added it to the active tab\n\t\tend\n\n\t\tif (!v:GetFilter()[class]) then\n\t\t\tv:AddLine({...}, true)\n\n\t\t\t-- mark other tabs as unread if we didn't show the message in the active tab\n\t\t\tif (!bShown) then\n\t\t\t\tv:GetButton():SetUnread(true)\n\t\t\tend\n\t\tend\n\tend\n\n\tif (bShown) then\n\t\tchat.PlaySound()\n\tend\nend\n\nvgui.Register(\"ixChatbox\", PANEL, \"EditablePanel\")\n"
  },
  {
    "path": "plugins/chatbox/derma/cl_chatboxcustomize.lua",
    "content": "\nlocal PLUGIN = PLUGIN\n\nlocal PANEL = {}\n\nfunction PANEL:Init()\n\tix.gui.chatTabCustomize = self\n\n\tself:SetTitle(L(\"chatNewTab\"))\n\tself:SetSizable(true)\n\tself:SetSize(ScrW() * 0.5, ScrH() * 0.5)\n\n\tself.settings = self:Add(\"ixSettings\")\n\tself.settings:Dock(FILL)\n\tself.settings:SetSearchEnabled(true)\n\tself.settings:AddCategory(L(\"chatAllowedClasses\"))\n\n\t-- controls\n\tlocal controlsPanel = self:Add(\"Panel\")\n\tcontrolsPanel:Dock(BOTTOM)\n\tcontrolsPanel:DockMargin(0, 4, 0, 0)\n\tcontrolsPanel:SetTall(32)\n\n\tself.create = controlsPanel:Add(\"DButton\")\n\tself.create:SetText(L(\"create\"))\n\tself.create:SizeToContents()\n\tself.create:Dock(FILL)\n\tself.create:DockMargin(0, 0, 4, 0)\n\tself.create.DoClick = ix.util.Bind(self, self.CreateClicked)\n\n\tlocal uncheckAll = controlsPanel:Add(\"DButton\")\n\tuncheckAll:SetText(L(\"uncheckAll\"))\n\tuncheckAll:SizeToContents()\n\tuncheckAll:Dock(RIGHT)\n\tuncheckAll.DoClick = function()\n\t\tself:SetAllValues(false)\n\tend\n\n\tlocal checkAll = controlsPanel:Add(\"DButton\")\n\tcheckAll:SetText(L(\"checkAll\"))\n\tcheckAll:SizeToContents()\n\tcheckAll:Dock(RIGHT)\n\tcheckAll:DockMargin(0, 0, 4, 0)\n\tcheckAll.DoClick = function()\n\t\tself:SetAllValues(true)\n\tend\n\n\t-- chat class settings\n\tself.name = self.settings:AddRow(ix.type.string)\n\tself.name:SetText(L(\"chatTabName\"))\n\tself.name:SetValue(L(\"chatNewTabTitle\"))\n\tself.name:SetZPos(-1)\n\n\tfor k, _ in SortedPairs(ix.chat.classes) do\n\t\tlocal panel = self.settings:AddRow(ix.type.bool, L(\"chatAllowedClasses\"))\n\t\tpanel:SetText(k)\n\t\tpanel:SetValue(true, true)\n\tend\n\n\tself.settings:SizeToContents()\n\tself:Center()\n\tself:MakePopup()\nend\n\nfunction PANEL:PopulateFromTab(name, filter)\n\tself.tab = name\n\n\tself:SetTitle(L(\"chatCustomize\"))\n\tself.create:SetText(L(\"update\"))\n\tself.name:SetValue(name)\n\n\tfor _, v in ipairs(self.settings:GetRows()) do\n\t\tif (filter[v:GetText()]) then\n\t\t\tv:SetValue(false, true)\n\t\tend\n\tend\nend\n\nfunction PANEL:SetAllValues(bValue)\n\tfor _, v in ipairs(self.settings:GetRows()) do\n\t\tif (v == self.name) then\n\t\t\tcontinue\n\t\tend\n\n\t\tv:SetValue(tobool(bValue), true)\n\tend\nend\n\nfunction PANEL:CreateClicked()\n\tlocal name = self.tab and self.tab or self.name:GetValue()\n\n\tif (self.tab != self.name:GetValue() and PLUGIN:TabExists(self.name:GetValue())) then\n\t\tix.util.Notify(L(\"chatTabExists\"))\n\t\treturn\n\tend\n\n\tlocal filter = {}\n\n\tfor _, v in ipairs(self.settings:GetRows()) do\n\t\t-- we only want to add entries for classes we don't want shown\n\t\tif (!v:GetValue()) then\n\t\t\tfilter[v:GetText()] = true\n\t\tend\n\tend\n\n\tif (self.tab) then\n\t\tself:OnTabUpdated(name, filter, self.name:GetValue())\n\telse\n\t\tself:OnTabCreated(name, filter)\n\tend\n\n\tself:Remove()\nend\n\nfunction PANEL:OnTabCreated(id, filter)\nend\n\nfunction PANEL:OnTabUpdated(id, filter, newID)\nend\n\nvgui.Register(\"ixChatboxTabCustomize\", PANEL, \"DFrame\")\n"
  },
  {
    "path": "plugins/chatbox/sh_plugin.lua",
    "content": "\nlocal PLUGIN = PLUGIN\n\nPLUGIN.name = \"Chatbox\"\nPLUGIN.author = \"`impulse\"\nPLUGIN.description = \"Replaces the chatbox to enable customization, autocomplete, and useful info.\"\n\nif (CLIENT) then\n\tix.chat.history = ix.chat.history or {} -- array of strings the player has entered into the chatbox\n\tix.chat.currentCommand = \"\"\n\tix.chat.currentArguments = {}\n\n\tix.option.Add(\"chatNotices\", ix.type.bool, false, {\n\t\tcategory = \"chat\"\n\t})\n\n\tix.option.Add(\"chatTimestamps\", ix.type.bool, false, {\n\t\tcategory = \"chat\"\n\t})\n\n\tix.option.Add(\"chatFontScale\", ix.type.number, 1, {\n\t\tcategory = \"chat\", min = 0.1, max = 2, decimals = 2,\n\t\tOnChanged = function()\n\t\t\thook.Run(\"LoadFonts\", ix.config.Get(\"font\"), ix.config.Get(\"genericFont\"))\n\t\t\tPLUGIN:CreateChat()\n\t\tend\n\t})\n\n\tix.option.Add(\"chatOutline\", ix.type.bool, false, {\n\t\tcategory = \"chat\"\n\t})\n\n\t-- tabs and their respective filters\n\tix.option.Add(\"chatTabs\", ix.type.string, \"\", {\n\t\tcategory = \"chat\",\n\t\thidden = function()\n\t\t\treturn true\n\t\tend\n\t})\n\n\t-- chatbox size and position\n\tix.option.Add(\"chatPosition\", ix.type.string, \"\", {\n\t\tcategory = \"chat\",\n\t\thidden = function()\n\t\t\treturn true\n\t\tend\n\t})\n\n\tfunction PLUGIN:CreateChat()\n\t\tif (IsValid(self.panel)) then\n\t\t\tself.panel:Remove()\n\t\tend\n\n\t\tself.panel = vgui.Create(\"ixChatbox\")\n\t\tself.panel:SetupTabs(util.JSONToTable(ix.option.Get(\"chatTabs\", \"\")))\n\t\tself.panel:SetupPosition(util.JSONToTable(ix.option.Get(\"chatPosition\", \"\")))\n\n\t\thook.Run(\"ChatboxCreated\")\n\tend\n\n\tfunction PLUGIN:TabExists(id)\n\t\tif (!IsValid(self.panel)) then\n\t\t\treturn false\n\t\tend\n\n\t\treturn self.panel.tabs:GetTabs()[id] != nil\n\tend\n\n\tfunction PLUGIN:SaveTabs()\n\t\tlocal tabs = {}\n\n\t\tfor id, panel in pairs(self.panel.tabs:GetTabs()) do\n\t\t\ttabs[id] = panel:GetFilter()\n\t\tend\n\n\t\tix.option.Set(\"chatTabs\", util.TableToJSON(tabs))\n\tend\n\n\tfunction PLUGIN:SavePosition()\n\t\tlocal x, y = self.panel:GetPos()\n\t\tlocal width, height = self.panel:GetSize()\n\n\t\tix.option.Set(\"chatPosition\", util.TableToJSON({x, y, width, height}))\n\tend\n\n\tfunction PLUGIN:InitPostEntity()\n\t\tself:CreateChat()\n\tend\n\n\tfunction PLUGIN:PlayerBindPress(client, bind, pressed)\n\t\tbind = bind:lower()\n\n\t\tif (bind:find(\"messagemode\") and pressed) then\n\t\t\tself.panel:SetActive(true)\n\n\t\t\treturn true\n\t\tend\n\tend\n\n\tfunction PLUGIN:OnPauseMenuShow()\n\t\tif (!IsValid(ix.gui.chat) or !ix.gui.chat:GetActive()) then\n\t\t\treturn\n\t\tend\n\n\t\tix.gui.chat:SetActive(false)\n\n\t\treturn false\n\tend\n\n\tfunction PLUGIN:HUDShouldDraw(element)\n\t\tif (element == \"CHudChat\") then\n\t\t\treturn false\n\t\tend\n\tend\n\n\tfunction PLUGIN:ScreenResolutionChanged(oldWidth, oldHeight)\n\t\tself:CreateChat()\n\tend\n\n\tfunction PLUGIN:ChatText(index, name, text, messageType)\n\t\tif (messageType == \"none\" and IsValid(self.panel)) then\n\t\t\tself.panel:AddMessage(text)\n\t\tend\n\tend\n\n\t-- luacheck: globals chat\n\tchat.ixAddText = chat.ixAddText or chat.AddText\n\n\tfunction chat.AddText(...)\n\t\tif (IsValid(PLUGIN.panel)) then\n\t\t\tPLUGIN.panel:AddMessage(...)\n\t\tend\n\n\t\t-- log chat message to console\n\t\tlocal text = {}\n\n\t\tfor _, v in ipairs({...}) do\n\t\t\tif (istable(v) or isstring(v)) then\n\t\t\t\ttext[#text + 1] = v\n\t\t\telseif (isentity(v) and v:IsPlayer()) then\n\t\t\t\ttext[#text + 1] = team.GetColor(v:Team())\n\t\t\t\ttext[#text + 1] = v:Name()\n\t\t\telseif (type(v) != \"IMaterial\") then\n\t\t\t\ttext[#text + 1] = tostring(v)\n\t\t\tend\n\t\tend\n\n\t\ttext[#text + 1] = \"\\n\"\n\t\tMsgC(unpack(text))\n\tend\nelse\n\tutil.AddNetworkString(\"ixChatMessage\")\n\n\tnet.Receive(\"ixChatMessage\", function(length, client)\n\t\tlocal text = net.ReadString()\n\n\t\tif ((client.ixNextChat or 0) < CurTime() and isstring(text) and text:find(\"%S\")) then\n\t\t\tlocal maxLength = ix.config.Get(\"chatMax\")\n\n\t\t\tif (text:utf8len() > maxLength) then\n\t\t\t\ttext = text:utf8sub(0, maxLength)\n\t\t\tend\n\n\t\t\thook.Run(\"PlayerSay\", client, text)\n\t\t\tclient.ixNextChat = CurTime() + 0.5\n\t\tend\n\tend)\nend\n"
  },
  {
    "path": "plugins/containers/entities/entities/ix_container.lua",
    "content": "\nENT.Type = \"anim\"\nENT.PrintName = \"Container\"\nENT.Category = \"Helix\"\nENT.Spawnable = false\nENT.bNoPersist = true\n\nfunction ENT:SetupDataTables()\n\tself:NetworkVar(\"Int\", 0, \"ID\")\n\tself:NetworkVar(\"Bool\", 0, \"Locked\")\n\tself:NetworkVar(\"String\", 0, \"DisplayName\")\nend\n\nif (SERVER) then\n\tfunction ENT:Initialize()\n\t\tself:PhysicsInit(SOLID_VPHYSICS)\n\t\tself:SetSolid(SOLID_VPHYSICS)\n\t\tself:SetUseType(SIMPLE_USE)\n\t\tself.receivers = {}\n\n\t\tlocal definition = ix.container.stored[self:GetModel():lower()]\n\n\t\tif (definition) then\n\t\t\tself:SetDisplayName(definition.name)\n\t\tend\n\n\t\tlocal physObj = self:GetPhysicsObject()\n\n\t\tif (IsValid(physObj)) then\n\t\t\tphysObj:EnableMotion(true)\n\t\t\tphysObj:Wake()\n\t\tend\n\tend\n\n\tfunction ENT:SetInventory(inventory)\n\t\tif (inventory) then\n\t\t\tself:SetID(inventory:GetID())\n\t\tend\n\tend\n\n\tfunction ENT:SetMoney(amount)\n\t\tself.money = math.max(0, math.Round(tonumber(amount) or 0))\n\tend\n\n\tfunction ENT:GetMoney()\n\t\treturn self.money or 0\n\tend\n\n\tfunction ENT:OnRemove()\n\t\tlocal index = self:GetID()\n\n\t\tif (!ix.shuttingDown and !self.ixIsSafe and ix.entityDataLoaded and index) then\n\t\t\tlocal inventory = ix.item.inventories[index]\n\n\t\t\tif (inventory) then\n\t\t\t\tix.item.inventories[index] = nil\n\n\t\t\t\tlocal query = mysql:Delete(\"ix_items\")\n\t\t\t\t\tquery:Where(\"inventory_id\", index)\n\t\t\t\tquery:Execute()\n\n\t\t\t\tquery = mysql:Delete(\"ix_inventories\")\n\t\t\t\t\tquery:Where(\"inventory_id\", index)\n\t\t\t\tquery:Execute()\n\n\t\t\t\thook.Run(\"ContainerRemoved\", self, inventory)\n\t\t\tend\n\t\tend\n\tend\n\n\tfunction ENT:OpenInventory(activator)\n\t\tlocal inventory = self:GetInventory()\n\n\t\tif (inventory) then\n\t\t\tlocal name = self:GetDisplayName()\n\t\t\tlocal definition = ix.container.stored[self:GetModel():lower()]\n\n\t\t\tix.storage.Open(activator, inventory, {\n\t\t\t\tname = name,\n\t\t\t\tentity = self,\n\t\t\t\tsearchTime = ix.config.Get(\"containerOpenTime\", 0.7),\n\t\t\t\tdata = {money = self:GetMoney()},\n\t\t\t\tOnPlayerOpen = function()\n\t\t\t\t\tif (definition.OnOpen) then\n\t\t\t\t\t    definition.OnOpen(self, activator)\n\t\t\t\t\tend\n\t\t\t\tend,\n\t\t\t\tOnPlayerClose = function()\n\t\t\t\t\tif (definition.OnClose) then\n\t\t\t\t\t\tdefinition.OnClose(self, activator)\n\t\t\t\t\tend\n\n\t\t\t\t\tix.log.Add(activator, \"closeContainer\", name, inventory:GetID())\n\t\t\t\tend\n\t\t\t})\n\n\t\t\tif (self:GetLocked()) then\n\t\t\t\tself.Sessions[activator:GetCharacter():GetID()] = true\n\t\t\tend\n\n\t\t\tix.log.Add(activator, \"openContainer\", name, inventory:GetID())\n\t\tend\n\tend\n\n\tfunction ENT:Use(activator)\n\t\tlocal inventory = self:GetInventory()\n\n\t\tif (inventory and (activator.ixNextOpen or 0) < CurTime()) then\n\t\t\tlocal character = activator:GetCharacter()\n\n\t\t\tif (character) then\n\t\t\t\tlocal definition = ix.container.stored[self:GetModel():lower()]\n\n\t\t\t\tif (self:GetLocked() and !self.Sessions[character:GetID()]) then\n\t\t\t\t\tself:EmitSound(definition.locksound or \"doors/default_locked.wav\")\n\n\t\t\t\t\tif (!self.keypad) then\n\t\t\t\t\t\tnet.Start(\"ixContainerPassword\")\n\t\t\t\t\t\t\tnet.WriteEntity(self)\n\t\t\t\t\t\tnet.Send(activator)\n\t\t\t\t\tend\n\t\t\t\telse\n\t\t\t\t\tself:OpenInventory(activator)\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tactivator.ixNextOpen = CurTime() + 1\n\t\tend\n\tend\nelse\n\tENT.PopulateEntityInfo = true\n\n\tlocal COLOR_LOCKED = Color(200, 38, 19, 200)\n\tlocal COLOR_UNLOCKED = Color(135, 211, 124, 200)\n\n\tfunction ENT:OnPopulateEntityInfo(tooltip)\n\t\tlocal definition = ix.container.stored[self:GetModel():lower()]\n\t\tlocal bLocked = self:GetLocked()\n\n\t\tsurface.SetFont(\"ixIconsSmall\")\n\n\t\tlocal iconText = bLocked and \"P\" or \"Q\"\n\t\tlocal iconWidth, iconHeight = surface.GetTextSize(iconText)\n\n\t\t-- minimal tooltips have centered text so we'll draw the icon above the name instead\n\t\tif (tooltip:IsMinimal()) then\n\t\t\tlocal icon = tooltip:AddRow(\"icon\")\n\t\t\ticon:SetFont(\"ixIconsSmall\")\n\t\t\ticon:SetTextColor(bLocked and COLOR_LOCKED or COLOR_UNLOCKED)\n\t\t\ticon:SetText(iconText)\n\t\t\ticon:SizeToContents()\n\t\tend\n\n\t\tlocal title = tooltip:AddRow(\"name\")\n\t\ttitle:SetImportant()\n\t\ttitle:SetText(self:GetDisplayName())\n\t\ttitle:SetBackgroundColor(ix.config.Get(\"color\"))\n\t\ttitle:SetTextInset(iconWidth + 8, 0)\n\t\ttitle:SizeToContents()\n\n\t\tif (!tooltip:IsMinimal()) then\n\t\t\ttitle.Paint = function(panel, width, height)\n\t\t\t\tpanel:PaintBackground(width, height)\n\n\t\t\t\tsurface.SetFont(\"ixIconsSmall\")\n\t\t\t\tsurface.SetTextColor(bLocked and COLOR_LOCKED or COLOR_UNLOCKED)\n\t\t\t\tsurface.SetTextPos(4, height * 0.5 - iconHeight * 0.5)\n\t\t\t\tsurface.DrawText(iconText)\n\t\t\tend\n\t\tend\n\n\t\tlocal description = tooltip:AddRow(\"description\")\n\t\tdescription:SetText(definition.description)\n\t\tdescription:SizeToContents()\n\tend\nend\n\nfunction ENT:GetInventory()\n\treturn ix.item.inventories[self:GetID()]\nend\n"
  },
  {
    "path": "plugins/containers/sh_definitions.lua",
    "content": "--[[\n\tix.container.Register(model, {\n\t\tname = \"Crate\",\n\t\tdescription = \"A simple wooden create.\",\n\t\twidth = 4,\n\t\theight = 4,\n\t\tlocksound = \"\",\n\t\topensound = \"\"\n\t})\n]]--\n\nix.container.Register(\"models/props_junk/wood_crate001a.mdl\", {\n\tname = \"Crate\",\n\tdescription = \"A simple wooden crate.\",\n\twidth = 4,\n\theight = 4,\n})\n\nix.container.Register(\"models/props_c17/lockers001a.mdl\", {\n\tname = \"Locker\",\n\tdescription = \"A white locker.\",\n\twidth = 3,\n\theight = 5,\n})\n\nix.container.Register(\"models/props_wasteland/controlroom_storagecloset001a.mdl\", {\n\tname = \"Metal Cabinet\",\n\tdescription = \"A green metal cabinet.\",\n\twidth = 4,\n\theight = 5,\n})\n\nix.container.Register(\"models/props_wasteland/controlroom_storagecloset001b.mdl\", {\n\tname = \"Metal Cabinet\",\n\tdescription = \"A green metal cabinet.\",\n\twidth = 4,\n\theight = 5,\n})\n\nix.container.Register(\"models/props_wasteland/controlroom_filecabinet001a.mdl\", {\n\tname = \"File Cabinet\",\n\tdescription = \"A metal filing cabinet.\",\n\twidth = 5,\n\theight = 3\n})\n\nix.container.Register(\"models/props_wasteland/controlroom_filecabinet002a.mdl\", {\n\tname = \"File Cabinet\",\n\tdescription = \"A metal filing cabinet.\",\n\twidth = 3,\n\theight = 6,\n})\n\nix.container.Register(\"models/props_lab/filecabinet02.mdl\", {\n\tname = \"File Cabinet\",\n\tdescription = \"A metal filing cabinet.\",\n\twidth = 5,\n\theight = 3\n})\n\nix.container.Register(\"models/props_c17/furniturefridge001a.mdl\", {\n\tname = \"Refrigerator\",\n\tdescription = \"A metal box for keeping food in.\",\n\twidth = 2,\n\theight = 3,\n})\n\nix.container.Register(\"models/props_wasteland/kitchen_fridge001a.mdl\", {\n\tname = \"Large Refrigerator\",\n\tdescription = \"A large metal box for storing even more food in.\",\n\twidth = 4,\n\theight = 5,\n})\n\nix.container.Register(\"models/props_junk/trashbin01a.mdl\", {\n\tname = \"Trash Bin\",\n\tdescription = \"What do you expect to find in here?\",\n\twidth = 2,\n\theight = 2,\n})\n\nix.container.Register(\"models/props_junk/trashdumpster01a.mdl\", {\n\tname = \"Dumpster\",\n\tdescription = \"A dumpster meant to stow away trash. It emanates an unpleasant smell.\",\n\twidth = 6,\n\theight = 3\n})\n\nix.container.Register(\"models/items/ammocrate_smg1.mdl\", {\n\tname = \"Ammo Crate\",\n\tdescription = \"A heavy crate that stores ammo.\",\n\twidth = 5,\n\theight = 3,\n\tOnOpen = function(entity, activator)\n\t\tlocal closeSeq = entity:LookupSequence(\"Close\")\n\t\tentity:ResetSequence(closeSeq)\n\n\t\ttimer.Simple(2, function()\n\t\t\tif (entity and IsValid(entity)) then\n\t\t\t\tlocal openSeq = entity:LookupSequence(\"Open\")\n\t\t\t\tentity:ResetSequence(openSeq)\n\t\t\tend\n\t\tend)\n\tend\n})\n\nix.container.Register(\"models/props_forest/footlocker01_closed.mdl\", {\n\tname = \"Footlocker\",\n\tdescription = \"A small chest to store belongings in.\",\n\twidth = 5,\n\theight = 3\n})\n\nix.container.Register(\"models/Items/item_item_crate.mdl\", {\n\tname = \"Item Crate\",\n\tdescription = \"A crate to store some belongings in.\",\n\twidth = 5,\n\theight = 3\n})\n\nix.container.Register(\"models/props_c17/cashregister01a.mdl\", {\n\tname = \"Cash Register\",\n\tdescription = \"A register with some buttons and a drawer.\",\n\twidth = 2,\n\theight = 1\n})\n"
  },
  {
    "path": "plugins/containers/sh_plugin.lua",
    "content": "\nlocal PLUGIN = PLUGIN\n\nPLUGIN.name = \"Containers\"\nPLUGIN.author = \"Chessnut\"\nPLUGIN.description = \"Provides the ability to store items.\"\n\nix.container = ix.container or {}\nix.container.stored = ix.container.stored or {}\n\nix.config.Add(\"containerSave\", true, \"Whether or not containers will save after a server restart.\", nil, {\n\tcategory = \"Containers\"\n})\n\nix.config.Add(\"containerOpenTime\", 0.7, \"How long it takes to open a container.\", nil, {\n\tdata = {min = 0, max = 50},\n\tcategory = \"Containers\"\n})\n\nfunction ix.container.Register(model, data)\n\tix.container.stored[model:lower()] = data\nend\n\nix.util.Include(\"sh_definitions.lua\")\n\nif (SERVER) then\n\tutil.AddNetworkString(\"ixContainerPassword\")\n\n\tfunction PLUGIN:PlayerSpawnedProp(client, model, entity)\n\t\tmodel = tostring(model):lower()\n\t\tlocal data = ix.container.stored[model]\n\n\t\tif (data) then\n\t\t\tif (hook.Run(\"CanPlayerSpawnContainer\", client, model, entity) == false) then return end\n\n\t\t\tlocal container = ents.Create(\"ix_container\")\n\t\t\tcontainer:SetPos(entity:GetPos())\n\t\t\tcontainer:SetAngles(entity:GetAngles())\n\t\t\tcontainer:SetModel(model)\n\t\t\tcontainer:Spawn()\n\n\t\t\tix.inventory.New(0, \"container:\" .. model:lower(), function(inventory)\n\t\t\t\t-- we'll technically call this a bag since we don't want other bags to go inside\n\t\t\t\tinventory.vars.isBag = true\n\t\t\t\tinventory.vars.isContainer = true\n\n\t\t\t\tif (IsValid(container)) then\n\t\t\t\t\tcontainer:SetInventory(inventory)\n\t\t\t\t\tself:SaveContainer()\n\t\t\t\tend\n\t\t\tend)\n\n\t\t\tentity:Remove()\n\t\tend\n\tend\n\n\tfunction PLUGIN:CanSaveContainer(entity, inventory)\n\t\treturn ix.config.Get(\"containerSave\", true)\n\tend\n\n\tfunction PLUGIN:SaveContainer()\n\t\tlocal data = {}\n\n\t\tfor _, v in ipairs(ents.FindByClass(\"ix_container\")) do\n\t\t\tif (hook.Run(\"CanSaveContainer\", v, v:GetInventory()) != false) then\n\t\t\t\tlocal inventory = v:GetInventory()\n\n\t\t\t\tif (inventory) then\n\t\t\t\t\tdata[#data + 1] = {\n\t\t\t\t\t\tv:GetPos(),\n\t\t\t\t\t\tv:GetAngles(),\n\t\t\t\t\t\tinventory:GetID(),\n\t\t\t\t\t\tv:GetModel(),\n\t\t\t\t\t\tv.password,\n\t\t\t\t\t\tv:GetDisplayName(),\n\t\t\t\t\t\tv:GetMoney()\n\t\t\t\t\t}\n\t\t\t\tend\n\t\t\telse\n\t\t\t\tlocal index = v:GetID()\n\n\t\t\t\tlocal query = mysql:Delete(\"ix_items\")\n\t\t\t\t\tquery:Where(\"inventory_id\", index)\n\t\t\t\tquery:Execute()\n\n\t\t\t\tquery = mysql:Delete(\"ix_inventories\")\n\t\t\t\t\tquery:Where(\"inventory_id\", index)\n\t\t\t\tquery:Execute()\n\t\t\tend\n\t\tend\n\n\t\tself:SetData(data)\n\tend\n\n\tfunction PLUGIN:SaveData()\n\t\tif (!ix.shuttingDown) then\n\t\t\tself:SaveContainer()\n\t\tend\n\tend\n\n\tfunction PLUGIN:ContainerRemoved(entity, inventory)\n\t\tself:SaveContainer()\n\tend\n\n\tfunction PLUGIN:LoadData()\n\t\tlocal data = self:GetData()\n\n\t\tif (data) then\n\t\t\tfor _, v in ipairs(data) do\n\t\t\t\tlocal data2 = ix.container.stored[v[4]:lower()]\n\n\t\t\t\tif (data2) then\n\t\t\t\t\tlocal inventoryID = tonumber(v[3])\n\n\t\t\t\t\tif (!inventoryID or inventoryID < 1) then\n\t\t\t\t\t\tErrorNoHalt(string.format(\n\t\t\t\t\t\t\t\"[Helix] Attempted to restore container inventory with invalid inventory ID '%s' (%s, %s)\\n\",\n\t\t\t\t\t\t\ttostring(inventoryID), v[6] or \"no name\", v[4] or \"no model\"))\n\n\t\t\t\t\t\tcontinue\n\t\t\t\t\tend\n\n\t\t\t\t\tlocal entity = ents.Create(\"ix_container\")\n\t\t\t\t\tentity:SetPos(v[1])\n\t\t\t\t\tentity:SetAngles(v[2])\n\t\t\t\t\tentity:Spawn()\n\t\t\t\t\tentity:SetModel(v[4])\n\t\t\t\t\tentity:SetSolid(SOLID_VPHYSICS)\n\t\t\t\t\tentity:PhysicsInit(SOLID_VPHYSICS)\n\n\t\t\t\t\tif (v[5]) then\n\t\t\t\t\t\tentity.password = v[5]\n\t\t\t\t\t\tentity:SetLocked(true)\n\t\t\t\t\t\tentity.Sessions = {}\n\t\t\t\t\t\tentity.PasswordAttempts = {}\n\t\t\t\t\tend\n\n\t\t\t\t\tif (v[6]) then\n\t\t\t\t\t\tentity:SetDisplayName(v[6])\n\t\t\t\t\tend\n\n\t\t\t\t\tif (v[7]) then\n\t\t\t\t\t\tentity:SetMoney(v[7])\n\t\t\t\t\tend\n\n\t\t\t\t\tix.inventory.Restore(inventoryID, data2.width, data2.height, function(inventory)\n\t\t\t\t\t\tinventory.vars.isBag = true\n\t\t\t\t\t\tinventory.vars.isContainer = true\n\n\t\t\t\t\t\tif (IsValid(entity)) then\n\t\t\t\t\t\t\tentity:SetInventory(inventory)\n\t\t\t\t\t\tend\n\t\t\t\t\tend)\n\n\t\t\t\t\tlocal physObject = entity:GetPhysicsObject()\n\n\t\t\t\t\tif (IsValid(physObject)) then\n\t\t\t\t\t\tphysObject:EnableMotion()\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend\n\n\tnet.Receive(\"ixContainerPassword\", function(length, client)\n\t\tif ((client.ixNextContainerPassword or 0) > RealTime()) then\n\t\t\treturn\n\t\tend\n\n\t\tlocal entity = net.ReadEntity()\n\t\tlocal steamID = client:SteamID()\n\t\tlocal attempts = entity.PasswordAttempts[steamID]\n\n\t\tif (attempts and attempts >= 10) then\n\t\t\tclient:NotifyLocalized(\"passwordAttemptLimit\")\n\n\t\t\treturn\n\t\tend\n\n\t\tlocal password = net.ReadString()\n\t\tlocal dist = entity:GetPos():DistToSqr(client:GetPos())\n\n\t\tif (dist < 16384 and password) then\n\t\t\tif (entity.password and entity.password == password) then\n\t\t\t\tentity:OpenInventory(client)\n\t\t\telse\n\t\t\t\tentity.PasswordAttempts[steamID] = attempts and attempts + 1 or 1\n\n\t\t\t\tclient:NotifyLocalized(\"wrongPassword\")\n\t\t\tend\n\t\tend\n\n\t\tclient.ixNextContainerPassword = RealTime() + 1\n\tend)\n\n\tix.log.AddType(\"containerPassword\", function(client, ...)\n\t\tlocal arg = {...}\n\t\treturn string.format(\"%s has %s the password for '%s'.\", client:Name(), arg[3] and \"set\" or \"removed\", arg[1], arg[2])\n\tend)\n\n\tix.log.AddType(\"containerName\", function(client, ...)\n\t\tlocal arg = {...}\n\n\t\tif (arg[3]) then\n\t\t\treturn string.format(\"%s has set container %d name to '%s'.\", client:Name(), arg[2], arg[1])\n\t\telse\n\t\t\treturn string.format(\"%s has removed container %d name.\", client:Name(), arg[2])\n\t\tend\n\tend)\n\n\tix.log.AddType(\"openContainer\", function(client, ...)\n\t\tlocal arg = {...}\n\t\treturn string.format(\"%s opened the '%s' #%d container.\", client:Name(), arg[1], arg[2])\n\tend, FLAG_NORMAL)\n\n\tix.log.AddType(\"closeContainer\", function(client, ...)\n\t\tlocal arg = {...}\n\t\treturn string.format(\"%s closed the '%s' #%d container.\", client:Name(), arg[1], arg[2])\n\tend, FLAG_NORMAL)\nelse\n\tnet.Receive(\"ixContainerPassword\", function(length)\n\t\tlocal entity = net.ReadEntity()\n\n\t\tDerma_StringRequest(\n\t\t\tL(\"containerPasswordWrite\"),\n\t\t\tL(\"containerPasswordWrite\"),\n\t\t\t\"\",\n\t\t\tfunction(val)\n\t\t\t\tnet.Start(\"ixContainerPassword\")\n\t\t\t\t\tnet.WriteEntity(entity)\n\t\t\t\t\tnet.WriteString(val)\n\t\t\t\tnet.SendToServer()\n\t\t\tend\n\t\t)\n\tend)\nend\n\nfunction PLUGIN:InitializedPlugins()\n\tfor k, v in pairs(ix.container.stored) do\n\t\tif (v.name and v.width and v.height) then\n\t\t\tix.inventory.Register(\"container:\" .. k:lower(), v.width, v.height)\n\t\telse\n\t\t\tErrorNoHalt(\"[Helix] Container for '\"..k..\"' is missing all inventory information!\\n\")\n\t\t\tix.container.stored[k] = nil\n\t\tend\n\tend\nend\n\n-- properties\nproperties.Add(\"container_setpassword\", {\n\tMenuLabel = \"Set Password\",\n\tOrder = 400,\n\tMenuIcon = \"icon16/lock_edit.png\",\n\n\tFilter = function(self, entity, client)\n\t\tif (entity:GetClass() != \"ix_container\") then return false end\n\t\tif (!gamemode.Call(\"CanProperty\", client, \"container_setpassword\", entity)) then return false end\n\n\t\treturn true\n\tend,\n\n\tAction = function(self, entity)\n\t\tDerma_StringRequest(L(\"containerPasswordWrite\"), \"\", \"\", function(text)\n\t\t\tself:MsgStart()\n\t\t\t\tnet.WriteEntity(entity)\n\t\t\t\tnet.WriteString(text)\n\t\t\tself:MsgEnd()\n\t\tend)\n\tend,\n\n\tReceive = function(self, length, client)\n\t\tlocal entity = net.ReadEntity()\n\n\t\tif (!IsValid(entity)) then return end\n\t\tif (!self:Filter(entity, client)) then return end\n\n\t\tlocal password = net.ReadString()\n\n\t\tentity.Sessions = {}\n\t\tentity.PasswordAttempts = {}\n\n\t\tif (password:len() != 0) then\n\t\t\tentity:SetLocked(true)\n\t\t\tentity.password = password\n\n\t\t\tclient:NotifyLocalized(\"containerPassword\", password)\n\t\telse\n\t\t\tentity:SetLocked(false)\n\t\t\tentity.password = nil\n\n\t\t\tclient:NotifyLocalized(\"containerPasswordRemove\")\n\t\tend\n\n\t\tlocal name = entity:GetDisplayName()\n\t\tlocal inventory = entity:GetInventory()\n\n\t\tix.log.Add(client, \"containerPassword\", name, inventory:GetID(), password:len() != 0)\n\tend\n})\n\nproperties.Add(\"container_setname\", {\n\tMenuLabel = \"Set Name\",\n\tOrder = 400,\n\tMenuIcon = \"icon16/tag_blue_edit.png\",\n\n\tFilter = function(self, entity, client)\n\t\tif (entity:GetClass() != \"ix_container\") then return false end\n\t\tif (!gamemode.Call(\"CanProperty\", client, \"container_setname\", entity)) then return false end\n\n\t\treturn true\n\tend,\n\n\tAction = function(self, entity)\n\t\tDerma_StringRequest(L(\"containerNameWrite\"), \"\", \"\", function(text)\n\t\t\tself:MsgStart()\n\t\t\t\tnet.WriteEntity(entity)\n\t\t\t\tnet.WriteString(text)\n\t\t\tself:MsgEnd()\n\t\tend)\n\tend,\n\n\tReceive = function(self, length, client)\n\t\tlocal entity = net.ReadEntity()\n\n\t\tif (!IsValid(entity)) then return end\n\t\tif (!self:Filter(entity, client)) then return end\n\n\t\tlocal name = net.ReadString()\n\n\t\tif (name:len() != 0) then\n\t\t\tentity:SetDisplayName(name)\n\n\t\t\tclient:NotifyLocalized(\"containerName\", name)\n\t\telse\n\t\t\tlocal definition = ix.container.stored[entity:GetModel():lower()]\n\n\t\t\tentity:SetDisplayName(definition.name)\n\n\t\t\tclient:NotifyLocalized(\"containerNameRemove\")\n\t\tend\n\n\t\tlocal inventory = entity:GetInventory()\n\n\t\tix.log.Add(client, \"containerName\", name, inventory:GetID(), name:len() != 0)\n\tend\n})\n"
  },
  {
    "path": "plugins/crosshair.lua",
    "content": "\nlocal PLUGIN = PLUGIN\n\nPLUGIN.name = \"Crosshair\"\nPLUGIN.author = \"Black Tea\"\nPLUGIN.description = \"A Crosshair.\"\n\nif (CLIENT) then\n\tlocal function drawdot( pos, size, col )\n\t\tlocal color = col[2]\n\t\tsurface.SetDrawColor(color.r, color.g, color.b, color.a)\n\t\tsurface.DrawRect(pos[1] - size/2, pos[2] - size/2, size, size)\n\n\t\tcolor = col[1]\n\t\tsurface.SetDrawColor(color.r, color.g, color.b, color.a)\n\t\tsurface.DrawOutlinedRect(pos[1] - size/2, pos[2] - size/2 , size, size)\n\tend\n\n\tlocal aimVector, punchAngle, ft, screen, scaleFraction, distance\n\tlocal math_round = math.Round\n\tlocal curGap = 0\n\tlocal curAlpha = 0\n\tlocal maxDistance = 1000 ^ 2\n\tlocal crossSize = 4\n\tlocal crossGap = 0\n\tlocal colors = {color_black}\n\tlocal filter = {}\n\n\tfunction PLUGIN:DrawCrosshair(x, y, trace)\n\t\tlocal entity = trace.Entity\n\t\tdistance = trace.StartPos:DistToSqr(trace.HitPos)\n\t\tscaleFraction = 1 - math.Clamp(distance / maxDistance, 0, .5)\n\t\tcrossSize = 4\n\t\tcrossGap = 25 * (scaleFraction - (LocalPlayer():IsWepRaised() and 0 or .1))\n\n\t\tif (IsValid(entity) and entity:GetClass() == \"ix_item\" and\n\t\t\tentity:GetPos():DistToSqr(trace.StartPos) <= 16384) then\n\t\t\tcrossGap = 0\n\t\t\tcrossSize = 5\n\t\tend\n\n\t\tcurGap = Lerp(ft * 2, curGap, crossGap)\n\t\tcurAlpha = Lerp(ft * 2, curAlpha, !LocalPlayer():IsWepRaised() and 255 or 150)\n\t\tcurAlpha = hook.Run(\"GetCrosshairAlpha\", curAlpha) or curAlpha\n\t\tcolors[2] = Color(255, curAlpha, curAlpha, curAlpha)\n\n\t\tif (curAlpha > 1) then\n\t\t\tdrawdot( {math_round(screen.x), math_round(screen.y)}, crossSize, colors)\n\t\t\tdrawdot( {math_round(screen.x + curGap), math_round(screen.y)}, crossSize, colors)\n\t\t\tdrawdot( {math_round(screen.x - curGap), math_round(screen.y)}, crossSize, colors)\n\t\t\tdrawdot( {math_round(screen.x), math_round(screen.y + curGap * .8)}, crossSize, colors)\n\t\t\tdrawdot( {math_round(screen.x), math_round(screen.y - curGap * .8)}, crossSize, colors)\n\t\tend\n\tend\n\n\t-- luacheck: globals g_ContextMenu\n\tfunction PLUGIN:PostDrawHUD()\n\t\tlocal client = LocalPlayer()\n\t\tif (!client:GetCharacter() or !client:Alive()) then\n\t\t\treturn\n\t\tend\n\n\t\tlocal entity = Entity(client:GetLocalVar(\"ragdoll\", 0))\n\n\t\tif (entity:IsValid()) then\n\t\t\treturn\n\t\tend\n\n\t\tlocal wep = client:GetActiveWeapon()\n\t\tlocal bShouldDraw = hook.Run(\"ShouldDrawCrosshair\", client, wep)\n\n\t\tif (bShouldDraw == false or !IsValid(wep) or wep.DrawCrosshair == false) then\n\t\t\treturn\n\t\tend\n\n\t\tif (bShouldDraw == false or g_ContextMenu:IsVisible() or\n\t\t\t(IsValid(ix.gui.characterMenu) and !ix.gui.characterMenu:IsClosing())) then\n\t\t\treturn\n\t\tend\n\n\t\taimVector = client:EyeAngles()\n\t\tpunchAngle = client:GetViewPunchAngles()\n\t\tft = FrameTime()\n\t\tfilter = {client}\n\n\t\tlocal vehicle = client:GetVehicle()\n\t\tif (vehicle and IsValid(vehicle)) then\n\t\t\taimVector = aimVector + vehicle:GetAngles()\n\t\t\ttable.insert(filter, vehicle)\n\t\tend\n\n\t\tlocal data = {}\n\t\t\tdata.start = client:GetShootPos()\n\t\t\tdata.endpos = data.start + (aimVector + punchAngle):Forward() * 65535\n\t\t\tdata.filter = filter\n\t\tlocal trace = util.TraceLine(data)\n\n\t\tlocal drawTarget = self\n\t\tlocal drawFunction = self.DrawCrosshair\n\n\t\t-- we'll manually call this since CHudCrosshair is never drawn; checks are already performed\n\t\tif (wep.DoDrawCrosshair) then\n\t\t\tdrawTarget = wep\n\t\t\tdrawFunction = wep.DoDrawCrosshair\n\t\tend\n\n\t\tscreen = trace.HitPos:ToScreen()\n\t\tdrawFunction(drawTarget, screen.x, screen.y, trace)\n\tend\nend\n"
  },
  {
    "path": "plugins/doors/cl_plugin.lua",
    "content": "\n-- luacheck: globals ACCESS_LABELS\nACCESS_LABELS = {}\nACCESS_LABELS[DOOR_OWNER] = \"owner\"\nACCESS_LABELS[DOOR_TENANT] = \"tenant\"\nACCESS_LABELS[DOOR_GUEST] = \"guest\"\nACCESS_LABELS[DOOR_NONE] = \"none\"\n\nfunction PLUGIN:GetDefaultDoorInfo(door)\n\tlocal owner = IsValid(door:GetDTEntity(0)) and door:GetDTEntity(0) or nil\n\tlocal name = door:GetNetVar(\"title\", door:GetNetVar(\"name\", IsValid(owner) and L\"dTitleOwned\" or L\"dTitle\"))\n\tlocal description = door:GetNetVar(\"ownable\") and L(\"dIsOwnable\") or L(\"dIsNotOwnable\")\n\tlocal color = ix.config.Get(\"color\")\n\tlocal faction = door:GetNetVar(\"faction\")\n\tlocal class = door:GetNetVar(\"class\")\n\n\tif (class) then\n\t\tlocal classData = ix.class.list[class]\n\n\t\tif (classData) then\n\t\t\tif (classData.color) then\n\t\t\t\tcolor = classData.color\n\t\t\tend\n\n\t\t\tif (!owner) then\n\t\t\t\tdescription = L(\"dOwnedBy\", L2(classData.name) or classData.name)\n\t\t\tend\n\t\tend\n\telseif (faction) then\n\t\tlocal info = ix.faction.indices[faction]\n\t\tcolor = team.GetColor(faction)\n\n\t\tif (info and !owner) then\n\t\t\tdescription = L(\"dOwnedBy\", L2(info.name) or info.name)\n\t\tend\n\tend\n\n\tif (owner) then\n\t\tdescription = L(\"dOwnedBy\", owner:GetName())\n\tend\n\n\treturn {\n\t\tname = name,\n\t\tdescription = description,\n\t\tcolor = color\n\t}\nend\n\nfunction PLUGIN:DrawDoorInfo(door, width, position, angles, scale, clientPosition)\n\tlocal alpha = math.max((1 - clientPosition:DistToSqr(door:GetPos()) / 65536) * 255, 0)\n\n\tif (alpha < 1) then\n\t\treturn\n\tend\n\n\tlocal info = hook.Run(\"GetDoorInfo\", door) or self:GetDefaultDoorInfo(door)\n\n\tif (!istable(info) or table.IsEmpty(info)) then\n\t\treturn\n\tend\n\n\t-- title + background\n\tsurface.SetFont(\"ix3D2DMediumFont\")\n\tlocal nameWidth, nameHeight = surface.GetTextSize(info.name)\n\n\tderma.SkinFunc(\"DrawImportantBackground\", -width * 0.5, -nameHeight * 0.5,\n\t\twidth, nameHeight, ColorAlpha(info.color, alpha * 0.5))\n\n\tsurface.SetTextColor(ColorAlpha(color_white, alpha))\n\tsurface.SetTextPos(-nameWidth * 0.5, -nameHeight * 0.5)\n\tsurface.DrawText(info.name)\n\n\t-- description\n\tlocal lines = ix.util.WrapText(info.description, width, \"ix3D2DSmallFont\")\n\tlocal y = nameHeight * 0.5 + 4\n\n\tfor i = 1, #lines do\n\t\tlocal line = lines[i]\n\t\tlocal textWidth, textHeight = surface.GetTextSize(line)\n\n\t\tsurface.SetTextPos(-textWidth * 0.5, y)\n\t\tsurface.DrawText(line)\n\n\t\ty = y + textHeight\n\tend\n\n\t-- background blur\n\tix.util.PushBlur(function()\n\t\tcam.Start3D2D(position, angles, scale)\n\t\t\tsurface.SetDrawColor(11, 11, 11, math.max(alpha - 100, 0))\n\t\t\tsurface.DrawRect(-width * 0.5, -nameHeight * 0.5, width, y + nameHeight * 0.5 + 4)\n\t\tcam.End3D2D()\n\tend)\nend\n\nfunction PLUGIN:PostDrawTranslucentRenderables(bDepth, bSkybox)\n\tif (bDepth or bSkybox or !LocalPlayer():GetCharacter()) then\n\t\treturn\n\tend\n\n\tlocal entities = ents.FindInSphere(EyePos(), 256)\n\tlocal clientPosition = LocalPlayer():GetPos()\n\n\tfor _, v in ipairs(entities) do\n\t\tif (!IsValid(v) or !v:IsDoor() or !v:GetNetVar(\"visible\")) then\n\t\t\tcontinue\n\t\tend\n\n\t\tlocal color = v:GetColor()\n\n\t\tif (v:IsEffectActive(EF_NODRAW) or color.a <= 0) then\n\t\t\tcontinue\n\t\tend\n\n\t\tlocal position = v:LocalToWorld(v:OBBCenter())\n\t\tlocal mins, maxs = v:GetCollisionBounds()\n\t\tlocal width = 0\n\t\tlocal size = maxs - mins\n\t\tlocal trace = {\n\t\t\tcollisiongroup = COLLISION_GROUP_WORLD,\n\t\t\tignoreworld = true,\n\t\t\tendpos = position\n\t\t}\n\n\t\t-- trace from shortest side to center to get correct position for rendering\n\t\tif (size.z < size.x and size.z < size.y) then\n\t\t\ttrace.start = position - v:GetUp() * size.z\n\t\t\twidth = size.y\n\t\telseif (size.x < size.y) then\n\t\t\ttrace.start = position - v:GetForward() * size.x\n\t\t\twidth = size.y\n\t\telseif (size.y < size.x) then\n\t\t\ttrace.start = position - v:GetRight() * size.y\n\t\t\twidth = size.x\n\t\tend\n\n\t\twidth = math.max(width, 12)\n\t\ttrace = util.TraceLine(trace)\n\n\t\tlocal angles = trace.HitNormal:Angle()\n\t\tlocal anglesOpposite = trace.HitNormal:Angle()\n\n\t\tangles:RotateAroundAxis(angles:Forward(), 90)\n\t\tangles:RotateAroundAxis(angles:Right(), 90)\n\t\tanglesOpposite:RotateAroundAxis(anglesOpposite:Forward(), 90)\n\t\tanglesOpposite:RotateAroundAxis(anglesOpposite:Right(), -90)\n\n\t\tlocal positionFront = trace.HitPos - (((position - trace.HitPos):Length() * 2) + 1) * trace.HitNormal\n\t\tlocal positionOpposite = trace.HitPos + (trace.HitNormal * 2)\n\n\t\tif (trace.HitNormal:Dot((clientPosition - position):GetNormalized()) < 0) then\n\t\t\t-- draw front\n\t\t\tcam.Start3D2D(positionFront, angles, 0.1)\n\t\t\t\tself:DrawDoorInfo(v, width * 8, positionFront, angles, 0.1, clientPosition)\n\t\t\tcam.End3D2D()\n\t\telse\n\t\t\t-- draw back\n\t\t\tcam.Start3D2D(positionOpposite, anglesOpposite, 0.1)\n\t\t\t\tself:DrawDoorInfo(v, width * 8, positionOpposite, anglesOpposite, 0.1, clientPosition)\n\t\t\tcam.End3D2D()\n\t\tend\n\tend\nend\n\nnet.Receive(\"ixDoorMenu\", function()\n\tif (IsValid(ix.gui.door)) then\n\t\treturn ix.gui.door:Remove()\n\tend\n\n\tlocal door = net.ReadEntity()\n\tlocal access = net.ReadTable()\n\n\tif (IsValid(door) and !table.IsEmpty(access)) then\n\t\tlocal entity = net.ReadEntity()\n\n\t\tix.gui.door = vgui.Create(\"ixDoorMenu\")\n\t\tix.gui.door:SetDoor(door, access, entity)\n\tend\nend)\n\nnet.Receive(\"ixDoorPermission\", function()\n\tlocal door = net.ReadEntity()\n\n\tif (!IsValid(door)) then\n\t\treturn\n\tend\n\n\tlocal target = net.ReadEntity()\n\tlocal access = net.ReadUInt(4)\n\n\tlocal panel = door.ixPanel\n\n\tif (IsValid(panel) and IsValid(target)) then\n\t\tpanel.access[target] = access\n\n\t\tfor _, v in ipairs(panel.access:GetLines()) do\n\t\t\tif (v.player == target) then\n\t\t\t\tv:SetColumnText(2, L(ACCESS_LABELS[access or 0]))\n\n\t\t\t\treturn\n\t\t\tend\n\t\tend\n\tend\nend)\n"
  },
  {
    "path": "plugins/doors/derma/cl_door.lua",
    "content": "\nlocal PANEL = {}\n\nlocal function DoorSetPermission(door, target, permission)\n\tnet.Start(\"ixDoorPermission\")\n\t\tnet.WriteEntity(door)\n\t\tnet.WriteEntity(target)\n\t\tnet.WriteUInt(permission, 4)\n\tnet.SendToServer()\nend\n\nfunction PANEL:Init()\n\tself:SetSize(280, 240)\n\tself:SetTitle(L\"doorSettings\")\n\tself:Center()\n\tself:MakePopup()\n\n\tself.access = self:Add(\"DListView\")\n\tself.access:Dock(FILL)\n\tself.access:AddColumn(L\"name\").Header:SetTextColor(Color(25, 25, 25))\n\tself.access:AddColumn(L\"access\").Header:SetTextColor(Color(25, 25, 25))\n\tself.access.OnClickLine = function(this, line, selected)\n\t\tif (IsValid(line.player)) then\n\t\t\tlocal menu = DermaMenu()\n\t\t\t\tmenu:AddOption(L\"tenant\", function()\n\t\t\t\t\tif (self.accessData and self.accessData[line.player] != DOOR_TENANT) then\n\t\t\t\t\t\tDoorSetPermission(self.door, line.player, DOOR_TENANT)\n\t\t\t\t\tend\n\t\t\t\tend):SetImage(\"icon16/user_add.png\")\n\t\t\t\tmenu:AddOption(L\"guest\", function()\n\t\t\t\t\tif (self.accessData and self.accessData[line.player] != DOOR_GUEST) then\n\t\t\t\t\t\tDoorSetPermission(self.door, line.player, DOOR_GUEST)\n\t\t\t\t\tend\n\t\t\t\tend):SetImage(\"icon16/user_green.png\")\n\t\t\t\tmenu:AddOption(L\"none\", function()\n\t\t\t\t\tif (self.accessData and self.accessData[line.player] != DOOR_NONE) then\n\t\t\t\t\t\tDoorSetPermission(self.door, line.player, DOOR_NONE)\n\t\t\t\t\tend\n\t\t\t\tend):SetImage(\"icon16/user_red.png\")\n\t\t\tmenu:Open()\n\t\tend\n\tend\nend\n\nfunction PANEL:SetDoor(door, access, door2)\n\tdoor.ixPanel = self\n\n\tself.accessData = access\n\tself.door = door\n\n\tfor _, v in player.Iterator() do\n\t\tif (v != LocalPlayer() and v:GetCharacter()) then\n\t\t\tself.access:AddLine(v:Name(), L(ACCESS_LABELS[access[v] or 0])).player = v\n\t\tend\n\tend\n\n\tif (self:CheckAccess(DOOR_OWNER)) then\n\t\tself.sell = self:Add(\"DButton\")\n\t\tself.sell:Dock(BOTTOM)\n\t\tself.sell:SetText(L\"sell\")\n\t\tself.sell:SetTextColor(color_white)\n\t\tself.sell:DockMargin(0, 5, 0, 0)\n\t\tself.sell.DoClick = function(this)\n\t\t\tself:Remove()\n\t\t\tix.command.Send(\"doorsell\")\n\t\tend\n\tend\n\n\tif (self:CheckAccess(DOOR_TENANT)) then\n\t\tself.name = self:Add(\"DTextEntry\")\n\t\tself.name:Dock(TOP)\n\t\tself.name:DockMargin(0, 0, 0, 5)\n\t\tself.name.Think = function(this)\n\t\t\tif (!this:IsEditing()) then\n\t\t\t\tlocal entity = IsValid(door2) and door2 or door\n\n\t\t\t\tself.name:SetText(entity:GetNetVar(\"title\", L\"dTitleOwned\"))\n\t\t\tend\n\t\tend\n\t\tself.name.OnEnter = function(this)\n\t\t\tix.command.Send(\"doorsettitle\", this:GetText())\n\t\tend\n\tend\nend\n\nfunction PANEL:CheckAccess(access)\n\taccess = access or DOOR_GUEST\n\n\tif ((self.accessData[LocalPlayer()] or 0) >= access) then\n\t\treturn true\n\tend\n\n\treturn false\nend\n\nfunction PANEL:Think()\n\tif (self.accessData and !IsValid(self.door) and self:CheckAccess()) then\n\t\tself:Remove()\n\tend\nend\n\nvgui.Register(\"ixDoorMenu\", PANEL, \"DFrame\")\n"
  },
  {
    "path": "plugins/doors/entities/weapons/ix_keys.lua",
    "content": "\nAddCSLuaFile()\n\nif (CLIENT) then\n\tSWEP.PrintName = \"Keys\"\n\tSWEP.Slot = 0\n\tSWEP.SlotPos = 2\n\tSWEP.DrawAmmo = false\n\tSWEP.DrawCrosshair = false\nend\n\nSWEP.Author = \"Chessnut\"\nSWEP.Instructions = \"Primary Fire: Lock\\nSecondary Fire: Unlock\"\nSWEP.Purpose = \"Hitting things and knocking on doors.\"\nSWEP.Drop = false\n\nSWEP.ViewModelFOV = 45\nSWEP.ViewModelFlip = false\nSWEP.AnimPrefix\t = \"rpg\"\n\nSWEP.ViewTranslation = 4\n\nSWEP.Primary.ClipSize = -1\nSWEP.Primary.DefaultClip = -1\nSWEP.Primary.Automatic = false\nSWEP.Primary.Ammo = \"\"\nSWEP.Primary.Damage = 5\nSWEP.Primary.Delay = 0.75\n\nSWEP.Secondary.ClipSize = -1\nSWEP.Secondary.DefaultClip = 0\nSWEP.Secondary.Automatic = false\nSWEP.Secondary.Ammo = \"\"\n\nSWEP.ViewModel = Model(\"models/weapons/c_arms_animations.mdl\")\nSWEP.WorldModel = \"\"\n\nSWEP.UseHands = false\nSWEP.LowerAngles = Angle(0, 5, -14)\nSWEP.LowerAngles2 = Angle(0, 5, -22)\n\nSWEP.IsAlwaysLowered = true\nSWEP.FireWhenLowered = true\nSWEP.HoldType = \"passive\"\n\n-- luacheck: globals ACT_VM_FISTS_DRAW ACT_VM_FISTS_HOLSTER\nACT_VM_FISTS_DRAW = 2\nACT_VM_FISTS_HOLSTER = 1\n\nfunction SWEP:Holster()\n\tif (!IsValid(self.Owner)) then\n\t\treturn\n\tend\n\n\tlocal viewModel = self.Owner:GetViewModel()\n\n\tif (IsValid(viewModel)) then\n\t\tviewModel:SetPlaybackRate(1)\n\t\tviewModel:ResetSequence(ACT_VM_FISTS_HOLSTER)\n\tend\n\n\treturn true\nend\n\nfunction SWEP:Precache()\nend\n\nfunction SWEP:Initialize()\n\tself:SetHoldType(self.HoldType)\nend\n\nfunction SWEP:PrimaryAttack()\n\tlocal time = ix.config.Get(\"doorLockTime\", 1)\n\tlocal time2 = math.max(time, 1)\n\n\tself:SetNextPrimaryFire(CurTime() + time2)\n\tself:SetNextSecondaryFire(CurTime() + time2)\n\n\tif (!IsFirstTimePredicted()) then\n\t\treturn\n\tend\n\n\tif (CLIENT) then\n\t\treturn\n\tend\n\n\tlocal data = {}\n\t\tdata.start = self.Owner:GetShootPos()\n\t\tdata.endpos = data.start + self.Owner:GetAimVector()*96\n\t\tdata.filter = self.Owner\n\tlocal entity = util.TraceLine(data).Entity\n\n\t--[[\n\t\tLocks the entity if the contiditon fits:\n\t\t\t1. The entity is door and client has access to the door.\n\t\t\t2. The entity is vehicle and the \"owner\" variable is same as client's character ID.\n\t--]]\n\tif (IsValid(entity) and\n\t\t(\n\t\t\t(entity:IsDoor() and entity:CheckDoorAccess(self.Owner)) or\n\t\t\t(entity:IsVehicle() and entity.CPPIGetOwner and entity:CPPIGetOwner() == self.Owner)\n\t\t)\n\t) then\n\t\tself.Owner:SetAction(\"@locking\", time, function()\n\t\t\tself:ToggleLock(entity, true)\n\t\tend)\n\n\t\treturn\n\tend\nend\n\nfunction SWEP:ToggleLock(door, state)\n\tif (IsValid(self.Owner) and self.Owner:GetPos():Distance(door:GetPos()) > 96) then\n\t\treturn\n\tend\n\n\tif (door:IsDoor()) then\n\t\tlocal partner = door:GetDoorPartner()\n\n\t\tif (state) then\n\t\t\tif (IsValid(partner)) then\n\t\t\t\tpartner:Fire(\"lock\")\n\t\t\tend\n\n\t\t\tdoor:Fire(\"lock\")\n\t\t\tself.Owner:EmitSound(\"doors/door_latch3.wav\")\n\n\t\t\thook.Run(\"PlayerLockedDoor\", self.Owner, door, partner)\n\t\telse\n\t\t\tif (IsValid(partner)) then\n\t\t\t\tpartner:Fire(\"unlock\")\n\t\t\tend\n\n\t\t\tdoor:Fire(\"unlock\")\n\t\t\tself.Owner:EmitSound(\"doors/door_latch1.wav\")\n\n\t\t\thook.Run(\"PlayerUnlockedDoor\", self.Owner, door, partner)\n\t\tend\n\telseif (door:IsVehicle()) then\n\t\tif (state) then\n\t\t\tdoor:Fire(\"lock\")\n\n\t\t\tif (door.IsSimfphyscar) then\n\t\t\t\tdoor.IsLocked = true\n\t\t\tend\n\n\t\t\tself.Owner:EmitSound(\"doors/door_latch3.wav\")\n\t\t\thook.Run(\"PlayerLockedVehicle\", self.Owner, door)\n\t\telse\n\t\t\tdoor:Fire(\"unlock\")\n\n\t\t\tif (door.IsSimfphyscar) then\n\t\t\t\tdoor.IsLocked = nil\n\t\t\tend\n\n\t\t\tself.Owner:EmitSound(\"doors/door_latch1.wav\")\n\t\t\thook.Run(\"PlayerUnlockedVehicle\", self.Owner, door)\n\t\tend\n\tend\nend\n\nfunction SWEP:SecondaryAttack()\n\tlocal time = ix.config.Get(\"doorLockTime\", 1)\n\tlocal time2 = math.max(time, 1)\n\n\tself:SetNextPrimaryFire(CurTime() + time2)\n\tself:SetNextSecondaryFire(CurTime() + time2)\n\n\tif (!IsFirstTimePredicted()) then\n\t\treturn\n\tend\n\n\tif (CLIENT) then\n\t\treturn\n\tend\n\n\tlocal data = {}\n\t\tdata.start = self.Owner:GetShootPos()\n\t\tdata.endpos = data.start + self.Owner:GetAimVector()*96\n\t\tdata.filter = self.Owner\n\tlocal entity = util.TraceLine(data).Entity\n\n\n\t--[[\n\t\tUnlocks the entity if the contiditon fits:\n\t\t\t1. The entity is door and client has access to the door.\n\t\t\t2. The entity is vehicle and the \"owner\" variable is same as client's character ID.\n\t]]--\n\tif (IsValid(entity) and\n\t\t(\n\t\t\t(entity:IsDoor() and entity:CheckDoorAccess(self.Owner)) or\n\t\t\t(entity:IsVehicle() and entity.CPPIGetOwner and entity:CPPIGetOwner() == self.Owner)\n\t\t)\n\t) then\n\t\tself.Owner:SetAction(\"@unlocking\", time, function()\n\t\t\tself:ToggleLock(entity, false)\n\t\tend)\n\n\t\treturn\n\tend\nend\n"
  },
  {
    "path": "plugins/doors/sh_commands.lua",
    "content": "\nlocal PLUGIN = PLUGIN\n\nix.command.Add(\"DoorSell\", {\n\tdescription = \"@cmdDoorSell\",\n\tOnRun = function(self, client, arguments)\n\t\t-- Get the entity 96 units infront of the player.\n\t\tlocal data = {}\n\t\t\tdata.start = client:GetShootPos()\n\t\t\tdata.endpos = data.start + client:GetAimVector() * 96\n\t\t\tdata.filter = client\n\t\tlocal trace = util.TraceLine(data)\n\t\tlocal entity = trace.Entity\n\n\t\t-- Check if the entity is a valid door.\n\t\tif (IsValid(entity) and entity:IsDoor() and !entity:GetNetVar(\"disabled\")) then\n\t\t\t-- Check if the player owners the door.\n\t\t\tif (client == entity:GetDTEntity(0)) then\n\t\t\t\tentity = IsValid(entity.ixParent) and entity.ixParent or entity\n\n\t\t\t\t-- Get the price that the door is sold for.\n\t\t\t\tlocal price = math.Round(entity:GetNetVar(\"price\", ix.config.Get(\"doorCost\")) * ix.config.Get(\"doorSellRatio\"))\n\t\t\t\tlocal character = client:GetCharacter()\n\n\t\t\t\t-- Remove old door information.\n\t\t\t\tentity:RemoveDoorAccessData()\n\n\t\t\t\tlocal doors = character:GetVar(\"doors\") or {}\n\n\t\t\t\tfor k, v in ipairs(doors) do\n\t\t\t\t\tif (v == entity) then\n\t\t\t\t\t\ttable.remove(doors, k)\n\t\t\t\t\tend\n\t\t\t\tend\n\n\t\t\t\tcharacter:SetVar(\"doors\", doors, true)\n\n\t\t\t\t-- Take their money and notify them.\n\t\t\t\tcharacter:GiveMoney(price)\n\t\t\t\thook.Run(\"OnPlayerPurchaseDoor\", client, entity, false, PLUGIN.CallOnDoorChildren)\n\n\t\t\t\tix.log.Add(client, \"selldoor\")\n\t\t\t\treturn \"@dSold\", ix.currency.Get(price)\n\t\t\telse\n\t\t\t\t-- Otherwise tell them they can not.\n\t\t\t\treturn \"@notOwner\"\n\t\t\tend\n\t\telse\n\t\t\t-- Tell the player the door isn't valid.\n\t\t\treturn \"@dNotValid\"\n\t\tend\n\tend\n})\n\nix.command.Add(\"DoorBuy\", {\n\tdescription = \"@cmdDoorBuy\",\n\tOnRun = function(self, client, arguments)\n\t\t-- Get the entity 96 units infront of the player.\n\t\tlocal data = {}\n\t\t\tdata.start = client:GetShootPos()\n\t\t\tdata.endpos = data.start + client:GetAimVector() * 96\n\t\t\tdata.filter = client\n\t\tlocal trace = util.TraceLine(data)\n\t\tlocal entity = trace.Entity\n\n\t\t-- Check if the entity is a valid door.\n\t\tif (IsValid(entity) and entity:IsDoor() and !entity:GetNetVar(\"disabled\")) then\n\t\t\tif (!entity:GetNetVar(\"ownable\") or entity:GetNetVar(\"faction\") or entity:GetNetVar(\"class\")) then\n\t\t\t\treturn \"@dNotAllowedToOwn\"\n\t\t\tend\n\n\t\t\tif (IsValid(entity:GetDTEntity(0))) then\n\t\t\t\treturn \"@dOwnedBy\", entity:GetDTEntity(0):Name()\n\t\t\tend\n\n\t\t\tentity = IsValid(entity.ixParent) and entity.ixParent or entity\n\n\t\t\t-- Get the price that the door is bought for.\n\t\t\tlocal price = entity:GetNetVar(\"price\", ix.config.Get(\"doorCost\"))\n\t\t\tlocal character = client:GetCharacter()\n\n\t\t\t-- Check if the player can actually afford it.\n\t\t\tif (character:HasMoney(price)) then\n\t\t\t\t-- Set the door to be owned by this player.\n\t\t\t\tentity:SetDTEntity(0, client)\n\t\t\t\tentity.ixAccess = {\n\t\t\t\t\t[client] = DOOR_OWNER\n\t\t\t\t}\n\n\t\t\t\tPLUGIN:CallOnDoorChildren(entity, function(child)\n\t\t\t\t\tchild:SetDTEntity(0, client)\n\t\t\t\tend)\n\n\t\t\t\tlocal doors = character:GetVar(\"doors\") or {}\n\t\t\t\t\tdoors[#doors + 1] = entity\n\t\t\t\tcharacter:SetVar(\"doors\", doors, true)\n\n\t\t\t\t-- Take their money and notify them.\n\t\t\t\tcharacter:TakeMoney(price)\n\t\t\t\thook.Run(\"OnPlayerPurchaseDoor\", client, entity, true, PLUGIN.CallOnDoorChildren)\n\n\t\t\t\tix.log.Add(client, \"buydoor\")\n\t\t\t\treturn \"@dPurchased\", ix.currency.Get(price)\n\t\t\telse\n\t\t\t\t-- Otherwise tell them they can not.\n\t\t\t\treturn \"@canNotAfford\"\n\t\t\tend\n\t\telse\n\t\t\t-- Tell the player the door isn't valid.\n\t\t\treturn \"@dNotValid\"\n\t\tend\n\tend\n})\n\nix.command.Add(\"DoorSetUnownable\", {\n\tdescription = \"@cmdDoorSetUnownable\",\n\tprivilege = \"Manage Doors\",\n\tadminOnly = true,\n\targuments = ix.type.text,\n\tOnRun = function(self, client, name)\n\t\t-- Get the door the player is looking at.\n\t\tlocal entity = client:GetEyeTrace().Entity\n\n\t\t-- Validate it is a door.\n\t\tif (IsValid(entity) and entity:IsDoor() and !entity:GetNetVar(\"disabled\")) then\n\t\t\t-- Set it so it is unownable.\n\t\t\tentity:SetNetVar(\"ownable\", nil)\n\n\t\t\t-- Change the name of the door if needed.\n\t\t\tif (name:find(\"%S\")) then\n\t\t\t\tentity:SetNetVar(\"name\", name)\n\t\t\tend\n\n\t\t\tPLUGIN:CallOnDoorChildren(entity, function(child)\n\t\t\t\tchild:SetNetVar(\"ownable\", nil)\n\n\t\t\t\tif (name:find(\"%S\")) then\n\t\t\t\t\tchild:SetNetVar(\"name\", name)\n\t\t\t\tend\n\t\t\tend)\n\n\t\t\t-- Save the door information.\n\t\t\tPLUGIN:SaveDoorData()\n\t\t\treturn \"@dMadeUnownable\"\n\t\telse\n\t\t\t-- Tell the player the door isn't valid.\n\t\t\treturn \"@dNotValid\"\n\t\tend\n\tend\n})\n\nix.command.Add(\"DoorSetOwnable\", {\n\tdescription = \"@cmdDoorSetOwnable\",\n\tprivilege = \"Manage Doors\",\n\tadminOnly = true,\n\targuments = ix.type.text,\n\tOnRun = function(self, client, name)\n\t\t-- Get the door the player is looking at.\n\t\tlocal entity = client:GetEyeTrace().Entity\n\n\t\t-- Validate it is a door.\n\t\tif (IsValid(entity) and entity:IsDoor() and !entity:GetNetVar(\"disabled\")) then\n\t\t\t-- Set it so it is ownable.\n\t\t\tentity:SetNetVar(\"ownable\", true)\n\t\t\tentity:SetNetVar(\"visible\", true)\n\n\t\t\t-- Update the name.\n\t\t\tif (name:find(\"%S\")) then\n\t\t\t\tentity:SetNetVar(\"name\", name)\n\t\t\tend\n\n\t\t\tPLUGIN:CallOnDoorChildren(entity, function(child)\n\t\t\t\tchild:SetNetVar(\"ownable\", true)\n\t\t\t\tchild:SetNetVar(\"visible\", true)\n\n\t\t\t\tif (name:find(\"%S\")) then\n\t\t\t\t\tchild:SetNetVar(\"name\", name)\n\t\t\t\tend\n\t\t\tend)\n\n\t\t\t-- Save the door information.\n\t\t\tPLUGIN:SaveDoorData()\n\t\t\treturn \"@dMadeOwnable\"\n\t\telse\n\t\t\t-- Tell the player the door isn't valid.\n\t\t\treturn \"@dNotValid\"\n\t\tend\n\tend\n})\n\nix.command.Add(\"DoorSetFaction\", {\n\tdescription = \"@cmdDoorSetFaction\",\n\tprivilege = \"Manage Doors\",\n\tadminOnly = true,\n\targuments = bit.bor(ix.type.text, ix.type.optional),\n\tOnRun = function(self, client, name)\n\t\t-- Get the door the player is looking at.\n\t\tlocal entity = client:GetEyeTrace().Entity\n\n\t\t-- Validate it is a door.\n\t\tif (IsValid(entity) and entity:IsDoor() and !entity:GetNetVar(\"disabled\")) then\n\t\t\tif (!name or name == \"\") then\n\t\t\t\tentity.ixFactionID = nil\n\t\t\t\tentity:SetNetVar(\"faction\", nil)\n\n\t\t\t\tPLUGIN:CallOnDoorChildren(entity, function()\n\t\t\t\t\tentity.ixFactionID = nil\n\t\t\t\t\tentity:SetNetVar(\"faction\", nil)\n\t\t\t\tend)\n\n\t\t\t\tPLUGIN:SaveDoorData()\n\t\t\t\treturn \"@dRemoveFaction\"\n\t\t\tend\n\n\t\t\tlocal faction\n\n\t\t\t-- Loop through each faction, checking the uniqueID and name.\n\t\t\tfor k, v in pairs(ix.faction.teams) do\n\t\t\t\tif (ix.util.StringMatches(k, name) or ix.util.StringMatches(L(v.name, client), name)) then\n\t\t\t\t\t-- This faction matches the provided string.\n\t\t\t\t\tfaction = v\n\n\t\t\t\t\t-- Escape the loop.\n\t\t\t\t\tbreak\n\t\t\t\tend\n\t\t\tend\n\n\t\t\t-- Check if a faction was found.\n\t\t\tif (faction) then\n\t\t\t\tentity.ixFactionID = faction.uniqueID\n\t\t\t\tentity:SetNetVar(\"faction\", faction.index)\n\n\t\t\t\tPLUGIN:CallOnDoorChildren(entity, function()\n\t\t\t\t\tentity.ixFactionID = faction.uniqueID\n\t\t\t\t\tentity:SetNetVar(\"faction\", faction.index)\n\t\t\t\tend)\n\n\t\t\t\tPLUGIN:SaveDoorData()\n\t\t\t\treturn \"@dSetFaction\", L(faction.name, client)\n\t\t\t-- The faction was not found.\n\t\t\telse\n\t\t\t\treturn \"@invalidFaction\"\n\t\t\tend\n\t\tend\n\tend\n})\n\nix.command.Add(\"DoorSetDisabled\", {\n\tdescription = \"@cmdDoorSetDisabled\",\n\tprivilege = \"Manage Doors\",\n\tadminOnly = true,\n\targuments = ix.type.bool,\n\tOnRun = function(self, client, bDisabled)\n\t\t-- Get the door the player is looking at.\n\t\tlocal entity = client:GetEyeTrace().Entity\n\n\t\t-- Validate it is a door.\n\t\tif (IsValid(entity) and entity:IsDoor()) then\n\t\t\t-- Set it so it is ownable.\n\t\t\tentity:SetNetVar(\"disabled\", bDisabled)\n\n\t\t\tPLUGIN:CallOnDoorChildren(entity, function(child)\n\t\t\t\tchild:SetNetVar(\"disabled\", bDisabled)\n\t\t\tend)\n\n\t\t\tPLUGIN:SaveDoorData()\n\n\t\t\t-- Tell the player they have made the door (un)disabled.\n\t\t\treturn \"@dSet\" .. (bDisabled and \"\" or \"Not\") .. \"Disabled\"\n\t\telse\n\t\t\t-- Tell the player the door isn't valid.\n\t\t\treturn \"@dNotValid\"\n\t\tend\n\tend\n})\n\nix.command.Add(\"DoorSetTitle\", {\n\tdescription = \"@cmdDoorSetTitle\",\n\targuments = ix.type.text,\n\tOnRun = function(self, client, name)\n\t\t-- Get the door infront of the player.\n\t\tlocal data = {}\n\t\t\tdata.start = client:GetShootPos()\n\t\t\tdata.endpos = data.start + client:GetAimVector() * 96\n\t\t\tdata.filter = client\n\t\tlocal trace = util.TraceLine(data)\n\t\tlocal entity = trace.Entity\n\n\t\t-- Validate the door.\n\t\tif (IsValid(entity) and entity:IsDoor() and !entity:GetNetVar(\"disabled\")) then\n\t\t\t-- Make sure the name contains actual characters.\n\t\t\tif (!name:find(\"%S\")) then\n\t\t\t\treturn \"@invalidArg\", 1\n\t\t\tend\n\n\t\t\t--[[\n\t\t\t\tNOTE: Here, we are setting two different networked names.\n\t\t\t\tThe title is a temporary name, while the other name is the\n\t\t\t\tdefault name for the door. The reason for this is so when the\n\t\t\t\tserver closes while someone owns the door, it doesn't save THEIR\n\t\t\t\ttitle, which could lead to unwanted things.\n\t\t\t--]]\n\n\t\t\tname = name:utf8sub(1, 24)\n\n\t\t\t-- Check if they are allowed to change the door's name.\n\t\t\tif (entity:CheckDoorAccess(client, DOOR_TENANT)) then\n\t\t\t\tentity:SetNetVar(\"title\", name)\n\t\t\telseif (CAMI.PlayerHasAccess(client, \"Helix - Manage Doors\", nil)) then\n\t\t\t\tentity:SetNetVar(\"name\", name)\n\n\t\t\t\tPLUGIN:CallOnDoorChildren(entity, function(child)\n\t\t\t\t\tchild:SetNetVar(\"name\", name)\n\t\t\t\tend)\n\t\t\telse\n\t\t\t\t-- Otherwise notify the player he/she can't.\n\t\t\t\treturn \"@notOwner\"\n\t\t\tend\n\t\telse\n\t\t\t-- Notification of the door not being valid.\n\t\t\treturn \"@dNotValid\"\n\t\tend\n\tend\n})\n\nix.command.Add(\"DoorSetParent\", {\n\tdescription = \"@cmdDoorSetParent\",\n\tprivilege = \"Manage Doors\",\n\tadminOnly = true,\n\tOnRun = function(self, client, arguments)\n\t\t-- Get the door the player is looking at.\n\t\tlocal entity = client:GetEyeTrace().Entity\n\n\t\t-- Validate it is a door.\n\t\tif (IsValid(entity) and entity:IsDoor() and !entity:GetNetVar(\"disabled\")) then\n\t\t\tclient.ixDoorParent = entity\n\t\t\treturn \"@dSetParentDoor\"\n\t\telse\n\t\t\t-- Tell the player the door isn't valid.\n\t\t\treturn \"@dNotValid\"\n\t\tend\n\tend\n})\n\nix.command.Add(\"DoorSetChild\", {\n\tdescription = \"@cmdDoorSetChild\",\n\tprivilege = \"Manage Doors\",\n\tadminOnly = true,\n\tOnRun = function(self, client, arguments)\n\t\t-- Get the door the player is looking at.\n\t\tlocal entity = client:GetEyeTrace().Entity\n\n\t\t-- Validate it is a door.\n\t\tif (IsValid(entity) and entity:IsDoor() and !entity:GetNetVar(\"disabled\")) then\n\t\t\tif (client.ixDoorParent == entity) then\n\t\t\t\treturn \"@dCanNotSetAsChild\"\n\t\t\tend\n\n\t\t\t-- Check if the player has set a door as a parent.\n\t\t\tif (IsValid(client.ixDoorParent)) then\n\t\t\t\t-- Add the door to the parent's list of children.\n\t\t\t\tclient.ixDoorParent.ixChildren = client.ixDoorParent.ixChildren or {}\n\t\t\t\tclient.ixDoorParent.ixChildren[entity:MapCreationID()] = true\n\n\t\t\t\t-- Set the door's parent to the parent.\n\t\t\t\tentity.ixParent = client.ixDoorParent\n\n\t\t\t\t-- Save the door information.\n\t\t\t\tPLUGIN:SaveDoorData()\n\t\t\t\tPLUGIN:CopyParentDoor(entity)\n\n\t\t\t\treturn \"@dAddChildDoor\"\n\t\t\telse\n\t\t\t\t-- Tell the player they do not have a door parent.\n\t\t\t\treturn \"@dNoParentDoor\"\n\t\t\tend\n\t\telse\n\t\t\t-- Tell the player the door isn't valid.\n\t\t\treturn \"@dNotValid\"\n\t\tend\n\tend\n})\n\nix.command.Add(\"DoorRemoveChild\", {\n\tdescription = \"@cmdDoorRemoveChild\",\n\tprivilege = \"Manage Doors\",\n\tadminOnly = true,\n\tOnRun = function(self, client, arguments)\n\t\t-- Get the door the player is looking at.\n\t\tlocal entity = client:GetEyeTrace().Entity\n\n\t\t-- Validate it is a door.\n\t\tif (IsValid(entity) and entity:IsDoor() and !entity:GetNetVar(\"disabled\")) then\n\t\t\tif (client.ixDoorParent == entity) then\n\t\t\t\tPLUGIN:CallOnDoorChildren(entity, function(child)\n\t\t\t\t\tchild.ixParent = nil\n\t\t\t\tend)\n\n\t\t\t\tentity.ixChildren = nil\n\t\t\t\treturn \"@dRemoveChildren\"\n\t\t\tend\n\n\t\t\t-- Check if the player has set a door as a parent.\n\t\t\tif (IsValid(entity.ixParent) and entity.ixParent.ixChildren) then\n\t\t\t\t-- Remove the door from the list of children.\n\t\t\t\tentity.ixParent.ixChildren[entity:MapCreationID()] = nil\n\t\t\t\t-- Remove the variable for the parent.\n\t\t\t\tentity.ixParent = nil\n\n\t\t\t\tPLUGIN:SaveDoorData()\n\t\t\t\treturn \"@dRemoveChildDoor\"\n\t\t\tend\n\t\telse\n\t\t\t-- Tell the player the door isn't valid.\n\t\t\treturn \"@dNotValid\"\n\t\tend\n\tend\n})\n\nix.command.Add(\"DoorSetHidden\", {\n\tdescription = \"@cmdDoorSetHidden\",\n\tprivilege = \"Manage Doors\",\n\tadminOnly = true,\n\targuments = ix.type.bool,\n\tOnRun = function(self, client, bHidden)\n\t\t-- Get the door the player is looking at.\n\t\tlocal entity = client:GetEyeTrace().Entity\n\n\t\t-- Validate it is a door.\n\t\tif (IsValid(entity) and entity:IsDoor()) then\n\t\t\tentity:SetNetVar(\"visible\", !bHidden)\n\n\t\t\tPLUGIN:CallOnDoorChildren(entity, function(child)\n\t\t\t\tchild:SetNetVar(\"visible\", !bHidden)\n\t\t\tend)\n\n\t\t\tPLUGIN:SaveDoorData()\n\n\t\t\t-- Tell the player they have made the door (un)hidden.\n\t\t\treturn \"@dSet\" .. (bHidden and \"\" or \"Not\") .. \"Hidden\"\n\t\telse\n\t\t\t-- Tell the player the door isn't valid.\n\t\t\treturn \"@dNotValid\"\n\t\tend\n\tend\n})\n\nix.command.Add(\"DoorSetClass\", {\n\tdescription = \"@cmdDoorSetClass\",\n\tprivilege = \"Manage Doors\",\n\tadminOnly = true,\n\targuments = bit.bor(ix.type.text, ix.type.optional),\n\tOnRun = function(self, client, name)\n\t\t-- Get the door the player is looking at.\n\t\tlocal entity = client:GetEyeTrace().Entity\n\n\t\t-- Validate it is a door.\n\t\tif (IsValid(entity) and entity:IsDoor() and !entity:GetNetVar(\"disabled\")) then\n\t\t\tif (!name or name == \"\") then\n\t\t\t\tentity:SetNetVar(\"class\", nil)\n\n\t\t\t\tPLUGIN:CallOnDoorChildren(entity, function()\n\t\t\t\t\tentity:SetNetVar(\"class\", nil)\n\t\t\t\tend)\n\n\t\t\t\tPLUGIN:SaveDoorData()\n\t\t\t\treturn \"@dRemoveClass\"\n\t\t\tend\n\n\t\t\tlocal class, classData\n\n\t\t\tfor k, v in pairs(ix.class.list) do\n\t\t\t\tif (ix.util.StringMatches(v.name, name) or ix.util.StringMatches(L(v.name, client), name)) then\n\t\t\t\t\tclass, classData = k, v\n\n\t\t\t\t\tbreak\n\t\t\t\tend\n\t\t\tend\n\n\t\t\t-- Check if a faction was found.\n\t\t\tif (class) then\n\t\t\t\tentity.ixClassID = class\n\t\t\t\tentity:SetNetVar(\"class\", class)\n\n\t\t\t\tPLUGIN:CallOnDoorChildren(entity, function()\n\t\t\t\t\tentity.ixClassID = class\n\t\t\t\t\tentity:SetNetVar(\"class\", class)\n\t\t\t\tend)\n\n\t\t\t\tPLUGIN:SaveDoorData()\n\t\t\t\treturn \"@dSetClass\", L(classData.name, client)\n\t\t\telse\n\t\t\t\treturn \"@invalidClass\"\n\t\t\tend\n\t\tend\n\tend\n})\n"
  },
  {
    "path": "plugins/doors/sh_plugin.lua",
    "content": "\nlocal PLUGIN = PLUGIN\n\nPLUGIN.name = \"Doors\"\nPLUGIN.author = \"Chessnut\"\nPLUGIN.description = \"A simple door system.\"\n\n-- luacheck: globals DOOR_OWNER DOOR_TENANT DOOR_GUEST DOOR_NONE\nDOOR_OWNER = 3\nDOOR_TENANT = 2\nDOOR_GUEST = 1\nDOOR_NONE = 0\n\nix.util.Include(\"sv_plugin.lua\")\nix.util.Include(\"cl_plugin.lua\")\nix.util.Include(\"sh_commands.lua\")\n\ndo\n\tlocal entityMeta = FindMetaTable(\"Entity\")\n\n\tfunction entityMeta:CheckDoorAccess(client, access)\n\t\tif (!self:IsDoor()) then\n\t\t\treturn false\n\t\tend\n\n\t\taccess = access or DOOR_GUEST\n\n\t\tlocal parent = self.ixParent\n\n\t\tif (IsValid(parent)) then\n\t\t\treturn parent:CheckDoorAccess(client, access)\n\t\tend\n\n\t\tif (hook.Run(\"CanPlayerAccessDoor\", client, self, access)) then\n\t\t\treturn true\n\t\tend\n\n\t\tif (self.ixAccess and (self.ixAccess[client] or 0) >= access) then\n\t\t\treturn true\n\t\tend\n\n\t\treturn false\n\tend\n\n\tif (SERVER) then\n\t\tfunction entityMeta:RemoveDoorAccessData()\n\t\t\tlocal receivers = {}\n\n\t\t\tfor k, _ in pairs(self.ixAccess or {}) do\n\t\t\t\treceivers[#receivers + 1] = k\n\t\t\tend\n\n\t\t\tif (#receivers > 0) then\n\t\t\t\tnet.Start(\"ixDoorMenu\")\n\t\t\t\t\tnet.WriteEntity(self)\n\t\t\t\t\tnet.WriteTable({})\n\t\t\t\tnet.Send(receivers)\n\t\t\tend\n\n\t\t\tself.ixAccess = {}\n\t\t\tself:SetDTEntity(0, nil)\n\n\t\t\t-- Remove door information on child doors\n\t\t\tPLUGIN:CallOnDoorChildren(self, function(child)\n\t\t\t\tchild:SetDTEntity(0, nil)\n\t\t\tend)\n\t\tend\n\tend\nend\n\n-- Configurations for door prices.\nix.config.Add(\"doorCost\", 10, \"The price to purchase a door.\", nil, {\n\tdata = {min = 0, max = 500},\n\tcategory = \"dConfigName\"\n})\nix.config.Add(\"doorSellRatio\", 0.5, \"How much of the door price is returned when selling a door.\", nil, {\n\tdata = {min = 0, max = 1.0, decimals = 1},\n\tcategory = \"dConfigName\"\n})\nix.config.Add(\"doorLockTime\", 1, \"How long it takes to (un)lock a door.\", nil, {\n\tdata = {min = 0, max = 10.0, decimals = 1},\n\tcategory = \"dConfigName\"\n})\n"
  },
  {
    "path": "plugins/doors/sv_plugin.lua",
    "content": "\nutil.AddNetworkString(\"ixDoorMenu\")\nutil.AddNetworkString(\"ixDoorPermission\")\n\n-- Variables for door data.\nlocal variables = {\n\t-- Whether or not the door will be disabled.\n\t\"disabled\",\n\t-- The name of the door.\n\t\"name\",\n\t-- Price of the door.\n\t\"price\",\n\t-- If the door is ownable.\n\t\"ownable\",\n\t-- The faction that owns a door.\n\t\"faction\",\n\t-- The class that owns a door.\n\t\"class\",\n\t-- Whether or not the door will be hidden.\n\t\"visible\"\n}\n\nfunction PLUGIN:CallOnDoorChildren(entity, callback)\n\tlocal parent\n\n\tif (entity.ixChildren) then\n\t\tparent = entity\n\telseif (entity.ixParent) then\n\t\tparent = entity.ixParent\n\tend\n\n\tif (IsValid(parent)) then\n\t\tcallback(parent)\n\n\t\tfor k, _ in pairs(parent.ixChildren) do\n\t\t\tlocal child = ents.GetMapCreatedEntity(k)\n\n\t\t\tif (IsValid(child)) then\n\t\t\t\tcallback(child)\n\t\t\tend\n\t\tend\n\tend\nend\n\nfunction PLUGIN:CopyParentDoor(child)\n\tlocal parent = child.ixParent\n\n\tif (IsValid(parent)) then\n\t\tfor _, v in ipairs(variables) do\n\t\t\tlocal value = parent:GetNetVar(v)\n\n\t\t\tif (child:GetNetVar(v) != value) then\n\t\t\t\tchild:SetNetVar(v, value)\n\t\t\tend\n\t\tend\n\tend\nend\n\n-- Called after the entities have loaded.\nfunction PLUGIN:LoadData()\n\t-- Restore the saved door information.\n\tlocal data = self:GetData()\n\n\tif (!data) then\n\t\treturn\n\tend\n\n\t-- Loop through all of the saved doors.\n\tfor k, v in pairs(data) do\n\t\t-- Get the door entity from the saved ID.\n\t\tlocal entity = ents.GetMapCreatedEntity(k)\n\n\t\t-- Check it is a valid door in-case something went wrong.\n\t\tif (IsValid(entity) and entity:IsDoor()) then\n\t\t\t-- Loop through all of our door variables.\n\t\t\tfor k2, v2 in pairs(v) do\n\t\t\t\tif (k2 == \"children\") then\n\t\t\t\t\tentity.ixChildren = v2\n\n\t\t\t\t\tfor index, _ in pairs(v2) do\n\t\t\t\t\t\tlocal door = ents.GetMapCreatedEntity(index)\n\n\t\t\t\t\t\tif (IsValid(door)) then\n\t\t\t\t\t\t\tdoor.ixParent = entity\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\t\t\t\telseif (k2 == \"faction\") then\n\t\t\t\t\tfor k3, v3 in pairs(ix.faction.teams) do\n\t\t\t\t\t\tif (k3 == v2) then\n\t\t\t\t\t\t\tentity.ixFactionID = k3\n\t\t\t\t\t\t\tentity:SetNetVar(\"faction\", v3.index)\n\n\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\t\t\t\telse\n\t\t\t\t\tentity:SetNetVar(k2, v2)\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend\nend\n\n-- Called before the gamemode shuts down.\nfunction PLUGIN:SaveDoorData()\n\t-- Create an empty table to save information in.\n\tlocal data = {}\n\t\tlocal doors = {}\n\n\t\tfor _, v in ents.Iterator() do\n\t\t\tif (v:IsDoor()) then\n\t\t\t\tdoors[v:MapCreationID()] = v\n\t\t\tend\n\t\tend\n\n\t\tlocal doorData\n\n\t\t-- Loop through doors with information.\n\t\tfor k, v in pairs(doors) do\n\t\t\t-- Another empty table for actual information regarding the door.\n\t\t\tdoorData = {}\n\n\t\t\t-- Save all of the needed variables to the doorData table.\n\t\t\tfor _, v2 in ipairs(variables) do\n\t\t\t\tlocal value = v:GetNetVar(v2)\n\n\t\t\t\tif (value) then\n\t\t\t\t\tdoorData[v2] = v:GetNetVar(v2)\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tif (v.ixChildren) then\n\t\t\t\tdoorData.children = v.ixChildren\n\t\t\tend\n\n\t\t\tif (v.ixClassID) then\n\t\t\t\tdoorData.class = v.ixClassID\n\t\t\tend\n\n\t\t\tif (v.ixFactionID) then\n\t\t\t\tdoorData.faction = v.ixFactionID\n\t\t\tend\n\n\t\t\t-- Add the door to the door information.\n\t\t\tif (!table.IsEmpty(doorData)) then\n\t\t\t\tdata[k] = doorData\n\t\t\tend\n\t\tend\n\t-- Save all of the door information.\n\tself:SetData(data)\nend\n\nfunction PLUGIN:CanPlayerUseDoor(client, entity)\n\tif (entity:GetNetVar(\"disabled\")) then\n\t\treturn false\n\tend\nend\n\n-- Whether or not a player a player has any abilities over the door, such as locking.\nfunction PLUGIN:CanPlayerAccessDoor(client, door, access)\n\tlocal faction = door:GetNetVar(\"faction\")\n\n\t-- If the door has a faction set which the client is a member of, allow access.\n\tif (faction and client:Team() == faction) then\n\t\treturn true\n\tend\n\n\tlocal class = door:GetNetVar(\"class\")\n\n\t-- If the door has a faction set which the client is a member of, allow access.\n\tlocal classData = ix.class.list[class]\n\tlocal charClass = client:GetCharacter():GetClass()\n\tlocal classData2 = ix.class.list[charClass]\n\n\tif (class and classData and classData2) then\n\t\tif (classData.team) then\n\t\t\tif (classData.team != classData2.team) then\n\t\t\t\treturn false\n\t\t\tend\n\t\telse\n\t\t\tif (charClass != class) then\n\t\t\t\treturn false\n\t\t\tend\n\t\tend\n\n\t\treturn true\n\tend\nend\n\nfunction PLUGIN:PostPlayerLoadout(client)\n\tclient:Give(\"ix_keys\")\nend\n\nfunction PLUGIN:ShowTeam(client)\n\tlocal data = {}\n\t\tdata.start = client:GetShootPos()\n\t\tdata.endpos = data.start + client:GetAimVector() * 96\n\t\tdata.filter = client\n\tlocal trace = util.TraceLine(data)\n\tlocal entity = trace.Entity\n\n\tif (IsValid(entity) and entity:IsDoor() and !entity:GetNetVar(\"faction\") and !entity:GetNetVar(\"class\")) then\n\t\tif (entity:CheckDoorAccess(client, DOOR_TENANT)) then\n\t\t\tlocal door = entity\n\n\t\t\tif (IsValid(door.ixParent)) then\n\t\t\t\tdoor = door.ixParent\n\t\t\tend\n\n\t\t\tnet.Start(\"ixDoorMenu\")\n\t\t\t\tnet.WriteEntity(door)\n\t\t\t\tnet.WriteTable(door.ixAccess)\n\t\t\t\tnet.WriteEntity(entity)\n\t\t\tnet.Send(client)\n\t\telseif (!IsValid(entity:GetDTEntity(0))) then\n\t\t\tix.command.Run(client, \"doorbuy\")\n\t\telse\n\t\t\tclient:NotifyLocalized(\"notAllowed\")\n\t\tend\n\n\t\treturn true\n\tend\nend\n\nfunction PLUGIN:PlayerLoadedCharacter(client, curChar, prevChar)\n\tif (prevChar) then\n\t\tlocal doors = prevChar:GetVar(\"doors\") or {}\n\n\t\tfor _, v in ipairs(doors) do\n\t\t\tif (IsValid(v) and v:IsDoor() and v:GetDTEntity(0) == client) then\n\t\t\t\tv:RemoveDoorAccessData()\n\t\t\tend\n\t\tend\n\n\t\tprevChar:SetVar(\"doors\", nil)\n\tend\nend\n\nfunction PLUGIN:PlayerDisconnected(client)\n\tlocal character = client:GetCharacter()\n\n\tif (character) then\n\t\tlocal doors = character:GetVar(\"doors\") or {}\n\n\t\tfor _, v in ipairs(doors) do\n\t\t\tif (IsValid(v) and v:IsDoor() and v:GetDTEntity(0) == client) then\n\t\t\t\tv:RemoveDoorAccessData()\n\t\t\tend\n\t\tend\n\n\t\tcharacter:SetVar(\"doors\", nil)\n\tend\nend\n\nnet.Receive(\"ixDoorPermission\", function(length, client)\n\tlocal door = net.ReadEntity()\n\tlocal target = net.ReadEntity()\n\tlocal access = net.ReadUInt(4)\n\n\tif (IsValid(target) and target:GetCharacter() and door.ixAccess and door:GetDTEntity(0) == client and target != client) then\n\t\taccess = math.Clamp(access or 0, DOOR_NONE, DOOR_TENANT)\n\n\t\tif (access == door.ixAccess[target]) then\n\t\t\treturn\n\t\tend\n\n\t\tdoor.ixAccess[target] = access\n\n\t\tlocal recipient = {}\n\n\t\tfor k, v in pairs(door.ixAccess) do\n\t\t\tif (v > DOOR_GUEST) then\n\t\t\t\trecipient[#recipient + 1] = k\n\t\t\tend\n\t\tend\n\n\t\tif (#recipient > 0) then\n\t\t\tnet.Start(\"ixDoorPermission\")\n\t\t\t\tnet.WriteEntity(door)\n\t\t\t\tnet.WriteEntity(target)\n\t\t\t\tnet.WriteUInt(access, 4)\n\t\t\tnet.Send(recipient)\n\t\tend\n\tend\nend)\n"
  },
  {
    "path": "plugins/logging.lua",
    "content": "\nPLUGIN.name = \"Logging\"\nPLUGIN.author = \"Black Tea\"\nPLUGIN.description = \"You can modfiy the logging text/lists on this plugin.\"\n\nif (SERVER) then\n\tlocal L = Format\n\n\tix.log.AddType(\"chat\", function(client, ...)\n\t\tlocal arg = {...}\n\t\treturn L(\"[%s] %s: %s\", arg[1], client:Name(), arg[2])\n\tend)\n\n\tix.log.AddType(\"command\", function(client, ...)\n\t\tlocal arg = {...}\n\n\t\tif (arg[2] and #arg[2] > 0) then\n\t\t\treturn L(\"%s used command '%s %s'.\", client:Name(), arg[1], arg[2])\n\t\telse\n\t\t\treturn L(\"%s used command '%s'.\", client:Name(), arg[1])\n\t\tend\n\tend)\n\n\tix.log.AddType(\"cfgSet\", function(client, ...)\n\t\tlocal arg = {...}\n\t\treturn L(\"%s set %s to '%s'.\", client:Name(), arg[1], arg[2])\n\tend, FLAG_DANGER)\n\n\tix.log.AddType(\"connect\", function(client, ...)\n\t\treturn L(\"%s has connected.\", client:SteamName())\n\tend, FLAG_NORMAL)\n\n\tix.log.AddType(\"disconnect\", function(client, ...)\n\t\tif (client:IsTimingOut()) then\n\t\t\treturn L(\"%s (%s) has disconnected (timed out).\", client:SteamName(), client:SteamID())\n\t\telse\n\t\t\treturn L(\"%s (%s) has disconnected.\", client:SteamName(), client:SteamID())\n\t\tend\n\tend, FLAG_NORMAL)\n\n\tix.log.AddType(\"charCreate\", function(client, ...)\n\t\tlocal arg = {...}\n\t\treturn L(\"%s created the character '%s'\", client:SteamName(), arg[1])\n\tend, FLAG_SERVER)\n\n\tix.log.AddType(\"charLoad\", function(client, ...)\n\t\tlocal arg = {...}\n\t\treturn L(\"%s loaded the character '%s'\", client:SteamName(), arg[1])\n\tend, FLAG_SERVER)\n\n\tix.log.AddType(\"charDelete\", function(client, ...)\n\t\tlocal arg = {...}\n\t\treturn L(\"%s (%s) deleted character '%s'\", client:SteamName(), client:SteamID(), arg[1])\n\tend, FLAG_SERVER)\n\n\tix.log.AddType(\"itemAction\", function(client, ...)\n\t\tlocal arg = {...}\n\t\tlocal item = arg[2]\n\t\treturn L(\"%s ran '%s' on item '%s' (#%s)\", client:Name(), arg[1], item:GetName(), item:GetID())\n\tend, FLAG_NORMAL)\n\n\tix.log.AddType(\"itemDestroy\", function(client, itemName, itemID)\n\t\tlocal name = client:GetName() ~= \"\" and client:GetName() or client:GetClass()\n\t\treturn L(\"%s destroyed a '%s' #%d.\", name, itemName, itemID)\n\tend, FLAG_WARNING)\n\n\tix.log.AddType(\"shipmentTake\", function(client, ...)\n\t\tlocal arg = {...}\n\t\treturn L(\"%s took '%s' from the shipment\", client:Name(), arg[1])\n\tend, FLAG_WARNING)\n\n\tix.log.AddType(\"shipmentOrder\", function(client, ...)\n\t\treturn L(\"%s ordered a shipment\", client:Name())\n\tend, FLAG_SUCCESS)\n\n\tix.log.AddType(\"buy\", function(client, ...)\n\t\tlocal arg = {...}\n\t\treturn L(\"%s purchased '%s' from the NPC\", client:Name(), arg[1])\n\tend, FLAG_SUCCESS)\n\n\tix.log.AddType(\"buydoor\", function(client, ...)\n\t\treturn L(\"%s has purchased a door.\", client:Name())\n\tend, FLAG_SUCCESS)\n\n\tix.log.AddType(\"selldoor\", function(client, ...)\n\t\treturn L(\"%s has sold a door.\", client:Name())\n\tend, FLAG_SUCCESS)\n\n\tix.log.AddType(\"playerHurt\", function(client, ...)\n\t\tlocal arg = {...}\n\t\treturn L(\"%s has taken %d damage from %s.\", client:Name(), arg[1], arg[2])\n\tend, FLAG_WARNING)\n\n\tix.log.AddType(\"playerDeath\", function(client, ...)\n\t\tlocal arg = {...}\n\t\treturn L(\"%s has killed %s%s.\", arg[1], client:Name(), arg[2] and (\" with \" .. arg[2]) or \"\")\n\tend, FLAG_DANGER)\n\n\tix.log.AddType(\"money\", function(client, amount)\n\t\treturn L(\"%s has %s %s.\", client:Name(), amount < 0 and \"lost\" or \"gained\", ix.currency.Get(math.abs(amount)))\n\tend, FLAG_SUCCESS)\n\n\tix.log.AddType(\"inventoryAdd\", function(client, characterName, itemName, itemID)\n\t\treturn L(\"%s has gained a '%s' #%d.\", characterName, itemName, itemID)\n\tend, FLAG_WARNING)\n\n\tix.log.AddType(\"inventoryRemove\", function(client, characterName, itemName, itemID)\n\t\treturn L(\"%s has lost a '%s' #%d.\", characterName, itemName, itemID)\n\tend, FLAG_WARNING)\n\n\tix.log.AddType(\"storageMoneyTake\", function(client, entity, amount, total)\n\t\tlocal name = entity.GetDisplayName and entity:GetDisplayName() or entity:GetName()\n\n\t\treturn string.format(\"%s has taken %d %s from '%s' #%d (%d %s left).\",\n\t\t\tclient:GetName(), amount, ix.currency.plural, name,\n\t\t\tentity:GetInventory():GetID(), total, ix.currency.plural)\n\tend)\n\n\tix.log.AddType(\"storageMoneyGive\", function(client, entity, amount, total)\n\t\tlocal name = entity.GetDisplayName and entity:GetDisplayName() or entity:GetName()\n\n\t\treturn string.format(\"%s has given %d %s to '%s' #%d (%d %s left).\",\n\t\t\tclient:GetName(), amount, ix.currency.plural, name,\n\t\t\tentity:GetInventory():GetID(), total, ix.currency.plural)\n\tend)\n\n\tix.log.AddType(\"roll\", function(client, value, max)\n\t\treturn string.format(\"%s rolled %d out of %d.\", client:Name(), value, max)\n\tend)\n\n\tix.log.AddType(\"pluginLoaded\", function(client, uniqueID)\n\t\treturn string.format(\"%s has enabled the %s plugin for next restart.\", client:GetName(), uniqueID)\n\tend)\n\n\tix.log.AddType(\"pluginUnloaded\", function(client, uniqueID)\n\t\treturn string.format(\"%s has disabled the %s plugin for next restart.\", client:GetName(), uniqueID)\n\tend)\n\n\tfunction PLUGIN:PlayerInitialSpawn(client)\n\t\tix.log.Add(client, \"connect\")\n\tend\n\n\tfunction PLUGIN:PlayerDisconnected(client)\n\t\tix.log.Add(client, \"disconnect\")\n\tend\n\n\tfunction PLUGIN:OnCharacterCreated(client, character)\n\t\tix.log.Add(client, \"charCreate\", character:GetName())\n\tend\n\n\tfunction PLUGIN:CharacterLoaded(character)\n\t\tlocal client = character:GetPlayer()\n\t\tix.log.Add(client, \"charLoad\", character:GetName())\n\tend\n\n\tfunction PLUGIN:PreCharacterDeleted(client, character)\n\t\tix.log.Add(client, \"charDelete\", character:GetName())\n\tend\n\n\tfunction PLUGIN:ShipmentItemTaken(client, itemClass, amount)\n\t\tlocal itemTable = ix.item.list[itemClass]\n\t\tix.log.Add(client, \"shipmentTake\", itemTable:GetName())\n\tend\n\n\tfunction PLUGIN:CreateShipment(client, shipmentEntity)\n\t\tix.log.Add(client, \"shipmentOrder\")\n\tend\n\n\tfunction PLUGIN:CharacterVendorTraded(client, vendor, x, y, invID, price, isSell)\n\tend\n\n\tfunction PLUGIN:PlayerInteractItem(client, action, item)\n\t\tif (isentity(item)) then\n\t\t\tif (IsValid(item)) then\n\t\t\t\tlocal itemID = item.ixItemID\n\t\t\t\titem = ix.item.instances[itemID]\n\t\t\telse\n\t\t\t\treturn\n\t\t\tend\n\t\telseif (isnumber(item)) then\n\t\t\titem = ix.item.instances[item]\n\t\tend\n\n\t\tif (!item) then\n\t\t\treturn\n\t\tend\n\n\t\tix.log.Add(client, \"itemAction\", action, item)\n\tend\n\n\tfunction PLUGIN:InventoryItemAdded(oldInv, inventory, item)\n\t\tif (!inventory.owner or (oldInv and oldInv.owner == inventory.owner)) then\n\t\t\treturn\n\t\tend\n\n\t\tlocal character = ix.char.loaded[inventory.owner]\n\n\t\tix.log.Add(character:GetPlayer(), \"inventoryAdd\", character:GetName(), item:GetName(), item:GetID())\n\n\t\tif (item.isBag) then\n\t\t\tlocal bagInventory = item:GetInventory()\n\n\t\t\tif (!bagInventory) then\n\t\t\t\treturn\n\t\t\tend\n\n\t\t\tfor k, _ in bagInventory:Iter() do\n\t\t\t\tix.log.Add(character:GetPlayer(), \"inventoryAdd\", character:GetName(), k:GetName(), k:GetID())\n\t\t\tend\n\t\tend\n\tend\n\n\tfunction PLUGIN:InventoryItemRemoved(inventory, item)\n\t\tif (!inventory.owner) then\n\t\t\treturn\n\t\tend\n\n\t\tlocal character = ix.char.loaded[inventory.owner]\n\n\t\tix.log.Add(character:GetPlayer(), \"inventoryRemove\", character:GetName(), item:GetName(), item:GetID())\n\n\t\tif (item.isBag) then\n\t\t\tfor k, _ in item:GetInventory():Iter() do\n\t\t\t\tix.log.Add(character:GetPlayer(), \"inventoryRemove\", character:GetName(), k:GetName(), k:GetID())\n\t\t\tend\n\t\tend\n\tend\nend\n"
  },
  {
    "path": "plugins/mapscene.lua",
    "content": "\nlocal PLUGIN = PLUGIN\n\nPLUGIN.name = \"Map Scenes\"\nPLUGIN.author = \"Chessnut\"\nPLUGIN.description = \"Adds areas of the map that are visible during character selection.\"\nPLUGIN.scenes = PLUGIN.scenes or {}\n\nlocal x3, y3 = 0, 0\nlocal realOrigin = Vector(0, 0, 0)\nlocal realAngles = Angle(0, 0, 0)\nlocal view = {}\n\nif (CLIENT) then\n\tPLUGIN.ordered = PLUGIN.ordered or {}\n\n\tfunction PLUGIN:CalcView(client, origin, angles, fov)\n\t\tlocal scenes = self.scenes\n\n\t\tif (IsValid(ix.gui.characterMenu) and !IsValid(ix.gui.menu) and !ix.gui.characterMenu:IsClosing() and\n\t\t\t!table.IsEmpty(scenes)) then\n\t\t\tlocal key = self.index\n\t\t\tlocal value = scenes[self.index]\n\n\t\t\tif (!self.index or !value) then\n\t\t\t\tvalue, key = table.Random(scenes)\n\t\t\t\tself.index = key\n\t\t\tend\n\n\t\t\tif (self.orderedIndex or value.origin or isvector(key)) then\n\t\t\t\tlocal curTime = CurTime()\n\n\t\t\t\tself.orderedIndex = self.orderedIndex or 1\n\n\t\t\t\tlocal ordered = self.ordered[self.orderedIndex]\n\n\t\t\t\tif (ordered) then\n\t\t\t\t\tkey = ordered[1]\n\t\t\t\t\tvalue = ordered[2]\n\t\t\t\tend\n\n\t\t\t\tif (!self.startTime) then\n\t\t\t\t\tself.startTime = curTime\n\t\t\t\t\tself.finishTime = curTime + 30\n\t\t\t\tend\n\n\t\t\t\tlocal fraction = math.min(math.TimeFraction(self.startTime, self.finishTime, CurTime()), 1)\n\n\t\t\t\tif (value) then\n\t\t\t\t\trealOrigin = LerpVector(fraction, key, value[1])\n\t\t\t\t\trealAngles = LerpAngle(fraction, value[2], value[3])\n\t\t\t\tend\n\n\t\t\t\tif (fraction >= 1) then\n\t\t\t\t\tself.startTime = curTime\n\t\t\t\t\tself.finishTime = curTime + 30\n\n\t\t\t\t\tif (ordered) then\n\t\t\t\t\t\tself.orderedIndex = self.orderedIndex + 1\n\n\t\t\t\t\t\tif (self.orderedIndex > #self.ordered) then\n\t\t\t\t\t\t\tself.orderedIndex = 1\n\t\t\t\t\t\tend\n\t\t\t\t\telse\n\t\t\t\t\t\tlocal keys = {}\n\n\t\t\t\t\t\tfor k, _ in pairs(scenes) do\n\t\t\t\t\t\t\tif (isvector(k)) then\n\t\t\t\t\t\t\t\tkeys[#keys + 1] = k\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\tend\n\n\t\t\t\t\t\tself.index = keys[ math.random( #keys ) ]\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\telseif (value) then\n\t\t\t\trealOrigin = value[1]\n\t\t\t\trealAngles = value[2]\n\t\t\tend\n\n\t\t\tlocal x, y = gui.MousePos()\n\t\t\tlocal x2, y2 = surface.ScreenWidth() * 0.5, surface.ScreenHeight() * 0.5\n\t\t\tlocal frameTime = FrameTime() * 0.5\n\n\t\t\ty3 = Lerp(frameTime, y3, math.Clamp((y - y2) / y2, -1, 1) * -6)\n\t\t\tx3 = Lerp(frameTime, x3, math.Clamp((x - x2) / x2, -1, 1) * 6)\n\n\t\t\tview.origin = realOrigin + realAngles:Up()*y3 + realAngles:Right()*x3\n\t\t\tview.angles = realAngles + Angle(y3 * -0.5, x3 * -0.5, 0)\n\n\t\t\treturn view\n\t\tend\n\tend\n\n\tfunction PLUGIN:PreDrawViewModel(viewModel, client, weapon)\n\t\tif (IsValid(ix.gui.characterMenu) and !ix.gui.characterMenu:IsClosing()) then\n\t\t\treturn true\n\t\tend\n\tend\n\n\tnet.Receive(\"ixMapSceneAdd\", function()\n\t\tlocal data = net.ReadTable()\n\n\t\tPLUGIN.scenes[#PLUGIN.scenes + 1] = data\n\tend)\n\n\tnet.Receive(\"ixMapSceneRemove\", function()\n\t\tlocal index = net.ReadUInt(16)\n\n\t\tPLUGIN.scenes[index] = nil\n\tend)\n\n\tnet.Receive(\"ixMapSceneAddPair\", function()\n\t\tlocal data = net.ReadTable()\n\t\tlocal origin = net.ReadVector()\n\n\t\tPLUGIN.scenes[origin] = data\n\n\t\ttable.insert(PLUGIN.ordered, {origin, data})\n\tend)\n\n\tnet.Receive(\"ixMapSceneRemovePair\", function()\n\t\tlocal key = net.ReadVector()\n\n\t\tPLUGIN.scenes[key] = nil\n\n\t\tfor k, v in ipairs(PLUGIN.ordered) do\n\t\t\tif (v[1] == key) then\n\t\t\t\ttable.remove(PLUGIN.ordered, k)\n\n\t\t\t\tbreak\n\t\t\tend\n\t\tend\n\tend)\n\n\tnet.Receive(\"ixMapSceneSync\", function()\n\t\tlocal length = net.ReadUInt(32)\n\t\tlocal data = net.ReadData(length)\n\t\tlocal uncompressed = util.Decompress(data)\n\n\t\tif (!uncompressed) then\n\t\t\tErrorNoHalt(\"[Helix] Unable to decompress map scene data!\\n\")\n\t\t\treturn\n\t\tend\n\n\t\t-- Set the list of texts to the ones provided by the server.\n\t\tPLUGIN.scenes = util.JSONToTable(uncompressed)\n\n\t\tfor k, v in pairs(PLUGIN.scenes) do\n\t\t\tif (v.origin or isvector(k)) then\n\t\t\t\ttable.insert(PLUGIN.ordered, {v.origin and v.origin or k, v})\n\t\t\tend\n\t\tend\n\tend)\nelse\n\tutil.AddNetworkString(\"ixMapSceneSync\")\n\tutil.AddNetworkString(\"ixMapSceneAdd\")\n\tutil.AddNetworkString(\"ixMapSceneRemove\")\n\n\tutil.AddNetworkString(\"ixMapSceneAddPair\")\n\tutil.AddNetworkString(\"ixMapSceneRemovePair\")\n\n\tfunction PLUGIN:SaveScenes()\n\t\tself:SetData(self.scenes)\n\tend\n\n\tfunction PLUGIN:LoadData()\n\t\tself.scenes = self:GetData() or {}\n\tend\n\n\tfunction PLUGIN:PlayerInitialSpawn(client)\n\t\tlocal json = util.TableToJSON(self.scenes)\n\t\tlocal compressed = util.Compress(json)\n\t\tlocal length = compressed:len()\n\n\t\tnet.Start(\"ixMapSceneSync\")\n\t\t\tnet.WriteUInt(length, 32)\n\t\t\tnet.WriteData(compressed, length)\n\t\tnet.Send(client)\n\tend\n\n\tfunction PLUGIN:AddScene(position, angles, position2, angles2)\n\t\tlocal data\n\n\t\tif (position2) then\n\t\t\tdata = {origin=position, position2, angles, angles2}\n\t\t\tself.scenes[#self.scenes + 1] = data\n\n\t\t\tnet.Start(\"ixMapSceneAddPair\")\n\t\t\t\tnet.WriteTable(data)\n\t\t\t\tnet.WriteVector(position)\n\t\t\tnet.Broadcast()\n\t\telse\n\t\t\tdata = {position, angles}\n\t\t\tself.scenes[#self.scenes + 1] = data\n\n\t\t\tnet.Start(\"ixMapSceneAdd\")\n\t\t\t\tnet.WriteTable(data)\n\t\t\tnet.Broadcast()\n\t\tend\n\n\t\tself:SaveScenes()\n\tend\nend\n\nix.command.Add(\"MapSceneAdd\", {\n\tdescription = \"@cmdMapSceneAdd\",\n\tprivilege = \"Manage Map Scenes\",\n\tadminOnly = true,\n\targuments = bit.bor(ix.type.bool, ix.type.optional),\n\tOnRun = function(self, client, bIsPair)\n\t\tlocal position, angles = client:EyePos(), client:EyeAngles()\n\n\t\t-- This scene is in a pair for moving scenes.\n\t\tif (tobool(bIsPair) and !client.ixScnPair) then\n\t\t\tclient.ixScnPair = {position, angles}\n\n\t\t\treturn \"@mapRepeat\"\n\t\telse\n\t\t\tif (client.ixScnPair) then\n\t\t\t\tPLUGIN:AddScene(client.ixScnPair[1], client.ixScnPair[2], position, angles)\n\t\t\t\tclient.ixScnPair = nil\n\t\t\telse\n\t\t\t\tPLUGIN:AddScene(position, angles)\n\t\t\tend\n\n\t\t\treturn \"@mapAdd\"\n\t\tend\n\tend\n})\n\nix.command.Add(\"MapSceneRemove\", {\n\tdescription = \"@cmdMapSceneRemove\",\n\tprivilege = \"Manage Map Scenes\",\n\tadminOnly = true,\n\targuments = bit.bor(ix.type.number, ix.type.optional),\n\tOnRun = function(self, client, radius)\n\t\tradius = radius or 280\n\n\t\tlocal position = client:GetPos()\n\t\tlocal i = 0\n\n\t\tfor k, v in pairs(PLUGIN.scenes) do\n\t\t\tlocal delete = false\n\n\t\t\tif (isvector(k)) then\n\t\t\t\tif (k:Distance(position) <= radius or v[1]:Distance(position) <= radius) then\n\t\t\t\t\tdelete = true\n\t\t\t\tend\n\t\t\telseif (v[1]:Distance(position) <= radius) then\n\t\t\t\tdelete = true\n\t\t\tend\n\n\t\t\tif (delete) then\n\t\t\t\tif (isvector(k)) then\n\t\t\t\t\tnet.Start(\"ixMapSceneRemovePair\")\n\t\t\t\t\t\tnet.WriteVector(k)\n\t\t\t\t\tnet.Broadcast()\n\t\t\t\telse\n\t\t\t\t\tnet.Start(\"ixMapSceneRemove\")\n\t\t\t\t\t\tnet.WriteString(k)\n\t\t\t\t\tnet.Broadcast()\n\t\t\t\tend\n\n\t\t\t\tPLUGIN.scenes[k] = nil\n\n\t\t\t\ti = i + 1\n\t\t\tend\n\t\tend\n\n\t\tif (i > 0) then\n\t\t\tPLUGIN:SaveScenes()\n\t\tend\n\n\t\treturn \"@mapDel\", i\n\tend\n})\n"
  },
  {
    "path": "plugins/observer.lua",
    "content": "\nPLUGIN.name = \"Observer\"\nPLUGIN.author = \"Chessnut\"\nPLUGIN.description = \"Adds on to the no-clip mode to prevent intrusion.\"\n\nCAMI.RegisterPrivilege({\n\tName = \"Helix - Observer\",\n\tMinAccess = \"admin\"\n})\n\nix.option.Add(\"observerTeleportBack\", ix.type.bool, true, {\n\tbNetworked = true,\n\tcategory = \"observer\",\n\thidden = function()\n\t\treturn !CAMI.PlayerHasAccess(LocalPlayer(), \"Helix - Observer\", nil)\n\tend\n})\n\nif (CLIENT) then\n\tix.option.Add(\"observerESP\", ix.type.bool, true, {\n\t\tcategory = \"observer\",\n\t\thidden = function()\n\t\t\treturn !CAMI.PlayerHasAccess(LocalPlayer(), \"Helix - Observer\", nil)\n\t\tend\n\t})\n\n\tlocal dimDistance = 1024\n\tlocal aimLength = 128\n\tlocal barHeight = 2\n\n\tfunction PLUGIN:HUDPaint()\n\t\tlocal client = LocalPlayer()\n\n\t\tif (ix.option.Get(\"observerESP\", true) and client:GetMoveType() == MOVETYPE_NOCLIP and\n\t\t\t!client:InVehicle() and CAMI.PlayerHasAccess(client, \"Helix - Observer\", nil)) then\n\t\t\tlocal scrW, scrH = ScrW(), ScrH()\n\n\t\t\tfor _, v in player.Iterator() do\n\t\t\t\tif (v == client or !v:GetCharacter() or client:GetAimVector():Dot((v:GetPos() - client:GetPos()):GetNormal()) < 0.65) then\n\t\t\t\t\tcontinue\n\t\t\t\tend\n\n\t\t\t\tlocal screenPosition = v:GetPos():ToScreen()\n\t\t\t\tlocal aimPosition = (v:GetPos() + v:GetAimVector() * aimLength):ToScreen()\n\n\t\t\t\tlocal marginX, marginY = scrH * .1, scrH * .1\n\t\t\t\tlocal x, y = math.Clamp(screenPosition.x, marginX, scrW - marginX), math.Clamp(screenPosition.y, marginY, scrH - marginY)\n\t\t\t\tlocal aimX, aimY = math.Clamp(aimPosition.x, marginX, scrW - marginX), math.Clamp(aimPosition.y, marginY, scrH - marginY)\n\n\t\t\t\tlocal teamColor = team.GetColor(v:Team())\n\t\t\t\tlocal distance = client:GetPos():Distance(v:GetPos())\n\t\t\t\tlocal factor = 1 - math.Clamp(distance / dimDistance, 0, 1)\n\t\t\t\tlocal size = math.max(10, 32 * factor)\n\t\t\t\tlocal alpha = math.max(255 * factor, 80)\n\t\t\t\tlocal aimAlpha = (1 - factor * 1.5) * 80\n\n\t\t\t\tsurface.SetDrawColor(teamColor.r, teamColor.g, teamColor.b, alpha)\n\t\t\t\tsurface.SetFont(\"ixGenericFont\")\n\n\t\t\t\tlocal text = v:Name()\n\t\t\t\tlocal textWidth, textHeight = surface.GetTextSize(text)\n\t\t\t\tlocal barWidth = math.Clamp((v:Health() / v:GetMaxHealth()) * textWidth, 0, textWidth)\n\n\t\t\t\tsurface.DrawRect(x - size / 2, y - size / 2, size, size)\n\n\t\t\t\t-- we can assume that if we're using cheap blur, we'd want to save some fps here\n\t\t\t\tif (!ix.option.Get(\"cheapBlur\", false)) then\n\t\t\t\t\tlocal data = {}\n\t\t\t\t\tdata.start = client:EyePos()\n\t\t\t\t\tdata.endpos = v:EyePos()\n\t\t\t\t\tdata.filter = {client, v}\n\n\t\t\t\t\tif (util.TraceLine(data).Hit) then\n\t\t\t\t\t\taimAlpha = alpha\n\t\t\t\t\telse\n\t\t\t\t\t\taimAlpha = (1 - factor * 4) * 80\n\t\t\t\t\tend\n\t\t\t\tend\n\n\t\t\t\tif (aimPosition.visible) then\n\t\t\t\t\tsurface.SetDrawColor(teamColor.r * 1.2, teamColor.g * 1.2, teamColor.b * 1.2, aimAlpha)\n\t\t\t\t\tsurface.DrawLine(x, y, aimX, aimY)\n\t\t\t\t\tsurface.DrawLine(x, y + 1, aimX, aimY + 1)\n\t\t\t\tend\n\n\t\t\t\tsurface.SetDrawColor(teamColor.r * 1.6, teamColor.g * 1.6, teamColor.b * 1.6, alpha)\n\t\t\t\tsurface.DrawRect(x - barWidth / 2, y - size - textHeight / 2, barWidth, barHeight)\n\n\t\t\t\tix.util.DrawText(text, x, y - size, ColorAlpha(teamColor, alpha), TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER, nil, alpha)\n\t\t\tend\n\t\tend\n\tend\n\n\tfunction PLUGIN:ShouldPopulateEntityInfo(entity)\n\t\tif (IsValid(entity)) then\n\t\t\tif ((entity:IsPlayer() or IsValid(entity:GetNetVar(\"player\"))) and entity:GetMoveType() == MOVETYPE_NOCLIP) then\n\t\t\t\treturn false\n\t\t\tend\n\t\tend\n\tend\n\n\tfunction PLUGIN:DrawPhysgunBeam(client, physgun, enabled, target, bone, hitPos)\n\t\tif (client != LocalPlayer() and client:GetMoveType() == MOVETYPE_NOCLIP) then\n\t\t\treturn false\n\t\tend\n\tend\n\n\tfunction PLUGIN:PrePlayerDraw(client)\n\t\tif (client:GetMoveType() == MOVETYPE_NOCLIP and !client:InVehicle()) then\n\t\t\treturn true\n\t\tend\n\tend\nelse\n\tix.log.AddType(\"observerEnter\", function(client, ...)\n\t\treturn string.format(\"%s entered observer.\", client:Name())\n\tend)\n\n\tix.log.AddType(\"observerExit\", function(client, ...)\n\t\tif (ix.option.Get(client, \"observerTeleportBack\", true)) then\n\t\t\treturn string.format(\"%s exited observer.\", client:Name())\n\t\telse\n\t\t\treturn string.format(\"%s exited observer at their location.\", client:Name())\n\t\tend\n\tend)\n\n\tfunction PLUGIN:CanPlayerEnterObserver(client)\n\t\tif (CAMI.PlayerHasAccess(client, \"Helix - Observer\", nil)) then\n\t\t\treturn true\n\t\tend\n\tend\n\n\tfunction PLUGIN:CanPlayerEnterVehicle(client, vehicle, role)\n\t\tif (client:GetMoveType() == MOVETYPE_NOCLIP) then\n\t\t\treturn false\n\t\tend\n\tend\n\n\tfunction PLUGIN:PlayerNoClip(client, state)\n\t\tif (hook.Run(\"CanPlayerEnterObserver\", client)) then\n\t\t\tif (state) then\n\t\t\t\tclient.ixObsData = {client:GetPos(), client:EyeAngles()}\n\n\t\t\t\t-- Hide them so they are not visible.\n\t\t\t\tclient:SetNoDraw(true)\n\t\t\t\tclient:SetNotSolid(true)\n\t\t\t\tclient:DrawWorldModel(false)\n\t\t\t\tclient:DrawShadow(false)\n\t\t\t\tclient:GodEnable()\n\t\t\t\tclient:SetNoTarget(true)\n\n\t\t\t\thook.Run(\"OnPlayerObserve\", client, state)\n\t\t\telse\n\t\t\t\tif (client.ixObsData) then\n\t\t\t\t\t-- Move they player back if they want.\n\t\t\t\t\tif (ix.option.Get(client, \"observerTeleportBack\", true)) then\n\t\t\t\t\t\tlocal position, angles = client.ixObsData[1], client.ixObsData[2]\n\n\t\t\t\t\t\t-- Do it the next frame since the player can not be moved right now.\n\t\t\t\t\t\ttimer.Simple(0, function()\n\t\t\t\t\t\t\tclient:SetPos(position)\n\t\t\t\t\t\t\tclient:SetEyeAngles(angles)\n\t\t\t\t\t\t\tclient:SetVelocity(Vector(0, 0, 0))\n\t\t\t\t\t\tend)\n\t\t\t\t\tend\n\n\t\t\t\t\tclient.ixObsData = nil\n\t\t\t\tend\n\n\t\t\t\t-- Make the player visible again.\n\t\t\t\tclient:SetNoDraw(false)\n\t\t\t\tclient:SetNotSolid(false)\n\t\t\t\tclient:DrawWorldModel(true)\n\t\t\t\tclient:DrawShadow(true)\n\t\t\t\tclient:GodDisable()\n\t\t\t\tclient:SetNoTarget(false)\n\n\t\t\t\thook.Run(\"OnPlayerObserve\", client, state)\n\t\t\tend\n\n\t\t\treturn true\n\t\tend\n\tend\n\n\tfunction PLUGIN:OnPlayerObserve(client, state)\n\t\tif (state) then\n\t\t\tix.log.Add(client, \"observerEnter\")\n\t\telse\n\t\t\tix.log.Add(client, \"observerExit\")\n\t\tend\n\tend\nend\n"
  },
  {
    "path": "plugins/pac.lua",
    "content": "\n-- luacheck: globals pac pace\n\n-- This Library is just for PAC3 Integration.\n-- You must install PAC3 to make this library work.\n\nPLUGIN.name = \"PAC3 Integration\"\nPLUGIN.author = \"Black Tea\"\nPLUGIN.description = \"PAC3 integration for item parts.\"\n\nif (!pace) then return end\n\nix.pac = ix.pac or {}\nix.pac.list = ix.pac.list or {}\n\nCAMI.RegisterPrivilege({\n\tName = \"Helix - Manage PAC\",\n\tMinAccess = \"superadmin\"\n})\n\n-- this stores pac3 part information to plugin's table'\nfunction ix.pac.RegisterPart(id, outfit)\n\tix.pac.list[id] = outfit\nend\n\n-- Fixing the PAC3's default stuffs to fit on Helix.\nif (CLIENT) then\n\t-- Disable the \"in editor\" HUD element.\n\thook.Add(\"InitializedPlugins\", \"PAC3Fixer\", function()\n\t\thook.Remove(\"HUDPaint\", \"pac_in_editor\")\n\tend)\n\n\t-- Remove PAC3 LoadParts\n\tfunction pace.LoadParts(name, clear, override_part) end\n\n\t-- Prohibits players from deleting their own PAC3 outfit.\n\tconcommand.Add(\"pac_clear_parts\", function()\n\t\tRunConsoleCommand(\"pac_restart\")\n\tend)\n\n\t-- you need the proper permission to open the editor\n\tfunction PLUGIN:PrePACEditorOpen()\n\t\tif (!CAMI.PlayerHasAccess(LocalPlayer(), \"Helix - Manage PAC\", nil)) then\n\t\t\treturn false\n\t\tend\n\tend\nend\n\nfunction PLUGIN:pac_CanWearParts(client)\n\tif (!CAMI.PlayerHasAccess(client, \"Helix - Manage PAC\", nil)) then\n\t\treturn false\n\tend\nend\n\nlocal meta = FindMetaTable(\"Player\")\n\n-- Get Player's PAC3 Parts.\nfunction meta:GetParts()\n\tif (!pac) then return end\n\n\treturn self:GetNetVar(\"parts\", {})\nend\n\nif (SERVER) then\n\tutil.AddNetworkString(\"ixPartWear\")\n\tutil.AddNetworkString(\"ixPartRemove\")\n\tutil.AddNetworkString(\"ixPartReset\")\n\n\tfunction meta:AddPart(uniqueID, item)\n\t\tif (!pac) then return end\n\n\t\tlocal curParts = self:GetParts()\n\n\t\t-- wear the parts.\n\t\tnet.Start(\"ixPartWear\")\n\t\t\tnet.WriteEntity(self)\n\t\t\tnet.WriteString(uniqueID)\n\t\tnet.Broadcast()\n\n\t\tcurParts[uniqueID] = true\n\n\t\tself:SetNetVar(\"parts\", curParts)\n\tend\n\n\tfunction meta:RemovePart(uniqueID)\n\t\tif (!pac) then return end\n\n\t\tlocal curParts = self:GetParts()\n\n\t\t-- remove the parts.\n\t\tnet.Start(\"ixPartRemove\")\n\t\t\tnet.WriteEntity(self)\n\t\t\tnet.WriteString(uniqueID)\n\t\tnet.Broadcast()\n\n\t\tcurParts[uniqueID] = nil\n\n\t\tself:SetNetVar(\"parts\", curParts)\n\tend\n\n\tfunction meta:ResetParts()\n\t\tif (!pac) then return end\n\n\t\tnet.Start(\"ixPartReset\")\n\t\t\tnet.WriteEntity(self)\n\t\t\tnet.WriteTable(self:GetParts())\n\t\tnet.Broadcast()\n\n\t\tself:SetNetVar(\"parts\", {})\n\tend\n\n\tfunction PLUGIN:PlayerLoadedCharacter(client, curChar, prevChar)\n\t\t-- Reset the characters parts.\n\t\tlocal curParts = client:GetParts()\n\n\t\tif (curParts) then\n\t\t\tclient:ResetParts()\n\t\tend\n\n\t\t-- After resetting all PAC3 outfits, wear all equipped PAC3 outfits.\n\t\tif (curChar) then\n\t\t\tlocal inv = curChar:GetInventory()\n\n\t\t\tfor k, _ in inv:Iter() do\n\t\t\t\tif (k:GetData(\"equip\") == true and k.pacData) then\n\t\t\t\t\tclient:AddPart(k.uniqueID, k)\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend\n\n\tfunction PLUGIN:PlayerSwitchWeapon(client, oldWeapon, newWeapon)\n\t\tlocal oldItem = IsValid(oldWeapon) and oldWeapon.ixItem\n\t\tlocal newItem = IsValid(newWeapon) and newWeapon.ixItem\n\n\t\tif (oldItem and oldItem.isWeapon and oldItem:GetData(\"equip\") and oldItem.pacData) then\n\t\t\toldItem:WearPAC(client)\n\t\tend\n\n\t\tif (newItem and newItem.isWeapon and newItem.pacData) then\n\t\t\tnewItem:RemovePAC(client)\n\t\tend\n\tend\n\n\t-- Hides PAC parts when a player enters observer.\n\tfunction PLUGIN:OnPlayerObserve(client, state)\n\t\tlocal curParts = client:GetParts()\n\n\t\t-- Remove all the parts\n\t\tif (curParts) then\n\t\t\tclient:ResetParts()\n\t\tend\n\n\t\t-- If exiting of observer, re-add all parts.\n\t\tif (!state) then\n\t\t\tlocal character = client:GetCharacter()\n\t\t\tlocal inventory = character:GetInventory()\n\n\t\t\tfor k, _ in inventory:Iter() do\n\t\t\t\tif (k:GetData(\"equip\") == true and k.pacData) then\n\t\t\t\t\tclient:AddPart(k.uniqueID, k)\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend\nelse\n\tlocal function AttachPart(client, uniqueID)\n\t\tlocal itemTable = ix.item.list[uniqueID]\n\t\tlocal pacData = ix.pac.list[uniqueID]\n\n\t\tif (pacData) then\n\t\t\tif (itemTable and itemTable.pacAdjust) then\n\t\t\t\tpacData = table.Copy(pacData)\n\t\t\t\tpacData = itemTable:pacAdjust(pacData, client)\n\t\t\tend\n\n\t\t\tif (isfunction(client.AttachPACPart)) then\n\t\t\t\tclient:AttachPACPart(pacData)\n\t\t\telse\n\t\t\t\tpac.SetupENT(client)\n\n\t\t\t\ttimer.Simple(0.1, function()\n\t\t\t\t\tif (IsValid(client) and isfunction(client.AttachPACPart)) then\n\t\t\t\t\t\tclient:AttachPACPart(pacData)\n\t\t\t\t\tend\n\t\t\t\tend)\n\t\t\tend\n\t\tend\n\tend\n\n\tlocal function RemovePart(client, uniqueID)\n\t\tlocal itemTable = ix.item.list[uniqueID]\n\t\tlocal pacData = ix.pac.list[uniqueID]\n\n\t\tif (pacData) then\n\t\t\tif (itemTable and itemTable.pacAdjust) then\n\t\t\t\tpacData = table.Copy(pacData)\n\t\t\t\tpacData = itemTable:pacAdjust(pacData, client)\n\t\t\tend\n\n\t\t\tif (isfunction(client.RemovePACPart)) then\n\t\t\t\tclient:RemovePACPart(pacData)\n\t\t\telse\n\t\t\t\tpac.SetupENT(client)\n\t\t\tend\n\t\tend\n\tend\n\n\thook.Add(\"Think\", \"ix_pacupdate\", function()\n\t\tif (!pac) then\n\t\t\thook.Remove(\"Think\", \"ix_pacupdate\")\n\t\t\treturn\n\t\tend\n\n\t\tif (IsValid(pac.LocalPlayer)) then\n\t\t\tfor _, v in player.Iterator() do\n\t\t\t\tlocal character = v:GetCharacter()\n\n\t\t\t\tif (character) then\n\t\t\t\t\tlocal parts = v:GetParts()\n\n\t\t\t\t\tfor k2, _ in pairs(parts) do\n\t\t\t\t\t\tAttachPart(v, k2)\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\n\t\t\thook.Remove(\"Think\", \"ix_pacupdate\")\n\t\tend\n\tend)\n\n\tnet.Receive(\"ixPartWear\", function(length)\n\t\tif (!pac) then return end\n\n\t\tlocal wearer = net.ReadEntity()\n\t\tlocal uid = net.ReadString()\n\n\t\tif (!wearer.pac_owner) then\n\t\t\tpac.SetupENT(wearer)\n\t\tend\n\n\t\tAttachPart(wearer, uid)\n\tend)\n\n\tnet.Receive(\"ixPartRemove\", function(length)\n\t\tif (!pac) then return end\n\n\t\tlocal wearer = net.ReadEntity()\n\t\tlocal uid = net.ReadString()\n\n\t\tif (!wearer.pac_owner) then\n\t\t\tpac.SetupENT(wearer)\n\t\tend\n\n\t\tRemovePart(wearer, uid)\n\tend)\n\n\tnet.Receive(\"ixPartReset\", function(length)\n\t\tif (!pac) then return end\n\n\t\tlocal wearer = net.ReadEntity()\n\t\tlocal uidList = net.ReadTable()\n\n\t\tif (!wearer.pac_owner) then\n\t\t\tpac.SetupENT(wearer)\n\t\tend\n\n\t\tfor k, _ in pairs(uidList) do\n\t\t\tRemovePart(wearer, k)\n\t\tend\n\tend)\n\n\tfunction PLUGIN:DrawPlayerRagdoll(entity)\n\t\tlocal ply = entity.objCache\n\n\t\tif (IsValid(ply)) then\n\t\t\tif (!entity.overridePAC3) then\n\t\t\t\tif ply.pac_parts then\n\t\t\t\t\tfor _, part in pairs(ply.pac_parts) do\n\t\t\t\t\t\tif part.last_owner and part.last_owner:IsValid() then\n\t\t\t\t\t\t\thook.Run(\"OnPAC3PartTransferred\", part)\n\t\t\t\t\t\t\tpart:SetOwner(entity)\n\t\t\t\t\t\t\tpart.last_owner = entity\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\t\tply.pac_playerspawn = pac.RealTime -- used for events\n\n\t\t\t\tentity.overridePAC3 = true\n\t\t\tend\n\t\tend\n\tend\n\n\tfunction PLUGIN:OnEntityCreated(entity)\n\t\tlocal class = entity:GetClass()\n\n\t\t-- For safe progress, I skip one frame.\n\t\ttimer.Simple(0.01, function()\n\t\t\tif (class == \"prop_ragdoll\") then\n\t\t\t\tif (entity:GetNetVar(\"player\")) then\n\t\t\t\t\tentity.RenderOverride = function()\n\t\t\t\t\t\tentity.objCache = entity:GetNetVar(\"player\")\n\t\t\t\t\t\tentity:DrawModel()\n\n\t\t\t\t\t\thook.Run(\"DrawPlayerRagdoll\", entity)\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tif (class:find(\"HL2MPRagdoll\")) then\n\t\t\t\tfor _, v in player.Iterator() do\n\t\t\t\t\tif (v:GetRagdollEntity() == entity) then\n\t\t\t\t\t\tentity.objCache = v\n\t\t\t\t\tend\n\t\t\t\tend\n\n\t\t\t\tentity.RenderOverride = function()\n\t\t\t\t\tentity:DrawModel()\n\n\t\t\t\t\thook.Run(\"DrawPlayerRagdoll\", entity)\n\t\t\t\tend\n\t\t\tend\n\t\tend)\n\tend\n\n\tfunction PLUGIN:DrawCharacterOverview()\n\t\tif (!pac) then\n\t\t\treturn\n\t\tend\n\n\t\tif (LocalPlayer().pac_outfits) then\n\t\t\tpac.RenderOverride(LocalPlayer(), \"opaque\")\n\t\t\tpac.RenderOverride(LocalPlayer(), \"translucent\", true)\n\t\tend\n\tend\n\n\tfunction PLUGIN:DrawHelixModelView(panel, ent)\n\t\tif (!pac) then\n\t\t\treturn\n\t\tend\n\n\t\tif (LocalPlayer():GetCharacter()) then\n\t\t\tpac.RenderOverride(ent, \"opaque\")\n\t\t\tpac.RenderOverride(ent, \"translucent\", true)\n\t\tend\n\tend\nend\n\nfunction PLUGIN:InitializedPlugins()\n\tlocal items = ix.item.list\n\n\tfor _, v in pairs(items) do\n\t\tif (v.pacData) then\n\t\t\tix.pac.list[v.uniqueID] = v.pacData\n\t\tend\n\tend\nend\n"
  },
  {
    "path": "plugins/permakill.lua",
    "content": "\nPLUGIN.name = \"Permakill\"\nPLUGIN.author = \"Thadah Denyse\"\nPLUGIN.description = \"Adds permanent death in the server options.\"\n\nix.config.Add(\"permakill\", false, \"Whether or not permakill is activated on the server.\", nil, {\n\tcategory = \"Permakill\"\n})\n\nix.config.Add(\"permakillWorld\", false, \"Whether or not world and self damage produce permanent death.\", nil, {\n\tcategory = \"Permakill\"\n})\n\nfunction PLUGIN:PlayerDeath(client, inflictor, attacker)\n\tlocal character = client:GetCharacter()\n\n\tif (ix.config.Get(\"permakill\") and character) then\n\t\tif (hook.Run(\"ShouldPermakillCharacter\", client, character, inflictor, attacker) == false) then\n\t\t\treturn\n\t\tend\n\n\t\tif (ix.config.Get(\"permakillWorld\") and (client == attacker or inflictor:IsWorld())) then\n\t\t\treturn\n\t\tend\n\n\t\tcharacter:SetData(\"permakilled\", true)\n\tend\nend\n\nfunction PLUGIN:PlayerSpawn(client)\n\tlocal character = client:GetCharacter()\n\n\tif (ix.config.Get(\"permakill\") and character and character:GetData(\"permakilled\")) then\n\t\tcharacter:Ban()\n\t\tcharacter:SetData(\"permakilled\")\n\tend\nend\n"
  },
  {
    "path": "plugins/persistence.lua",
    "content": "\nlocal PLUGIN = PLUGIN\n\nPLUGIN.name = \"Persistence\"\nPLUGIN.description = \"Define entities to persist through restarts.\"\nPLUGIN.author = \"alexgrist\"\nPLUGIN.stored = PLUGIN.stored or {}\n\nlocal function GetRealModel(entity)\n\treturn entity:GetClass() == \"prop_effect\" and entity.AttachedEntity:GetModel() or entity:GetModel()\nend\n\nproperties.Add(\"persist\", {\n\tMenuLabel = \"#makepersistent\",\n\tOrder = 400,\n\tMenuIcon = \"icon16/link.png\",\n\n\tFilter = function(self, entity, client)\n\t\tif (entity:IsPlayer() or entity:IsVehicle() or entity.bNoPersist) then return false end\n\t\tif (!gamemode.Call(\"CanProperty\", client, \"persist\", entity)) then return false end\n\n\t\treturn !entity:GetNetVar(\"Persistent\", false)\n\tend,\n\n\tAction = function(self, entity)\n\t\tself:MsgStart()\n\t\t\tnet.WriteEntity(entity)\n\t\tself:MsgEnd()\n\tend,\n\n\tReceive = function(self, length, client)\n\t\tlocal entity = net.ReadEntity()\n\n\t\tif (!IsValid(entity)) then return end\n\t\tif (!self:Filter(entity, client)) then return end\n\n\t\tPLUGIN.stored[#PLUGIN.stored + 1] = entity\n\n\t\tentity:SetNetVar(\"Persistent\", true)\n\n\t\tix.log.Add(client, \"persist\", GetRealModel(entity), true)\n\tend\n})\n\nproperties.Add(\"persist_end\", {\n\tMenuLabel = \"#stoppersisting\",\n\tOrder = 400,\n\tMenuIcon = \"icon16/link_break.png\",\n\n\tFilter = function(self, entity, client)\n\t\tif (entity:IsPlayer()) then return false end\n\t\tif (!gamemode.Call(\"CanProperty\", client, \"persist\", entity)) then return false end\n\n\t\treturn entity:GetNetVar(\"Persistent\", false)\n\tend,\n\n\tAction = function(self, entity)\n\t\tself:MsgStart()\n\t\t\tnet.WriteEntity(entity)\n\t\tself:MsgEnd()\n\tend,\n\n\tReceive = function(self, length, client)\n\t\tlocal entity = net.ReadEntity()\n\n\t\tif (!IsValid(entity)) then return end\n\t\tif (!self:Filter(entity, client)) then return end\n\n\t\tfor k, v in ipairs(PLUGIN.stored) do\n\t\t\tif (v == entity) then\n\t\t\t\ttable.remove(PLUGIN.stored, k)\n\n\t\t\t\tbreak\n\t\t\tend\n\t\tend\n\n\t\tentity:SetNetVar(\"Persistent\", false)\n\n\t\tix.log.Add(client, \"persist\", GetRealModel(entity), false)\n\tend\n})\n\nfunction PLUGIN:PhysgunPickup(client, entity)\n\tif (entity:GetNetVar(\"Persistent\", false)) then\n\t\treturn false\n\tend\nend\n\nif (SERVER) then\n\tfunction PLUGIN:LoadData()\n\t\tlocal entities = self:GetData() or {}\n\n\t\tfor _, v in ipairs(entities) do\n\t\t\tlocal entity = ents.Create(v.Class)\n\n\t\t\tif (IsValid(entity)) then\n\t\t\t\tentity:SetPos(v.Pos)\n\t\t\t\tentity:SetAngles(v.Angle)\n\t\t\t\tentity:SetModel(v.Model)\n\t\t\t\tentity:SetSkin(v.Skin)\n\t\t\t\tentity:SetColor(v.Color)\n\t\t\t\tentity:SetMaterial(v.Material)\n\t\t\t\tentity:Spawn()\n\t\t\t\tentity:Activate()\n\n\t\t\t\tif (v.bNoCollision) then\n\t\t\t\t\tentity:SetCollisionGroup(COLLISION_GROUP_WORLD)\n\t\t\t\tend\n\n\t\t\t\tif (istable(v.BodyGroups)) then\n\t\t\t\t\tfor k2, v2 in pairs(v.BodyGroups) do\n\t\t\t\t\t\tentity:SetBodygroup(k2, v2)\n\t\t\t\t\tend\n\t\t\t\tend\n\n\t\t\t\tif (istable(v.SubMaterial)) then\n\t\t\t\t\tfor k2, v2 in pairs(v.SubMaterial) do\n\t\t\t\t\t\tif (!isnumber(k2) or !isstring(v2)) then\n\t\t\t\t\t\t\tcontinue\n\t\t\t\t\t\tend\n\n\t\t\t\t\t\tentity:SetSubMaterial(k2 - 1, v2)\n\t\t\t\t\tend\n\t\t\t\tend\n\n\t\t\t\tlocal physicsObject = entity:GetPhysicsObject()\n\n\t\t\t\tif (IsValid(physicsObject)) then\n\t\t\t\t\tphysicsObject:EnableMotion(v.Movable)\n\t\t\t\tend\n\n\t\t\t\tself.stored[#self.stored + 1] = entity\n\n\t\t\t\tentity:SetNetVar(\"Persistent\", true)\n\t\t\tend\n\t\tend\n\tend\n\n\tfunction PLUGIN:SaveData()\n\t\tlocal entities = {}\n\n\t\tfor _, v in ipairs(self.stored) do\n\t\t\tif (IsValid(v)) then\n\t\t\t\tlocal data = {}\n\t\t\t\tdata.Class = v.ClassOverride or v:GetClass()\n\t\t\t\tdata.Pos = v:GetPos()\n\t\t\t\tdata.Angle = v:GetAngles()\n\t\t\t\tdata.Model = GetRealModel(v)\n\t\t\t\tdata.Skin = v:GetSkin()\n\t\t\t\tdata.Color = v:GetColor()\n\t\t\t\tdata.Material = v:GetMaterial()\n\t\t\t\tdata.bNoCollision = v:GetCollisionGroup() == COLLISION_GROUP_WORLD\n\n\t\t\t\tlocal materials = v:GetMaterials()\n\n\t\t\t\tif (istable(materials)) then\n\t\t\t\t\tdata.SubMaterial = {}\n\n\t\t\t\t\tfor k2, _ in pairs(materials) do\n\t\t\t\t\t\tif (v:GetSubMaterial(k2 - 1) != \"\") then\n\t\t\t\t\t\t\tdata.SubMaterial[k2] = v:GetSubMaterial(k2 - 1)\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\t\t\t\tend\n\n\t\t\t\tlocal bodyGroups = v:GetBodyGroups()\n\n\t\t\t\tif (istable(bodyGroups)) then\n\t\t\t\t\tdata.BodyGroups = {}\n\n\t\t\t\t\tfor _, v2 in pairs(bodyGroups) do\n\t\t\t\t\t\tif (v:GetBodygroup(v2.id) > 0) then\n\t\t\t\t\t\t\tdata.BodyGroups[v2.id] = v:GetBodygroup(v2.id)\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\t\t\t\tend\n\n\t\t\t\tlocal physicsObject = v:GetPhysicsObject()\n\n\t\t\t\tif (IsValid(physicsObject)) then\n\t\t\t\t\tdata.Movable = physicsObject:IsMoveable()\n\t\t\t\tend\n\n\t\t\t\tentities[#entities + 1] = data\n\t\t\tend\n\t\tend\n\n\t\tself:SetData(entities)\n\tend\n\n\tix.log.AddType(\"persist\", function(client, ...)\n\t\tlocal arg = {...}\n\t\treturn string.format(\"%s has %s persistence for '%s'.\", client:Name(), arg[2] and \"enabled\" or \"disabled\", arg[1])\n\tend)\nend\n"
  },
  {
    "path": "plugins/propprotect.lua",
    "content": "\nPLUGIN.name = \"Basic Prop Protection\"\nPLUGIN.author = \"Chessnut\"\nPLUGIN.description = \"Adds a simple prop protection system.\"\n\nCAMI.RegisterPrivilege({\n\tName = \"Helix - Bypass Prop Protection\",\n\tMinAccess = \"admin\"\n})\n\nlocal PROP_BLACKLIST = {\n\t[\"models/props_combine/combinetrain02b.mdl\"] = true,\n\t[\"models/props_combine/combinetrain02a.mdl\"] = true,\n\t[\"models/props_combine/combinetrain01.mdl\"] = true,\n\t[\"models/cranes/crane_frame.mdl\"] = true,\n\t[\"models/props_junk/trashdumpster02.mdl\"] = true,\n\t[\"models/props_c17/oildrum001_explosive.mdl\"] = true,\n\t[\"models/props_canal/canal_bridge02.mdl\"] = true,\n\t[\"models/props_canal/canal_bridge01.mdl\"] = true,\n\t[\"models/props_canal/canal_bridge03a.mdl\"] = true,\n\t[\"models/props_canal/canal_bridge03b.mdl\"] = true,\n\t[\"models/props_wasteland/cargo_container01.mdl\"] = true,\n\t[\"models/props_wasteland/cargo_container01c.mdl\"] = true,\n\t[\"models/props_wasteland/cargo_container01b.mdl\"] = true,\n\t[\"models/props_combine/combine_mine01.mdl\"] = true,\n\t[\"models/props_junk/glassjug01.mdl\"] = true,\n\t[\"models/props_c17/paper01.mdl\"] = true,\n\t[\"models/props_junk/garbage_takeoutcarton001a.mdl\"] = true,\n\t[\"models/props_c17/trappropeller_engine.mdl\"] = true,\n\t[\"models/props/cs_office/microwave.mdl\"] = true,\n\t[\"models/items/item_item_crate.mdl\"] = true,\n\t[\"models/props_junk/gascan001a.mdl\"] = true,\n\t[\"models/props_c17/consolebox01a.mdl\"] = true,\n\t[\"models/props_buildings/building_002a.mdl\"] = true,\n\t[\"models/props_phx/mk-82.mdl\"] = true,\n\t[\"models/props_phx/cannonball.mdl\"] = true,\n\t[\"models/props_phx/ball.mdl\"] = true,\n\t[\"models/props_phx/amraam.mdl\"] = true,\n\t[\"models/props_phx/misc/flakshell_big.mdl\"] = true,\n\t[\"models/props_phx/ww2bomb.mdl\"] = true,\n\t[\"models/props_phx/torpedo.mdl\"] = true,\n\t[\"models/props/de_train/biohazardtank.mdl\"] = true,\n\t[\"models/props_buildings/project_building01.mdl\"] = true,\n\t[\"models/props_combine/prison01c.mdl\"] = true,\n\t[\"models/props/cs_militia/silo_01.mdl\"] = true,\n\t[\"models/props_phx/huge/evildisc_corp.mdl\"] = true,\n\t[\"models/props_phx/misc/potato_launcher_explosive.mdl\"] = true,\n\t[\"models/props_combine/combine_citadel001.mdl\"] = true,\n\t[\"models/props_phx/oildrum001_explosive.mdl\"] = true,\n\t[\"models/props_junk/wood_crate01_explosive.mdl\"] = true,\n\t[\"models/props_junk/propane_tank001a.mdl\"] = true,\n\t[\"models/props_explosive/explosive_butane_can.mdl\"] = true,\n\t[\"models/props_explosive/explosive_butane_can02.mdl\"] = true\n}\n\nif (SERVER) then\n\tix.log.AddType(\"spawnProp\", function(client, ...)\n\t\tlocal arg = {...}\n\t\treturn string.format(\"%s has spawned '%s'.\", client:Name(), arg[1])\n\tend)\n\n\tix.log.AddType(\"spawnEntity\", function(client, ...)\n\t\tlocal arg = {...}\n\t\treturn string.format(\"%s has spawned a '%s'.\", client:Name(), arg[1])\n\tend)\n\n\tfunction PLUGIN:PlayerSpawnObject(client, model, entity)\n\t\tif ((client.ixNextSpawn or 0) < CurTime()) then\n\t\t\tclient.ixNextSpawn = CurTime() + 0.75\n\t\telse\n\t\t\treturn false\n\t\tend\n\n\t\tif (!client:IsAdmin() and PROP_BLACKLIST[model:lower()]) then\n\t\t\treturn false\n\t\tend\n\tend\n\n\tfunction PLUGIN:PhysgunPickup(client, entity)\n\t\tlocal characterID = client:GetCharacter():GetID()\n\n\t\tif (entity:GetNetVar(\"owner\", 0) != characterID\n\t\tand !CAMI.PlayerHasAccess(client, \"Helix - Bypass Prop Protection\", nil)) then\n\t\t\treturn false\n\t\tend\n\tend\n\n\tfunction PLUGIN:OnPhysgunReload(weapon, client)\n\t\tlocal characterID = client:GetCharacter():GetID()\n\t\tlocal trace = client:GetEyeTrace()\n\n\t\tif (IsValid(trace.Entity) and trace.Entity:GetNetVar(\"owner\", 0) != characterID\n\t\tand !CAMI.PlayerHasAccess(client, \"Helix - Bypass Prop Protection\", nil)) then\n\t\t\treturn false\n\t\tend\n\tend\n\n\tfunction PLUGIN:CanProperty(client, property, entity)\n\t\tlocal characterID = client:GetCharacter():GetID()\n\n\t\tif (entity:GetNetVar(\"owner\", 0) != characterID\n\t\tand !CAMI.PlayerHasAccess(client, \"Helix - Bypass Prop Protection\", nil)) then\n\t\t\treturn false\n\t\tend\n\tend\n\n\tfunction PLUGIN:CanTool(client, trace, tool)\n\t\tlocal entity = trace.Entity\n\t\tlocal characterID = client:GetCharacter():GetID()\n\n\t\tif (IsValid(entity) and entity:GetNetVar(\"owner\", 0) != characterID\n\t\tand !CAMI.PlayerHasAccess(client, \"Helix - Bypass Prop Protection\", nil)) then\n\t\t\treturn false\n\t\tend\n\tend\n\n\tfunction PLUGIN:PlayerSpawnedProp(client, model, entity)\n\t\tix.log.Add(client, \"spawnProp\", model)\n\tend\n\n\tPLUGIN.PlayerSpawnedEffect = PLUGIN.PlayerSpawnedProp\n\tPLUGIN.PlayerSpawnedRagdoll = PLUGIN.PlayerSpawnedProp\n\n\tfunction PLUGIN:PlayerSpawnedNPC(client, entity)\n\t\tix.log.Add(client, \"spawnEntity\", entity)\n\tend\n\n\tPLUGIN.PlayerSpawnedSWEP = PLUGIN.PlayerSpawnedNPC\n\tPLUGIN.PlayerSpawnedSENT = PLUGIN.PlayerSpawnedNPC\n\tPLUGIN.PlayerSpawnedVehicle = PLUGIN.PlayerSpawnedNPC\nelse\n\tfunction PLUGIN:PhysgunPickup(client, entity)\n\t\tif (entity:GetNetVar(\"owner\", 0) != client:GetCharacter():GetID()\n\t\tand !CAMI.PlayerHasAccess(client, \"Helix - Bypass Prop Protection\", nil)) then\n\t\t\treturn false\n\t\tend\n\tend\n\n\tfunction PLUGIN:CanProperty(client, property, entity)\n\t\tlocal characterID = client:GetCharacter():GetID()\n\n\t\tif (entity:GetNetVar(\"owner\", 0) != characterID\n\t\tand !CAMI.PlayerHasAccess(client, \"Helix - Bypass Prop Protection\", nil)) then\n\t\t\treturn false\n\t\tend\n\tend\n\n\tfunction PLUGIN:CanTool(client, trace, tool)\n\t\tlocal entity = trace.Entity\n\t\tlocal characterID = client:GetCharacter():GetID()\n\n\t\tif (IsValid(entity) and entity:GetNetVar(\"owner\", 0) != characterID\n\t\tand !CAMI.PlayerHasAccess(client, \"Helix - Bypass Prop Protection\", nil)) then\n\t\t\treturn false\n\t\tend\n\tend\nend\n"
  },
  {
    "path": "plugins/recognition.lua",
    "content": "\nPLUGIN.name = \"Recognition\"\nPLUGIN.author = \"Chessnut\"\nPLUGIN.description = \"Adds the ability to recognize people.\"\n\ndo\n\tlocal character = ix.meta.character\n\n\tif (SERVER) then\n\t\tfunction character:Recognize(id)\n\t\t\tif (!isnumber(id) and id.GetID) then\n\t\t\t\tid = id:GetID()\n\t\t\tend\n\n\t\t\tlocal recognized = self:GetData(\"rgn\", \"\")\n\n\t\t\tif (recognized != \"\" and recognized:find(\",\"..id..\",\")) then\n\t\t\t\treturn false\n\t\t\tend\n\n\t\t\tself:SetData(\"rgn\", recognized..\",\"..id..\",\")\n\n\t\t\treturn true\n\t\tend\n\tend\n\n\tfunction character:DoesRecognize(id)\n\t\tif (!isnumber(id) and id.GetID) then\n\t\t\tid = id:GetID()\n\t\tend\n\n\t\treturn hook.Run(\"IsCharacterRecognized\", self, id)\n\tend\n\n\tfunction PLUGIN:IsCharacterRecognized(char, id)\n\t\tif (char.id == id) then\n\t\t\treturn true\n\t\tend\n\n\t\tlocal other = ix.char.loaded[id]\n\n\t\tif (other) then\n\t\t\tlocal faction = ix.faction.indices[other:GetFaction()]\n\n\t\t\tif (faction and faction.isGloballyRecognized) then\n\t\t\t\treturn true\n\t\t\tend\n\t\tend\n\n\t\tlocal recognized = char:GetData(\"rgn\", \"\")\n\n\t\tif (recognized != \"\" and recognized:find(\",\"..id..\",\")) then\n\t\t\treturn true\n\t\tend\n\tend\nend\n\nif (CLIENT) then\n\tCHAT_RECOGNIZED = CHAT_RECOGNIZED or {}\n\tCHAT_RECOGNIZED[\"ic\"] = true\n\tCHAT_RECOGNIZED[\"y\"] = true\n\tCHAT_RECOGNIZED[\"w\"] = true\n\tCHAT_RECOGNIZED[\"me\"] = true\n\n\tfunction PLUGIN:IsRecognizedChatType(chatType)\n\t\tif (CHAT_RECOGNIZED[chatType]) then\n\t\t\treturn true\n\t\tend\n\tend\n\n\tfunction PLUGIN:GetCharacterDescription(client)\n\t\tif (client:GetCharacter() and client != LocalPlayer() and LocalPlayer():GetCharacter() and\n\t\t\t!LocalPlayer():GetCharacter():DoesRecognize(client:GetCharacter()) and !hook.Run(\"IsPlayerRecognized\", client)) then\n\t\t\treturn L\"noRecog\"\n\t\tend\n\tend\n\n\tfunction PLUGIN:ShouldAllowScoreboardOverride(client)\n\t\tif (ix.config.Get(\"scoreboardRecognition\")) then\n\t\t\treturn true\n\t\tend\n\tend\n\n\tfunction PLUGIN:GetCharacterName(client, chatType)\n\t\tif (client != LocalPlayer()) then\n\t\t\tlocal character = client:GetCharacter()\n\t\t\tlocal ourCharacter = LocalPlayer():GetCharacter()\n\n\t\t\tif (ourCharacter and character and !ourCharacter:DoesRecognize(character) and !hook.Run(\"IsPlayerRecognized\", client)) then\n\t\t\t\tif (chatType and hook.Run(\"IsRecognizedChatType\", chatType)) then\n\t\t\t\t\tlocal description = character:GetDescription()\n\n\t\t\t\t\tif (#description > 40) then\n\t\t\t\t\t\tdescription = description:utf8sub(1, 37)..\"...\"\n\t\t\t\t\tend\n\n\t\t\t\t\treturn \"[\"..description..\"]\"\n\t\t\t\telseif (!chatType) then\n\t\t\t\t\treturn L\"unknown\"\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend\n\n\tlocal function Recognize(level)\n\t\tnet.Start(\"ixRecognize\")\n\t\t\tnet.WriteUInt(level, 2)\n\t\tnet.SendToServer()\n\tend\n\n\tnet.Receive(\"ixRecognizeMenu\", function(length)\n\t\tlocal menu = DermaMenu()\n\t\t\tmenu:AddOption(L\"rgnLookingAt\", function()\n\t\t\t\tRecognize(0)\n\t\t\tend)\n\t\t\tmenu:AddOption(L\"rgnWhisper\", function()\n\t\t\t\tRecognize(1)\n\t\t\tend)\n\t\t\tmenu:AddOption(L\"rgnTalk\", function()\n\t\t\t\tRecognize(2)\n\t\t\tend)\n\t\t\tmenu:AddOption(L\"rgnYell\", function()\n\t\t\t\tRecognize(3)\n\t\t\tend)\n\t\tmenu:Open()\n\t\tmenu:MakePopup()\n\t\tmenu:Center()\n\tend)\n\n\tnet.Receive(\"ixRecognizeDone\", function(length)\n\t\thook.Run(\"CharacterRecognized\")\n\tend)\n\n\tfunction PLUGIN:CharacterRecognized(client, recogCharID)\n\t\tsurface.PlaySound(\"buttons/button17.wav\")\n\tend\nelse\n\tutil.AddNetworkString(\"ixRecognize\")\n\tutil.AddNetworkString(\"ixRecognizeMenu\")\n\tutil.AddNetworkString(\"ixRecognizeDone\")\n\n\tfunction PLUGIN:ShowSpare1(client)\n\t\tif (client:GetCharacter()) then\n\t\t\tnet.Start(\"ixRecognizeMenu\")\n\t\t\tnet.Send(client)\n\t\tend\n\tend\n\n\tnet.Receive(\"ixRecognize\", function(length, client)\n\t\tlocal level = net.ReadUInt(2)\n\n\t\tif (isnumber(level)) then\n\t\t\tlocal targets = {}\n\n\t\t\tif (level < 1) then\n\t\t\t\tlocal entity = client:GetEyeTraceNoCursor().Entity\n\n\t\t\t\tif (IsValid(entity) and entity:IsPlayer() and entity:GetCharacter()\n\t\t\t\tand ix.chat.classes.ic:CanHear(client, entity)) then\n\t\t\t\t\ttargets[1] = entity\n\t\t\t\tend\n\t\t\telse\n\t\t\t\tlocal class = \"w\"\n\n\t\t\t\tif (level == 2) then\n\t\t\t\t\tclass = \"ic\"\n\t\t\t\telseif (level == 3) then\n\t\t\t\t\tclass = \"y\"\n\t\t\t\tend\n\n\t\t\t\tclass = ix.chat.classes[class]\n\n\t\t\t\tfor _, v in player.Iterator() do\n\t\t\t\t\tif (client != v and v:GetCharacter() and class:CanHear(client, v)) then\n\t\t\t\t\t\ttargets[#targets + 1] = v\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tif (#targets > 0) then\n\t\t\t\tlocal id = client:GetCharacter():GetID()\n\t\t\t\tlocal i = 0\n\n\t\t\t\tfor _, v in ipairs(targets) do\n\t\t\t\t\tif (v:GetCharacter():Recognize(id)) then\n\t\t\t\t\t\ti = i + 1\n\t\t\t\t\tend\n\t\t\t\tend\n\n\t\t\t\tif (i > 0) then\n\t\t\t\t\tnet.Start(\"ixRecognizeDone\")\n\t\t\t\t\tnet.Send(client)\n\n\t\t\t\t\thook.Run(\"CharacterRecognized\", client, id)\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend)\nend\n"
  },
  {
    "path": "plugins/saveitems.lua",
    "content": "\nPLUGIN.name = \"Save Items\"\nPLUGIN.author = \"Chessnut\"\nPLUGIN.description = \"Saves items that were dropped.\"\n\n--[[\n\tfunction PLUGIN:OnSavedItemLoaded(items)\n\t\tfor k, v in ipairs(items) do\n\t\t\t-- do something\n\t\tend\n\tend\n\n\tfunction PLUGIN:ShouldDeleteSavedItems()\n\t\treturn true\n\tend\n]]--\n\n-- as title says.\n\nfunction PLUGIN:LoadData()\n\tlocal items = self:GetData()\n\n\tif (items) then\n\t\tlocal idRange = {}\n\t\tlocal info = {}\n\n\t\tfor _, v in ipairs(items) do\n\t\t\tidRange[#idRange + 1] = v[1]\n\t\t\tinfo[v[1]] = {v[2], v[3], v[4]}\n\t\tend\n\n\t\tif (#idRange > 0) then\n\t\t\tif (hook.Run(\"ShouldDeleteSavedItems\") == true) then\n\t\t\t\t-- don't spawn saved item and just delete them.\n\t\t\t\tlocal query = mysql:Delete(\"ix_items\")\n\t\t\t\t\tquery:WhereIn(\"item_id\", idRange)\n\t\t\t\tquery:Execute()\n\n\t\t\t\tprint(\"Server Deleted Server Items (does not includes Logical Items)\")\n\t\t\telse\n\t\t\t\tlocal query = mysql:Select(\"ix_items\")\n\t\t\t\t\tquery:Select(\"item_id\")\n\t\t\t\t\tquery:Select(\"unique_id\")\n\t\t\t\t\tquery:Select(\"data\")\n\t\t\t\t\tquery:WhereIn(\"item_id\", idRange)\n\t\t\t\t\tquery:Callback(function(result)\n\t\t\t\t\t\tif (istable(result)) then\n\t\t\t\t\t\t\tlocal loadedItems = {}\n\t\t\t\t\t\t\tlocal bagInventories = {}\n\n\t\t\t\t\t\t\tfor _, v in ipairs(result) do\n\t\t\t\t\t\t\t\tlocal itemID = tonumber(v.item_id)\n\t\t\t\t\t\t\t\tlocal data = util.JSONToTable(v.data or \"[]\")\n\t\t\t\t\t\t\t\tlocal uniqueID = v.unique_id\n\t\t\t\t\t\t\t\tlocal itemTable = ix.item.list[uniqueID]\n\n\t\t\t\t\t\t\t\tif (itemTable and itemID) then\n\t\t\t\t\t\t\t\t\tlocal item = ix.item.New(uniqueID, itemID)\n\t\t\t\t\t\t\t\t\titem.data = data or {}\n\n\t\t\t\t\t\t\t\t\tlocal itemInfo = info[itemID]\n\t\t\t\t\t\t\t\t\tlocal position, angles, bMovable = itemInfo[1], itemInfo[2], true\n\n\t\t\t\t\t\t\t\t\tif (isbool(itemInfo[3])) then\n\t\t\t\t\t\t\t\t\t\tbMovable = itemInfo[3]\n\t\t\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\t\t\t\tlocal itemEntity = item:Spawn(position, angles)\n\t\t\t\t\t\t\t\t\titemEntity.ixItemID = itemID\n\n\t\t\t\t\t\t\t\t\tlocal physicsObject = itemEntity:GetPhysicsObject()\n\n\t\t\t\t\t\t\t\t\tif (IsValid(physicsObject)) then\n\t\t\t\t\t\t\t\t\t\tphysicsObject:EnableMotion(bMovable)\n\t\t\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\t\t\t\titem.invID = 0\n\t\t\t\t\t\t\t\t\tloadedItems[#loadedItems + 1] = item\n\n\t\t\t\t\t\t\t\t\tif (item.isBag) then\n\t\t\t\t\t\t\t\t\t\tlocal invType = ix.item.inventoryTypes[uniqueID]\n\t\t\t\t\t\t\t\t\t\tbagInventories[item:GetData(\"id\")] = {invType.w, invType.h}\n\t\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\t\t-- we need to manually restore bag inventories in the world since they don't have a current owner\n\t\t\t\t\t\t\t-- that it can automatically restore along with the character when it's loaded\n\t\t\t\t\t\t\tif (!table.IsEmpty(bagInventories)) then\n\t\t\t\t\t\t\t\tix.inventory.Restore(bagInventories)\n\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\t\thook.Run(\"OnSavedItemLoaded\", loadedItems) -- when you have something in the dropped item.\n\t\t\t\t\t\tend\n\t\t\t\t\tend)\n\t\t\t\tquery:Execute()\n\t\t\tend\n\t\tend\n\tend\nend\n\nfunction PLUGIN:SaveData()\n\tlocal items = {}\n\n\tfor _, v in ipairs(ents.FindByClass(\"ix_item\")) do\n\t\tif (v.ixItemID and !v.bTemporary) then\n\t\t\tlocal physicsObject = v:GetPhysicsObject()\n\t\t\tlocal bMovable = nil\n\n\t\t\tif (IsValid(physicsObject)) then\n\t\t\t\tbMovable = physicsObject:IsMoveable()\n\t\t\tend\n\n\t\t\titems[#items + 1] = {\n\t\t\t\tv.ixItemID, v:GetPos(), v:GetAngles(), bMovable\n\t\t\t}\n\t\tend\n\tend\n\n\tself:SetData(items)\nend\n"
  },
  {
    "path": "plugins/spawns.lua",
    "content": "\nlocal PLUGIN = PLUGIN\n\nPLUGIN.name = \"Spawns\"\nPLUGIN.description = \"Spawn points for factions and classes.\"\nPLUGIN.author = \"Chessnut\"\nPLUGIN.spawns = PLUGIN.spawns or {}\n\nfunction PLUGIN:PlayerLoadout(client)\n\tlocal character = client:GetCharacter()\n\n\tif (self.spawns and !table.IsEmpty(self.spawns) and character) then\n\t\tlocal class = character:GetClass()\n\t\tlocal points\n\t\tlocal className = \"default\"\n\n\t\tfor k, v in ipairs(ix.faction.indices) do\n\t\t\tif (k == client:Team()) then\n\t\t\t\tpoints = self.spawns[v.uniqueID] or {}\n\n\t\t\t\tbreak\n\t\t\tend\n\t\tend\n\n\t\tif (points) then\n\t\t\tfor _, v in ipairs(ix.class.list) do\n\t\t\t\tif (class == v.index) then\n\t\t\t\t\tclassName = v.uniqueID\n\n\t\t\t\t\tbreak\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tpoints = points[className] or points[\"default\"]\n\n\t\t\tif (points and !table.IsEmpty(points)) then\n\t\t\t\tlocal position = points[ math.random( #points ) ]\n\n\t\t\t\tclient:SetPos(position)\n\t\t\tend\n\t\tend\n\tend\nend\n\nfunction PLUGIN:LoadData()\n\tself.spawns = self:GetData() or {}\nend\n\nfunction PLUGIN:SaveSpawns()\n\tself:SetData(self.spawns)\nend\n\nix.command.Add(\"SpawnAdd\", {\n\tdescription = \"@cmdSpawnAdd\",\n\tprivilege = \"Manage Spawn Points\",\n\tadminOnly = true,\n\targuments = {\n\t\tix.type.string,\n\t\tbit.bor(ix.type.text, ix.type.optional)\n\t},\n\tOnRun = function(self, client, name, class)\n\t\tlocal info = ix.faction.indices[name:lower()]\n\t\tlocal info2\n\t\tlocal faction\n\n\t\tif (!info) then\n\t\t\tfor _, v in ipairs(ix.faction.indices) do\n\t\t\t\tif (ix.util.StringMatches(v.uniqueID, name) or ix.util.StringMatches(L(v.name, client), name)) then\n\t\t\t\t\tfaction = v.uniqueID\n\t\t\t\t\tinfo = v\n\n\t\t\t\t\tbreak\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\n\t\tif (info) then\n\t\t\tif (class and class != \"\") then\n\t\t\t\tlocal found = false\n\n\t\t\t\tfor _, v in ipairs(ix.class.list) do\n\t\t\t\t\tif (v.faction == info.index and\n\t\t\t\t\t\t(v.uniqueID:lower() == class:lower() or ix.util.StringMatches(L(v.name, client), class))) then\n\t\t\t\t\t\tclass = v.uniqueID\n\t\t\t\t\t\tinfo2 = v\n\t\t\t\t\t\tfound = true\n\n\t\t\t\t\t\tbreak\n\t\t\t\t\tend\n\t\t\t\tend\n\n\t\t\t\tif (!found) then\n\t\t\t\t\treturn \"@invalidClass\"\n\t\t\t\tend\n\t\t\telse\n\t\t\t\tclass = \"default\"\n\t\t\tend\n\n\t\t\tPLUGIN.spawns[faction] = PLUGIN.spawns[faction] or {}\n\t\t\tPLUGIN.spawns[faction][class] = PLUGIN.spawns[faction][class] or {}\n\n\t\t\ttable.insert(PLUGIN.spawns[faction][class], client:GetPos())\n\n\t\t\tPLUGIN:SaveSpawns()\n\n\t\t\tname = L(info.name, client)\n\n\t\t\tif (info2) then\n\t\t\t\tname = name .. \" (\" .. L(info2.name, client) .. \")\"\n\t\t\tend\n\n\t\t\treturn \"@spawnAdded\", name\n\t\telse\n\t\t\treturn \"@invalidFaction\"\n\t\tend\n\tend\n})\n\nix.command.Add(\"SpawnRemove\", {\n\tdescription = \"@cmdSpawnRemove\",\n\tprivilege = \"Manage Spawn Points\",\n\tadminOnly = true,\n\targuments = bit.bor(ix.type.number, ix.type.optional),\n\tOnRun = function(self, client, radius)\n\t\tradius = radius or 120\n\n\t\tlocal position = client:GetPos()\n\t\tlocal i = 0\n\n\t\tfor _, v in pairs(PLUGIN.spawns) do\n\t\t\tfor _, v2 in pairs(v) do\n\t\t\t\tfor k3, v3 in pairs(v2) do\n\t\t\t\t\tif (v3:Distance(position) <= radius) then\n\t\t\t\t\t\tv2[k3] = nil\n\t\t\t\t\t\ti = i + 1\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\n\t\tif (i > 0) then\n\t\t\tPLUGIN:SaveSpawns()\n\t\tend\n\n\t\treturn \"@spawnDeleted\", i\n\tend\n})\n"
  },
  {
    "path": "plugins/spawnsaver.lua",
    "content": "PLUGIN.name = \"Spawn Saver\"\nPLUGIN.author = \"Chessnut\"\nPLUGIN.description = \"Saves the position of a character.\"\n\n-- Called right before the character has its information save.\nfunction PLUGIN:CharacterPreSave(character)\n\t-- Get the player from the character.\n\tlocal client = character:GetPlayer()\n\n\t-- Check to see if we can get the player's position.\n\tif (IsValid(client)) then\n\t\tlocal position, eyeAngles = client:GetPos(), client:EyeAngles()\n\t\t-- Use pre-observer position to prevent spawning in the air.\n\t\tif (client.ixObsData) then\n\t\t\tposition, eyeAngles = client.ixObsData[1], client.ixObsData[2]\n\t\tend\n\t\t-- Store the position in the character's data.\n\t\tcharacter:SetData(\"pos\", {position, eyeAngles, game.GetMap()})\n\tend\nend\n\n-- Called after the player's loadout has been set.\nfunction PLUGIN:PlayerLoadedCharacter(client, character, lastChar)\n\ttimer.Simple(0, function()\n\t\tif (IsValid(client)) then\n\t\t\t-- Get the saved position from the character data.\n\t\t\tlocal position = character:GetData(\"pos\")\n\n\t\t\t-- Check if the position was set.\n\t\t\tif (position) then\n\t\t\t\tif (position[3] and position[3]:lower() == game.GetMap():lower()) then\n\t\t\t\t\t-- Restore the player to that position.\n\t\t\t\t\tclient:SetPos(position[1].x and position[1] or client:GetPos())\n\t\t\t\t\tclient:SetEyeAngles(position[2].p and position[2] or angle_zero)\n\t\t\t\tend\n\n\t\t\t\t-- Remove the position data since it is no longer needed.\n\t\t\t\tcharacter:SetData(\"pos\", nil)\n\t\t\tend\n\t\tend\n\tend)\nend\n"
  },
  {
    "path": "plugins/stamina/attributes/sh_end.lua",
    "content": "ATTRIBUTE.name = \"Endurance\"\nATTRIBUTE.description = \"Affects how long you can run for.\""
  },
  {
    "path": "plugins/stamina/attributes/sh_stm.lua",
    "content": "ATTRIBUTE.name = \"Stamina\"\nATTRIBUTE.description = \"Affects how fast you can run.\"\n\nfunction ATTRIBUTE:OnSetup(client, value)\n\tclient:SetRunSpeed(ix.config.Get(\"runSpeed\") + value)\nend\n"
  },
  {
    "path": "plugins/stamina/sh_plugin.lua",
    "content": "PLUGIN.name = \"Stamina\"\nPLUGIN.author = \"Chessnut\"\nPLUGIN.description = \"Adds a stamina system to limit running.\"\n\n-- luacheck: push ignore 631\nix.config.Add(\"staminaDrain\", 1, \"How much stamina to drain per tick (every quarter second). This is calculated before attribute reduction.\", nil, {\n\tdata = {min = 0, max = 10, decimals = 2},\n\tcategory = \"characters\"\n})\n\nix.config.Add(\"staminaRegeneration\", 1.75, \"How much stamina to regain per tick (every quarter second).\", nil, {\n\tdata = {min = 0, max = 10, decimals = 2},\n\tcategory = \"characters\"\n})\n\nix.config.Add(\"staminaCrouchRegeneration\", 2, \"How much stamina to regain per tick (every quarter second) while crouching.\", nil, {\n\tdata = {min = 0, max = 10, decimals = 2},\n\tcategory = \"characters\"\n})\n\nix.config.Add(\"punchStamina\", 10, \"How much stamina punches use up.\", nil, {\n\tdata = {min = 0, max = 100},\n\tcategory = \"characters\"\n})\n-- luacheck: pop\nlocal function CalcStaminaChange(client)\n\tlocal character = client:GetCharacter()\n\n\tif (!character or client:GetMoveType() == MOVETYPE_NOCLIP) then\n\t\treturn 0\n\tend\n\n\tlocal walkSpeed = ix.config.Get(\"walkSpeed\")\n\tlocal maxAttributes = ix.config.Get(\"maxAttributes\", 100)\n\tlocal offset\n\n\tif (client:KeyDown(IN_SPEED) and client:GetVelocity():LengthSqr() >= (walkSpeed * walkSpeed) and client:OnGround()) then\n\t\t-- characters could have attribute values greater than max if the config was changed\n\t\toffset = -ix.config.Get(\"staminaDrain\", 1) + math.min(character:GetAttribute(\"end\", 0), maxAttributes) / 100\n\telse\n\t\toffset = client:Crouching() and ix.config.Get(\"staminaCrouchRegeneration\", 2) or ix.config.Get(\"staminaRegeneration\", 1.75)\n\tend\n\n\toffset = hook.Run(\"AdjustStaminaOffset\", client, offset) or offset\n\n\tif (CLIENT) then\n\t\treturn offset -- for the client we need to return the estimated stamina change\n\telse\n\t\tlocal current = client:GetLocalVar(\"stm\", 0)\n\t\tlocal value = math.Clamp(current + offset, 0, 100)\n\n\t\tif (current != value) then\n\t\t\tclient:SetLocalVar(\"stm\", value)\n\n\t\t\tif (value == 0 and !client:GetNetVar(\"brth\", false)) then\n\t\t\t\tclient:SetNetVar(\"brth\", true)\n\n\t\t\t\tcharacter:UpdateAttrib(\"end\", 0.1)\n\t\t\t\tcharacter:UpdateAttrib(\"stm\", 0.01)\n\n\t\t\t\thook.Run(\"PlayerStaminaLost\", client)\n\t\t\telseif (value >= 50 and client:GetNetVar(\"brth\", false)) then\n\t\t\t\tclient:SetNetVar(\"brth\", nil)\n\n\t\t\t\thook.Run(\"PlayerStaminaGained\", client)\n\t\t\tend\n\t\tend\n\tend\nend\n\nfunction PLUGIN:SetupMove(client, mv, cmd)\n\tif (client:GetNetVar(\"brth\", false)) then\n\t\tmv:SetMaxClientSpeed(client:GetWalkSpeed())\n\tend\nend\n\nif (SERVER) then\n\tfunction PLUGIN:PostPlayerLoadout(client)\n\t\tlocal uniqueID = \"ixStam\" .. client:SteamID()\n\n\t\ttimer.Create(uniqueID, 0.25, 0, function()\n\t\t\tif (!IsValid(client)) then\n\t\t\t\ttimer.Remove(uniqueID)\n\t\t\t\treturn\n\t\t\tend\n\n\t\t\tCalcStaminaChange(client)\n\t\tend)\n\tend\n\n\tfunction PLUGIN:CharacterPreSave(character)\n\t\tlocal client = character:GetPlayer()\n\n\t\tif (IsValid(client)) then\n\t\t\tcharacter:SetData(\"stamina\", client:GetLocalVar(\"stm\", 0))\n\t\tend\n\tend\n\n\tfunction PLUGIN:PlayerLoadedCharacter(client, character)\n\t\ttimer.Simple(0.25, function()\n\t\t\tclient:SetLocalVar(\"stm\", character:GetData(\"stamina\", 100))\n\t\tend)\n\tend\n\n\tlocal playerMeta = FindMetaTable(\"Player\")\n\n\tfunction playerMeta:RestoreStamina(amount)\n\t\tlocal current = self:GetLocalVar(\"stm\", 0)\n\t\tlocal value = math.Clamp(current + amount, 0, 100)\n\n\t\tself:SetLocalVar(\"stm\", value)\n\tend\n\n\tfunction playerMeta:ConsumeStamina(amount)\n\t\tlocal current = self:GetLocalVar(\"stm\", 0)\n\t\tlocal value = math.Clamp(current - amount, 0, 100)\n\n\t\tself:SetLocalVar(\"stm\", value)\n\tend\n\nelse\n\n\tlocal predictedStamina = 100\n\n\tfunction PLUGIN:Think()\n\t\tlocal offset = CalcStaminaChange(LocalPlayer())\n\t\t-- the server check it every 0.25 sec, here we check it every [FrameTime()] seconds\n\t\toffset = math.Remap(FrameTime(), 0, 0.25, 0, offset)\n\n\t\tif (offset != 0) then\n\t\t\tpredictedStamina = math.Clamp(predictedStamina + offset, 0, 100)\n\t\tend\n\tend\n\n\tfunction PLUGIN:OnLocalVarSet(key, var)\n\t\tif (key != \"stm\") then return end\n\t\tif (math.abs(predictedStamina - var) > 5) then\n\t\t\tpredictedStamina = var\n\t\tend\n\tend\n\n\tix.bar.Add(function()\n\t\treturn predictedStamina / 100\n\tend, Color(200, 200, 40), nil, \"stm\")\nend\n"
  },
  {
    "path": "plugins/strength/attributes/sh_str.lua",
    "content": "ATTRIBUTE.name = \"Strength\"\nATTRIBUTE.description = \"A measure of how strong you are.\""
  },
  {
    "path": "plugins/strength/sh_plugin.lua",
    "content": "PLUGIN.name = \"Strength\"\nPLUGIN.author = \"Chessnut\"\nPLUGIN.description = \"Adds a strength attribute.\"\n\nif (SERVER) then\n\tfunction PLUGIN:GetPlayerPunchDamage(client, damage, context)\n\t\tif (client:GetCharacter()) then\n\t\t\t-- Add to the total fist damage.\n\t\t\tcontext.damage = context.damage + (client:GetCharacter():GetAttribute(\"str\", 0) * ix.config.Get(\"strengthMultiplier\"))\n\t\tend\n\tend\n\n\tfunction PLUGIN:PlayerThrowPunch(client, trace)\n\t\tif (client:GetCharacter() and IsValid(trace.Entity) and trace.Entity:IsPlayer()) then\n\t\t\tclient:GetCharacter():UpdateAttrib(\"str\", 0.001)\n\t\tend\n\tend\nend\n\n-- Configuration for the plugin\nix.config.Add(\"strengthMultiplier\", 0.3, \"The strength multiplier scale\", nil, {\n\tdata = {min = 0, max = 1.0, decimals = 1},\n\tcategory = \"Strength\"\n})\n"
  },
  {
    "path": "plugins/thirdperson.lua",
    "content": "\nlocal PLUGIN = PLUGIN\n\nPLUGIN.name = \"Third Person\"\nPLUGIN.author = \"Black Tea\"\nPLUGIN.description = \"Enables third person camera usage.\"\n\nix.config.Add(\"thirdperson\", false, \"Allow Thirdperson in the server.\", nil, {\n\tcategory = \"server\"\n})\n\nif (CLIENT) then\n\tlocal function isHidden()\n\t\treturn !ix.config.Get(\"thirdperson\")\n\tend\n\n\tix.option.Add(\"thirdpersonEnabled\", ix.type.bool, false, {\n\t\tcategory = \"thirdperson\",\n\t\thidden = isHidden,\n\t\tOnChanged = function(oldValue, value)\n\t\t\thook.Run(\"ThirdPersonToggled\", oldValue, value)\n\t\tend\n\t})\n\n\tix.option.Add(\"thirdpersonClassic\", ix.type.bool, false, {\n\t\tcategory = \"thirdperson\",\n\t\thidden = isHidden\n\t})\n\n\tix.option.Add(\"thirdpersonVertical\", ix.type.number, 10, {\n\t\tcategory = \"thirdperson\", min = 0, max = 30,\n\t\thidden = isHidden\n\t})\n\n\tix.option.Add(\"thirdpersonHorizontal\", ix.type.number, 0, {\n\t\tcategory = \"thirdperson\", min = -30, max = 30,\n\t\thidden = isHidden\n\t})\n\n\tix.option.Add(\"thirdpersonDistance\", ix.type.number, 50, {\n\t\tcategory = \"thirdperson\", min = 0, max = 100,\n\t\thidden = isHidden\n\t})\n\n\n\tconcommand.Add(\"ix_togglethirdperson\", function()\n\t\tlocal bEnabled = !ix.option.Get(\"thirdpersonEnabled\", false)\n\n\t\tix.option.Set(\"thirdpersonEnabled\", bEnabled)\n\tend)\n\n\tlocal function isAllowed()\n\t\treturn ix.config.Get(\"thirdperson\")\n\tend\n\n\tlocal playerMeta = FindMetaTable(\"Player\")\n\tlocal traceMin = Vector(-10, -10, -10)\n\tlocal traceMax = Vector(10, 10, 10)\n\n\tfunction playerMeta:CanOverrideView()\n\t\tlocal entity = Entity(self:GetLocalVar(\"ragdoll\", 0))\n\n\t\tif (IsValid(ix.gui.characterMenu) and !ix.gui.characterMenu:IsClosing() and ix.gui.characterMenu:IsVisible()) then\n\t\t\treturn false\n\t\tend\n\n\t\tif (IsValid(ix.gui.menu) and ix.gui.menu:GetCharacterOverview()) then\n\t\t\treturn false\n\t\tend\n\n\t\tif (ix.option.Get(\"thirdpersonEnabled\", false) and\n\t\t\t!IsValid(self:GetVehicle()) and\n\t\t\tisAllowed() and\n\t\t\tIsValid(self) and\n\t\t\tself:GetCharacter() and\n\t\t\t!self:GetNetVar(\"actEnterAngle\") and\n\t\t\t!IsValid(entity) and\n\t\t\tLocalPlayer():Alive()\n\t\t\t) then\n\t\t\treturn true\n\t\tend\n\tend\n\n\tlocal view, traceData, traceData2, aimOrigin, crouchFactor, ft, curAng, owner\n\tlocal clmp = math.Clamp\n\tcrouchFactor = 0\n\n\tfunction PLUGIN:CalcView(client, origin, angles, fov)\n\t\tft = FrameTime()\n\n\t\tif (client:CanOverrideView() and LocalPlayer():GetViewEntity() == LocalPlayer()) then\n\t\t\tlocal bNoclip = LocalPlayer():GetMoveType() == MOVETYPE_NOCLIP\n\n\t\t\tif ((client:OnGround() and client:KeyDown(IN_DUCK)) or client:Crouching()) then\n\t\t\t\tcrouchFactor = Lerp(ft*5, crouchFactor, 1)\n\t\t\telse\n\t\t\t\tcrouchFactor = Lerp(ft*5, crouchFactor, 0)\n\t\t\tend\n\n\t\t\tcurAng = owner.camAng or angle_zero\n\t\t\tview = {}\n\t\t\ttraceData = {}\n\t\t\t\ttraceData.start = \tclient:GetPos() + client:GetViewOffset() +\n\t\t\t\t\t\t\t\t\tcurAng:Up() * ix.option.Get(\"thirdpersonVertical\", 10) +\n\t\t\t\t\t\t\t\t\tcurAng:Right() * ix.option.Get(\"thirdpersonHorizontal\", 0) -\n\t\t\t\t\t\t\t\t\tclient:GetViewOffsetDucked() * .5 * crouchFactor\n\t\t\t\ttraceData.endpos = traceData.start - curAng:Forward() * ix.option.Get(\"thirdpersonDistance\", 50)\n\t\t\t\ttraceData.filter = client\n\t\t\t\ttraceData.ignoreworld = bNoclip\n\t\t\t\ttraceData.mins = traceMin\n\t\t\t\ttraceData.maxs = traceMax\n\t\t\tview.origin = util.TraceHull(traceData).HitPos\n\t\t\taimOrigin = view.origin\n\t\t\tview.angles = curAng + client:GetViewPunchAngles()\n\n\t\t\ttraceData2 = {}\n\t\t\t\ttraceData2.start = \taimOrigin\n\t\t\t\ttraceData2.endpos = aimOrigin + curAng:Forward() * 65535\n\t\t\t\ttraceData2.filter = client\n\t\t\t\ttraceData2.ignoreworld = bNoclip\n\n\t\t\tlocal bClassic = ix.option.Get(\"thirdpersonClassic\", false)\n\n\t\t\tif (bClassic or owner:IsWepRaised() or\n\t\t\t\t(owner:KeyDown(bit.bor(IN_FORWARD, IN_BACK, IN_MOVELEFT, IN_MOVERIGHT)) and owner:GetVelocity():Length() >= 10)) then\n\t\t\t\tclient:SetEyeAngles((util.TraceLine(traceData2).HitPos - client:GetShootPos()):Angle())\n\t\t\telse\n\t\t\t\tlocal currentAngles = client:EyeAngles()\n\t\t\t\tcurrentAngles.pitch = (util.TraceLine(traceData2).HitPos - client:GetShootPos()):Angle().pitch\n\n\t\t\t\tclient:SetEyeAngles(currentAngles)\n\t\t\tend\n\n\t\t\treturn view\n\t\tend\n\tend\n\n\tlocal diff, fm, sm\n\tfunction PLUGIN:CreateMove(cmd)\n\t\towner = LocalPlayer()\n\n\t\tif (owner:CanOverrideView() and owner:GetMoveType() != MOVETYPE_NOCLIP and\n\t\t\tLocalPlayer():GetViewEntity() == LocalPlayer()) then\n\t\t\tfm = cmd:GetForwardMove()\n\t\t\tsm = cmd:GetSideMove()\n\t\t\tdiff = (owner:EyeAngles() - (owner.camAng or Angle(0, 0, 0)))[2] or 0\n\t\t\tdiff = diff / 90\n\n\t\t\tcmd:SetForwardMove(fm + sm * diff)\n\t\t\tcmd:SetSideMove(sm + fm * diff)\n\t\t\treturn false\n\t\tend\n\tend\n\n\tfunction PLUGIN:InputMouseApply(cmd, x, y, ang)\n\t\towner = LocalPlayer()\n\n\t\tif (!owner.camAng) then\n\t\t\towner.camAng = Angle(0, 0, 0)\n\t\tend\n\n\t\towner.camAng.p = clmp(math.NormalizeAngle(owner.camAng.p + y / 50), -85, 85)\n\t\towner.camAng.y = math.NormalizeAngle(owner.camAng.y - x / 50)\n\n\t\tif (owner:CanOverrideView() and LocalPlayer():GetViewEntity() == LocalPlayer()) then\n\t\t\treturn true\n\t\tend\n\tend\n\n\tfunction PLUGIN:ShouldDrawLocalPlayer()\n\t\tif (LocalPlayer():GetViewEntity() == LocalPlayer() and !IsValid(LocalPlayer():GetVehicle())) then\n\t\t\treturn LocalPlayer():CanOverrideView()\n\t\tend\n\tend\nend\n"
  },
  {
    "path": "plugins/typing.lua",
    "content": "\nlocal PLUGIN = PLUGIN\n\nPLUGIN.name = \"Typing Indicator\"\nPLUGIN.description = \"Shows an indicator when someone is typing.\"\nPLUGIN.author = \"`impulse\"\nPLUGIN.animationTime = 0.5\n\nif (CLIENT) then\n\tlocal standingOffset = Vector(0, 0, 72)\n\tlocal crouchingOffset = Vector(0, 0, 38)\n\tlocal boneOffset = Vector(0, 0, 10)\n\tlocal textColor = Color(250, 250, 250)\n\tlocal shadowColor = Color(66, 66, 66)\n\tlocal currentClass\n\n\t-- we can't rely on matching non-alphanumeric characters (i.e %W) due to patterns matching single bytes and not UTF-8 chars\n\tlocal symbolPattern = \"[~`!@#$%%%^&*()_%+%-={}%[%]|;:'\\\",%./<>?]\"\n\n\tfunction PLUGIN:LoadFonts(font, genericFont)\n\t\tsurface.CreateFont(\"ixTypingIndicator\", {\n\t\t\tfont = genericFont,\n\t\t\tsize = 128,\n\t\t\textended = true,\n\t\t\tweight = 1000\n\t\t})\n\tend\n\n\tfunction PLUGIN:ChatTextChanged(text)\n\t\tif (!IsValid(LocalPlayer())) then\n\t\t\treturn\n\t\tend\n\n\t\tlocal character = LocalPlayer():GetCharacter()\n\n\t\tif (!character) then\n\t\t\treturn\n\t\tend\n\n\t\tif (text == \"\") then\n\t\t\tcurrentClass = nil\n\n\t\t\tnet.Start(\"ixTypeClass\")\n\t\t\t\tnet.WriteString(\"\")\n\t\t\tnet.SendToServer()\n\n\t\t\treturn\n\t\tend\n\n\t\tlocal newClass = hook.Run(\"GetTypingIndicator\", character, text)\n\n\t\tif (newClass != currentClass) then\n\t\t\tcurrentClass = newClass\n\n\t\t\tnet.Start(\"ixTypeClass\")\n\t\t\t\tnet.WriteString(currentClass or \"\")\n\t\t\tnet.SendToServer()\n\t\tend\n\tend\n\n\tfunction PLUGIN:FinishChat()\n\t\tcurrentClass = nil\n\n\t\tnet.Start(\"ixTypeClass\")\n\t\t\tnet.WriteString(\"\")\n\t\tnet.SendToServer()\n\tend\n\n\tfunction PLUGIN:GetTypingIndicator(character, text)\n\t\tlocal prefix = text:utf8sub(1, 1)\n\n\t\tif (!prefix:find(symbolPattern) and text:utf8len() > 1) then\n\t\t\treturn \"ic\"\n\t\telse\n\t\t\tlocal chatType = ix.chat.Parse(nil, text)\n\n\t\t\tif (chatType and chatType != \"ic\") then\n\t\t\t\treturn !ix.chat.classes[chatType].bNoIndicator and chatType or nil\n\t\t\tend\n\n\t\t\t-- some commands will have their own typing indicator, so we'll make sure we're actually typing out a command first\n\t\t\tlocal start, _, commandName = text:find(\"/(%S+)%s\")\n\n\t\t\tif (start == 1) then\n\t\t\t\tfor uniqueID, command in pairs(ix.command.list) do\n\t\t\t\t\tif (command.bNoIndicator) then\n\t\t\t\t\t\tcontinue\n\t\t\t\t\tend\n\n\t\t\t\t\tif (commandName == uniqueID) then\n\t\t\t\t\t\treturn command.indicator and \"@\" .. command.indicator or \"ooc\"\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend\n\n\tfunction PLUGIN:GetTypingIndicatorPosition(client)\n\t\tlocal head\n\n\t\tfor i = 1, client:GetBoneCount() do\n\t\t\tlocal name = client:GetBoneName(i)\n\n\t\t\tif (string.find(name:lower(), \"head\")) then\n\t\t\t\thead = i\n\t\t\t\tbreak\n\t\t\tend\n\t\tend\n\n\t\tlocal position = head and client:GetBonePosition(head) or (client:Crouching() and crouchingOffset or standingOffset)\n\t\treturn position + boneOffset\n\tend\n\n\tfunction PLUGIN:PostDrawTranslucentRenderables()\n\t\tlocal client = LocalPlayer()\n\t\tlocal position = client:GetPos()\n\n\t\tfor _, v in player.Iterator() do\n\t\t\tif (v == client) then\n\t\t\t\tcontinue\n\t\t\tend\n\n\t\t\tlocal distance = v:GetPos():DistToSqr(position)\n\t\t\tlocal moveType = v:GetMoveType()\n\n\t\t\tif (!IsValid(v) or !v:Alive() or\n\t\t\t\t(moveType != MOVETYPE_WALK and moveType != MOVETYPE_NONE) or\n\t\t\t\t!v.ixChatClassText or\n\t\t\t\tdistance >= v.ixChatClassRange) then\n\t\t\t\tcontinue\n\t\t\tend\n\n\t\t\tlocal text = v.ixChatClassText\n\t\t\tlocal range = v.ixChatClassRange\n\n\t\t\tlocal bAnimation = !ix.option.Get(\"disableAnimations\", false)\n\t\t\tlocal fraction\n\n\t\t\tif (bAnimation) then\n\t\t\t\tlocal bComplete = v.ixChatClassTween:update(FrameTime())\n\n\t\t\t\tif (bComplete and !v.ixChatStarted) then\n\t\t\t\t\tv.ixChatClassText = nil\n\t\t\t\t\tv.ixChatClassRange = nil\n\n\t\t\t\t\tcontinue\n\t\t\t\tend\n\n\t\t\t\tfraction = v.ixChatClassAnimation\n\t\t\telse\n\t\t\t\tfraction = 1\n\t\t\tend\n\n\t\t\tlocal angle = EyeAngles()\n\t\t\tangle:RotateAroundAxis(angle:Forward(), 90)\n\t\t\tangle:RotateAroundAxis(angle:Right(), 90)\n\n\t\t\tcam.Start3D2D(self:GetTypingIndicatorPosition(v), Angle(0, angle.y, 90), 0.05)\n\t\t\t\tsurface.SetFont(\"ixTypingIndicator\")\n\n\t\t\t\tlocal _, textHeight = surface.GetTextSize(text)\n\t\t\t\tlocal alpha = bAnimation and ((1 - math.min(distance, range) / range) * 255 * fraction) or 255\n\n\t\t\t\tdraw.SimpleTextOutlined(text, \"ixTypingIndicator\", 0,\n\t\t\t\t\t-textHeight * 0.5 * fraction,\n\t\t\t\t\tColorAlpha(textColor, alpha),\n\t\t\t\t\tTEXT_ALIGN_CENTER,\n\t\t\t\t\tTEXT_ALIGN_CENTER, 4,\n\t\t\t\t\tColorAlpha(shadowColor, alpha)\n\t\t\t\t)\n\t\t\tcam.End3D2D()\n\t\tend\n\tend\n\n\tnet.Receive(\"ixTypeClass\", function()\n\t\tlocal client = net.ReadEntity()\n\n\t\tif (!IsValid(client) or client == LocalPlayer()) then\n\t\t\treturn\n\t\tend\n\n\t\tlocal newClass = net.ReadString()\n\t\tlocal chatClass = ix.chat.classes[newClass]\n\t\tlocal text\n\t\tlocal range\n\n\t\tif (chatClass) then\n\t\t\ttext = L(chatClass.indicator or \"chatTyping\")\n\t\t\trange = chatClass.range or math.pow(ix.config.Get(\"chatRange\", 280), 2)\n\t\telseif (newClass and newClass:sub(1, 1) == \"@\") then\n\t\t\ttext = L(newClass:sub(2))\n\t\t\trange = math.pow(ix.config.Get(\"chatRange\", 280), 2)\n\t\tend\n\n\t\tif (ix.option.Get(\"disableAnimations\", false)) then\n\t\t\tclient.ixChatClassText = text\n\t\t\tclient.ixChatClassRange = range\n\t\telse\n\t\t\tclient.ixChatClassAnimation = tonumber(client.ixChatClassAnimation) or 0\n\n\t\t\tif (text and !client.ixChatStarted) then\n\t\t\t\tclient.ixChatClassTween = ix.tween.new(PLUGIN.animationTime, client, {ixChatClassAnimation = 1}, \"outCubic\")\n\n\t\t\t\tclient.ixChatClassText = text\n\t\t\t\tclient.ixChatClassRange = range\n\t\t\t\tclient.ixChatStarted = true\n\t\t\telseif (!text and client.ixChatStarted) then\n\t\t\t\tclient.ixChatClassTween = ix.tween.new(PLUGIN.animationTime, client, {ixChatClassAnimation = 0}, \"inCubic\")\n\t\t\t\tclient.ixChatStarted = nil\n\t\t\tend\n\t\tend\n\tend)\nelse\n\tutil.AddNetworkString(\"ixTypeClass\")\n\n\tfunction PLUGIN:PlayerSpawn(client)\n\t\tnet.Start(\"ixTypeClass\")\n\t\t\tnet.WriteEntity(client)\n\t\t\tnet.WriteString(\"\")\n\t\tnet.Broadcast()\n\tend\n\n\tnet.Receive(\"ixTypeClass\", function(length, client)\n\t\tif ((client.ixNextTypeClass or 0) > RealTime()) then\n\t\t\treturn\n\t\tend\n\n\t\tlocal newClass = net.ReadString()\n\n\t\t-- send message to players in pvs only since they're the only ones who can see the indicator\n\t\t-- we'll broadcast if the type class is empty because they might move out of pvs before the ending net message is sent\n\t\tnet.Start(\"ixTypeClass\")\n\t\tnet.WriteEntity(client)\n\t\tnet.WriteString(newClass)\n\n\t\tif (newClass == \"\") then\n\t\t\tnet.Broadcast()\n\t\telse\n\t\t\tnet.SendPVS(client:GetPos())\n\t\tend\n\n\t\tclient.ixNextTypeClass = RealTime() + 0.2\n\tend)\nend\n"
  },
  {
    "path": "plugins/vendor/derma/cl_vendor.lua",
    "content": "\nlocal PANEL = {}\n\nAccessorFunc(PANEL, \"bReadOnly\", \"ReadOnly\", FORCE_BOOL)\n\nfunction PANEL:Init()\n\tself:SetSize(ScrW() * 0.45, ScrH() * 0.65)\n\tself:SetTitle(\"\")\n\tself:MakePopup()\n\tself:Center()\n\n\tlocal header = self:Add(\"DPanel\")\n\theader:SetTall(34)\n\theader:Dock(TOP)\n\n\tself.vendorName = header:Add(\"DLabel\")\n\tself.vendorName:Dock(LEFT)\n\tself.vendorName:SetWide(self:GetWide() * 0.5 - 7)\n\tself.vendorName:SetText(\"John Doe\")\n\tself.vendorName:SetTextInset(4, 0)\n\tself.vendorName:SetTextColor(color_white)\n\tself.vendorName:SetFont(\"ixMediumFont\")\n\n\tself.ourName = header:Add(\"DLabel\")\n\tself.ourName:Dock(RIGHT)\n\tself.ourName:SetWide(self:GetWide() * 0.5 - 7)\n\tself.ourName:SetText(L\"you\"..\" (\"..ix.currency.Get(LocalPlayer():GetCharacter():GetMoney())..\")\")\n\tself.ourName:SetTextInset(0, 0)\n\tself.ourName:SetTextColor(color_white)\n\tself.ourName:SetFont(\"ixMediumFont\")\n\n\tlocal footer = self:Add(\"DPanel\")\n\tfooter:SetTall(34)\n\tfooter:Dock(BOTTOM)\n\tfooter:SetPaintBackground(false)\n\n\tself.vendorSell = footer:Add(\"DButton\")\n\tself.vendorSell:SetFont(\"ixMediumFont\")\n\tself.vendorSell:SetWide(self.vendorName:GetWide())\n\tself.vendorSell:Dock(LEFT)\n\tself.vendorSell:SetContentAlignment(5)\n\t-- The text says purchase but the vendor is selling it to us.\n\tself.vendorSell:SetText(L\"purchase\")\n\tself.vendorSell:SetTextColor(color_white)\n\n\tself.vendorSell.DoClick = function(this)\n\t\tif (IsValid(self.activeSell)) then\n\t\t\tnet.Start(\"ixVendorTrade\")\n\t\t\t\tnet.WriteString(self.activeSell.item)\n\t\t\t\tnet.WriteBool(false)\n\t\t\tnet.SendToServer()\n\t\tend\n\tend\n\n\tself.vendorBuy = footer:Add(\"DButton\")\n\tself.vendorBuy:SetFont(\"ixMediumFont\")\n\tself.vendorBuy:SetWide(self.ourName:GetWide())\n\tself.vendorBuy:Dock(RIGHT)\n\tself.vendorBuy:SetContentAlignment(5)\n\tself.vendorBuy:SetText(L\"sell\")\n\tself.vendorBuy:SetTextColor(color_white)\n\tself.vendorBuy.DoClick = function(this)\n\t\tif (IsValid(self.activeBuy)) then\n\t\t\tnet.Start(\"ixVendorTrade\")\n\t\t\t\tnet.WriteString(self.activeBuy.item)\n\t\t\t\tnet.WriteBool(true)\n\t\t\tnet.SendToServer()\n\t\tend\n\tend\n\n\tself.selling = self:Add(\"DScrollPanel\")\n\tself.selling:SetWide(self:GetWide() * 0.5 - 7)\n\tself.selling:Dock(LEFT)\n\tself.selling:DockMargin(0, 4, 0, 4)\n\tself.selling:SetPaintBackground(true)\n\n\tself.sellingItems = self.selling:Add(\"DListLayout\")\n\tself.sellingItems:SetSize(self.selling:GetSize())\n\tself.sellingItems:DockPadding(0, 0, 0, 4)\n\tself.sellingItems:SetTall(ScrH())\n\n\tself.buying = self:Add(\"DScrollPanel\")\n\tself.buying:SetWide(self:GetWide() * 0.5 - 7)\n\tself.buying:Dock(RIGHT)\n\tself.buying:DockMargin(0, 4, 0, 4)\n\tself.buying:SetPaintBackground(true)\n\n\tself.buyingItems = self.buying:Add(\"DListLayout\")\n\tself.buyingItems:SetSize(self.buying:GetSize())\n\tself.buyingItems:DockPadding(0, 0, 0, 4)\n\n\tself.sellingList = {}\n\tself.buyingList = {}\nend\n\nfunction PANEL:addItem(uniqueID, listID)\n\tlocal entity = self.entity\n\tlocal items = entity.items\n\tlocal data = items[uniqueID]\n\n\tif ((!listID or listID == \"selling\") and !IsValid(self.sellingList[uniqueID])\n\tand ix.item.list[uniqueID]) then\n\t\tif (data and data[VENDOR_MODE] and data[VENDOR_MODE] != VENDOR_BUYONLY) then\n\t\t\tlocal item = self.sellingItems:Add(\"ixVendorItem\")\n\t\t\titem:Setup(uniqueID)\n\n\t\t\tself.sellingList[uniqueID] = item\n\t\t\tself.sellingItems:InvalidateLayout()\n\t\tend\n\tend\n\n\tif ((!listID or listID == \"buying\") and !IsValid(self.buyingList[uniqueID])\n\tand LocalPlayer():GetCharacter():GetInventory():HasItem(uniqueID)) then\n\t\tif (data and data[VENDOR_MODE] and data[VENDOR_MODE] != VENDOR_SELLONLY) then\n\t\t\tlocal item = self.buyingItems:Add(\"ixVendorItem\")\n\t\t\titem:Setup(uniqueID)\n\t\t\titem.isLocal = true\n\n\t\t\tself.buyingList[uniqueID] = item\n\t\t\tself.buyingItems:InvalidateLayout()\n\t\tend\n\tend\nend\n\nfunction PANEL:removeItem(uniqueID, listID)\n\tif (!listID or listID == \"selling\") then\n\t\tif (IsValid(self.sellingList[uniqueID])) then\n\t\t\tself.sellingList[uniqueID]:Remove()\n\t\t\tself.sellingItems:InvalidateLayout()\n\t\tend\n\tend\n\n\tif (!listID or listID == \"buying\") then\n\t\tif (IsValid(self.buyingList[uniqueID])) then\n\t\t\tself.buyingList[uniqueID]:Remove()\n\t\t\tself.buyingItems:InvalidateLayout()\n\t\tend\n\tend\nend\n\nfunction PANEL:Setup(entity)\n\tself.entity = entity\n\tself:SetTitle(entity:GetDisplayName())\n\tself.vendorName:SetText(entity:GetDisplayName()..(entity.money and \" (\"..entity.money..\")\" or \"\"))\n\n\tself.vendorBuy:SetEnabled(!self:GetReadOnly())\n\tself.vendorSell:SetEnabled(!self:GetReadOnly())\n\n\tfor k, _ in SortedPairs(entity.items) do\n\t\tself:addItem(k, \"selling\")\n\tend\n\n\tfor _, v in SortedPairs(LocalPlayer():GetCharacter():GetInventory():GetItems()) do\n\t\tself:addItem(v.uniqueID, \"buying\")\n\tend\nend\n\nfunction PANEL:OnRemove()\n\tnet.Start(\"ixVendorClose\")\n\tnet.SendToServer()\n\n\tif (IsValid(ix.gui.vendorEditor)) then\n\t\tix.gui.vendorEditor:Remove()\n\tend\nend\n\nfunction PANEL:Think()\n\tlocal entity = self.entity\n\n\tif (!IsValid(entity)) then\n\t\tself:Remove()\n\n\t\treturn\n\tend\n\n\tif ((self.nextUpdate or 0) < CurTime()) then\n\t\tself:SetTitle(self.entity:GetDisplayName())\n\t\tself.vendorName:SetText(entity:GetDisplayName()..(entity.money and \" (\"..ix.currency.Get(entity.money)..\")\" or \"\"))\n\t\tself.ourName:SetText(L\"you\"..\" (\"..ix.currency.Get(LocalPlayer():GetCharacter():GetMoney())..\")\")\n\n\t\tself.nextUpdate = CurTime() + 0.25\n\tend\nend\n\nfunction PANEL:OnItemSelected(panel)\n\tlocal price = self.entity:GetPrice(panel.item, panel.isLocal)\n\n\tif (panel.isLocal) then\n\t\tself.vendorBuy:SetText(L\"sell\"..\" (\"..ix.currency.Get(price)..\")\")\n\telse\n\t\tself.vendorSell:SetText(L\"purchase\"..\" (\"..ix.currency.Get(price)..\")\")\n\tend\nend\n\nvgui.Register(\"ixVendor\", PANEL, \"DFrame\")\n\nPANEL = {}\n\nfunction PANEL:Init()\n\tself:SetTall(36)\n\tself:DockMargin(4, 4, 4, 0)\n\n\tself.icon = self:Add(\"SpawnIcon\")\n\tself.icon:SetPos(2, 2)\n\tself.icon:SetSize(32, 32)\n\tself.icon:SetModel(\"models/error.mdl\")\n\n\tself.name = self:Add(\"DLabel\")\n\tself.name:Dock(FILL)\n\tself.name:DockMargin(42, 0, 0, 0)\n\tself.name:SetFont(\"ixChatFont\")\n\tself.name:SetTextColor(color_white)\n\tself.name:SetExpensiveShadow(1, Color(0, 0, 0, 200))\n\n\tself.click = self:Add(\"DButton\")\n\tself.click:Dock(FILL)\n\tself.click:SetText(\"\")\n\tself.click.Paint = function() end\n\tself.click.DoClick = function(this)\n\t\tif (self.isLocal) then\n\t\t\tix.gui.vendor.activeBuy = self\n\t\telse\n\t\t\tix.gui.vendor.activeSell = self\n\t\tend\n\n\t\tix.gui.vendor:OnItemSelected(self)\n\tend\nend\n\nfunction PANEL:SetCallback(callback)\n\tself.click.DoClick = function(this)\n\t\tcallback()\n\t\tself.selected = true\n\tend\nend\n\nfunction PANEL:Setup(uniqueID)\n\tlocal item = ix.item.list[uniqueID]\n\n\tif (item) then\n\t\tself.item = uniqueID\n\t\tself.icon:SetModel(item:GetModel(), item:GetSkin())\n\t\tself.name:SetText(item:GetName())\n\t\tself.itemName = item:GetName()\n\n\t\tself.click:SetHelixTooltip(function(tooltip)\n\t\t\tix.hud.PopulateItemTooltip(tooltip, item)\n\n\t\t\tlocal entity = ix.gui.vendor.entity\n\t\t\tif (entity and entity.items[self.item] and entity.items[self.item][VENDOR_MAXSTOCK]) then\n\t\t\t\tlocal info = entity.items[self.item]\n\t\t\t\tlocal stock = tooltip:AddRowAfter(\"name\", \"stock\")\n\t\t\t\tstock:SetText(string.format(\"Stock: %d/%d\", info[VENDOR_STOCK], info[VENDOR_MAXSTOCK]))\n\t\t\t\tstock:SetBackgroundColor(derma.GetColor(\"Info\", self))\n\t\t\t\tstock:SizeToContents()\n\t\t\tend\n\t\tend)\n\tend\nend\n\nfunction PANEL:Think()\n\tif ((self.nextUpdate or 0) < CurTime()) then\n\t\tlocal entity = ix.gui.vendor.entity\n\n\t\tif (entity and self.isLocal) then\n\t\t\tlocal count = LocalPlayer():GetCharacter():GetInventory():GetItemCount(self.item)\n\n\t\t\tif (count == 0) then\n\t\t\t\tself:Remove()\n\t\t\tend\n\t\tend\n\n\t\tself.nextUpdate = CurTime() + 0.1\n\tend\nend\n\nfunction PANEL:Paint(w, h)\n\tif (ix.gui.vendor.activeBuy == self or ix.gui.vendor.activeSell == self) then\n\t\tsurface.SetDrawColor(ix.config.Get(\"color\"))\n\telse\n\t\tsurface.SetDrawColor(0, 0, 0, 100)\n\tend\n\n\tsurface.DrawRect(0, 0, w, h)\nend\n\nvgui.Register(\"ixVendorItem\", PANEL, \"DPanel\")\n"
  },
  {
    "path": "plugins/vendor/derma/cl_vendoreditor.lua",
    "content": "\nlocal PANEL = {}\n\nfunction PANEL:Init()\n\tlocal entity = ix.gui.vendor.entity\n\n\tself:SetSize(320, 480)\n\tself:MoveLeftOf(ix.gui.vendor, 8)\n\tself:MakePopup()\n\tself:CenterVertical()\n\tself:SetTitle(L\"vendorEditor\")\n\tself.lblTitle:SetTextColor(color_white)\n\n\tself.name = self:Add(\"DTextEntry\")\n\tself.name:Dock(TOP)\n\tself.name:SetText(entity:GetDisplayName())\n\tself.name:SetPlaceholderText(L\"name\")\n\tself.name.OnEnter = function(this)\n\t\tif (entity:GetDisplayName() != this:GetText()) then\n\t\t\tself:updateVendor(\"name\", this:GetText())\n\t\tend\n\tend\n\n\tself.description = self:Add(\"DTextEntry\")\n\tself.description:Dock(TOP)\n\tself.description:DockMargin(0, 4, 0, 0)\n\tself.description:SetText(entity:GetDescription())\n\tself.description:SetPlaceholderText(L\"description\")\n\tself.description.OnEnter = function(this)\n\t\tif (entity:GetDescription() != this:GetText()) then\n\t\t\tself:updateVendor(\"description\", this:GetText())\n\t\tend\n\tend\n\n\tself.model = self:Add(\"DTextEntry\")\n\tself.model:Dock(TOP)\n\tself.model:DockMargin(0, 4, 0, 0)\n\tself.model:SetText(entity:GetModel())\n\tself.model:SetPlaceholderText(L\"model\")\n\tself.model.OnEnter = function(this)\n\t\tif (entity:GetModel():lower() != this:GetText():lower()) then\n\t\t\tself:updateVendor(\"model\", this:GetText():lower())\n\t\tend\n\tend\n\n\tlocal useMoney = tonumber(entity.money) != nil\n\n\tself.money = self:Add(\"DTextEntry\")\n\tself.money:Dock(TOP)\n\tself.money:DockMargin(0, 4, 0, 0)\n\tself.money:SetText(!useMoney and \"∞\" or entity.money)\n\tself.money:SetPlaceholderText(L\"money\")\n\tself.money:SetDisabled(!useMoney)\n\tself.money:SetEnabled(useMoney)\n\tself.money:SetNumeric(true)\n\tself.money.OnEnter = function(this)\n\t\tlocal value = tonumber(this:GetText()) or entity.money\n\n\t\tif (value == entity.money) then\n\t\t\treturn\n\t\tend\n\n\t\tself:updateVendor(\"money\", value)\n\tend\n\n\tself.bubble = self:Add(\"DCheckBoxLabel\")\n\tself.bubble:SetText(L\"vendorNoBubble\")\n\tself.bubble:Dock(TOP)\n\tself.bubble:DockMargin(0, 4, 0, 0)\n\tself.bubble:SetValue(entity:GetNoBubble() and 1 or 0)\n\tself.bubble.OnChange = function(this, value)\n\t\tif (this.noSend) then\n\t\t\tthis.noSend = nil\n\t\telse\n\t\t\tself:updateVendor(\"bubble\", value)\n\t\tend\n\tend\n\n\tself.useMoney = self:Add(\"DCheckBoxLabel\")\n\tself.useMoney:SetText(L\"vendorUseMoney\")\n\tself.useMoney:Dock(TOP)\n\tself.useMoney:DockMargin(0, 4, 0, 0)\n\tself.useMoney:SetChecked(useMoney)\n\tself.useMoney.OnChange = function(this, value)\n\t\tself:updateVendor(\"useMoney\")\n\tend\n\n\tself.sellScale = self:Add(\"DNumSlider\")\n\tself.sellScale:Dock(TOP)\n\tself.sellScale:DockMargin(0, 4, 0, 0)\n\tself.sellScale:SetText(L\"vendorSellScale\")\n\tself.sellScale.Label:SetTextColor(color_white)\n\tself.sellScale.TextArea:SetTextColor(color_white)\n\tself.sellScale:SetDecimals(1)\n\tself.sellScale.noSend = true\n\tself.sellScale:SetValue(entity.scale)\n\tself.sellScale.OnValueChanged = function(this, value)\n\t\tif (this.noSend) then\n\t\t\tthis.noSend = nil\n\t\telse\n\t\t\ttimer.Create(\"ixVendorScale\", 1, 1, function()\n\t\t\t\tif (IsValid(self) and IsValid(self.sellScale)) then\n\t\t\t\t\tvalue = self.sellScale:GetValue()\n\n\t\t\t\t\tif (value != entity.scale) then\n\t\t\t\t\t\tself:updateVendor(\"scale\", value)\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend)\n\t\tend\n\tend\n\n\tself.faction = self:Add(\"DButton\")\n\tself.faction:SetText(L\"vendorFaction\")\n\tself.faction:Dock(TOP)\n\tself.faction:SetTextColor(color_white)\n\tself.faction:DockMargin(0, 4, 0, 0)\n\tself.faction.DoClick = function(this)\n\t\tif (IsValid(ix.gui.editorFaction)) then\n\t\t\tix.gui.editorFaction:Remove()\n\t\tend\n\n\t\tix.gui.editorFaction = vgui.Create(\"ixVendorFactionEditor\")\n\t\tix.gui.editorFaction.updateVendor = self.updateVendor\n\t\tix.gui.editorFaction.entity = entity\n\t\tix.gui.editorFaction:Setup()\n\tend\n\n\tself.searchBar = self:Add(\"DTextEntry\")\n\tself.searchBar:Dock(TOP)\n\tself.searchBar:DockMargin(0, 4, 0, 0)\n\tself.searchBar:SetUpdateOnType(true)\n\tself.searchBar:SetPlaceholderText(\"Search...\")\n\tself.searchBar.OnValueChange = function(this, value)\n\t\tself:ReloadItemList(value)\n\tend\n\n\tlocal menu\n\n\tself.items = self:Add(\"DListView\")\n\tself.items:Dock(FILL)\n\tself.items:DockMargin(0, 4, 0, 0)\n\tself.items:AddColumn(L\"name\").Header:SetTextColor(color_black)\n\tself.items:AddColumn(L\"category\").Header:SetTextColor(color_black)\n\tself.items:AddColumn(L\"mode\").Header:SetTextColor(color_black)\n\tself.items:AddColumn(L\"price\").Header:SetTextColor(color_black)\n\tself.items:AddColumn(L\"stock\").Header:SetTextColor(color_black)\n\tself.items:SetMultiSelect(false)\n\tself.items.OnRowRightClick = function(this, index, line)\n\t\tif (IsValid(menu)) then\n\t\t\tmenu:Remove()\n\t\tend\n\n\t\tlocal uniqueID = line.item\n\n\t\tmenu = DermaMenu()\n\t\t\t-- Modes of the item.\n\t\t\tlocal mode, panel = menu:AddSubMenu(L\"mode\")\n\t\t\tpanel:SetImage(\"icon16/key.png\")\n\n\t\t\t-- Disable buying/selling of the item.\n\t\t\tmode:AddOption(L\"none\", function()\n\t\t\t\tself:updateVendor(\"mode\", {uniqueID, nil})\n\t\t\tend):SetImage(\"icon16/cog_error.png\")\n\n\t\t\t-- Allow the vendor to sell and buy this item.\n\t\t\tmode:AddOption(L\"vendorBoth\", function()\n\t\t\t\tself:updateVendor(\"mode\", {uniqueID, VENDOR_SELLANDBUY})\n\t\t\tend):SetImage(\"icon16/cog.png\")\n\n\t\t\t-- Only allow the vendor to buy this item from players.\n\t\t\tmode:AddOption(L\"vendorBuy\", function()\n\t\t\t\tself:updateVendor(\"mode\", {uniqueID, VENDOR_BUYONLY})\n\t\t\tend):SetImage(\"icon16/cog_delete.png\")\n\n\t\t\t-- Only allow the vendor to sell this item to players.\n\t\t\tmode:AddOption(L\"vendorSell\", function()\n\t\t\t\tself:updateVendor(\"mode\", {uniqueID, VENDOR_SELLONLY})\n\t\t\tend):SetImage(\"icon16/cog_add.png\")\n\n\t\t\tlocal itemTable = ix.item.list[uniqueID]\n\n\t\t\t-- Set the price of the item.\n\t\t\tmenu:AddOption(L\"price\", function()\n\t\t\t\tDerma_StringRequest(\n\t\t\t\t\titemTable.GetName and itemTable:GetName() or L(itemTable.name),\n\t\t\t\t\tL\"vendorPriceReq\",\n\t\t\t\t\tentity:GetPrice(uniqueID),\n\t\t\t\t\tfunction(text)\n\t\t\t\t\t\ttext = tonumber(text)\n\n\t\t\t\t\t\tif (text == itemTable.price) then\n\t\t\t\t\t\t\ttext = nil\n\t\t\t\t\t\tend\n\n\t\t\t\t\t\tself:updateVendor(\"price\", {uniqueID, text})\n\t\t\t\t\tend\n\t\t\t\t)\n\t\t\tend):SetImage(\"icon16/coins.png\")\n\n\t\t\t-- Set the stock of the item or disable it.\n\t\t\tlocal stock, menuPanel = menu:AddSubMenu(L\"stock\")\n\t\t\tmenuPanel:SetImage(\"icon16/table.png\")\n\n\t\t\t-- Disable the use of stocks for this item.\n\t\t\tstock:AddOption(L\"disable\", function()\n\t\t\t\tself:updateVendor(\"stockDisable\", uniqueID)\n\t\t\tend):SetImage(\"icon16/table_delete.png\")\n\n\t\t\t-- Edit the maximum stock for this item.\n\t\t\tstock:AddOption(L\"edit\", function()\n\t\t\t\tlocal _, max = entity:GetStock(uniqueID)\n\n\t\t\t\tDerma_StringRequest(\n\t\t\t\t\titemTable.GetName and itemTable:GetName() or L(itemTable.name),\n\t\t\t\t\tL\"vendorStockReq\",\n\t\t\t\t\tmax or 1,\n\t\t\t\t\tfunction(text)\n\t\t\t\t\t\tself:updateVendor(\"stockMax\", {uniqueID, text})\n\t\t\t\t\tend\n\t\t\t\t)\n\t\t\tend):SetImage(\"icon16/table_edit.png\")\n\n\t\t\t-- Edit the current stock of this item.\n\t\t\tstock:AddOption(L\"vendorEditCurStock\", function()\n\t\t\t\tDerma_StringRequest(\n\t\t\t\t\titemTable.GetName and itemTable:GetName() or L(itemTable.name),\n\t\t\t\t\tL\"vendorStockCurReq\",\n\t\t\t\t\tentity:GetStock(uniqueID) or 0,\n\t\t\t\t\tfunction(text)\n\t\t\t\t\t\tself:updateVendor(\"stock\", {uniqueID, text})\n\t\t\t\t\tend\n\t\t\t\t)\n\t\t\tend):SetImage(\"icon16/table_edit.png\")\n\t\tmenu:Open()\n\tend\n\n\tself:ReloadItemList()\nend\n\nfunction PANEL:ReloadItemList(filter)\n\tlocal entity = ix.gui.vendor.entity\n\tself.lines = {}\n\n\tself.items:Clear()\n\n\tfor k, v in SortedPairs(ix.item.list) do\n\t\tlocal itemName = v.GetName and v:GetName() or L(v.name)\n\n\t\tif (filter and !itemName:lower():find(filter:lower(), 1, false)) then\n\t\t\tcontinue\n\t\tend\n\n\t\tlocal mode = entity.items[k] and entity.items[k][VENDOR_MODE]\n\t\tlocal current, max = entity:GetStock(k)\n\t\tlocal panel = self.items:AddLine(\n\t\t\titemName,\n\t\t\tv.category or L\"none\",\n\t\t\tmode and L(VENDOR_TEXT[mode]) or L\"none\",\n\t\t\tentity:GetPrice(k),\n\t\t\tmax and current..\"/\"..max or \"-\"\n\t\t)\n\n\t\tpanel.item = k\n\t\tself.lines[k] = panel\n\tend\nend\n\nfunction PANEL:OnRemove()\n\tif (IsValid(ix.gui.vendor)) then\n\t\tix.gui.vendor:Remove()\n\tend\n\n\tif (IsValid(ix.gui.editorFaction)) then\n\t\tix.gui.editorFaction:Remove()\n\tend\nend\n\nfunction PANEL:updateVendor(key, value)\n\tnet.Start(\"ixVendorEdit\")\n\t\tnet.WriteString(key)\n\t\tnet.WriteType(value)\n\tnet.SendToServer()\nend\n\nvgui.Register(\"ixVendorEditor\", PANEL, \"DFrame\")\n"
  },
  {
    "path": "plugins/vendor/derma/cl_vendorfaction.lua",
    "content": "\nlocal PANEL = {}\n\nfunction PANEL:Init()\n\tself:SetSize(256, 280)\n\tself:Center()\n\tself:MakePopup()\n\tself:SetTitle(L\"vendorFaction\")\n\tself.scroll = self:Add(\"DScrollPanel\")\n\tself.scroll:Dock(FILL)\n\tself.scroll:DockPadding(0, 0, 0, 4)\n\n\tself.factions = {}\n\tself.classes = {}\n\n\tfor k, v in ipairs(ix.faction.indices) do\n\t\tlocal panel = self.scroll:Add(\"DPanel\")\n\t\tpanel:Dock(TOP)\n\t\tpanel:DockPadding(4, 4, 4, 4)\n\t\tpanel:DockMargin(0, 0, 0, 4)\n\n\t\tlocal faction = panel:Add(\"DCheckBoxLabel\")\n\t\tfaction:Dock(TOP)\n\t\tfaction:SetText(L(v.name))\n\t\tfaction:DockMargin(0, 0, 0, 4)\n\t\tfaction.OnChange = function(this, state)\n\t\t\tself:updateVendor(\"faction\", v.uniqueID)\n\t\tend\n\n\t\tself.factions[v.uniqueID] = faction\n\n\t\tfor _, v2 in ipairs(ix.class.list) do\n\t\t\tif (v2.faction == k) then\n\t\t\t\tlocal class = panel:Add(\"DCheckBoxLabel\")\n\t\t\t\tclass:Dock(TOP)\n\t\t\t\tclass:DockMargin(16, 0, 0, 4)\n\t\t\t\tclass:SetText(L(v2.name))\n\t\t\t\tclass.OnChange = function(this, state)\n\t\t\t\t\tself:updateVendor(\"class\", v2.uniqueID)\n\t\t\t\tend\n\n\t\t\t\tself.classes[v2.uniqueID] = class\n\n\t\t\t\tpanel:SetTall(panel:GetTall() + class:GetTall() + 4)\n\t\t\tend\n\t\tend\n\tend\nend\n\nfunction PANEL:Setup()\n\tfor k, _ in pairs(self.entity.factions or {}) do\n\t\tself.factions[k]:SetChecked(true)\n\tend\n\n\tfor k, _ in pairs(self.entity.classes or {}) do\n\t\tself.classes[k]:SetChecked(true)\n\tend\nend\n\nvgui.Register(\"ixVendorFactionEditor\", PANEL, \"DFrame\")\n"
  },
  {
    "path": "plugins/vendor/entities/entities/ix_vendor.lua",
    "content": "\nENT.Type = \"anim\"\nENT.PrintName = \"Vendor\"\nENT.Category = \"Helix\"\nENT.Spawnable = true\nENT.AdminOnly = true\nENT.isVendor = true\nENT.bNoPersist = true\n\nfunction ENT:SetupDataTables()\n\tself:NetworkVar(\"Bool\", 0, \"NoBubble\")\n\tself:NetworkVar(\"String\", 0, \"DisplayName\")\n\tself:NetworkVar(\"String\", 1, \"Description\")\nend\n\nfunction ENT:Initialize()\n\tif (SERVER) then\n\t\tself:SetModel(\"models/mossman.mdl\")\n\t\tself:SetUseType(SIMPLE_USE)\n\t\tself:SetMoveType(MOVETYPE_NONE)\n\t\tself:DrawShadow(true)\n\t\tself:InitPhysObj()\n\n\t\tself:AddCallback(\"OnAngleChange\", function(entity)\n\t\t\tlocal mins, maxs = entity:GetAxisAlignedBoundingBox()\n\n\t\t\tentity:SetCollisionBounds(mins, maxs)\n\t\tend)\n\n\t\tself.items = {}\n\t\tself.messages = {}\n\t\tself.factions = {}\n\t\tself.classes = {}\n\n\t\tself:SetDisplayName(\"John Doe\")\n\t\tself:SetDescription(\"\")\n\n\t\tself.receivers = {}\n\tend\n\n\ttimer.Simple(1, function()\n\t\tif (IsValid(self)) then\n\t\t\tself:SetAnim()\n\t\tend\n\tend)\nend\n\nfunction ENT:InitPhysObj()\n\tlocal mins, maxs = self:GetAxisAlignedBoundingBox()\n\tlocal bPhysObjCreated = self:PhysicsInitBox(mins, maxs)\n\n\tif (bPhysObjCreated) then\n\t\tlocal physObj = self:GetPhysicsObject()\n\t\tphysObj:EnableMotion(false)\n\t\tphysObj:Sleep()\n\tend\nend\n\nfunction ENT:GetAxisAlignedBoundingBox()\n\tlocal mins, maxs = self:GetModelBounds()\n\tmins = Vector(mins.x, mins.y, 0)\n\tmins, maxs = self:GetRotatedAABB(mins, maxs)\n\n\treturn mins, maxs\nend\n\nfunction ENT:CanAccess(client)\n\tlocal bAccess = false\n\tlocal uniqueID = ix.faction.indices[client:Team()].uniqueID\n\n\tif (self.factions and !table.IsEmpty(self.factions)) then\n\t\tif (self.factions[uniqueID]) then\n\t\t\tbAccess = true\n\t\telse\n\t\t\treturn false\n\t\tend\n\tend\n\n\tif (bAccess and self.classes and !table.IsEmpty(self.classes)) then\n\t\tlocal class = ix.class.list[client:GetCharacter():GetClass()]\n\t\tlocal classID = class and class.uniqueID\n\n\t\tif (classID and !self.classes[classID]) then\n\t\t\treturn false\n\t\tend\n\tend\n\n\treturn true\nend\n\nfunction ENT:GetStock(uniqueID)\n\tif (self.items[uniqueID] and self.items[uniqueID][VENDOR_MAXSTOCK]) then\n\t\treturn self.items[uniqueID][VENDOR_STOCK] or 0, self.items[uniqueID][VENDOR_MAXSTOCK]\n\tend\nend\n\nfunction ENT:GetPrice(uniqueID, selling)\n\tlocal price = ix.item.list[uniqueID] and self.items[uniqueID] and\n\t\tself.items[uniqueID][VENDOR_PRICE] or ix.item.list[uniqueID].price or 0\n\n\tif (selling) then\n\t\tprice = math.floor(price * (self.scale or 0.5))\n\tend\n\n\treturn price\nend\n\nfunction ENT:CanSellToPlayer(client, uniqueID)\n\tlocal data = self.items[uniqueID]\n\n\tif (!data or !client:GetCharacter() or !ix.item.list[uniqueID]) then\n\t\treturn false\n\tend\n\n\tif (data[VENDOR_MODE] == VENDOR_BUYONLY) then\n\t\treturn false\n\tend\n\n\tif (!client:GetCharacter():HasMoney(self:GetPrice(uniqueID))) then\n\t\treturn false\n\tend\n\n\tif (data[VENDOR_STOCK] and data[VENDOR_STOCK] < 1) then\n\t\treturn false\n\tend\n\n\treturn true\nend\n\nfunction ENT:CanBuyFromPlayer(client, uniqueID)\n\tlocal data = self.items[uniqueID]\n\n\tif (!data or !client:GetCharacter() or !ix.item.list[uniqueID]) then\n\t\treturn false\n\tend\n\n\tif (data[VENDOR_MODE] != VENDOR_SELLONLY) then\n\t\treturn false\n\tend\n\n\tif (!self:HasMoney(data[VENDOR_PRICE] or ix.item.list[uniqueID].price or 0)) then\n\t\treturn false\n\tend\n\n\treturn true\nend\n\nfunction ENT:HasMoney(amount)\n\t-- Vendor not using money system so they can always afford it.\n\tif (!self.money) then\n\t\treturn true\n\tend\n\n\treturn self.money >= amount\nend\n\nfunction ENT:SetAnim()\n\tfor k, v in ipairs(self:GetSequenceList()) do\n\t\tif (v:lower():find(\"idle\") and v != \"idlenoise\") then\n\t\t\treturn self:ResetSequence(k)\n\t\tend\n\tend\n\n\tif (self:GetSequenceCount() > 1) then\n\t\tself:ResetSequence(4)\n\tend\nend\n\nif (SERVER) then\n\tlocal PLUGIN = PLUGIN\n\n\tfunction ENT:SpawnFunction(client, trace)\n\t\tlocal angles = (trace.HitPos - client:GetPos()):Angle()\n\t\tangles.r = 0\n\t\tangles.p = 0\n\t\tangles.y = angles.y + 180\n\n\t\tlocal entity = ents.Create(\"ix_vendor\")\n\t\tentity:SetPos(trace.HitPos)\n\t\tentity:SetAngles(angles)\n\t\tentity:Spawn()\n\n\t\tPLUGIN:SaveData()\n\n\t\treturn entity\n\tend\n\n\tfunction ENT:Use(activator)\n\t\tlocal character = activator:GetCharacter()\n\n\t\tif (!self:CanAccess(activator) or hook.Run(\"CanPlayerUseVendor\", activator, self) == false) then\n\t\t\tif (self.messages[VENDOR_NOTRADE]) then\n\t\t\t\tactivator:ChatPrint(self:GetDisplayName()..\": \"..self.messages[VENDOR_NOTRADE])\n\t\t\telse\n\t\t\t\tactivator:NotifyLocalized(\"vendorNoTrade\")\n\t\t\tend\n\n\t\t\treturn\n\t\tend\n\n\t\tself.receivers[#self.receivers + 1] = activator\n\n\t\tif (self.messages[VENDOR_WELCOME]) then\n\t\t\tactivator:ChatPrint(self:GetDisplayName()..\": \"..self.messages[VENDOR_WELCOME])\n\t\tend\n\n\t\tlocal items = {}\n\n\t\t-- Only send what is needed.\n\t\tfor k, v in pairs(self.items) do\n\t\t\tif (!table.IsEmpty(v) and (CAMI.PlayerHasAccess(activator, \"Helix - Manage Vendors\", nil) or v[VENDOR_MODE])) then\n\t\t\t\titems[k] = v\n\t\t\tend\n\t\tend\n\n\t\tself.scale = self.scale or 0.5\n\n\t\tactivator.ixVendor = self\n\n\t\t-- force sync to prevent outdated inventories while buying/selling\n\t\tif (character) then\n\t\t\tcharacter:GetInventory():Sync(activator, true)\n\t\tend\n\n\t\tnet.Start(\"ixVendorOpen\")\n\t\t\tnet.WriteEntity(self)\n\t\t\tnet.WriteUInt(self.money or 0, 16)\n\t\t\tnet.WriteTable(items)\n\t\t\tnet.WriteFloat(self.scale or 0.5)\n\t\tnet.Send(activator)\n\n\t\tix.log.Add(activator, \"vendorUse\", self:GetDisplayName())\n\tend\n\n\tfunction ENT:SetMoney(value)\n\t\tself.money = value\n\n\t\tnet.Start(\"ixVendorMoney\")\n\t\t\tnet.WriteUInt(value and value or -1, 16)\n\t\tnet.Send(self.receivers)\n\tend\n\n\tfunction ENT:GiveMoney(value)\n\t\tif (self.money) then\n\t\t\tself:SetMoney(self:GetMoney() + value)\n\t\tend\n\tend\n\n\tfunction ENT:TakeMoney(value)\n\t\tif (self.money) then\n\t\t\tself:GiveMoney(-value)\n\t\tend\n\tend\n\n\tfunction ENT:SetStock(uniqueID, value)\n\t\tif (!self.items[uniqueID][VENDOR_MAXSTOCK]) then\n\t\t\treturn\n\t\tend\n\n\t\tself.items[uniqueID] = self.items[uniqueID] or {}\n\t\tself.items[uniqueID][VENDOR_STOCK] = math.min(value, self.items[uniqueID][VENDOR_MAXSTOCK])\n\n\t\tnet.Start(\"ixVendorStock\")\n\t\t\tnet.WriteString(uniqueID)\n\t\t\tnet.WriteUInt(value, 16)\n\t\tnet.Send(self.receivers)\n\tend\n\n\tfunction ENT:AddStock(uniqueID, value)\n\t\tif (!self.items[uniqueID][VENDOR_MAXSTOCK]) then\n\t\t\treturn\n\t\tend\n\n\t\tself:SetStock(uniqueID, self:GetStock(uniqueID) + (value or 1))\n\tend\n\n\tfunction ENT:TakeStock(uniqueID, value)\n\t\tif (!self.items[uniqueID][VENDOR_MAXSTOCK]) then\n\t\t\treturn\n\t\tend\n\n\t\tself:AddStock(uniqueID, -(value or 1))\n\tend\nelse\n\tfunction ENT:CreateBubble()\n\t\tself.bubble = ClientsideModel(\"models/extras/info_speech.mdl\", RENDERGROUP_OPAQUE)\n\t\tself.bubble:SetPos(self:GetPos() + Vector(0, 0, 84))\n\t\tself.bubble:SetModelScale(0.6, 0)\n\tend\n\n\tfunction ENT:Draw()\n\t\tlocal bubble = self.bubble\n\n\t\tif (IsValid(bubble)) then\n\t\t\tlocal realTime = RealTime()\n\n\t\t\tbubble:SetRenderOrigin(self:GetPos() + Vector(0, 0, 84 + math.sin(realTime * 3) * 0.05))\n\t\t\tbubble:SetRenderAngles(Angle(0, realTime * 100, 0))\n\t\tend\n\n\t\tself:DrawModel()\n\tend\n\n\tfunction ENT:Think()\n\t\tlocal noBubble = self:GetNoBubble()\n\n\t\tif (IsValid(self.bubble) and noBubble) then\n\t\t\tself.bubble:Remove()\n\t\telseif (!IsValid(self.bubble) and !noBubble) then\n\t\t\tself:CreateBubble()\n\t\tend\n\n\t\tif ((self.nextAnimCheck or 0) < CurTime()) then\n\t\t\tself:SetAnim()\n\t\t\tself.nextAnimCheck = CurTime() + 60\n\t\tend\n\n\t\tself:SetNextClientThink(CurTime() + 0.25)\n\n\t\treturn true\n\tend\n\n\tfunction ENT:OnRemove()\n\t\tif (IsValid(self.bubble)) then\n\t\t\tself.bubble:Remove()\n\t\tend\n\tend\n\n\tENT.PopulateEntityInfo = true\n\n\tfunction ENT:OnPopulateEntityInfo(container)\n\t\tlocal name = container:AddRow(\"name\")\n\t\tname:SetImportant()\n\t\tname:SetText(self:GetDisplayName())\n\t\tname:SizeToContents()\n\n\t\tlocal descriptionText = self:GetDescription()\n\n\t\tif (descriptionText != \"\") then\n\t\t\tlocal description = container:AddRow(\"description\")\n\t\t\tdescription:SetText(self:GetDescription())\n\t\t\tdescription:SizeToContents()\n\t\tend\n\tend\nend\n\nfunction ENT:GetMoney()\n\treturn self.money\nend\n"
  },
  {
    "path": "plugins/vendor/sh_plugin.lua",
    "content": "\n-- luacheck: globals VENDOR_BUY VENDOR_SELL VENDOR_BOTH VENDOR_WELCOME VENDOR_LEAVE VENDOR_NOTRADE VENDOR_PRICE\n-- luacheck: globals VENDOR_STOCK VENDOR_MODE VENDOR_MAXSTOCK VENDOR_SELLANDBUY VENDOR_SELLONLY VENDOR_BUYONLY VENDOR_TEXT\n\nlocal PLUGIN = PLUGIN\n\nPLUGIN.name = \"Vendors\"\nPLUGIN.author = \"Chessnut\"\nPLUGIN.description = \"Adds NPC vendors that can sell things.\"\n\nCAMI.RegisterPrivilege({\n\tName = \"Helix - Manage Vendors\",\n\tMinAccess = \"admin\"\n})\n\nVENDOR_BUY = 1\nVENDOR_SELL = 2\nVENDOR_BOTH = 3\n\n-- Keys for vendor messages.\nVENDOR_WELCOME = 1\nVENDOR_LEAVE = 2\nVENDOR_NOTRADE = 3\n\n-- Keys for item information.\nVENDOR_PRICE = 1\nVENDOR_STOCK = 2\nVENDOR_MODE = 3\nVENDOR_MAXSTOCK = 4\n\n-- Sell and buy the item.\nVENDOR_SELLANDBUY = 1\n-- Only sell the item to the player.\nVENDOR_SELLONLY = 2\n-- Only buy the item from the player.\nVENDOR_BUYONLY = 3\n\nif (SERVER) then\n\tutil.AddNetworkString(\"ixVendorOpen\")\n\tutil.AddNetworkString(\"ixVendorClose\")\n\tutil.AddNetworkString(\"ixVendorTrade\")\n\n\tutil.AddNetworkString(\"ixVendorEdit\")\n\tutil.AddNetworkString(\"ixVendorEditFinish\")\n\tutil.AddNetworkString(\"ixVendorEditor\")\n\tutil.AddNetworkString(\"ixVendorMoney\")\n\tutil.AddNetworkString(\"ixVendorStock\")\n\tutil.AddNetworkString(\"ixVendorAddItem\")\n\n\tfunction PLUGIN:SaveData()\n\t\tlocal data = {}\n\n\t\tfor _, entity in ipairs(ents.FindByClass(\"ix_vendor\")) do\n\t\t\tlocal bodygroups = {}\n\n\t\t\tfor _, v in ipairs(entity:GetBodyGroups() or {}) do\n\t\t\t\tbodygroups[v.id] = entity:GetBodygroup(v.id)\n\t\t\tend\n\n\t\t\tdata[#data + 1] = {\n\t\t\t\tname = entity:GetDisplayName(),\n\t\t\t\tdescription = entity:GetDescription(),\n\t\t\t\tpos = entity:GetPos(),\n\t\t\t\tangles = entity:GetAngles(),\n\t\t\t\tmodel = entity:GetModel(),\n\t\t\t\tskin = entity:GetSkin(),\n\t\t\t\tbodygroups = bodygroups,\n\t\t\t\tbubble = entity:GetNoBubble(),\n\t\t\t\titems = entity.items,\n\t\t\t\tfactions = entity.factions,\n\t\t\t\tclasses = entity.classes,\n\t\t\t\tmoney = entity.money,\n\t\t\t\tscale = entity.scale\n\t\t\t}\n\t\tend\n\n\t\tself:SetData(data)\n\tend\n\n\tfunction PLUGIN:LoadData()\n\t\tfor _, v in ipairs(self:GetData() or {}) do\n\t\t\tlocal entity = ents.Create(\"ix_vendor\")\n\t\t\tentity:SetPos(v.pos)\n\t\t\tentity:SetAngles(v.angles)\n\t\t\tentity:Spawn()\n\n\t\t\tentity:SetModel(v.model)\n\t\t\tentity:SetSkin(v.skin or 0)\n\t\t\tentity:InitPhysObj()\n\n\t\t\tentity:SetNoBubble(v.bubble)\n\t\t\tentity:SetDisplayName(v.name)\n\t\t\tentity:SetDescription(v.description)\n\n\t\t\tfor id, bodygroup in pairs(v.bodygroups or {}) do\n\t\t\t\tentity:SetBodygroup(id, bodygroup)\n\t\t\tend\n\n\t\t\tlocal items = {}\n\n\t\t\tfor uniqueID, data in pairs(v.items) do\n\t\t\t\titems[tostring(uniqueID)] = data\n\t\t\tend\n\n\t\t\tentity.items = items\n\t\t\tentity.factions = v.factions or {}\n\t\t\tentity.classes = v.classes or {}\n\t\t\tentity.money = v.money\n\t\t\tentity.scale = v.scale or 0.5\n\t\tend\n\tend\n\n\tfunction PLUGIN:CanVendorSellItem(client, vendor, itemID)\n\t\tlocal tradeData = vendor.items[itemID]\n\t\tlocal char = client:GetCharacter()\n\n\t\tif (!tradeData or !char) then\n\t\t\treturn false\n\t\tend\n\n\t\tif (!char:HasMoney(tradeData[1] or 0)) then\n\t\t\treturn false\n\t\tend\n\n\t\treturn true\n\tend\n\n\tix.log.AddType(\"vendorUse\", function(client, ...)\n\t\tlocal arg = {...}\n\t\treturn string.format(\"%s used the '%s' vendor.\", client:Name(), arg[1])\n\tend)\n\n\tix.log.AddType(\"vendorBuy\", function(client, ...)\n\t\tlocal arg = {...}\n\n\t\treturn string.format(\"%s purchased a '%s' from the '%s' vendor for %s.\", client:Name(), arg[1], arg[2], arg[3])\n\tend)\n\n\tix.log.AddType(\"vendorSell\", function(client, ...)\n\t\tlocal arg = {...}\n\n\t\treturn string.format(\"%s sold a '%s' to the '%s' vendor for %s.\", client:Name(), arg[1], arg[2], arg[3])\n\tend)\n\n\tnet.Receive(\"ixVendorClose\", function(length, client)\n\t\tlocal entity = client.ixVendor\n\n\t\tif (IsValid(entity)) then\n\t\t\tfor k, v in ipairs(entity.receivers) do\n\t\t\t\tif (v == client) then\n\t\t\t\t\ttable.remove(entity.receivers, k)\n\n\t\t\t\t\tbreak\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tclient.ixVendor = nil\n\t\tend\n\tend)\n\n\tlocal function UpdateEditReceivers(receivers, key, value)\n\t\tnet.Start(\"ixVendorEdit\")\n\t\t\tnet.WriteString(key)\n\t\t\tnet.WriteType(value)\n\t\tnet.Send(receivers)\n\tend\n\n\tnet.Receive(\"ixVendorEdit\", function(length, client)\n\t\tif (!CAMI.PlayerHasAccess(client, \"Helix - Manage Vendors\", nil)) then\n\t\t\treturn\n\t\tend\n\n\t\tlocal entity = client.ixVendor\n\n\t\tif (!IsValid(entity)) then\n\t\t\treturn\n\t\tend\n\n\t\tlocal key = net.ReadString()\n\t\tlocal data = net.ReadType()\n\t\tlocal feedback = true\n\n\t\tif (key == \"name\") then\n\t\t\tentity:SetDisplayName(data)\n\t\telseif (key == \"description\") then\n\t\t\tentity:SetDescription(data)\n\t\telseif (key == \"bubble\") then\n\t\t\tentity:SetNoBubble(data)\n\t\telseif (key == \"mode\") then\n\t\t\tlocal uniqueID = data[1]\n\n\t\t\tentity.items[uniqueID] = entity.items[uniqueID] or {}\n\t\t\tentity.items[uniqueID][VENDOR_MODE] = data[2]\n\n\t\t\tUpdateEditReceivers(entity.receivers, key, data)\n\t\telseif (key == \"price\") then\n\t\t\tlocal uniqueID = data[1]\n\t\t\tdata[2] = tonumber(data[2])\n\n\t\t\tif (data[2]) then\n\t\t\t\tdata[2] = math.Round(data[2])\n\t\t\tend\n\n\t\t\tentity.items[uniqueID] = entity.items[uniqueID] or {}\n\t\t\tentity.items[uniqueID][VENDOR_PRICE] = data[2]\n\n\t\t\tUpdateEditReceivers(entity.receivers, key, data)\n\n\t\t\tdata = uniqueID\n\t\telseif (key == \"stockDisable\") then\n\t\t\tlocal uniqueID = data[1]\n\n\t\t\tentity.items[data] = entity.items[uniqueID] or {}\n\t\t\tentity.items[data][VENDOR_MAXSTOCK] = nil\n\n\t\t\tUpdateEditReceivers(entity.receivers, key, data)\n\t\telseif (key == \"stockMax\") then\n\t\t\tlocal uniqueID = data[1]\n\t\t\tdata[2] = math.max(math.Round(tonumber(data[2]) or 1), 1)\n\n\t\t\tentity.items[uniqueID] = entity.items[uniqueID] or {}\n\t\t\tentity.items[uniqueID][VENDOR_MAXSTOCK] = data[2]\n\t\t\tentity.items[uniqueID][VENDOR_STOCK] = math.Clamp(entity.items[uniqueID][VENDOR_STOCK] or data[2], 1, data[2])\n\n\t\t\tdata[3] = entity.items[uniqueID][VENDOR_STOCK]\n\n\t\t\tUpdateEditReceivers(entity.receivers, key, data)\n\n\t\t\tdata = uniqueID\n\t\telseif (key == \"stock\") then\n\t\t\tlocal uniqueID = data[1]\n\n\t\t\tentity.items[uniqueID] = entity.items[uniqueID] or {}\n\n\t\t\tif (!entity.items[uniqueID][VENDOR_MAXSTOCK]) then\n\t\t\t\tdata[2] = math.max(math.Round(tonumber(data[2]) or 0), 0)\n\t\t\t\tentity.items[uniqueID][VENDOR_MAXSTOCK] = data[2]\n\t\t\tend\n\n\t\t\tdata[2] = math.Clamp(math.Round(tonumber(data[2]) or 0), 0, entity.items[uniqueID][VENDOR_MAXSTOCK])\n\t\t\tentity.items[uniqueID][VENDOR_STOCK] = data[2]\n\n\t\t\tUpdateEditReceivers(entity.receivers, key, data)\n\n\t\t\tdata = uniqueID\n\t\telseif (key == \"faction\") then\n\t\t\tlocal faction = ix.faction.teams[data]\n\n\t\t\tif (faction) then\n\t\t\t\tentity.factions[data] = !entity.factions[data]\n\n\t\t\t\tif (!entity.factions[data]) then\n\t\t\t\t\tentity.factions[data] = nil\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tlocal uniqueID = data\n\t\t\tdata = {uniqueID, entity.factions[uniqueID]}\n\t\telseif (key == \"class\") then\n\t\t\tlocal class\n\n\t\t\tfor _, v in ipairs(ix.class.list) do\n\t\t\t\tif (v.uniqueID == data) then\n\t\t\t\t\tclass = v\n\n\t\t\t\t\tbreak\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tif (class) then\n\t\t\t\tentity.classes[data] = !entity.classes[data]\n\n\t\t\t\tif (!entity.classes[data]) then\n\t\t\t\t\tentity.classes[data] = nil\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tlocal uniqueID = data\n\t\t\tdata = {uniqueID, entity.classes[uniqueID]}\n\t\telseif (key == \"model\") then\n\t\t\tentity:SetModel(data)\n\t\t\tentity:InitPhysObj()\n\t\t\tentity:SetAnim()\n\t\telseif (key == \"useMoney\") then\n\t\t\tif (entity.money) then\n\t\t\t\tentity:SetMoney()\n\t\t\telse\n\t\t\t\tentity:SetMoney(0)\n\t\t\tend\n\t\telseif (key == \"money\") then\n\t\t\tdata = math.Round(math.abs(tonumber(data) or 0))\n\n\t\t\tentity:SetMoney(data)\n\t\t\tfeedback = false\n\t\telseif (key == \"scale\") then\n\t\t\tdata = tonumber(data) or 0.5\n\n\t\t\tentity.scale = data\n\n\t\t\tUpdateEditReceivers(entity.receivers, key, data)\n\t\tend\n\n\t\tPLUGIN:SaveData()\n\n\t\tif (feedback) then\n\t\t\tlocal receivers = {}\n\n\t\t\tfor _, v in ipairs(entity.receivers) do\n\t\t\t\tif (CAMI.PlayerHasAccess(v, \"Helix - Manage Vendors\", nil)) then\n\t\t\t\t\treceivers[#receivers + 1] = v\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tnet.Start(\"ixVendorEditFinish\")\n\t\t\t\tnet.WriteString(key)\n\t\t\t\tnet.WriteType(data)\n\t\t\tnet.Send(receivers)\n\t\tend\n\tend)\n\n\tnet.Receive(\"ixVendorTrade\", function(length, client)\n\t\tif ((client.ixVendorTry or 0) < CurTime()) then\n\t\t\tclient.ixVendorTry = CurTime() + 0.33\n\t\telse\n\t\t\treturn\n\t\tend\n\n\t\tlocal entity = client.ixVendor\n\n\t\tif (!IsValid(entity) or client:GetPos():Distance(entity:GetPos()) > 192) then\n\t\t\treturn\n\t\tend\n\n\t\tif (!entity:CanAccess(client)) then\n\t\t\treturn\n\t\tend\n\n\t\tlocal uniqueID = net.ReadString()\n\t\tlocal isSellingToVendor = net.ReadBool()\n\n\t\tif (entity.items[uniqueID] and\n\t\t\thook.Run(\"CanPlayerTradeWithVendor\", client, entity, uniqueID, isSellingToVendor) != false) then\n\t\t\tlocal price = entity:GetPrice(uniqueID, isSellingToVendor)\n\n\t\t\tif (isSellingToVendor) then\n\t\t\t\tlocal found = false\n\t\t\t\tlocal name\n\n\t\t\t\tif (!entity:HasMoney(price)) then\n\t\t\t\t\treturn client:NotifyLocalized(\"vendorNoMoney\")\n\t\t\t\tend\n\n\t\t\t\tlocal stock, max = entity:GetStock(uniqueID)\n\n\t\t\t\tif (stock and stock >= max) then\n\t\t\t\t\treturn client:NotifyLocalized(\"vendorMaxStock\")\n\t\t\t\tend\n\n\t\t\t\tlocal invOkay = true\n\n\t\t\t\tfor k, _ in client:GetCharacter():GetInventory():Iter() do\n\t\t\t\t\tif (k.uniqueID == uniqueID and k:GetID() != 0 and ix.item.instances[k:GetID()] and k:GetData(\"equip\", false) == false) then\n\t\t\t\t\t\tinvOkay = k:Remove()\n\t\t\t\t\t\tfound = true\n\t\t\t\t\t\tname = L(k.name, client)\n\n\t\t\t\t\t\tbreak\n\t\t\t\t\tend\n\t\t\t\tend\n\n\t\t\t\tif (!found) then\n\t\t\t\t\treturn\n\t\t\t\tend\n\n\t\t\t\tif (!invOkay) then\n\t\t\t\t\tclient:GetCharacter():GetInventory():Sync(client, true)\n\t\t\t\t\treturn client:NotifyLocalized(\"tellAdmin\", \"trd!iid\")\n\t\t\t\tend\n\n\t\t\t\tclient:GetCharacter():GiveMoney(price, price == 0)\n\t\t\t\tclient:NotifyLocalized(\"businessSell\", name, ix.currency.Get(price))\n\t\t\t\tentity:TakeMoney(price)\n\t\t\t\tentity:AddStock(uniqueID)\n\n\t\t\t\tix.log.Add(client, \"vendorSell\", name, entity:GetDisplayName(), ix.currency.Get(price))\n\t\t\telse\n\t\t\t\tlocal stock = entity:GetStock(uniqueID)\n\n\t\t\t\tif (stock and stock < 1) then\n\t\t\t\t\treturn client:NotifyLocalized(\"vendorNoStock\")\n\t\t\t\tend\n\n\t\t\t\tif (!client:GetCharacter():HasMoney(price)) then\n\t\t\t\t\treturn client:NotifyLocalized(\"canNotAfford\")\n\t\t\t\tend\n\n\t\t\t\tif !entity:CanSellToPlayer(client, uniqueID) then\n\t\t\t\t\treturn false\n\t\t\t\tend\n\n\t\t\t\tlocal name = L(ix.item.list[uniqueID].name, client)\n\n\t\t\t\tclient:GetCharacter():TakeMoney(price, price == 0)\n\t\t\t\tclient:NotifyLocalized(\"businessPurchase\", name, ix.currency.Get(price))\n\n\t\t\t\tentity:GiveMoney(price)\n\n\t\t\t\tif (!client:GetCharacter():GetInventory():Add(uniqueID)) then\n\t\t\t\t\tix.item.Spawn(uniqueID, client)\n\t\t\t\telse\n\t\t\t\t\tnet.Start(\"ixVendorAddItem\")\n\t\t\t\t\t\tnet.WriteString(uniqueID)\n\t\t\t\t\tnet.Send(client)\n\t\t\t\tend\n\n\t\t\t\tentity:TakeStock(uniqueID)\n\n\t\t\t\tix.log.Add(client, \"vendorBuy\", name, entity:GetDisplayName(), ix.currency.Get(price))\n\t\t\tend\n\n\t\t\tPLUGIN:SaveData()\n\t\t\thook.Run(\"CharacterVendorTraded\", client, entity, uniqueID, isSellingToVendor)\n\t\telse\n\t\t\tclient:NotifyLocalized(\"vendorNoTrade\")\n\t\tend\n\tend)\nelse\n\tVENDOR_TEXT = {}\n\tVENDOR_TEXT[VENDOR_SELLANDBUY] = \"vendorBoth\"\n\tVENDOR_TEXT[VENDOR_BUYONLY] = \"vendorBuy\"\n\tVENDOR_TEXT[VENDOR_SELLONLY] = \"vendorSell\"\n\n\tnet.Receive(\"ixVendorOpen\", function()\n\t\tlocal entity = net.ReadEntity()\n\n\t\tif (!IsValid(entity)) then\n\t\t\treturn\n\t\tend\n\n\t\tentity.money = net.ReadUInt(16)\n\t\tentity.items = net.ReadTable()\n\t\tentity.scale = net.ReadFloat()\n\n\t\tix.gui.vendor = vgui.Create(\"ixVendor\")\n\t\tix.gui.vendor:SetReadOnly(false)\n\t\tix.gui.vendor:Setup(entity)\n\tend)\n\n\tnet.Receive(\"ixVendorEditor\", function()\n\t\tlocal entity = net.ReadEntity()\n\n\t\tif (!IsValid(entity) or !CAMI.PlayerHasAccess(LocalPlayer(), \"Helix - Manage Vendors\", nil)) then\n\t\t\treturn\n\t\tend\n\n\t\tentity.money = net.ReadUInt(16)\n\t\tentity.items = net.ReadTable()\n\t\tentity.scale = net.ReadFloat()\n\t\tentity.messages = net.ReadTable()\n\t\tentity.factions = net.ReadTable()\n\t\tentity.classes = net.ReadTable()\n\n\t\tix.gui.vendor = vgui.Create(\"ixVendor\")\n\t\tix.gui.vendor:SetReadOnly(true)\n\t\tix.gui.vendor:Setup(entity)\n\t\tix.gui.vendorEditor = vgui.Create(\"ixVendorEditor\")\n\tend)\n\n\tnet.Receive(\"ixVendorEdit\", function()\n\t\tlocal panel = ix.gui.vendor\n\n\t\tif (!IsValid(panel)) then\n\t\t\treturn\n\t\tend\n\n\t\tlocal entity = panel.entity\n\n\t\tif (!IsValid(entity)) then\n\t\t\treturn\n\t\tend\n\n\t\tlocal key = net.ReadString()\n\t\tlocal data = net.ReadType()\n\n\t\tif (key == \"mode\") then\n\t\t\tentity.items[data[1]] = entity.items[data[1]] or {}\n\t\t\tentity.items[data[1]][VENDOR_MODE] = data[2]\n\n\t\t\tif (!data[2]) then\n\t\t\t\tpanel:removeItem(data[1])\n\t\t\telseif (data[2] == VENDOR_SELLANDBUY) then\n\t\t\t\tpanel:addItem(data[1])\n\t\t\telse\n\t\t\t\tpanel:addItem(data[1], data[2] == VENDOR_SELLONLY and \"selling\" or \"buying\")\n\t\t\t\tpanel:removeItem(data[1], data[2] == VENDOR_SELLONLY and \"buying\" or \"selling\")\n\t\t\tend\n\t\telseif (key == \"price\") then\n\t\t\tlocal uniqueID = data[1]\n\n\t\t\tentity.items[uniqueID] = entity.items[uniqueID] or {}\n\t\t\tentity.items[uniqueID][VENDOR_PRICE] = tonumber(data[2])\n\t\telseif (key == \"stockDisable\") then\n\t\t\tif (entity.items[data]) then\n\t\t\t\tentity.items[data][VENDOR_MAXSTOCK] = nil\n\t\t\tend\n\t\telseif (key == \"stockMax\") then\n\t\t\tlocal uniqueID = data[1]\n\t\t\tlocal value = data[2]\n\t\t\tlocal current = data[3]\n\n\t\t\tentity.items[uniqueID] = entity.items[uniqueID] or {}\n\t\t\tentity.items[uniqueID][VENDOR_MAXSTOCK] = value\n\t\t\tentity.items[uniqueID][VENDOR_STOCK] = current\n\t\telseif (key == \"stock\") then\n\t\t\tlocal uniqueID = data[1]\n\t\t\tlocal value = data[2]\n\n\t\t\tentity.items[uniqueID] = entity.items[uniqueID] or {}\n\n\t\t\tif (!entity.items[uniqueID][VENDOR_MAXSTOCK]) then\n\t\t\t\tentity.items[uniqueID][VENDOR_MAXSTOCK] = value\n\t\t\tend\n\n\t\t\tentity.items[uniqueID][VENDOR_STOCK] = value\n\t\telseif (key == \"scale\") then\n\t\t\tentity.scale = data\n\t\tend\n\tend)\n\n\tnet.Receive(\"ixVendorEditFinish\", function()\n\t\tlocal panel = ix.gui.vendor\n\t\tlocal editor = ix.gui.vendorEditor\n\n\t\tif (!IsValid(panel) or !IsValid(editor)) then\n\t\t\treturn\n\t\tend\n\n\t\tlocal entity = panel.entity\n\n\t\tif (!IsValid(entity)) then\n\t\t\treturn\n\t\tend\n\n\t\tlocal key = net.ReadString()\n\t\tlocal data = net.ReadType()\n\n\t\tif (key == \"name\") then\n\t\t\teditor.name:SetText(data)\n\t\telseif (key == \"description\") then\n\t\t\teditor.description:SetText(data)\n\t\telseif (key == \"bubble\") then\n\t\t\teditor.bubble.noSend = true\n\t\t\teditor.bubble:SetValue(data and 1 or 0)\n\t\telseif (key == \"mode\") then\n\t\t\tif (data[2] == nil) then\n\t\t\t\teditor.lines[data[1]]:SetValue(3, L\"none\")\n\t\t\telse\n\t\t\t\teditor.lines[data[1]]:SetValue(3, L(VENDOR_TEXT[data[2]]))\n\t\t\tend\n\t\telseif (key == \"price\") then\n\t\t\teditor.lines[data]:SetValue(4, entity:GetPrice(data))\n\t\telseif (key == \"stockDisable\") then\n\t\t\teditor.lines[data]:SetValue(5, \"-\")\n\t\telseif (key == \"stockMax\" or key == \"stock\") then\n\t\t\tlocal current, max = entity:GetStock(data)\n\n\t\t\teditor.lines[data]:SetValue(5, current..\"/\"..max)\n\t\telseif (key == \"faction\") then\n\t\t\tlocal uniqueID = data[1]\n\t\t\tlocal state = data[2]\n\t\t\tlocal editPanel = ix.gui.editorFaction\n\n\t\t\tentity.factions[uniqueID] = state\n\n\t\t\tif (IsValid(editPanel) and IsValid(editPanel.factions[uniqueID])) then\n\t\t\t\teditPanel.factions[uniqueID]:SetChecked(state == true)\n\t\t\tend\n\t\telseif (key == \"class\") then\n\t\t\tlocal uniqueID = data[1]\n\t\t\tlocal state = data[2]\n\t\t\tlocal editPanel = ix.gui.editorFaction\n\n\t\t\tentity.classes[uniqueID] = state\n\n\t\t\tif (IsValid(editPanel) and IsValid(editPanel.classes[uniqueID])) then\n\t\t\t\teditPanel.classes[uniqueID]:SetChecked(state == true)\n\t\t\tend\n\t\telseif (key == \"model\") then\n\t\t\teditor.model:SetText(entity:GetModel())\n\t\telseif (key == \"scale\") then\n\t\t\teditor.sellScale.noSend = true\n\t\t\teditor.sellScale:SetValue(data)\n\t\tend\n\n\t\tsurface.PlaySound(\"buttons/button14.wav\")\n\tend)\n\n\tnet.Receive(\"ixVendorMoney\", function()\n\t\tlocal panel = ix.gui.vendor\n\n\t\tif (!IsValid(panel)) then\n\t\t\treturn\n\t\tend\n\n\t\tlocal entity = panel.entity\n\n\t\tif (!IsValid(entity)) then\n\t\t\treturn\n\t\tend\n\n\t\tlocal value = net.ReadUInt(16)\n\t\tvalue = value != -1 and value or nil\n\n\t\tentity.money = value\n\n\t\tlocal editor = ix.gui.vendorEditor\n\n\t\tif (IsValid(editor)) then\n\t\t\tlocal useMoney = tonumber(value) != nil\n\n\t\t\teditor.money:SetDisabled(!useMoney)\n\t\t\teditor.money:SetEnabled(useMoney)\n\t\t\teditor.money:SetText(useMoney and value or \"∞\")\n\t\tend\n\tend)\n\n\tnet.Receive(\"ixVendorStock\", function()\n\t\tlocal panel = ix.gui.vendor\n\n\t\tif (!IsValid(panel)) then\n\t\t\treturn\n\t\tend\n\n\t\tlocal entity = panel.entity\n\n\t\tif (!IsValid(entity)) then\n\t\t\treturn\n\t\tend\n\n\t\tlocal uniqueID = net.ReadString()\n\t\tlocal amount = net.ReadUInt(16)\n\n\t\tentity.items[uniqueID] = entity.items[uniqueID] or {}\n\t\tentity.items[uniqueID][VENDOR_STOCK] = amount\n\n\t\tlocal editor = ix.gui.vendorEditor\n\n\t\tif (IsValid(editor)) then\n\t\t\tlocal _, max = entity:GetStock(uniqueID)\n\n\t\t\teditor.lines[uniqueID]:SetValue(4, amount .. \"/\" .. max)\n\t\tend\n\tend)\n\n\tnet.Receive(\"ixVendorAddItem\", function()\n\t\tlocal uniqueID = net.ReadString()\n\n\t\tif (IsValid(ix.gui.vendor)) then\n\t\t\tix.gui.vendor:addItem(uniqueID, \"buying\")\n\t\tend\n\tend)\nend\n\nproperties.Add(\"vendor_edit\", {\n\tMenuLabel = \"Edit Vendor\",\n\tOrder = 999,\n\tMenuIcon = \"icon16/user_edit.png\",\n\n\tFilter = function(self, entity, client)\n\t\tif (!IsValid(entity)) then return false end\n\t\tif (entity:GetClass() != \"ix_vendor\") then return false end\n\t\tif (!gamemode.Call( \"CanProperty\", client, \"vendor_edit\", entity)) then return false end\n\n\t\treturn CAMI.PlayerHasAccess(client, \"Helix - Manage Vendors\", nil)\n\tend,\n\n\tAction = function(self, entity)\n\t\tself:MsgStart()\n\t\t\tnet.WriteEntity(entity)\n\t\tself:MsgEnd()\n\tend,\n\n\tReceive = function(self, length, client)\n\t\tlocal entity = net.ReadEntity()\n\n\t\tif (!IsValid(entity)) then return end\n\t\tif (!self:Filter(entity, client)) then return end\n\n\t\tentity.receivers[#entity.receivers + 1] = client\n\n\t\tlocal itemsTable = {}\n\n\t\tfor k, v in pairs(entity.items) do\n\t\t\tif (!table.IsEmpty(v)) then\n\t\t\t\titemsTable[k] = v\n\t\t\tend\n\t\tend\n\n\t\tclient.ixVendor = entity\n\n\t\tnet.Start(\"ixVendorEditor\")\n\t\t\tnet.WriteEntity(entity)\n\t\t\tnet.WriteUInt(entity.money or 0, 16)\n\t\t\tnet.WriteTable(itemsTable)\n\t\t\tnet.WriteFloat(entity.scale or 0.5)\n\t\t\tnet.WriteTable(entity.messages)\n\t\t\tnet.WriteTable(entity.factions)\n\t\t\tnet.WriteTable(entity.classes)\n\t\tnet.Send(client)\n\tend\n})\n"
  },
  {
    "path": "plugins/wepselect.lua",
    "content": "\nPLUGIN.name = \"Weapon Select\"\nPLUGIN.author = \"Chessnut\"\nPLUGIN.description = \"A replacement for the default weapon selection.\"\n\nif (CLIENT) then\n\tPLUGIN.index = PLUGIN.index or 1\n\tPLUGIN.deltaIndex = PLUGIN.deltaIndex or PLUGIN.index\n\tPLUGIN.infoAlpha = PLUGIN.infoAlpha or 0\n\tPLUGIN.alpha = PLUGIN.alpha or 0\n\tPLUGIN.alphaDelta = PLUGIN.alphaDelta or PLUGIN.alpha\n\tPLUGIN.fadeTime = PLUGIN.fadeTime or 0\n\n\tlocal matrixScale = Vector(1, 1, 0)\n\n\tfunction PLUGIN:LoadFonts(font, genericFont)\n\t\tsurface.CreateFont(\"ixWeaponSelectFont\", {\n\t\t\tfont = font,\n\t\t\tsize = ScreenScale(16),\n\t\t\textended = true,\n\t\t\tweight = 1000\n\t\t})\n\tend\n\n\tfunction PLUGIN:HUDShouldDraw(name)\n\t\tif (name == \"CHudWeaponSelection\") then\n\t\t\treturn false\n\t\tend\n\tend\n\n\tfunction PLUGIN:HUDPaint()\n\t\tlocal frameTime = FrameTime()\n\n\t\tself.alphaDelta = Lerp(frameTime * 10, self.alphaDelta, self.alpha)\n\n\t\tlocal fraction = self.alphaDelta\n\n\t\tif (fraction > 0.01) then\n\t\t\tlocal x, y = ScrW() * 0.5, ScrH() * 0.5\n\t\t\tlocal spacing = math.pi * 0.85\n\t\t\tlocal radius = 240 * self.alphaDelta\n\t\t\tlocal shiftX = ScrW() * .02\n\n\t\t\tself.deltaIndex = Lerp(frameTime * 12, self.deltaIndex, self.index)\n\n\t\t\tlocal weapons = LocalPlayer():GetWeapons()\n\t\t\tlocal index = self.deltaIndex\n\n\t\t\tif (!weapons[self.index]) then\n\t\t\t\tself.index = #weapons\n\t\t\tend\n\n\t\t\tfor i = 1, #weapons do\n\t\t\t\tlocal theta = (i - index) * 0.1\n\t\t\t\tlocal color = ColorAlpha(\n\t\t\t\t\ti == self.index and ix.config.Get(\"color\") or color_white,\n\t\t\t\t\t(255 - math.abs(theta * 3) * 255) * fraction\n\t\t\t\t)\n\n\t\t\t\tlocal lastY = 0\n\n\t\t\t\tif (self.markup and (i < self.index or i == 1)) then\n\t\t\t\t\tif (self.index != 1) then\n\t\t\t\t\t\tlocal _, h = self.markup:Size()\n\t\t\t\t\t\tlastY = h * fraction\n\t\t\t\t\tend\n\n\t\t\t\t\tif (i == 1 or i == self.index - 1) then\n\t\t\t\t\t\tself.infoAlpha = Lerp(frameTime * 3, self.infoAlpha, 255)\n\t\t\t\t\t\tself.markup:Draw(x + 6 + shiftX, y + 30, 0, 0, self.infoAlpha * fraction)\n\t\t\t\t\tend\n\t\t\t\tend\n\n\t\t\t\tsurface.SetFont(\"ixWeaponSelectFont\")\n\t\t\t\tlocal weaponName = language.GetPhrase(weapons[i]:GetPrintName()):utf8upper()\n\t\t\t\tlocal _, ty = surface.GetTextSize(weaponName)\n\t\t\t\tlocal scale = 1 - math.abs(theta * 2)\n\n\t\t\t\tlocal matrix = Matrix()\n\t\t\t\tmatrix:Translate(Vector(\n\t\t\t\t\tshiftX + x + math.cos(theta * spacing + math.pi) * radius + radius,\n\t\t\t\t\ty + lastY + math.sin(theta * spacing + math.pi) * radius - ty / 2 ,\n\t\t\t\t\t1))\n\t\t\t\tmatrix:Scale(matrixScale * scale)\n\n\t\t\t\tcam.PushModelMatrix(matrix)\n\t\t\t\t\tix.util.DrawText(weaponName, 2, ty / 2, color, 0, 1, \"ixWeaponSelectFont\")\n\t\t\t\tcam.PopModelMatrix()\n\t\t\tend\n\n\t\t\tif (self.fadeTime < CurTime() and self.alpha > 0) then\n\t\t\t\tself.alpha = 0\n\t\t\tend\n\t\tend\n\tend\n\n\tfunction PLUGIN:OnIndexChanged(weapon)\n\t\tself.alpha = 1\n\t\tself.fadeTime = CurTime() + 5\n\t\tself.markup = nil\n\n\t\tif (IsValid(weapon)) then\n\t\t\tlocal instructions = weapon.Instructions\n\t\t\tlocal text = \"\"\n\n\t\t\tif (instructions != nil and instructions:find(\"%S\")) then\n\t\t\t\tlocal color = ix.config.Get(\"color\")\n\t\t\t\ttext = text .. string.format(\n\t\t\t\t\t\"<font=ixItemBoldFont><color=%d,%d,%d>%s</font></color>\\n%s\\n\",\n\t\t\t\t\tcolor.r, color.g, color.b, L(\"Instructions\"), instructions\n\t\t\t\t)\n\t\t\tend\n\n\t\t\tif (text != \"\") then\n\t\t\t\tself.markup = markup.Parse(\"<font=ixItemDescFont>\"..text, ScrW() * 0.3)\n\t\t\t\tself.infoAlpha = 0\n\t\t\tend\n\n\t\t\tlocal source, pitch = hook.Run(\"WeaponCycleSound\")\n\t\t\tLocalPlayer():EmitSound(source or \"common/talk.wav\", 50, pitch or 180)\n\t\tend\n\tend\n\n\tfunction PLUGIN:PlayerBindPress(client, bind, pressed)\n\t\tbind = bind:lower()\n\n\t\tif (!pressed or !bind:find(\"invprev\") and !bind:find(\"invnext\")\n\t\tand !bind:find(\"slot\") and !bind:find(\"attack\")) then\n\t\t\treturn\n\t\tend\n\n\t\tlocal currentWeapon = client:GetActiveWeapon()\n\t\tlocal bValid = IsValid(currentWeapon)\n\t\tlocal bTool\n\n\t\tif (client:InVehicle() or (bValid and currentWeapon:GetClass() == \"weapon_physgun\" and client:KeyDown(IN_ATTACK))) then\n\t\t\treturn\n\t\tend\n\n\t\tif (bValid and currentWeapon:GetClass() == \"gmod_tool\") then\n\t\t\tlocal tool = client:GetTool()\n\t\t\tbTool = tool and (tool.Scroll != nil)\n\t\tend\n\n\t\tlocal weapons = client:GetWeapons()\n\n\t\tif (bind:find(\"invprev\") and !bTool) then\n\t\t\tlocal oldIndex = self.index\n\t\t\tself.index = math.min(self.index + 1, #weapons)\n\n\t\t\tif (self.alpha == 0 or oldIndex != self.index) then\n\t\t\t\tself:OnIndexChanged(weapons[self.index])\n\t\t\tend\n\n\t\t\treturn true\n\t\telseif (bind:find(\"invnext\") and !bTool) then\n\t\t\tlocal oldIndex = self.index\n\t\t\tself.index = math.max(self.index - 1, 1)\n\n\t\t\tif (self.alpha == 0 or oldIndex != self.index) then\n\t\t\t\tself:OnIndexChanged(weapons[self.index])\n\t\t\tend\n\n\t\t\treturn true\n\t\telseif (bind:find(\"slot\")) then\n\t\t\tself.index = math.Clamp(tonumber(bind:match(\"slot(%d)\")) or 1, 1, #weapons)\n\t\t\tself:OnIndexChanged(weapons[self.index])\n\n\t\t\treturn true\n\t\telseif (bind:find(\"attack\") and self.alpha > 0) then\n\t\t\tlocal weapon = weapons[self.index]\n\n\t\t\tif (IsValid(weapon)) then\n\t\t\t\tLocalPlayer():EmitSound(hook.Run(\"WeaponSelectSound\", weapon) or \"HL2Player.Use\")\n\n\t\t\t\tinput.SelectWeapon(weapon)\n\t\t\t\tself.alpha = 0\n\t\t\tend\n\n\t\t\treturn true\n\t\tend\n\tend\n\n\tfunction PLUGIN:Think()\n\t\tlocal client = LocalPlayer()\n\t\tif (!IsValid(client) or !client:Alive()) then\n\t\t\tself.alpha = 0\n\t\tend\n\tend\n\n\tfunction PLUGIN:ScoreboardShow()\n\t\tself.alpha = 0\n\tend\n\n\tfunction PLUGIN:ShouldPopulateEntityInfo(entity)\n\t\tif (self.alpha > 0) then\n\t\t\treturn false\n\t\tend\n\tend\nend\n"
  }
]