diff --git a/source/client/defs/fte.qc b/source/client/defs/fte.qc index c9435a0..4ffd191 100644 --- a/source/client/defs/fte.qc +++ b/source/client/defs/fte.qc @@ -2093,7 +2093,7 @@ vector(entity e, float s, vector p) getsurfaceclippedpoint = #439; /* Part of DP float(string s) tokenize = #441; /* Part of KRIMZON_SV_PARSECLIENTCOMMAND*/ string(float n) argv = #442; /* Part of KRIMZON_SV_PARSECLIENTCOMMAND*/ void(entity e, entity tagentity, string tagname) setattachment = #443; /* Part of DP_GFX_QUAKE3MODELTAGS*/ -searchhandle(string pattern, enumflags:float{SB_CASEINSENSITIVE=1<<0,SB_FULLPACKAGEPATH=1<<1,SB_ALLOWDUPES=1<<2,SB_FORCESEARCH=1<<3} flags, float quiet, optional string filterpackage) search_begin = #444; /* Part of DP_QC_FS_SEARCH, DP_QC_FS_SEARCH_PACKFILE +searchhandle(string pattern, enumflags:float{SB_CASEINSENSITIVE=1<<0,SB_FULLPACKAGEPATH=1<<1,SB_ALLOWDUPES=1<<2,SB_FORCESEARCH=1<<3,SB_MULTISEARCH=1<<4} flags, float quiet, optional string filterpackage) search_begin = #444; /* Part of DP_QC_FS_SEARCH, DP_QC_FS_SEARCH_PACKFILE initiate a filesystem scan based upon filenames. Be sure to call search_end on the returned handle. SB_FULLPACKAGEPATH interprets the filterpackage arg as a full package path to avoid gamedir ambiguity, equivelent to whichpack's WP_FULLPACKAGEPATH flag. SB_ALLOWDUPES allows returning multiple entries with the same name (but different package, useful with search_fopen). SB_FORCESEARCH requires use of the filterpackage and SB_FULLPACKAGEPATH flag, initiating searches from gamedirs/packages which are not currently active. */ void(searchhandle handle) search_end = #445; /* Part of DP_QC_FS_SEARCH, DP_QC_FS_SEARCH_PACKFILE*/ diff --git a/source/client/hud.qc b/source/client/hud.qc index 860ac7a..92122c9 100644 --- a/source/client/hud.qc +++ b/source/client/hud.qc @@ -779,6 +779,7 @@ void(float width, float height) HUD_Rounds = else if (rounds_change == 7)//blink white while fading back { stopwatch_round_isactive = true; + stopwatch_round_hr = stopwatch_round_min = stopwatch_round_sec = 0; if (!color_shift_init) { color_shift_end[0] = 107; diff --git a/source/client/main.qc b/source/client/main.qc index a1997a6..ea5fc5e 100644 --- a/source/client/main.qc +++ b/source/client/main.qc @@ -25,6 +25,8 @@ */ +float need_vid_reload; + void() ToggleMenu = { if (serverkey("constate") != "disconnected") @@ -65,6 +67,95 @@ float(float isnew) SetZombieSkinning = 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"); @@ -150,10 +241,19 @@ noref void(float apiver, string enginename, float enginever) CSQC_Init = } 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; diff --git a/source/server/ai/fte/waypoints_core.qc b/source/server/ai/fte/waypoints_core.qc index 5e15eea..7f50908 100644 --- a/source/server/ai/fte/waypoints_core.qc +++ b/source/server/ai/fte/waypoints_core.qc @@ -218,9 +218,9 @@ float Link (entity from, entity to) { float i; entity tempe; for (i = 0; i < MAX_WAY_TARGETS; i++) { - tempe = find (world, waynum, from.targets[i]); + tempe = find(world, waynum, from.targets[i]); - if (tempe == world || tempe == to) { + if (tempe == world || !from.targets[i]) { from.targets[i] = to.waynum; bprint(PRINT_HIGH, "Linked waypoint "); bprint(PRINT_HIGH, to.waynum); @@ -253,24 +253,38 @@ void () Link_Waypoints = return; if (Waypoint_Linked_To(current_way, active_way)) { - bprint(PRINT_HIGH, "These waypoints are already linked!\n"); + bprint(PRINT_HIGH, "[INFO]: These waypoints are already linked!\n"); return; } float i; entity tempe; for (i = 0; i < MAX_WAY_TARGETS; i++) { - tempe = findfloat (world, waynum, active_way.targets[i]); - - if (tempe == world) { - if (Link(active_way, current_way)) { + // First pass - if the target field is blank no extra + // validation is needed, just link. + if (!active_way.targets[i]) { + if (Link(active_way, current_way)) return; - } + + bprint(PRINT_HIGH, "[INFO]: Got Linkable Waypoints but Linking failed!\n"); + return; + } + + // Second pass - if the targets field is occupied + // check if the waypoint still exists, link if it + // does not. + if (find(world, waynum, active_way.targets[i]) == world) { + bprint(PRINT_HIGH, sprintf("[INFO]: Found Waypoint ID %s but not entity, overwriting link..\n", active_way.targets[i])); + + if (Link(active_way, current_way)) + return; + + bprint(PRINT_HIGH, " ..Failed!\n"); + return; } } - - bprint(PRINT_HIGH, "no targets remaining!\n"); + bprint(PRINT_HIGH, "[INFO]: All Waypoint links occupied for this Waypoint!\n"); } diff --git a/source/server/defs/custom.qc b/source/server/defs/custom.qc index 8203253..4fb17d6 100644 --- a/source/server/defs/custom.qc +++ b/source/server/defs/custom.qc @@ -225,7 +225,7 @@ var struct guninventory_struct //Knife .float knife_delay; -.float bowie; +.float has_bowie_knife; //Grenades .float grenades; diff --git a/source/server/entities/perk_a_cola.qc b/source/server/entities/perk_a_cola.qc index ada2836..c0f07ab 100644 --- a/source/server/entities/perk_a_cola.qc +++ b/source/server/entities/perk_a_cola.qc @@ -643,7 +643,8 @@ void() Perk_RandomDecide = } // Choose which of the eight machines we want to become, if permitted. - while (true) { + float num_attempts; + for(num_attempts = 64; num_attempts > 0; num_attempts--) { i = random(); // Quick Revive if (i < (1/8) && (self.spawnflags & SPAWNFLAG_PERKRANDOM_QUICKREVIVE)) { @@ -694,6 +695,9 @@ void() Perk_RandomDecide = break; } } + + if (!num_attempts) + objerror("perk_random: Failed 64 times to pick a Perk-A-Cola. Bailing!\n"); }; void() Perk_RandomPrecaches = diff --git a/source/server/entities/wall_weapon.qc b/source/server/entities/wall_weapon.qc index 51d5b7c..aa6759c 100644 --- a/source/server/entities/wall_weapon.qc +++ b/source/server/entities/wall_weapon.qc @@ -202,10 +202,7 @@ void () WallWeapon_TouchTrigger = Player_RemoveScore(other, wcost); if (self.enemy) { - oldent = self; - self = self.enemy; - self.use(); - self = oldent; + SUB_UseTargets(); } } } else @@ -247,10 +244,7 @@ void () WallWeapon_TouchTrigger = Player_RemoveScore(other, wcost); if (self.enemy) { - oldent = self; - self = self.enemy; - self.use(); - self = oldent; + SUB_UseTargets(); } } } @@ -282,10 +276,7 @@ void () WallWeapon_TouchTrigger = nzp_bettyprompt(other); if (self.enemy) { - oldent = self; - self = self.enemy; - self.use(); - self = oldent; + SUB_UseTargets(); } } other.semi_actions |= SEMIACTION_USE; @@ -315,10 +306,7 @@ void () WallWeapon_TouchTrigger = other.primary_grenades = 4; if (self.enemy) { - oldent = self; - self = self.enemy; - self.use(); - self = oldent; + SUB_UseTargets(); } } other.semi_actions |= SEMIACTION_USE; @@ -326,7 +314,7 @@ void () WallWeapon_TouchTrigger = } else if (self.weapon == W_BOWIE) { - if (!other.bowie) { + if (!other.has_bowie_knife) { useprint(other, 4, self.cost2, self.weapon); if (other.button7) { @@ -341,16 +329,13 @@ void () WallWeapon_TouchTrigger = other.ach_tracker_coll++; if (self.enemy) { - oldent = self; - self = self.enemy; - self.use(); - self = oldent; + SUB_UseTargets(); } entity tempz; tempz = self; self = other; Set_W_Frame(15, 30, 2.75, 0, 0, W_PlayTakeOut, "models/weapons/knife/v_bowie.mdl", false, S_BOTH, true); - self.bowie = 1; + self.has_bowie_knife = true; } } } @@ -378,10 +363,7 @@ void () WallWeapon_TouchTrigger = other.reload_delay = 0; Player_RemoveScore(other, self.cost); if (self.enemy) { - oldent = self; - self = self.enemy; - self.use(); - self = oldent; + SUB_UseTargets(); } tempe = self; self = other; diff --git a/source/server/main.qc b/source/server/main.qc index 439e73d..d3e5b71 100644 --- a/source/server/main.qc +++ b/source/server/main.qc @@ -395,6 +395,23 @@ void() worldspawn = G_WORLDTEXT = 1; G_PERKS = 0; G_PERKPOWER = 0; + + // + // World Gravity Multiplier + // + + // FIXME: This should technically poll sv_gravity but + // we do not currently enforce a default that resets + // on a new server. + float sv_gravity = 800; + + // worldspawn "gravity" field is a multiplier to be applied + // to sv_gravity. + if (!world.gravity) world.gravity = 1; + world.gravity = clamp(world.gravity, 0.1, 2); + + // Update the cvar for the server. + cvar_set("sv_gravity", ftos(sv_gravity * world.gravity)); } void() SpectatorConnect = diff --git a/source/server/player/player_core.qc b/source/server/player/player_core.qc index 622895b..e101d8e 100644 --- a/source/server/player/player_core.qc +++ b/source/server/player/player_core.qc @@ -926,7 +926,8 @@ void() PlayerSpawn = setsize(self, PLAYER_MINS_STANDING, PLAYER_MAXS_STANDING); } - self.stance = 2; + self.has_bowie_knife = false; + self.stance = PLAYER_STANCE_STAND; self.new_ofs_z = self.view_ofs_z; self.oldz = self.origin_z; self.grenades = self.grenades | 1; // add frag grenades to player inventory diff --git a/source/server/weapons/weapon_core.qc b/source/server/weapons/weapon_core.qc index 61b4124..7937b2d 100644 --- a/source/server/weapons/weapon_core.qc +++ b/source/server/weapons/weapon_core.qc @@ -1268,17 +1268,17 @@ void() WeaponCore_Melee = // Apply damage to the entity. if (trace_ent.takedamage) { - float melee_damage = WepDef_CalculateMeleeDamage(self.weapon, self.bowie); + float melee_damage = WepDef_CalculateMeleeDamage(self.weapon, self.has_bowie_knife); DamageHandler (trace_ent, self, melee_damage, DMG_TYPE_MELEE); } } } // Grab animation stats for our melee weapon. - float start_frame = WepDef_GetMeleeFirstFrame(self.weapon, did_lunge, self.bowie); - float end_frame = WepDef_GetMeleeLastFrame(self.weapon, did_lunge, self.bowie); - float anim_duration = WepDef_GetMeleeAnimDuration(self.weapon, did_lunge, self.bowie); - string model_path = WepDef_GetMeleeModel(self.weapon, self.bowie); + float start_frame = WepDef_GetMeleeFirstFrame(self.weapon, did_lunge, self.has_bowie_knife); + float end_frame = WepDef_GetMeleeLastFrame(self.weapon, did_lunge, self.has_bowie_knife); + float anim_duration = WepDef_GetMeleeAnimDuration(self.weapon, did_lunge, self.has_bowie_knife); + string model_path = WepDef_GetMeleeModel(self.weapon, self.has_bowie_knife); // Ensure we play the Take Out animation if the melee model is not our active weapon void() end_func; diff --git a/source/shared/weapon_stats.qc b/source/shared/weapon_stats.qc index 3c0f809..dfd51ac 100644 --- a/source/shared/weapon_stats.qc +++ b/source/shared/weapon_stats.qc @@ -1550,7 +1550,7 @@ vector GetWeaponADSOfs(float wep) = { case W_COLT: case W_BIATCH: - return [-5479.2, 1850, -4000]; + return [-3479.2, 1050, -4000]; case W_KAR: case W_ARMAGEDDON: return [-5495.9, 3006.9, -6000]; @@ -1928,7 +1928,7 @@ float(float wep, float frametype, optional float z) GetFrame = case SPRINT_OUT_END: return 31; case TAKE_OUT_START: - return 38; + return 37; case TAKE_OUT_END: return 41; case PUT_OUT_START: