@@ -47,13 +47,6 @@ void() ToggleMenu =
4747 }
4848}
4949
50- float (float isnew) SetZombieSkinning =
51- {
52- self. drawmask = MASK_ENGINE;
53- setcustomskin(self, __NULL__, sprintf("replace \"\" \" models/ai/zfull.mdl_%d.pcx\"\n " , self. skin));
54- return PREDRAW_NEXT;
55- };
56-
5750//
5851// GenerateAlphaTransparencyQ3Shaders()
5952// What a mouth-full! Anyway, NZ:P supports
@@ -720,6 +713,180 @@ void() Camera_SniperSway =
720713
721714float gamepad_enabled;
722715
716+ vector cam_snap_difference;
717+ float pressed_zoom_in;
718+
719+ #define CAMERA_AIM_SNAP_MAX_DIST 1024 // hard cutoff (quake units)
720+
721+ // score weighting (higher DOT_WEIGHT = more center preference)
722+ #define CAMERA_AIM_SNAP_DOT_WEIGHT 2.0
723+ #define CAMERA_AIM_SNAP_DIST_WEIGHT 1.0
724+
725+ #define CAMERA_AIM_SNAP_NEAR_DIST 128
726+ #define CAMERA_AIM_SNAP_FAR_DIST 1024
727+
728+ #define CAMERA_AIM_SNAP_NEAR_DOT 0.980 // yaw cone, near (looser)
729+ #define CAMERA_AIM_SNAP_FAR_DOT 0.9998 // yaw cone, far (tighter)
730+
731+ #define CAMERA_AIM_SNAP_NEAR_PITCH 13 // near = allow more vertical slop
732+ #define CAMERA_AIM_SNAP_FAR_PITCH 8 // far = be stricter
733+
734+ float Camera_InFront(vector ent1_origin, vector ent2_origin, vector client_view_angles)
735+ {
736+ vector to3d = ent2_origin - ent1_origin;
737+
738+ // Distance for scaling (use horizontal distance so stairs/height don't distort t)
739+ vector to2d = to3d;
740+ to2d[2 ] = 0 ;
741+
742+ float dist = vlen(to2d);
743+ if (dist < 1 )
744+ return true;
745+
746+ // normalize distance t in [0..1]
747+ float t = (dist - CAMERA_AIM_SNAP_NEAR_DIST)
748+ / (CAMERA_AIM_SNAP_FAR_DIST - CAMERA_AIM_SNAP_NEAR_DIST);
749+ if (t < 0 ) t = 0 ;
750+ if (t > 1 ) t = 1 ;
751+
752+ vector yaw_angles = client_view_angles;
753+ yaw_angles[0 ] = 0 ;
754+ yaw_angles[2 ] = 0 ;
755+ makevectors(yaw_angles);
756+
757+ vector dir2d = normalize(to2d);
758+ float dotp = dotproduct(dir2d, v_forward);
759+
760+ float yaw_threshold = CAMERA_AIM_SNAP_NEAR_DOT
761+ + t * (CAMERA_AIM_SNAP_FAR_DOT - CAMERA_AIM_SNAP_NEAR_DOT);
762+
763+ if (dotp <= yaw_threshold)
764+ return false;
765+
766+ vector dir3d = normalize(to3d);
767+ vector targ_ang = vectoangles(dir3d);
768+ targ_ang[0 ] *= -1 ;
769+
770+ float pitch_tol = CAMERA_AIM_SNAP_NEAR_PITCH
771+ + t * (CAMERA_AIM_SNAP_FAR_PITCH - CAMERA_AIM_SNAP_NEAR_PITCH);
772+
773+ float dpitch = fabs(angledelta(targ_ang[0 ] - client_view_angles[0 ]));
774+ if (dpitch > pitch_tol)
775+ return false;
776+
777+ return true;
778+ }
779+
780+ float Camera_YawDot(vector from_origin, vector to_origin, vector client_view_angles)
781+ {
782+ vector to = to_origin - from_origin;
783+ to[2 ] = 0 ;
784+
785+ if (vlen(to) < 1 )
786+ return 1 ;
787+
788+ vector dir = normalize(to);
789+
790+ vector yaw_angles = client_view_angles;
791+ yaw_angles[0 ] = 0 ;
792+ yaw_angles[2 ] = 0 ;
793+ makevectors(yaw_angles);
794+
795+ return dotproduct(dir, v_forward);
796+ }
797+
798+ void (vector client_view_angles) Camera_AimSnap =
799+ {
800+ float zoom_value = getstatf(STAT_WEAPONZOOM);
801+
802+ if (! (gamepad_enabled && cvar("in_aimassist" ) == 1 ))
803+ return ;
804+
805+ if (zoom_value == 1 || zoom_value == 2 ) {
806+ if (pressed_zoom_in == false) {
807+ pressed_zoom_in = true;
808+ } else {
809+ return ;
810+ }
811+ } else {
812+ pressed_zoom_in = false;
813+ return ;
814+ }
815+
816+ //print("snap start\n");
817+
818+ entity best_ai = world;
819+ float best_score = -999999 ;
820+ vector best_target_origin = [0 , 0 , 0 ];
821+
822+ vector client_origin = getproperty(VF_ORIGIN);
823+
824+ // Iterate over all of our AI meshes.
825+ for (int i = 0 ; i < zombie_snap_struct. length; i++ ) {
826+ float model_index = getmodelindex(zombie_snap_struct[i]. model_path);
827+
828+ entity snap_ai = findfloat(world, modelindex, model_index);
829+ while (snap_ai != world) {
830+
831+ // use the same target point for gating + snapping
832+ vector snap_offset = (getstatf(STAT_PERKS) & P_DEAD) ? zombie_snap_struct[i]. deadshot_snap_offset : zombie_snap_struct[i]. snap_offset;
833+ vector target_origin = snap_ai. origin + snap_offset;
834+
835+ // Hard max distance cutoff
836+ float dist = vlen(target_origin - client_origin);
837+ if (dist > CAMERA_AIM_SNAP_MAX_DIST) {
838+ snap_ai = findfloat(snap_ai, modelindex, model_index);
839+ continue ;
840+ }
841+
842+ // Gate: in-front + pitch sanity
843+ if (Camera_InFront(client_origin, target_origin, client_view_angles)) {
844+
845+ // LOS must be clear
846+ traceline(target_origin, client_origin, true, world);
847+ if (trace_fraction >= 1 ) {
848+ float dotp = Camera_YawDot(client_origin, target_origin, client_view_angles);
849+
850+ float dist01 = 1 - (dist / CAMERA_AIM_SNAP_MAX_DIST);
851+ if (dist01 < 0 ) dist01 = 0 ;
852+
853+ // prefer centered, then closeness
854+ float score = (dotp * CAMERA_AIM_SNAP_DOT_WEIGHT)
855+ + (dist01 * CAMERA_AIM_SNAP_DIST_WEIGHT);
856+
857+ // Pick best
858+ if (score > best_score) {
859+ best_score = score;
860+ best_ai = snap_ai;
861+ best_target_origin = target_origin;
862+ //print(sprintf("best score=%f dot=%f dist=%f org=[%v]\n", score, dotp, dist, target_origin));
863+ }
864+ }
865+ }
866+
867+ snap_ai = findfloat(snap_ai, modelindex, model_index);
868+ }
869+ }
870+
871+ if (best_ai != world) {
872+ vector distance_vector = best_target_origin - client_origin;
873+ distance_vector = normalize(distance_vector);
874+
875+ vector distance_angles = vectoangles(distance_vector);
876+ distance_angles[0 ] += (distance_angles[0 ] > 180 ) ? -360 : 0 ;
877+ distance_angles[0 ] *= -1 ;
878+
879+ if (distance_angles[0 ] < -70 || distance_angles[0 ] > 80 )
880+ return ;
881+
882+ cam_snap_difference = distance_angles - client_view_angles;
883+ //print(sprintf("cam_snap_difference: [%v]\n", cam_snap_difference));
884+ }
885+
886+ //print("snap end\n");
887+ };
888+
889+
723890// CALLED EVERY CLIENT RENDER FRAME
724891float pap_flash_alternate;
725892noref void (float width, float height, float menushown) CSQC_UpdateView =
@@ -799,6 +966,12 @@ noref void(float width, float height, float menushown) CSQC_UpdateView =
799966 camang[1 ] += sniper_sway[1 ];
800967 camang[2 ] += sniper_sway[2 ];
801968
969+ Camera_AimSnap(camang);
970+
971+ camang[0 ] += cam_snap_difference[0 ];
972+ camang[1 ] += cam_snap_difference[1 ];
973+ camang[2 ] += cam_snap_difference[2 ];
974+
802975 setviewprop(VF_ANGLES, camang);
803976
804977 if (cvar("chase_active" )) {
@@ -1020,6 +1193,7 @@ noref void() CSQC_Input_Frame =
10201193{
10211194 input_angles += gun_kick;
10221195 input_angles += sniper_sway;
1196+ input_angles += cam_snap_difference;
10231197}
10241198
10251199#define DEG2RAD(x) (x * M_PI / 180.f)
0 commit comments