/* client/main.qc main csqc code Copyright (C) 2021-2024 NZ:P Team This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to: Free Software Foundation, Inc. 59 Temple Place - Suite 330 Boston, MA 02111-1307, USA */ float need_vid_reload; void() ToggleMenu = { if(serverkey("constate") != "disconnected") { if (player_count == 0) localcmd("cmd pause\n"); if(in_menu == MENU_NONE) { in_menu = MENU_PAUSE; time_in_menu = 0; local float i; for(i = 0; i < buttons.length; i++) { buttons[i].active = 1; } setcursormode(TRUE, cvar_string("cl_cursor"), __NULL__, cvar("cl_cursor_scale")); } else { in_menu = MENU_NONE; setcursormode(FALSE); } } else { in_menu = MENU_MAIN; time_in_menu = 0; setcursormode(TRUE, cvar_string("cl_cursor"), __NULL__, cvar("cl_cursor_scale")); } } // // SetGamepadBindings() // Since our menu architecture currently sucks, // we don't yet support a good binds menu that // let's you get good gamepad control.. so // force these binds (sorry!) // void() SetGamepadBindings = { localcmd("unbindall; bind ~ \"toggleconsole\"; bind ` \"toggleconsole\"; bind ESCAPE \"togglemenu\"; joyadvaxisr 4; joyadvaxisu 2; joyadvaxisx 3; joyadvaxisy -1; bind JOY1 \"+button5\"; bindlevel JOY2 30 \"null\"; bind JOY3 \"+button4\"; bind GP_LSHOULDER \"impulse 33\"; bind AUX1 \"+moveup\"; bind AUX2 \"+button3\"; bindlevel AUX3 30 \"null\"; bindlevel AUX4 30 \"null\"; bind AUX5 \"togglemenu\"; bind AUX10 \"+attack\"; bind GP_Y \"+button4\"; bindlevel GP_A 30 \"impulse 10\"; bindlevel GP_B 30 \"impulse 30\"; bindlevel GP_X 30 \"+button5\"; bindlevel GP_LTHUMB 30 \"impulse 23\"; bindlevel GP_RTHUMB 30 \"+button6\"; bind GP_LTRIGGER \"+button8\"; bindlevel GP_DPAD_DOWN 30 \"+button7\"; bindlevel GP_RSHOULDER 30 \"+button3\"; bindlevel GP_DPAD_RIGHT 30 \"impulse 33\"; bindlevel GP_DPAD_UP 30 \"+button7\"; bind GP_VIEW \"showscores\"; joysidesensitivity 0.8; joyyawsensitivity 0.6; joypitchsensitivity 0.55\n"); } float(float isnew) SetZombieSkinning = { self.drawmask = MASK_ENGINE; setcustomskin(self, __NULL__, sprintf("replace \"\" \"models/ai/zfull.mdl_%d.pcx\"\n", self.skin)); return PREDRAW_NEXT; }; // // GenerateAlphaTransparencyQ3Shaders() // What a mouth-full! Anyway, NZ:P supports // alpha transparency via a pseudo-hack where // if the last character of a model is "$" // we render with special blend modes. To // do the same on FTE, we need to generate // Quake III "shader" files. Returns TRUE // if we had shader modifications. // float() GenerateAlphaTransparencyQ3Shaders = { searchhandle alias_models; float amod_count; float need_reload = false; alias_models = search_begin("*.mdl:*/*.mdl:*/*/*.mdl:*/*/*/*.mdl:*/*/*/*/*.mdl:*/*/*/*/*/*.mdl", SB_CASEINSENSITIVE | SB_MULTISEARCH, true); // gross. amod_count = search_getsize(alias_models); for (float i = 0; i < amod_count; i++) { string full_path = search_getfilename(alias_models, i); // Single out character before ".mdl" extension. string special_character = substring(full_path, strlen(full_path) - 5, 1); // Early out, not a special guy. if (special_character != "$") continue; // Isolate its basename.. manually :( string basename = ""; for (float j = strlen(full_path); j > 0; j--) { if (str2chr(full_path, j) == str2chr("/", 0)) { // Strip path basename = substring(full_path, j + 1, strlen(full_path) - (j + 1)); // Strip extension basename = substring(basename, 0, strlen(basename) - 4); break; } } if (basename == "") { print(sprintf("[ERROR]: Unable to calculate basename for [%s]!\n", full_path)); continue; } float shader_file; string shader_path = sprintf("scripts/%s.shader", basename); // Check if the shader already exists. shader_file = fopen(shader_path, FILE_READ); if (shader_file != -1) { fclose(shader_file); continue; } // Begin to write. shader_file = fopen(shader_path, FILE_WRITE); if (shader_file == -1) { print(sprintf("[ERROR]: Unable to generate Q3 shader for [%s]!\n", full_path)); continue; } // Body of our shader file we're writing. string shader_content = sprintf( "//\n" "// Quake III Shader generated automatically by Nazi Zombies: Portable. Do not modify.\n" "//\n" "\n" "%s_0.lmp\n" // full_path "{\n" " program defaultskin\n" " progblendfunc GL_SRC_ALPHA GL_ONE_MINUS_SRC_ALPHA\n" " diffusemap %s_0.tga\n" // full_path " alphafunc ge128\n" "}\n" , full_path, full_path); fputs(shader_file, shader_content); fclose(shader_file); need_reload = true; } search_end(alias_models); return need_reload; }; noref void(float apiver, string enginename, float enginever) CSQC_Init = { setwindowcaption("Nazi Zombies: Portable"); precache_sound("sounds/menu/enter.wav"); precache_sound("sounds/menu/navigate.wav"); precache_model("models/player.mdl"); registercommand("togglemenu"); registercommand("startwalk"); registercommand("stopwalk"); registercommand("promptjoin"); registercommand("showscores"); cvar_set("sv_cheats", ftos(1)); cvar_set("r_fb_models", ftos(0)); autocvar(r_viewmodel_default_fov, 70); autocvar(cl_controllerglyphs, "xbox"); autocvar(in_rumbleenabled, 1); autocvar(in_aimassist, 0); // Runtime check if we're running this in WebASM/WebGL. if (cvar_string("sys_platform") == "Web") platform_is_web = true; else platform_is_web = false; if (platform_is_web) { cvar_set("com_protocolname", "NZP-REBOOT-WEB"); } //print("CSQC Started\n"); if(serverkey("constate") == "disconnected") ToggleMenu(); //bgpos = 0; // default button binds buttonBind[0] = "+forward"; buttonBind[1] = "+back"; buttonBind[2] = "+moveleft"; buttonBind[3] = "+moveright"; buttonBind[4] = "impulse 10"; buttonBind[5] = "impulse 23"; buttonBind[6] = "impulse 30"; buttonBind[7] = "+button4"; buttonBind[8] = "+button4"; buttonBind[9] = "+button7"; buttonBind[10] = "+button5"; buttonBind[11] = "+button6"; buttonBind[12] = "+button3"; buttonBind[13] = "impulse 33"; // default/current width and height active_swidth = cvar("vid_width"); active_sheight = cvar("vid_height"); fullscreenval = cvar("vid_fullscreen"); // cvars for custom settings autocvar(cl_cinematic, 0); autocvar(nzp_particles, 1); autocvar(nzp_decals, 1); autocvar(vid_ultrawide_limiter, 0); autocvar(cl_bobside, 0.02); // per-channel volume autocvar(snd_channel1volume, 1); autocvar(snd_channel2volume, 1); autocvar(snd_channel3volume, 1); autocvar(snd_channel4volume, 1); autocvar(snd_channel5volume, 1); autocvar(snd_channel6volume, 1); // FTE is overriding cl_bobup values.. so be evil // and force it to 0.02. cvar_set("cl_bobup", "0.02"); // force nearest filtering for hud elements to avoid blur cvar_set("gl_texturemode2d", "gl_nearest"); cvar_set("r_font_linear", "0"); // force build date text in menu cvar_set("cl_showbuildtime", "1"); // in-game stopwatch autocvar(scr_serverstopwatch, 0); stopwatch_sec = stopwatch_min = stopwatch_hr = 0; stopwatch_round_sec = stopwatch_round_min = stopwatch_round_hr = 0; stopwatch_round_starttime = time; // FTE via emscripten (for the fault of the browser?) bugs out // when using mousewheel. So as a hack, just unbind the mouse // wheel. :/ if (platform_is_web) { localcmd("unbind MWHEELUP; unbind MWHEELDOWN\n"); localcmd("bind 1 +button4; bind 2 +button4\n"); } // retrieve custom maps Customs_Get(); // // get the build date // float file = fopen("version.txt", FILE_READ); if (file != -1) { build_datetime = fgets(file); fclose(file); } // Intercept every Zombie mesh to apply custom skinning for(float i = 0; i < zombie_skins.length; i++) { deltalisten(zombie_skins[i], SetZombieSkinning, 0); } InitKerningMap(); // If we've made shader changes, we should perform // a vid_reload at a reasonable time. need_vid_reload = GenerateAlphaTransparencyQ3Shaders(); }; noref void() CSQC_WorldLoaded = { if (need_vid_reload == true) { need_vid_reload = false; localcmd("vid_reload\n"); } Achievement_Init(); Particles_Init(); nameprint_time = time + 8; huddir = "gfx/hud/"; }; // This is from COW lol! #define ADS_THOMPSON '-3 +5.80 +4.52' /*vector adsOffset; float adsAmount; float adsDir;*/ vector weapon_bob_factor; float weapon_bob_factor_z_coef; vector dampening_factor; float vaccel; float vzaccel; // FIXME: Move Power-Up CSQC drawing to separate file :) float mdlflag_poweruprotate_duration; float mdlflag_poweruprotate_starttime; vector mdlflag_poweruprotate_startangles; vector mdlflag_poweruprotate_differenceangles; vector mdlflag_poweruprotate_currentangles; float last_puframetime; void() PU_UpdateAngles = { // Don't update more than once per frame. if (last_puframetime != frametime) { // New cycle, dictate new rotation time and target angle. if (mdlflag_poweruprotate_duration <= time) { mdlflag_poweruprotate_starttime = time; mdlflag_poweruprotate_duration = time + (random() * 25 + 25)/10; // Take between 2.5 and 5 seconds. mdlflag_poweruprotate_startangles[0] = mdlflag_poweruprotate_currentangles[0]; mdlflag_poweruprotate_startangles[1] = mdlflag_poweruprotate_currentangles[1]; mdlflag_poweruprotate_startangles[2] = mdlflag_poweruprotate_currentangles[2]; float target_pitch = (random() * 120) - 60; float target_yaw = (random() * 240) + 60; float target_roll = (random() * 90) - 45; vector target_angles; target_angles[0] = target_pitch; target_angles[1] = target_yaw; target_angles[2] = target_roll; // Calculate the difference from our start to our target. for(float i = 0; i < 2; i++) { if (mdlflag_poweruprotate_currentangles[i] > target_angles[i]) mdlflag_poweruprotate_differenceangles[i] = (mdlflag_poweruprotate_currentangles[i] - target_angles[i]) * -1; else mdlflag_poweruprotate_differenceangles[i] = fabs(mdlflag_poweruprotate_currentangles[i] - target_angles[i]); } } float percentage_complete = (time - mdlflag_poweruprotate_starttime) / (mdlflag_poweruprotate_duration - mdlflag_poweruprotate_starttime); for(float j = 0; j < 2; j++) { mdlflag_poweruprotate_currentangles[j] = mdlflag_poweruprotate_startangles[j] + (mdlflag_poweruprotate_differenceangles[j] * percentage_complete); } last_puframetime = frametime; } }; float() PU_PreDraw = { PU_UpdateAngles(); self.angles = mdlflag_poweruprotate_currentangles; addentity(self); return PREDRAW_NEXT; }; float Player_PreDraw() = { self.lerpfrac -= frametime*10; while(self.lerpfrac < 0) { self.frame2 = self.frame; self.lerpfrac += 1; } if (self.entnum == player_localentnum) { self.movetype = MOVETYPE_WALK; // Prepare rollback vector vOldOrigin = self.origin; vector vOldVelocity = self.velocity; float fOldPMoveFlags = self.pmove_flags; // Apply physics for every single input-frame that has not yet been // acknowledged by the server (servercommandframe = last acknowledged frame) for (int i = servercommandframe + 1; i <= clientcommandframe; i++) { float flSuccess = getinputstate(i); if (flSuccess == FALSE) { continue; } // Partial frames are the worst if (input_timelength == 0) { break; } runstandardplayerphysics(self); } // Smooth stair stepping, this has to be done manually! playerOriginOld = playerOrigin; if ((self.flags & FL_ONGROUND) && (self.origin_z - playerOriginOld_z > 0)) { playerOriginOld_z += frametime * 150; if (playerOriginOld_z > self.origin_z) { playerOriginOld_z = self.origin_z; } if (self.origin_z - playerOriginOld_z > 18) { playerOriginOld_z = self.origin_z - 18; } playerOrigin_z += playerOriginOld_z - self.origin_z; } else { playerOriginOld_z = self.origin_z; } playerOrigin = [self.origin_x, self.origin_y, playerOriginOld_z]; playerVelocity = self.velocity; addentity(self); // Time to roll back self.origin = vOldOrigin; setorigin(self, self.origin); self.velocity = vOldVelocity; self.pmove_flags = fOldPMoveFlags; self.movetype = MOVETYPE_NONE; // Set renderflag for mirrors! self.renderflags = RF_EXTERNALMODEL; } else { addentity(self); } return PREDRAW_NEXT; } noref void(float isnew) CSQC_Ent_Update = { float ent_type = readbyte(); // Player if (ent_type == 1) { if (isnew == TRUE) { self.classname = "player"; self.solid = SOLID_SLIDEBOX; self.predraw = Player_PreDraw; self.drawmask = MASK_ENGINE; setmodel(self, "models/player.mdl"); } float old_points = self.points; self.origin_x = readcoord(); self.origin_y = readcoord(); self.origin_z = readcoord(); self.angles_x = readangle(); self.angles_y = readangle(); self.angles_z = readangle(); self.velocity_x = readshort(); self.velocity_y = readshort(); self.velocity_z = readshort(); self.playernum = readbyte(); self.modelindex = readshort(); self.frame = readbyte(); self.movetype = readshort(); self.flags = readshort(); self.stance = readbyte(); self.points = readfloat(); // FIXME: this should be made a short, but I know we use price of 1 for some test maps, so I can't do /10 *10 shenanigans. self.kills = readshort(); self.is_in_menu = readbyte(); RegisterPointChange(self.points - old_points, self.playernum); if (self.movetype == MOVETYPE_BOUNCE) self.solid = SOLID_NOT; else self.solid = SOLID_SLIDEBOX; setmodelindex(self, self.modelindex); if (map_compatibility_mode != MAP_COMPAT_BETA) { if (self.stance == 2) setsize(self, PLAYER_MINS_STANDING, PLAYER_MAXS_STANDING); else setsize(self, PLAYER_MINS_CROUCHING, PLAYER_MAXS_CROUCHING); } else { setsize(self, PLAYER_MINS_QUAKE, PLAYER_MAXS_QUAKE); } } // Power-Up else if (ent_type == 2) { if (isnew == TRUE) { self.classname = "item_powerup"; self.solid = SOLID_NOT; self.predraw = PU_PreDraw; self.drawmask = MASK_ENGINE; } self.origin_x = readcoord(); self.origin_y = readcoord(); self.origin_z = readcoord(); self.modelindex = readshort(); setmodelindex(self, self.modelindex); } else { if(isnew) addentity(self); } } float(__inout vector v) VectorNormalize = { float length = sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]); if (length) { float ilength = 1 / length; v[0] *= ilength; v[1] *= ilength; v[2] *= ilength; } return length; } void() DropRecoilKick = { float len; if (crosshair_spread_time > time) return; len = VectorNormalize(gun_kick); len = len - frametime*5; if (len < 0) len = 0; gun_kick[0] *= len; gun_kick[1] *= len; gun_kick[2] *= len; } string() LoadScreen_GiveTip = { float index = rint((random() * (80 - 1))) + 1; switch(index) { case 1: return "Released in 1996, Quake is over 25 years old!"; case 2: return "Use the Kar98k to be the hero we need!"; case 3: return "Lots of modern engines are based on Quake!"; case 4: return "NZ:P began development on September 27 2009!"; case 5: return "NZ:P was first released on December 25, 2010!"; case 6: return "NZ:P Beta 1.1 has over 300,000 downloads!"; case 7: return "NZ:P has been downloaded over 500,000 times!"; case 8: return "A lot of people have worked on NZ:P!"; case 9: return "Blubswillrule, or \"blubs\", is from the US."; case 10: return "Jukki is from Finland."; case 11: return "Ju[s]tice, or \"tom\" is from Lithuania."; case 12: return "This game has given us bad sleeping habits!"; case 13: return "We had a lot of fun making this game!"; case 14: return "Pro Tip: you can make your own custom map!"; case 15: return "Try Retro Mode, it's in the Graphics Settings!"; case 16: return "Tired of our maps? Go make your own!"; case 17: return "Slay zombies & be grateful."; case 18: return "Custom maps, CUSTOM MAPS!"; case 19: return "Go outside & build a snowman!"; case 20: return "Please surround yourself with zombies!"; case 21: return "Don't play for too long.. zombies may eat you."; case 22: return "That was epic... EPIC FOR THE WIIIN!"; //why case 23: return "FTEQW is an awesome Quake Engine!"; case 24: return "You dead yet?"; case 25: return "Now 21% cooler!"; case 26: return "your lg is nothink on the lan!"; //what case 27: return "I'm not your chaotic on dm6!"; case 28: return "Shoot or knife zombies to kill them, up to you!"; case 29: return "How many people forgot to Compile today?"; case 30: return "ggnore"; case 31: return "NZ:P is also on PC, Switch, Vita, and PSP!"; case 32: return "Submerge your device in water for godmode!"; case 33: return "10/10/10 was a good day."; case 34: return "Also check out \"FreeCS\" by eukara!"; case 35: return "CypressImplex, or \"Ivy\", is from the USA."; case 36: return "Zombies don't like bullets."; case 37: return "Thanks for being an awesome fan!"; case 38: return "Removed Herobrine"; case 39: return "Pack-a-Punch the Kar98k to get to round 100000."; case 40: return "I feel like I'm being gaslit."; case 41: return "Heads up! You will die if you are killed!"; case 42: return "Zombies legally can't kill you if you say no!"; case 43: return "Please help me find the meaning of . Thanks."; case 44: return "Discord is ONLY for Thomas the Tank Engine RP!"; case 45: return "\"Get rid of the 21% tip, it's an MLP reference.\""; case 46: return "You're playing on FTE!"; case 47: return "Don't leak the beta!"; case 48: return "Jugger-Nog increases your health!"; case 49: return "greg was here"; case 50: return "Where the hell is the Mystery Box?!"; case 51: return "Zombies like getting shot.. I think."; case 52: return "pro tip: aiming helps"; case 53: return "\"my mom gave me plunger money\""; case 54: return "dolphin dive on top of your friend for god mode"; case 55: return "no free rides. ass, grass, or cash!"; case 56: return "https://nzp.gay/"; case 57: return "im an mlg gamer girl so its pretty guaranteed"; case 58: return "this is a w because you cant have enough fnaf"; case 59: return "i hope santa drops bombs on the uk"; case 60: return "Hoyl shit, bro! You fucking ported fortnite!"; case 61: return "icarly feet futtishist."; case 62: return "Well, it's impossible to play, I'm disgusted."; case 63: return "I like my women to not be cartoons"; case 64: return "Plot twist: NZP was always broken"; case 65: return "testing some think."; case 66: return "fnaf is older than gay marriage in the us"; case 67: return "i want that twink Obliterated"; case 68: return "i think he started the femboy transition process"; case 69: return "nice"; case 70: return "He's FUCKING annoying"; case 71: return "yeah pog female bikers"; case 72: return "Its either a stroke of genius or just a stroke"; case 73: return "Play some Custom Maps!"; case 74: return "Real OGs play on Win9X Machines!"; case 75: return "Adding this tip improved framerate by 39%!"; case 76: return "The NZ in NZP stands for New Zealand!"; case 77: return "The P in NZP stands for Professional!"; case 78: return "Remember to stay hydrated!"; case 79: return "cofe"; } return "wut wut"; }; noref void(float width, float height, float notmenu) CSQC_UpdateViewLoading = { if (loadscreen_mapname != "") { drawpic([0, 0], strcat("gfx/lscreen/", loadscreen_mapname), [width, height], [1, 1, 1], 1); } drawfill ([0, 0], [width, 32, 1], [0, 0, 0], 0.68, 0); drawfill ([0, height - 32], [width, 32, 1], [0, 0, 0], 0.68, 0); drawstring([width/2 - (stringwidth(loadscreen_tip, 0, [12, 12])/2), height - 16, 0], loadscreen_tip, [12, 12], [1, 1, 1], 1, 0); drawstring([6, 6], loadscreen_maptitle, [24, 24], TEXT_ORANGE, 1, 0); drawfill ([width/2 - 160, height - 42], [320, 18, 1], [0.27, 0.27, 0.27], 1, 0); drawfill ([width/2 - 164, height - 46], [328, 26, 1], [0, 0, 0], 0.77, 0); drawstring([width/2 - (stringwidth("Loading...", 0, [14, 14])/2), height - 40, 0], "Loading...", [14, 14], [1, 1, 1], 1, 0); }; // // Key_IsControllerGlyph(bind) // Returns TRUE if the string provided belongs to // a gamepad/controller. // float(string bind) Key_IsControllerGlyph = { switch(bind) { case "GP_A": case "GP_B": case "GP_X": case "GP_Y": case "GP_LSHOULDER": case "GP_RSHOULDER": case "GP_LTRIGGER": case "GP_RTRIGGER": case "GP_BACK": case "GP_START": case "GP_LTHUMB": case "GP_RTHUMB": case "GP_DPAD_UP": case "GP_DPAD_DOWN": case "GP_DPAD_LEFT": case "GP_DPAD_RIGHT": return true; } return false; }; // // Key_DrawControllerGlyph(position, bind, scale) // Draws the associated glyph for the bind name from the // Glyph tilemap at the provided position, with the // provided scale. // void(vector position, string bind, vector scale) Key_DrawControllerGlyph = { vector tilemap_position = [0, 0]; // The coordinates in 0-1 range of each button in the glyph tilemap. switch(bind) { case "GP_A": tilemap_position = [0, 0]; break; case "GP_B": tilemap_position = [0.125, 0]; break; case "GP_X": tilemap_position = [0.250, 0]; break; case "GP_Y": tilemap_position = [0.375, 0]; break; case "GP_LSHOULDER": tilemap_position = [0.250, 0.125]; break; case "GP_RSHOULDER": tilemap_position = [0.375, 0.125]; break; case "GP_LTRIGGER": tilemap_position = [0.500, 0.125]; break; case "GP_RTRIGGER": tilemap_position = [0.625, 0.125]; break; case "GP_BACK": tilemap_position = [0.875, 0.125]; break; case "GP_START": tilemap_position = [0.750, 0.125]; break; case "GP_LTHUMB": tilemap_position = [0, 0.125]; break; case "GP_RTHUMB": tilemap_position = [0.125, 0.125]; break; case "GP_DPAD_UP": tilemap_position = [0.500, 0]; break; case "GP_DPAD_DOWN": tilemap_position = [0.625, 0]; break; case "GP_DPAD_LEFT": tilemap_position = [0.750, 0]; break; case "GP_DPAD_RIGHT": tilemap_position = [0.875, 0]; break; } drawsubpic([position_x, position_y], [scale_x, scale_y], sprintf("gfx/controller_glyphs/%s.tga", cvar_string("cl_controllerglyphs")), [tilemap_position_x, tilemap_position_y], [0.125, 0.125], [1, 1, 1], 1); }; #define SCALE_CONSTANT 8 //MOVEME float(float a) angledelta = { a = anglemod(a); if (a > 180) a -= 360; return a; } //MOVEME float delta_pitch, delta_yaw; vector sniper_sway; // Sways the camera while scoped in. void() Camera_SniperSway = { if (getstatf(STAT_WEAPONZOOM) != 2 || (getstatf(STAT_PERKS) & P_DEAD)) { sniper_sway = '0 0 0'; return; } delta_pitch = (cos(cltime/0.7) + cos(cltime) + sin(cltime/1.1)) * 0.5; delta_yaw = (sin(cltime/0.4) + cos(cltime/0.56) + sin(cltime)) * 0.5; sniper_sway[0] = angledelta(delta_pitch); sniper_sway[1] = angledelta(delta_yaw); }; float gamepad_enabled; // CALLED EVERY CLIENT RENDER FRAME float pap_flash_alternate; noref void(float width, float height, float menushown) CSQC_UpdateView = { //clear and update our global screen resolution vars clearscene(); g_width = width; g_height = height; // camang is controlled by our punchangles camang = getproperty(VF_ANGLES); //disable quake status bar and quake crosshair setviewprop(VF_DRAWENGINESBAR, 0); setviewprop(VF_DRAWCROSSHAIR, 0); float sensitivity_factor; if (in_menu == MENU_PAUSE) sensitivity_factor = 0; else sensitivity_factor = (1 + SCALE_CONSTANT * getstatf(STAT_VIEWZOOM)) / (1 + SCALE_CONSTANT); if (gamepad_enabled && getstatf(STAT_FACINGENEMY) && cvar("in_aimassist") == 1) sensitivity_factor *= 0.5; setsensitivityscaler(sensitivity_factor); setviewprop(VF_AFOV, autocvar(fov,90)*getstatf(STAT_VIEWZOOM)); cvar_set("r_viewmodel_fov", ftos(cvar("r_viewmodel_default_fov")*getstatf(STAT_VIEWZOOM))); // Increment the stopwatch // FIXME: I don't really liket his being in UpdateView.. this has nothing to do with rendering. stopwatch_sec = time - (stopwatch_min * 60 + (stopwatch_hr * 3600)); if (stopwatch_sec >= 60) { stopwatch_min += stopwatch_sec/60; } if (stopwatch_min >= 60) { stopwatch_hr += stopwatch_min/60; stopwatch_min = 0; } if (stopwatch_round_isactive) { stopwatch_round_sec = (time - stopwatch_round_starttime) - (stopwatch_round_min * 60 + (stopwatch_round_hr * 3600)); if (stopwatch_round_sec >= 60) { stopwatch_round_min += stopwatch_round_sec/60; } if (stopwatch_round_min >= 60) { stopwatch_round_hr += stopwatch_round_min/60; stopwatch_round_min = 0; } } else { stopwatch_round_starttime = time; } //autoadd entities received from servers for drawing addentities(MASK_ENGINE); setproperty(VF_ORIGIN, playerOrigin + [ 0, 0, getstatf(STAT_VIEWHEIGHT)]); //setproperty(VF_ANGLES, view_angles); // Draw the client's viewmodels. ViewModel_Draw(); DropRecoilKick(); Camera_SniperSway(); camang[0] += gun_kick[0]; camang[1] += gun_kick[1]; camang[2] += gun_kick[2]; camang[0] += sniper_sway[0]; camang[1] += sniper_sway[1]; camang[2] += sniper_sway[2]; setviewprop(VF_ANGLES, camang); //does what you think it does renderscene(); if (in_loadscreen) { in_loadscreen = false; localcmd(strcat("map ", loadscreen_mapname, "\n")); } if(in_menu) { // sorta-nasty hack: flashes should still draw if we're // drawing the menu. if (screenflash_duration > time) HUD_Screenflash(); Draw_Menu(); setlocaluserinfo(0, "in_menu", "1"); if (gamepad_enabled) buttons[20].gray_out = true; else buttons[20].gray_out = false; } else { HUD_Draw(g_width, g_height); Chat_Draw(); setlocaluserinfo(0, "in_menu", "0"); } }; noref float(string cmd) CSQC_ConsoleCommand = { //self = theplayer; //if (!self) // return FALSE; tokenize(cmd); switch(argv(0)) { case "togglemenu": ToggleMenu(); return TRUE; case "map": return FALSE; case "startwalk": walk = TRUE; return FALSE; case "stopwalk": walk = FALSE; return FALSE; case "promptjoin": menu_join(); return TRUE; case "showscores": if (score_show) score_show = FALSE; else score_show = TRUE; return TRUE; default: return FALSE; } return FALSE; }; //**********************************************************************// // Input_Movecheck // // // // Called at InputEvent and allows to set var if key is at that state // // NOTE: ALL movekeys are called in order to prevent unsetting keys // //**********************************************************************// void(float scanx, float setval) Input_Movecheck = { tokenize(findkeysforcommand("+moveleft")); if (scanx == stof(argv(0))) K_LEFTDOWN = setval; tokenize(findkeysforcommand("+moveright")); if (scanx == stof(argv(0))) K_RIGHTDOWN = setval; tokenize(findkeysforcommand("+forward")); if (scanx == stof(argv(0))) K_FORWARDDOWN = setval; tokenize(findkeysforcommand("+back")); if (scanx == stof(argv(0))) K_BACKDOWN = setval; } void(float button, string key) setToBind = { local string fullbind, unbind, oldkey; local string btn; editBind[button] = FALSE; btn = buttonBind[button]; tokenize(findkeysforcommandex(btn)); oldkey = argv(0); unbind = strcat("bind ", oldkey, " null\n"); fullbind = strcat("bind ", key, " \"", btn, "\"\n"); localcmd(unbind); localcmd(fullbind); } noref float(float evtype, float scanx, float chary, float devid) CSQC_InputEvent = { last_input_deviceid = devid; if (evtype == IE_KEYDOWN) { switch (scanx) { case K_GP_A: case K_GP_B: case K_GP_X: case K_GP_Y: case K_GP_LSHOULDER: case K_GP_RSHOULDER: case K_GP_LTRIGGER: case K_GP_RTRIGGER: case K_GP_BACK: case K_GP_START: case K_GP_LTHUMB: case K_GP_RTHUMB: case K_GP_DPAD_UP: case K_GP_DPAD_DOWN: case K_GP_DPAD_LEFT: case K_GP_DPAD_RIGHT: case K_GP_LTHUMB_UP: case K_GP_LTHUMB_DOWN: case K_GP_LTHUMB_LEFT: case K_GP_LTHUMB_RIGHT: case K_GP_RTHUMB_UP: case K_GP_RTHUMB_DOWN: case K_GP_RTHUMB_LEFT: case K_GP_RTHUMB_RIGHT: if (!GPButtonHeldBeginTime[scanx-816]) GPButtonHeldBeginTime[scanx-816] = cltime; break; } } if (evtype == IE_KEYUP) { switch (scanx) { case K_GP_A: case K_GP_B: case K_GP_X: case K_GP_Y: case K_GP_LSHOULDER: case K_GP_RSHOULDER: case K_GP_LTRIGGER: case K_GP_RTRIGGER: case K_GP_BACK: case K_GP_START: case K_GP_LTHUMB: case K_GP_RTHUMB: case K_GP_DPAD_UP: case K_GP_DPAD_DOWN: case K_GP_DPAD_LEFT: case K_GP_DPAD_RIGHT: case K_GP_LTHUMB_UP: case K_GP_LTHUMB_DOWN: case K_GP_LTHUMB_LEFT: case K_GP_LTHUMB_RIGHT: case K_GP_RTHUMB_UP: case K_GP_RTHUMB_DOWN: case K_GP_RTHUMB_LEFT: case K_GP_RTHUMB_RIGHT: //print(sprintf("Key: %f Duration: %f\n", scanx, cltime-GPButtonHeldBeginTime[scanx-816])); GPButtonHeldBeginTime[scanx-816] = 0; break; } } switch(evtype) { case IE_KEYDOWN: if(in_menu != MENU_NONE) { if(scanx == K_MOUSE1) { Menu_Click(0); if (in_menu == MENU_CUSTOMS) Menu_Click_Custom(); return TRUE; } else if (scanx == K_MOUSE2) { Menu_Click(1); return TRUE; } if (in_menu == MENU_CSETTINGS) { for (int i = 0; i < MAX_BINDS; i += 1) { if (editBind[i] == TRUE) setToBind(i, keynumtostring(scanx)); } } if (in_menu == MENU_MULTI) { // GO AWAY! if (scanx == K_ENTER || scanx == K_ESCAPE) { editing_player_name = editing_server_id = editing_password = editing_hostname = false; return FALSE; } if (editing_player_name == true) { // Update the temp string. temp_player_name = GetUserInput(temp_player_name, scanx, chary, 18); // Always append. cvar_set("name", temp_player_name); // No, I don't want to read binds. Thanks. return TRUE; } } else if (in_menu == MENU_CREATE || in_menu == MENU_JOIN) { // GO AWAY! if (scanx == K_ENTER || scanx == K_ESCAPE) { editing_player_name = editing_server_id = editing_password = false; return FALSE; } // Server IP if (editing_server_id == true) { // Update the temp string. temp_server_name = GetUserInput(temp_server_name, scanx, chary, 18); // No, I don't want to read binds. Thanks. return TRUE; } // Password if (editing_password == true) { // Update the temp string. temp_password = GetUserInput(temp_password, scanx, chary, 18); // Always append. cvar_set("password", temp_password); // No, I don't want to read binds. Thanks. return TRUE; } // Hostname if (editing_hostname == true) { // Update the temp string. temp_hostname = GetUserInput(temp_hostname, scanx, chary, 18); // Always append. cvar_set("hostname", temp_hostname); // No, I don't want to read binds. Thanks. return TRUE; } } } Input_Movecheck(scanx, 1); return FALSE; case IE_KEYUP: Input_Movecheck(scanx, 0); return FALSE; case IE_MOUSEDELTA: return FALSE; case IE_MOUSEABS: //if(devid != 0 && devid != 1) // return FALSE; cursor_pos_x = scanx; cursor_pos_y = chary; return FALSE; } return FALSE; }; noref void() CSQC_Input_Frame = { input_angles += gun_kick; input_angles += sniper_sway; } #define DEG2RAD(x) (x * M_PI / 180.f) noref void() CSQC_Parse_Event = { float event_type = readbyte(); switch (event_type) { case CSQC_EVENT_PARTICLE: float particle_type = readbyte(); float part_pos_x = readcoord(); float part_pos_y = readcoord(); float part_pos_z = readcoord(); float part_optional = readbyte(); float part_entity = readentitynum(); vector particle_pos = [part_pos_x, part_pos_y, part_pos_z]; Particles_RunParticle(particle_type, particle_pos, part_optional, part_entity); break; case CSQC_EVENT_USEPRINT: useprint_type = readbyte(); useprint_cost = readshort(); useprint_weapon = readbyte(); useprint_time = time + 0.1; break; case CSQC_EVENT_ROUNDCHANGE: rounds = readbyte(); HUD_Change_time = time + 6; break; case CSQC_EVENT_BROADCASTMESSAGE: broadcast_type = readbyte(); broadcast_time = time + readbyte(); broadcast_num = readbyte(); break; case CSQC_EVENT_MAXAMMOTEXT: hud_maxammo_endtime = time + 2; hud_maxammo_starttime = time; break; case CSQC_EVENT_MUSICSTREAM: string track_name = readstring(); string track_extension; if (platform_is_web) track_extension = ".wav"; else track_extension = ".ogg"; localsound_enhanced(strcat("tracks/", track_name, track_extension), CHAN_MUSIC, 0.85); break; case CSQC_EVENT_GIVEACHIEVEMENT: float achievement_id = readbyte(); // Don't unlock an existing achievement or any if on emscripten. if (achievements[achievement_id].unlocked || platform_is_web) return; Achievement_Unlock(achievement_id); break; case CSQC_EVENT_PLAYERNAME: character_name = readstring(); break; case CSQC_EVENT_SCREENFLASH: screenflash_color = readbyte(); screenflash_duration = time + readbyte(); screenflash_type = readbyte(); screenflash_worktime = 0; screenflash_starttime = time; break; case CSQC_EVENT_RUMBLE: float low_frequency = readshort(); float high_frequency = readshort(); float duration = readshort(); if (cvar("in_rumbleenabled")) gp_rumble(last_input_deviceid, low_frequency, high_frequency, duration); break; case EVENT_WEAPONRECOIL: local vector rec; rec_x = readcoord()/5; rec_y = readcoord()/5; rec_z = readcoord()/5; gun_kick += rec; break; case EVENT_CHATMESSAGE: int sender = readbyte(); int player_id = readbyte(); string message = readstring(); Chat_Register(sender, player_id, message); break; case EVENT_ENDGAME: game_over = true; break; case EVENT_DOUBLETAPUPDATE: double_tap_version = readbyte(); break; case EVENT_UPDATE: float updatetype = readbyte(); float var_1 = readbyte(); float var_2 = readbyte(); float var_3 = readbyte(); switch (updatetype) { case 1: HUD_Change_time = time + var_1; break; case 2: rounds_change = var_1; break; case 3: if (Hitmark_time < time) Hitmark_time = time + 0.2; break; case 5: crosshair_spread_time = time + 70/getWeaponRecoilReturn(getstatf(STAT_ACTIVEWEAPON)); break; default: break; } break; case EVENT_REVIVECHANGE: float revivechange_player_index = readbyte() - 1; // playernum starts at one. float state = readbyte(); revive_icons[revivechange_player_index].state = state; break; case EVENT_REVIVEON: float reviveon_player_index = readbyte() - 1; // playernum starts at one. revive_icons[reviveon_player_index].state = 1; revive_icons[reviveon_player_index].draw = true; break; case EVENT_REVIVEOFF: float reviveoff_player_index = readbyte() - 1; // playernum starts at one. revive_icons[reviveoff_player_index].state = 0; revive_icons[reviveoff_player_index].timer = 0; revive_icons[reviveoff_player_index].draw = false; break; case EVENT_MAPTYPE: map_compatibility_mode = readbyte(); break; case EVENT_WORLDDATA: chaptertitle = readstring(); location = readstring(); date = readstring(); person = readstring(); if (chaptertitle == "") chaptertitle = "'Nazi Zombies'"; break; case EVENT_PLAYERUPDATE: player_count = readbyte(); break; case EVENT_GRENADEPULSE: crosshair_pulse_grenade = true; break; case EVENT_BETTYPROMPT: bettyprompt_time = time + 4; break; } }