From: Mario Date: Wed, 4 Feb 2015 16:38:30 +0000 (+1100) Subject: 435 conflicts, 135 files conflicts, fixed in 7 hours #nolife X-Git-Url: https://git.rm.cloudns.org/?a=commitdiff_plain;h=af31a0377d1aa0e1f84bfda4a1584c4af9a66b40;p=xonotic%2Fxonotic-data.pk3dir.git 435 conflicts, 135 files conflicts, fixed in 7 hours #nolife --- af31a0377d1aa0e1f84bfda4a1584c4af9a66b40 diff --cc qcsrc/client/announcer.qc index e77a54431,116dba7d3..ad3dbb9d6 --- a/qcsrc/client/announcer.qc +++ b/qcsrc/client/announcer.qc @@@ -1,22 -1,16 +1,33 @@@ - float announcer_1min; - float announcer_5min; + #if defined(CSQC) + #include "../dpdefs/csprogsdefs.qh" + #include "../common/stats.qh" + #include "../common/util.qh" + #include "autocvars.qh" + #include "../common/notifications.qh" + #include "main.qh" + #elif defined(MENUQC) + #elif defined(SVQC) + #endif + + bool announcer_1min; + bool announcer_5min; +string AnnouncerOption() +{ + if(autocvar_cl_announcer_force || sv_announcer == "" || autocvar_cl_announcer != "default") { return autocvar_cl_announcer; } + + return sv_announcer; // use server side announcer if available +} + +void Announcer_Precache() +{ +#define MSG_ANNCE_NOTIF(default,name,channel,sound,volume,position) \ + precache_sound(sprintf("announcer/%s/%s.wav", AnnouncerOption(), sound)); + + MSG_ANNCE_NOTIFICATIONS + +#undef MSG_ANNCE_NOTIF +} + void Announcer_Countdown() { float starttime = getstatf(STAT_GAMESTARTTIME); diff --cc qcsrc/client/announcer.qh index 000000000,000000000..c8b81a1d6 new file mode 100644 --- /dev/null +++ b/qcsrc/client/announcer.qh @@@ -1,0 -1,0 +1,7 @@@ ++#ifndef ANNOUNCER_H ++#define ANNOUNCER_H ++ ++string AnnouncerOption(); ++void Announcer_Precache(); ++ ++#endif diff --cc qcsrc/client/autocvars.qh index c799cb2b1,e35bf82dd..013a1179f --- a/qcsrc/client/autocvars.qh +++ b/qcsrc/client/autocvars.qh @@@ -16,70 -19,70 +19,70 @@@ float autocvar_camera_speed_attenuation float autocvar_camera_speed_chase; float autocvar_camera_speed_free; float autocvar_camera_speed_roll; - float autocvar_chase_active; - float autocvar_cl_allow_uid2name; + int autocvar_chase_active; + int autocvar_cl_allow_uid2name; string autocvar_cl_announcer; - var float autocvar_cl_announcer_antispam = 2; - var float autocvar_cl_announcer_maptime = 3; - float autocvar_cl_announcer_force; - float autocvar_cl_autodemo_delete; - float autocvar_cl_autodemo_delete_keeprecords; - float autocvar_cl_casings; ++bool autocvar_cl_announcer_force; + float autocvar_cl_announcer_antispam = 2; + float autocvar_cl_announcer_maptime = 3; + bool autocvar_cl_autodemo_delete; + bool autocvar_cl_autodemo_delete_keeprecords; + bool autocvar_cl_casings; float autocvar_cl_casings_bronze_time; - var float autocvar_cl_casings_maxcount = 100; + int autocvar_cl_casings_maxcount = 100; float autocvar_cl_casings_shell_time; - var float autocvar_cl_casings_sloppy = 1; - var float autocvar_cl_casings_ticrate = 0.1; - float autocvar_cl_db_saveasdump; - float autocvar_cl_deathscoreboard; + bool autocvar_cl_casings_sloppy = 1; + float autocvar_cl_casings_ticrate = 0.1; + bool autocvar_cl_db_saveasdump; + bool autocvar_cl_deathscoreboard; float autocvar_cl_effects_lightningarc_branchfactor_add; float autocvar_cl_effects_lightningarc_branchfactor_start; float autocvar_cl_effects_lightningarc_drift_end; float autocvar_cl_effects_lightningarc_drift_start; float autocvar_cl_effects_lightningarc_segmentlength; - float autocvar_cl_effects_lightningarc_simple; - float autocvar_cl_gentle; - float autocvar_cl_gentle_damage; - float autocvar_cl_gentle_gibs; - float autocvar_cl_gentle_messages; - var float autocvar_cl_gibs_damageforcescale = 3.5; - var float autocvar_cl_gibs_lifetime = 14; - var float autocvar_cl_gibs_maxcount = 100; - var float autocvar_cl_gibs_sloppy = 1; - var float autocvar_cl_gibs_ticrate = 0.1; - var float autocvar_cl_gibs_velocity_random = 1; - var float autocvar_cl_gibs_velocity_scale = 1; - var float autocvar_cl_gibs_avelocity_scale = 1; + bool autocvar_cl_effects_lightningarc_simple; + int autocvar_cl_gentle; + int autocvar_cl_gentle_damage; + int autocvar_cl_gentle_gibs; + int autocvar_cl_gentle_messages; + float autocvar_cl_gibs_damageforcescale = 3.5; + float autocvar_cl_gibs_lifetime = 14; + float autocvar_cl_gibs_maxcount = 100; + bool autocvar_cl_gibs_sloppy = 1; + float autocvar_cl_gibs_ticrate = 0.1; + float autocvar_cl_gibs_velocity_random = 1; + float autocvar_cl_gibs_velocity_scale = 1; + float autocvar_cl_gibs_avelocity_scale = 1; float autocvar_cl_gibs_velocity_up; - float autocvar_cl_gunalign; - float autocvar_cl_hidewaypoints; - float autocvar_cl_lockview; - float autocvar_cl_nogibs; - float autocvar_cl_orthoview; - float autocvar_cl_orthoview_nofog; - float autocvar_cl_particlegibs; - float autocvar_cl_particles_oldvortexbeam; - float autocvar_cl_particles_newvortexbeam; ++bool autocvar_cl_particles_newvortexbeam; ++bool autocvar_cl_nexuiz_hook; + int autocvar_cl_gunalign; + bool autocvar_cl_hidewaypoints; + bool autocvar_cl_lockview; + bool autocvar_cl_nogibs; + bool autocvar_cl_orthoview; + bool autocvar_cl_orthoview_nofog; + bool autocvar_cl_particlegibs; + bool autocvar_cl_particles_oldvortexbeam; float autocvar_cl_particles_quality; - float autocvar_cl_projectiles_sloppy; - float autocvar_cl_readpicture_force; - float autocvar_cl_nexuiz_hook; - var float autocvar_cl_reticle = 1; - var float autocvar_cl_reticle_normal_alpha = 1; - var float autocvar_cl_reticle_weapon = 1; - var float autocvar_cl_reticle_weapon_alpha = 1; - float autocvar_cl_reticle_stretch; - float autocvar_cl_spawn_event_particles; - var float autocvar_cl_spawn_event_sound = 1; + bool autocvar_cl_projectiles_sloppy; + bool autocvar_cl_readpicture_force; + bool autocvar_cl_reticle = 1; + float autocvar_cl_reticle_normal_alpha = 1; + bool autocvar_cl_reticle_weapon = 1; + float autocvar_cl_reticle_weapon_alpha = 1; + bool autocvar_cl_reticle_stretch; + bool autocvar_cl_spawn_event_particles; + bool autocvar_cl_spawn_event_sound = 1; // float autocvar_cl_spawn_point_model; - float autocvar_cl_spawn_point_particles; - var float autocvar_cl_spawnzoom = 1; - var float autocvar_cl_spawnzoom_speed = 1; - var float autocvar_cl_spawnzoom_factor = 2; - float autocvar_cl_stripcolorcodes; - float autocvar_cl_velocityzoom_enabled; + bool autocvar_cl_spawn_point_particles; + bool autocvar_cl_spawnzoom = 1; + float autocvar_cl_spawnzoom_speed = 1; + float autocvar_cl_spawnzoom_factor = 2; + bool autocvar_cl_stripcolorcodes; -float autocvar_cl_vehicle_spiderbot_cross_alpha = 0.6; -float autocvar_cl_vehicle_spiderbot_cross_size = 1; -bool autocvar_cl_vehicles_hud_tactical = 1; + bool autocvar_cl_velocityzoom_enabled; float autocvar_cl_velocityzoom_factor; - var float autocvar_cl_velocityzoom_type = 3; + int autocvar_cl_velocityzoom_type = 3; float autocvar_cl_velocityzoom_speed; float autocvar_cl_velocityzoom_time; string autocvar_cl_weaponpriority; @@@ -145,8 -148,7 +148,8 @@@ bool autocvar_crosshair_ring_reload float autocvar_crosshair_ring_reload_alpha; float autocvar_crosshair_ring_reload_size; float autocvar_crosshair_size; - float autocvar_cl_vaporizerbeam; - float autocvar_ekg; ++int autocvar_cl_vaporizerbeam; + int autocvar_ekg; float autocvar_fov; float autocvar_g_balance_damagepush_speedfactor; float autocvar_g_balance_tuba_attenuation; @@@ -284,81 -286,78 +287,80 @@@ float autocvar_hud_panel_physics_accele float autocvar_hud_panel_physics_acceleration_progressbar_scale; float autocvar_hud_panel_physics_acceleration_progressbar_nonlinear; float autocvar_hud_panel_physics_acceleration_max; - float autocvar_hud_panel_physics_progressbar; - float autocvar_hud_panel_physics_acceleration_vertical; - float autocvar_hud_panel_physics_baralign; - float autocvar_hud_panel_physics_flip; + int autocvar_hud_panel_physics_progressbar; + bool autocvar_hud_panel_physics_acceleration_vertical; + int autocvar_hud_panel_physics_baralign; + bool autocvar_hud_panel_physics_flip; float autocvar_hud_panel_physics_speed_max; - float autocvar_hud_panel_physics_speed_unit; - float autocvar_hud_panel_physics_speed_unit_show; - float autocvar_hud_panel_physics_speed_vertical; - float autocvar_hud_panel_physics_text; + int autocvar_hud_panel_physics_speed_unit; + bool autocvar_hud_panel_physics_speed_unit_show; + bool autocvar_hud_panel_physics_speed_vertical; + int autocvar_hud_panel_physics_text; float autocvar_hud_panel_physics_text_scale; - float autocvar_hud_panel_physics_topspeed; + bool autocvar_hud_panel_physics_topspeed; float autocvar_hud_panel_physics_topspeed_time; - float autocvar_hud_panel_powerups; - float autocvar_hud_panel_powerups_baralign; - float autocvar_hud_panel_powerups_flip; - float autocvar_hud_panel_powerups_iconalign; - float autocvar_hud_panel_powerups_progressbar; - var float autocvar_hud_panel_buffs = 1; + bool autocvar_hud_panel_powerups; + int autocvar_hud_panel_powerups_baralign; + bool autocvar_hud_panel_powerups_flip; + int autocvar_hud_panel_powerups_iconalign; + bool autocvar_hud_panel_powerups_progressbar; -bool autocvar_hud_panel_buffs; ++bool autocvar_hud_panel_buffs = 1; //float autocvar_hud_panel_buffs_iconalign; -string autocvar_hud_panel_powerups_progressbar_shield; -string autocvar_hud_panel_powerups_progressbar_strength; string autocvar_hud_panel_powerups_progressbar_superweapons; - float autocvar_hud_panel_powerups_text; - float autocvar_hud_panel_pressedkeys; + bool autocvar_hud_panel_powerups_text; + int autocvar_hud_panel_pressedkeys; float autocvar_hud_panel_pressedkeys_aspect; - float autocvar_hud_panel_pressedkeys_attack; - float autocvar_hud_panel_quickmenu_translatecommands; - var string autocvar_hud_panel_quickmenu_file = "quickmenu.txt"; - var float autocvar_hud_panel_quickmenu_time = 15; - float autocvar_hud_panel_racetimer; - float autocvar_hud_panel_radar; ++bool autocvar_hud_panel_quickmenu_translatecommands; ++string autocvar_hud_panel_quickmenu_file = "quickmenu.txt"; ++float autocvar_hud_panel_quickmenu_time = 15; + bool autocvar_hud_panel_pressedkeys_attack; + bool autocvar_hud_panel_racetimer; + int autocvar_hud_panel_radar; float autocvar_hud_panel_radar_foreground_alpha; float autocvar_hud_panel_radar_maximized_scale; vector autocvar_hud_panel_radar_maximized_size; float autocvar_hud_panel_radar_rotation; float autocvar_hud_panel_radar_scale; - float autocvar_hud_panel_radar_zoommode; + int autocvar_hud_panel_radar_zoommode; float autocvar_hud_panel_radar_maximized_rotation; - float autocvar_hud_panel_radar_maximized_zoommode; - float autocvar_hud_panel_score; - float autocvar_hud_panel_score_rankings; - float autocvar_hud_panel_timer; - float autocvar_hud_panel_timer_increment; + int autocvar_hud_panel_radar_maximized_zoommode; + bool autocvar_hud_panel_score; + bool autocvar_hud_panel_score_rankings; + bool autocvar_hud_panel_timer; + bool autocvar_hud_panel_timer_increment; float autocvar_hud_panel_update_interval; - float autocvar_hud_panel_vote; + bool autocvar_hud_panel_vote; float autocvar_hud_panel_vote_alreadyvoted_alpha; string autocvar_hud_panel_vote_bg_alpha; - float autocvar_hud_panel_weapons; - float autocvar_hud_panel_weapons_accuracy; - float autocvar_hud_panel_weapons_ammo; + bool autocvar_hud_panel_weapons; + bool autocvar_hud_panel_weapons_accuracy; + bool autocvar_hud_panel_weapons_ammo; float autocvar_hud_panel_weapons_ammo_alpha; string autocvar_hud_panel_weapons_ammo_color; - float autocvar_hud_panel_weapons_ammo_full_cells; - var float autocvar_hud_panel_weapons_ammo_full_plasma = 180; - float autocvar_hud_panel_weapons_ammo_full_fuel; - float autocvar_hud_panel_weapons_ammo_full_nails; - float autocvar_hud_panel_weapons_ammo_full_rockets; - float autocvar_hud_panel_weapons_ammo_full_shells; + int autocvar_hud_panel_weapons_ammo_full_cells; -int autocvar_hud_panel_weapons_ammo_full_plasma; ++int autocvar_hud_panel_weapons_ammo_full_plasma = 180; + int autocvar_hud_panel_weapons_ammo_full_fuel; + int autocvar_hud_panel_weapons_ammo_full_nails; + int autocvar_hud_panel_weapons_ammo_full_rockets; + int autocvar_hud_panel_weapons_ammo_full_shells; float autocvar_hud_panel_weapons_aspect; - float autocvar_hud_panel_weapons_complainbubble; + bool autocvar_hud_panel_weapons_complainbubble; string autocvar_hud_panel_weapons_complainbubble_color_donthave; string autocvar_hud_panel_weapons_complainbubble_color_outofammo; string autocvar_hud_panel_weapons_complainbubble_color_unavailable; float autocvar_hud_panel_weapons_complainbubble_fadetime; float autocvar_hud_panel_weapons_complainbubble_padding; float autocvar_hud_panel_weapons_complainbubble_time; - float autocvar_hud_panel_weapons_label; + int autocvar_hud_panel_weapons_label; float autocvar_hud_panel_weapons_label_scale = 0.5; - float autocvar_hud_panel_weapons_onlyowned; + bool autocvar_hud_panel_weapons_onlyowned; float autocvar_hud_panel_weapons_timeout; - float autocvar_hud_panel_weapons_timeout_effect; + int autocvar_hud_panel_weapons_timeout_effect; float autocvar_hud_panel_weapons_timeout_fadebgmin; float autocvar_hud_panel_weapons_timeout_fadefgmin; - var float autocvar_hud_panel_weapons_timeout_speed_in = 0.25; - var float autocvar_hud_panel_weapons_timeout_speed_out = 0.75; - //float autocvar_hud_panel_quickmenu; +float autocvar_hud_panel_quickmenu_align; + float autocvar_hud_panel_weapons_timeout_speed_in = 0.25; + float autocvar_hud_panel_weapons_timeout_speed_out = 0.75; vector autocvar_hud_progressbar_acceleration_color; vector autocvar_hud_progressbar_acceleration_neg_color; float autocvar_hud_progressbar_alpha; @@@ -366,13 -365,15 +368,13 @@@ vector autocvar_hud_progressbar_armor_c vector autocvar_hud_progressbar_fuel_color; vector autocvar_hud_progressbar_health_color; vector autocvar_hud_progressbar_nexball_color; -vector autocvar_hud_progressbar_shield_color; vector autocvar_hud_progressbar_speed_color; -vector autocvar_hud_progressbar_strength_color; vector autocvar_hud_progressbar_superweapons_color; - float autocvar_hud_showbinds; - float autocvar_hud_showbinds_limit; - float autocvar__hud_showbinds_reload; - float autocvar_hud_shownames; - float autocvar_hud_shownames_enemies; + bool autocvar_hud_showbinds; + bool autocvar_hud_showbinds_limit; + bool autocvar__hud_showbinds_reload; + bool autocvar_hud_shownames; + bool autocvar_hud_shownames_enemies; float autocvar_hud_shownames_crosshairdistance; float autocvar_hud_shownames_crosshairdistance_time; float autocvar_hud_shownames_crosshairdistance_antioverlap; @@@ -392,16 -393,16 +394,15 @@@ float autocvar_hud_shownames_offset string autocvar_hud_skin; float autocvar_menu_mouse_speed; string autocvar_menu_skin; - float autocvar_r_fakelight; - float autocvar_r_fullbright; + int autocvar_r_fakelight; + int autocvar_r_fullbright; float autocvar_r_letterbox; - var float autocvar_scoreboard_accuracy = 1; - float autocvar_scoreboard_accuracy_nocolors; -bool autocvar_scoreboard_accuracy; -bool autocvar_scoreboard_accuracy_doublerows; ++bool autocvar_scoreboard_accuracy = 1; + bool autocvar_scoreboard_accuracy_nocolors; float autocvar_scoreboard_alpha_bg; - var float autocvar_scoreboard_extras = 1; - var float autocvar_scoreboard_alpha_fg = 1.0; - var float autocvar_scoreboard_alpha_name = 0.9; - var float autocvar_scoreboard_alpha_name_self = 1; + float autocvar_scoreboard_alpha_fg = 1.0; + float autocvar_scoreboard_alpha_name = 0.9; + float autocvar_scoreboard_alpha_name_self = 1; float autocvar_scoreboard_bg_scale; float autocvar_scoreboard_border_thickness; float autocvar_scoreboard_color_bg_b; @@@ -409,11 -410,11 +410,12 @@@ float autocvar_scoreboard_color_bg_g float autocvar_scoreboard_color_bg_r; float autocvar_scoreboard_color_bg_team; string autocvar_scoreboard_columns; - var float autocvar_scoreboard_fadeinspeed = 10; - var float autocvar_scoreboard_fadeoutspeed = 5; - float autocvar_scoreboard_highlight; - var float autocvar_scoreboard_highlight_alpha = 0.10; - var float autocvar_scoreboard_highlight_alpha_self = 0.25; ++bool autocvar_scoreboard_extras = 1; + float autocvar_scoreboard_fadeinspeed = 10; + float autocvar_scoreboard_fadeoutspeed = 5; + bool autocvar_scoreboard_highlight; + float autocvar_scoreboard_highlight_alpha = 0.10; + float autocvar_scoreboard_highlight_alpha_self = 0.25; float autocvar_scoreboard_offset_left; float autocvar_scoreboard_offset_right; float autocvar_scoreboard_offset_vertical; @@@ -423,62 -424,49 +425,63 @@@ float autocvar_vid_conheight float autocvar_vid_conwidth; float autocvar_vid_pixelheight; float autocvar_viewsize; - float autocvar_cl_hitsound; - var float autocvar_cl_hitsound_min_pitch = 0.75; - var float autocvar_cl_hitsound_max_pitch = 1.5; - var float autocvar_cl_hitsound_nom_damage = 25; - float autocvar_cl_hitsound_antispam_time; - var float autocvar_cl_eventchase_death = 1; - var float autocvar_cl_eventchase_nexball = 1; +float autocvar_cl_eventchase_vehicle; - var float autocvar_cl_eventchase_distance = 140; - var float autocvar_cl_eventchase_speed = 1.3; - var vector autocvar_cl_eventchase_maxs = '12 12 8'; - var vector autocvar_cl_eventchase_mins = '-12 -12 -8'; - var vector autocvar_cl_eventchase_viewoffset = '0 0 20'; - var vector autocvar_cl_eventchase_vehicle_viewoffset = '0 0 80'; - var float autocvar_cl_eventchase_vehicle_distance = 250; - var vector autocvar_cl_eventchase_generator_viewoffset = '0 0 80'; - var float autocvar_cl_eventchase_generator_distance = 400; - float autocvar_cl_lerpexcess; ++vector autocvar_cl_eventchase_vehicle_viewoffset = '0 0 80'; ++float autocvar_cl_eventchase_vehicle_distance = 250; ++vector autocvar_cl_eventchase_generator_viewoffset = '0 0 80'; ++float autocvar_cl_eventchase_generator_distance = 400; + int autocvar_cl_hitsound; + float autocvar_cl_hitsound_min_pitch = 0.75; + float autocvar_cl_hitsound_max_pitch = 1.5; + float autocvar_cl_hitsound_nom_damage = 25; + float autocvar_cl_hitsound_antispam_time; + int autocvar_cl_eventchase_death = 1; + int autocvar_cl_eventchase_nexball = 1; + float autocvar_cl_eventchase_distance = 140; + float autocvar_cl_eventchase_speed = 1.3; + vector autocvar_cl_eventchase_maxs = '12 12 8'; + vector autocvar_cl_eventchase_mins = '-12 -12 -8'; + vector autocvar_cl_eventchase_viewoffset = '0 0 20'; + float autocvar_cl_lerpexcess; // TODO: int? string autocvar__togglezoom; - float autocvar_cl_damageeffect; + int autocvar_cl_damageeffect; float autocvar_cl_damageeffect_ticrate; float autocvar_cl_damageeffect_bones; - float autocvar_cl_damageeffect_distribute; + bool autocvar_cl_damageeffect_distribute; float autocvar_cl_damageeffect_lifetime; float autocvar_cl_damageeffect_lifetime_min; float autocvar_cl_damageeffect_lifetime_max; - float autocvar_cl_playerdetailreduction; - float autocvar_cl_modeldetailreduction; + int autocvar_cl_playerdetailreduction; + int autocvar_cl_modeldetailreduction; float autocvar_cl_loddistance1 = 768; float autocvar_cl_loddistance2 = 2048; - float autocvar_cl_forceplayermodels; - float autocvar_cl_forceplayercolors; + bool autocvar_cl_forceplayermodels; + bool autocvar_cl_forceplayercolors; string autocvar_cl_forcemyplayermodel; - float autocvar_cl_forcemyplayermodel_always; - float autocvar_cl_forcemyplayerskin; - float autocvar_cl_forcemyplayercolors; - float autocvar__cl_color; - float autocvar__cl_playerskin; ++bool autocvar_cl_forcemyplayermodel_always; + int autocvar_cl_forcemyplayerskin; + int autocvar_cl_forcemyplayercolors; + int autocvar__cl_color; + int autocvar__cl_playerskin; string autocvar__cl_playermodel; float autocvar_cl_deathglow; - float autocvar_developer_csqcentities; + bool autocvar_developer_csqcentities; float autocvar_g_jetpack_attenuation; - var float autocvar_cl_conquest_capture_ring_size_min = 32; - var float autocvar_cl_conquest_capture_ring_size_max = 128; - float autocvar_cl_conquest_capture_ring_hud; - float autocvar_cl_conquest_capture_text_align; - float autocvar_cl_showspectators; - var string autocvar_crosshair_hmg = ""; - var vector autocvar_crosshair_hmg_color = '0.2 1.0 0.2'; - var float autocvar_crosshair_hmg_alpha = 1; - var float autocvar_crosshair_hmg_size = 1; - var string autocvar_crosshair_rpc = ""; - var vector autocvar_crosshair_rpc_color = '0.2 1.0 0.2'; - var float autocvar_crosshair_rpc_alpha = 1; - var float autocvar_crosshair_rpc_size = 1; - float autocvar_r_drawviewmodel; - float autocvar_cl_crouch; - var float autocvar_cl_nade_timer = 1; - float autocvar_cl_specfov; // disable by default? ++float autocvar_cl_conquest_capture_ring_size_min = 32; ++float autocvar_cl_conquest_capture_ring_size_max = 128; ++bool autocvar_cl_conquest_capture_ring_hud; ++int autocvar_cl_conquest_capture_text_align; ++bool autocvar_cl_showspectators; ++bool autocvar_r_drawviewmodel; ++bool autocvar_cl_crouch; ++bool autocvar_cl_specfov; // disable by default? + string autocvar_crosshair_hmg = ""; + vector autocvar_crosshair_hmg_color = '0.2 1.0 0.2'; + float autocvar_crosshair_hmg_alpha = 1; + float autocvar_crosshair_hmg_size = 1; + string autocvar_crosshair_rpc = ""; + vector autocvar_crosshair_rpc_color = '0.2 1.0 0.2'; + float autocvar_crosshair_rpc_alpha = 1; + float autocvar_crosshair_rpc_size = 1; + int autocvar_cl_nade_timer; + #endif diff --cc qcsrc/client/conquest.qc index 839709238,000000000..b028aaa96 mode 100644,000000..100644 --- a/qcsrc/client/conquest.qc +++ b/qcsrc/client/conquest.qc @@@ -1,149 -1,0 +1,156 @@@ ++#include "conquest.qh" ++#include "waypointsprites.qh" ++#include "teamradar.qh" ++#include "miscfunctions.qh" ++#include "hud.qh" ++#include "defs.qh" ++ +.float cq_factor; +void conquest_controlpoint_draw2d() +{ + float dist = vlen(self.origin - view_origin); + + if(self.cq_capdistance > 0) + if(dist > self.cq_capdistance) + return; + + string img = "gfx/crosshair_ring.tga"; + vector psize = draw_getimagesize(img); + vector loc = project_3d_to_2d(self.origin + '0 0 128'); // - 0.5 * psize; + vector textloc = loc; + if(!(loc_z < 0 || loc_x < 0 || loc_y < 0 || loc_x > vid_conwidth || loc_y > vid_conheight) || autocvar_cl_conquest_capture_ring_hud) + { + loc_z = psize_z = textloc_z = 0; + float imgsize = autocvar_cl_conquest_capture_ring_size_min; + vector textsize; + if(autocvar_cl_conquest_capture_ring_hud) + { + loc = eX * 0.5 * vid_conwidth + eY * 0.6 * vid_conheight; + textloc = eY * 0.64 * vid_conheight; + textsize = eX * vid_conwidth + eY * 0.025 * vid_conheight; + imgsize = 0.1 * vid_conheight; + } + else + { + imgsize = max(autocvar_cl_conquest_capture_ring_size_min, (1 - dist / self.cq_capdistance) * autocvar_cl_conquest_capture_ring_size_max); + textsize = '4 2 0' * autocvar_cl_conquest_capture_ring_size_min; + if(autocvar_cl_conquest_capture_text_align == 1) + textloc_x -= textsize_x / 2; + else if(autocvar_cl_conquest_capture_text_align == 2) + textloc_x -= textsize_x; + } + + //drawstring_aspect(eY * 0.64 * vid_conheight, _("Capture progress"), eX * vid_conwidth + eY * 0.025 * vid_conheight, '1 1 1', 1, DRAWFLAG_NORMAL); + float player_team = (GetPlayerColor(player_localnum) + 1); + string msg = ""; + if(self.team == player_team) + { + switch(self.cq_status) + { + default: + case CP_NEUTRAL: msg = sprintf(_("Capturing %s"), self.netname); break; + case CP_CAPTURED: msg = sprintf(_("Defending %s"), self.netname); break; + } + } + else if(spectatee_status) + msg = sprintf(_("Observing %s"), self.netname); + else if(self.team && self.cq_status == CP_CAPTURED) + msg = sprintf(_("Liberating %s"), self.netname); + + if(msg != "") + { + drawstring_aspect(textloc, msg, textsize, '1 1 1', 1, DRAWFLAG_NORMAL); + DrawCircleClippedPic(loc, imgsize, img, self.cq_factor, self.teamradar_color, 1, DRAWFLAG_ADDITIVE); + } + } +} + +.float cq_rate; +void conquest_controlpoint_predraw() +{ + self.enemy.angles_y += 32 * frametime; + float step = self.cq_rate * frametime; + if ( self.cq_factor < self.health - step/2 ) + self.cq_factor += step; + else if ( self.cq_factor > self.health + step/2 ) + self.cq_factor -= step; + setorigin(self.enemy, self.origin + '0 0 1' * (50 + (self.cq_factor * 140))); +} + +void conquest_controlpoint_remove() +{ + if(self.enemy) + remove(self.enemy); + + if(self.netname) + strunzone(self.netname); +} + +void conquest_controlpoint_read(float bIsNew) +{ + float sf = ReadByte(); + + if(sf & CQSF_SETUP) + { + self.origin_x = ReadCoord(); + self.origin_y = ReadCoord(); + self.origin_z = ReadCoord(); + setorigin(self, self.origin); + self.drawmask = MASK_NORMAL; + + self.angles_y = ReadCoord(); + + self.cq_capdistance = ReadLong(); + + precache_model("models/conquest/stand.md3"); + setmodel(self, "models/conquest/stand.md3"); + setsize(self, CQ_CP_MIN, CQ_CP_MAX); + self.predraw = conquest_controlpoint_predraw; + self.draw2d = conquest_controlpoint_draw2d; + self.cq_rate = 0.2; + + self.teamradar_icon = RADARICON_CONTROLPOINT; + + if(!self.enemy) + self.enemy = spawn(); + + precache_model("models/conquest/flag.md3"); + setmodel(self.enemy, "models/conquest/flag.md3"); + self.enemy.drawmask = MASK_NORMAL; + self.enemy.scale = 1.5; + self.entremove = conquest_controlpoint_remove; + } + + if(sf & CQSF_STATE) + { + self.cq_status = ReadByte(); + } + + if(sf & CQSF_TEAM) + { + self.team = ReadByte(); + self.teamradar_color = Team_ColorRGB(self.team - 1); + + if(self.cq_status == CP_CAPTURED) + self.enemy.colormap = self.colormap = 1024 + (self.team - 1) * 17; + else + self.enemy.colormap = self.colormap = 1024 * 17; + } + + if(sf & CQSF_HEALTH) + { + float newhealth = ReadByte(); + if(newhealth != 0) { newhealth /= 255; } + self.health = newhealth; + self.cq_rate = ReadLong() / 100 * 0.2; + self.teamradar_color = Team_ColorRGB(self.team - 1) * self.health + '1 1 1' * (1-self.health); + } + + if(sf & CQSF_NAME) + { + if(self.netname) + strunzone(self.netname); + + self.netname = strzone(ReadString()); + } +} diff --cc qcsrc/client/conquest.qh index 6cf1b7cbb,000000000..92360dc8b mode 100644,000000..100644 --- a/qcsrc/client/conquest.qh +++ b/qcsrc/client/conquest.qh @@@ -1,17 -1,0 +1,20 @@@ ++#ifndef CL_CONQUEST_H ++#define CL_CONQUEST_H ++ +//csqc networking flags - #define CQSF_SETUP 1 //! Initial setup, responsible for communicating location, y-angle and model - #define CQSF_TEAM 2 //! What team point belong to - #define CQSF_HEALTH 4 //! Capture progress. Networked as 0--255 - #define CQSF_STATE 8 //! Captured or not - #define CQSF_NAME 16 //! Display name (can be defined by mapper) ++const int CQSF_SETUP = 1; //! Initial setup, responsible for communicating location, y-angle and model ++const int CQSF_TEAM = 2; //! What team point belong to ++const int CQSF_HEALTH = 4; //! Capture progress. Networked as 0--255 ++const int CQSF_STATE = 8; //! Captured or not ++const int CQSF_NAME = 16; //! Display name (can be defined by mapper) + - .float cq_status; - #define CP_NEUTRAL 1 - #define CP_CAPTURED 2 ++.int cq_status; ++const int CP_NEUTRAL = 1; ++const int CP_CAPTURED = 2; + +.float cq_capdistance; + - #define CQ_CP_MIN ('-35 -35 -3') - #define CQ_CP_MAX ('35 35 195') ++const vector CQ_CP_MIN = ('-35 -35 -3'); ++const vector CQ_CP_MAX = ('35 35 195'); + - .float health; ++#endif diff --cc qcsrc/client/controlpoint.qc index c01835c9f,000000000..c0b5f3487 mode 100644,000000..100644 --- a/qcsrc/client/controlpoint.qc +++ b/qcsrc/client/controlpoint.qc @@@ -1,210 -1,0 +1,214 @@@ ++#include "controlpoint.qh" ++#include "waypointsprites.qh" ++#include "teamradar.qh" ++ +float cpicon_precached; +.float count; +.float pain_finished; + +.float iscaptured; + +.vector cp_origin, cp_bob_origin; +.float cp_bob_spd; + +.vector cp_bob_dmg; + +.vector punchangle; + +.float max_health; + +.entity icon_realmodel; + +void cpicon_precache() +{ + if(cpicon_precached) + return; // already precached + + precache_model("models/onslaught/controlpoint_icon_dmg3.md3"); + precache_model("models/onslaught/controlpoint_icon_dmg2.md3"); + precache_model("models/onslaught/controlpoint_icon_dmg1.md3"); + precache_model("models/onslaught/controlpoint_icon.md3"); + - cpicon_precached = TRUE; ++ cpicon_precached = true; +} + +void cpicon_draw() +{ + if(time < self.move_time) { return; } + + if(self.cp_bob_dmg_z > 0) + self.cp_bob_dmg_z = self.cp_bob_dmg_z - 3 * frametime; + else + self.cp_bob_dmg_z = 0; + self.cp_bob_origin_z = 4 * PI * (1 - cos(self.cp_bob_spd)); + self.cp_bob_spd = self.cp_bob_spd + 1.875 * frametime; + self.colormod = '1 1 1' * (2 - bound(0, (self.pain_finished - time) / 10, 1)); + + if(!self.iscaptured) self.alpha = self.health / self.max_health; + + if(self.iscaptured) + { + if (self.punchangle_x > 0) + { + self.punchangle_x = self.punchangle_x - 60 * frametime; + if (self.punchangle_x < 0) + self.punchangle_x = 0; + } + else if (self.punchangle_x < 0) + { + self.punchangle_x = self.punchangle_x + 60 * frametime; + if (self.punchangle_x > 0) + self.punchangle_x = 0; + } + + if (self.punchangle_y > 0) + { + self.punchangle_y = self.punchangle_y - 60 * frametime; + if (self.punchangle_y < 0) + self.punchangle_y = 0; + } + else if (self.punchangle_y < 0) + { + self.punchangle_y = self.punchangle_y + 60 * frametime; + if (self.punchangle_y > 0) + self.punchangle_y = 0; + } + + if (self.punchangle_z > 0) + { + self.punchangle_z = self.punchangle_z - 60 * frametime; + if (self.punchangle_z < 0) + self.punchangle_z = 0; + } + else if (self.punchangle_z < 0) + { + self.punchangle_z = self.punchangle_z + 60 * frametime; + if (self.punchangle_z > 0) + self.punchangle_z = 0; + } + + self.angles_x = self.punchangle_x; + self.angles_y = self.punchangle_y + self.move_angles_y; + self.angles_z = self.punchangle_z; + self.move_angles_y = self.move_angles_y + 45 * frametime; + } + + setorigin(self, self.cp_origin + self.cp_bob_origin + self.cp_bob_dmg); +} + +void cpicon_damage(float hp) +{ + if(!self.iscaptured) { return; } + + if(hp < self.max_health * 0.25) + setmodel(self, "models/onslaught/controlpoint_icon_dmg3.md3"); + else if(hp < self.max_health * 0.50) + setmodel(self, "models/onslaught/controlpoint_icon_dmg2.md3"); + else if(hp < self.max_health * 0.75) + setmodel(self, "models/onslaught/controlpoint_icon_dmg1.md3"); + else if(hp <= self.max_health || hp >= self.max_health) + setmodel(self, "models/onslaught/controlpoint_icon.md3"); + + self.punchangle = (2 * randomvec() - '1 1 1') * 45; + + self.cp_bob_dmg_z = (2 * random() - 1) * 15; + self.pain_finished = time + 1; + self.colormod = '2 2 2'; + + setsize(self, CPICON_MIN, CPICON_MAX); +} + +void cpicon_construct() +{ + self.netname = "Control Point Icon"; + + setmodel(self, "models/onslaught/controlpoint_icon.md3"); + setsize(self, CPICON_MIN, CPICON_MAX); + + if(self.icon_realmodel == world) + { + self.icon_realmodel = spawn(); + setmodel(self.icon_realmodel, "null"); + setorigin(self.icon_realmodel, self.origin); + setsize(self.icon_realmodel, CPICON_MIN, CPICON_MAX); + self.icon_realmodel.movetype = MOVETYPE_NOCLIP; + self.icon_realmodel.solid = SOLID_NOT; + self.icon_realmodel.move_origin = self.icon_realmodel.origin; + } + + if(self.iscaptured) { self.icon_realmodel.solid = SOLID_BBOX; } + + self.move_movetype = MOVETYPE_NOCLIP; + self.solid = SOLID_NOT; + self.movetype = MOVETYPE_NOCLIP; + self.move_origin = self.origin; + self.move_time = time; + self.drawmask = MASK_NORMAL; + self.alpha = 1; + self.draw = cpicon_draw; + self.cp_origin = self.origin; + self.cp_bob_origin = '0 0 0.1'; + self.cp_bob_spd = 0; +} + +.vector glowmod; +void cpicon_changeteam() +{ + if(self.team) + { + self.glowmod = Team_ColorRGB(self.team - 1); + self.teamradar_color = Team_ColorRGB(self.team - 1); + self.colormap = 1024 + (self.team - 1) * 17; + } + else + { + self.colormap = 1024; + self.glowmod = '1 1 0'; + self.teamradar_color = '1 1 0'; + } +} + +void ent_cpicon() +{ + float sf; + sf = ReadByte(); + + if(sf & CPSF_SETUP) + { + self.origin_x = ReadCoord(); + self.origin_y = ReadCoord(); + self.origin_z = ReadCoord(); + setorigin(self, self.origin); + + self.health = ReadByte(); + self.max_health = ReadByte(); + self.count = ReadByte(); + self.team = ReadByte(); + self.iscaptured = ReadByte(); + + if(!self.count) + self.count = (self.health - self.max_health) * frametime; + + cpicon_changeteam(); + cpicon_precache(); + cpicon_construct(); + } + + if(sf & CPSF_STATUS) + { + float _tmp; + _tmp = ReadByte(); + if(_tmp != self.team) + { + self.team = _tmp; + cpicon_changeteam(); + } + + _tmp = ReadByte(); + + if(_tmp != self.health) + cpicon_damage(_tmp); + + self.health = _tmp; + } +} diff --cc qcsrc/client/controlpoint.qh index 5b18f86f2,000000000..8469b5226 mode 100644,000000..100644 --- a/qcsrc/client/controlpoint.qh +++ b/qcsrc/client/controlpoint.qh @@@ -1,8 -1,0 +1,13 @@@ ++#ifndef CONTROLPOINT_H ++#define CONTROLPOINT_H ++ +const vector CPICON_MIN = '-32 -32 -9'; +const vector CPICON_MAX = '32 32 25'; + - float CPSF_STATUS = 4; - float CPSF_SETUP = 8; ++const int CPSF_STATUS = 4; ++const int CPSF_SETUP = 8; + +void ent_cpicon(); +void cpicon_precache(); ++ ++#endif diff --cc qcsrc/client/damage.qc index e01659c4e,000ef8bcc..ce132dbd0 --- a/qcsrc/client/damage.qc +++ b/qcsrc/client/damage.qc @@@ -1,3 -1,19 +1,19 @@@ + #if defined(CSQC) + #include "../dpdefs/csprogsdefs.qh" + #include "defs.qh" + #include "../common/constants.qh" + #include "../common/util.qh" + #include "../common/weapons/weapons.qh" + #include "autocvars.qh" + #include "../common/deathtypes.qh" + #include "damage.qh" + #include "movetypes.qh" + #include "prandom.qh" - #include "vehicles/vehicles.qh" ++ #include "../common/vehicles/cl_vehicles.qh" + #elif defined(MENUQC) + #elif defined(SVQC) + #endif + void DamageEffect_Think() { // if particle distribution is enabled, slow ticrate by total number of damages diff --cc qcsrc/client/defs.qh index 000000000,82c43e2e2..9b6b219bc mode 000000,100644..100644 --- a/qcsrc/client/defs.qh +++ b/qcsrc/client/defs.qh @@@ -1,0 -1,134 +1,143 @@@ + #ifndef DEFS_H + #define DEFS_H + + // Additional OPTIONAL Fields and Globals + //float intermission; + float scoreboard_showscores; + float scoreboard_showaccuracy; + .string message; + .int renderflags; + // float coop; + // float deathmatch; + + float dmg_take; + // float dmg_save; + // vector dmg_origin; + + // Darkplaces Render Modifications + #if 0 + .float alpha; + .float renderflags; + .vector colormod; + .float scale; + #endif + ++.int cnt; // end effect ++.vector colormod; ++.int state; // on-off ++.int count; // flags for the laser ++.vector velocity; ++.float alpha; ++.float scale; // scaling factor of the thickness ++.float modelscale; // scaling factor of the dlight ++ + // Basic variables + .float enttype; // entity type sent from server + .int sv_entnum; // entity number sent from server + .int team; + .int team_size; + + float vid_conwidth, vid_conheight; + int binddb; + + // QUALIFYING + float race_checkpoint; + float race_time; + float race_laptime; + float race_checkpointtime; + float race_previousbesttime; + string race_previousbestname; + float race_nextcheckpoint; + float race_nextbesttime; + string race_nextbestname; + float race_penaltyaccumulator; // qualifying: total penalty time in tenths + float race_penaltyeventtime; // time when the player got the penalty + float race_penaltytime; // duration of penalty time, in tenths + string race_penaltyreason; // reason for penalty + float race_server_record; // server record + float race_speedaward; + string race_speedaward_holder; + float race_speedaward_alltimebest; + string race_speedaward_alltimebest_holder; + + // RACE + float race_mycheckpoint; + float race_mycheckpointtime; + float race_mycheckpointdelta; + float race_mycheckpointlapsdelta; + string race_mycheckpointenemy; + float race_othercheckpoint; + float race_othercheckpointtime; + float race_othercheckpointdelta; + float race_othercheckpointlapsdelta; + string race_othercheckpointenemy; + float scoreboard_showscores_force; + float race_status; + string race_status_name; + float race_myrank; + + // Nexball + float nb_pb_period; + + // Spectating + float spectatee_status; + + // short mapname + string shortmapname; + + // database for misc stuff + int tempdb; + int ClientProgsDB; + vector hook_shotorigin[4]; + vector lightning_shotorigin[4]; + + + #ifdef BLURTEST + float blurtest_time0, blurtest_time1, blurtest_radius, blurtest_power; + #endif + + float servertime, serverprevtime, serverdeltatime; + + float ticrate; + + .float damageforcescale; + const float MIN_DAMAGEEXTRARADIUS = 2; + const float MAX_DAMAGEEXTRARADIUS = 16; + .float damageextraradius; + .void(float thisdmg, int hittype, vector org, vector thisforce) event_damage; + + // only for Porto + float angles_held_status; + vector angles_held; + + // weapons + .bool silent; + + int w_deathtype; + float w_issilent, w_random; + vector w_org, w_backoff; + + float rifle_scope; + float vortex_scope; + + float minelayer_maxmines; + + float hagar_maxrockets; + + float bgmtime; + + string weaponorder_byimpulse; + string weaponorder_bypriority; + + float vortex_charge_movingavg; + + int serverflags; + + float uid2name_dialog; + + .bool csqcmodel_isdead; // used by shownames and miscfunctions (float getplayerisdead(float) {}) to know when a player is dead + + #define player_currententnum (spectatee_status > 0 ? spectatee_status : player_localnum + 1) + + float g_balance_porto_secondary; + #endif diff --cc qcsrc/client/generator.qc index 4ee798f01,000000000..43b264641 mode 100644,000000..100644 --- a/qcsrc/client/generator.qc +++ b/qcsrc/client/generator.qc @@@ -1,250 -1,0 +1,253 @@@ - float generator_precached; - .float count; - .float max_health; ++#include "generator.qh" ++#include "waypointsprites.qh" ++ ++bool generator_precached; ++.int count; ++.int max_health; + +void generator_precache() +{ + if(generator_precached) + return; // already precached + + precache_model("models/onslaught/generator.md3"); + precache_model("models/onslaught/generator_dead.md3"); + precache_model("models/onslaught/generator_dmg1.md3"); + precache_model("models/onslaught/generator_dmg2.md3"); + precache_model("models/onslaught/generator_dmg3.md3"); + precache_model("models/onslaught/generator_dmg4.md3"); + precache_model("models/onslaught/generator_dmg5.md3"); + precache_model("models/onslaught/generator_dmg6.md3"); + precache_model("models/onslaught/generator_dmg7.md3"); + precache_model("models/onslaught/generator_dmg8.md3"); + precache_model("models/onslaught/generator_dmg9.md3"); + precache_model("models/onslaught/generator_dead.md3"); + + precache_model("models/onslaught/ons_ray.md3"); + precache_sound("onslaught/shockwave.wav"); + precache_sound("weapons/grenade_impact.wav"); + precache_sound("weapons/rocket_impact.wav"); + precache_sound("onslaught/electricity_explode.wav"); + - generator_precached = TRUE; ++ generator_precached = true; +} + +void ons_generator_ray_draw() +{ + if(time < self.move_time) + return; + + self.move_time = time + 0.05; + + if(self.count > 10) + { + remove(self); + return; + } + + if(self.count > 5) + self.alpha -= 0.1; + else + self.alpha += 0.1; + + self.scale += 0.2; + self.count +=1; +} + +void ons_generator_ray_spawn(vector org) +{ + entity e; + e = spawn(); + e.classname = "ons_ray"; + setmodel(e, "models/onslaught/ons_ray.md3"); + setorigin(e, org); + e.angles = randomvec() * 360; + e.move_origin = org; + e.movetype = MOVETYPE_NONE; + e.alpha = 0; + e.scale = random() * 5 + 8; + e.move_time = time + 0.05; + e.drawmask = MASK_NORMAL; + e.draw = ons_generator_ray_draw; +} + +void generator_draw() +{ + if(time < self.move_time) + return; + + if(self.health > 0) + { + // damaged fx (less probable the more damaged is the generator) + if(random() < 0.9 - self.health / self.max_health) + if(random() < 0.01) + { + pointparticles(particleeffectnum("electro_ballexplode"), self.origin + randompos('-50 -50 -20', '50 50 50'), '0 0 0', 1); + sound(self, CH_TRIGGER, "onslaught/electricity_explode.wav", VOL_BASE, ATTEN_NORM); + } + else + pointparticles(particleeffectnum("torch_small"), self.origin + randompos('-60 -60 -20', '60 60 60'), '0 0 0', 1); + + self.move_time = time + 0.1; + + return; + } + + if(self.count <= 0) + return; + + vector org; + float i; + + // White shockwave + if(self.count==40||self.count==20) + { + sound(self, CH_TRIGGER, "onslaught/shockwave.wav", VOL_BASE, ATTEN_NORM); + pointparticles(particleeffectnum("electro_combo"), self.origin, '0 0 0', 6); + } + + // rays + if(random() > 0.25) + { + ons_generator_ray_spawn(self.origin); + } + + // Spawn fire balls + for(i=0;i < 10;++i) + { + org = self.origin + randompos('-30 -30 -30' * i + '0 0 -20', '30 30 30' * i + '0 0 20'); + pointparticles(particleeffectnum("onslaught_generator_gib_explode"), org, '0 0 0', 1); + } + + // Short explosion sound + small explosion + if(random() < 0.25) + { + te_explosion(self.origin); + sound(self, CH_TRIGGER, "weapons/grenade_impact.wav", VOL_BASE, ATTEN_NORM); + } + + // Particles + org = self.origin + randompos(self.mins + '8 8 8', self.maxs + '-8 -8 -8'); + pointparticles(particleeffectnum("onslaught_generator_smallexplosion"), org, '0 0 0', 1); + + // Final explosion + if(self.count==1) + { + org = self.origin; + te_explosion(org); + pointparticles(particleeffectnum("onslaught_generator_finalexplosion"), org, '0 0 0', 1); + sound(self, CH_TRIGGER, "weapons/rocket_impact.wav", VOL_BASE, ATTEN_NORM); + } + + self.move_time = time + 0.05; + + self.count -= 1; +} + +void generator_damage(float hp) +{ + if(hp <= 0) + setmodel(self, "models/onslaught/generator_dead.md3"); + else if(hp < self.max_health * 0.10) + setmodel(self, "models/onslaught/generator_dmg9.md3"); + else if(hp < self.max_health * 0.20) + setmodel(self, "models/onslaught/generator_dmg8.md3"); + else if(hp < self.max_health * 0.30) + setmodel(self, "models/onslaught/generator_dmg7.md3"); + else if(hp < self.max_health * 0.40) + setmodel(self, "models/onslaught/generator_dmg6.md3"); + else if(hp < self.max_health * 0.50) + setmodel(self, "models/onslaught/generator_dmg5.md3"); + else if(hp < self.max_health * 0.60) + setmodel(self, "models/onslaught/generator_dmg4.md3"); + else if(hp < self.max_health * 0.70) + setmodel(self, "models/onslaught/generator_dmg3.md3"); + else if(hp < self.max_health * 0.80) + setmodel(self, "models/onslaught/generator_dmg2.md3"); + else if(hp < self.max_health * 0.90) + setmodel(self, "models/onslaught/generator_dmg1.md3"); + else if(hp <= self.max_health || hp >= self.max_health) + setmodel(self, "models/onslaught/generator.md3"); + + setsize(self, GENERATOR_MIN, GENERATOR_MAX); +} + +void generator_construct() +{ + self.netname = "Generator"; + self.classname = "onslaught_generator"; + + setorigin(self, self.origin); + setmodel(self, "models/onslaught/generator.md3"); + setsize(self, GENERATOR_MIN, GENERATOR_MAX); + + self.move_movetype = MOVETYPE_NOCLIP; + self.solid = SOLID_BBOX; + self.movetype = MOVETYPE_NOCLIP; + self.move_origin = self.origin; + self.move_time = time; + self.drawmask = MASK_NORMAL; + self.alpha = 1; + self.draw = generator_draw; +} + +.vector glowmod; +void generator_changeteam() +{ + if(self.team) + { + self.glowmod = Team_ColorRGB(self.team - 1); + self.teamradar_color = Team_ColorRGB(self.team - 1); + self.colormap = 1024 + (self.team - 1) * 17; + } + else + { + self.colormap = 1024; + self.glowmod = '1 1 0'; + self.teamradar_color = '1 1 0'; + } +} + +void ent_generator() +{ + float sf; + sf = ReadByte(); + + if(sf & GSF_SETUP) + { + self.origin_x = ReadCoord(); + self.origin_y = ReadCoord(); + self.origin_z = ReadCoord(); + setorigin(self, self.origin); + + self.health = ReadByte(); + self.max_health = ReadByte(); + self.count = ReadByte(); + self.team = ReadByte(); + + if(!self.count) + self.count = 40; + + generator_changeteam(); + generator_precache(); + generator_construct(); + } + + if(sf & GSF_STATUS) + { + float _tmp; + _tmp = ReadByte(); + if(_tmp != self.team) + { + self.team = _tmp; + generator_changeteam(); + } + + _tmp = ReadByte(); + + if(_tmp != self.health) + generator_damage(_tmp); + + self.health = _tmp; + } +} diff --cc qcsrc/client/generator.qh index 6ba1ce858,000000000..81b825be7 mode 100644,000000..100644 --- a/qcsrc/client/generator.qh +++ b/qcsrc/client/generator.qh @@@ -1,8 -1,0 +1,13 @@@ ++#ifndef GENERATOR_H ++#define GENERATOR_H ++ +const vector GENERATOR_MIN = '-52 -52 -14'; +const vector GENERATOR_MAX = '52 52 75'; + - float GSF_STATUS = 4; - float GSF_SETUP = 8; ++const int GSF_STATUS = 4; ++const int GSF_SETUP = 8; + +void ent_generator(); - void generator_precache(); ++void generator_precache(); ++ ++#endif diff --cc qcsrc/client/hud.qc index a8bd4ce65,024a0761e..360ce1fc3 --- a/qcsrc/client/hud.qc +++ b/qcsrc/client/hud.qc @@@ -1,3 -1,11 +1,14 @@@ ++#include "mapvoting.qh" + #include "scoreboard.qh" + #include "teamradar.qh" + #include "../common/buffs.qh" + #include "../common/counting.qh" + #include "../common/mapinfo.qh" + #include "../common/nades.qh" + #include "../server/t_items.qh" ++#include "../server/mutators/gamemode_ctf.qh" ++#include "../server/mutators/gamemode_keyhunt.qh" + /* ================== Misc HUD functions @@@ -1222,26 -1225,74 +1225,16 @@@ void HUD_Powerups(void mySize -= '2 2 0' * panel_bg_padding; } - float panel_ar = mySize_x/mySize_y; + float panel_ar = mySize.x/mySize.y; float is_vertical = (panel_ar < 1); - vector shield_offset = '0 0 0', strength_offset = '0 0 0', superweapons_offset = '0 0 0'; + vector superweapons_offset = '0 0 0'; float superweapons_is = -1; - if(superweapons_time) - { - if(strength_time) - { - if(shield_time) - superweapons_is = 0; - else - superweapons_is = 2; - } - else - { - if(shield_time) - superweapons_is = 1; - else - superweapons_is = 2; - } - } + if(superweapons_time) { superweapons_is = 2; } -- // FIXME handle superweapons here - if (panel_ar >= 4 || (panel_ar >= 1/4 && panel_ar < 1)) - if(superweapons_is == 0) -- { - mySize_x *= 0.5; - if (panel_ar >= 4 || (panel_ar >= 1/4 && panel_ar < 1)) - { - mySize.x *= (1.0 / 3.0); - superweapons_offset.x = mySize.x; - if (autocvar_hud_panel_powerups_flip) - shield_offset.x = 2*mySize.x; - else - strength_offset.x = 2*mySize.x; - } - else - { - mySize.y *= (1.0 / 3.0); - superweapons_offset.y = mySize.y; - if (autocvar_hud_panel_powerups_flip) - shield_offset.y = 2*mySize.y; - else - strength_offset.y = 2*mySize.y; - } -- } -- else -- { - mySize_y *= 0.5; - if (panel_ar >= 4 || (panel_ar >= 1/4 && panel_ar < 1)) - { - mySize.x *= 0.5; - if (autocvar_hud_panel_powerups_flip) - shield_offset.x = mySize.x; - else - strength_offset.x = mySize.x; - } - else - { - mySize.y *= 0.5; - if (autocvar_hud_panel_powerups_flip) - shield_offset.y = mySize.y; - else - strength_offset.y = mySize.y; - } -- } -- - float shield_baralign, strength_baralign, superweapons_baralign; - float shield_iconalign, strength_iconalign, superweapons_iconalign; + float superweapons_baralign; + float superweapons_iconalign; if (autocvar_hud_panel_powerups_flip) { @@@ -2140,21 -2059,6 +2133,21 @@@ void HUD_Radar(void draw_teamradar_player(view_origin, view_angles, '1 1 1'); drawresetcliparea(); + + if ( hud_panel_radar_mouse ) + { + string message = "Click to select teleport destination"; + + if ( getstati(STAT_HEALTH) <= 0 ) + { + message = "Click to select spawn location"; + } + - drawcolorcodedstring(pos + '0.5 0 0' * (mySize_x - stringwidth(message, TRUE, hud_fontsize)) - '0 1 0' * hud_fontsize_y * 2, ++ drawcolorcodedstring(pos + '0.5 0 0' * (mySize_x - stringwidth(message, true, hud_fontsize)) - '0 1 0' * hud_fontsize_y * 2, + message, hud_fontsize, hud_panel_radar_foreground_alpha, DRAWFLAG_NORMAL); + + hud_panel_radar_bottom = pos_y + mySize_y + hud_fontsize_y; + } } // Score (#7) @@@ -3894,114 -3407,6 +3871,114 @@@ void HUD_ModIcons(void draw_endBoldFont(); } +void HUD_DiscoMode(void) +{ + // vectors for top right, bottom right, bottom and bottom left corners + + vector topright; + vector bottom; + vector bottomright; + vector bottomleft; + + topright_x = vid_conwidth; + topright_y = 0; + topright_z = 0; + + bottom_x = vid_conwidth/2; + bottom_y = vid_conheight; + bottom_z = 0; + + bottomright_x = vid_conwidth; + bottomright_y = vid_conheight; + bottomright_z = 0; + + bottomleft_x = 0; + bottomleft_y = vid_conheight; + bottomleft_z = 0; + + local vector dmpos; + local float dmalpha; + dmpos = bottomleft; + local vector offset = '0 0 0'; + + dmalpha = 1; + + local vector dmcolor = '0 0 1'; + dmcolor_x = random(); + + drawfill('0 0 0', bottomright, dmcolor, dmalpha * random(), DRAWFLAG_ADDITIVE); + + local string mycolor = ""; + local string mychar; + local float j; + local string dmstring = _("^1D^2i^3s^4c^5o ^6Mode"); + + local float k; + + local float discoball; + local float discoballs = 8; + for ( k = 0; k < discoballs; ++k ) + { + dmpos_y = vid_conheight / 2; + dmpos_x = vid_conwidth / 2; + + //offset_y = 32 * (k+1) * sin(3 * time) * (mod(k, 2)? 1 : -1) * (mod(k, 3)? 1 : -1); + //offset_x = offset_y * (mod(k, 2)? -1 : 1);// * (mod(k, 3)? 0.5 : 1); + offset_x = 48 * (k+1) * sin(4*time+10*k) * cos(6.28*k/discoballs); + offset_y = 48 * (k+1) * sin(4*time+10*k) * sin(6.28*k/discoballs); + + dmcolor_x = 1; + dmcolor_y = cos(16*time+k)/2+0.5; + dmcolor_z = 0; + + discoball = 32 + (discoballs-k)*8 + 16*(cos(5*time+10*k)); + offset_x -= discoball/2; + offset_y -= discoball/2; + + dmalpha = 0.6 + cos(64*time+k)*0.3; + + drawfill(dmpos + offset, discoball*'1 1 0', dmcolor, dmalpha, DRAWFLAG_NORMAL); + + } + + draw_beginBoldFont(); + + for(k = 0; k < 2; ++k) + { + dmpos_y = vid_conheight / 2 - 12; + dmpos_x = 0.5 * (vid_conwidth - 0.6025 * strlennocol(dmstring) * 24); + for(j = 0; j < strlen(dmstring); ++j) + { + mychar = substring(dmstring, j, 1); + + if(mychar == "^") + { + if(substring(dmstring, j+1, 1) == "x") + { + mycolor = substring(dmstring, j, 5); + j += 5; + } + else + { + mycolor = substring(dmstring, j, 2); + ++j; + } + continue; + } + + offset_y = 10 * ((k*10)+1) * cos(dmpos_x + 3 * time) * ((j % 2)? 1 : -1) * (k? 1 : -1); + offset_x = offset_y * ((j % 2)? -1 : 1); + local string resultstr = strcat(mycolor, mychar); + + dummyfunction(0, 0, 0, 0, 0, 0, 0, 0); // work around DP bug (set OFS_PARAM5 to 0) - dmpos_x += stringwidth(resultstr, TRUE, '24 24 0'); ++ dmpos_x += stringwidth(resultstr, true, '24 24 0'); + drawcolorcodedstring(dmpos + offset, resultstr, '24 24 0', dmalpha * 0.8, DRAWFLAG_ADDITIVE); + } + } + + draw_endBoldFont(); +} + // Draw pressed keys (#11) // void HUD_PressedKeys(void) @@@ -4274,8 -3680,8 +4252,8 @@@ void HUD_InfoMessages(void if(spectatee_status == -1) s = _("^1Observing"); else - s = sprintf(_("^1Spectating: ^7%s"), GetPlayerName(player_localentnum - 1)); + s = sprintf(_("^1Spectating: ^7%s ^1(^3fov: %d^1)"), GetPlayerName(player_localentnum - 1), fovlock); - drawInfoMessage(s) + drawInfoMessage(s); if(spectatee_status == -1) s = sprintf(_("^1Press ^3%s^1 to spectate"), getcommandkey("primary fire", "+fire")); @@@ -4320,28 -3726,9 +4298,28 @@@ if(warmup_stage && !intermission) { s = _("^2Currently in ^1warmup^2 stage!"); - drawInfoMessage(s) + drawInfoMessage(s); } + if(autocvar_cl_showspectators) + if(num_spectators) + //if(spectatee_status != -1) + { + s = ((spectatee_status) ? _("^1Spectating this player:") : _("^1Spectating you:")); + //drawInfoMessage(s) + float limit = min(num_spectators, MAX_SPECTATORS); + float i; + for(i = 0; i < limit; ++i) + { + float slot = spectatorlist[i]; + if(i == 0) + s = strcat(s, " ^3", GetPlayerName(slot)); + else + s = strcat("^3", GetPlayerName(slot)); - drawInfoMessage(s) ++ drawInfoMessage(s); + } + } + string blinkcolor; if(time % 1 >= 0.5) blinkcolor = "^1"; @@@ -5008,595 -4387,6 +4986,595 @@@ void HUD_CenterPrint (void } } +// QuickMenu (#17) +// +// QUICKMENU_MAXLINES must be <= 10 +#define QUICKMENU_MAXLINES 10 +#define QUICKMENU_MAXENTRIES 256 +string QuickMenu_Command[QUICKMENU_MAXLINES]; +string QuickMenu_Description[QUICKMENU_MAXLINES]; +float QuickMenu_CurrentPage; +float QuickMenu_IsLastPage; +// each quickmenu entry (submenu or command) is composed of 2 entries in the buffer +#define QUICKMENU_BUFFER_MAXENTRIES 2*QUICKMENU_MAXENTRIES +var float QuickMenu_Buffer = -1; +float QuickMenu_Buffer_Size; +float QuickMenu_Buffer_Index; +string QuickMenu_CurrentSubMenu; +float QuickMenu_CurrentPage_FirstEntry; +var float QuickMenu_Entries; +float QuickMenu_TimeOut; +void HUD_QuickMenu_load_entry(float i, string s, string s1) +{ + //printf("^xc80 entry %d: %s, %s\n", i, s, s1); + if (QuickMenu_Description[i]) + strunzone(QuickMenu_Description[i]); + QuickMenu_Description[i] = strzone(s); + if (QuickMenu_Command[i]) + strunzone(QuickMenu_Command[i]); + QuickMenu_Command[i] = strzone(s1); +} +void HUD_QuickMenu_clear_entry(float i) +{ + if (QuickMenu_Description[i]) + strunzone(QuickMenu_Description[i]); + QuickMenu_Description[i] = string_null; + if (QuickMenu_Command[i]) + strunzone(QuickMenu_Command[i]); + QuickMenu_Command[i] = string_null; +} + +void HUD_QuickMenu_Load_DefaultEntries(); +float HUD_QuickMenu_Buffer_Init() +{ + float fh = -1; + string s; + if(autocvar_hud_panel_quickmenu_file != "") + if(autocvar_hud_panel_quickmenu_file != "0") + fh = fopen(autocvar_hud_panel_quickmenu_file, FILE_READ); + if(fh < 0) + { + QuickMenu_Buffer = buf_create(); + if(QuickMenu_Buffer < 0) + return false; + HUD_QuickMenu_Load_DefaultEntries(); + QuickMenu_TimeOut = time + autocvar_hud_panel_quickmenu_time; + return true; + } + + QuickMenu_Buffer = buf_create(); + if (QuickMenu_Buffer < 0) + { + fclose(fh); + return false; + } + + QuickMenu_Buffer_Size = 0; + while((s = fgets(fh)) && QuickMenu_Buffer_Size < QUICKMENU_BUFFER_MAXENTRIES) + { + // first skip invalid entries, so we don't check them anymore + float argc; + argc = tokenize_console(s); + if(argc == 0 || argc > 2) + continue; + if(argv(0) == "") + continue; + if(argc == 2 && argv(1) == "") + continue; + + if(argc == 1) + bufstr_set(QuickMenu_Buffer, QuickMenu_Buffer_Size, strcat("S", argv(0))); // Submenu + else + { + bufstr_set(QuickMenu_Buffer, QuickMenu_Buffer_Size, strcat("T", argv(0))); // command Title + ++QuickMenu_Buffer_Size; + bufstr_set(QuickMenu_Buffer, QuickMenu_Buffer_Size, argv(1)); // command + } + ++QuickMenu_Buffer_Size; + QuickMenu_TimeOut = time + autocvar_hud_panel_quickmenu_time; + } + + if (QuickMenu_Buffer_Size <= 0) + { + buf_del(QuickMenu_Buffer); + QuickMenu_Buffer = -1; + } + fclose(fh); + return true; +} + +void HUD_QuickMenu_Buffer_Close() +{ + if (QuickMenu_Buffer >= 0) + { + buf_del(QuickMenu_Buffer); + QuickMenu_Buffer = -1; + QuickMenu_Buffer_Size = 0; + } +} + +void HUD_QuickMenu_Close() +{ + if (QuickMenu_CurrentSubMenu) + strunzone(QuickMenu_CurrentSubMenu); + QuickMenu_CurrentSubMenu = string_null; + float i; + for (i = 0; i < QUICKMENU_MAXLINES; ++i) + HUD_QuickMenu_clear_entry(i); + QuickMenu_Entries = 0; + hud_panel_quickmenu = 0; + mouseClicked = 0; + prevMouseClicked = 0; + HUD_QuickMenu_Buffer_Close(); + + if(autocvar_hud_cursormode) + if(!mv_active) + setcursormode(0); +} + +// It assumes submenu open tag is already detected +void HUD_QuickMenu_skip_submenu(string submenu) +{ + string s, z_submenu; + z_submenu = strzone(submenu); + for(++QuickMenu_Buffer_Index ; QuickMenu_Buffer_Index < QuickMenu_Buffer_Size; ++QuickMenu_Buffer_Index) + { + s = bufstr_get(QuickMenu_Buffer, QuickMenu_Buffer_Index); + if(substring(s, 0, 1) != "S") + continue; + if(substring(s, 1, strlen(s) - 1) == z_submenu) // submenu end + break; + HUD_QuickMenu_skip_submenu(substring(s, 1, strlen(s) - 1)); + } + strunzone(z_submenu); +} + +float HUD_QuickMenu_IsOpened() +{ + return (QuickMenu_Entries > 0); +} + +// new_page 0 means page 0, new_page != 0 means next page +float QuickMenu_Buffer_Index_Prev; +float HUD_QuickMenu_Page(string target_submenu, float new_page) +{ + string s, z_submenu; + + if (new_page == 0) + QuickMenu_CurrentPage = 0; + else + ++QuickMenu_CurrentPage; + QuickMenu_CurrentPage_FirstEntry = QuickMenu_CurrentPage * (QUICKMENU_MAXLINES - 2); + + z_submenu = strzone(target_submenu); + if (QuickMenu_CurrentSubMenu) + strunzone(QuickMenu_CurrentSubMenu); + QuickMenu_CurrentSubMenu = strzone(z_submenu); + - QuickMenu_IsLastPage = TRUE; ++ QuickMenu_IsLastPage = true; + QuickMenu_Entries = 0; + + QuickMenu_Buffer_Index = 0; + if (z_submenu != "") + { + // skip everything until the submenu open tag is found + for( ; QuickMenu_Buffer_Index < QuickMenu_Buffer_Size; ++QuickMenu_Buffer_Index) + { + s = bufstr_get(QuickMenu_Buffer, QuickMenu_Buffer_Index); + if(substring(s, 0, 1) == "S" && substring(s, 1, strlen(s) - 1) == z_submenu) + { + // printf("^3 beginning of %s\n", z_submenu); + ++QuickMenu_Buffer_Index; + break; + } + // printf("^1 skipping %s\n", s); + } + } + float total = 0; + for( ; QuickMenu_Buffer_Index < QuickMenu_Buffer_Size; ++QuickMenu_Buffer_Index) + { + s = bufstr_get(QuickMenu_Buffer, QuickMenu_Buffer_Index); + + if(z_submenu != "" && substring(s, 1, strlen(s) - 1) == z_submenu) + { + // printf("^3 end of %s\n", z_submenu); + break; + } + + if (total - QuickMenu_CurrentPage_FirstEntry >= 0) + { + ++QuickMenu_Entries; + if(QuickMenu_Entries == QUICKMENU_MAXLINES - 2) + QuickMenu_Buffer_Index_Prev = QuickMenu_Buffer_Index; + else if(QuickMenu_Entries == QUICKMENU_MAXLINES) + { + HUD_QuickMenu_clear_entry(QUICKMENU_MAXLINES - 1); + QuickMenu_Buffer_Index = QuickMenu_Buffer_Index_Prev; - QuickMenu_IsLastPage = FALSE; ++ QuickMenu_IsLastPage = false; + break; + } + } + + // NOTE: entries are loaded starting from 1, not from 0 + if(substring(s, 0, 1) == "S") // submenu + { + if (total - QuickMenu_CurrentPage_FirstEntry >= 0) + HUD_QuickMenu_load_entry(QuickMenu_Entries, substring(s, 1, strlen(s) - 1), ""); + HUD_QuickMenu_skip_submenu(substring(s, 1, strlen(s) - 1)); + } + else if (total - QuickMenu_CurrentPage_FirstEntry >= 0) + { + ++QuickMenu_Buffer_Index; + string cmd = bufstr_get(QuickMenu_Buffer, QuickMenu_Buffer_Index); + HUD_QuickMenu_load_entry(QuickMenu_Entries, substring(s, 1, strlen(s) - 1), cmd); + } + + ++total; + } + strunzone(z_submenu); + if (QuickMenu_Entries == 0) + { + HUD_QuickMenu_Close(); + return 0; + } + QuickMenu_TimeOut = time + autocvar_hud_panel_quickmenu_time; + return 1; +} + +void HUD_QuickMenu_Open() +{ + if(!HUD_QuickMenu_Buffer_Init()) return; + + hud_panel_quickmenu = 1; + if(autocvar_hud_cursormode) + setcursormode(1); + hudShiftState = 0; + + HUD_QuickMenu_Page("", 0); +} + +float HUD_QuickMenu_ActionForNumber(float num) +{ + if (!QuickMenu_IsLastPage) + { + if (num < 0 || num >= QUICKMENU_MAXLINES) + return 0; + if (num == QUICKMENU_MAXLINES - 1) + return 0; + if (num == 0) + { + HUD_QuickMenu_Page(QuickMenu_CurrentSubMenu, +1); + return 0; + } + } else if (num <= 0 || num > QuickMenu_Entries) + return 0; + + if (QuickMenu_Command[num] != "") + { + localcmd(strcat("\n", QuickMenu_Command[num], "\n")); + return 1; + } + if (QuickMenu_Description[num] != "") + HUD_QuickMenu_Page(QuickMenu_Description[num], 0); + return 0; +} + +float HUD_QuickMenu_InputEvent(float bInputType, float nPrimary, float nSecondary) +{ + // we only care for keyboard events + if(bInputType == 2) + return false; + + if(!HUD_QuickMenu_IsOpened() || autocvar__hud_configure || mv_active) + return false; + + if(bInputType == 3) + { + mousepos_x = nPrimary; + mousepos_y = nSecondary; + return true; + } + + // allow console bind to work + string con_keys; + float keys; + con_keys = findkeysforcommand("toggleconsole", 0); + keys = tokenize(con_keys); // findkeysforcommand returns data for this + + float hit_con_bind = 0, i; + for (i = 0; i < keys; ++i) + { + if(nPrimary == stof(argv(i))) + hit_con_bind = 1; + } + + if(bInputType == 0) { + if(nPrimary == K_ALT) hudShiftState |= S_ALT; + if(nPrimary == K_CTRL) hudShiftState |= S_CTRL; + if(nPrimary == K_SHIFT) hudShiftState |= S_SHIFT; + } + else if(bInputType == 1) { + if(nPrimary == K_ALT) hudShiftState -= (hudShiftState & S_ALT); + if(nPrimary == K_CTRL) hudShiftState -= (hudShiftState & S_CTRL); + if(nPrimary == K_SHIFT) hudShiftState -= (hudShiftState & S_SHIFT); + } + + if(nPrimary == K_ESCAPE) + { + if (bInputType == 1) + return true; + HUD_QuickMenu_Close(); + } + else if(nPrimary >= '0' && nPrimary <= '9') + { + if (bInputType == 1) + return true; + HUD_QuickMenu_ActionForNumber(stof(chr2str(nPrimary))); + } + if(nPrimary == K_MOUSE1) + { + if(bInputType == 0) // key pressed + mouseClicked |= S_MOUSE1; + else if(bInputType == 1) // key released + mouseClicked -= (mouseClicked & S_MOUSE1); + } + else if(nPrimary == K_MOUSE2) + { + if(bInputType == 0) // key pressed + mouseClicked |= S_MOUSE2; + else if(bInputType == 1) // key released + mouseClicked -= (mouseClicked & S_MOUSE2); + } + else if(hit_con_bind) + return false; + + return true; +} +void HUD_QuickMenu_Mouse() +{ + if(mv_active) return; + + if(!mouseClicked) + if(prevMouseClicked & S_MOUSE2) + { + HUD_QuickMenu_Close(); + return; + } + + if(!autocvar_hud_cursormode) + { + mousepos = mousepos + getmousepos() * autocvar_menu_mouse_speed; + + mousepos_x = bound(0, mousepos_x, vid_conwidth); + mousepos_y = bound(0, mousepos_y, vid_conheight); + } + - HUD_Panel_UpdateCvars() ++ HUD_Panel_UpdateCvars(); + + if(panel_bg_padding) + { + panel_pos += '1 1 0' * panel_bg_padding; + panel_size -= '2 2 0' * panel_bg_padding; + } + + float first_entry_pos, entries_height; + vector fontsize; + fontsize = '1 1 0' * (panel_size_y / QUICKMENU_MAXLINES); + first_entry_pos = panel_pos_y + ((QUICKMENU_MAXLINES - QuickMenu_Entries) * fontsize_y) / 2; + entries_height = panel_size_y - ((QUICKMENU_MAXLINES - QuickMenu_Entries) * fontsize_y); + + if (mousepos_x >= panel_pos_x && mousepos_y >= first_entry_pos && mousepos_x <= panel_pos_x + panel_size_x && mousepos_y <= first_entry_pos + entries_height) + { + float entry_num; + entry_num = floor((mousepos_y - first_entry_pos) / fontsize_y); + if (QuickMenu_IsLastPage || entry_num != QUICKMENU_MAXLINES - 2) + { + panel_pos_y = first_entry_pos + entry_num * fontsize_y; + vector color; + if(mouseClicked & S_MOUSE1) + color = '0.5 1 0.5'; + else if(hudShiftState & S_CTRL) + color = '1 1 0.3'; + else + color = '1 1 1'; + drawfill(panel_pos, eX * panel_size_x + eY * fontsize_y, color, .2, DRAWFLAG_NORMAL); + + if(!mouseClicked && (prevMouseClicked & S_MOUSE1)) + { + float f; + if (entry_num < QUICKMENU_MAXLINES - 1) + f = HUD_QuickMenu_ActionForNumber(entry_num + 1); + else + f = HUD_QuickMenu_ActionForNumber(0); + if(f && !(hudShiftState & S_CTRL)) + HUD_QuickMenu_Close(); + } + } + } + + vector cursorsize = '32 32 0'; + drawpic(mousepos, strcat("gfx/menu/", autocvar_menu_skin, "/cursor.tga"), cursorsize, '1 1 1', 0.8, DRAWFLAG_NORMAL); + + prevMouseClicked = mouseClicked; +} +void HUD_QuickMenu_DrawEntry(vector pos, string s, vector fontsize) +{ + string entry; + float offset; + entry = textShortenToWidth(s, panel_size_x, fontsize, stringwidth_colors); + if (autocvar_hud_panel_quickmenu_align > 0) + { + offset = (panel_size_x - stringwidth_colors(entry, fontsize)) * min(autocvar_hud_panel_quickmenu_align, 1); + drawcolorcodedstring(pos + eX * offset, entry, fontsize, panel_fg_alpha, DRAWFLAG_ADDITIVE); + } + else + drawcolorcodedstring(pos, entry, fontsize, panel_fg_alpha, DRAWFLAG_ADDITIVE); +} +void HUD_QuickMenu(void) +{ + if(!autocvar__hud_configure) + { + if (hud_configure_prev && hud_configure_prev != -1) + HUD_QuickMenu_Close(); + + if(!hud_draw_maximized) return; + if(mv_active) return; + //if(!autocvar_hud_panel_quickmenu) return; + if(!hud_panel_quickmenu) return; + + if(time > QuickMenu_TimeOut) + { + HUD_QuickMenu_Close(); + return; + } + } + else + { + if(!HUD_QuickMenu_IsOpened()) + { + QuickMenu_Entries = 1; + HUD_QuickMenu_load_entry(QuickMenu_Entries, sprintf(_("Submenu%d"), QuickMenu_Entries), ""); + ++QuickMenu_Entries; + HUD_QuickMenu_load_entry(QuickMenu_Entries, sprintf(_("Submenu%d"), QuickMenu_Entries), ""); + ++QuickMenu_Entries; + // although real command doesn't matter here, it must not be empty + // otherwise the entry is displayed like a submenu + for (; QuickMenu_Entries < QUICKMENU_MAXLINES - 1; ++QuickMenu_Entries) + HUD_QuickMenu_load_entry(QuickMenu_Entries, sprintf(_("Command%d"), QuickMenu_Entries), "-"); + ++QuickMenu_Entries; + HUD_QuickMenu_clear_entry(QuickMenu_Entries); - QuickMenu_IsLastPage = FALSE; ++ QuickMenu_IsLastPage = false; + } + } + + HUD_Panel_UpdateCvars(); + + HUD_Panel_DrawBg(1); + + if(panel_bg_padding) + { + panel_pos += '1 1 0' * panel_bg_padding; + panel_size -= '2 2 0' * panel_bg_padding; + } + + float i; + vector fontsize; + string color; + fontsize = '1 1 0' * (panel_size_y / QUICKMENU_MAXLINES); + + if (!QuickMenu_IsLastPage) + { + color = "^5"; + HUD_QuickMenu_DrawEntry(panel_pos + eY * (panel_size_y - fontsize_y), sprintf("%d: %s%s", 0, color, _("Continue...")), fontsize); + } + else + panel_pos_y += ((QUICKMENU_MAXLINES - QuickMenu_Entries) * fontsize_y) / 2; + + for (i = 1; i <= QuickMenu_Entries; ++i) { + if (QuickMenu_Description[i] == "") + break; + if (QuickMenu_Command[i] == "") + color = "^4"; + else + color = "^3"; + HUD_QuickMenu_DrawEntry(panel_pos, sprintf("%d: %s%s", i, color, QuickMenu_Description[i]), fontsize); + panel_pos_y += fontsize_y; + } +} + +#define QUICKMENU_SMENU(submenu) \ + if(QuickMenu_Buffer_Size < QUICKMENU_BUFFER_MAXENTRIES) \ + bufstr_set(QuickMenu_Buffer, QuickMenu_Buffer_Size, strcat("S", submenu)); \ + ++QuickMenu_Buffer_Size; + +#define QUICKMENU_ENTRY(title,command) { \ + if(QuickMenu_Buffer_Size + 1 < QUICKMENU_BUFFER_MAXENTRIES) \ + { \ + bufstr_set(QuickMenu_Buffer, QuickMenu_Buffer_Size, strcat("T", title)); \ + ++QuickMenu_Buffer_Size; \ + bufstr_set(QuickMenu_Buffer, QuickMenu_Buffer_Size, command); \ + } \ + ++QuickMenu_Buffer_Size; \ +} + +// useful to Translate a string inside the Command +#define QUICKMENU_ENTRY_TC(title,command,text,translated_text) \ + if(cvar_string("prvm_language") == "en") \ + QUICKMENU_ENTRY(title, sprintf(command, text)) \ + else if(!autocvar_hud_panel_quickmenu_translatecommands || translated_text == text) \ + QUICKMENU_ENTRY(strcat("(en)", title), sprintf(command, text)) \ + else \ + QUICKMENU_ENTRY(strcat("(", cvar_string("prvm_language"), ")", title), sprintf(command, translated_text)) + +void HUD_QuickMenu_Load_DefaultEntries() +{ +QUICKMENU_SMENU(CTX(_("QMCMD^Chat"))) + QUICKMENU_ENTRY_TC(CTX(_("QMCMD^nice one")), "say %s", ":-) / nice one", CTX(_("QMCMD^:-) / nice one"))) + QUICKMENU_ENTRY_TC(CTX(_("QMCMD^good game")), "say %s", "good game", CTX(_("QMCMD^good game"))) + QUICKMENU_ENTRY_TC(CTX(_("QMCMD^hi / good luck")), "say %s", "hi / good luck and have fun", CTX(_("QMCMD^hi / good luck and have fun"))) +QUICKMENU_SMENU(CTX(_("QMCMD^Chat"))) + +QUICKMENU_SMENU(CTX(_("QMCMD^Team chat"))) + QUICKMENU_ENTRY_TC(CTX(_("QMCMD^quad soon")), "say_team %s", "quad soon", CTX(_("QMCMD^quad soon"))) + QUICKMENU_ENTRY_TC(CTX(_("QMCMD^free item, icon")), "say_team %s; g_waypointsprite_team_here_p", "free item %x^7 (l:%y^7)", CTX(_("QMCMD^free item %x^7 (l:%y^7)"))) + QUICKMENU_ENTRY_TC(CTX(_("QMCMD^took item, icon")), "say_team %s; g_waypointsprite_team_here", "took item (l:%l^7)", CTX(_("QMCMD^took item (l:%l^7)"))) + QUICKMENU_ENTRY_TC(CTX(_("QMCMD^negative")), "say_team %s", "negative", CTX(_("QMCMD^negative"))) + QUICKMENU_ENTRY_TC(CTX(_("QMCMD^positive")), "say_team %s", "positive", CTX(_("QMCMD^positive"))) + QUICKMENU_ENTRY_TC(CTX(_("QMCMD^need help, icon")), "say_team %s; g_waypointsprite_team_helpme; cmd voice needhelp", "need help (l:%l^7) (h:%h^7 a:%a^7 w:%w^7)", CTX(_("QMCMD^need help (l:%l^7) (h:%h^7 a:%a^7 w:%w^7)"))) + QUICKMENU_ENTRY_TC(CTX(_("QMCMD^enemy seen, icon")), "say_team %s; g_waypointsprite_team_danger_p; cmd voice incoming", "enemy seen (l:%y^7)", CTX(_("QMCMD^enemy seen (l:%y^7)"))) + QUICKMENU_ENTRY_TC(CTX(_("QMCMD^flag seen, icon")), "say_team %s; g_waypointsprite_team_here_p; cmd voice seenflag", "flag seen (l:%y^7)", CTX(_("QMCMD^flag seen (l:%y^7)"))) + QUICKMENU_ENTRY_TC(CTX(_("QMCMD^defending, icon")), "say_team %s; g_waypointsprite_team_here", "defending (l:%l^7) (h:%h^7 a:%a^7 w:%w^7)", CTX(_("QMCMD^defending (l:%l^7) (h:%h^7 a:%a^7 w:%w^7)"))) + QUICKMENU_ENTRY_TC(CTX(_("QMCMD^roaming, icon")), "say_team %s; g_waypointsprite_team_here", "roaming (l:%l^7) (h:%h^7 a:%a^7 w:%w^7)", CTX(_("QMCMD^roaming (l:%l^7) (h:%h^7 a:%a^7 w:%w^7)"))) + QUICKMENU_ENTRY_TC(CTX(_("QMCMD^attacking, icon")), "say_team %s; g_waypointsprite_team_here", "attacking (l:%l^7) (h:%h^7 a:%a^7 w:%w^7)", CTX(_("QMCMD^attacking (l:%l^7) (h:%h^7 a:%a^7 w:%w^7)"))) + QUICKMENU_ENTRY_TC(CTX(_("QMCMD^killed flag, icon")), "say_team %s; g_waypointsprite_team_here_p", "killed flagcarrier (l:%y^7)", CTX(_("QMCMD^killed flagcarrier (l:%y^7)"))) + QUICKMENU_ENTRY_TC(CTX(_("QMCMD^dropped flag, icon")), "say_team %s; g_waypointsprite_team_here_d", "dropped flag (l:%d^7)", CTX(_("QMCMD^dropped flag (l:%d^7)"))) + QUICKMENU_ENTRY_TC(CTX(_("QMCMD^drop gun, icon")), "say_team %s; g_waypointsprite_team_here; wait; dropweapon", "dropped gun %w^7 (l:%l^7)", CTX(_("QMCMD^dropped gun %w^7 (l:%l^7)"))) + QUICKMENU_ENTRY_TC(CTX(_("QMCMD^drop flag/key, icon")), "say_team %s; g_waypointsprite_team_here; wait; +use", "dropped flag/key %w^7 (l:%l^7)", CTX(_("QMCMD^dropped flag/key %w^7 (l:%l^7)"))) +QUICKMENU_SMENU(CTX(_("QMCMD^Team chat"))) + +QUICKMENU_SMENU(CTX(_("QMCMD^Settings"))) + QUICKMENU_SMENU(CTX(_("QMCMD^View/HUD settings"))) + QUICKMENU_ENTRY(CTX(_("QMCMD^1st/3rd person view")), "toggle chase_active") + QUICKMENU_ENTRY(CTX(_("QMCMD^Player models like mine on/off")), "toggle cl_forceplayermodels") + QUICKMENU_ENTRY(CTX(_("QMCMD^Names above players on/off")), "toggle hud_shownames") + QUICKMENU_ENTRY(CTX(_("QMCMD^Crosshair per weapon on/off")), "toggle crosshair_per_weapon") + QUICKMENU_ENTRY(CTX(_("QMCMD^FPS on/off")), "toggle hud_panel_engineinfo") + QUICKMENU_ENTRY(CTX(_("QMCMD^Net graph on/off")), "toggle shownetgraph") + QUICKMENU_SMENU(CTX(_("QMCMD^View/HUD settings"))) + + QUICKMENU_SMENU(CTX(_("QMCMD^Sound settings"))) + QUICKMENU_ENTRY(CTX(_("QMCMD^Hit sound on/off")), "toggle cl_hitsound") + QUICKMENU_ENTRY(CTX(_("QMCMD^Chat sound on/off")), "toggle cl_chatsound") + QUICKMENU_SMENU(CTX(_("QMCMD^Sound settings"))) + + QUICKMENU_SMENU(CTX(_("QMCMD^Spectator camera"))) + QUICKMENU_ENTRY(CTX(_("QMCMD^1st person")), "chase_active 0; -use") + QUICKMENU_ENTRY(CTX(_("QMCMD^3rd person free")), "chase_active 1; +use") + QUICKMENU_ENTRY(CTX(_("QMCMD^3rd person behind")), "chase_active 1; -use") + QUICKMENU_SMENU(CTX(_("QMCMD^Spectator camera"))) + + QUICKMENU_SMENU(CTX(_("QMCMD^Observer camera"))) + QUICKMENU_ENTRY(CTX(_("QMCMD^Increase speed")), "weapnext") + QUICKMENU_ENTRY(CTX(_("QMCMD^Decrease speed")), "weapprev") + QUICKMENU_ENTRY(CTX(_("QMCMD^Wall collision off")), "-use") + QUICKMENU_ENTRY(CTX(_("QMCMD^Wall collision on")), "+use") + QUICKMENU_SMENU(CTX(_("QMCMD^Observer camera"))) + + QUICKMENU_ENTRY(CTX(_("QMCMD^toggle fullscreen")), "toggle vid_fullscreen; vid_restart") +QUICKMENU_SMENU(CTX(_("QMCMD^Settings"))) + +QUICKMENU_SMENU(CTX(_("QMCMD^Call a vote"))) + QUICKMENU_ENTRY(CTX(_("QMCMD^Restart the map")), "vcall restart") + QUICKMENU_ENTRY(CTX(_("QMCMD^End match")), "vcall endmatch") + QUICKMENU_ENTRY(CTX(_("QMCMD^Reduce match time")), "vcall reducematchtime") + QUICKMENU_ENTRY(CTX(_("QMCMD^Extend match time")), "vcall extendmatchtime") + QUICKMENU_ENTRY(CTX(_("QMCMD^Shuffle teams")), "vcall shuffleteams") +QUICKMENU_SMENU(CTX(_("QMCMD^Call a vote"))) +} +#undef QUICKMENU_SMENU +#undef QUICKMENU_ENTRY + + // Buffs (#18) // void HUD_Buffs(void) diff --cc qcsrc/client/hud.qh index fb4816726,16a7645fd..ceae47bee --- a/qcsrc/client/hud.qh +++ b/qcsrc/client/hud.qh @@@ -102,58 -101,45 +107,57 @@@ string panel_bg_padding_str float current_player; - float mv_active; - + float GetPlayerColorForce(int i); - ++float GetPlayerColor(int i); +.float panel_showflags; +const float PANEL_SHOW_NEVER = 0x00; +const float PANEL_SHOW_MAINGAME = 0x01; +const float PANEL_SHOW_MINIGAME = 0x02; +const float PANEL_SHOW_ALWAYS = 0xff; +float HUD_Panel_CheckFlags(float showflags); - #define HUD_PANELS \ - HUD_PANEL(WEAPONS , HUD_Weapons , weapons, PANEL_SHOW_MAINGAME ) \ - HUD_PANEL(AMMO , HUD_Ammo , ammo, PANEL_SHOW_MAINGAME ) \ - HUD_PANEL(POWERUPS , HUD_Powerups , powerups, PANEL_SHOW_MAINGAME ) \ - HUD_PANEL(HEALTHARMOR , HUD_HealthArmor , healtharmor, PANEL_SHOW_MAINGAME ) \ - HUD_PANEL(NOTIFY , HUD_Notify , notify, PANEL_SHOW_ALWAYS ) \ - HUD_PANEL(TIMER , HUD_Timer , timer, PANEL_SHOW_ALWAYS ) \ - HUD_PANEL(RADAR , HUD_Radar , radar, PANEL_SHOW_MAINGAME ) \ - HUD_PANEL(SCORE , HUD_Score , score, PANEL_SHOW_ALWAYS ) \ - HUD_PANEL(RACETIMER , HUD_RaceTimer , racetimer, PANEL_SHOW_MAINGAME ) \ - HUD_PANEL(VOTE , HUD_Vote , vote, PANEL_SHOW_ALWAYS ) \ - HUD_PANEL(MODICONS , HUD_ModIcons , modicons, PANEL_SHOW_MAINGAME ) \ - HUD_PANEL(PRESSEDKEYS , HUD_PressedKeys , pressedkeys, PANEL_SHOW_MAINGAME ) \ - HUD_PANEL(CHAT , HUD_Chat , chat, PANEL_SHOW_ALWAYS ) \ - HUD_PANEL(ENGINEINFO , HUD_EngineInfo , engineinfo, PANEL_SHOW_ALWAYS ) \ - HUD_PANEL(INFOMESSAGES , HUD_InfoMessages , infomessages, PANEL_SHOW_MAINGAME ) \ - HUD_PANEL(PHYSICS , HUD_Physics , physics, PANEL_SHOW_MAINGAME ) \ - HUD_PANEL(CENTERPRINT , HUD_CenterPrint , centerprint, PANEL_SHOW_MAINGAME ) \ - HUD_PANEL(QUICKMENU , HUD_QuickMenu , quickmenu, PANEL_SHOW_MAINGAME ) \ - HUD_PANEL(BUFFS , HUD_Buffs , buffs, PANEL_SHOW_MAINGAME ) \ - HUD_PANEL(MINIGAME_BOARD, HUD_MinigameBoard ,minigameboard, PANEL_SHOW_MINIGAME ) \ - HUD_PANEL(MINIGAME_STATUS,HUD_MinigameStatus,minigamestatus,PANEL_SHOW_MINIGAME ) \ - HUD_PANEL(MINIGAME_HELP, HUD_MinigameHelp ,minigamehelp, PANEL_SHOW_MINIGAME ) \ - HUD_PANEL(MINIGAME_MENU, HUD_MinigameMenu ,minigamemenu, PANEL_SHOW_ALWAYS ) - - #define HUD_PANEL(NAME,draw_func,name,showflags) \ - float HUD_PANEL_##NAME; \ - void draw_func(void); \ - void RegisterHUD_Panel_##NAME() \ - { \ - HUD_PANEL_LAST = HUD_PANEL_##NAME = HUD_PANEL_NUM; \ - entity hud_panelent = spawn(); \ - hud_panel[HUD_PANEL_##NAME] = hud_panelent; \ - hud_panelent.classname = "hud_panel"; \ - hud_panelent.panel_name = #name; \ - hud_panelent.panel_id = HUD_PANEL_##NAME; \ - hud_panelent.panel_draw = draw_func; \ - hud_panelent.panel_showflags = showflags; \ - ++HUD_PANEL_NUM; \ - } \ + #define HUD_PANELS(HUD_PANEL) \ - HUD_PANEL(WEAPONS , HUD_Weapons , weapons) \ - HUD_PANEL(AMMO , HUD_Ammo , ammo) \ - HUD_PANEL(POWERUPS , HUD_Powerups , powerups) \ - HUD_PANEL(HEALTHARMOR , HUD_HealthArmor , healtharmor) \ - HUD_PANEL(NOTIFY , HUD_Notify , notify) \ - HUD_PANEL(TIMER , HUD_Timer , timer) \ - HUD_PANEL(RADAR , HUD_Radar , radar) \ - HUD_PANEL(SCORE , HUD_Score , score) \ - HUD_PANEL(RACETIMER , HUD_RaceTimer , racetimer) \ - HUD_PANEL(VOTE , HUD_Vote , vote) \ - HUD_PANEL(MODICONS , HUD_ModIcons , modicons) \ - HUD_PANEL(PRESSEDKEYS , HUD_PressedKeys , pressedkeys) \ - HUD_PANEL(CHAT , HUD_Chat , chat) \ - HUD_PANEL(ENGINEINFO , HUD_EngineInfo , engineinfo) \ - HUD_PANEL(INFOMESSAGES , HUD_InfoMessages , infomessages) \ - HUD_PANEL(PHYSICS , HUD_Physics , physics) \ - HUD_PANEL(CENTERPRINT , HUD_CenterPrint , centerprint) \ - HUD_PANEL(BUFFS , HUD_Buffs , buffs) - -#define HUD_PANEL(NAME, draw_func, name) \ ++ HUD_PANEL(WEAPONS , HUD_Weapons , weapons, PANEL_SHOW_MAINGAME ) \ ++ HUD_PANEL(AMMO , HUD_Ammo , ammo, PANEL_SHOW_MAINGAME ) \ ++ HUD_PANEL(POWERUPS , HUD_Powerups , powerups, PANEL_SHOW_MAINGAME ) \ ++ HUD_PANEL(HEALTHARMOR , HUD_HealthArmor , healtharmor, PANEL_SHOW_MAINGAME ) \ ++ HUD_PANEL(NOTIFY , HUD_Notify , notify, PANEL_SHOW_ALWAYS ) \ ++ HUD_PANEL(TIMER , HUD_Timer , timer, PANEL_SHOW_ALWAYS ) \ ++ HUD_PANEL(RADAR , HUD_Radar , radar, PANEL_SHOW_MAINGAME ) \ ++ HUD_PANEL(SCORE , HUD_Score , score, PANEL_SHOW_ALWAYS ) \ ++ HUD_PANEL(RACETIMER , HUD_RaceTimer , racetimer, PANEL_SHOW_MAINGAME ) \ ++ HUD_PANEL(VOTE , HUD_Vote , vote, PANEL_SHOW_ALWAYS ) \ ++ HUD_PANEL(MODICONS , HUD_ModIcons , modicons, PANEL_SHOW_MAINGAME ) \ ++ HUD_PANEL(PRESSEDKEYS , HUD_PressedKeys , pressedkeys, PANEL_SHOW_MAINGAME ) \ ++ HUD_PANEL(CHAT , HUD_Chat , chat, PANEL_SHOW_ALWAYS ) \ ++ HUD_PANEL(ENGINEINFO , HUD_EngineInfo , engineinfo, PANEL_SHOW_ALWAYS ) \ ++ HUD_PANEL(INFOMESSAGES , HUD_InfoMessages , infomessages, PANEL_SHOW_MAINGAME ) \ ++ HUD_PANEL(PHYSICS , HUD_Physics , physics, PANEL_SHOW_MAINGAME ) \ ++ HUD_PANEL(CENTERPRINT , HUD_CenterPrint , centerprint, PANEL_SHOW_MAINGAME ) \ ++ HUD_PANEL(QUICKMENU , HUD_QuickMenu , quickmenu, PANEL_SHOW_MAINGAME ) \ ++ HUD_PANEL(BUFFS , HUD_Buffs , buffs, PANEL_SHOW_MAINGAME ) \ ++ HUD_PANEL(MINIGAME_BOARD, HUD_MinigameBoard ,minigameboard, PANEL_SHOW_MINIGAME ) \ ++ HUD_PANEL(MINIGAME_STATUS,HUD_MinigameStatus,minigamestatus,PANEL_SHOW_MINIGAME ) \ ++ HUD_PANEL(MINIGAME_HELP, HUD_MinigameHelp ,minigamehelp, PANEL_SHOW_MINIGAME ) \ ++ HUD_PANEL(MINIGAME_MENU, HUD_MinigameMenu ,minigamemenu, PANEL_SHOW_ALWAYS ) ++ ++#define HUD_PANEL(NAME, draw_func, name, showflags) \ + int HUD_PANEL_##NAME; \ + void draw_func(void); \ + void RegisterHUD_Panel_##NAME() { \ + HUD_PANEL_LAST = HUD_PANEL_##NAME = HUD_PANEL_NUM; \ + entity hud_panelent = spawn(); \ + hud_panel[HUD_PANEL_##NAME] = hud_panelent; \ + hud_panelent.classname = "hud_panel"; \ + hud_panelent.panel_name = #name; \ + hud_panelent.panel_id = HUD_PANEL_##NAME; \ - hud_panelent.panel_draw = draw_func; \ ++ hud_panelent.panel_draw = draw_func; \ ++ hud_panelent.panel_showflags = showflags; \ + HUD_PANEL_NUM++; \ + } \ ACCUMULATE_FUNCTION(RegisterHUD_Panels, RegisterHUD_Panel_##NAME); - HUD_PANELS + HUD_PANELS(HUD_PANEL) #undef HUD_PANEL #define HUD_PANEL(NAME) hud_panel[HUD_PANEL_##NAME] diff --cc qcsrc/client/hud_config.qc index cfde7c24c,898f3eb56..d5b3f0546 --- a/qcsrc/client/hud_config.qc +++ b/qcsrc/client/hud_config.qc @@@ -927,15 -934,12 +933,15 @@@ float HUD_Panel_Check_Mouse_Pos(float a j += 1; panel = hud_panel[i]; + if ( ! HUD_Panel_CheckFlags(panel.panel_showflags) ) + continue; + - HUD_Panel_UpdatePosSize() + HUD_Panel_UpdatePosSize(); - border = max(8, panel_bg_border); // FORCED border so a small border size doesn't mean you can't resize + float border = max(8, panel_bg_border); // FORCED border so a small border size doesn't mean you can't resize // move - if(allow_move && mousepos_x > panel_pos_x && mousepos_y > panel_pos_y && mousepos_x < panel_pos_x + panel_size_x && mousepos_y < panel_pos_y + panel_size_y) + if(allow_move && mousepos.x > panel_pos.x && mousepos.y > panel_pos.y && mousepos.x < panel_pos.x + panel_size.x && mousepos.y < panel_pos.y + panel_size.y) { return 1; } @@@ -1011,15 -1015,12 +1017,15 @@@ void HUD_Panel_Highlight(float allow_mo j += 1; panel = hud_panel[i]; + if ( ! HUD_Panel_CheckFlags(panel.panel_showflags) ) + continue; + - HUD_Panel_UpdatePosSize() + HUD_Panel_UpdatePosSize(); - border = max(8, panel_bg_border); // FORCED border so a small border size doesn't mean you can't resize + float border = max(8, panel_bg_border); // FORCED border so a small border size doesn't mean you can't resize // move - if(allow_move && mousepos_x > panel_pos_x && mousepos_y > panel_pos_y && mousepos_x < panel_pos_x + panel_size_x && mousepos_y < panel_pos_y + panel_size_y) + if(allow_move && mousepos.x > panel_pos.x && mousepos.y > panel_pos.y && mousepos.x < panel_pos.x + panel_size.x && mousepos.y < panel_pos.y + panel_size.y) { highlightedPanel = hud_panel[i]; HUD_Panel_FirstInDrawQ(i); diff --cc qcsrc/client/laser.qc index f6b3fc4cb,a74c6683a..987ae3cad --- a/qcsrc/client/laser.qc +++ b/qcsrc/client/laser.qc @@@ -1,14 -12,14 +12,6 @@@ // a laser goes from origin in direction angles // it has color 'colormod' // and stops when something is in the way - .float cnt; // end effect -.int cnt; // end effect --.vector colormod; - .float state; // on-off - .float count; // flags for the laser -.int state; // on-off -.int count; // flags for the laser --.vector velocity; --.float alpha; --.float scale; // scaling factor of the thickness --.float modelscale; // scaling factor of the dlight void Draw_Laser() { diff --cc qcsrc/client/main.qc index 000000000,13f9545fa..fce3d1071 mode 000000,100644..100644 --- a/qcsrc/client/main.qc +++ b/qcsrc/client/main.qc @@@ -1,0 -1,1319 +1,1519 @@@ ++#include "announcer.qh" ++#include "conquest.qh" ++#include "controlpoint.qh" ++#include "generator.qh" + #include "mapvoting.qh" + #include "modeleffects.qh" + #include "particles.qh" + #include "scoreboard.qh" + #include "shownames.qh" + #include "target_music.qh" -#include "tturrets.qh" ++#include "../common/turrets/cl_turrets.qh" + #include "tuba.qh" + #include "wall.qh" + #include "waypointsprites.qh" + -#include "vehicles/vehicles.qh" ++#include "../common/effects.qh" + -#include "../server/vehicles/bumblebee.qh" ++#include "../common/vehicles/unit/bumblebee.qh" ++#include "../common/vehicles/cl_vehicles.qh" + + #include "../common/net_notice.qh" + + #include "../common/monsters/monsters.qh" ++#include "../common/turrets/turrets.qh" ++#include "../common/vehicles/vehicles.qh" + + #include "../warpzonelib/client.qh" + + // -------------------------------------------------------------------------- + // BEGIN REQUIRED CSQC FUNCTIONS + //include "main.qh" + + entity clearentity_ent; + void clearentity(entity e) + { + if (!clearentity_ent) + { + clearentity_ent = spawn(); + clearentity_ent.classname = "clearentity"; + } + int n = e.entnum; + copyentity(clearentity_ent, e); + e.entnum = n; + } + + #define DP_CSQC_ENTITY_REMOVE_IS_B0RKED + void menu_show_error() + { + drawstring('0 200 0', _("ERROR - MENU IS VISIBLE BUT NO MENU WAS DEFINED!"), '8 8 0', '1 0 0', 1, 0); + } + + // CSQC_Init : Called every time the CSQC code is initialized (essentially at map load) + // Useful for precaching things + + void menu_sub_null() + { + } + + string forcefog; + void WaypointSprite_Load(); + void ConsoleCommand_macro_init(); + void CSQC_Init(void) + { + prvm_language = cvar_string("prvm_language"); + + #ifdef WATERMARK + dprintf("^4CSQC Build information: ^1%s\n", WATERMARK); + #endif + + int i; + + binddb = db_create(); + tempdb = db_create(); + ClientProgsDB = db_load("client.db"); + compressShortVector_init(); + + draw_endBoldFont(); + menu_visible = false; + menu_show = menu_show_error; + menu_action = func_null; + + for(i = 0; i < 255; ++i) + if(getplayerkeyvalue(i, "viewentity") == "") + break; + maxclients = i; + ++ localcmd("\nalias nobobbing \"cl_bob 0; cl_bob2 0; cl_followmodel 0; cl_leanmodel 0\"\n"); ++ localcmd("\nalias quickmenu \"cl_cmd hud quickmenu\"\n"); ++ localcmd("\nalias physics \"cmd physics ${* ?}\"\n"); ++ localcmd("\nalias editmob \"cmd editmob ${* ?}\"\n"); ++ localcmd("\nalias spawnmob \"cmd editmob spawn ${* ?}\"\n"); ++ localcmd("\nalias killmob \"cmd editmob kill ${* ?}\"\n"); ++ if(getkeybind(K_F8) == "") { localcmd("\nbind f8 \"cl_cmd hud quickmenu\"\n"); } ++ if(getkeybind(K_F9) == "") { localcmd("\nbind f9 \"cl_cmd hud minigame\"\n"); } ++ ++ localcmd("\nalias weapon_hmg \"impulse 248\"\n"); ++ localcmd("\nalias weapon_shockwave \"impulse 250\"\n"); ++ localcmd("\nalias weapon_arc \"impulse 251\"\n"); ++ + //registercommand("hud_configure"); + //registercommand("hud_save"); + //registercommand("menu_action"); + + ConsoleCommand_macro_init(); + + registercvar("hud_usecsqc", "1"); + registercvar("scoreboard_columns", "default"); + ++ registercvar("cl_freeze", "0"); ++ registercvar("cl_pony", "0"); ++ registercvar("cl_pony_skin", "0"); ++ registercvar("cl_sparkle", "0"); ++ registercvar("cl_multijump", "0"); ++ registercvar("cl_dodging", "0"); ++ registercvar("cl_spawn_near_teammate", "1"); ++ registercvar("cl_damnfurries", "0"); ++ registercvar("cl_robot", "0"); ++ registercvar("cl_thestars", "0"); ++ registercvar("cl_charge", "0"); ++ registercvar("cl_magical_hax", ""); + registercvar("cl_nade_type", "3"); + registercvar("cl_pokenade_type", "zombie"); ++ registercvar("cl_autovote", ""); ++ registercvar("cl_nocarry", "0"); ++ registercvar("cl_buffs_autoreplace", "1"); ++ registercvar("cl_physics", "default"); // default is invalid, so it will be overridden by standard physics cvars ++ ++ // these need to be defined for quickmenu to work ++ registercvar("hud_panel_quickmenu_pos", "0.010000 0.410000"); ++ registercvar("hud_panel_quickmenu_size", "0.210000 0.250000"); ++ registercvar("hud_panel_quickmenu_bg", ""); ++ registercvar("hud_panel_quickmenu_bg_color", ""); ++ registercvar("hud_panel_quickmenu_bg_color_team", ""); ++ registercvar("hud_panel_quickmenu_bg_alpha", ""); ++ registercvar("hud_panel_quickmenu_bg_border", ""); ++ registercvar("hud_panel_quickmenu_bg_padding", ""); ++ registercvar("hud_panel_quickmenu_align", "0"); ++ ++ // these need to be defined for buffs to work ++ registercvar("hud_panel_buffs", "1"); ++ registercvar("hud_panel_buffs_pos", "0.45 0.855"); ++ registercvar("hud_panel_buffs_size", "0.05 0.07"); ++ registercvar("hud_panel_buffs_bg", ""); ++ registercvar("hud_panel_buffs_bg_color", ""); ++ registercvar("hud_panel_buffs_bg_color_team", ""); ++ registercvar("hud_panel_buffs_bg_alpha", ""); ++ registercvar("hud_panel_buffs_bg_border", ""); ++ registercvar("hud_panel_buffs_bg_padding", ""); ++ //registercvar("hud_panel_buffs_iconalign", "0"); ++ ++ ++ // these need to be defined for minigames to work ++ registercvar("hud_panel_minigameboard", "1"); ++ registercvar("hud_panel_minigameboard_pos", "0.22 0.15"); ++ registercvar("hud_panel_minigameboard_size", "0.50 0.60"); ++ registercvar("hud_panel_minigameboard_bg", "border_small"); ++ registercvar("hud_panel_minigameboard_bg_color", ""); ++ registercvar("hud_panel_minigameboard_bg_color_team", ""); ++ registercvar("hud_panel_minigameboard_bg_alpha", ""); ++ registercvar("hud_panel_minigameboard_bg_border", ""); ++ registercvar("hud_panel_minigameboard_bg_padding", ""); ++ ++ registercvar("hud_panel_minigamestatus", "1"); ++ registercvar("hud_panel_minigamestatus_pos", "0.74 0.15"); ++ registercvar("hud_panel_minigamestatus_size", "0.2 0.60"); ++ registercvar("hud_panel_minigamestatus_bg", "border_small"); ++ registercvar("hud_panel_minigamestatus_bg_color", ""); ++ registercvar("hud_panel_minigamestatus_bg_color_team", ""); ++ registercvar("hud_panel_minigamestatus_bg_alpha", ""); ++ registercvar("hud_panel_minigamestatus_bg_border", ""); ++ registercvar("hud_panel_minigamestatus_bg_padding", ""); ++ ++ registercvar("hud_panel_minigamehelp", "1"); ++ registercvar("hud_panel_minigamehelp_pos", "0.22 0.78"); ++ registercvar("hud_panel_minigamehelp_size", "0.50 0.20"); ++ registercvar("hud_panel_minigamehelp_bg", ""); ++ registercvar("hud_panel_minigamehelp_bg_color", ""); ++ registercvar("hud_panel_minigamehelp_bg_color_team", ""); ++ registercvar("hud_panel_minigamehelp_bg_alpha", ""); ++ registercvar("hud_panel_minigamehelp_bg_border", ""); ++ registercvar("hud_panel_minigamehelp_bg_padding", ""); ++ ++ registercvar("hud_panel_minigamemenu", "0"); ++ registercvar("hud_panel_minigamemenu_pos", "0 0.26"); ++ registercvar("hud_panel_minigamemenu_size", "0.2 0.49"); ++ registercvar("hud_panel_minigamemenu_bg", "border_small"); ++ registercvar("hud_panel_minigamemenu_bg_color", ""); ++ registercvar("hud_panel_minigamemenu_bg_color_team", ""); ++ registercvar("hud_panel_minigamemenu_bg_alpha", ""); ++ registercvar("hud_panel_minigamemenu_bg_border", ""); ++ registercvar("hud_panel_minigamemenu_bg_padding", ""); + + gametype = 0; + + // hud_fields uses strunzone on the titles! + for(i = 0; i < MAX_HUD_FIELDS; ++i) + hud_title[i] = strzone("(null)"); + + Cmd_HUD_SetFields(0); + + postinit = false; + + calledhooks = 0; + + teams = Sort_Spawn(); + players = Sort_Spawn(); + + GetTeam(NUM_SPECTATOR, true); // add specs first + + // needs to be done so early because of the constants they create + CALL_ACCUMULATED_FUNCTION(RegisterWeapons); - CALL_ACCUMULATED_FUNCTION(RegisterMonsters); ++ CALL_ACCUMULATED_FUNCTION(RegisterTurrets); ++ CALL_ACCUMULATED_FUNCTION(RegisterVehicles); + CALL_ACCUMULATED_FUNCTION(RegisterGametypes); + CALL_ACCUMULATED_FUNCTION(RegisterNotifications); + CALL_ACCUMULATED_FUNCTION(RegisterDeathtypes); + CALL_ACCUMULATED_FUNCTION(RegisterHUD_Panels); + CALL_ACCUMULATED_FUNCTION(RegisterBuffs); ++ CALL_ACCUMULATED_FUNCTION(RegisterEffects); ++ ++ initialize_minigames(); + + WaypointSprite_Load(); + + // precaches + precache_model("null"); + precache_sound("misc/hit.wav"); + precache_sound("misc/typehit.wav"); ++ precache_model("models/ctf/shield.md3"); + ++ generator_precache(); + Projectile_Precache(); + Hook_Precache(); + GibSplash_Precache(); + Casings_Precache(); - Vehicles_Precache(); - turrets_precache(); + Tuba_Precache(); + CSQCPlayer_Precache(); + + if(autocvar_cl_reticle) + { + precache_pic("gfx/reticle_normal"); + // weapon reticles are precached in weapon files + } + + get_mi_min_max_texcoords(1); // try the CLEVER way first + minimapname = strcat("gfx/", mi_shortname, "_radar.tga"); + shortmapname = mi_shortname; + + if(precache_pic(minimapname) == "") + { + // but maybe we have a non-clever minimap + minimapname = strcat("gfx/", mi_shortname, "_mini.tga"); + if(precache_pic(minimapname) == "") + minimapname = ""; // FAIL + else + get_mi_min_max_texcoords(0); // load new texcoords + } + + mi_center = (mi_min + mi_max) * 0.5; + mi_scale = mi_max - mi_min; + minimapname = strzone(minimapname); + + WarpZone_Init(); + + hud_skin_path = strzone(strcat("gfx/hud/", autocvar_hud_skin)); + hud_configure_prev = -1; + + draw_currentSkin = strzone(strcat("gfx/menu/", cvar_string("menu_skin"))); + } + + // CSQC_Shutdown : Called every time the CSQC code is shutdown (changing maps, quitting, etc) + void Shutdown(void) + { + WarpZone_Shutdown(); + + remove(teams); + remove(players); + db_close(binddb); + db_close(tempdb); + if(autocvar_cl_db_saveasdump) + db_dump(ClientProgsDB, "client.db"); + else + db_save(ClientProgsDB, "client.db"); + db_close(ClientProgsDB); + + if(camera_active) + cvar_set("chase_active",ftos(chase_active_backup)); + + // unset the event chasecam's chase_active + if(autocvar_chase_active < 0) + cvar_set("chase_active", "0"); + + if (!isdemo()) + { + if (!(calledhooks & HOOK_START)) + localcmd("\n_cl_hook_gamestart nop\n"); + if (!(calledhooks & HOOK_END)) + localcmd("\ncl_hook_gameend\n"); + } ++ ++ deactivate_minigame(); ++ HUD_MinigameMenu_Close(); + } + + .float has_team; + float SetTeam(entity o, int Team) + { + entity tm; + if(teamplay) + { + switch(Team) + { + case -1: + case NUM_TEAM_1: + case NUM_TEAM_2: + case NUM_TEAM_3: + case NUM_TEAM_4: + break; + default: + if(GetTeam(Team, false) == world) + { + dprintf("trying to switch to unsupported team %d\n", Team); + Team = NUM_SPECTATOR; + } + break; + } + } + else + { + switch(Team) + { + case -1: + case 0: + break; + default: + if(GetTeam(Team, false) == world) + { + dprintf("trying to switch to unsupported team %d\n", Team); + Team = NUM_SPECTATOR; + } + break; + } + } + if(Team == -1) // leave + { + if(o.has_team) + { + tm = GetTeam(o.team, false); + tm.team_size -= 1; + o.has_team = 0; + return true; + } + } + else + { + if (!o.has_team) + { + o.team = Team; + tm = GetTeam(Team, true); + tm.team_size += 1; + o.has_team = 1; + return true; + } + else if(Team != o.team) + { + tm = GetTeam(o.team, false); + tm.team_size -= 1; + o.team = Team; + tm = GetTeam(Team, true); + tm.team_size += 1; + return true; + } + } + return false; + } + + void Playerchecker_Think() + { + int i; + entity e; + for(i = 0; i < maxclients; ++i) + { + e = playerslots[i]; + if(GetPlayerName(i) == "") + { + if(e.sort_prev) + { + // player disconnected + SetTeam(e, -1); + RemovePlayer(e); + e.sort_prev = world; + //e.gotscores = 0; + } + } + else + { + if (!e.sort_prev) + { + // player connected + if (!e) + playerslots[i] = e = spawn(); + e.sv_entnum = i; + e.ping = 0; + e.ping_packetloss = 0; + e.ping_movementloss = 0; + //e.gotscores = 0; // we might already have the scores... + SetTeam(e, GetPlayerColor(i)); // will not hurt; later updates come with HUD_UpdatePlayerTeams + RegisterPlayer(e); + HUD_UpdatePlayerPos(e); + } + } + } + self.nextthink = time + 0.2; + } + ++void FPSReporter_Think() ++{ ++ localcmd("\ncmd report fps ", ftos(floor(cl_fps)), "\n"); ++ self.nextthink = time + sv_showfps; ++} ++ ++void FPSReporter_Init() ++{ ++ if(!sv_showfps) ++ return; ++ ++ entity e = spawn(); ++ e.think = FPSReporter_Think; ++ e.nextthink = time + 1; ++} ++ + void Porto_Init(); + void TrueAim_Init(); + void PostInit(void) + { + entity playerchecker; + playerchecker = spawn(); + playerchecker.think = Playerchecker_Think; + playerchecker.nextthink = time + 0.2; + + Porto_Init(); + TrueAim_Init(); ++ FPSReporter_Init(); ++ ++ fovlock = -1; + + postinit = true; + } + + // CSQC_InputEvent : Used to perform actions based on any key pressed, key released and mouse on the client. + // Return value should be 1 if CSQC handled the input, otherwise return 0 to have the input passed to the engine. + // All keys are in ascii. + // bInputType = 0 is key pressed, 1 is key released, 2 and 3 are mouse input. + // In the case of keyboard input, nPrimary is the ascii code, and nSecondary is 0. + // In the case of mouse input, nPrimary is xdelta, nSecondary is ydelta. + // In the case of mouse input after a setcursormode(1) call, nPrimary is xpos, nSecondary is ypos. + float CSQC_InputEvent(float bInputType, float nPrimary, float nSecondary) + { + float bSkipKey; + bSkipKey = false; + + if (HUD_Panel_InputEvent(bInputType, nPrimary, nSecondary)) + return true; + ++ if ( HUD_Radar_InputEvent(bInputType, nPrimary, nSecondary) ) ++ return true; ++ ++ if (HUD_QuickMenu_InputEvent(bInputType, nPrimary, nSecondary)) ++ return true; ++ + if (MapVote_InputEvent(bInputType, nPrimary, nSecondary)) + return true; + ++ if (HUD_Minigame_InputEvent(bInputType, nPrimary, nSecondary)) ++ return true; ++ + if(menu_visible && menu_action) + if(menu_action(bInputType, nPrimary, nSecondary)) + return true; + + return bSkipKey; + } + + // END REQUIRED CSQC FUNCTIONS + // -------------------------------------------------------------------------- + + // -------------------------------------------------------------------------- + // BEGIN OPTIONAL CSQC FUNCTIONS + void Ent_RemoveEntCS() + { + entcs_receiver[self.sv_entnum] = world; + } + void Ent_ReadEntCS() + { + int sf; + InterpolateOrigin_Undo(); + + self.classname = "entcs_receiver"; + sf = ReadByte(); + + if(sf & 1) + self.sv_entnum = ReadByte(); + if(sf & 2) + { + self.origin_x = ReadShort(); + self.origin_y = ReadShort(); + self.origin_z = ReadShort(); + setorigin(self, self.origin); + } + if(sf & 4) + { + self.angles_y = ReadByte() * 360.0 / 256; + self.angles_x = self.angles_z = 0; + } + if(sf & 8) + self.healthvalue = ReadByte() * 10; + if(sf & 16) + self.armorvalue = ReadByte() * 10; + + entcs_receiver[self.sv_entnum] = self; + self.entremove = Ent_RemoveEntCS; + self.iflags |= IFLAG_ORIGIN; + + InterpolateOrigin_Note(); + } + + void Ent_Remove(); + + void Ent_RemovePlayerScore() + { + if(self.owner) { + SetTeam(self.owner, -1); + self.owner.gotscores = 0; + for(int i = 0; i < MAX_SCORE; ++i) { + self.owner.(scores[i]) = 0; // clear all scores + } + } + } + + void Ent_ReadPlayerScore() + { + int i, n; + bool isNew; + entity o; + + // damnit -.- don't want to go change every single .sv_entnum in hud.qc AGAIN + // (no I've never heard of M-x replace-string, sed, or anything like that) + isNew = !self.owner; // workaround for DP bug + n = ReadByte()-1; + + #ifdef DP_CSQC_ENTITY_REMOVE_IS_B0RKED + if(!isNew && n != self.sv_entnum) + { + //print("A CSQC entity changed its owner!\n"); + printf("A CSQC entity changed its owner! (edict: %d, classname: %s)\n", num_for_edict(self), self.classname); + isNew = true; + Ent_Remove(); + self.enttype = ENT_CLIENT_SCORES; + } + #endif + + self.sv_entnum = n; + + if (!(playerslots[self.sv_entnum])) + playerslots[self.sv_entnum] = spawn(); + o = self.owner = playerslots[self.sv_entnum]; + o.sv_entnum = self.sv_entnum; + o.gotscores = 1; + + //if (!o.sort_prev) + // RegisterPlayer(o); + //playerchecker will do this for us later, if it has not already done so + + int sf, lf; + #if MAX_SCORE <= 8 + sf = ReadByte(); + lf = ReadByte(); + #else + sf = ReadShort(); + lf = ReadShort(); + #endif + int p; + for(i = 0, p = 1; i < MAX_SCORE; ++i, p *= 2) + if(sf & p) + { + if(lf & p) + o.(scores[i]) = ReadInt24_t(); + else + o.(scores[i]) = ReadChar(); + } + + if(o.sort_prev) + HUD_UpdatePlayerPos(o); // if not registered, we cannot do this yet! + + self.entremove = Ent_RemovePlayerScore; + } + + void Ent_ReadTeamScore() + { + int i; + entity o; + + self.team = ReadByte(); + o = self.owner = GetTeam(self.team, true); // these team numbers can always be trusted + + int sf, lf; + #if MAX_TEAMSCORE <= 8 + sf = ReadByte(); + lf = ReadByte(); + #else + sf = ReadShort(); + lf = ReadShort(); + #endif + int p; + for(i = 0, p = 1; i < MAX_TEAMSCORE; ++i, p *= 2) + if(sf & p) + { + if(lf & p) + o.(teamscores[i]) = ReadInt24_t(); + else + o.(teamscores[i]) = ReadChar(); + } + + HUD_UpdateTeamPos(o); + } + + void Ent_ClientData() + { + float newspectatee_status; + + int f = ReadByte(); + + scoreboard_showscores_force = (f & 1); + + if(f & 2) + { + newspectatee_status = ReadByte(); + if(newspectatee_status == player_localnum + 1) + newspectatee_status = -1; // observing + } + else + newspectatee_status = 0; + + spectatorbutton_zoom = (f & 4); + + if(f & 8) + { + angles_held_status = 1; + angles_held.x = ReadAngle(); + angles_held.y = ReadAngle(); + angles_held.z = 0; + } + else + angles_held_status = 0; + ++ if(f & 16) ++ { ++ fovlock = ReadByte(); ++ num_spectators = ReadByte(); ++ ++ if(fovlock <= 0) ++ fovlock = -1; ++ ++ float i, slot; ++ ++ for(i = 0; i < MAX_SPECTATORS; ++i) ++ spectatorlist[i] = 0; // reset list first ++ ++ for(i = 0; i < num_spectators; ++i) ++ { ++ slot = ReadByte(); ++ spectatorlist[i] = slot - 1; ++ } ++ } ++ + if(newspectatee_status != spectatee_status) + { + // clear race stuff + race_laptime = 0; + race_checkpointtime = 0; + } + if (autocvar_hud_panel_healtharmor_progressbar_gfx) + { + if ( (spectatee_status == -1 && newspectatee_status > 0) //before observing, now spectating + || (spectatee_status > 0 && newspectatee_status > 0 && spectatee_status != newspectatee_status) //changed spectated player + ) + prev_p_health = -1; + else if(spectatee_status && !newspectatee_status) //before observing/spectating, now playing + prev_health = -1; + } + spectatee_status = newspectatee_status; + + // we could get rid of spectatee_status, and derive it from player_localentnum and player_localnum + } + + void Ent_Nagger() + { + int i, j, b, f; + + int nags = ReadByte(); // NAGS NAGS NAGS NAGS NAGS NAGS NADZ NAGS NAGS NAGS + + if(!(nags & 4)) + { + if(vote_called_vote) + strunzone(vote_called_vote); + vote_called_vote = string_null; + vote_active = 0; + } + else + { + vote_active = 1; + } + + if(nags & 64) + { + vote_yescount = ReadByte(); + vote_nocount = ReadByte(); + vote_needed = ReadByte(); + vote_highlighted = ReadChar(); + } + + if(nags & 128) + { + if(vote_called_vote) + strunzone(vote_called_vote); + vote_called_vote = strzone(ColorTranslateRGB(ReadString())); + } + + if(nags & 1) + { + for(j = 0; j < maxclients; ++j) + if(playerslots[j]) + playerslots[j].ready = 1; + for(i = 1; i <= maxclients; i += 8) + { + f = ReadByte(); + for(j = i-1, b = 1; b < 256; b *= 2, ++j) + if (!(f & b)) + if(playerslots[j]) + playerslots[j].ready = 0; + } + } + + ready_waiting = (nags & 1); + ready_waiting_for_me = (nags & 2); + vote_waiting = (nags & 4); + vote_waiting_for_me = (nags & 8); + warmup_stage = (nags & 16); + } + + void Ent_EliminatedPlayers() + { + int i, j, b, f; + + int sf = ReadByte(); + if(sf & 1) + { + for(j = 0; j < maxclients; ++j) + if(playerslots[j]) + playerslots[j].eliminated = 1; + for(i = 1; i <= maxclients; i += 8) + { + f = ReadByte(); + for(j = i-1, b = 1; b < 256; b *= 2, ++j) + if (!(f & b)) + if(playerslots[j]) + playerslots[j].eliminated = 0; + } + } + } + + void Ent_RandomSeed() + { + float s; + prandom_debug(); + s = ReadShort(); + psrandom(s); + } + + void Ent_ReadAccuracy(void) + { + int f, w; + int sf = ReadInt24_t(); + if(sf == 0) + { + for(w = 0; w <= WEP_LAST - WEP_FIRST; ++w) + weapon_accuracy[w] = -1; + return; + } + + for(w = 0, f = 1; w <= WEP_LAST - WEP_FIRST; ++w) + { + if(sf & f) + { + int b = ReadByte(); + if(b == 0) + weapon_accuracy[w] = -1; + else if(b == 255) + weapon_accuracy[w] = 1.0; // no better error handling yet, sorry + else + weapon_accuracy[w] = (b - 1.0) / 100.0; + } + if(f == 0x800000) + f = 1; + else + f *= 2; + } + } + ++.float jb_camera_active; ++void ent_jailcamera() ++{ ++ float sf = ReadByte(); ++ ++ if(sf & 1) ++ { ++ self.origin_x = ReadCoord(); ++ self.origin_y = ReadCoord(); ++ self.origin_z = ReadCoord(); ++ setorigin(self, self.origin); ++ ++ self.angles_x = ReadAngle(); ++ self.angles_y = ReadAngle(); ++ self.angles_z = 0; ++ ++ self.classname = "jailcamera"; ++ self.jb_camera_active = false; ++ } ++ ++ if(sf & 2) ++ { ++ self.jb_camera_active = ReadByte(); ++ } ++} ++ + void Spawn_Draw(void) + { + pointparticles(self.cnt, self.origin + '0 0 28', '0 0 2', bound(0, frametime, 0.1)); + } + + void Ent_ReadSpawnPoint(float is_new) // entity for spawnpoint + { + float teamnum = (ReadByte() - 1); + vector spn_origin; + spn_origin.x = ReadShort(); + spn_origin.y = ReadShort(); + spn_origin.z = ReadShort(); + + if(is_new) + { + self.origin = spn_origin; + setsize(self, PL_MIN, PL_MAX); + droptofloor(); + + /*if(autocvar_cl_spawn_point_model) // needs a model first + { + self.mdl = "models/spawnpoint.md3"; + self.colormod = Team_ColorRGB(teamnum); + precache_model(self.mdl); + setmodel(self, self.mdl); + self.drawmask = MASK_NORMAL; + //self.movetype = MOVETYPE_NOCLIP; + //self.draw = Spawn_Draw; + }*/ + if(autocvar_cl_spawn_point_particles) + { + if((serverflags & SERVERFLAG_TEAMPLAY)) + { + switch(teamnum) + { + case NUM_TEAM_1: self.cnt = particleeffectnum("spawn_point_red"); break; + case NUM_TEAM_2: self.cnt = particleeffectnum("spawn_point_blue"); break; + case NUM_TEAM_3: self.cnt = particleeffectnum("spawn_point_yellow"); break; + case NUM_TEAM_4: self.cnt = particleeffectnum("spawn_point_pink"); break; + default: self.cnt = particleeffectnum("spawn_point_neutral"); break; + } + } + else { self.cnt = particleeffectnum("spawn_point_neutral"); } + + self.draw = Spawn_Draw; + } + } + + //printf("Ent_ReadSpawnPoint(is_new = %d); origin = %s, team = %d, effect = %d\n", is_new, vtos(self.origin), teamnum, self.cnt); + } + + void Ent_ReadSpawnEvent(float is_new) + { + // If entnum is 0, ONLY do the local spawn actions + // this way the server can disable the sending of + // spawn origin or such to clients if wanted. + float entnum = ReadByte(); + + if(entnum) + { + self.origin_x = ReadShort(); + self.origin_y = ReadShort(); + self.origin_z = ReadShort(); + + if(is_new) + { + float teamnum = GetPlayerColor(entnum - 1); + + if(autocvar_cl_spawn_event_particles) + { + switch(teamnum) + { + case NUM_TEAM_1: pointparticles(particleeffectnum("spawn_event_red"), self.origin, '0 0 0', 1); break; + case NUM_TEAM_2: pointparticles(particleeffectnum("spawn_event_blue"), self.origin, '0 0 0', 1); break; + case NUM_TEAM_3: pointparticles(particleeffectnum("spawn_event_yellow"), self.origin, '0 0 0', 1); break; + case NUM_TEAM_4: pointparticles(particleeffectnum("spawn_event_pink"), self.origin, '0 0 0', 1); break; + default: pointparticles(particleeffectnum("spawn_event_neutral"), self.origin, '0 0 0', 1); break; + } + } + if(autocvar_cl_spawn_event_sound) + { + sound(self, CH_TRIGGER, "misc/spawn.wav", VOL_BASE, ATTEN_NORM); + } + } + } + + // local spawn actions + if(is_new && (!entnum || (entnum == player_localentnum))) + { + zoomin_effect = 1; + current_viewzoom = (1 / bound(1, autocvar_cl_spawnzoom_factor, 16)); + + if(autocvar_cl_unpress_zoom_on_spawn) + { + localcmd("-zoom\n"); + button_zoom = false; + } + } - ++ HUD_Radar_Hide_Maximized(); + //printf("Ent_ReadSpawnEvent(is_new = %d); origin = %s, entnum = %d, localentnum = %d\n", is_new, vtos(self.origin), entnum, player_localentnum); + } + + // CSQC_Ent_Update : Called every frame that the server has indicated an update to the SSQC / CSQC entity has occured. + // The only parameter reflects if the entity is "new" to the client, meaning it just came into the client's PVS. + void Ent_RadarLink(); + void Ent_Init(); + void Ent_ScoresInfo(); + void CSQC_Ent_Update(float bIsNewEntity) + { + float t; + float savetime; + t = ReadByte(); + + if(autocvar_developer_csqcentities) + printf("CSQC_Ent_Update(%d) with self=%i self.entnum=%d self.enttype=%d t=%d\n", bIsNewEntity, self, self.entnum, self.enttype, t); + + // set up the "time" global for received entities to be correct for interpolation purposes + savetime = time; + if(servertime) + { + time = servertime; + } + else + { + serverprevtime = time; + serverdeltatime = getstatf(STAT_MOVEVARS_TICRATE) * getstatf(STAT_MOVEVARS_TIMESCALE); + time = serverprevtime + serverdeltatime; + } + + #ifdef DP_CSQC_ENTITY_REMOVE_IS_B0RKED + if(self.enttype) + { + if(t != self.enttype || bIsNewEntity) + { + //print("A CSQC entity changed its type!\n"); + printf("A CSQC entity changed its type! (edict: %d, server: %d, type: %d -> %d)\n", num_for_edict(self), self.entnum, self.enttype, t); + Ent_Remove(); + clearentity(self); + bIsNewEntity = 1; + } + } + else + { + if(!bIsNewEntity) + { + printf("A CSQC entity appeared out of nowhere! (edict: %d, server: %d, type: %d)\n", num_for_edict(self), self.entnum, t); + bIsNewEntity = 1; + } + } + #endif + self.enttype = t; + switch(t) + { + case ENT_CLIENT_ENTCS: Ent_ReadEntCS(); break; + case ENT_CLIENT_SCORES: Ent_ReadPlayerScore(); break; + case ENT_CLIENT_TEAMSCORES: Ent_ReadTeamScore(); break; + case ENT_CLIENT_POINTPARTICLES: Ent_PointParticles(); break; + case ENT_CLIENT_RAINSNOW: Ent_RainOrSnow(); break; + case ENT_CLIENT_LASER: Ent_Laser(); break; + case ENT_CLIENT_NAGGER: Ent_Nagger(); break; + case ENT_CLIENT_ELIMINATEDPLAYERS: Ent_EliminatedPlayers(); break; + case ENT_CLIENT_WAYPOINT: Ent_WaypointSprite(); break; + case ENT_CLIENT_RADARLINK: Ent_RadarLink(); break; + case ENT_CLIENT_PROJECTILE: Ent_Projectile(); break; + case ENT_CLIENT_GIBSPLASH: Ent_GibSplash(bIsNewEntity); break; + case ENT_CLIENT_DAMAGEINFO: Ent_DamageInfo(bIsNewEntity); break; + case ENT_CLIENT_CASING: Ent_Casing(bIsNewEntity); break; + case ENT_CLIENT_INIT: Ent_Init(); break; + case ENT_CLIENT_SCORES_INFO: Ent_ScoresInfo(); break; + case ENT_CLIENT_MAPVOTE: Ent_MapVote(); break; + case ENT_CLIENT_CLIENTDATA: Ent_ClientData(); break; + case ENT_CLIENT_RANDOMSEED: Ent_RandomSeed(); break; + case ENT_CLIENT_WALL: Ent_Wall(); break; + case ENT_CLIENT_MODELEFFECT: Ent_ModelEffect(bIsNewEntity); break; + case ENT_CLIENT_TUBANOTE: Ent_TubaNote(bIsNewEntity); break; + case ENT_CLIENT_WARPZONE: WarpZone_Read(bIsNewEntity); break; + case ENT_CLIENT_WARPZONE_CAMERA: WarpZone_Camera_Read(bIsNewEntity); break; + case ENT_CLIENT_WARPZONE_TELEPORTED: WarpZone_Teleported_Read(bIsNewEntity); break; + case ENT_CLIENT_TRIGGER_MUSIC: Ent_ReadTriggerMusic(); break; + case ENT_CLIENT_HOOK: Ent_ReadHook(bIsNewEntity, ENT_CLIENT_HOOK); break; + case ENT_CLIENT_ARC_BEAM: Ent_ReadArcBeam(bIsNewEntity); break; + case ENT_CLIENT_ACCURACY: Ent_ReadAccuracy(); break; + case ENT_CLIENT_AUXILIARYXHAIR: Net_AuXair2(bIsNewEntity); break; + case ENT_CLIENT_TURRET: ent_turret(); break; ++ case ENT_CLIENT_GENERATOR: ent_generator(); break; ++ case ENT_CLIENT_CONTROLPOINT_ICON: ent_cpicon(); break; + case ENT_CLIENT_MODEL: CSQCModel_Read(bIsNewEntity); break; + case ENT_CLIENT_ITEM: ItemRead(bIsNewEntity); break; + case ENT_CLIENT_BUMBLE_RAYGUN: bumble_raygun_read(bIsNewEntity); break; ++ case ENT_CLIENT_CONQUEST_CONTROLPOINT: conquest_controlpoint_read(bIsNewEntity); break; + case ENT_CLIENT_SPAWNPOINT: Ent_ReadSpawnPoint(bIsNewEntity); break; + case ENT_CLIENT_SPAWNEVENT: Ent_ReadSpawnEvent(bIsNewEntity); break; + case ENT_CLIENT_NOTIFICATION: Read_Notification(bIsNewEntity); break; + case ENT_CLIENT_HEALING_ORB: ent_healer(); break; ++ case ENT_CLIENT_JAILCAMERA: ent_jailcamera(); break; ++ case ENT_CLIENT_MINIGAME: ent_read_minigame(); break; ++ case ENT_CLIENT_EFFECT: Read_Effect(bIsNewEntity); break; + + default: + //error(strcat(_("unknown entity type in CSQC_Ent_Update: %d\n"), self.enttype)); + error(sprintf("Unknown entity type in CSQC_Ent_Update (enttype: %d, edict: %d, classname: %s)\n", self.enttype, num_for_edict(self), self.classname)); + break; + } + + time = savetime; + } + // Destructor, but does NOT deallocate the entity by calling remove(). Also + // used when an entity changes its type. For an entity that someone interacts + // with others, make sure it can no longer do so. + void Ent_Remove() + { + if(self.entremove) + self.entremove(); + + if(self.skeletonindex) + { + skel_delete(self.skeletonindex); + self.skeletonindex = 0; + } + + if(self.snd_looping > 0) + { + sound(self, self.snd_looping, "misc/null.wav", VOL_BASE, autocvar_g_jetpack_attenuation); + self.snd_looping = 0; + } + + self.enttype = 0; + self.classname = ""; + self.draw = menu_sub_null; + self.entremove = menu_sub_null; + // TODO possibly set more stuff to defaults + } + // CSQC_Ent_Remove : Called when the server requests a SSQC / CSQC entity to be removed. Essentially call remove(self) as well. + void CSQC_Ent_Remove() + { + if(autocvar_developer_csqcentities) + printf("CSQC_Ent_Remove() with self=%i self.entnum=%d self.enttype=%d\n", self, self.entnum, self.enttype); + + if(wasfreed(self)) + { + print("WARNING: CSQC_Ent_Remove called for already removed entity. Packet loss?\n"); + return; + } + if(self.enttype) + Ent_Remove(); + remove(self); + } + + void Gamemode_Init() + { + if (!isdemo()) + { + if(!(calledhooks & HOOK_START)) + localcmd("\n_cl_hook_gamestart ", MapInfo_Type_ToString(gametype), "\n"); + calledhooks |= HOOK_START; + } + } + // CSQC_Parse_StuffCmd : Provides the stuffcmd string in the first parameter that the server provided. To execute standard behavior, simply execute localcmd with the string. + void CSQC_Parse_StuffCmd(string strMessage) + { + if(autocvar_developer_csqcentities) + printf("CSQC_Parse_StuffCmd(\"%s\")\n", strMessage); + + localcmd(strMessage); + } + // CSQC_Parse_Print : Provides the print string in the first parameter that the server provided. To execute standard behavior, simply execute print with the string. + void CSQC_Parse_Print(string strMessage) + { + if(autocvar_developer_csqcentities) + printf("CSQC_Parse_Print(\"%s\")\n", strMessage); + + print(ColorTranslateRGB(strMessage)); + } + + // CSQC_Parse_CenterPrint : Provides the centerprint_hud string in the first parameter that the server provided. + void CSQC_Parse_CenterPrint(string strMessage) + { + if(autocvar_developer_csqcentities) + printf("CSQC_Parse_CenterPrint(\"%s\")\n", strMessage); + + centerprint_hud(strMessage); + } + + string notranslate_fogcmd1 = "\nfog "; + string notranslate_fogcmd2 = "\nr_fog_exp2 0\nr_drawfog 1\n"; + void Fog_Force() + { + // TODO somehow thwart prvm_globalset client ... + + if(autocvar_cl_orthoview && autocvar_cl_orthoview_nofog) + { localcmd("\nr_drawfog 0\n"); } + else if(forcefog != "") + { localcmd(strcat(notranslate_fogcmd1, forcefog, notranslate_fogcmd2)); } + } + + void Gamemode_Init(); + void Ent_ScoresInfo() + { + int i; + self.classname = "ent_client_scores_info"; + gametype = ReadInt24_t(); + HUD_ModIcons_SetFunc(); + for(i = 0; i < MAX_SCORE; ++i) + { + if(scores_label[i]) + strunzone(scores_label[i]); + scores_label[i] = strzone(ReadString()); + scores_flags[i] = ReadByte(); + } + for(i = 0; i < MAX_TEAMSCORE; ++i) + { + if(teamscores_label[i]) + strunzone(teamscores_label[i]); + teamscores_label[i] = strzone(ReadString()); + teamscores_flags[i] = ReadByte(); + } + HUD_InitScores(); + Gamemode_Init(); + } + + void Ent_Init() + { + self.classname = "ent_client_init"; + + nb_pb_period = ReadByte() / 32; //Accuracy of 1/32th + + hook_shotorigin[0] = decompressShotOrigin(ReadInt24_t()); + hook_shotorigin[1] = decompressShotOrigin(ReadInt24_t()); + hook_shotorigin[2] = decompressShotOrigin(ReadInt24_t()); + hook_shotorigin[3] = decompressShotOrigin(ReadInt24_t()); + arc_shotorigin[0] = decompressShotOrigin(ReadInt24_t()); + arc_shotorigin[1] = decompressShotOrigin(ReadInt24_t()); + arc_shotorigin[2] = decompressShotOrigin(ReadInt24_t()); + arc_shotorigin[3] = decompressShotOrigin(ReadInt24_t()); + + if(forcefog) + strunzone(forcefog); + forcefog = strzone(ReadString()); + + armorblockpercent = ReadByte() / 255.0; + + g_balance_mortar_bouncefactor = ReadCoord(); + g_balance_mortar_bouncestop = ReadCoord(); + g_balance_electro_secondary_bouncefactor = ReadCoord(); + g_balance_electro_secondary_bouncestop = ReadCoord(); + + vortex_scope = !ReadByte(); + rifle_scope = !ReadByte(); + + serverflags = ReadByte(); + + minelayer_maxmines = ReadByte(); + + hagar_maxrockets = ReadByte(); + + g_trueaim_minrange = ReadCoord(); + g_balance_porto_secondary = ReadByte(); + ++ vaporizer_delay = ReadByte(); ++ ++ sv_showfps = ReadByte(); ++ ++ sv_announcer = strzone(ReadString()); ++ ++ if(sv_announcer != "") { Announcer_Precache(); } ++ + if(!postinit) + PostInit(); + } + + void Net_ReadRace() + { + float b; + + b = ReadByte(); + + switch(b) + { + case RACE_NET_CHECKPOINT_HIT_QUALIFYING: + race_checkpoint = ReadByte(); + race_time = ReadInt24_t(); + race_previousbesttime = ReadInt24_t(); + if(race_previousbestname) + strunzone(race_previousbestname); + race_previousbestname = strzone(ColorTranslateRGB(ReadString())); + + race_checkpointtime = time; + + if(race_checkpoint == 0 || race_checkpoint == 254) + { + race_penaltyaccumulator = 0; + race_laptime = time; // valid + } + + break; + + case RACE_NET_CHECKPOINT_CLEAR: + race_laptime = 0; + race_checkpointtime = 0; + break; + + case RACE_NET_CHECKPOINT_NEXT_SPEC_QUALIFYING: + race_laptime = ReadCoord(); + race_checkpointtime = -99999; + // fall through + case RACE_NET_CHECKPOINT_NEXT_QUALIFYING: + race_nextcheckpoint = ReadByte(); + + race_nextbesttime = ReadInt24_t(); + if(race_nextbestname) + strunzone(race_nextbestname); + race_nextbestname = strzone(ColorTranslateRGB(ReadString())); + break; + + case RACE_NET_CHECKPOINT_HIT_RACE: + race_mycheckpoint = ReadByte(); + race_mycheckpointtime = time; + race_mycheckpointdelta = ReadInt24_t(); + race_mycheckpointlapsdelta = ReadByte(); + if(race_mycheckpointlapsdelta >= 128) + race_mycheckpointlapsdelta -= 256; + if(race_mycheckpointenemy) + strunzone(race_mycheckpointenemy); + race_mycheckpointenemy = strzone(ColorTranslateRGB(ReadString())); + break; + + case RACE_NET_CHECKPOINT_HIT_RACE_BY_OPPONENT: + race_othercheckpoint = ReadByte(); + race_othercheckpointtime = time; + race_othercheckpointdelta = ReadInt24_t(); + race_othercheckpointlapsdelta = ReadByte(); + if(race_othercheckpointlapsdelta >= 128) + race_othercheckpointlapsdelta -= 256; + if(race_othercheckpointenemy) + strunzone(race_othercheckpointenemy); + race_othercheckpointenemy = strzone(ColorTranslateRGB(ReadString())); + break; + + case RACE_NET_PENALTY_RACE: + race_penaltyeventtime = time; + race_penaltytime = ReadShort(); + //race_penaltyaccumulator += race_penaltytime; + if(race_penaltyreason) + strunzone(race_penaltyreason); + race_penaltyreason = strzone(ReadString()); + break; + + case RACE_NET_PENALTY_QUALIFYING: + race_penaltyeventtime = time; + race_penaltytime = ReadShort(); + race_penaltyaccumulator += race_penaltytime; + if(race_penaltyreason) + strunzone(race_penaltyreason); + race_penaltyreason = strzone(ReadString()); + break; + + case RACE_NET_SERVER_RECORD: + race_server_record = ReadInt24_t(); + break; + case RACE_NET_SPEED_AWARD: + race_speedaward = ReadInt24_t(); + if(race_speedaward_holder) + strunzone(race_speedaward_holder); + race_speedaward_holder = strzone(ReadString()); + break; + case RACE_NET_SPEED_AWARD_BEST: + race_speedaward_alltimebest = ReadInt24_t(); + if(race_speedaward_alltimebest_holder) + strunzone(race_speedaward_alltimebest_holder); + race_speedaward_alltimebest_holder = strzone(ReadString()); + break; + case RACE_NET_SERVER_RANKINGS: + float prevpos, del; + int pos = ReadShort(); + prevpos = ReadShort(); + del = ReadShort(); + + // move other rankings out of the way + int i; + if (prevpos) { + for (i=prevpos-1;i>pos-1;--i) { + grecordtime[i] = grecordtime[i-1]; + if(grecordholder[i]) + strunzone(grecordholder[i]); + grecordholder[i] = strzone(grecordholder[i-1]); + } + } else if (del) { // a record has been deleted by the admin + for (i=pos-1; i<= RANKINGS_CNT-1; ++i) { + if (i == RANKINGS_CNT-1) { // clear out last record + grecordtime[i] = 0; + if (grecordholder[i]) + strunzone(grecordholder[i]); + grecordholder[i] = string_null; + } + else { + grecordtime[i] = grecordtime[i+1]; + if (grecordholder[i]) + strunzone(grecordholder[i]); + grecordholder[i] = strzone(grecordholder[i+1]); + } + } + } else { // player has no ranked record yet + for (i=RANKINGS_CNT-1;i>pos-1;--i) { + grecordtime[i] = grecordtime[i-1]; + if(grecordholder[i]) + strunzone(grecordholder[i]); + grecordholder[i] = strzone(grecordholder[i-1]); + } + } + + // store new ranking + if(grecordholder[pos-1] != "") + strunzone(grecordholder[pos-1]); + grecordholder[pos-1] = strzone(ReadString()); + grecordtime[pos-1] = ReadInt24_t(); + if(grecordholder[pos-1] == GetPlayerName(player_localnum)) + race_myrank = pos; + break; + case RACE_NET_SERVER_STATUS: + race_status = ReadShort(); + if(race_status_name) + strunzone(race_status_name); + race_status_name = strzone(ReadString()); + } + } + + void Net_TeamNagger() + { + teamnagger = 1; + } + + void Net_ReadPingPLReport() + { + int e, pi, pl, ml; + e = ReadByte(); + pi = ReadShort(); + pl = ReadByte(); + ml = ReadByte(); + if (!(playerslots[e])) + return; + playerslots[e].ping = pi; + playerslots[e].ping_packetloss = pl / 255.0; + playerslots[e].ping_movementloss = ml / 255.0; + } + + void Net_WeaponComplain() + { + complain_weapon = ReadByte(); + + if(complain_weapon_name) + strunzone(complain_weapon_name); + complain_weapon_name = strzone(WEP_NAME(complain_weapon)); + + complain_weapon_type = ReadByte(); + + complain_weapon_time = time; + weapontime = time; // ping the weapon panel + + switch(complain_weapon_type) + { + case 0: Local_Notification(MSG_MULTI, ITEM_WEAPON_NOAMMO, complain_weapon); break; + case 1: Local_Notification(MSG_MULTI, ITEM_WEAPON_DONTHAVE, complain_weapon); break; + default: Local_Notification(MSG_MULTI, ITEM_WEAPON_UNAVAILABLE, complain_weapon); break; + } + } + + // CSQC_Parse_TempEntity : Handles all temporary entity network data in the CSQC layer. + // You must ALWAYS first acquire the temporary ID, which is sent as a byte. + // Return value should be 1 if CSQC handled the temporary entity, otherwise return 0 to have the engine process the event. + float CSQC_Parse_TempEntity() + { + float bHandled; + bHandled = true; + // Acquire TE ID + float nTEID; + nTEID = ReadByte(); + + if(autocvar_developer_csqcentities) + printf("CSQC_Parse_TempEntity() with nTEID=%d\n", nTEID); + + // NOTE: Could just do return instead of break... + switch(nTEID) + { + case TE_CSQC_TARGET_MUSIC: + Net_TargetMusic(); + bHandled = true; + break; + case TE_CSQC_PICTURE: + Net_MapVote_Picture(); + bHandled = true; + break; + case TE_CSQC_RACE: + Net_ReadRace(); + bHandled = true; + break; + case TE_CSQC_VORTEXBEAMPARTICLE: + Net_ReadVortexBeamParticle(); + bHandled = true; + break; + case TE_CSQC_TEAMNAGGER: + Net_TeamNagger(); + bHandled = true; + break; + case TE_CSQC_ARC: + Net_ReadArc(); + bHandled = true; + break; + case TE_CSQC_PINGPLREPORT: + Net_ReadPingPLReport(); + bHandled = true; + break; + case TE_CSQC_WEAPONCOMPLAIN: + Net_WeaponComplain(); + bHandled = true; + break; + case TE_CSQC_VEHICLESETUP: + Net_VehicleSetup(); + bHandled = true; + break; + case TE_CSQC_SVNOTICE: + cl_notice_read(); + bHandled = true; + break; + case TE_CSQC_SHOCKWAVEPARTICLE: + Net_ReadShockwaveParticle(); + bHandled = true; + break; ++ case TE_CSQC_SUPERBLASTPARTICLE: ++ Net_ReadSuperBlastParticle(); ++ bHandled = true; ++ break; + default: + // No special logic for this temporary entity; return 0 so the engine can handle it + bHandled = false; + break; + } + + return bHandled; + } + + string getcommandkey(string text, string command) + { + string keys; + float n, j, k, l = 0; + + if (!autocvar_hud_showbinds) + return text; + + keys = db_get(binddb, command); + if (keys == "") + { + n = tokenize(findkeysforcommand(command, 0)); // uses '...' strings + for(j = 0; j < n; ++j) + { + k = stof(argv(j)); + if(k != -1) + { + if ("" == keys) + keys = keynumtostring(k); + else + keys = strcat(keys, ", ", keynumtostring(k)); + + ++l; + if (autocvar_hud_showbinds_limit > 0 && autocvar_hud_showbinds_limit <= l) + break; + } + + } + if (keys == "") + keys = "NO_KEY"; + db_put(binddb, command, keys); + } + + if (keys == "NO_KEY") { + if (autocvar_hud_showbinds > 1) + return sprintf(_("%s (not bound)"), text); + else + return text; + } + else if (autocvar_hud_showbinds > 1) + return sprintf("%s (%s)", text, keys); + else + return keys; + } diff --cc qcsrc/client/main.qh index 1c2ccdf8c,b8823e629..45aa3c05b --- a/qcsrc/client/main.qh +++ b/qcsrc/client/main.qh @@@ -96,12 -97,11 +97,12 @@@ vector view_origin, view_forward, view_ float button_zoom; float spectatorbutton_zoom; +float button_attack; float button_attack2; - float activeweapon; - float switchingweapon; - float switchweapon; + int activeweapon; + int switchingweapon; + int switchweapon; float current_viewzoom; float zoomin_effect; float warmup_stage; @@@ -148,20 -148,5 +149,22 @@@ entity entcs_receiver[255]; // 255 is t float hud; float view_quality; - float framecount; + +float vaporizer_delay; + +float fovlock; + +float cl_fps; - float sv_showfps; ++int sv_showfps; + +string sv_announcer; + - float num_spectators; - #define MAX_SPECTATORS 7 - float spectatorlist[MAX_SPECTATORS]; ++int num_spectators; ++const int MAX_SPECTATORS = 7; ++int spectatorlist[MAX_SPECTATORS]; ++ ++bool camera_drawviewmodel_locked; ++bool camera_drawviewmodel_backup; + - float camera_drawviewmodel_locked; - float camera_drawviewmodel_backup; + int framecount; + #endif diff --cc qcsrc/client/miscfunctions.qc index 566212dc0,d74a24a32..c2938fbd4 --- a/qcsrc/client/miscfunctions.qc +++ b/qcsrc/client/miscfunctions.qc @@@ -378,20 -315,10 +315,20 @@@ void drawcolorcodedstring_expanding(vec } void drawcolorcodedstring_aspect_expanding(vector pos, string text, vector sz, float theAlpha, float drawflag, float fadelerp) { - SET_POS_AND_SZ_Y_ASPECT(TRUE) - drawcolorcodedstring_expanding(pos, text, '1 1 0' * sz_y, theAlpha, drawflag, fadelerp); + SET_POS_AND_SZ_Y_ASPECT(true); + drawcolorcodedstring_expanding(pos, text, '1 1 0' * sz.y, theAlpha, drawflag, fadelerp); } +void drawrotpic(vector org, float rot, string pic, vector sz, vector hotspot, vector rgb, float a, float f); + +void drawpic_rotated(vector position, string pic, vector sz, vector rgb, float angle, float theAlpha, float flag) { + drawrotpic(position + sz * 0.5, angle, pic, sz, sz * 0.5, rgb, theAlpha, flag); +} + +void drawpic_rotated_ex(vector position, string pic, vector sz, vector rgb, float angle, vector hotspot, float theAlpha, float flag) { + drawrotpic(position + hotspot, angle, pic, sz, hotspot, rgb, theAlpha, flag); +} + // this draws the triangles of a model DIRECTLY. Don't expect high performance, really... float PolyDrawModelSurface(entity e, float i_s) { diff --cc qcsrc/client/player_skeleton.qh index 7ee8d3a8b,6c95ce231..683e9dd89 --- a/qcsrc/client/player_skeleton.qh +++ b/qcsrc/client/player_skeleton.qh @@@ -7,6 -10,4 +10,7 @@@ void skeleton_loadinfo(entity e) .float bone_aim[MAX_AIM_BONES]; .float bone_aimweight[MAX_AIM_BONES]; .float fixbone; - .float bone_hat; ++.int bone_hat; +.vector hat_height; +.float hat_scale; + #endif diff --cc qcsrc/client/progs.src index 87e319e6f,f80da18d6..aabcdfa6c --- a/qcsrc/client/progs.src +++ b/qcsrc/client/progs.src @@@ -1,159 -1,73 +1,85 @@@ ../../csprogs.dat ../common/util-pre.qh - sys-pre.qh - ../dpdefs/csprogsdefs.qc - sys-post.qh + ../dpdefs/csprogsdefs.qh - Defs.qc - ../dpdefs/keycodes.qc - ../common/constants.qh - ../common/stats.qh - - ../warpzonelib/anglestransform.qh - ../warpzonelib/mathlib.qh - ../warpzonelib/common.qh - ../warpzonelib/client.qh - - ../common/playerstats.qh - ../common/teams.qh - ../common/util.qh - ../common/nades.qh - ../common/buffs.qh - ../common/test.qh - ../common/counting.qh - ../common/weapons/weapons.qh // TODO - ../common/mapinfo.qh - ../common/command/markup.qh - ../common/command/rpn.qh - ../common/command/generic.qh - ../common/command/shared_defs.qh - ../common/command/script.qh - ../common/urllib.qh - ../common/animdecide.qh - command/cl_cmd.qh - - autocvars.qh - - ../common/jeff.qh - - ../common/notifications.qh // must be after autocvars - ../common/deathtypes.qh // must be after notifications - - - ../common/effects.qh - - ../common/monsters/monsters.qh - - ../common/turrets/turrets.qh - ../common/turrets/cl_turrets.qh - - damage.qh - - ../csqcmodellib/interpolate.qh - teamradar.qh - hud.qh - scoreboard.qh - waypointsprites.qh - movetypes.qh - prandom.qh - bgmscript.qh - noise.qh - ../server/movelib.qc - generator.qh - controlpoint.qh - main.qh - ../common/vehicles/vehicles_include.qh - ../common/csqcmodel_settings.qh - ../csqcmodellib/common.qh - ../csqcmodellib/cl_model.qh - ../csqcmodellib/cl_player.qh - weapons/projectile.qh // TODO - player_skeleton.qh - - ../server/mutators/gamemode_ctf.qh // TODO - ../server/mutators/gamemode_keyhunt.qh - conquest.qh - - sortlist.qc - miscfunctions.qc - ../server/t_items.qh - ../server/t_items.qc - - ../common/minigames/cl_minigames.qh - teamradar.qc - hud_config.qc - hud.qc - scoreboard.qc - mapvoting.qc + announcer.qc + bgmscript.qc + casings.qc ++controlpoint.qc ++conquest.qc csqcmodel_hooks.qc - ../common/net_notice.qc - - rubble.qc - hook.qc - particles.qc - laser.qc - weapons/projectile.qc // TODO - gibs.qc damage.qc - casings.qc - ../csqcmodellib/cl_model.qc - ../csqcmodellib/cl_player.qc effects.qc - wall.qc ++generator.qc + gibs.qc + hook.qc + hud_config.qc + hud.qc + laser.qc + main.qc + mapvoting.qc + miscfunctions.qc modeleffects.qc - tuba.qc + movetypes.qc + noise.qc + particles.qc + player_skeleton.qc + prandom.qc + rubble.qc + scoreboard.qc + shownames.qc + sortlist.qc target_music.qc + teamradar.qc -tturrets.qc + tuba.qc -vehicles/vehicles.qc + view.qc + wall.qc + waypointsprites.qc - ../common/vehicles/vehicles_include.qc - shownames.qh - shownames.qc + command/cl_cmd.qc - conquest.qc - announcer.qc - Main.qc - View.qc - ../csqcmodellib/interpolate.qc - waypointsprites.qc - movetypes.qc - prandom.qc - bgmscript.qc - noise.qc + weapons/projectile.qc // TODO + ../common/animdecide.qc + ../common/buffs.qc + ../common/mapinfo.qc + ../common/nades.qc + ../common/net_notice.qc + ../common/notifications.qc + ../common/playerstats.qc ../common/test.qc + ../common/urllib.qc ../common/util.qc - ../common/playerstats.qc - ../common/notifications.qc + + ../common/command/generic.qc ../common/command/markup.qc ../common/command/rpn.qc - ../common/command/generic.qc +../common/command/script.qc - ../common/mapinfo.qc - ../common/weapons/weapons.qc // TODO - ../common/urllib.qc - command/cl_cmd.qc + +../common/effects.qc + ++../common/minigames/minigames.qc ++../common/minigames/cl_minigames.qc + ../common/monsters/monsters.qc +../common/turrets/cl_turrets.qc +../common/turrets/turrets.qc + - ../common/nades.qc - ../common/buffs.qc ++../common/vehicles/cl_vehicles.qc ++../common/vehicles/vehicles.qc + - ../warpzonelib/anglestransform.qc - ../warpzonelib/mathlib.qc - ../warpzonelib/common.qc - ../warpzonelib/client.qc - - generator.qc - controlpoint.qc + ../common/weapons/weapons.qc // TODO - player_skeleton.qc - ../common/animdecide.qc + ../csqcmodellib/cl_model.qc + ../csqcmodellib/cl_player.qc + ../csqcmodellib/interpolate.qc + ../server/movelib.qc + ../server/t_items.qc -../server/vehicles/bumblebee.qc - ../common/minigames/minigames.qc - ../common/minigames/cl_minigames.qc + ../warpzonelib/anglestransform.qc + ../warpzonelib/client.qc + ../warpzonelib/common.qc + ../warpzonelib/mathlib.qc diff --cc qcsrc/client/scoreboard.qc index 92cae0095,c6d871807..c26c0888e --- a/qcsrc/client/scoreboard.qc +++ b/qcsrc/client/scoreboard.qc @@@ -1,3 -1,5 +1,6 @@@ + #include "scoreboard.qh" ++#include "../common/minigames/cl_minigames.qh" + float scoreboard_alpha_bg; float scoreboard_alpha_fg; float scoreboard_highlight; @@@ -371,19 -362,6 +373,19 @@@ void Cmd_HUD_SetFields(float argc str = substring(str, 1, strlen(str) - 1); } + if(substring(str, 0, 1) == "~") + { - nocomplain = TRUE; ++ nocomplain = true; + + if(autocvar_scoreboard_extras) + str = substring(str, 1, strlen(str) - 1); + else + { + str = ""; + continue; + } + } + slash = strstrofs(str, "/", 0); if(slash >= 0) { @@@ -1006,25 -969,13 +1010,25 @@@ float HUD_WouldDrawScoreboard() float average_accuracy; vector HUD_DrawScoreboardAccuracyStats(vector pos, vector rgb, vector bg_size) { + WepSet weapons_stat = WepSet_GetFromStat(); + WepSet weapons_inmap = WepSet_GetFromStat_InMap(); - float i; + int i; - int weapon_cnt = WEP_COUNT - 3; // either vaporizer/vortex are hidden, no port-o-launch, no tuba - float rows; - if(autocvar_scoreboard_accuracy_doublerows) - rows = 2; - else - rows = 1; + float weapon_stats; - float disownedcnt = 0; ++ int disownedcnt = 0; + for(i = WEP_FIRST; i <= WEP_LAST; ++i) + { + self = get_weaponinfo(i); + if(!self.weapon) + continue; + + weapon_stats = weapon_accuracy[i-WEP_FIRST]; + + if(weapon_stats < 0 && !(weapons_stat & WepSet_FromWeapon(i) || weapons_inmap & WepSet_FromWeapon(i))) + ++disownedcnt; + } + - float weapon_cnt = WEP_COUNT - disownedcnt; // either vaporizer/vortex are hidden, no port-o-launch, no tuba - float rows = 1; ++ int weapon_cnt = WEP_COUNT - disownedcnt; // either vaporizer/vortex are hidden, no port-o-launch, no tuba ++ int rows = 1; float height = 40; float fontsize = height * 1/3; float weapon_height = height * 2/3; diff --cc qcsrc/client/teamradar.qc index b12f43571,1822acb5e..a5ef45ae1 --- a/qcsrc/client/teamradar.qc +++ b/qcsrc/client/teamradar.qc @@@ -46,37 -43,9 +43,37 @@@ vector teamradar_texcoord_to_2dcoord(ve return out; } + +vector teamradar_2dcoord_to_texcoord(vector in) +{ + vector out; + out = in; + + out -= teamradar_origin2d; + if(v_flipped) + out_x = -out_x; + out = out / teamradar_size; + + out_y = - out_y; // screen space is reversed + out = rotate(out, -teamradar_angle * DEG2RAD); + + out += teamradar_origin3d_in_texcoord; + + return out; +} + +vector teamradar_texcoord_to_3dcoord(vector in,float z) +{ + vector out; + out_x = in_x * (mi_picmax_x - mi_picmin_x) + mi_picmin_x; + out_y = in_y * (mi_picmax_y - mi_picmin_y) + mi_picmin_y; + out_z = z; + return out; +} + vector yinvert(vector v) { - v_y = 1 - v_y; + v.y = 1 - v.y; return v; } diff --cc qcsrc/client/teamradar.qh index 2dbdf8965,31defc8ef..da99652fe --- a/qcsrc/client/teamradar.qh +++ b/qcsrc/client/teamradar.qh @@@ -3,5 -6,50 +6,54 @@@ const int MAX_TEAMRADAR_TIMES = 32 // to make entities have dots on the team radar .float teamradar_icon; .float teamradar_times[MAX_TEAMRADAR_TIMES]; - .float teamradar_time_index; + .int teamradar_time_index; .vector teamradar_color; + + float teamradar_angle; // player yaw angle + vector teamradar_origin3d_in_texcoord; // player origin + vector teamradar_origin2d; // 2D origin + vector teamradar_size2d; // 2D size + vector teamradar_extraclip_mins, teamradar_extraclip_maxs; // for non-centered radar display + float teamradar_size; // 2D scale factor + float v_flipped; + + float hud_panel_radar_scale; // window size = ...qu + float hud_panel_radar_foreground_alpha; + float hud_panel_radar_rotation; + vector hud_panel_radar_size; + float hud_panel_radar_zoommode; + float hud_panel_radar_maximized_zoommode; + float hud_panel_radar_maximized_rotation; + + + float vlen2d(vector v); + + float vlen_maxnorm2d(vector v); + + float vlen_minnorm2d(vector v); + ++vector teamradar_2dcoord_to_texcoord(vector in); ++ + vector teamradar_3dcoord_to_texcoord(vector in); + + vector teamradar_texcoord_to_2dcoord(vector in); + ++vector teamradar_texcoord_to_3dcoord(vector in,float z); ++ + vector yinvert(vector v); + + void draw_teamradar_background(float fg); + + void draw_teamradar_player(vector coord3d, vector pangles, vector rgb); + + void draw_teamradar_icon(vector coord, float icon, entity pingdata, vector rgb, float a); + + void draw_teamradar_link(vector start, vector end, int colors); + + void teamradar_loadcvars(); + + // radar links + + void Ent_RadarLink(); + + #endif diff --cc qcsrc/client/tturrets.qc index ba640068e,d2469851c..000000000 deleted file mode 100644,100644 --- a/qcsrc/client/tturrets.qc +++ /dev/null @@@ -1,685 -1,687 +1,0 @@@ -#include "tturrets.qh" -#include "waypointsprites.qh" - -#include "../server/movelib.qh" - --string tid2info_base; --string tid2info_head; --string tid2info_name; --vector tid2info_min; --vector tid2info_max; -- --void turret_tid2info(float _tid); --void turret_precache(float _tid); --float turret_is_precache[TID_LAST]; -- --void turrets_precache() --{ -- turret_precache(TID_COMMON); --} -- - void turret_precache(float _tid) -void turret_precache(int _tid) --{ -- if (!turret_is_precache[TID_COMMON]) -- { -- precache_sound ("weapons/rocket_impact.wav"); -- precache_model ("models/turrets/base-gib1.md3"); -- precache_model ("models/turrets/base-gib2.md3"); -- precache_model ("models/turrets/base-gib3.md3"); -- precache_model ("models/turrets/base-gib4.md3"); -- precache_model ("models/turrets/head-gib1.md3"); -- precache_model ("models/turrets/head-gib2.md3"); -- precache_model ("models/turrets/head-gib3.md3"); -- precache_model ("models/turrets/head-gib4.md3"); -- precache_model ("models/turrets/terrainbase.md3"); -- precache_model ("models/turrets/base.md3"); -- precache_model ("models/turrets/rocket.md3"); -- } -- turret_tid2info(_tid); -- if(turret_is_precache[_tid]) -- return; -- -- switch(_tid) -- { -- case TID_EWHEEL: -- precache_model ("models/turrets/ewheel-base2.md3"); -- precache_model ("models/turrets/ewheel-gun1.md3"); -- break; -- case TID_FLAC: -- precache_model ("models/turrets/flac.md3"); -- break; -- case TID_FUSION: -- precache_model ("models/turrets/reactor.md3"); -- break; -- case TID_HELLION: -- precache_model ("models/turrets/hellion.md3"); -- break; -- case TID_HK: -- precache_model ("models/turrets/hk.md3"); -- break; -- case TID_MACHINEGUN: -- precache_model ("models/turrets/machinegun.md3"); -- precache_sound ("weapons/uzi_fire.wav"); -- break; -- case TID_MLRS: -- precache_model ("models/turrets/mlrs.md3"); -- break; -- case TID_PHASER: -- precache_model ("models/turrets/phaser.md3"); -- precache_model ("models/turrets/phaser_beam.md3"); -- precache_sound ("turrets/phaser.wav"); -- break; -- case TID_PLASMA: -- precache_model ("models/turrets/plasma.md3"); -- break; -- case TID_PLASMA_DUAL: -- precache_model ("models/turrets/plasmad.md3"); -- break; -- case TID_TESLA: -- precache_model ("models/turrets/tesla_head.md3"); -- precache_model ("models/turrets/tesla_base.md3"); -- break; -- case TID_WALKER: -- precache_model ("models/turrets/walker_head_minigun.md3"); -- precache_model ("models/turrets/walker_body.md3"); -- precache_sound ("weapons/uzi_fire.wav"); -- break; -- } - turret_is_precache[_tid] = TRUE; - turret_is_precache[_tid] = true; --} -- --void turret_tid2info(float _tid) --{ -- tid2info_base = "models/turrets/base.md3"; -- tid2info_min = '-32 -32 0'; -- tid2info_max = '32 32 64'; -- -- switch(_tid) -- { -- case TID_EWHEEL: -- tid2info_base = "models/turrets/ewheel-base2.md3"; -- tid2info_head = "models/turrets/ewheel-gun1.md3"; -- tid2info_name = "eWheel"; -- break; -- case TID_FLAC: -- tid2info_head = "models/turrets/flac.md3"; -- tid2info_name = "Flac Cannon"; -- break; -- case TID_FUSION: -- tid2info_head = "models/turrets/reactor.md3"; -- tid2info_name = "Fusion Reactor"; -- tid2info_min = '-34 -34 0'; -- tid2info_max = '34 34 90'; -- break; -- case TID_HELLION: -- tid2info_head = "models/turrets/hellion.md3"; -- tid2info_name = "Hellion"; -- break; -- case TID_HK: -- tid2info_head = "models/turrets/hk.md3"; -- tid2info_name = "Hunter-Killer"; -- break; -- case TID_MACHINEGUN: -- tid2info_head = "models/turrets/machinegun.md3"; -- tid2info_name = "Machinegun"; -- break; -- case TID_MLRS: -- tid2info_head = "models/turrets/mlrs.md3"; -- tid2info_name = "MLRS"; -- break; -- case TID_PHASER: -- tid2info_head = "models/turrets/phaser.md3"; -- tid2info_name = "Phaser"; -- break; -- case TID_PLASMA: -- tid2info_head = "models/turrets/plasma.md3"; -- tid2info_name = "Plasma"; -- break; -- case TID_PLASMA_DUAL: -- tid2info_head = "models/turrets/plasmad.md3"; -- tid2info_name = "Dual Plasma"; -- break; -- case TID_TESLA: -- tid2info_base = "models/turrets/tesla_base.md3"; -- tid2info_head = "models/turrets/tesla_head.md3"; -- tid2info_name = "Tesla coil"; -- tid2info_min = '-60 -60 0'; -- tid2info_max ='60 60 128'; -- break; -- case TID_WALKER: -- tid2info_base = "models/turrets/walker_body.md3"; -- tid2info_head = "models/turrets/walker_head_minigun.md3"; -- tid2info_name = "Walker"; -- tid2info_min = '-70 -70 0'; -- tid2info_max = '70 70 95'; -- break; -- } --} -- --void turret_remove() --{ -- remove(self.tur_head); -- //remove(self.enemy); -- self.tur_head = world; --} -- --.vector glowmod; --void turret_changeteam() --{ -- switch(self.team - 1) -- { -- case NUM_TEAM_1: // Red -- self.glowmod = '2 0 0'; -- self.teamradar_color = '1 0 0'; -- break; -- -- case NUM_TEAM_2: // Blue -- self.glowmod = '0 0 2'; -- self.teamradar_color = '0 0 1'; -- break; -- -- case NUM_TEAM_3: // Yellow -- self.glowmod = '1 1 0'; -- self.teamradar_color = '1 1 0'; -- break; -- -- case NUM_TEAM_4: // Pink -- self.glowmod = '1 0 1'; -- self.teamradar_color = '1 0 1'; -- break; -- } -- -- if(self.team) -- self.colormap = 1024 + (self.team - 1) * 17; -- -- self.tur_head.colormap = self.colormap; -- self.tur_head.glowmod = self.glowmod; -- --} -- --void turret_head_draw() --{ -- self.drawmask = MASK_NORMAL; --} -- --void turret_draw() --{ -- float dt; -- -- dt = time - self.move_time; -- self.move_time = time; -- if(dt <= 0) -- return; -- -- self.tur_head.angles += dt * self.tur_head.move_avelocity; -- -- if (self.health < 127) -- { -- dt = random(); -- -- if(dt < 0.03) -- te_spark(self.origin + '0 0 40', randomvec() * 256 + '0 0 256', 16); -- } -- -- if(self.health < 85) -- if(dt < 0.01) -- pointparticles(particleeffectnum("smoke_large"), (self.origin + (randomvec() * 80)), '0 0 0', 1); -- -- if(self.health < 32) -- if(dt < 0.015) -- pointparticles(particleeffectnum("smoke_small"), (self.origin + (randomvec() * 80)), '0 0 0', 1); -- --} -- --void turret_draw2d() --{ -- if(self.netname == "") -- return; -- -- if(!autocvar_g_waypointsprite_turrets) -- return; -- -- if(autocvar_cl_hidewaypoints) -- return; -- -- float dist = vlen(self.origin - view_origin); -- float t = (GetPlayerColor(player_localnum) + 1); -- -- vector o; -- string txt; -- -- if(autocvar_cl_vehicles_hud_tactical) -- if(dist < 10240 && t != self.team) -- { -- // TODO: Vehicle tactical hud -- o = project_3d_to_2d(self.origin + '0 0 32'); - if(o_z < 0 - || o_x < (vid_conwidth * waypointsprite_edgeoffset_left) - || o_y < (vid_conheight * waypointsprite_edgeoffset_top) - || o_x > (vid_conwidth - (vid_conwidth * waypointsprite_edgeoffset_right)) - || o_y > (vid_conheight - (vid_conheight * waypointsprite_edgeoffset_bottom))) - if(o.z < 0 - || o.x < (vid_conwidth * waypointsprite_edgeoffset_left) - || o.y < (vid_conheight * waypointsprite_edgeoffset_top) - || o.x > (vid_conwidth - (vid_conwidth * waypointsprite_edgeoffset_right)) - || o.y > (vid_conheight - (vid_conheight * waypointsprite_edgeoffset_bottom))) -- return; // Dont draw wp's for turrets out of view - o_z = 0; - o.z = 0; -- if(hud != HUD_NORMAL) -- { -- switch(hud) -- { -- case HUD_SPIDERBOT: -- case HUD_WAKIZASHI: -- case HUD_RAPTOR: -- case HUD_BUMBLEBEE: -- if(self.turret_type == TID_EWHEEL || self.turret_type == TID_WALKER) -- txt = "gfx/vehicles/vth-mover.tga"; -- else -- txt = "gfx/vehicles/vth-stationary.tga"; -- -- vector pz = drawgetimagesize(txt) * 0.25; -- drawpic(o - pz * 0.5, txt, pz , '1 1 1', 0.75, DRAWFLAG_NORMAL); -- break; -- } -- } -- } -- -- if(dist > self.maxdistance) -- return; -- -- string spriteimage = self.netname; -- float a = self.alpha * autocvar_hud_panel_fg_alpha; -- vector rgb = spritelookupcolor(spriteimage, self.teamradar_color); -- -- -- if(self.maxdistance > waypointsprite_normdistance) -- a *= pow(bound(0, (self.maxdistance - dist) / (self.maxdistance - waypointsprite_normdistance), 1), waypointsprite_distancealphaexponent); -- else if(self.maxdistance > 0) -- a *= pow(bound(0, (waypointsprite_fadedistance - dist) / (waypointsprite_fadedistance - waypointsprite_normdistance), 1), waypointsprite_distancealphaexponent) * (1 - waypointsprite_minalpha) + waypointsprite_minalpha; -- -- if(rgb == '0 0 0') -- { -- self.teamradar_color = '1 0 1'; -- printf("WARNING: sprite of name %s has no color, using pink so you notice it\n", spriteimage); -- } -- -- txt = self.netname; -- if(autocvar_g_waypointsprite_spam && waypointsprite_count >= autocvar_g_waypointsprite_spam) -- txt = _("Spam"); -- else -- txt = spritelookuptext(spriteimage); -- -- if(time - floor(time) > 0.5 && t == self.team) -- { -- if(self.helpme && time < self.helpme) -- { -- a *= SPRITE_HELPME_BLINK; -- txt = sprintf(_("%s under attack!"), txt); -- } -- else -- a *= spritelookupblinkvalue(spriteimage); -- } -- -- if(autocvar_g_waypointsprite_uppercase) -- txt = strtoupper(txt); -- -- if(a > 1) -- { -- rgb *= a; -- a = 1; -- } -- -- if(a <= 0) -- return; -- -- rgb = fixrgbexcess(rgb); -- -- o = project_3d_to_2d(self.origin + '0 0 64'); - if(o_z < 0 - || o_x < (vid_conwidth * waypointsprite_edgeoffset_left) - || o_y < (vid_conheight * waypointsprite_edgeoffset_top) - || o_x > (vid_conwidth - (vid_conwidth * waypointsprite_edgeoffset_right)) - || o_y > (vid_conheight - (vid_conheight * waypointsprite_edgeoffset_bottom))) - if(o.z < 0 - || o.x < (vid_conwidth * waypointsprite_edgeoffset_left) - || o.y < (vid_conheight * waypointsprite_edgeoffset_top) - || o.x > (vid_conwidth - (vid_conwidth * waypointsprite_edgeoffset_right)) - || o.y > (vid_conheight - (vid_conheight * waypointsprite_edgeoffset_bottom))) -- return; // Dont draw wp's for turrets out of view -- - o_z = 0; - o.z = 0; -- -- float edgedistance_min, crosshairdistance; - edgedistance_min = min((o_y - (vid_conheight * waypointsprite_edgeoffset_top)), - (o_x - (vid_conwidth * waypointsprite_edgeoffset_left)), - (vid_conwidth - (vid_conwidth * waypointsprite_edgeoffset_right)) - o_x, - (vid_conheight - (vid_conheight * waypointsprite_edgeoffset_bottom)) - o_y); - edgedistance_min = min((o.y - (vid_conheight * waypointsprite_edgeoffset_top)), - (o.x - (vid_conwidth * waypointsprite_edgeoffset_left)), - (vid_conwidth - (vid_conwidth * waypointsprite_edgeoffset_right)) - o.x, - (vid_conheight - (vid_conheight * waypointsprite_edgeoffset_bottom)) - o.y); -- -- float vidscale = max(vid_conwidth / vid_width, vid_conheight / vid_height); -- - crosshairdistance = sqrt( pow(o_x - vid_conwidth/2, 2) + pow(o_y - vid_conheight/2, 2) ); - crosshairdistance = sqrt( pow(o.x - vid_conwidth/2, 2) + pow(o.y - vid_conheight/2, 2) ); -- -- t = waypointsprite_scale * vidscale; -- a *= waypointsprite_alpha; -- -- { -- a = a * (1 - (1 - waypointsprite_distancefadealpha) * (bound(0, dist/waypointsprite_distancefadedistance, 1))); -- t = t * (1 - (1 - waypointsprite_distancefadescale) * (bound(0, dist/waypointsprite_distancefadedistance, 1))); -- } -- if (edgedistance_min < waypointsprite_edgefadedistance) { -- a = a * (1 - (1 - waypointsprite_edgefadealpha) * (1 - bound(0, edgedistance_min/waypointsprite_edgefadedistance, 1))); -- t = t * (1 - (1 - waypointsprite_edgefadescale) * (1 - bound(0, edgedistance_min/waypointsprite_edgefadedistance, 1))); -- } -- if(crosshairdistance < waypointsprite_crosshairfadedistance) { -- a = a * (1 - (1 - waypointsprite_crosshairfadealpha) * (1 - bound(0, crosshairdistance/waypointsprite_crosshairfadedistance, 1))); -- t = t * (1 - (1 - waypointsprite_crosshairfadescale) * (1 - bound(0, crosshairdistance/waypointsprite_crosshairfadedistance, 1))); -- } -- -- o = drawspritearrow(o, M_PI, rgb, a, SPRITE_ARROW_SCALE * t); -- o = drawspritetext(o, M_PI, (SPRITE_HEALTHBAR_WIDTH + 2 * SPRITE_HEALTHBAR_BORDER) * t, rgb, a, waypointsprite_fontsize * '1 1 0', txt); -- drawhealthbar( -- o, -- 0, -- self.health / 255, -- '0 0 0', -- '0 0 0', -- 0.5 * SPRITE_HEALTHBAR_WIDTH * t, -- 0.5 * SPRITE_HEALTHBAR_HEIGHT * t, -- SPRITE_HEALTHBAR_MARGIN * t + 0.5 * waypointsprite_fontsize, -- SPRITE_HEALTHBAR_BORDER * t, -- 0, -- rgb, -- a * SPRITE_HEALTHBAR_BORDERALPHA, -- rgb, -- a * SPRITE_HEALTHBAR_HEALTHALPHA, -- DRAWFLAG_NORMAL -- ); --} -- --void turret_walker_draw() --{ -- float dt; -- -- dt = time - self.move_time; -- self.move_time = time; -- if(dt <= 0) -- return; -- -- fixedmakevectors(self.angles); -- movelib_groundalign4point(300, 100, 0.25, 45); -- setorigin(self, self.origin + self.velocity * dt); -- self.tur_head.angles += dt * self.tur_head.move_avelocity; - self.angles_y = self.move_angles_y; - self.angles_y = self.move_angles.y; -- -- if (self.health < 127) -- if(random() < 0.15) -- te_spark(self.origin + '0 0 40', randomvec() * 256 + '0 0 256', 16); --} -- --void turret_ewheel_draw() --{ -- float dt; -- -- dt = time - self.move_time; -- self.move_time = time; -- if(dt <= 0) -- return; -- -- fixedmakevectors(self.angles); -- setorigin(self, self.origin + self.velocity * dt); -- self.tur_head.angles += dt * self.tur_head.move_avelocity; - self.angles_y = self.move_angles_y; - self.angles_y = self.move_angles.y; -- -- if (self.health < 127) -- if(random() < 0.05) -- te_spark(self.origin + '0 0 40', randomvec() * 256 + '0 0 256', 16); --} -- - void(entity e, entity tagentity, string tagname) setattachment = #443; --void turret_construct() --{ -- if(self.tur_head == world) -- self.tur_head = spawn(); -- -- turret_tid2info(self.turret_type); -- self.netname = tid2info_name; -- -- setorigin(self, self.origin); -- setmodel(self, tid2info_base); -- setmodel(self.tur_head, tid2info_head); -- setsize(self, tid2info_min, tid2info_max); -- setsize(self.tur_head, '0 0 0', '0 0 0'); -- -- if(self.turret_type == TID_EWHEEL) -- setattachment(self.tur_head, self, ""); -- else -- setattachment(self.tur_head, self, "tag_head"); -- -- self.tur_head.classname = "turret_head"; -- self.tur_head.owner = self; -- self.tur_head.move_movetype = MOVETYPE_NOCLIP; -- self.move_movetype = MOVETYPE_NOCLIP; -- self.tur_head.angles = self.angles; -- self.health = 255; -- self.solid = SOLID_BBOX; -- self.tur_head.solid = SOLID_NOT; -- self.movetype = MOVETYPE_NOCLIP; -- self.tur_head.movetype = MOVETYPE_NOCLIP; -- self.draw = turret_draw; -- self.entremove = turret_remove; -- self.drawmask = MASK_NORMAL; -- self.tur_head.drawmask = MASK_NORMAL; -- self.anim_start_time = 0; -- self.draw2d = turret_draw2d; -- self.maxdistance = autocvar_g_waypointsprite_turrets_maxdist; -- self.teamradar_color = '1 0 0'; -- self.alpha = 1; -- -- if(self.turret_type == TID_EWHEEL || self.turret_type == TID_WALKER) -- { -- self.gravity = 1; -- self.movetype = MOVETYPE_BOUNCE; -- self.move_movetype = MOVETYPE_BOUNCE; -- self.move_origin = self.origin; -- self.move_time = time; -- switch(self.turret_type) -- { -- case TID_EWHEEL: -- self.draw = turret_ewheel_draw; -- break; -- case TID_WALKER: -- self.draw = turret_walker_draw; -- break; -- -- } -- } --} -- --entity turret_gibtoss(string _model, vector _from, vector _to, vector _cmod, float _explode); --void turret_gibboom(); --void turret_gib_draw() --{ -- Movetype_Physics_MatchTicrate(autocvar_cl_gibs_ticrate, autocvar_cl_gibs_sloppy); -- -- self.drawmask = MASK_NORMAL; -- -- if(self.cnt) -- { -- if(time >= self.nextthink) -- { -- turret_gibboom(); -- remove(self); -- } -- } -- else -- { -- self.alpha = bound(0, self.nextthink - time, 1); -- if(self.alpha < ALPHA_MIN_VISIBLE) -- remove(self); -- } --} -- --void turret_gibboom() --{ -- float i; -- -- sound (self, CH_SHOTS, "weapons/rocket_impact.wav", VOL_BASE, ATTEN_NORM); -- pointparticles(particleeffectnum("rocket_explode"), self.origin, '0 0 0', 1); -- -- for (i = 1; i < 5; i = i + 1) - turret_gibtoss(strcat("models/turrets/head-gib", ftos(i), ".md3"), self.origin + '0 0 2', self.velocity + randomvec() * 700, '0 0 0', FALSE); - turret_gibtoss(strcat("models/turrets/head-gib", ftos(i), ".md3"), self.origin + '0 0 2', self.velocity + randomvec() * 700, '0 0 0', false); --} -- --entity turret_gibtoss(string _model, vector _from, vector _to, vector _cmod, float _explode) --{ -- entity gib; -- -- traceline(_from, _to, MOVE_NOMONSTERS, world); -- if(trace_startsolid) -- return world; -- -- gib = spawn(); -- setorigin(gib, _from); -- setmodel(gib, _model); -- gib.colormod = _cmod; -- gib.solid = SOLID_CORPSE; -- gib.draw = turret_gib_draw; -- gib.cnt = _explode; -- setsize(gib, '-1 -1 -1', '1 1 1'); -- if(_explode) -- { -- gib.nextthink = time + 0.2 * (autocvar_cl_gibs_lifetime * (1 + prandom() * 0.15)); -- gib.effects = EF_FLAME; -- } -- else -- gib.nextthink = time + autocvar_cl_gibs_lifetime * (1 + prandom() * 0.15); -- -- gib.gravity = 1; -- gib.move_movetype = MOVETYPE_BOUNCE; -- gib.move_origin = _from; -- setorigin(gib, _from); -- gib.move_velocity = _to; -- gib.move_avelocity = prandomvec() * 32; -- gib.move_time = time; -- gib.damageforcescale = 1; -- gib.classname = "turret_gib"; -- -- return gib; --} -- --void turret_die() --{ -- -- sound (self, CH_SHOTS, "weapons/rocket_impact.wav", VOL_BASE, ATTEN_NORM); -- pointparticles(particleeffectnum("rocket_explode"), self.origin, '0 0 0', 1); -- turret_tid2info(self.turret_type); -- if (!autocvar_cl_nogibs) -- { -- // Base -- if(self.turret_type == TID_EWHEEL) - turret_gibtoss(tid2info_base, self.origin + '0 0 18', self.velocity + '0 0 400' + '0.1 0.1 1' * (random() * 400), '-1 -1 -1', TRUE); - turret_gibtoss(tid2info_base, self.origin + '0 0 18', self.velocity + '0 0 400' + '0.1 0.1 1' * (random() * 400), '-1 -1 -1', true); -- else if (self.turret_type == TID_WALKER) - turret_gibtoss(tid2info_base, self.origin + '0 0 18', self.velocity + '0 0 300' + '0.1 0.1 1' * (random() * 200), '-1 -1 -1', TRUE); - turret_gibtoss(tid2info_base, self.origin + '0 0 18', self.velocity + '0 0 300' + '0.1 0.1 1' * (random() * 200), '-1 -1 -1', true); -- else if (self.turret_type == TID_TESLA) - turret_gibtoss(tid2info_base, self.origin + '0 0 18', '0 0 200', '-1 -1 -1', FALSE); - turret_gibtoss(tid2info_base, self.origin + '0 0 18', '0 0 200', '-1 -1 -1', false); -- else -- { -- if (random() > 0.5) -- { - turret_gibtoss("models/turrets/base-gib2.md3", self.origin + '0 0 8', '0 0 50' + randomvec() * 150, '0 0 0', FALSE); - turret_gibtoss("models/turrets/base-gib3.md3", self.origin + '0 0 8', '0 0 50' + randomvec() * 150, '0 0 0', FALSE); - turret_gibtoss("models/turrets/base-gib4.md3", self.origin + '0 0 8', '0 0 50' + randomvec() * 150, '0 0 0', FALSE); - turret_gibtoss("models/turrets/base-gib2.md3", self.origin + '0 0 8', '0 0 50' + randomvec() * 150, '0 0 0', false); - turret_gibtoss("models/turrets/base-gib3.md3", self.origin + '0 0 8', '0 0 50' + randomvec() * 150, '0 0 0', false); - turret_gibtoss("models/turrets/base-gib4.md3", self.origin + '0 0 8', '0 0 50' + randomvec() * 150, '0 0 0', false); -- } -- else - turret_gibtoss("models/turrets/base-gib1.md3", self.origin + '0 0 8', '0 0 0', '0 0 0', TRUE); - turret_gibtoss("models/turrets/base-gib1.md3", self.origin + '0 0 8', '0 0 0', '0 0 0', true); -- - entity headgib = turret_gibtoss(tid2info_head, self.origin + '0 0 32', '0 0 200' + randomvec() * 200, '-1 -1 -1', TRUE); - entity headgib = turret_gibtoss(tid2info_head, self.origin + '0 0 32', '0 0 200' + randomvec() * 200, '-1 -1 -1', true); -- if(headgib) -- { -- headgib.angles = headgib.move_angles = self.tur_head.angles; -- headgib.avelocity = headgib.move_avelocity = self.tur_head.move_avelocity + randomvec() * 45; - headgib.avelocity_y = headgib.move_avelocity_y = headgib.move_avelocity_y * 5; - headgib.avelocity_y = headgib.move_avelocity_y = headgib.move_avelocity.y * 5; -- headgib.gravity = 0.5; -- } -- } -- } -- -- setmodel(self, "null"); -- setmodel(self.tur_head, "null"); --} -- --void ent_turret() --{ - float sf; - sf = ReadByte(); - int sf = ReadByte(); -- -- if(sf & TNSF_SETUP) -- { -- self.turret_type = ReadByte(); -- -- self.origin_x = ReadCoord(); -- self.origin_y = ReadCoord(); -- self.origin_z = ReadCoord(); -- setorigin(self, self.origin); -- -- self.angles_x = ReadAngle(); -- self.angles_y = ReadAngle(); -- -- turret_precache(self.turret_type); -- turret_construct(); -- self.colormap = 1024; -- self.glowmod = '0 1 1'; -- self.tur_head.colormap = self.colormap; -- self.tur_head.glowmod = self.glowmod; -- } -- -- if(sf & TNSF_ANG) -- { -- if(self.tur_head == world) // aparenly this can happpen before TNSF_SETUP. great. -- self.tur_head = spawn(); -- -- self.tur_head.move_angles_x = ReadShort(); -- self.tur_head.move_angles_y = ReadShort(); -- //self.tur_head.angles = self.angles + self.tur_head.move_angles; -- self.tur_head.angles = self.tur_head.move_angles; -- } -- -- if(sf & TNSF_AVEL) -- { -- if(self.tur_head == world) // aparenly this can happpen before TNSF_SETUP. great. -- self.tur_head = spawn(); -- -- self.tur_head.move_avelocity_x = ReadShort(); -- self.tur_head.move_avelocity_y = ReadShort(); -- } -- -- if(sf & TNSF_MOVE) -- { -- self.origin_x = ReadShort(); -- self.origin_y = ReadShort(); -- self.origin_z = ReadShort(); -- setorigin(self, self.origin); -- -- self.velocity_x = ReadShort(); -- self.velocity_y = ReadShort(); -- self.velocity_z = ReadShort(); -- -- self.move_angles_y = ReadShort(); -- -- self.move_time = time; -- self.move_velocity = self.velocity; -- self.move_origin = self.origin; -- } -- -- if(sf & TNSF_ANIM) -- { -- self.frame1time = ReadCoord(); -- self.frame = ReadByte(); -- } -- -- if(sf & TNSF_STATUS) -- { - float _tmp; - _tmp = ReadByte(); - int _tmp = ReadByte(); -- if(_tmp != self.team) -- { -- self.team = _tmp; -- turret_changeteam(); -- } -- -- _tmp = ReadByte(); -- if(_tmp == 0 && self.health != 0) -- turret_die(); -- else if(self.health && self.health != _tmp) -- self.helpme = servertime + 10; -- -- self.health = _tmp; -- } -- //self.enemy.health = self.health / 255; --} diff --cc qcsrc/client/tturrets.qh index cf74f48ad,6316d795b..000000000 deleted file mode 100644,100644 --- a/qcsrc/client/tturrets.qh +++ /dev/null @@@ -1,3 -1,9 +1,0 @@@ -#ifndef TTURRETS_H -#define TTURRETS_H - -#include "../server/tturrets/include/turrets_early.qh" - --void ent_turret(); --void turrets_precache(); --.entity tur_head; -#endif diff --cc qcsrc/client/vehicles/vehicles.qc index d8727e176,290385e49..000000000 deleted file mode 100644,100644 --- a/qcsrc/client/vehicles/vehicles.qc +++ /dev/null @@@ -1,1042 -1,1057 +1,0 @@@ - #define hud_bg "gfx/vehicles/frame.tga" - #define hud_sh "gfx/vehicles/vh-shield.tga" -#if defined(CSQC) - #include "../../dpdefs/csprogsdefs.qh" - #include "../defs.qh" - #include "../../common/constants.qh" - #include "../../common/stats.qh" - #include "../../common/util.qh" - #include "../../common/buffs.qh" - #include "../autocvars.qh" - #include "../movetypes.qh" - #include "../prandom.qh" - #include "../main.qh" - #include "vehicles.qh" - #include "../../csqcmodellib/cl_model.qh" - #include "../../server/t_items.qh" -#elif defined(MENUQC) -#elif defined(SVQC) -#endif -- - #define hud_hp_bar "gfx/vehicles/bar_up_left.tga" - #define hud_hp_ico "gfx/vehicles/health.tga" - #define hud_sh_bar "gfx/vehicles/bar_dwn_left.tga" - #define hud_sh_ico "gfx/vehicles/shield.tga" -const string hud_bg = "gfx/vehicles/frame.tga"; -const string hud_sh = "gfx/vehicles/vh-shield.tga"; -- - #define hud_ammo1_bar "gfx/vehicles/bar_up_right.tga" - #define hud_ammo1_ico "gfx/vehicles/bullets.tga" - #define hud_ammo2_bar "gfx/vehicles/bar_dwn_right.tga" - #define hud_ammo2_ico "gfx/vehicles/rocket.tga" - #define hud_energy "gfx/vehicles/energy.tga" -const string hud_hp_bar = "gfx/vehicles/bar_up_left.tga"; -const string hud_hp_ico = "gfx/vehicles/health.tga"; -const string hud_sh_bar = "gfx/vehicles/bar_dwn_left.tga"; -const string hud_sh_ico = "gfx/vehicles/shield.tga"; -- - #define SBRM_FIRST 1 - #define SBRM_VOLLY 1 - #define SBRM_GUIDE 2 - #define SBRM_ARTILLERY 3 - #define SBRM_LAST 3 -const string hud_ammo1_bar = "gfx/vehicles/bar_up_right.tga"; -const string hud_ammo1_ico = "gfx/vehicles/bullets.tga"; -const string hud_ammo2_bar = "gfx/vehicles/bar_dwn_right.tga"; -const string hud_ammo2_ico = "gfx/vehicles/rocket.tga"; -const string hud_energy = "gfx/vehicles/energy.tga"; -- - #define RSM_FIRST 1 - #define RSM_BOMB 1 - #define RSM_FLARE 2 - #define RSM_LAST 2 -const int SBRM_FIRST = 1; -const int SBRM_VOLLY = 1; -const int SBRM_GUIDE = 2; -const int SBRM_ARTILLERY = 3; -const int SBRM_LAST = 3; - -const int RSM_FIRST = 1; -const int RSM_BOMB = 1; -const int RSM_FLARE = 2; -const int RSM_LAST = 2; -- --entity dropmark; - var float autocvar_cl_vehicles_hudscale = 0.5; - var float autocvar_cl_vehicles_hudalpha = 0.75; -float autocvar_cl_vehicles_hudscale = 0.5; -float autocvar_cl_vehicles_hudalpha = 0.75; -- - #define raptor_ico "gfx/vehicles/raptor.tga" - #define raptor_gun "gfx/vehicles/raptor_guns.tga" - #define raptor_bomb "gfx/vehicles/raptor_bombs.tga" - #define raptor_drop "gfx/vehicles/axh-dropcross.tga" -const string raptor_ico = "gfx/vehicles/raptor.tga"; -const string raptor_gun = "gfx/vehicles/raptor_guns.tga"; -const string raptor_bomb = "gfx/vehicles/raptor_bombs.tga"; -const string raptor_drop = "gfx/vehicles/axh-dropcross.tga"; --string raptor_xhair; -- --void CSQC_WAKIZASHI_HUD(); --void CSQC_SPIDER_HUD(); --void CSQC_RAPTOR_HUD(); --void CSQC_BUMBLE_HUD(); --void CSQC_BUMBLE_GUN_HUD(); -- - #define MAX_AXH 4 -const int MAX_AXH = 4; --entity AuxiliaryXhair[MAX_AXH]; -- --.string axh_image; --.float axh_fadetime; --.float axh_drawflag; --.float axh_scale; -- - #define bumb_ico "gfx/vehicles/bumb.tga" - #define bumb_lgun "gfx/vehicles/bumb_lgun.tga" - #define bumb_rgun "gfx/vehicles/bumb_rgun.tga" -const string bumb_ico = "gfx/vehicles/bumb.tga"; -const string bumb_lgun = "gfx/vehicles/bumb_lgun.tga"; -const string bumb_rgun = "gfx/vehicles/bumb_rgun.tga"; -- - #define bumb_gun_ico "gfx/vehicles/bumb_side.tga" - #define bumb_gun_gun "gfx/vehicles/bumb_side_gun.tga" -const string bumb_gun_ico = "gfx/vehicles/bumb_side.tga"; -const string bumb_gun_gun = "gfx/vehicles/bumb_side_gun.tga"; -- - #define spider_ico "gfx/vehicles/sbot.tga" - #define spider_rkt "gfx/vehicles/sbot_rpods.tga" - #define spider_mgun "gfx/vehicles/sbot_mguns.tga" -const string spider_ico = "gfx/vehicles/sbot.tga"; -const string spider_rkt = "gfx/vehicles/sbot_rpods.tga"; -const string spider_mgun = "gfx/vehicles/sbot_mguns.tga"; --string spider_xhair; // = "gfx/vehicles/axh-special1.tga"; -- - #define waki_ico "gfx/vehicles/waki.tga" - #define waki_eng "gfx/vehicles/waki_e.tga" - #define waki_gun "gfx/vehicles/waki_guns.tga" - #define waki_rkt "gfx/vehicles/waki_rockets.tga" - #define waki_xhair "gfx/vehicles/axh-special1.tga" -const string waki_ico = "gfx/vehicles/waki.tga"; -const string waki_eng = "gfx/vehicles/waki_e.tga"; -const string waki_gun = "gfx/vehicles/waki_guns.tga"; -const string waki_rkt = "gfx/vehicles/waki_rockets.tga"; -const string waki_xhair = "gfx/vehicles/axh-special1.tga"; -- --float alarm1time; --float alarm2time; - float weapon2mode; -int weapon2mode; -- --void AuxiliaryXhair_Draw2D() --{ -- vector loc, psize; -- -- psize = self.axh_scale * draw_getimagesize(self.axh_image); -- loc = project_3d_to_2d(self.move_origin) - 0.5 * psize; - if (!(loc_z < 0 || loc_x < 0 || loc_y < 0 || loc_x > vid_conwidth || loc_y > vid_conheight)) - if (!(loc.z < 0 || loc.x < 0 || loc.y < 0 || loc.x > vid_conwidth || loc.y > vid_conheight)) -- { - loc_z = 0; - psize_z = 0; - loc.z = 0; - psize.z = 0; -- drawpic(loc, self.axh_image, psize, self.colormod, self.alpha, self.axh_drawflag); -- } -- -- if(time - self.cnt > self.axh_fadetime) -- self.draw2d = func_null; --} -- - void Net_AuXair2(float bIsNew) -void Net_AuXair2(bool bIsNew) --{ - float axh_id = bound(0, ReadByte(), MAX_AXH); - int axh_id = bound(0, ReadByte(), MAX_AXH); -- entity axh = AuxiliaryXhair[axh_id]; -- -- if(axh == world || wasfreed(axh)) // MADNESS? THIS IS QQQQCCCCCCCCC (wasfreed, why do you exsist?) -- { -- axh = spawn(); -- axh.draw2d = func_null; -- axh.drawmask = MASK_NORMAL; -- axh.axh_drawflag = DRAWFLAG_ADDITIVE; -- axh.axh_fadetime = 0.1; -- axh.axh_image = "gfx/vehicles/axh-ring.tga"; -- axh.axh_scale = 1; -- axh.alpha = 1; -- AuxiliaryXhair[axh_id] = axh; -- } -- - axh.move_origin_x = ReadCoord(); - axh.move_origin_y = ReadCoord(); - axh.move_origin_z = ReadCoord(); - axh.colormod_x = ReadByte() / 255; - axh.colormod_y = ReadByte() / 255; - axh.colormod_z = ReadByte() / 255; - axh.move_origin_x = ReadCoord(); - axh.move_origin_y = ReadCoord(); - axh.move_origin_z = ReadCoord(); - axh.colormod_x = ReadByte() / 255; - axh.colormod_y = ReadByte() / 255; - axh.colormod_z = ReadByte() / 255; -- axh.cnt = time; -- axh.draw2d = AuxiliaryXhair_Draw2D; --} -- --void Net_VehicleSetup() --{ - - float i; - - float hud_id = ReadByte(); - int hud_id = ReadByte(); -- -- // Weapon update? -- if(hud_id > HUD_VEHICLE_LAST) -- { -- weapon2mode = hud_id - HUD_VEHICLE_LAST; -- return; -- } -- -- // hud_id == 0 means we exited a vehicle, so stop alarm sound/s -- if(hud_id == 0) -- { -- sound(self, CH_TRIGGER_SINGLE, "misc/null.wav", VOL_BASEVOICE, ATTEN_NONE); -- sound(self, CH_PAIN_SINGLE, "misc/null.wav", VOL_BASEVOICE, ATTEN_NONE); -- return; -- } -- -- hud_id = bound(HUD_VEHICLE_FIRST, hud_id, HUD_VEHICLE_LAST); -- -- // Init auxiliary crosshairs - entity axh; - int i; -- for(i = 0; i < MAX_AXH; ++i) -- { - axh = AuxiliaryXhair[i]; - entity axh = AuxiliaryXhair[i]; -- if(axh != world && !wasfreed(axh)) // MADNESS? THIS IS QQQQCCCCCCCCC (wasfreed, why do you exsist?) -- remove(axh); -- -- axh = spawn(); -- axh.draw2d = func_null; -- axh.drawmask = MASK_NORMAL; -- axh.axh_drawflag = DRAWFLAG_NORMAL; -- axh.axh_fadetime = 0.1; -- axh.axh_image = "gfx/vehicles/axh-ring.tga"; -- axh.axh_scale = 1; -- axh.alpha = 1; -- AuxiliaryXhair[i] = axh; -- } -- -- switch(hud_id) -- { -- case HUD_SPIDERBOT: -- // Minigun1 -- AuxiliaryXhair[0].axh_image = "gfx/vehicles/axh-ring.tga"; -- AuxiliaryXhair[0].axh_scale = 0.25; -- // Minigun2 -- AuxiliaryXhair[1].axh_image = "gfx/vehicles/axh-ring.tga"; -- AuxiliaryXhair[1].axh_scale = 0.25; -- // Rocket -- AuxiliaryXhair[2].axh_image = "gfx/vehicles/axh-special1.tga"; -- AuxiliaryXhair[2].axh_scale = 0.5; -- break; -- -- case HUD_WAKIZASHI: -- AuxiliaryXhair[0].axh_image = "gfx/vehicles/axh-bracket.tga"; -- AuxiliaryXhair[0].axh_scale = 0.25; -- break; -- -- case HUD_RAPTOR: -- AuxiliaryXhair[0].axh_image = "gfx/vehicles/axh-special2.tga"; -- AuxiliaryXhair[0].axh_scale = 0.5; -- //AuxiliaryXhair[0].alpha = 0.5; -- -- AuxiliaryXhair[1].axh_image = "gfx/vehicles/axh-bracket.tga"; -- AuxiliaryXhair[1].axh_scale = 0.25; -- //AuxiliaryXhair[1].alpha = 0.75; -- //AuxiliaryXhair[1].axh_drawflag = DRAWFLAG_NORMAL; -- break; -- -- case HUD_BUMBLEBEE: -- // Raygun-locked -- AuxiliaryXhair[0].axh_image = "gfx/vehicles/axh-bracket.tga"; -- AuxiliaryXhair[0].axh_scale = 0.5; -- -- // Gunner1 -- AuxiliaryXhair[1].axh_image = "gfx/vehicles/axh-target.tga"; -- AuxiliaryXhair[1].axh_scale = 0.75; -- -- // Gunner2 -- AuxiliaryXhair[2].axh_image = "gfx/vehicles/axh-target.tga"; -- AuxiliaryXhair[2].axh_scale = 0.75; -- break; -- case HUD_BUMBLEBEE_GUN: -- // Plasma cannons -- AuxiliaryXhair[0].axh_image = "gfx/vehicles/axh-bracket.tga"; -- AuxiliaryXhair[0].axh_scale = 0.25; -- // Raygun -- AuxiliaryXhair[1].axh_image = "gfx/vehicles/axh-bracket.tga"; -- AuxiliaryXhair[1].axh_scale = 0.25; -- break; -- } --} --#define HUD_GETSTATS \ - local noref float vh_health = getstati(STAT_VEHICLESTAT_HEALTH); \ - local noref float shield = getstati(STAT_VEHICLESTAT_SHIELD); \ - local noref float energy = getstati(STAT_VEHICLESTAT_ENERGY); \ - local noref float ammo1 = getstati(STAT_VEHICLESTAT_AMMO1); \ - local noref float reload1 = getstati(STAT_VEHICLESTAT_RELOAD1); \ - local noref float ammo2 = getstati(STAT_VEHICLESTAT_AMMO2); \ - local noref float reload2 = getstati(STAT_VEHICLESTAT_RELOAD2); - int vh_health = getstati(STAT_VEHICLESTAT_HEALTH); \ - float shield = getstati(STAT_VEHICLESTAT_SHIELD); \ - noref int energy = getstati(STAT_VEHICLESTAT_ENERGY); \ - noref float ammo1 = getstati(STAT_VEHICLESTAT_AMMO1); \ - noref float reload1 = getstati(STAT_VEHICLESTAT_RELOAD1); \ - noref int ammo2 = getstati(STAT_VEHICLESTAT_AMMO2); \ - noref int reload2 = getstati(STAT_VEHICLESTAT_RELOAD2); -- --void CSQC_BUMBLE_HUD() --{ --/* -- drawpic(hudloc, waki_s, picsize, '1 1 1', shield, DRAWFLAG_NORMAL); -- drawpic(hudloc, waki_b, picsize, '0 1 0' * health + '1 0 0' * (1 - health), 1, DRAWFLAG_NORMAL); -- drawpic(hudloc, waki_r, picsize, '1 1 1' * reload1 + '1 0 0' * (1 - reload1), 1, DRAWFLAG_NORMAL); -- drawpic(hudloc, waki_e, picsize, '1 1 1' * energy + '1 0 0' * (1 - energy), 1, DRAWFLAG_NORMAL); --*/ -- if(autocvar_r_letterbox) -- return; -- -- vector picsize, hudloc = '0 0 0', pic2size, picloc; -- -- // Fetch health & ammo stats -- HUD_GETSTATS -- -- picsize = draw_getimagesize(hud_bg) * autocvar_cl_vehicles_hudscale; - hudloc_y = vid_conheight - picsize_y; - hudloc_x = vid_conwidth * 0.5 - picsize_x * 0.5; - hudloc.y = vid_conheight - picsize.y; - hudloc.x = vid_conwidth * 0.5 - picsize.x * 0.5; -- -- drawpic(hudloc, hud_bg, picsize, '1 1 1', autocvar_cl_vehicles_hudalpha, DRAWFLAG_NORMAL); -- -- shield *= 0.01; -- vh_health *= 0.01; -- energy *= 0.01; -- reload1 *= 0.01; -- -- pic2size = draw_getimagesize(bumb_ico) * (autocvar_cl_vehicles_hudscale * 0.8); -- picloc = picsize * 0.5 - pic2size * 0.5; -- -- if(vh_health < 0.25) -- drawpic(hudloc + picloc, bumb_ico, pic2size, '1 0 0' + '0 1 1' * sin(time * 8), 1, DRAWFLAG_NORMAL); -- else -- drawpic(hudloc + picloc, bumb_ico, pic2size, '1 1 1' * vh_health + '1 0 0' * (1 - vh_health), 1, DRAWFLAG_NORMAL); -- -- drawpic(hudloc + picloc, bumb_lgun, pic2size, '1 1 1' * energy + '1 0 0' * (1 - energy), 1, DRAWFLAG_NORMAL); -- drawpic(hudloc + picloc, bumb_lgun, pic2size, '1 1 1' * energy + '1 0 0' * (1 - energy), 1, DRAWFLAG_NORMAL); -- drawpic(hudloc + picloc, hud_sh, pic2size, '1 1 1', shield, DRAWFLAG_NORMAL); -- --// Health bar -- picsize = draw_getimagesize(hud_hp_bar) * autocvar_cl_vehicles_hudscale; -- picloc = '69 69 0' * autocvar_cl_vehicles_hudscale; - drawsetcliparea(hudloc_x + picloc_x + (picsize_x * (1 - vh_health)), 0, vid_conwidth, vid_conheight); - drawsetcliparea(hudloc.x + picloc.x + (picsize.x * (1 - vh_health)), 0, vid_conwidth, vid_conheight); -- drawpic(hudloc + picloc, hud_hp_bar, picsize, '1 1 1', 1 , DRAWFLAG_NORMAL); -- drawresetcliparea(); --// .. and icon -- picsize = draw_getimagesize(hud_hp_ico) * autocvar_cl_vehicles_hudscale; -- picloc = '37 65 0' * autocvar_cl_vehicles_hudscale; -- if(vh_health < 0.25) -- { -- if(alarm1time < time) -- { -- alarm1time = time + 2; -- sound(self, CH_PAIN_SINGLE, "vehicles/alarm.wav", VOL_BASEVOICE, ATTEN_NONE); -- } -- -- drawpic(hudloc + picloc, hud_hp_ico, picsize, '1 0 0' + '0 1 1' * sin(time * 8), 1, DRAWFLAG_NORMAL); -- } -- else -- { -- drawpic(hudloc + picloc, hud_hp_ico, picsize, '1 1 1', 1, DRAWFLAG_NORMAL); -- if(alarm1time) -- { -- sound(self, CH_PAIN_SINGLE, "misc/null.wav", VOL_BASEVOICE, ATTEN_NONE); -- alarm1time = 0; -- } -- } -- --// Shield bar -- picsize = draw_getimagesize(hud_sh_bar) * autocvar_cl_vehicles_hudscale; -- picloc = '69 140 0' * autocvar_cl_vehicles_hudscale; - drawsetcliparea(hudloc_x + picloc_x + (picsize_x * (1 - shield)), 0, vid_conwidth, vid_conheight); - drawsetcliparea(hudloc.x + picloc.x + (picsize.x * (1 - shield)), 0, vid_conwidth, vid_conheight); -- drawpic(hudloc + picloc, hud_sh_bar, picsize, '1 1 1', 1, DRAWFLAG_NORMAL); -- drawresetcliparea(); --// .. and icon -- picloc = '40 136 0' * autocvar_cl_vehicles_hudscale; -- picsize = draw_getimagesize(hud_sh_ico) * autocvar_cl_vehicles_hudscale; -- if(shield < 0.25) -- { -- if(alarm2time < time) -- { -- alarm2time = time + 1; -- sound(self, CH_TRIGGER_SINGLE, "vehicles/alarm_shield.wav", VOL_BASEVOICE, ATTEN_NONE); -- } -- drawpic(hudloc + picloc, hud_sh_ico, picsize, '1 0 0' + '0 1 1' * sin(time * 8), 1, DRAWFLAG_NORMAL); -- } -- else -- { -- drawpic(hudloc + picloc, hud_sh_ico, picsize, '1 1 1', 1, DRAWFLAG_NORMAL); -- if(alarm2time) -- { -- sound(self, CH_TRIGGER_SINGLE, "misc/null.wav", VOL_BASEVOICE, ATTEN_NONE); -- alarm2time = 0; -- } -- } -- -- ammo1 *= 0.01; -- ammo2 *= 0.01; -- --// Gunner1 bar -- picsize = draw_getimagesize(hud_ammo1_bar) * autocvar_cl_vehicles_hudscale; -- picloc = '450 69 0' * autocvar_cl_vehicles_hudscale; - drawsetcliparea(hudloc_x + picloc_x, picloc_y, picsize_x * ammo1, vid_conheight); - drawsetcliparea(hudloc.x + picloc.x, picloc.y, picsize.x * ammo1, vid_conheight); -- drawpic(hudloc + picloc, hud_ammo1_bar, picsize, '1 1 1', 1, DRAWFLAG_NORMAL); -- drawresetcliparea(); -- --// Right gunner slot occupied? -- if(!AuxiliaryXhair[1].draw2d) -- { - shield = (picsize_x * 0.5) - (0.5 * stringwidth(_("No right gunner!"), FALSE, '1 0 0' * picsize_y + '0 1 0' * picsize_y)); - shield = (picsize.x * 0.5) - (0.5 * stringwidth(_("No right gunner!"), false, '1 0 0' * picsize.y + '0 1 0' * picsize.y)); -- drawfill(hudloc + picloc - '0.2 0.2 0', picsize + '0.4 0.4 0', '0.25 0.25 0.25', 0.75, DRAWFLAG_NORMAL); - drawstring(hudloc + picloc + '1 0 0' * shield, _("No right gunner!"), '1 0 0' * picsize_y + '0 1 0' * picsize_y, '1 0 0' + '0 1 1' * sin(time * 10), 1, DRAWFLAG_NORMAL); - drawstring(hudloc + picloc + '1 0 0' * shield, _("No right gunner!"), '1 0 0' * picsize.y + '0 1 0' * picsize.y, '1 0 0' + '0 1 1' * sin(time * 10), 1, DRAWFLAG_NORMAL); -- } -- --// .. and icon -- picsize = 1.5 * draw_getimagesize(hud_energy) * autocvar_cl_vehicles_hudscale; -- picloc = '664 60 0' * autocvar_cl_vehicles_hudscale; -- if(ammo1 < 0.2) -- drawpic(hudloc + picloc, hud_energy, picsize, '1 0 0' + '0 1 1' * sin(time * 8), 1, DRAWFLAG_NORMAL); -- else -- drawpic(hudloc + picloc, hud_energy, picsize, '1 1 1', 1, DRAWFLAG_NORMAL); -- --// Gunner2 bar -- picsize = draw_getimagesize(hud_ammo2_bar) * autocvar_cl_vehicles_hudscale; -- picloc = '450 140 0' * autocvar_cl_vehicles_hudscale; - drawsetcliparea(hudloc_x + picloc_x, picloc_y, picsize_x * ammo2, vid_conheight); - drawsetcliparea(hudloc.x + picloc.x, picloc.y, picsize.x * ammo2, vid_conheight); -- drawpic(hudloc + picloc, hud_ammo2_bar, picsize, '1 1 1', 1, DRAWFLAG_NORMAL); -- drawresetcliparea(); --// Left gunner slot occupied? -- if(!AuxiliaryXhair[2].draw2d) -- { - shield = (picsize_x * 0.5) - (0.5 * stringwidth(_("No left gunner!"), FALSE, '1 0 0' * picsize_y + '0 1 0' * picsize_y)); - shield = (picsize.x * 0.5) - (0.5 * stringwidth(_("No left gunner!"), false, '1 0 0' * picsize.y + '0 1 0' * picsize.y)); -- drawfill(hudloc + picloc - '0.2 0.2 0', picsize + '0.4 0.4 0', '0.25 0.25 0.25', 0.75, DRAWFLAG_NORMAL); - drawstring(hudloc + picloc + '1 0 0' * shield, _("No left gunner!"), '1 0 0' * picsize_y + '0 1 0' * picsize_y, '1 0 0' + '0 1 1' * sin(time * 10), 1, DRAWFLAG_NORMAL); - drawstring(hudloc + picloc + '1 0 0' * shield, _("No left gunner!"), '1 0 0' * picsize.y + '0 1 0' * picsize.y, '1 0 0' + '0 1 1' * sin(time * 10), 1, DRAWFLAG_NORMAL); -- } -- --// .. and icon -- picsize = 1.5 * draw_getimagesize(hud_energy) * autocvar_cl_vehicles_hudscale; -- picloc = '664 130 0' * autocvar_cl_vehicles_hudscale; -- if(ammo2 < 0.2) -- drawpic(hudloc + picloc, hud_energy, picsize, '1 0 0' + '0 1 1' * sin(time * 8), 1, DRAWFLAG_NORMAL); -- else -- drawpic(hudloc + picloc, hud_energy, picsize, '1 1 1', 1, DRAWFLAG_NORMAL); -- -- if (scoreboard_showscores) -- HUD_DrawScoreboard(); -- else -- { -- picsize = draw_getimagesize(waki_xhair); - picsize_x *= 0.5; - picsize_y *= 0.5; - drawpic('0.5 0 0' * (vid_conwidth - picsize_x) + '0 0.5 0' * (vid_conheight - picsize_y), waki_xhair, picsize, '1 1 1', 1, DRAWFLAG_NORMAL); - picsize.x *= 0.5; - picsize.y *= 0.5; - drawpic('0.5 0 0' * (vid_conwidth - picsize.x) + '0 0.5 0' * (vid_conheight - picsize.y), waki_xhair, picsize, '1 1 1', 1, DRAWFLAG_NORMAL); -- } -- --} -- --void CSQC_BUMBLE_GUN_HUD() --{ -- -- if(autocvar_r_letterbox) -- return; -- -- vector picsize, hudloc = '0 0 0', pic2size, picloc; -- -- // Fetch health & ammo stats -- HUD_GETSTATS -- -- picsize = draw_getimagesize(hud_bg) * autocvar_cl_vehicles_hudscale; - hudloc_y = vid_conheight - picsize_y; - hudloc_x = vid_conwidth * 0.5 - picsize_x * 0.5; - hudloc.y = vid_conheight - picsize.y; - hudloc.x = vid_conwidth * 0.5 - picsize.x * 0.5; -- -- drawpic(hudloc, hud_bg, picsize, '1 1 1', autocvar_cl_vehicles_hudalpha, DRAWFLAG_NORMAL); -- -- shield *= 0.01; -- vh_health *= 0.01; -- energy *= 0.01; -- reload1 *= 0.01; -- -- pic2size = draw_getimagesize(bumb_gun_ico) * (autocvar_cl_vehicles_hudscale * 0.8); -- picloc = picsize * 0.5 - pic2size * 0.5; -- -- if(vh_health < 0.25) -- drawpic(hudloc + picloc, bumb_gun_ico, pic2size, '1 0 0' + '0 1 1' * sin(time * 8), 1, DRAWFLAG_NORMAL); -- else -- drawpic(hudloc + picloc, bumb_gun_ico, pic2size, '1 1 1' * vh_health + '1 0 0' * (1 - vh_health), 1, DRAWFLAG_NORMAL); -- -- drawpic(hudloc + picloc, bumb_gun_gun, pic2size, '1 1 1' * energy + '1 0 0' * (1 - energy), 1, DRAWFLAG_NORMAL); -- drawpic(hudloc + picloc, hud_sh, pic2size, '1 1 1', shield, DRAWFLAG_NORMAL); -- --// Health bar -- picsize = draw_getimagesize(hud_hp_bar) * autocvar_cl_vehicles_hudscale; -- picloc = '69 69 0' * autocvar_cl_vehicles_hudscale; - drawsetcliparea(hudloc_x + picloc_x + (picsize_x * (1 - vh_health)), 0, vid_conwidth, vid_conheight); - drawsetcliparea(hudloc.x + picloc.x + (picsize.x * (1 - vh_health)), 0, vid_conwidth, vid_conheight); -- drawpic(hudloc + picloc, hud_hp_bar, picsize, '1 1 1', 1 , DRAWFLAG_NORMAL); -- drawresetcliparea(); --// .. and icon -- picsize = draw_getimagesize(hud_hp_ico) * autocvar_cl_vehicles_hudscale; -- picloc = '37 65 0' * autocvar_cl_vehicles_hudscale; -- if(vh_health < 0.25) -- { -- if(alarm1time < time) -- { -- alarm1time = time + 2; -- sound(self, CH_PAIN_SINGLE, "vehicles/alarm.wav", VOL_BASEVOICE, ATTEN_NONE); -- } -- -- drawpic(hudloc + picloc, hud_hp_ico, picsize, '1 0 0' + '0 1 1' * sin(time * 8), 1, DRAWFLAG_NORMAL); -- } -- else -- { -- drawpic(hudloc + picloc, hud_hp_ico, picsize, '1 1 1', 1, DRAWFLAG_NORMAL); -- if(alarm1time) -- { -- sound(self, CH_PAIN_SINGLE, "misc/null.wav", VOL_BASEVOICE, ATTEN_NONE); -- alarm1time = 0; -- } -- } -- --// Shield bar -- picsize = draw_getimagesize(hud_sh_bar) * autocvar_cl_vehicles_hudscale; -- picloc = '69 140 0' * autocvar_cl_vehicles_hudscale; - drawsetcliparea(hudloc_x + picloc_x + (picsize_x * (1 - shield)), 0, vid_conwidth, vid_conheight); - drawsetcliparea(hudloc.x + picloc.x + (picsize.x * (1 - shield)), 0, vid_conwidth, vid_conheight); -- drawpic(hudloc + picloc, hud_sh_bar, picsize, '1 1 1', 1, DRAWFLAG_NORMAL); -- drawresetcliparea(); --// .. and icon -- picloc = '40 136 0' * autocvar_cl_vehicles_hudscale; -- picsize = draw_getimagesize(hud_sh_ico) * autocvar_cl_vehicles_hudscale; -- if(shield < 0.25) -- { -- if(alarm2time < time) -- { -- alarm2time = time + 1; -- sound(self, CH_TRIGGER_SINGLE, "vehicles/alarm_shield.wav", VOL_BASEVOICE, ATTEN_NONE); -- } -- drawpic(hudloc + picloc, hud_sh_ico, picsize, '1 0 0' + '0 1 1' * sin(time * 8), 1, DRAWFLAG_NORMAL); -- } -- else -- { -- drawpic(hudloc + picloc, hud_sh_ico, picsize, '1 1 1', 1, DRAWFLAG_NORMAL); -- if(alarm2time) -- { -- sound(self, CH_TRIGGER_SINGLE, "misc/null.wav", VOL_BASEVOICE, ATTEN_NONE); -- alarm2time = 0; -- } -- } -- --// Gun bar -- picsize = draw_getimagesize(hud_ammo1_bar) * autocvar_cl_vehicles_hudscale; -- picloc = '450 69 0' * autocvar_cl_vehicles_hudscale; - drawsetcliparea(hudloc_x + picloc_x, picloc_y, picsize_x * energy, vid_conheight); - drawsetcliparea(hudloc.x + picloc.x, picloc.y, picsize.x * energy, vid_conheight); -- drawpic(hudloc + picloc, hud_ammo1_bar, picsize, '1 1 1', 1, DRAWFLAG_NORMAL); -- drawresetcliparea(); -- --// .. and icon -- picsize = 1.5 * draw_getimagesize(hud_energy) * autocvar_cl_vehicles_hudscale; -- picloc = '664 60 0' * autocvar_cl_vehicles_hudscale; -- if(energy < 0.2) -- drawpic(hudloc + picloc, hud_energy, picsize, '1 0 0' + '0 1 1' * sin(time * 8), 1, DRAWFLAG_NORMAL); -- else -- drawpic(hudloc + picloc, hud_energy, picsize, '1 1 1', 1, DRAWFLAG_NORMAL); -- -- if (scoreboard_showscores) -- HUD_DrawScoreboard(); -- /* -- else -- { -- picsize = draw_getimagesize(waki_xhair); -- picsize_x *= 0.5; -- picsize_y *= 0.5; -- -- -- drawpic('0.5 0 0' * (vid_conwidth - picsize_x) + '0 0.5 0' * (vid_conheight - picsize_y), waki_xhair, picsize, '1 1 1', 1, DRAWFLAG_NORMAL); -- } -- */ --} -- -- -- --void CSQC_SPIDER_HUD() --{ -- if(autocvar_r_letterbox) -- return; -- -- vector picsize, hudloc = '0 0 0', pic2size, picloc; - float i; - int i; -- -- // Fetch health & ammo stats -- HUD_GETSTATS -- -- picsize = draw_getimagesize(hud_bg) * autocvar_cl_vehicles_hudscale; - hudloc_y = vid_conheight - picsize_y; - hudloc_x = vid_conwidth * 0.5 - picsize_x * 0.5; - hudloc.y = vid_conheight - picsize.y; - hudloc.x = vid_conwidth * 0.5 - picsize.x * 0.5; -- -- drawpic(hudloc, hud_bg, picsize, '1 1 1', autocvar_cl_vehicles_hudalpha, DRAWFLAG_NORMAL); -- -- ammo1 *= 0.01; -- shield *= 0.01; -- vh_health *= 0.01; -- reload2 *= 0.01; -- -- pic2size = draw_getimagesize(spider_ico) * (autocvar_cl_vehicles_hudscale * 0.8); -- picloc = picsize * 0.5 - pic2size * 0.5; -- if(vh_health < 0.25) -- drawpic(hudloc + picloc, spider_ico, pic2size, '1 0 0' + '0 1 1' * sin(time * 8), 1, DRAWFLAG_NORMAL); -- else -- drawpic(hudloc + picloc, spider_ico, pic2size, '1 1 1' * vh_health + '1 0 0' * (1 - vh_health), 1, DRAWFLAG_NORMAL); -- drawpic(hudloc + picloc, spider_rkt, pic2size, '1 1 1' * reload2 + '1 0 0' * (1 - reload2), 1, DRAWFLAG_NORMAL); -- drawpic(hudloc + picloc, spider_mgun, pic2size, '1 1 1' * ammo1 + '1 0 0' * (1 - ammo1), 1, DRAWFLAG_NORMAL); -- drawpic(hudloc + picloc, hud_sh, pic2size, '1 1 1', shield, DRAWFLAG_NORMAL); -- --// Health bar -- picsize = draw_getimagesize(hud_hp_bar) * autocvar_cl_vehicles_hudscale; -- picloc = '69 69 0' * autocvar_cl_vehicles_hudscale; - drawsetcliparea(hudloc_x + picloc_x + (picsize_x * (1 - vh_health)), 0, vid_conwidth, vid_conheight); - drawsetcliparea(hudloc.x + picloc.x + (picsize.x * (1 - vh_health)), 0, vid_conwidth, vid_conheight); -- drawpic(hudloc + picloc, hud_hp_bar, picsize, '1 1 1', 1 , DRAWFLAG_NORMAL); -- drawresetcliparea(); --// .. and icon -- picsize = draw_getimagesize(hud_hp_ico) * autocvar_cl_vehicles_hudscale; -- picloc = '37 65 0' * autocvar_cl_vehicles_hudscale; -- if(vh_health < 0.25) -- { -- if(alarm1time < time) -- { -- alarm1time = time + 2; -- sound(self, CH_PAIN_SINGLE, "vehicles/alarm.wav", VOL_BASEVOICE, ATTEN_NONE); -- } -- drawpic(hudloc + picloc, hud_hp_ico, picsize, '1 0 0' + '0 1 1' * sin(time * 8), 1, DRAWFLAG_NORMAL); -- } -- else -- { -- drawpic(hudloc + picloc, hud_hp_ico, picsize, '1 1 1', 1, DRAWFLAG_NORMAL); -- if(alarm1time) -- { -- sound(self, CH_PAIN_SINGLE, "misc/null.wav", VOL_BASEVOICE, ATTEN_NONE); -- alarm1time = 0; -- } -- } --// Shield bar -- picsize = draw_getimagesize(hud_sh_bar) * autocvar_cl_vehicles_hudscale; -- picloc = '69 140 0' * autocvar_cl_vehicles_hudscale; - drawsetcliparea(hudloc_x + picloc_x + (picsize_x * (1 - shield)), 0, vid_conwidth, vid_conheight); - drawsetcliparea(hudloc.x + picloc.x + (picsize.x * (1 - shield)), 0, vid_conwidth, vid_conheight); -- drawpic(hudloc + picloc, hud_sh_bar, picsize, '1 1 1', 1, DRAWFLAG_NORMAL); -- drawresetcliparea(); --// .. and icon -- picloc = '40 136 0' * autocvar_cl_vehicles_hudscale; -- picsize = draw_getimagesize(hud_sh_ico) * autocvar_cl_vehicles_hudscale; -- if(shield < 0.25) -- { -- if(alarm2time < time) -- { -- alarm2time = time + 1; -- sound(self, CH_TRIGGER_SINGLE, "vehicles/alarm_shield.wav", VOL_BASEVOICE, ATTEN_NONE); -- } -- drawpic(hudloc + picloc, hud_sh_ico, picsize, '1 0 0' + '0 1 1' * sin(time * 8), 1, DRAWFLAG_NORMAL); -- } -- else -- { -- drawpic(hudloc + picloc, hud_sh_ico, picsize, '1 1 1', 1, DRAWFLAG_NORMAL); -- if(alarm2time) -- { -- sound(self, CH_TRIGGER_SINGLE, "misc/null.wav", VOL_BASEVOICE, ATTEN_NONE); -- alarm2time = 0; -- } -- } -- --// Minigun bar -- picsize = draw_getimagesize(hud_ammo1_bar) * autocvar_cl_vehicles_hudscale; -- picloc = '450 69 0' * autocvar_cl_vehicles_hudscale; - drawsetcliparea(hudloc_x + picloc_x, picloc_y, picsize_x * ammo1, vid_conheight); - drawsetcliparea(hudloc.x + picloc.x, picloc.y, picsize.x * ammo1, vid_conheight); -- drawpic(hudloc + picloc, hud_ammo1_bar, picsize, '1 1 1', 1, DRAWFLAG_NORMAL); -- drawresetcliparea(); --// .. and icon -- picsize = draw_getimagesize(hud_ammo1_ico) * autocvar_cl_vehicles_hudscale; -- picloc = '664 60 0' * autocvar_cl_vehicles_hudscale; -- if(ammo1 < 0.2) -- drawpic(hudloc + picloc, hud_ammo1_ico, picsize, '1 0 0' + '0 1 1' * sin(time * 8), 1, DRAWFLAG_NORMAL); -- else -- drawpic(hudloc + picloc, hud_ammo1_ico, picsize, '1 1 1', 1, DRAWFLAG_NORMAL); -- --// Rocket ammo bar -- picsize = draw_getimagesize(hud_ammo2_bar) * autocvar_cl_vehicles_hudscale; - ammo1 = picsize_x / 8; - ammo1 = picsize.x / 8; -- picloc = '450 140 0' * autocvar_cl_vehicles_hudscale; - drawsetcliparea(hudloc_x + picloc_x, hudloc_y + picloc_y, picsize_x * reload2, vid_conheight); - drawsetcliparea(hudloc.x + picloc.x, hudloc.y + picloc.y, picsize.x * reload2, vid_conheight); -- drawpic(hudloc + picloc, hud_ammo2_bar, picsize, '1 1 1', 1, DRAWFLAG_NORMAL); -- drawresetcliparea(); -- --// .. and icons -- pic2size = 0.35 * draw_getimagesize(hud_ammo2_ico) * autocvar_cl_vehicles_hudscale; - picloc_x -= pic2size_x; - picloc_y += pic2size_y * 2.25; - picloc.x -= pic2size.x; - picloc.y += pic2size.y * 2.25; -- if(ammo2 == 9) -- { -- for(i = 1; i < 9; ++i) -- { - picloc_x += ammo1; - picloc.x += ammo1; -- drawpic(hudloc + picloc, hud_ammo2_ico, pic2size, ((8 * reload2 <= i) ? '0 0 0' : '1 1 1'), 0.75, DRAWFLAG_NORMAL); -- } -- } -- else -- { -- for(i = 1; i < 9; ++i) -- { - picloc_x += ammo1; - picloc.x += ammo1; -- drawpic(hudloc + picloc, hud_ammo2_ico, pic2size, ((i >= ammo2) ? '1 1 1' : '0 0 0'), 0.75, DRAWFLAG_NORMAL); -- } -- } -- pic2size = draw_getimagesize(hud_ammo2_ico) * autocvar_cl_vehicles_hudscale; -- picloc = '664 130 0' * autocvar_cl_vehicles_hudscale; -- if(ammo2 == 9) -- drawpic(hudloc + picloc, hud_ammo2_ico, pic2size, '1 0 0' + '0 1 1' * sin(time * 8), 1, DRAWFLAG_NORMAL); -- else -- drawpic(hudloc + picloc, hud_ammo2_ico, pic2size, '1 1 1', 1, DRAWFLAG_NORMAL); -- -- if (scoreboard_showscores) -- HUD_DrawScoreboard(); -- else -- { -- switch(weapon2mode) -- { -- case SBRM_VOLLY: -- spider_xhair = "gfx/vehicles/axh-bracket.tga"; -- break; -- case SBRM_GUIDE: -- spider_xhair = "gfx/vehicles/axh-cross.tga"; -- break; -- case SBRM_ARTILLERY: -- spider_xhair = "gfx/vehicles/axh-tag.tga"; -- break; -- default: -- spider_xhair= "gfx/vehicles/axh-tag.tga"; -- } -- -- picsize = draw_getimagesize(spider_xhair); - picsize_x *= autocvar_cl_vehicle_spiderbot_cross_size; - picsize_y *= autocvar_cl_vehicle_spiderbot_cross_size; - picsize.x *= autocvar_cl_vehicle_spiderbot_cross_size; - picsize.y *= autocvar_cl_vehicle_spiderbot_cross_size; -- - drawpic('0.5 0 0' * (vid_conwidth - picsize_x) + '0 0.5 0' * (vid_conheight - picsize_y), spider_xhair, picsize, '1 1 1', autocvar_cl_vehicle_spiderbot_cross_alpha, DRAWFLAG_ADDITIVE); - drawpic('0.5 0 0' * (vid_conwidth - picsize.x) + '0 0.5 0' * (vid_conheight - picsize.y), spider_xhair, picsize, '1 1 1', autocvar_cl_vehicle_spiderbot_cross_alpha, DRAWFLAG_ADDITIVE); -- } --} -- --void CSQC_RAPTOR_HUD() --{ -- if(autocvar_r_letterbox) -- return; -- -- vector picsize, hudloc = '0 0 0', pic2size, picloc; -- -- // Fetch health & ammo stats -- HUD_GETSTATS -- -- picsize = draw_getimagesize(hud_bg) * autocvar_cl_vehicles_hudscale; - hudloc_y = vid_conheight - picsize_y; - hudloc_x = vid_conwidth * 0.5 - picsize_x * 0.5; - hudloc.y = vid_conheight - picsize.y; - hudloc.x = vid_conwidth * 0.5 - picsize.x * 0.5; -- -- drawpic(hudloc, hud_bg, picsize, '1 1 1', autocvar_cl_vehicles_hudalpha, DRAWFLAG_NORMAL); -- -- ammo1 *= 0.01; -- ammo2 *= 0.01; -- shield *= 0.01; -- vh_health *= 0.01; -- energy *= 0.01; -- reload1 = reload2 * 0.01; -- //reload2 *= 0.01; -- -- pic2size = draw_getimagesize(spider_ico) * (autocvar_cl_vehicles_hudscale * 0.8); -- picloc = picsize * 0.5 - pic2size * 0.5; -- if(vh_health < 0.25) -- drawpic(hudloc + picloc, raptor_ico, pic2size, '1 0 0' + '0 1 1' * sin(time * 8), 1, DRAWFLAG_NORMAL); -- else -- drawpic(hudloc + picloc, raptor_ico, pic2size, '1 1 1' * vh_health + '1 0 0' * (1 - vh_health), 1, DRAWFLAG_NORMAL); -- drawpic(hudloc + picloc, raptor_bomb, pic2size, '1 1 1' * reload1 + '1 0 0' * (1 - reload1), 1, DRAWFLAG_NORMAL); -- drawpic(hudloc + picloc, raptor_gun, pic2size, '1 1 1' * energy + '1 0 0' * (1 - energy), 1, DRAWFLAG_NORMAL); -- drawpic(hudloc + picloc, hud_sh, pic2size, '1 1 1', shield, DRAWFLAG_NORMAL); -- --// Health bar -- picsize = draw_getimagesize(hud_hp_bar) * autocvar_cl_vehicles_hudscale; -- picloc = '69 69 0' * autocvar_cl_vehicles_hudscale; - drawsetcliparea(hudloc_x + picloc_x + (picsize_x * (1 - vh_health)), 0, vid_conwidth, vid_conheight); - drawsetcliparea(hudloc.x + picloc.x + (picsize.x * (1 - vh_health)), 0, vid_conwidth, vid_conheight); -- drawpic(hudloc + picloc, hud_hp_bar, picsize, '1 1 1', 1 , DRAWFLAG_NORMAL); -- drawresetcliparea(); --// .. and icon -- picsize = draw_getimagesize(hud_hp_ico) * autocvar_cl_vehicles_hudscale; -- picloc = '37 65 0' * autocvar_cl_vehicles_hudscale; -- if(vh_health < 0.25) -- { -- if(alarm1time < time) -- { -- alarm1time = time + 2; -- sound(self, CH_PAIN_SINGLE, "vehicles/alarm.wav", VOL_BASEVOICE, ATTEN_NONE); -- } -- -- drawpic(hudloc + picloc, hud_hp_ico, picsize, '1 0 0' + '0 1 1' * sin(time * 8), 1, DRAWFLAG_NORMAL); -- } -- else -- { -- drawpic(hudloc + picloc, hud_hp_ico, picsize, '1 1 1', 1, DRAWFLAG_NORMAL); -- if(alarm1time) -- { -- sound(self, CH_PAIN_SINGLE, "misc/null.wav", VOL_BASEVOICE, ATTEN_NONE); -- alarm1time = 0; -- } -- } -- --// Shield bar -- picsize = draw_getimagesize(hud_sh_bar) * autocvar_cl_vehicles_hudscale; -- picloc = '69 140 0' * autocvar_cl_vehicles_hudscale; - drawsetcliparea(hudloc_x + picloc_x + (picsize_x * (1 - shield)), 0, vid_conwidth, vid_conheight); - drawsetcliparea(hudloc.x + picloc.x + (picsize.x * (1 - shield)), 0, vid_conwidth, vid_conheight); -- drawpic(hudloc + picloc, hud_sh_bar, picsize, '1 1 1', 1, DRAWFLAG_NORMAL); -- drawresetcliparea(); --// .. and icon -- picloc = '40 136 0' * autocvar_cl_vehicles_hudscale; -- picsize = draw_getimagesize(hud_sh_ico) * autocvar_cl_vehicles_hudscale; -- if(shield < 0.25) -- { -- if(alarm2time < time) -- { -- alarm2time = time + 1; -- sound(self, CH_TRIGGER_SINGLE, "vehicles/alarm_shield.wav", VOL_BASEVOICE, ATTEN_NONE); -- } -- drawpic(hudloc + picloc, hud_sh_ico, picsize, '1 0 0' + '0 1 1' * sin(time * 8), 1, DRAWFLAG_NORMAL); -- } -- else -- { -- drawpic(hudloc + picloc, hud_sh_ico, picsize, '1 1 1', 1, DRAWFLAG_NORMAL); -- if(alarm2time) -- { -- sound(self, CH_TRIGGER_SINGLE, "misc/null.wav", VOL_BASEVOICE, ATTEN_NONE); -- alarm2time = 0; -- } -- } -- --// Gun bar -- picsize = draw_getimagesize(hud_ammo1_bar) * autocvar_cl_vehicles_hudscale; -- picloc = '450 69 0' * autocvar_cl_vehicles_hudscale; - drawsetcliparea(hudloc_x + picloc_x, picloc_y, picsize_x * energy, vid_conheight); - drawsetcliparea(hudloc.x + picloc.x, picloc.y, picsize.x * energy, vid_conheight); -- drawpic(hudloc + picloc, hud_ammo1_bar, picsize, '1 1 1', 1, DRAWFLAG_NORMAL); -- drawresetcliparea(); --// .. and icon -- picsize = draw_getimagesize(hud_ammo1_ico) * autocvar_cl_vehicles_hudscale; -- picloc = '664 60 0' * autocvar_cl_vehicles_hudscale; -- if(energy < 0.2) -- drawpic(hudloc + picloc, hud_ammo1_ico, picsize, '1 0 0' + '0 1 1' * sin(time * 8), 1, DRAWFLAG_NORMAL); -- else -- drawpic(hudloc + picloc, hud_ammo1_ico, picsize, '1 1 1', 1, DRAWFLAG_NORMAL); -- --// Bomb bar -- picsize = draw_getimagesize(hud_ammo2_bar) * autocvar_cl_vehicles_hudscale; -- picloc = '450 140 0' * autocvar_cl_vehicles_hudscale; - drawsetcliparea(hudloc_x + picloc_x, hudloc_y + picloc_y, picsize_x * reload1, vid_conheight); - drawsetcliparea(hudloc.x + picloc.x, hudloc.y + picloc.y, picsize.x * reload1, vid_conheight); -- drawpic(hudloc + picloc, hud_ammo2_bar, picsize, '1 1 1', 1, DRAWFLAG_NORMAL); -- drawresetcliparea(); --// .. and icon -- pic2size = draw_getimagesize(hud_ammo2_ico) * autocvar_cl_vehicles_hudscale; -- picloc = '664 130 0' * autocvar_cl_vehicles_hudscale; -- if(reload1 != 1) -- drawpic(hudloc + picloc, hud_ammo2_ico, pic2size, '1 0 0' + '0 1 1' * sin(time * 8), 1, DRAWFLAG_NORMAL); -- else -- drawpic(hudloc + picloc, hud_ammo2_ico, pic2size, '1 1 1', 1, DRAWFLAG_NORMAL); -- -- if(weapon2mode == RSM_FLARE) -- { -- raptor_xhair = "gfx/vehicles/axh-bracket.tga"; -- } -- else -- { -- raptor_xhair = "gfx/vehicles/axh-ring.tga"; -- -- // Bombing crosshair -- if(!dropmark) -- { -- dropmark = spawn(); -- dropmark.owner = self; -- dropmark.gravity = 1; -- } -- -- if(reload2 == 100) -- { -- vector where; -- -- setorigin(dropmark, pmove_org); -- dropmark.velocity = pmove_vel; -- tracetoss(dropmark, self); -- -- where = project_3d_to_2d(trace_endpos); -- -- setorigin(dropmark, trace_endpos); -- picsize = draw_getimagesize(raptor_drop) * 0.2; -- - if (!(where_z < 0 || where_x < 0 || where_y < 0 || where_x > vid_conwidth || where_y > vid_conheight)) - if (!(where.z < 0 || where.x < 0 || where.y < 0 || where.x > vid_conwidth || where.y > vid_conheight)) -- { - where_x -= picsize_x * 0.5; - where_y -= picsize_y * 0.5; - where_z = 0; - where.x -= picsize.x * 0.5; - where.y -= picsize.y * 0.5; - where.z = 0; -- drawpic(where, raptor_drop, picsize, '0 2 0', 1, DRAWFLAG_ADDITIVE); -- } -- dropmark.cnt = time + 5; -- } -- else -- { -- vector where; -- if(dropmark.cnt > time) -- { -- where = project_3d_to_2d(dropmark.origin); -- picsize = draw_getimagesize(raptor_drop) * 0.25; -- - if (!(where_z < 0 || where_x < 0 || where_y < 0 || where_x > vid_conwidth || where_y > vid_conheight)) - if (!(where.z < 0 || where.x < 0 || where.y < 0 || where.x > vid_conwidth || where.y > vid_conheight)) -- { - where_x -= picsize_x * 0.5; - where_y -= picsize_y * 0.5; - where_z = 0; - where.x -= picsize.x * 0.5; - where.y -= picsize.y * 0.5; - where.z = 0; -- drawpic(where, raptor_drop, picsize, '2 0 0', 1, DRAWFLAG_ADDITIVE); -- } -- } -- } -- } -- -- if (scoreboard_showscores) -- HUD_DrawScoreboard(); -- else -- { -- picsize = draw_getimagesize(raptor_xhair); - picsize_x *= 0.5; - picsize_y *= 0.5; - picsize.x *= 0.5; - picsize.y *= 0.5; -- - drawpic('0.5 0 0' * (vid_conwidth - picsize_x) + '0 0.5 0' * (vid_conheight - picsize_y), raptor_xhair, picsize, '1 1 1', 1, DRAWFLAG_NORMAL); - drawpic('0.5 0 0' * (vid_conwidth - picsize.x) + '0 0.5 0' * (vid_conheight - picsize.y), raptor_xhair, picsize, '1 1 1', 1, DRAWFLAG_NORMAL); -- } --} -- --void CSQC_WAKIZASHI_HUD() --{ --/* -- drawpic(hudloc, waki_s, picsize, '1 1 1', shield, DRAWFLAG_NORMAL); -- drawpic(hudloc, waki_b, picsize, '0 1 0' * health + '1 0 0' * (1 - health), 1, DRAWFLAG_NORMAL); -- drawpic(hudloc, waki_r, picsize, '1 1 1' * reload1 + '1 0 0' * (1 - reload1), 1, DRAWFLAG_NORMAL); -- drawpic(hudloc, waki_e, picsize, '1 1 1' * energy + '1 0 0' * (1 - energy), 1, DRAWFLAG_NORMAL); --*/ -- if(autocvar_r_letterbox) -- return; -- -- vector picsize, hudloc = '0 0 0', pic2size, picloc; -- -- // Fetch health & ammo stats -- HUD_GETSTATS -- -- picsize = draw_getimagesize(hud_bg) * autocvar_cl_vehicles_hudscale; - hudloc_y = vid_conheight - picsize_y; - hudloc_x = vid_conwidth * 0.5 - picsize_x * 0.5; - hudloc.y = vid_conheight - picsize.y; - hudloc.x = vid_conwidth * 0.5 - picsize.x * 0.5; -- -- drawpic(hudloc, hud_bg, picsize, '1 1 1', autocvar_cl_vehicles_hudalpha, DRAWFLAG_NORMAL); -- -- shield *= 0.01; -- vh_health *= 0.01; -- energy *= 0.01; -- reload1 *= 0.01; -- -- pic2size = draw_getimagesize(spider_ico) * (autocvar_cl_vehicles_hudscale * 0.8); -- picloc = picsize * 0.5 - pic2size * 0.5; -- if(vh_health < 0.25) -- drawpic(hudloc + picloc, waki_ico, pic2size, '1 0 0' + '0 1 1' * sin(time * 8), 1, DRAWFLAG_NORMAL); -- else -- drawpic(hudloc + picloc, waki_ico, pic2size, '1 1 1' * vh_health + '1 0 0' * (1 - vh_health), 1, DRAWFLAG_NORMAL); -- drawpic(hudloc + picloc, waki_eng, pic2size, '1 1 1' * energy + '1 0 0' * (1 - energy), 1, DRAWFLAG_NORMAL); -- drawpic(hudloc + picloc, waki_gun, pic2size, '1 1 1' * energy + '1 0 0' * (1 - energy), 1, DRAWFLAG_NORMAL); -- drawpic(hudloc + picloc, waki_rkt, pic2size, '1 1 1' * reload1 + '1 0 0' * (1 - reload1), 1, DRAWFLAG_NORMAL); -- drawpic(hudloc + picloc, hud_sh, pic2size, '1 1 1', shield, DRAWFLAG_NORMAL); -- --// Health bar -- picsize = draw_getimagesize(hud_hp_bar) * autocvar_cl_vehicles_hudscale; -- picloc = '69 69 0' * autocvar_cl_vehicles_hudscale; - drawsetcliparea(hudloc_x + picloc_x + (picsize_x * (1 - vh_health)), 0, vid_conwidth, vid_conheight); - drawsetcliparea(hudloc.x + picloc.x + (picsize.x * (1 - vh_health)), 0, vid_conwidth, vid_conheight); -- drawpic(hudloc + picloc, hud_hp_bar, picsize, '1 1 1', 1 , DRAWFLAG_NORMAL); -- drawresetcliparea(); --// .. and icon -- picsize = draw_getimagesize(hud_hp_ico) * autocvar_cl_vehicles_hudscale; -- picloc = '37 65 0' * autocvar_cl_vehicles_hudscale; -- if(vh_health < 0.25) -- { -- if(alarm1time < time) -- { -- alarm1time = time + 2; -- sound(self, CH_PAIN_SINGLE, "vehicles/alarm.wav", VOL_BASEVOICE, ATTEN_NONE); -- } -- -- drawpic(hudloc + picloc, hud_hp_ico, picsize, '1 0 0' + '0 1 1' * sin(time * 8), 1, DRAWFLAG_NORMAL); -- } -- else -- { -- drawpic(hudloc + picloc, hud_hp_ico, picsize, '1 1 1', 1, DRAWFLAG_NORMAL); -- if(alarm1time) -- { -- sound(self, CH_PAIN_SINGLE, "misc/null.wav", VOL_BASEVOICE, ATTEN_NONE); -- alarm1time = 0; -- } -- } -- -- --// Shield bar -- picsize = draw_getimagesize(hud_sh_bar) * autocvar_cl_vehicles_hudscale; -- picloc = '69 140 0' * autocvar_cl_vehicles_hudscale; - drawsetcliparea(hudloc_x + picloc_x + (picsize_x * (1 - shield)), 0, vid_conwidth, vid_conheight); - drawsetcliparea(hudloc.x + picloc.x + (picsize.x * (1 - shield)), 0, vid_conwidth, vid_conheight); -- drawpic(hudloc + picloc, hud_sh_bar, picsize, '1 1 1', 1, DRAWFLAG_NORMAL); -- drawresetcliparea(); --// .. and icon -- picloc = '40 136 0' * autocvar_cl_vehicles_hudscale; -- picsize = draw_getimagesize(hud_sh_ico) * autocvar_cl_vehicles_hudscale; -- if(shield < 0.25) -- { -- if(alarm2time < time) -- { -- alarm2time = time + 1; -- sound(self, CH_TRIGGER_SINGLE, "vehicles/alarm_shield.wav", VOL_BASEVOICE, ATTEN_NONE); -- } -- drawpic(hudloc + picloc, hud_sh_ico, picsize, '1 0 0' + '0 1 1' * sin(time * 8), 1, DRAWFLAG_NORMAL); -- } -- else -- { -- drawpic(hudloc + picloc, hud_sh_ico, picsize, '1 1 1', 1, DRAWFLAG_NORMAL); -- if(alarm2time) -- { -- sound(self, CH_TRIGGER_SINGLE, "misc/null.wav", VOL_BASEVOICE, ATTEN_NONE); -- alarm2time = 0; -- } -- } -- --// Gun bar -- picsize = draw_getimagesize(hud_ammo1_bar) * autocvar_cl_vehicles_hudscale; -- picloc = '450 69 0' * autocvar_cl_vehicles_hudscale; - drawsetcliparea(hudloc_x + picloc_x, picloc_y, picsize_x * energy, vid_conheight); - drawsetcliparea(hudloc.x + picloc.x, picloc.y, picsize.x * energy, vid_conheight); -- drawpic(hudloc + picloc, hud_ammo1_bar, picsize, '1 1 1', 1, DRAWFLAG_NORMAL); -- drawresetcliparea(); --// .. and icon -- picsize = draw_getimagesize(hud_ammo1_ico) * autocvar_cl_vehicles_hudscale; -- picloc = '664 60 0' * autocvar_cl_vehicles_hudscale; -- if(energy < 0.2) -- drawpic(hudloc + picloc, hud_ammo1_ico, picsize, '1 0 0' + '0 1 1' * sin(time * 8), 1, DRAWFLAG_NORMAL); -- else -- drawpic(hudloc + picloc, hud_ammo1_ico, picsize, '1 1 1', 1, DRAWFLAG_NORMAL); -- --// Bomb bar -- picsize = draw_getimagesize(hud_ammo2_bar) * autocvar_cl_vehicles_hudscale; -- picloc = '450 140 0' * autocvar_cl_vehicles_hudscale; - drawsetcliparea(hudloc_x + picloc_x, hudloc_y + picloc_y, picsize_x * reload1, vid_conheight); - drawsetcliparea(hudloc.x + picloc.x, hudloc.y + picloc.y, picsize.x * reload1, vid_conheight); -- drawpic(hudloc + picloc, hud_ammo2_bar, picsize, '1 1 1', 1, DRAWFLAG_NORMAL); -- drawresetcliparea(); --// .. and icon -- pic2size = draw_getimagesize(hud_ammo2_ico) * autocvar_cl_vehicles_hudscale; -- picloc = '664 130 0' * autocvar_cl_vehicles_hudscale; -- if(reload1 != 1) -- drawpic(hudloc + picloc, hud_ammo2_ico, pic2size, '1 0 0' + '0 1 1' * sin(time * 8), 1, DRAWFLAG_NORMAL); -- else -- drawpic(hudloc + picloc, hud_ammo2_ico, pic2size, '1 1 1', 1, DRAWFLAG_NORMAL); -- -- if (scoreboard_showscores) -- HUD_DrawScoreboard(); -- else -- { -- picsize = draw_getimagesize(waki_xhair); - picsize_x *= 0.5; - picsize_y *= 0.5; - picsize.x *= 0.5; - picsize.y *= 0.5; -- -- - drawpic('0.5 0 0' * (vid_conwidth - picsize_x) + '0 0.5 0' * (vid_conheight - picsize_y), waki_xhair, picsize, '1 1 1', 1, DRAWFLAG_NORMAL); - drawpic('0.5 0 0' * (vid_conwidth - picsize.x) + '0 0.5 0' * (vid_conheight - picsize.y), waki_xhair, picsize, '1 1 1', 1, DRAWFLAG_NORMAL); -- } --} -- --void Vehicles_Precache() --{ -- precache_model("models/vehicles/bomblet.md3"); -- precache_model("models/vehicles/clusterbomb.md3"); -- precache_model("models/vehicles/clusterbomb_fragment.md3"); -- precache_model("models/vehicles/rocket01.md3"); -- precache_model("models/vehicles/rocket02.md3"); -- -- precache_sound ("vehicles/alarm.wav"); -- precache_sound ("vehicles/alarm_shield.wav"); --} -- --void RaptorCBShellfragDraw() --{ -- if(wasfreed(self)) -- return; -- -- Movetype_Physics_MatchTicrate(autocvar_cl_gibs_ticrate, autocvar_cl_gibs_sloppy); -- self.move_avelocity += randomvec() * 15; -- self.renderflags = 0; -- -- if(self.cnt < time) -- self.alpha = bound(0, self.nextthink - time, 1); -- -- if(self.alpha < ALPHA_MIN_VISIBLE) -- remove(self); --} -- --void RaptorCBShellfragToss(vector _org, vector _vel, vector _ang) --{ -- entity sfrag; -- -- sfrag = spawn(); -- setmodel(sfrag, "models/vehicles/clusterbomb_fragment.md3"); -- setorigin(sfrag, _org); -- -- sfrag.move_movetype = MOVETYPE_BOUNCE; -- sfrag.gravity = 0.15; -- sfrag.solid = SOLID_CORPSE; -- -- sfrag.draw = RaptorCBShellfragDraw; -- -- sfrag.move_origin = sfrag.origin = _org; -- sfrag.move_velocity = _vel; -- sfrag.move_avelocity = prandomvec() * vlen(sfrag.move_velocity); -- sfrag.angles = self.move_angles = _ang; -- -- sfrag.move_time = time; -- sfrag.damageforcescale = 4; -- -- sfrag.nextthink = time + 3; -- sfrag.cnt = time + 2; -- sfrag.alpha = 1; -- sfrag.drawmask = MASK_NORMAL; --} diff --cc qcsrc/client/vehicles/vehicles.qh index 66b2af1bf,7e509e91e..000000000 deleted file mode 100644,100644 --- a/qcsrc/client/vehicles/vehicles.qh +++ /dev/null @@@ -1,4 -1,9 +1,0 @@@ -#ifndef VEHICLES_H -#define VEHICLES_H - --void RaptorCBShellfragDraw(); --void RaptorCBShellfragToss(vector _org, vector _vel, vector _ang); --void Vehicles_Precache(); - -void Net_AuXair2(bool bIsNew); -void Net_VehicleSetup(); -#endif diff --cc qcsrc/client/view.qc index 000000000,ee8ef320a..bbbad7c65 mode 000000,100644..100644 --- a/qcsrc/client/view.qc +++ b/qcsrc/client/view.qc @@@ -1,0 -1,1962 +1,2107 @@@ + #if defined(CSQC) + #include "../dpdefs/csprogsdefs.qh" + #include "defs.qh" + #include "../common/constants.qh" + #include "../common/stats.qh" + #include "../warpzonelib/mathlib.qh" + #include "../warpzonelib/common.qh" + #include "../warpzonelib/client.qh" + #include "../common/teams.qh" + #include "../common/util.qh" + #include "../common/nades.qh" + #include "../common/weapons/weapons.qh" + #include "../common/mapinfo.qh" + #include "autocvars.qh" + #include "hud.qh" + #include "scoreboard.qh" + #include "noise.qh" + #include "main.qh" ++ #include "waypointsprites.qh" + #include "../csqcmodellib/cl_player.qh" + #elif defined(MENUQC) + #elif defined(SVQC) + #endif + + entity porto; + vector polyline[16]; + void Porto_Draw() + { + vector p, dir, ang, q, nextdir; + float portal_number, portal1_idx; + + if(activeweapon != WEP_PORTO || spectatee_status || gametype == MAPINFO_TYPE_NEXBALL) + return; + if(g_balance_porto_secondary) + return; + if(intermission == 1) + return; + if(intermission == 2) + return; + if (getstati(STAT_HEALTH) <= 0) + return; + + dir = view_forward; + + if(angles_held_status) + { + makevectors(angles_held); + dir = v_forward; + } + + p = view_origin; + + polyline[0] = p; + int idx = 1; + portal_number = 0; + nextdir = dir; + + for (;;) + { + dir = nextdir; + traceline(p, p + 65536 * dir, true, porto); + if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT) + return; + nextdir = dir - 2 * (dir * trace_plane_normal) * trace_plane_normal; // mirror dir at trace_plane_normal + p = trace_endpos; + polyline[idx] = p; + ++idx; + if(idx >= 16) + return; + if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_SLICK || trace_dphitcontents & DPCONTENTS_PLAYERCLIP) + continue; + ++portal_number; + ang = vectoangles2(trace_plane_normal, dir); + ang.x = -ang.x; + makevectors(ang); + if(!CheckWireframeBox(porto, p - 48 * v_right - 48 * v_up + 16 * v_forward, 96 * v_right, 96 * v_up, 96 * v_forward)) + return; + if(portal_number == 1) + { + portal1_idx = idx; + if(portal_number >= 2) + break; + } + } + + while(idx >= 2) + { + p = polyline[idx-2]; + q = polyline[idx-1]; + if(idx == 2) + p = p - view_up * 16; + if(idx-1 >= portal1_idx) + { + Draw_CylindricLine(p, q, 4, "", 1, 0, '0 0 1', 0.5, DRAWFLAG_NORMAL, view_origin); + } + else + { + Draw_CylindricLine(p, q, 4, "", 1, 0, '1 0 0', 0.5, DRAWFLAG_NORMAL, view_origin); + } + --idx; + } + } + + void Porto_Init() + { + porto = spawn(); + porto.classname = "porto"; + porto.draw = Porto_Draw; + porto.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_PLAYERCLIP; + } + + float drawtime; + float avgspeed; + vector GetCurrentFov(float fov) + { + float zoomsensitivity, zoomspeed, zoomfactor, zoomdir; + float velocityzoom, curspeed; + vector v; + + zoomsensitivity = autocvar_cl_zoomsensitivity; + zoomfactor = autocvar_cl_zoomfactor; + if(zoomfactor < 1 || zoomfactor > 16) + zoomfactor = 2.5; + zoomspeed = autocvar_cl_zoomspeed; + if(zoomspeed >= 0) + if(zoomspeed < 0.5 || zoomspeed > 16) + zoomspeed = 3.5; + + zoomdir = button_zoom; + if(hud == HUD_NORMAL) ++ if(switchweapon == activeweapon) + if((activeweapon == WEP_VORTEX && vortex_scope) || (activeweapon == WEP_RIFLE && rifle_scope)) // do NOT use switchweapon here + zoomdir += button_attack2; + if(spectatee_status > 0 || isdemo()) + { + if(spectatorbutton_zoom) + { + if(zoomdir) + zoomdir = 0; + else + zoomdir = 1; + } + // fteqcc failed twice here already, don't optimize this + } + + if(zoomdir) { zoomin_effect = 0; } + + if(camera_active) + { + current_viewzoom = min(1, current_viewzoom + drawframetime); + } + else if(autocvar_cl_spawnzoom && zoomin_effect) + { + float spawnzoomfactor = bound(1, autocvar_cl_spawnzoom_factor, 16); + + current_viewzoom += (autocvar_cl_spawnzoom_speed * (spawnzoomfactor - current_viewzoom) * drawframetime); + current_viewzoom = bound(1 / spawnzoomfactor, current_viewzoom, 1); + if(current_viewzoom == 1) { zoomin_effect = 0; } + } + else + { + if(zoomspeed < 0) // instant zoom + { + if(zoomdir) + current_viewzoom = 1 / zoomfactor; + else + current_viewzoom = 1; + } + else + { + if(zoomdir) + current_viewzoom = 1 / bound(1, 1 / current_viewzoom + drawframetime * zoomspeed * (zoomfactor - 1), zoomfactor); + else + current_viewzoom = bound(1 / zoomfactor, current_viewzoom + drawframetime * zoomspeed * (1 - 1 / zoomfactor), 1); + } + } + + if(almost_equals(current_viewzoom, 1)) + current_zoomfraction = 0; + else if(almost_equals(current_viewzoom, 1/zoomfactor)) + current_zoomfraction = 1; + else + current_zoomfraction = (current_viewzoom - 1) / (1/zoomfactor - 1); + + if(zoomsensitivity < 1) + setsensitivityscale(pow(current_viewzoom, 1 - zoomsensitivity)); + else + setsensitivityscale(1); + + if(autocvar_cl_velocityzoom_enabled && autocvar_cl_velocityzoom_type) // _type = 0 disables velocity zoom too + { + if(intermission) { curspeed = 0; } + else + { + + makevectors(view_angles); + v = pmove_vel; + if(csqcplayer) + v = csqcplayer.velocity; + + switch(autocvar_cl_velocityzoom_type) + { + case 3: curspeed = max(0, v_forward * v); break; + case 2: curspeed = (v_forward * v); break; + case 1: default: curspeed = vlen(v); break; + } + } + + velocityzoom = bound(0, drawframetime / max(0.000000001, autocvar_cl_velocityzoom_time), 1); // speed at which the zoom adapts to player velocity + avgspeed = avgspeed * (1 - velocityzoom) + (curspeed / autocvar_cl_velocityzoom_speed) * velocityzoom; + velocityzoom = exp(float2range11(avgspeed * -autocvar_cl_velocityzoom_factor / 1) * 1); + + //print(ftos(avgspeed), " avgspeed, ", ftos(curspeed), " curspeed, ", ftos(velocityzoom), " return\n"); // for debugging + } + else + velocityzoom = 1; + + float frustumx, frustumy, fovx, fovy; + frustumy = tan(fov * M_PI / 360.0) * 0.75 * current_viewzoom * velocityzoom; + frustumx = frustumy * vid_width / vid_height / vid_pixelheight; + fovx = atan2(frustumx, 1) / M_PI * 360.0; + fovy = atan2(frustumy, 1) / M_PI * 360.0; + + return '1 0 0' * fovx + '0 1 0' * fovy; + } + + vector GetOrthoviewFOV(vector ov_worldmin, vector ov_worldmax, vector ov_mid, vector ov_org) + { + float fovx, fovy; + float width = (ov_worldmax.x - ov_worldmin.x); + float height = (ov_worldmax.y - ov_worldmin.y); + float distance_to_middle_of_world = vlen(ov_mid - ov_org); + fovx = atan2(width/2, distance_to_middle_of_world) / M_PI * 360.0; + fovy = atan2(height/2, distance_to_middle_of_world) / M_PI * 360.0; + return '1 0 0' * fovx + '0 1 0' * fovy; + } + + // this function must match W_SetupShot! + float zoomscript_caught; + + vector wcross_origin; + float wcross_scale_prev, wcross_alpha_prev; + vector wcross_color_prev; + float wcross_scale_goal_prev, wcross_alpha_goal_prev; + vector wcross_color_goal_prev; + float wcross_changedonetime; + + string wcross_name_goal_prev, wcross_name_goal_prev_prev; + float wcross_resolution_goal_prev, wcross_resolution_goal_prev_prev; + float wcross_name_changestarttime, wcross_name_changedonetime; + float wcross_name_alpha_goal_prev, wcross_name_alpha_goal_prev_prev; + + float wcross_ring_prev; + + entity trueaim; + entity trueaim_rifle; + + const float SHOTTYPE_HITTEAM = 1; + const float SHOTTYPE_HITOBSTRUCTION = 2; + const float SHOTTYPE_HITWORLD = 3; + const float SHOTTYPE_HITENEMY = 4; + + void TrueAim_Init() + { + trueaim = spawn(); + trueaim.classname = "trueaim"; + trueaim.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_CORPSE; + trueaim_rifle = spawn(); + trueaim_rifle.classname = "trueaim_rifle"; + trueaim_rifle.dphitcontentsmask = DPCONTENTS_BODY | DPCONTENTS_CORPSE; + } + + float EnemyHitCheck() + { + float t, n; + wcross_origin = project_3d_to_2d(trace_endpos); + wcross_origin.z = 0; + if(trace_ent) + n = trace_ent.entnum; + else + n = trace_networkentity; + if(n < 1) + return SHOTTYPE_HITWORLD; + if(n > maxclients) + return SHOTTYPE_HITWORLD; + t = GetPlayerColor(n - 1); + if(teamplay) + if(t == myteam) + return SHOTTYPE_HITTEAM; + if(t == NUM_SPECTATOR) + return SHOTTYPE_HITWORLD; + return SHOTTYPE_HITENEMY; + } + + float TrueAimCheck() + { + float nudge = 1; // added to traceline target and subtracted from result TOOD(divVerent): do we still need this? Doesn't the engine do this now for us? + vector vecs, trueaimpoint, w_shotorg; + vector mi, ma, dv; + float shottype; + entity ta; + float mv; + + mi = ma = '0 0 0'; + ta = trueaim; + mv = MOVE_NOMONSTERS; + + switch(activeweapon) // WEAPONTODO + { + case WEP_TUBA: // no aim + case WEP_PORTO: // shoots from eye + case WEP_HOOK: // no trueaim + case WEP_MORTAR: // toss curve + return SHOTTYPE_HITWORLD; + case WEP_VORTEX: + case WEP_VAPORIZER: + mv = MOVE_NORMAL; + break; + case WEP_RIFLE: + ta = trueaim_rifle; + mv = MOVE_NORMAL; + if(zoomscript_caught) + { + tracebox(view_origin, '0 0 0', '0 0 0', view_origin + view_forward * MAX_SHOT_DISTANCE, mv, ta); + return EnemyHitCheck(); + } + break; + case WEP_DEVASTATOR: // projectile has a size! + mi = '-3 -3 -3'; + ma = '3 3 3'; + break; + case WEP_FIREBALL: // projectile has a size! + mi = '-16 -16 -16'; + ma = '16 16 16'; + break; + case WEP_SEEKER: // projectile has a size! + mi = '-2 -2 -2'; + ma = '2 2 2'; + break; + case WEP_ELECTRO: // projectile has a size! + mi = '0 0 -3'; + ma = '0 0 -3'; + break; + } + + vector traceorigin = getplayerorigin(player_localentnum-1) + (eZ * getstati(STAT_VIEWHEIGHT)); + + vecs = decompressShotOrigin(getstati(STAT_SHOTORG)); + + traceline(traceorigin, traceorigin + view_forward * MAX_SHOT_DISTANCE, mv, ta); + trueaimpoint = trace_endpos; + + if(vlen(trueaimpoint - traceorigin) < g_trueaim_minrange) + trueaimpoint = traceorigin + view_forward * g_trueaim_minrange; + + if(vecs.x > 0) + vecs.y = -vecs.y; + else + vecs = '0 0 0'; + + dv = view_right * vecs.y + view_up * vecs.z; + w_shotorg = traceorigin + dv; + + // now move the vecs forward as much as requested if possible + tracebox(w_shotorg, mi, ma, w_shotorg + view_forward * (vecs.x + nudge), MOVE_NORMAL, ta); // FIXME this MOVE_NORMAL part will misbehave a little in csqc + w_shotorg = trace_endpos - view_forward * nudge; + + tracebox(w_shotorg, mi, ma, trueaimpoint, MOVE_NORMAL, ta); + shottype = EnemyHitCheck(); + if(shottype != SHOTTYPE_HITWORLD) + return shottype; + + #if 0 + // FIXME WHY DOES THIS NOT WORK FOR THE ROCKET LAUNCHER? + // or rather, I know why, but see no fix + if(vlen(trace_endpos - trueaimpoint) > vlen(ma) + vlen(mi) + 1) + // yes, this is an ugly hack... but it seems good enough to find out whether the test hits the same place as the initial trace + return SHOTTYPE_HITOBSTRUCTION; + #endif + + return SHOTTYPE_HITWORLD; + } + + void CSQC_common_hud(void); + + void PostInit(void); + void CSQC_Demo_Camera(); + float HUD_WouldDrawScoreboard(); + float camera_mode; + const float CAMERA_FREE = 1; + const float CAMERA_CHASE = 2; + float reticle_type; + string reticle_image; + string NextFrameCommand; -void CSQC_SPIDER_HUD(); -void CSQC_RAPTOR_HUD(); + + vector freeze_org, freeze_ang; + entity nightvision_noise, nightvision_noise2; + + const float MAX_TIME_DIFF = 5; + float pickup_crosshair_time, pickup_crosshair_size; + float hitindication_crosshair_size; + float use_vortex_chargepool; + + float myhealth, myhealth_prev; + float myhealth_flash; + + float old_blurradius, old_bluralpha; + float old_sharpen_intensity; + + vector myhealth_gentlergb; + + float contentavgalpha, liquidalpha_prev; + vector liquidcolor_prev; + + float eventchase_current_distance; + float eventchase_running; + float WantEventchase() + { + if(autocvar_cl_orthoview) + return false; + if(intermission) + return true; + if(spectatee_status >= 0) + { ++ if(hud != HUD_NORMAL && (autocvar_cl_eventchase_vehicle || spectatee_status > 0)) ++ return true; + if(autocvar_cl_eventchase_nexball && gametype == MAPINFO_TYPE_NEXBALL && !(WepSet_GetFromStat() & WepSet_FromWeapon(WEP_PORTO))) + return true; + if(autocvar_cl_eventchase_death && (getstati(STAT_HEALTH) <= 0)) + { + if(autocvar_cl_eventchase_death == 2) + { + // don't stop eventchase once it's started (even if velocity changes afterwards) + if(self.velocity == '0 0 0' || eventchase_running) + return true; + } + else return true; + } + } + return false; + } + + vector damage_blurpostprocess, content_blurpostprocess; + + float checkfail[16]; + + float unaccounted_damage = 0; + void UpdateDamage() + { + // accumulate damage with each stat update + static float damage_total_prev = 0; + float damage_total = getstati(STAT_DAMAGE_DEALT_TOTAL); + float unaccounted_damage_new = COMPARE_INCREASING(damage_total, damage_total_prev); + damage_total_prev = damage_total; + + static float damage_dealt_time_prev = 0; + float damage_dealt_time = getstatf(STAT_HIT_TIME); + if (damage_dealt_time != damage_dealt_time_prev) + { + unaccounted_damage += unaccounted_damage_new; + dprint("dmg total: ", ftos(unaccounted_damage), " (+", ftos(unaccounted_damage_new), ")", "\n"); + } + damage_dealt_time_prev = damage_dealt_time; + + // prevent hitsound when switching spectatee + static float spectatee_status_prev = 0; + if (spectatee_status != spectatee_status_prev) + unaccounted_damage = 0; + spectatee_status_prev = spectatee_status; + } + + void UpdateHitsound() + { + // varying sound pitch + + static float hitsound_time_prev = 0; + // HACK: the only way to get the arc to sound consistent with pitch shift is to ignore cl_hitsound_antispam_time + float arc_hack = activeweapon == WEP_ARC && autocvar_cl_hitsound >= 2; + if (arc_hack || COMPARE_INCREASING(time, hitsound_time_prev) > autocvar_cl_hitsound_antispam_time) + { + if (autocvar_cl_hitsound && unaccounted_damage) + { + // customizable gradient function that crosses (0,a), (c,1) and asymptotically approaches b + float a = autocvar_cl_hitsound_max_pitch; + float b = autocvar_cl_hitsound_min_pitch; + float c = autocvar_cl_hitsound_nom_damage; + float x = unaccounted_damage; + float pitch_shift = (b*x*(a-1) + a*c*(1-b)) / (x*(a-1) + c*(1-b)); + + // if sound variation is disabled, set pitch_shift to 1 + if (autocvar_cl_hitsound == 1) + pitch_shift = 1; + + // if pitch shift is reversed, mirror in (max-min)/2 + min + if (autocvar_cl_hitsound == 3) + { + float mirror_value = (a-b)/2 + b; + pitch_shift = mirror_value + (mirror_value - pitch_shift); + } + + dprint("dmg total (dmg): ", ftos(unaccounted_damage), " , pitch shift: ", ftos(pitch_shift), "\n"); + + // todo: avoid very long and very short sounds from wave stretching using different sound files? seems unnecessary + // todo: normalize sound pressure levels? seems unnecessary + + sound7(world, CH_INFO, "misc/hit.wav", VOL_BASE, ATTN_NONE, pitch_shift * 100, 0); + } + unaccounted_damage = 0; + hitsound_time_prev = time; + } + + static float typehit_time_prev = 0; + float typehit_time = getstatf(STAT_TYPEHIT_TIME); + if (COMPARE_INCREASING(typehit_time, typehit_time_prev) > autocvar_cl_hitsound_antispam_time) + { + sound(world, CH_INFO, "misc/typehit.wav", VOL_BASE, ATTN_NONE); + typehit_time_prev = typehit_time; + } + } + ++float last_beam; + void UpdateCrosshair() + { + static float rainbow_last_flicker; + static vector rainbow_prev_color; + entity e = self; + float f, i, j; - vector v; + if(getstati(STAT_FROZEN)) + drawfill('0 0 0', eX * vid_conwidth + eY * vid_conheight, ((getstatf(STAT_REVIVE_PROGRESS)) ? ('0.25 0.90 1' + ('1 0 0' * getstatf(STAT_REVIVE_PROGRESS)) + ('0 1 1' * getstatf(STAT_REVIVE_PROGRESS) * -1)) : '0.25 0.90 1'), autocvar_hud_colorflash_alpha, DRAWFLAG_ADDITIVE); + else if (getstatf(STAT_HEALING_ORB)>time) + drawfill('0 0 0', eX * vid_conwidth + eY * vid_conheight, Nade_Color(NADE_TYPE_HEAL), autocvar_hud_colorflash_alpha*getstatf(STAT_HEALING_ORB_ALPHA), DRAWFLAG_ADDITIVE); + if(!intermission) + if(getstatf(STAT_NADE_TIMER) && autocvar_cl_nade_timer) // give nade top priority, as it's a matter of life and death + { + DrawCircleClippedPic(eX * 0.5 * vid_conwidth + eY * 0.6 * vid_conheight, 0.1 * vid_conheight, "gfx/crosshair_ring.tga", getstatf(STAT_NADE_TIMER), '0.25 0.90 1' + ('1 0 0' * getstatf(STAT_NADE_TIMER)) - ('0 1 1' * getstatf(STAT_NADE_TIMER)), autocvar_hud_colorflash_alpha, DRAWFLAG_ADDITIVE); + drawstring_aspect(eY * 0.64 * vid_conheight, ((autocvar_cl_nade_timer == 2) ? _("Nade timer") : ""), eX * vid_conwidth + eY * 0.025 * vid_conheight, '1 1 1', 1, DRAWFLAG_NORMAL); + } ++ else if(getstatf(STAT_CAPTURE_PROGRESS)) ++ { ++ DrawCircleClippedPic(eX * 0.5 * vid_conwidth + eY * 0.6 * vid_conheight, 0.1 * vid_conheight, "gfx/crosshair_ring.tga", getstatf(STAT_CAPTURE_PROGRESS), '0.25 0.90 1', autocvar_hud_colorflash_alpha, DRAWFLAG_ADDITIVE); ++ drawstring_aspect(eY * 0.64 * vid_conheight, _("Capture progress"), eX * vid_conwidth + eY * 0.025 * vid_conheight, '1 1 1', 1, DRAWFLAG_NORMAL); ++ } + else if(getstatf(STAT_REVIVE_PROGRESS)) + { + DrawCircleClippedPic(eX * 0.5 * vid_conwidth + eY * 0.6 * vid_conheight, 0.1 * vid_conheight, "gfx/crosshair_ring.tga", getstatf(STAT_REVIVE_PROGRESS), '0.25 0.90 1', autocvar_hud_colorflash_alpha, DRAWFLAG_ADDITIVE); + drawstring_aspect(eY * 0.64 * vid_conheight, _("Revival progress"), eX * vid_conwidth + eY * 0.025 * vid_conheight, '1 1 1', 1, DRAWFLAG_NORMAL); + } + + if(autocvar_r_letterbox == 0) + if(autocvar_viewsize < 120) + CSQC_common_hud(); + ++ float tempcamera = (gametype == MAPINFO_TYPE_JAILBREAK && spectatee_status >= 0 && getstati(STAT_ROUNDLOST) && !getstati(STAT_PRISONED)); ++ + // crosshair goes VERY LAST - if(!scoreboard_active && !camera_active && intermission != 2 && spectatee_status != -1 && hud == HUD_NORMAL) ++ if(!scoreboard_active && !camera_active && intermission != 2 && ++ spectatee_status != -1 && hud == HUD_NORMAL && autocvar_chase_active >= 0 && ++ !HUD_QuickMenu_IsOpened() && !tempcamera && !HUD_MinigameMenu_IsOpened() ) + { + if (!autocvar_crosshair_enabled) // main toggle for crosshair rendering + return; + + string wcross_style; + float wcross_alpha, wcross_resolution; + wcross_style = autocvar_crosshair; + if (wcross_style == "0") + return; + wcross_resolution = autocvar_crosshair_size; + if (wcross_resolution == 0) + return; + wcross_alpha = autocvar_crosshair_alpha; + if (wcross_alpha == 0) + return; + + // TrueAim check + float shottype; + + // wcross_origin = '0.5 0 0' * vid_conwidth + '0 0.5 0' * vid_conheight; + wcross_origin = project_3d_to_2d(view_origin + MAX_SHOT_DISTANCE * view_forward); + wcross_origin.z = 0; + if(autocvar_crosshair_hittest) + { + vector wcross_oldorigin; + wcross_oldorigin = wcross_origin; + shottype = TrueAimCheck(); + if(shottype == SHOTTYPE_HITWORLD) + { - v = wcross_origin - wcross_oldorigin; ++ vector v = wcross_origin - wcross_oldorigin; + v.x /= vid_conwidth; + v.y /= vid_conheight; + if(vlen(v) > 0.01) + shottype = SHOTTYPE_HITOBSTRUCTION; + } + if(!autocvar_crosshair_hittest_showimpact) + wcross_origin = wcross_oldorigin; + } + else + shottype = SHOTTYPE_HITWORLD; + + vector wcross_color = '0 0 0', wcross_size = '0 0 0'; + string wcross_name = ""; + float wcross_scale, wcross_blur; + + if(autocvar_crosshair_per_weapon || (autocvar_crosshair_color_special == 1)) + { + e = get_weaponinfo(switchingweapon); + if(e) + { + if(autocvar_crosshair_per_weapon) + { + // WEAPONTODO: access these through some general settings (with non-balance config settings) + //wcross_resolution *= cvar(strcat("crosshair_", wcross_wep, "_size")); + //if (wcross_resolution == 0) + //return; + + //wcross_style = cvar_string(strcat("crosshair_", wcross_wep)); + wcross_resolution *= e.w_crosshair_size; + wcross_name = e.w_crosshair; + } + } + } + + if(wcross_name == "") + wcross_name = strcat("gfx/crosshair", wcross_style); + + // MAIN CROSSHAIR COLOR DECISION + switch(autocvar_crosshair_color_special) + { + case 1: // crosshair_color_per_weapon + { + if(e) + { + wcross_color = e.wpcolor; + break; + } + else { goto normalcolor; } + } + + case 2: // crosshair_color_by_health + { + float x = getstati(STAT_HEALTH); + + //x = red + //y = green + //z = blue + + wcross_color.z = 0; + + if(x > 200) + { + wcross_color.x = 0; + wcross_color.y = 1; + } + else if(x > 150) + { + wcross_color.x = 0.4 - (x-150)*0.02 * 0.4; + wcross_color.y = 0.9 + (x-150)*0.02 * 0.1; + } + else if(x > 100) + { + wcross_color.x = 1 - (x-100)*0.02 * 0.6; + wcross_color.y = 1 - (x-100)*0.02 * 0.1; + wcross_color.z = 1 - (x-100)*0.02; + } + else if(x > 50) + { + wcross_color.x = 1; + wcross_color.y = 1; + wcross_color.z = 0.2 + (x-50)*0.02 * 0.8; + } + else if(x > 20) + { + wcross_color.x = 1; + wcross_color.y = (x-20)*90/27/100; + wcross_color.z = (x-20)*90/27/100 * 0.2; + } + else + { + wcross_color.x = 1; + wcross_color.y = 0; + } + break; + } + + case 3: // crosshair_color_rainbow + { + if(time >= rainbow_last_flicker) + { + rainbow_prev_color = randomvec() * autocvar_crosshair_color_special_rainbow_brightness; + rainbow_last_flicker = time + autocvar_crosshair_color_special_rainbow_delay; + } + wcross_color = rainbow_prev_color; + break; + } + :normalcolor + default: { wcross_color = stov(autocvar_crosshair_color); break; } + } + + if(autocvar_crosshair_effect_scalefade) + { + wcross_scale = wcross_resolution; + wcross_resolution = 1; + } + else + { + wcross_scale = 1; + } + + if(autocvar_crosshair_pickup) + { + float stat_pickup_time = getstatf(STAT_LAST_PICKUP); + + if(pickup_crosshair_time < stat_pickup_time) + { + if(time - stat_pickup_time < MAX_TIME_DIFF) // don't trigger the animation if it's too old + pickup_crosshair_size = 1; + + pickup_crosshair_time = stat_pickup_time; + } + + if(pickup_crosshair_size > 0) + pickup_crosshair_size -= autocvar_crosshair_pickup_speed * frametime; + else + pickup_crosshair_size = 0; + + wcross_scale += sin(pickup_crosshair_size) * autocvar_crosshair_pickup; + } + + // todo: make crosshair hit indication dependent on damage dealt + if(autocvar_crosshair_hitindication) + { + vector hitindication_color = ((autocvar_crosshair_color_special == 1) ? stov(autocvar_crosshair_hitindication_per_weapon_color) : stov(autocvar_crosshair_hitindication_color)); + + if(unaccounted_damage) + { + hitindication_crosshair_size = 1; + } + + if(hitindication_crosshair_size > 0) + hitindication_crosshair_size -= autocvar_crosshair_hitindication_speed * frametime; + else + hitindication_crosshair_size = 0; + + wcross_scale += sin(hitindication_crosshair_size) * autocvar_crosshair_hitindication; + wcross_color.x += sin(hitindication_crosshair_size) * hitindication_color.x; + wcross_color.y += sin(hitindication_crosshair_size) * hitindication_color.y; + wcross_color.z += sin(hitindication_crosshair_size) * hitindication_color.z; + } + + if(shottype == SHOTTYPE_HITENEMY) + wcross_scale *= autocvar_crosshair_hittest; // is not queried if hittest is 0 + if(shottype == SHOTTYPE_HITTEAM) + wcross_scale /= autocvar_crosshair_hittest; // is not queried if hittest is 0 + + f = fabs(autocvar_crosshair_effect_time); + if(wcross_scale != wcross_scale_goal_prev || wcross_alpha != wcross_alpha_goal_prev || wcross_color != wcross_color_goal_prev) + { + wcross_changedonetime = time + f; + } + if(wcross_name != wcross_name_goal_prev || wcross_resolution != wcross_resolution_goal_prev) + { + wcross_name_changestarttime = time; + wcross_name_changedonetime = time + f; + if(wcross_name_goal_prev_prev) + strunzone(wcross_name_goal_prev_prev); + wcross_name_goal_prev_prev = wcross_name_goal_prev; + wcross_name_goal_prev = strzone(wcross_name); + wcross_name_alpha_goal_prev_prev = wcross_name_alpha_goal_prev; + wcross_resolution_goal_prev_prev = wcross_resolution_goal_prev; + wcross_resolution_goal_prev = wcross_resolution; + } + + wcross_scale_goal_prev = wcross_scale; + wcross_alpha_goal_prev = wcross_alpha; + wcross_color_goal_prev = wcross_color; + + if(shottype == SHOTTYPE_HITTEAM || (shottype == SHOTTYPE_HITOBSTRUCTION && autocvar_crosshair_hittest_blur && !autocvar_chase_active)) + { + wcross_blur = 1; + wcross_alpha *= 0.75; + } + else + wcross_blur = 0; + // *_prev is at time-frametime + // * is at wcross_changedonetime+f + // what do we have at time? + if(time < wcross_changedonetime) + { + f = frametime / (wcross_changedonetime - time + frametime); + wcross_scale = f * wcross_scale + (1 - f) * wcross_scale_prev; + wcross_alpha = f * wcross_alpha + (1 - f) * wcross_alpha_prev; + wcross_color = f * wcross_color + (1 - f) * wcross_color_prev; + } + + wcross_scale_prev = wcross_scale; + wcross_alpha_prev = wcross_alpha; + wcross_color_prev = wcross_color; + ++ if(!vaporizer_delay) ++ vaporizer_delay = 0.75; ++ ++ entity me = playerslots[player_localnum]; ++ ++ if(activeweapon == WEP_VAPORIZER) ++ if(time >= getstatf(STAT_GAMESTARTTIME)) ++ if(time >= getstatf(STAT_ROUNDSTARTTIME)) ++ if(!autocvar_chase_active) ++ if(!getstatf(STAT_FROZEN)) ++ if(!spectatee_status) ++ if(button_attack) ++ if(autocvar_cl_vaporizerbeam || (me.ping >= 200 && autocvar_cl_vaporizerbeam != -1)) ++ if(time >= last_beam) ++ { ++ string s; ++ float hit = 0; ++ vector v = '0 0 1' * getstati(STAT_VIEWHEIGHT); ++ ++ s = strcat("TE_TEI_G3", Static_Team_ColorName_Upper(myteam)); ++ ++ WarpZone_TraceLine(view_origin, view_origin + v + view_forward * MAX_SHOT_DISTANCE, MOVE_NORMAL, world); ++ ++ if(trace_fraction < 1 && !trace_ent.entnum) ++ hit = 0; ++ else ++ hit = 1; ++ ++ if(hit) ++ s = strcat(s, "_HIT"); ++ ++ WarpZone_TrailParticles(self, particleeffectnum(s), view_origin, view_origin + v + view_forward * MAX_SHOT_DISTANCE); ++ ++ //sound(self, CH_SHOTS, "weapons/minstanexfire.wav", VOL_BASE, ATTEN_LARGE); ++ ++ if(!(trace_dphitq3surfaceflags & (Q3SURFACEFLAG_SKY | Q3SURFACEFLAG_NOIMPACT))) ++ if(autocvar_cl_particles_newvortexbeam && (getstati(STAT_ALLOW_OLDVORTEXBEAM) || isdemo())) ++ pointparticles(particleeffectnum("nex_impact_new"), trace_endpos, '0 0 0', 1); ++ else ++ pointparticles(particleeffectnum("nex_impact"), trace_endpos, '0 0 0', 1); ++ ++ last_beam = time + vaporizer_delay; ++ } ++ + wcross_scale *= 1 - autocvar__menu_alpha; + wcross_alpha *= 1 - autocvar__menu_alpha; + wcross_size = draw_getimagesize(wcross_name) * wcross_scale; + + if(wcross_scale >= 0.001 && wcross_alpha >= 0.001) + { + // crosshair rings for weapon stats + if (autocvar_crosshair_ring || autocvar_crosshair_ring_reload) + { + // declarations and stats + float ring_value = 0, ring_scale = 0, ring_alpha = 0, ring_inner_value = 0, ring_inner_alpha = 0; + string ring_image = string_null, ring_inner_image = string_null; + vector ring_rgb = '0 0 0', ring_inner_rgb = '0 0 0'; + + ring_scale = autocvar_crosshair_ring_size; + + float weapon_clipload, weapon_clipsize; + weapon_clipload = getstati(STAT_WEAPON_CLIPLOAD); + weapon_clipsize = getstati(STAT_WEAPON_CLIPSIZE); + + float ok_ammo_charge, ok_ammo_chargepool; + ok_ammo_charge = getstatf(STAT_OK_AMMO_CHARGE); + ok_ammo_chargepool = getstatf(STAT_OK_AMMO_CHARGEPOOL); + + float vortex_charge, vortex_chargepool; + vortex_charge = getstatf(STAT_VORTEX_CHARGE); + vortex_chargepool = getstatf(STAT_VORTEX_CHARGEPOOL); + + float arc_heat = getstatf(STAT_ARC_HEAT); + + if(vortex_charge_movingavg == 0) // this should only happen if we have just loaded up the game + vortex_charge_movingavg = vortex_charge; + + + // handle the values + if (autocvar_crosshair_ring && activeweapon == WEP_VORTEX && vortex_charge && autocvar_crosshair_ring_vortex) // ring around crosshair representing velocity-dependent damage for the vortex + { + if (vortex_chargepool || use_vortex_chargepool) { + use_vortex_chargepool = 1; + ring_inner_value = vortex_chargepool; + } else { + vortex_charge_movingavg = (1 - autocvar_crosshair_ring_vortex_currentcharge_movingavg_rate) * vortex_charge_movingavg + autocvar_crosshair_ring_vortex_currentcharge_movingavg_rate * vortex_charge; + ring_inner_value = bound(0, autocvar_crosshair_ring_vortex_currentcharge_scale * (vortex_charge - vortex_charge_movingavg), 1); + } + + ring_inner_alpha = autocvar_crosshair_ring_vortex_inner_alpha; + ring_inner_rgb = eX * autocvar_crosshair_ring_vortex_inner_color_red + eY * autocvar_crosshair_ring_vortex_inner_color_green + eZ * autocvar_crosshair_ring_vortex_inner_color_blue; + ring_inner_image = "gfx/crosshair_ring_inner.tga"; + + // draw the outer ring to show the current charge of the weapon + ring_value = vortex_charge; + ring_alpha = autocvar_crosshair_ring_vortex_alpha; + ring_rgb = wcross_color; + ring_image = "gfx/crosshair_ring_nexgun.tga"; + } + else if (autocvar_crosshair_ring && activeweapon == WEP_MINE_LAYER && minelayer_maxmines && autocvar_crosshair_ring_minelayer) + { + ring_value = bound(0, getstati(STAT_LAYED_MINES) / minelayer_maxmines, 1); // if you later need to use the count of bullets in another place, then add a float for it. For now, no need to. + ring_alpha = autocvar_crosshair_ring_minelayer_alpha; + ring_rgb = wcross_color; + ring_image = "gfx/crosshair_ring.tga"; + } + else if (activeweapon == WEP_HAGAR && getstati(STAT_HAGAR_LOAD) && autocvar_crosshair_ring_hagar) + { + ring_value = bound(0, getstati(STAT_HAGAR_LOAD) / hagar_maxrockets, 1); + ring_alpha = autocvar_crosshair_ring_hagar_alpha; + ring_rgb = wcross_color; + ring_image = "gfx/crosshair_ring.tga"; + } + else if (ok_ammo_charge) + { + ring_value = ok_ammo_chargepool; + ring_alpha = autocvar_crosshair_ring_reload_alpha; + ring_rgb = wcross_color; + ring_image = "gfx/crosshair_ring.tga"; + } + else if(autocvar_crosshair_ring_reload && weapon_clipsize) // forces there to be only an ammo ring + { + ring_value = bound(0, weapon_clipload / weapon_clipsize, 1); + ring_scale = autocvar_crosshair_ring_reload_size; + ring_alpha = autocvar_crosshair_ring_reload_alpha; + ring_rgb = wcross_color; + + // Note: This is to stop Taoki from complaining that the image doesn't match all potential balances. + // if a new image for another weapon is added, add the code (and its respective file/value) here + if ((activeweapon == WEP_RIFLE) && (weapon_clipsize == 80)) + ring_image = "gfx/crosshair_ring_rifle.tga"; + else + ring_image = "gfx/crosshair_ring.tga"; + } + else if ( autocvar_crosshair_ring && autocvar_crosshair_ring_arc && arc_heat && activeweapon == WEP_ARC ) + { + ring_value = arc_heat; + ring_alpha = (1-arc_heat)*autocvar_crosshair_ring_arc_cold_alpha + + arc_heat*autocvar_crosshair_ring_arc_hot_alpha; + ring_rgb = (1-arc_heat)*wcross_color + arc_heat*autocvar_crosshair_ring_arc_hot_color; + ring_image = "gfx/crosshair_ring.tga"; + } + + // if in weapon switch animation, fade ring out/in + if(autocvar_crosshair_effect_time > 0) + { + f = (time - wcross_name_changestarttime) / autocvar_crosshair_effect_time; + if (f >= 1) + { + wcross_ring_prev = ((ring_image) ? true : false); + } + + if(wcross_ring_prev) + { + if(f < 1) + ring_alpha *= fabs(1 - bound(0, f, 1)); + } + else + { + if(f < 1) + ring_alpha *= bound(0, f, 1); + } + } + + if (autocvar_crosshair_ring_inner && ring_inner_value) // lets draw a ring inside a ring so you can ring while you ring + DrawCircleClippedPic(wcross_origin, wcross_size.x * ring_scale, ring_inner_image, ring_inner_value, ring_inner_rgb, wcross_alpha * ring_inner_alpha, DRAWFLAG_ADDITIVE); + + if (ring_value) + DrawCircleClippedPic(wcross_origin, wcross_size.x * ring_scale, ring_image, ring_value, ring_rgb, wcross_alpha * ring_alpha, DRAWFLAG_ADDITIVE); + } + + #define CROSSHAIR_DO_BLUR(M,sz,wcross_name,wcross_alpha) \ + do \ + { \ + if(wcross_blur > 0) \ + { \ + for(i = -2; i <= 2; ++i) \ + for(j = -2; j <= 2; ++j) \ + M(i,j,sz,wcross_name,wcross_alpha*0.04); \ + } \ + else \ + { \ + M(0,0,sz,wcross_name,wcross_alpha); \ + } \ + } \ + while(0) + + #define CROSSHAIR_DRAW_SINGLE(i,j,sz,wcross_name,wcross_alpha) \ + drawpic(wcross_origin - ('0.5 0 0' * (sz * wcross_size.x + i * wcross_blur) + '0 0.5 0' * (sz * wcross_size.y + j * wcross_blur)), wcross_name, sz * wcross_size, wcross_color, wcross_alpha, DRAWFLAG_NORMAL) + + #define CROSSHAIR_DRAW(sz,wcross_name,wcross_alpha) \ + CROSSHAIR_DO_BLUR(CROSSHAIR_DRAW_SINGLE,sz,wcross_name,wcross_alpha) + + if(time < wcross_name_changedonetime && wcross_name != wcross_name_goal_prev_prev && wcross_name_goal_prev_prev) + { + f = (wcross_name_changedonetime - time) / (wcross_name_changedonetime - wcross_name_changestarttime); + wcross_size = draw_getimagesize(wcross_name_goal_prev_prev) * wcross_scale; + CROSSHAIR_DRAW(wcross_resolution_goal_prev_prev, wcross_name_goal_prev_prev, wcross_alpha * f * wcross_name_alpha_goal_prev_prev); + f = 1 - f; + } + else + { + f = 1; + } + wcross_name_alpha_goal_prev = f; + + wcross_size = draw_getimagesize(wcross_name) * wcross_scale; + CROSSHAIR_DRAW(wcross_resolution, wcross_name, wcross_alpha * f); + + if(autocvar_crosshair_dot) + { + vector wcross_color_old; + wcross_color_old = wcross_color; + + if((autocvar_crosshair_dot_color_custom) && (autocvar_crosshair_dot_color != "0")) + wcross_color = stov(autocvar_crosshair_dot_color); + + CROSSHAIR_DRAW(wcross_resolution * autocvar_crosshair_dot_size, "gfx/crosshairdot.tga", f * autocvar_crosshair_dot_alpha); + // FIXME why don't we use wcross_alpha here?cl_notice_run(); + wcross_color = wcross_color_old; + } + } + } + else + { + wcross_scale_prev = 0; + wcross_alpha_prev = 0; + wcross_scale_goal_prev = 0; + wcross_alpha_goal_prev = 0; + wcross_changedonetime = 0; + if(wcross_name_goal_prev) + strunzone(wcross_name_goal_prev); + wcross_name_goal_prev = string_null; + if(wcross_name_goal_prev_prev) + strunzone(wcross_name_goal_prev_prev); + wcross_name_goal_prev_prev = string_null; + wcross_name_changestarttime = 0; + wcross_name_changedonetime = 0; + wcross_name_alpha_goal_prev = 0; + wcross_name_alpha_goal_prev_prev = 0; + wcross_resolution_goal_prev = 0; + wcross_resolution_goal_prev_prev = 0; + } + } + ++float fps_start; ++int fps_frames; ++ ++void FPSCounter_Update() ++{ ++ if(time - fps_start >= 1) ++ { ++ cl_fps = fps_frames; ++ fps_frames = 0; ++ fps_start = time; ++ } ++ ++ fps_frames++; ++} ++ + const int BUTTON_3 = 4; + const int BUTTON_4 = 8; + float cl_notice_run(); + float prev_myteam; ++float oldfov; + void CSQC_UpdateView(float w, float h) + { + entity e; + float fov; + float f; + int i; + vector vf_size, vf_min; - float a; ++ float pink_noise; + + execute_next_frame(); + + ++framecount; + + hud = getstati(STAT_HUD); + + if(autocvar__hud_showbinds_reload) // menu can set this one + { + db_close(binddb); + binddb = db_create(); + cvar_set("_hud_showbinds_reload", "0"); + } + + if(checkextension("DP_CSQC_MINFPS_QUALITY")) + view_quality = getproperty(VF_MINFPS_QUALITY); + else + view_quality = 1; + ++ button_attack = (input_buttons & 1); + button_attack2 = (input_buttons & BUTTON_3); + button_zoom = (input_buttons & BUTTON_4); + + #define CHECKFAIL_ASSERT(flag,func,parm,val) do { \ + float checkfailv = (func)(parm); \ + if (checkfailv != (val)) { \ + if (!checkfail[(flag)]) \ + localcmd(sprintf("\ncmd checkfail %s %s %d %d\n", #func, parm, val, checkfailv)); \ + checkfail[(flag)] = 1; \ + } \ + } while(0) + CHECKFAIL_ASSERT(0, cvar_type, "\{100}\{105}\{118}\{48}\{95}\{101}\{118}\{97}\{100}\{101}", 0); + CHECKFAIL_ASSERT(1, cvar_type, "\{97}\{97}\{95}\{101}\{110}\{97}\{98}\{108}\{101}", 0); + CHECKFAIL_ASSERT(2, cvar, "\{114}\{95}\{115}\{104}\{111}\{119}\{100}\{105}\{115}\{97}\{98}\{108}\{101}\{100}\{101}\{112}\{116}\{104}\{116}\{101}\{115}\{116}", 0); + CHECKFAIL_ASSERT(3, cvar, "\{114}\{95}\{115}\{104}\{111}\{119}\{111}\{118}\{101}\{114}\{100}\{114}\{97}\{119}", 0); + CHECKFAIL_ASSERT(4, cvar, "\{114}\{95}\{115}\{104}\{111}\{119}\{108}\{105}\{103}\{104}\{116}", 0); + CHECKFAIL_ASSERT(5, cvar, "\{114}\{95}\{115}\{104}\{111}\{119}\{115}\{104}\{97}\{100}\{111}\{119}\{118}\{111}\{108}\{117}\{109}\{101}\{115}", 0); + CHECKFAIL_ASSERT(6, cvar, "\{114}\{95}\{115}\{104}\{111}\{119}\{111}\{118}\{101}\{114}\{100}\{114}\{97}\{119}", 0); + + vf_size = getpropertyvec(VF_SIZE); + vf_min = getpropertyvec(VF_MIN); + vid_width = vf_size.x; + vid_height = vf_size.y; + + vector reticle_pos = '0 0 0', reticle_size = '0 0 0'; + vector splash_pos = '0 0 0', splash_size = '0 0 0'; + + WaypointSprite_Load(); + + CSQCPlayer_SetCamera(); + + myteam = GetPlayerColor(player_localentnum - 1); + + if(myteam != prev_myteam) + { + myteamcolors = colormapPaletteColor(myteam, 1); + for(i = 0; i < HUD_PANEL_NUM; ++i) + hud_panel[i].update_time = time; + prev_myteam = myteam; + } + + ticrate = getstatf(STAT_MOVEVARS_TICRATE) * getstatf(STAT_MOVEVARS_TIMESCALE); + + float is_dead = (getstati(STAT_HEALTH) <= 0); + + // FIXME do we need this hack? + if(isdemo()) + { + // in demos, input_buttons do not work + button_zoom = (autocvar__togglezoom == "-"); + } + else if(button_zoom + && autocvar_cl_unpress_zoom_on_death + && (spectatee_status >= 0) + && (is_dead || intermission)) + { + // no zoom while dead or in intermission please + localcmd("-zoom\n"); + button_zoom = false; + } + + // event chase camera + if(autocvar_chase_active <= 0) // greater than 0 means it's enabled manually, and this code is skipped + { - if(WantEventchase()) ++ float ons_roundlost = (gametype == MAPINFO_TYPE_ONSLAUGHT && getstati(STAT_ROUNDLOST)); ++ float vehicle_chase = (hud != HUD_NORMAL && (autocvar_cl_eventchase_vehicle || spectatee_status > 0)); ++ entity gen = world; ++ ++ if(ons_roundlost) ++ { ++ entity e; ++ for(e = world; (e = find(e, classname, "onslaught_generator")); ) ++ { ++ if(e.health <= 0) ++ { ++ gen = e; ++ break; ++ } ++ } ++ if(!gen) ++ ons_roundlost = false; // don't enforce the 3rd person camera if there is no dead generator to show ++ } ++ if(WantEventchase() || (!autocvar_cl_orthoview && ons_roundlost)) + { + eventchase_running = true; + + // make special vector since we can't use view_origin (It is one frame old as of this code, it gets set later with the results this code makes.) + vector current_view_origin = (csqcplayer ? csqcplayer.origin : pmove_org); ++ if(ons_roundlost) { current_view_origin = gen.origin; } + + // detect maximum viewoffset and use it - if(autocvar_cl_eventchase_viewoffset) ++ vector view_offset = autocvar_cl_eventchase_viewoffset; ++ if(vehicle_chase && autocvar_cl_eventchase_vehicle_viewoffset) { view_offset = autocvar_cl_eventchase_vehicle_viewoffset; } ++ if(ons_roundlost) { view_offset = autocvar_cl_eventchase_generator_viewoffset; } ++ ++ if(view_offset) + { - WarpZone_TraceLine(current_view_origin, current_view_origin + autocvar_cl_eventchase_viewoffset + ('0 0 1' * autocvar_cl_eventchase_maxs.z), MOVE_WORLDONLY, self); - if(trace_fraction == 1) { current_view_origin += autocvar_cl_eventchase_viewoffset; } ++ WarpZone_TraceLine(current_view_origin, current_view_origin + view_offset + ('0 0 1' * autocvar_cl_eventchase_maxs.z), MOVE_WORLDONLY, self); ++ if(trace_fraction == 1) { current_view_origin += view_offset; } + else { current_view_origin.z += max(0, (trace_endpos.z - current_view_origin.z) - autocvar_cl_eventchase_maxs.z); } ++ + } + + // We must enable chase_active to get a third person view (weapon viewmodel hidden and own player model showing). + // Ideally, there should be another way to enable third person cameras, such as through setproperty() + // -1 enables chase_active while marking it as set by this code, and not by the user (which would be 1) + if(!autocvar_chase_active) { cvar_set("chase_active", "-1"); } + + // make the camera smooth back - if(autocvar_cl_eventchase_speed && eventchase_current_distance < autocvar_cl_eventchase_distance) - eventchase_current_distance += autocvar_cl_eventchase_speed * (autocvar_cl_eventchase_distance - eventchase_current_distance) * frametime; // slow down the further we get - else if(eventchase_current_distance != autocvar_cl_eventchase_distance) - eventchase_current_distance = autocvar_cl_eventchase_distance; ++ float chase_distance = autocvar_cl_eventchase_distance; ++ if(vehicle_chase && autocvar_cl_eventchase_vehicle_distance) { chase_distance = autocvar_cl_eventchase_vehicle_distance; } ++ if(ons_roundlost) { chase_distance = autocvar_cl_eventchase_generator_distance; } ++ ++ if(autocvar_cl_eventchase_speed && eventchase_current_distance < chase_distance) ++ eventchase_current_distance += autocvar_cl_eventchase_speed * (chase_distance - eventchase_current_distance) * frametime; // slow down the further we get ++ else if(eventchase_current_distance != chase_distance) ++ eventchase_current_distance = chase_distance; + + makevectors(view_angles); + + vector eventchase_target_origin = (current_view_origin - (v_forward * eventchase_current_distance)); + WarpZone_TraceBox(current_view_origin, autocvar_cl_eventchase_mins, autocvar_cl_eventchase_maxs, eventchase_target_origin, MOVE_WORLDONLY, self); + + // If the boxtrace fails, revert back to line tracing. + if(trace_startsolid) + { + eventchase_target_origin = (current_view_origin - (v_forward * eventchase_current_distance)); + WarpZone_TraceLine(current_view_origin, eventchase_target_origin, MOVE_WORLDONLY, self); + setproperty(VF_ORIGIN, (trace_endpos - (v_forward * autocvar_cl_eventchase_mins.z))); + } + else { setproperty(VF_ORIGIN, trace_endpos); } + + setproperty(VF_ANGLES, WarpZone_TransformVAngles(WarpZone_trace_transform, view_angles)); + } + else if(autocvar_chase_active < 0) // time to disable chase_active if it was set by this code + { + eventchase_running = false; + cvar_set("chase_active", "0"); + eventchase_current_distance = 0; // start from 0 next time + } + } + // workaround for camera stuck between player's legs when using chase_active 1 + // because the engine stops updating the chase_active camera when the game ends + else if(intermission) + { + cvar_settemp("chase_active", "-1"); + eventchase_current_distance = 0; + } + + // do lockview after event chase camera so that it still applies whenever necessary. - if(autocvar_cl_lockview || (!autocvar_hud_cursormode && (autocvar__hud_configure && spectatee_status <= 0 || intermission > 1))) ++ if(autocvar_cl_lockview || (!autocvar_hud_cursormode && (autocvar__hud_configure && spectatee_status <= 0 || intermission > 1 || HUD_QuickMenu_IsOpened()))) + { + setproperty(VF_ORIGIN, freeze_org); + setproperty(VF_ANGLES, freeze_ang); + } + else + { + freeze_org = getpropertyvec(VF_ORIGIN); + freeze_ang = getpropertyvec(VF_ANGLES); + } + + WarpZone_FixView(); + //WarpZone_FixPMove(); + + vector ov_org = '0 0 0'; + vector ov_mid = '0 0 0'; + vector ov_worldmin = '0 0 0'; + vector ov_worldmax = '0 0 0'; + if(autocvar_cl_orthoview) + { + ov_worldmin = mi_picmin; + ov_worldmax = mi_picmax; + + float ov_width = (ov_worldmax.x - ov_worldmin.x); + float ov_height = (ov_worldmax.y - ov_worldmin.y); + float ov_distance = (max(vid_width, vid_height) * max(ov_width, ov_height)); + + ov_mid = ((ov_worldmax + ov_worldmin) * 0.5); + ov_org = vec3(ov_mid.x, ov_mid.y, (ov_mid.z + ov_distance)); + + float ov_nearest = vlen(ov_org - vec3( + bound(ov_worldmin.x, ov_org.x, ov_worldmax.x), + bound(ov_worldmin.y, ov_org.y, ov_worldmax.y), + bound(ov_worldmin.z, ov_org.z, ov_worldmax.z) + )); + + float ov_furthest = 0; + float dist = 0; + + if((dist = vlen(vec3(ov_worldmin.x, ov_worldmin.y, ov_worldmin.z) - ov_org)) > ov_furthest) { ov_furthest = dist; } + if((dist = vlen(vec3(ov_worldmax.x, ov_worldmin.y, ov_worldmin.z) - ov_org)) > ov_furthest) { ov_furthest = dist; } + if((dist = vlen(vec3(ov_worldmin.x, ov_worldmax.y, ov_worldmin.z) - ov_org)) > ov_furthest) { ov_furthest = dist; } + if((dist = vlen(vec3(ov_worldmin.x, ov_worldmin.y, ov_worldmax.z) - ov_org)) > ov_furthest) { ov_furthest = dist; } + if((dist = vlen(vec3(ov_worldmax.x, ov_worldmax.y, ov_worldmin.z) - ov_org)) > ov_furthest) { ov_furthest = dist; } + if((dist = vlen(vec3(ov_worldmin.x, ov_worldmax.y, ov_worldmax.z) - ov_org)) > ov_furthest) { ov_furthest = dist; } + if((dist = vlen(vec3(ov_worldmax.x, ov_worldmin.y, ov_worldmax.z) - ov_org)) > ov_furthest) { ov_furthest = dist; } + if((dist = vlen(vec3(ov_worldmax.x, ov_worldmax.y, ov_worldmax.z) - ov_org)) > ov_furthest) { ov_furthest = dist; } + + cvar_settemp("r_nearclip", ftos(ov_nearest)); + cvar_settemp("r_farclip_base", ftos(ov_furthest)); + cvar_settemp("r_farclip_world", "0"); + cvar_settemp("r_novis", "1"); + cvar_settemp("r_useportalculling", "0"); + cvar_settemp("r_useinfinitefarclip", "0"); + + setproperty(VF_ORIGIN, ov_org); + setproperty(VF_ANGLES, '90 0 0'); + + #if 0 + printf("OrthoView: org = %s, angles = %s, distance = %f, nearest = %f, furthest = %f\n", + vtos(ov_org), + vtos(getpropertyvec(VF_ANGLES)), + ov_distance, + ov_nearest, + ov_furthest); + #endif + } + + // Render the Scene + view_origin = getpropertyvec(VF_ORIGIN); + view_angles = getpropertyvec(VF_ANGLES); + makevectors(view_angles); + view_forward = v_forward; + view_right = v_right; + view_up = v_up; + + #ifdef BLURTEST + if(time > blurtest_time0 && time < blurtest_time1) + { + float r, t; + + t = (time - blurtest_time0) / (blurtest_time1 - blurtest_time0); + r = t * blurtest_radius; + f = 1 / pow(t, blurtest_power) - 1; + + cvar_set("r_glsl_postprocess", "1"); + cvar_set("r_glsl_postprocess_uservec1", strcat(ftos(r), " ", ftos(f), " 0 0")); + } + else + { + cvar_set("r_glsl_postprocess", "0"); + cvar_set("r_glsl_postprocess_uservec1", "0 0 0 0"); + } + #endif + + TargetMusic_Advance(); + Fog_Force(); + + if(drawtime == 0) + drawframetime = 0.01666667; // when we don't know fps yet, we assume 60fps + else + drawframetime = bound(0.000001, time - drawtime, 1); + drawtime = time; + + // watch for gametype changes here... + // in ParseStuffCMD the cmd isn't executed yet :/ + // might even be better to add the gametype to TE_CSQC_INIT...? + if(!postinit) + PostInit(); + + if(intermission && !isdemo() && !(calledhooks & HOOK_END)) + { + if(calledhooks & HOOK_START) + { + localcmd("\ncl_hook_gameend\n"); + calledhooks |= HOOK_END; + } + } + + Announcer(); + + fov = autocvar_fov; ++ ++ if(fov != oldfov) ++ { ++ localcmd(strcat("\ncmd report fov ", ftos(fov), "\n")); ++ oldfov = fov; ++ } ++ + if(fov <= 59.5) + { + if(!zoomscript_caught) + { + localcmd("+button9\n"); + zoomscript_caught = 1; + } + } + else + { + if(zoomscript_caught) + { + localcmd("-button9\n"); + zoomscript_caught = 0; + } + } + + ColorTranslateMode = autocvar_cl_stripcolorcodes; + + // next WANTED weapon (for HUD) + switchweapon = getstati(STAT_SWITCHWEAPON); + + // currently switching-to weapon (for crosshair) + switchingweapon = getstati(STAT_SWITCHINGWEAPON); + + // actually active weapon (for zoom) + activeweapon = getstati(STAT_ACTIVEWEAPON); + + f = (serverflags & SERVERFLAG_TEAMPLAY); + if(f != teamplay) + { + teamplay = f; + HUD_InitScores(); + } + + if(last_switchweapon != switchweapon) + { + weapontime = time; + last_switchweapon = switchweapon; + if(button_zoom && autocvar_cl_unpress_zoom_on_weapon_switch) + { + localcmd("-zoom\n"); + button_zoom = false; + } + if(autocvar_cl_unpress_attack_on_weapon_switch) + { + localcmd("-fire\n"); + localcmd("-fire2\n"); + button_attack2 = false; + } + } + if(last_activeweapon != activeweapon) + { + last_activeweapon = activeweapon; + + e = get_weaponinfo(activeweapon); + if(e.netname != "") + localcmd(strcat("\ncl_hook_activeweapon ", e.netname), "\n"); + else + localcmd("\ncl_hook_activeweapon none\n"); + } + + // ALWAYS Clear Current Scene First + clearscene(); + + setproperty(VF_ORIGIN, view_origin); + setproperty(VF_ANGLES, view_angles); + + // FIXME engine bug? VF_SIZE and VF_MIN are not restored to sensible values by this + setproperty(VF_SIZE, vf_size); + setproperty(VF_MIN, vf_min); + + // Assign Standard Viewflags + // Draw the World (and sky) + setproperty(VF_DRAWWORLD, 1); + + // Set the console size vars + vid_conwidth = autocvar_vid_conwidth; + vid_conheight = autocvar_vid_conheight; + vid_pixelheight = autocvar_vid_pixelheight; + ++ if(spectatee_status <= 0) ++ fovlock = -1; ++ + if(autocvar_cl_orthoview) { setproperty(VF_FOV, GetOrthoviewFOV(ov_worldmin, ov_worldmax, ov_mid, ov_org)); } ++ else if(fovlock > 0 && autocvar_cl_specfov) { setproperty(VF_FOV, GetCurrentFov(fovlock)); } + else { setproperty(VF_FOV, GetCurrentFov(fov)); } + - // Camera for demo playback - if(camera_active) ++ float tempcamera = false; ++ ++ if(gametype == MAPINFO_TYPE_JAILBREAK) ++ if(spectatee_status >= 0) ++ if(getstati(STAT_ROUNDLOST)) ++ if(!getstati(STAT_PRISONED)) ++ tempcamera = true; ++ ++ if(tempcamera) ++ { ++ if(!camera_drawviewmodel_locked) ++ { ++ camera_drawviewmodel_locked = true; ++ camera_drawviewmodel_backup = autocvar_r_drawviewmodel; ++ } ++ ++ entity cam = world, e; ++ for(e = world; (e = find(e, classname, "jailcamera")); ) ++ if(e.jb_camera_active) ++ { ++ cam = e; ++ break; // found our camera ++ } ++ ++ if(cam) ++ { ++ setproperty(VF_ORIGIN, cam.origin); ++ setproperty(VF_ANGLES, cam.angles); ++ cvar_settemp("r_drawviewmodel", "0"); ++ } ++ } ++ else if(camera_drawviewmodel_locked) ++ { ++ camera_drawviewmodel_locked = false; ++ cvar_set("r_drawviewmodel", ftos(camera_drawviewmodel_backup)); ++ } ++ else if(camera_active) // Camera for demo playback + { + if(autocvar_camera_enable) + CSQC_Demo_Camera(); + else + { + cvar_set("chase_active", ftos(chase_active_backup)); + cvar_set("cl_demo_mousegrab", "0"); + camera_active = false; + } + } + else + { + #ifdef CAMERATEST + if(autocvar_camera_enable) + #else + if(autocvar_camera_enable && isdemo()) + #endif + { + // Enable required Darkplaces cvars + chase_active_backup = autocvar_chase_active; + cvar_set("chase_active", "2"); + cvar_set("cl_demo_mousegrab", "1"); + camera_active = true; + camera_mode = false; + } + } + + // Draw the Crosshair + setproperty(VF_DRAWCROSSHAIR, 0); //Make sure engine crosshairs are always hidden + + // Draw the Engine Status Bar (the default Quake HUD) + setproperty(VF_DRAWENGINESBAR, 0); + + // Update the mouse position + /* + mousepos_x = vid_conwidth; + mousepos_y = vid_conheight; + mousepos = mousepos*0.5 + getmousepos(); + */ + + e = self; + for(self = world; (self = nextent(self)); ) + if(self.draw) + self.draw(); + self = e; + + addentities(MASK_NORMAL | MASK_ENGINE | MASK_ENGINEVIEWMODELS); + renderscene(); + + // now switch to 2D drawing mode by calling a 2D drawing function + // then polygon drawing will draw as 2D stuff, and NOT get queued until the + // next R_RenderScene call + drawstring('0 0 0', "", '1 1 0', '1 1 1', 0, 0); + + if(autocvar_r_fakelight >= 2 || autocvar_r_fullbright) + if (!(serverflags & SERVERFLAG_ALLOW_FULLBRIGHT)) + { + // apply night vision effect + vector tc_00, tc_01, tc_10, tc_11; + vector rgb = '0 0 0'; + + if(!nightvision_noise) + { + nightvision_noise = spawn(); + nightvision_noise.classname = "nightvision_noise"; + } + if(!nightvision_noise2) + { + nightvision_noise2 = spawn(); + nightvision_noise2.classname = "nightvision_noise2"; + } + + // color tint in yellow + drawfill('0 0 0', autocvar_vid_conwidth * '1 0 0' + autocvar_vid_conheight * '0 1 0', '0.5 1 0.3', 1, DRAWFLAG_MODULATE); + + // draw BG - a = Noise_Pink(nightvision_noise, frametime * 1.5) * 0.05 + 0.15; ++ pink_noise = Noise_Pink(nightvision_noise, frametime * 1.5) * 0.05 + 0.15; + rgb = '1 1 1'; + tc_00 = '0 0 0' + '0.2 0 0' * sin(time * 0.3) + '0 0.3 0' * cos(time * 0.7); + tc_01 = '0 2.25 0' + '0.6 0 0' * cos(time * 1.2) - '0 0.3 0' * sin(time * 2.2); + tc_10 = '1.5 0 0' - '0.2 0 0' * sin(time * 0.5) + '0 0.5 0' * cos(time * 1.7); + //tc_11 = '1 1 0' + '0.6 0 0' * sin(time * 0.6) + '0 0.3 0' * cos(time * 0.1); + tc_11 = tc_01 + tc_10 - tc_00; + R_BeginPolygon("gfx/nightvision-bg.tga", DRAWFLAG_ADDITIVE); - R_PolygonVertex('0 0 0', tc_00, rgb, a); - R_PolygonVertex(autocvar_vid_conwidth * '1 0 0', tc_10, rgb, a); - R_PolygonVertex(autocvar_vid_conwidth * '1 0 0' + autocvar_vid_conheight * '0 1 0', tc_11, rgb, a); - R_PolygonVertex(autocvar_vid_conheight * '0 1 0', tc_01, rgb, a); ++ R_PolygonVertex('0 0 0', tc_00, rgb, pink_noise); ++ R_PolygonVertex(autocvar_vid_conwidth * '1 0 0', tc_10, rgb, pink_noise); ++ R_PolygonVertex(autocvar_vid_conwidth * '1 0 0' + autocvar_vid_conheight * '0 1 0', tc_11, rgb, pink_noise); ++ R_PolygonVertex(autocvar_vid_conheight * '0 1 0', tc_01, rgb, pink_noise); + R_EndPolygon(); + + // draw FG - a = Noise_Pink(nightvision_noise2, frametime * 0.1) * 0.05 + 0.12; ++ pink_noise = Noise_Pink(nightvision_noise2, frametime * 0.1) * 0.05 + 0.12; + rgb = '0.3 0.6 0.4' + '0.1 0.4 0.2' * Noise_White(nightvision_noise2, frametime); + tc_00 = '0 0 0' + '1 0 0' * Noise_White(nightvision_noise2, frametime) + '0 1 0' * Noise_White(nightvision_noise2, frametime); + tc_01 = tc_00 + '0 3 0' * (1 + Noise_White(nightvision_noise2, frametime) * 0.2); + tc_10 = tc_00 + '2 0 0' * (1 + Noise_White(nightvision_noise2, frametime) * 0.3); + tc_11 = tc_01 + tc_10 - tc_00; + R_BeginPolygon("gfx/nightvision-fg.tga", DRAWFLAG_ADDITIVE); - R_PolygonVertex('0 0 0', tc_00, rgb, a); - R_PolygonVertex(autocvar_vid_conwidth * '1 0 0', tc_10, rgb, a); - R_PolygonVertex(autocvar_vid_conwidth * '1 0 0' + autocvar_vid_conheight * '0 1 0', tc_11, rgb, a); - R_PolygonVertex(autocvar_vid_conheight * '0 1 0', tc_01, rgb, a); ++ R_PolygonVertex('0 0 0', tc_00, rgb, pink_noise); ++ R_PolygonVertex(autocvar_vid_conwidth * '1 0 0', tc_10, rgb, pink_noise); ++ R_PolygonVertex(autocvar_vid_conwidth * '1 0 0' + autocvar_vid_conheight * '0 1 0', tc_11, rgb, pink_noise); ++ R_PolygonVertex(autocvar_vid_conheight * '0 1 0', tc_01, rgb, pink_noise); + R_EndPolygon(); + } + + if(autocvar_cl_reticle) + { + // Draw the aiming reticle for weapons that use it + // reticle_type is changed to the item we are zooming / aiming with, to decide which reticle to use + // It must be a persisted float for fading out to work properly (you let go of the zoom button for + // the view to go back to normal, so reticle_type would become 0 as we fade out) + if(spectatee_status || is_dead || hud != HUD_NORMAL) + { + // no zoom reticle while dead + reticle_type = 0; + } + else if(WEP_ACTION(activeweapon, WR_ZOOMRETICLE) && autocvar_cl_reticle_weapon) + { + if(reticle_image != "") { reticle_type = 2; } + else { reticle_type = 0; } + } + else if(button_zoom || zoomscript_caught) + { + // normal zoom + reticle_type = 1; + } + + if(reticle_type) + { + if(autocvar_cl_reticle_stretch) + { + reticle_size.x = vid_conwidth; + reticle_size.y = vid_conheight; + reticle_pos.x = 0; + reticle_pos.y = 0; + } + else + { + reticle_size.x = max(vid_conwidth, vid_conheight); + reticle_size.y = max(vid_conwidth, vid_conheight); + reticle_pos.x = (vid_conwidth - reticle_size.x) / 2; + reticle_pos.y = (vid_conheight - reticle_size.y) / 2; + } + + if(zoomscript_caught) + f = 1; + else + f = current_zoomfraction; + + if(f) + { + switch(reticle_type) + { + case 1: drawpic(reticle_pos, "gfx/reticle_normal", reticle_size, '1 1 1', f * autocvar_cl_reticle_normal_alpha, DRAWFLAG_NORMAL); break; + case 2: drawpic(reticle_pos, reticle_image, reticle_size, '1 1 1', f * autocvar_cl_reticle_weapon_alpha, DRAWFLAG_NORMAL); break; + } + } + } + } + else + { + if(reticle_type != 0) { reticle_type = 0; } + } + + + // improved polyblend + if(autocvar_hud_contents) + { + float contentalpha_temp, incontent, liquidalpha, contentfadetime; + vector liquidcolor; + + switch(pointcontents(view_origin)) + { + case CONTENT_WATER: + liquidalpha = autocvar_hud_contents_water_alpha; + liquidcolor = stov(autocvar_hud_contents_water_color); + incontent = 1; + break; + + case CONTENT_LAVA: + liquidalpha = autocvar_hud_contents_lava_alpha; + liquidcolor = stov(autocvar_hud_contents_lava_color); + incontent = 1; + break; + + case CONTENT_SLIME: + liquidalpha = autocvar_hud_contents_slime_alpha; + liquidcolor = stov(autocvar_hud_contents_slime_color); + incontent = 1; + break; + + default: + liquidalpha = 0; + liquidcolor = '0 0 0'; + incontent = 0; + break; + } + + if(incontent) // fade in/out at different speeds so you can do e.g. instant fade when entering water and slow when leaving it. + { // also lets delcare previous values for blending properties, this way it isn't reset until after you have entered a different content + contentfadetime = autocvar_hud_contents_fadeintime; + liquidalpha_prev = liquidalpha; + liquidcolor_prev = liquidcolor; + } + else + contentfadetime = autocvar_hud_contents_fadeouttime; + + contentalpha_temp = bound(0, drawframetime / max(0.0001, contentfadetime), 1); + contentavgalpha = contentavgalpha * (1 - contentalpha_temp) + incontent * contentalpha_temp; + + if(contentavgalpha) + drawfill('0 0 0', eX * vid_conwidth + eY * vid_conheight, liquidcolor_prev, contentavgalpha * liquidalpha_prev, DRAWFLAG_NORMAL); + + if(autocvar_hud_postprocessing) + { + if(autocvar_hud_contents_blur && contentavgalpha) + { + content_blurpostprocess.x = 1; + content_blurpostprocess.y = contentavgalpha * autocvar_hud_contents_blur; + content_blurpostprocess.z = contentavgalpha * autocvar_hud_contents_blur_alpha; + } + else + { + content_blurpostprocess.x = 0; + content_blurpostprocess.y = 0; + content_blurpostprocess.z = 0; + } + } + } + + if(autocvar_hud_damage && !getstati(STAT_FROZEN)) + { + splash_size.x = max(vid_conwidth, vid_conheight); + splash_size.y = max(vid_conwidth, vid_conheight); + splash_pos.x = (vid_conwidth - splash_size.x) / 2; + splash_pos.y = (vid_conheight - splash_size.y) / 2; + + float myhealth_flash_temp; + myhealth = getstati(STAT_HEALTH); + + // fade out + myhealth_flash = max(0, myhealth_flash - autocvar_hud_damage_fade_rate * frametime); + // add new damage + myhealth_flash = bound(0, myhealth_flash + dmg_take * autocvar_hud_damage_factor, autocvar_hud_damage_maxalpha); + + float pain_threshold, pain_threshold_lower, pain_threshold_lower_health; + pain_threshold = autocvar_hud_damage_pain_threshold; + pain_threshold_lower = autocvar_hud_damage_pain_threshold_lower; + pain_threshold_lower_health = autocvar_hud_damage_pain_threshold_lower_health; + + if(pain_threshold_lower && myhealth < pain_threshold_lower_health) + { + pain_threshold = pain_threshold - max(autocvar_hud_damage_pain_threshold_pulsating_min, fabs(sin(M_PI * time / autocvar_hud_damage_pain_threshold_pulsating_period))) * pain_threshold_lower * (1 - max(0, myhealth)/pain_threshold_lower_health); + } + + myhealth_flash_temp = bound(0, myhealth_flash - pain_threshold, 1); + + if(myhealth_prev < 1) + { + if(myhealth >= 1) + { + myhealth_flash = 0; // just spawned, clear the flash immediately + myhealth_flash_temp = 0; + } + else + { + myhealth_flash += autocvar_hud_damage_fade_rate * frametime; // dead + } + } + + if(spectatee_status == -1 || intermission) + { + myhealth_flash = 0; // observing, or match ended + myhealth_flash_temp = 0; + } + + myhealth_prev = myhealth; + + // IDEA: change damage color/picture based on player model for robot/alien species? + // pro: matches model better + // contra: it's not red because blood is red, but because red is an alarming color, so red should stay + // maybe different reddish pics? + if(autocvar_cl_gentle_damage || autocvar_cl_gentle) + { + if(autocvar_cl_gentle_damage == 2) + { + if(myhealth_flash < pain_threshold) // only randomize when the flash is gone + { + myhealth_gentlergb = eX * random() + eY * random() + eZ * random(); + } + } + else + myhealth_gentlergb = stov(autocvar_hud_damage_gentle_color); + + drawfill('0 0 0', eX * vid_conwidth + eY * vid_conheight, myhealth_gentlergb, autocvar_hud_damage_gentle_alpha_multiplier * bound(0, myhealth_flash_temp, 1) * autocvar_hud_damage, DRAWFLAG_NORMAL); + } + else + drawpic(splash_pos, "gfx/blood", splash_size, stov(autocvar_hud_damage_color), bound(0, myhealth_flash_temp, 1) * autocvar_hud_damage, DRAWFLAG_NORMAL); + + if(autocvar_hud_postprocessing) // we still need to set this anyway even when chase_active is set, this way it doesn't get stuck on. + { + if(autocvar_hud_damage_blur && myhealth_flash_temp) + { + damage_blurpostprocess.x = 1; + damage_blurpostprocess.y = bound(0, myhealth_flash_temp, 1) * autocvar_hud_damage_blur; + damage_blurpostprocess.z = bound(0, myhealth_flash_temp, 1) * autocvar_hud_damage_blur_alpha; + } + else + { + damage_blurpostprocess.x = 0; + damage_blurpostprocess.y = 0; + damage_blurpostprocess.z = 0; + } + } + } + + float e1 = (autocvar_hud_postprocessing_maxbluralpha != 0); + float e2 = (autocvar_hud_powerup != 0); + if(autocvar_hud_postprocessing && (e1 || e2)) // TODO: Remove this code and re-do the postprocess handling in the engine, where it properly belongs. + { + // enable or disable rendering types if they are used or not + if(cvar("r_glsl_postprocess_uservec1_enable") != e1) { cvar_set("r_glsl_postprocess_uservec1_enable", ftos(e1)); } + if(cvar("r_glsl_postprocess_uservec2_enable") != e2) { cvar_set("r_glsl_postprocess_uservec2_enable", ftos(e2)); } + + // blur postprocess handling done first (used by hud_damage and hud_contents) + if((damage_blurpostprocess.x || content_blurpostprocess.x)) + { + float blurradius = bound(0, damage_blurpostprocess.y + content_blurpostprocess.y, autocvar_hud_postprocessing_maxblurradius); + float bluralpha = bound(0, damage_blurpostprocess.z + content_blurpostprocess.z, autocvar_hud_postprocessing_maxbluralpha); + if(blurradius != old_blurradius || bluralpha != old_bluralpha) // reduce cvar_set spam as much as possible + { + cvar_set("r_glsl_postprocess_uservec1", strcat(ftos(blurradius), " ", ftos(bluralpha), " 0 0")); + old_blurradius = blurradius; + old_bluralpha = bluralpha; + } + } + else if(cvar_string("r_glsl_postprocess_uservec1") != "0 0 0 0") // reduce cvar_set spam as much as possible + { + cvar_set("r_glsl_postprocess_uservec1", "0 0 0 0"); + old_blurradius = 0; + old_bluralpha = 0; + } + + // edge detection postprocess handling done second (used by hud_powerup) - float sharpen_intensity = 0, strength_finished = getstatf(STAT_STRENGTH_FINISHED), invincible_finished = getstatf(STAT_INVINCIBLE_FINISHED); - if (strength_finished - time > 0) { sharpen_intensity += (strength_finished - time); } - if (invincible_finished - time > 0) { sharpen_intensity += (invincible_finished - time); } ++ float sharpen_intensity = 0, buffs = getstati(STAT_BUFFS, 0, 24); ++ entity e; ++ for(e = Buff_Type_first; e; e = e.enemy) ++ if(buffs & e.items) ++ ++sharpen_intensity; + + sharpen_intensity = bound(0, ((getstati(STAT_HEALTH) > 0) ? sharpen_intensity : 0), 5); // Check to see if player is alive (if not, set 0) - also bound to fade out starting at 5 seconds. + + if(autocvar_hud_powerup && sharpen_intensity > 0) + { + if(sharpen_intensity != old_sharpen_intensity) // reduce cvar_set spam as much as possible + { + cvar_set("r_glsl_postprocess_uservec2", strcat(ftos((sharpen_intensity / 5) * autocvar_hud_powerup), " ", ftos(-sharpen_intensity * autocvar_hud_powerup), " 0 0")); + old_sharpen_intensity = sharpen_intensity; + } + } + else if(cvar_string("r_glsl_postprocess_uservec2") != "0 0 0 0") // reduce cvar_set spam as much as possible + { + cvar_set("r_glsl_postprocess_uservec2", "0 0 0 0"); + old_sharpen_intensity = 0; + } + + if(cvar("r_glsl_postprocess") == 0) + cvar_set("r_glsl_postprocess", "2"); + } + else if(cvar("r_glsl_postprocess") == 2) + cvar_set("r_glsl_postprocess", "0"); + + if(menu_visible) + menu_show(); + + /*if(gametype == MAPINFO_TYPE_CTF) + { + ctf_view(); + } else */ + + // draw 2D entities + e = self; + for(self = world; (self = nextent(self)); ) + if(self.draw2d) + self.draw2d(); + self = e; + Draw_ShowNames_All(); + + scoreboard_active = HUD_WouldDrawScoreboard(); + + UpdateDamage(); + UpdateCrosshair(); + UpdateHitsound(); + + if(NextFrameCommand) + { + localcmd("\n", NextFrameCommand, "\n"); + NextFrameCommand = string_null; + } + + // we must do this check AFTER a frame was rendered, or it won't work + if(cs_project_is_b0rked == 0) + { + string w0, h0; + w0 = ftos(autocvar_vid_conwidth); + h0 = ftos(autocvar_vid_conheight); + //setproperty(VF_VIEWPORT, '0 0 0', '640 480 0'); + //setproperty(VF_FOV, '90 90 0'); + setproperty(VF_ORIGIN, '0 0 0'); + setproperty(VF_ANGLES, '0 0 0'); + setproperty(VF_PERSPECTIVE, 1); + makevectors('0 0 0'); + vector v1, v2; + cvar_set("vid_conwidth", "800"); + cvar_set("vid_conheight", "600"); + v1 = cs_project(v_forward); + cvar_set("vid_conwidth", "640"); + cvar_set("vid_conheight", "480"); + v2 = cs_project(v_forward); + if(v1 == v2) + cs_project_is_b0rked = 1; + else + cs_project_is_b0rked = -1; + cvar_set("vid_conwidth", w0); + cvar_set("vid_conheight", h0); + } + + if(autocvar__hud_configure) + HUD_Panel_Mouse(); ++ else if(HUD_QuickMenu_IsOpened()) ++ HUD_QuickMenu_Mouse(); ++ else if ( HUD_MinigameMenu_IsOpened() || minigame_isactive() ) ++ HUD_Minigame_Mouse(); ++ else ++ HUD_Radar_Mouse(); + + if(hud && !intermission) - { - if(hud == HUD_SPIDERBOT) - CSQC_SPIDER_HUD(); - else if(hud == HUD_WAKIZASHI) - CSQC_WAKIZASHI_HUD(); - else if(hud == HUD_RAPTOR) - CSQC_RAPTOR_HUD(); - else if(hud == HUD_BUMBLEBEE) - CSQC_BUMBLE_HUD(); - else if(hud == HUD_BUMBLEBEE_GUN) - CSQC_BUMBLE_GUN_HUD(); - } ++ VEH_ACTION(hud, VR_HUD); ++ ++ FPSCounter_Update(); + + cl_notice_run(); + + // let's reset the view back to normal for the end + setproperty(VF_MIN, '0 0 0'); + setproperty(VF_SIZE, '1 0 0' * w + '0 1 0' * h); + } + + + void CSQC_common_hud(void) + { + if(!(gametype == MAPINFO_TYPE_RACE || gametype == MAPINFO_TYPE_CTS)) + Accuracy_LoadLevels(); + + HUD_Main(); // always run these functions for alpha checks + HUD_DrawScoreboard(); + + if (scoreboard_active) // scoreboard/accuracy + HUD_Reset(); + else if (intermission == 2) // map voting screen + { + MapVote_Draw(); + HUD_Reset(); + } + } + + + // following vectors must be global to allow seamless switching between camera modes + vector camera_offset, current_camera_offset, mouse_angles, current_angles, current_origin, current_position; + void CSQC_Demo_Camera() + { + float speed, attenuation, dimensions; + vector tmp, delta; + + if( autocvar_camera_reset || !camera_mode ) + { + camera_offset = '0 0 0'; + current_angles = '0 0 0'; + camera_direction = '0 0 0'; + camera_offset.z += 30; + camera_offset.x += 30 * -cos(current_angles.y * DEG2RAD); + camera_offset.y += 30 * -sin(current_angles.y * DEG2RAD); + current_origin = view_origin; + current_camera_offset = camera_offset; + cvar_set("camera_reset", "0"); + camera_mode = CAMERA_CHASE; + } + + // Camera angles + if( camera_roll ) + mouse_angles.z += camera_roll * autocvar_camera_speed_roll; + + if(autocvar_camera_look_player) + { + vector dir; + float n; + + dir = normalize(view_origin - current_position); + n = mouse_angles.z; + mouse_angles = vectoangles(dir); + mouse_angles.x = mouse_angles.x * -1; + mouse_angles.z = n; + } + else + { + tmp = getmousepos() * 0.1; + if(vlen(tmp)>autocvar_camera_mouse_threshold) + { + mouse_angles.x += tmp.y * cos(mouse_angles.z * DEG2RAD) + (tmp.x * sin(mouse_angles.z * DEG2RAD)); + mouse_angles.y -= tmp.x * cos(mouse_angles.z * DEG2RAD) + (tmp.y * -sin(mouse_angles.z * DEG2RAD)); + } + } + + while (mouse_angles.x < -180) mouse_angles.x = mouse_angles.x + 360; + while (mouse_angles.x > 180) mouse_angles.x = mouse_angles.x - 360; + while (mouse_angles.y < -180) mouse_angles.y = mouse_angles.y + 360; + while (mouse_angles.y > 180) mouse_angles.y = mouse_angles.y - 360; + + // Fix difference when angles don't have the same sign + delta = '0 0 0'; + if(mouse_angles.y < -60 && current_angles.y > 60) + delta = '0 360 0'; + if(mouse_angles.y > 60 && current_angles.y < -60) + delta = '0 -360 0'; + + if(autocvar_camera_look_player) + attenuation = autocvar_camera_look_attenuation; + else + attenuation = autocvar_camera_speed_attenuation; + + attenuation = 1 / max(1, attenuation); + current_angles += (mouse_angles - current_angles + delta) * attenuation; + + while (current_angles.x < -180) current_angles.x = current_angles.x + 360; + while (current_angles.x > 180) current_angles.x = current_angles.x - 360; + while (current_angles.y < -180) current_angles.y = current_angles.y + 360; + while (current_angles.y > 180) current_angles.y = current_angles.y - 360; + + // Camera position + tmp = '0 0 0'; + dimensions = 0; + + if( camera_direction.x ) + { + tmp.x = camera_direction.x * cos(current_angles.y * DEG2RAD); + tmp.y = camera_direction.x * sin(current_angles.y * DEG2RAD); + if( autocvar_camera_forward_follows && !autocvar_camera_look_player ) + tmp.z = camera_direction.x * -sin(current_angles.x * DEG2RAD); + ++dimensions; + } + + if( camera_direction.y ) + { + tmp.x += camera_direction.y * -sin(current_angles.y * DEG2RAD); + tmp.y += camera_direction.y * cos(current_angles.y * DEG2RAD) * cos(current_angles.z * DEG2RAD); + tmp.z += camera_direction.y * sin(current_angles.z * DEG2RAD); + ++dimensions; + } + + if( camera_direction.z ) + { + tmp.z += camera_direction.z * cos(current_angles.z * DEG2RAD); + ++dimensions; + } + + if(autocvar_camera_free) + speed = autocvar_camera_speed_free; + else + speed = autocvar_camera_speed_chase; + + if(dimensions) + { + speed = speed * sqrt(1 / dimensions); + camera_offset += tmp * speed; + } + + current_camera_offset += (camera_offset - current_camera_offset) * attenuation; + + // Camera modes + if( autocvar_camera_free ) + { + if ( camera_mode == CAMERA_CHASE ) + { + current_camera_offset = current_origin + current_camera_offset; + camera_offset = current_origin + camera_offset; + } + + camera_mode = CAMERA_FREE; + current_position = current_camera_offset; + } + else + { + if ( camera_mode == CAMERA_FREE ) + { + current_origin = view_origin; + camera_offset = camera_offset - current_origin; + current_camera_offset = current_camera_offset - current_origin; + } + + camera_mode = CAMERA_CHASE; + + if(autocvar_camera_chase_smoothly) + current_origin += (view_origin - current_origin) * attenuation; + else + current_origin = view_origin; + + current_position = current_origin + current_camera_offset; + } + + setproperty(VF_ANGLES, current_angles); + setproperty(VF_ORIGIN, current_position); + } diff --cc qcsrc/client/waypointsprites.qh index d52a04807,3822793da..56fcd8e5f --- a/qcsrc/client/waypointsprites.qh +++ b/qcsrc/client/waypointsprites.qh @@@ -1,6 -1,86 +1,87 @@@ + #ifndef WAYPOINTSPRITES_H + #define WAYPOINTSPRITES_H + + + float waypointsprite_initialized; + float waypointsprite_fadedistance; + float waypointsprite_normdistance; + float waypointsprite_minscale; + float waypointsprite_minalpha; + float waypointsprite_distancealphaexponent; + float waypointsprite_timealphaexponent; + float waypointsprite_scale; + float waypointsprite_fontsize; + float waypointsprite_edgefadealpha; + float waypointsprite_edgefadescale; + float waypointsprite_edgefadedistance; + float waypointsprite_edgeoffset_bottom; + float waypointsprite_edgeoffset_left; + float waypointsprite_edgeoffset_right; + float waypointsprite_edgeoffset_top; + float waypointsprite_crosshairfadealpha; + float waypointsprite_crosshairfadescale; + float waypointsprite_crosshairfadedistance; + float waypointsprite_distancefadealpha; + float waypointsprite_distancefadescale; + float waypointsprite_distancefadedistance; + float waypointsprite_alpha; + + .float helpme; + .float rule; + .string netname; // primary picture + .string netname2; // secondary picture + .string netname3; // tertiary picture + .float team; // team that gets netname2 + .float lifetime; + .float fadetime; + .float maxdistance; + .int hideflags; + .float spawntime; + .float health; + .float build_started; + .float build_starthealth; + .float build_finished; + + const float SPRITE_HEALTHBAR_WIDTH = 144; + const float SPRITE_HEALTHBAR_HEIGHT = 9; + const float SPRITE_HEALTHBAR_MARGIN = 6; + const float SPRITE_HEALTHBAR_BORDER = 2; + const float SPRITE_HEALTHBAR_BORDERALPHA = 1; + const float SPRITE_HEALTHBAR_HEALTHALPHA = 0.5; + const float SPRITE_ARROW_SCALE = 1.0; + const float SPRITE_HELPME_BLINK = 2; + + float waypointsprite_count, waypointsprite_newcount; + + void drawrotpic(vector org, float rot, string pic, vector sz, vector hotspot, vector rgb, float a, float f); + + void drawquad(vector o, vector ri, vector up, string pic, vector rgb, float a, float f); + + void drawhealthbar(vector org, float rot, float h, vector sz, vector hotspot, float width, float height, float margin, float border, float align, vector rgb, float a, vector hrgb, float ha, float f); + + // returns location of sprite text + vector drawspritearrow(vector o, float ang, vector rgb, float a, float t); + + // returns location of sprite healthbar + vector drawspritetext(vector o, float ang, float minwidth, vector rgb, float a, vector fontsize, string s); + + float spritelookupblinkvalue(string s); + vector spritelookupcolor(string s, vector def); + string spritelookuptext(string s); + + vector fixrgbexcess_move(vector rgb, vector src, vector dst); + vector fixrgbexcess(vector rgb); + + void Draw_WaypointSprite(); + // they are drawn using a .draw function - void Ent_WaypointSprite(); + void Ent_RemoveWaypointSprite(); + + void Ent_WaypointSprite(); - .float health; + void WaypointSprite_Load_Frames(string ext); + + void WaypointSprite_Load(); + + #endif diff --cc qcsrc/common/animdecide.qc index fa1f8b9c5,ecc840f6f..d5e8617d9 --- a/qcsrc/common/animdecide.qc +++ b/qcsrc/common/animdecide.qc @@@ -129,10 -141,8 +141,10 @@@ vector animdecide_getupperanim(entity e vector animdecide_getloweranim(entity e) { // death etc. + if(e.anim_state & ANIMSTATE_FOLLOW) + return vec3(((e.anim_state & ANIMSTATE_DUCK) ? e.anim_duckidle_x : e.anim_idle_x), e.anim_time, ANIMPRIO_DEAD); // dead priority so it's above all if(e.anim_state & ANIMSTATE_FROZEN) - return vec3(e.anim_idle_x, e.anim_time, ANIMPRIO_DEAD); + return vec3(e.anim_idle.x, e.anim_time, ANIMPRIO_DEAD); if(e.anim_state & ANIMSTATE_DEAD1) return vec3(e.anim_die1_x, e.anim_time, ANIMPRIO_DEAD); if(e.anim_state & ANIMSTATE_DEAD2) diff --cc qcsrc/common/animdecide.qh index 2f67a3727,9dc7cf7db..5d441ef80 --- a/qcsrc/common/animdecide.qh +++ b/qcsrc/common/animdecide.qh @@@ -22,20 -25,19 +25,20 @@@ void animdecide_setframes(entity e, boo .float anim_upper_implicit_time; // explicit anim states (networked) - void animdecide_setstate(entity e, float newstate, float restart); - #define ANIMSTATE_DEAD1 1 // base frames: die1 - #define ANIMSTATE_DEAD2 2 // base frames: die2 - #define ANIMSTATE_DUCK 4 // turns walk into duckwalk, jump into duckjump, etc. - #define ANIMSTATE_FROZEN 8 // force idle - #define ANIMSTATE_FOLLOW 16 // also force idle + void animdecide_setstate(entity e, int newstate, float restart); + const int ANIMSTATE_DEAD1 = 1; // base frames: die1 + const int ANIMSTATE_DEAD2 = 2; // base frames: die2 + const int ANIMSTATE_DUCK = 4; // turns walk into duckwalk, jump into duckjump, etc. + const int ANIMSTATE_FROZEN = 8; // force idle ++const int ANIMSTATE_FOLLOW = 16; // also force idle // implicit anim states (inferred from velocity, etc.) - #define ANIMIMPLICITSTATE_INAIR 1 - #define ANIMIMPLICITSTATE_FORWARD 2 - #define ANIMIMPLICITSTATE_BACKWARDS 4 - #define ANIMIMPLICITSTATE_LEFT 8 - #define ANIMIMPLICITSTATE_RIGHT 16 - #define ANIMIMPLICITSTATE_JUMPRELEASED 32 + const int ANIMIMPLICITSTATE_INAIR = 1; + const int ANIMIMPLICITSTATE_FORWARD = 2; + const int ANIMIMPLICITSTATE_BACKWARDS = 4; + const int ANIMIMPLICITSTATE_LEFT = 8; + const int ANIMIMPLICITSTATE_RIGHT = 16; + const int ANIMIMPLICITSTATE_JUMPRELEASED = 32; // explicit actions (networked); negative values are for lower body void animdecide_setaction(entity e, float action, float restart); diff --cc qcsrc/common/buffs.qc index 9a00bcf75,cbb40d13d..89e2d80be --- a/qcsrc/common/buffs.qc +++ b/qcsrc/common/buffs.qc @@@ -43,7 -54,8 +54,7 @@@ int Buff_Type_FromSprite(string buff_sp return 0; } - float Buff_Skin(float buff_id) - -int Buff_Skin(float buff_id) ++int Buff_Skin(int buff_id) { entity e; for(e = Buff_Type_first; e; e = e.enemy) diff --cc qcsrc/common/buffs.qh index 1c597b63d,a629de607..5fb644bde --- a/qcsrc/common/buffs.qh +++ b/qcsrc/common/buffs.qh @@@ -1,16 -1,13 +1,18 @@@ + #ifndef BUFFS_H + #define BUFFS_H +// Welcome to the stuff behind the scenes +// Below, you will find the list of buffs +// Add new buffs here! +// Note: Buffs also need spawnfuncs, which are set below entity Buff_Type_first; entity Buff_Type_last; .entity enemy; // internal next pointer - var float BUFF_LAST = 1; - float BUFF_ALL; + int BUFF_LAST = 1; ++int BUFF_ALL; - .float items; // buff ID + .int items; // buff ID .string netname; // buff name .string message; // human readable name .vector colormod; // buff color @@@ -89,16 -85,13 +91,17 @@@ BUFF_SPAWNFUNC_Q3TA_COMPAT(ammoregen, BUFF_SPAWNFUNC_Q3TA_COMPAT(haste, BUFF_SPEED) BUFF_SPAWNFUNC_Q3TA_COMPAT(invis, BUFF_INVISIBLE) BUFF_SPAWNFUNC_Q3TA_COMPAT(medic, BUFF_MEDIC) + +#undef BUFF_SPAWNFUNC +#undef BUFF_SPAWNFUNC_Q3TA_COMPAT +#undef BUFF_SPAWNFUNCS #endif - vector Buff_Color(float buff_id); - string Buff_PrettyName(float buff_id); - string Buff_Name(float buff_id); - float Buff_Type_FromName(string buff_name); - float Buff_Type_FromSprite(string buff_sprite); - float Buff_Skin(float buff_id); - string Buff_Sprite(float buff_id); + vector Buff_Color(int buff_id); + string Buff_PrettyName(int buff_id); + string Buff_Name(int buff_id); + int Buff_Type_FromName(string buff_name); + int Buff_Type_FromSprite(string buff_sprite); + int Buff_Skin(int buff_id); + string Buff_Sprite(int buff_id); + #endif diff --cc qcsrc/common/command/generic.qc index 81ab30945,7f302e0a2..923ec7478 --- a/qcsrc/common/command/generic.qc +++ b/qcsrc/common/command/generic.qc @@@ -1,3 -1,6 +1,7 @@@ + #include "generic.qh" + #include "shared_defs.qh" ++#include "script.qh" + // ========================================================= // Generic program common command code, written by Samual // Last updated: February 19th, 2012 diff --cc qcsrc/common/command/script.qh index a35717016,000000000..612265869 mode 100644,000000..100644 --- a/qcsrc/common/command/script.qh +++ b/qcsrc/common/command/script.qh @@@ -1,11 -1,0 +1,15 @@@ ++#ifndef SCRIPT_H ++#define SCRIPT_H + +void GenericCommand_script(float request, float argc, string command); + +// Compile the source into an abstract sytax tree +entity script_compile(string source); + +// Recursively evaluate an abstract syntax tree +string script_evaluate(entity ast_root); + +// Recursively remove all the nodes in the abstract syntax tree +void script_cleanup(entity ast_root); ++ ++#endif diff --cc qcsrc/common/constants.qh index d5fe35966,45a65abbe..e9deb2eec --- a/qcsrc/common/constants.qh +++ b/qcsrc/common/constants.qh @@@ -1,134 -1,102 +1,109 @@@ - // COMMIT-TODO: Update if necessary before committing - // Revision 1: additional statistics sent (flag caps, returns, deaths) - // Revision 2: Mapvote preview pictures - // Revision 3: optimized map vote protocol - // Revision 4: CSQC config var system - // Revision 5: mapvote time fix - // Revision 6: more robust against packet loss/delays, also show not yet connected clients - // Revision 7: packet loss column - // Revision 8: race - // Revision 9: race delta - // Revision 10: scoreboard force - // Revision 11: scoreboard unforce; spectator support beginning - // Revision 12: smaller scores updates (SERVER: requires new engine) - // Revision 13: pointparticles - // Revision 14: laser - // Revision 15: zoom - // Revision 16: multi-weapons - // Revision 17: multi-weaponimpulses - // Revision 18: warmup - // Revision 19: fog - // Revision 20: naggers - // Revision 21: entcs for players optimized (position data down from 12 to 7 bytes); waypointsprites in csqc for team radar - // Revision 22: hook shot origin - // Revision 23: variable hit sound - #define CSQC_REVISION 23 - - const float AS_STRING = 1; - const float AS_INT = 2; - const float AS_FLOAT_TRUNCATED = 2; - const float AS_FLOAT = 8; - - const float TE_CSQC_PICTURE = 100; - const float TE_CSQC_RACE = 101; - const float TE_CSQC_VORTEXBEAMPARTICLE = 103; - const float TE_CSQC_ARC = 104; - const float TE_CSQC_TEAMNAGGER = 105; - const float TE_CSQC_PINGPLREPORT = 106; - const float TE_CSQC_TARGET_MUSIC = 107; - const float TE_CSQC_WEAPONCOMPLAIN = 108; - const float TE_CSQC_VORTEX_SCOPE = 109; - const float TE_CSQC_MINELAYER_MAXMINES = 110; - const float TE_CSQC_HAGAR_MAXROCKETS = 111; - const float TE_CSQC_VEHICLESETUP = 112; - const float TE_CSQC_SVNOTICE = 113; - const float TE_CSQC_SHOCKWAVEPARTICLE = 114; - const float TE_CSQC_SPECINFO = 115; - const float TE_CSQC_SUPERBLASTPARTICLE = 116; - - const float RACE_NET_CHECKPOINT_HIT_QUALIFYING = 0; // byte checkpoint, short time, short recordtime, string recordholder - const float RACE_NET_CHECKPOINT_CLEAR = 1; - const float RACE_NET_CHECKPOINT_NEXT_QUALIFYING = 2; // byte nextcheckpoint, short recordtime, string recordholder - const float RACE_NET_CHECKPOINT_HIT_RACE = 3; // byte checkpoint, short delta, byte lapsdelta, string opponent - const float RACE_NET_CHECKPOINT_HIT_RACE_BY_OPPONENT = 4; // byte checkpoint, short delta, byte lapsdelta, string opponent - const float RACE_NET_CHECKPOINT_NEXT_SPEC_QUALIFYING = 5; // byte nextcheckpoint, float laptime, short recordtime, string recordholder - const float RACE_NET_PENALTY_RACE = 6; // byte penaltytime, string reason - const float RACE_NET_PENALTY_QUALIFYING = 7; // byte penaltytime, string reason - const float RACE_NET_SERVER_RECORD = 8; // server record, sent to client - const float RACE_NET_SPEED_AWARD = 9; // speed award, sent to client - const float RACE_NET_SPEED_AWARD_BEST = 10; // all time best speed award, sent to client - const float RACE_NET_SERVER_RANKINGS = 11; - const float RACE_NET_SERVER_STATUS = 12; - const float RANKINGS_CNT = 15; - - const float ENT_CLIENT = 0; - const float ENT_CLIENT_DEAD = 1; - const float ENT_CLIENT_ENTCS = 2; - const float ENT_CLIENT_SCORES_INFO = 3; - const float ENT_CLIENT_SCORES = 4; - const float ENT_CLIENT_TEAMSCORES = 5; - const float ENT_CLIENT_POINTPARTICLES = 6; - const float ENT_CLIENT_RAINSNOW = 7; - const float ENT_CLIENT_LASER = 8; - const float ENT_CLIENT_NAGGER = 9; // flags [votecalledvote] - const float ENT_CLIENT_WAYPOINT = 10; // flags origin [team displayrule] [spritename] [spritename2] [spritename3] [lifetime maxdistance hideable] - const float ENT_CLIENT_RADARLINK = 11; // flags [startorigin] [endorigin] [startcolor+16*endcolor] - const float ENT_CLIENT_PROJECTILE = 12; - const float ENT_CLIENT_GIBSPLASH = 13; - const float ENT_CLIENT_DAMAGEINFO = 14; - const float ENT_CLIENT_CASING = 15; - const float ENT_CLIENT_INIT = 16; - const float ENT_CLIENT_MAPVOTE = 17; - const float ENT_CLIENT_CLIENTDATA = 18; - const float ENT_CLIENT_RANDOMSEED = 19; - const float ENT_CLIENT_WALL = 20; - const float ENT_CLIENT_SPIDERBOT = 21; - const float ENT_CLIENT_MODELEFFECT = 22; - const float ENT_CLIENT_TUBANOTE = 23; - const float ENT_CLIENT_WARPZONE = 24; - const float ENT_CLIENT_WARPZONE_CAMERA = 25; - const float ENT_CLIENT_TRIGGER_MUSIC = 26; - const float ENT_CLIENT_HOOK = 27; - const float ENT_CLIENT_ARC_BEAM = 29; // WEAPONTODO: fix numbers - const float ENT_CLIENT_ACCURACY = 30; - const float ENT_CLIENT_SHOWNAMES = 31; - const float ENT_CLIENT_WARPZONE_TELEPORTED = 32; - const float ENT_CLIENT_MODEL = 33; - const float ENT_CLIENT_ITEM = 34; - const float ENT_CLIENT_BUMBLE_RAYGUN = 35; - const float ENT_CLIENT_SPAWNPOINT = 36; - const float ENT_CLIENT_SPAWNEVENT = 37; - const float ENT_CLIENT_NOTIFICATION = 38; - const float ENT_CLIENT_ELIMINATEDPLAYERS = 39; - const float ENT_CLIENT_TURRET = 40; - const float ENT_CLIENT_AUXILIARYXHAIR = 50; - const float ENT_CLIENT_VEHICLE = 60; - const float ENT_CLIENT_GENERATOR = 70; - const float ENT_CLIENT_CONTROLPOINT_ICON = 71; - const float ENT_CLIENT_JAILCAMERA = 72; - const float ENT_CLIENT_EFFECT = 73; - const float ENT_CLIENT_CONQUEST_CONTROLPOINT = 74; - - const float ENT_CLIENT_HEALING_ORB = 80; - - const float ENT_CLIENT_MINIGAME = 81; - - const float SPRITERULE_DEFAULT = 0; - const float SPRITERULE_TEAMPLAY = 1; - - const float RADARICON_NONE = 0; - const float RADARICON_FLAG = 1; - const float RADARICON_FLAGCARRIER = 1; - const float RADARICON_HERE = 1; // TODO make these 3 and 4, and make images for them - const float RADARICON_DANGER = 1; - const float RADARICON_WAYPOINT = 1; - const float RADARICON_HELPME = 1; - const float RADARICON_CONTROLPOINT = 1; - const float RADARICON_GENERATOR = 1; - const float RADARICON_OBJECTIVE = 1; - const float RADARICON_DOMPOINT = 1; - const float RADARICON_POWERUP = 1; - const float RADARICON_TAGGED = 1; + #ifndef CONSTANTS_H + #define CONSTANTS_H + + const int AS_STRING = 1; + const int AS_INT = 2; + const int AS_FLOAT_TRUNCATED = 2; + const int AS_FLOAT = 8; + + const int TE_CSQC_PICTURE = 100; + const int TE_CSQC_RACE = 101; + const int TE_CSQC_VORTEXBEAMPARTICLE = 103; + const int TE_CSQC_ARC = 104; + const int TE_CSQC_TEAMNAGGER = 105; + const int TE_CSQC_PINGPLREPORT = 106; + const int TE_CSQC_TARGET_MUSIC = 107; + const int TE_CSQC_WEAPONCOMPLAIN = 108; + const int TE_CSQC_VORTEX_SCOPE = 109; + const int TE_CSQC_MINELAYER_MAXMINES = 110; + const int TE_CSQC_HAGAR_MAXROCKETS = 111; + const int TE_CSQC_VEHICLESETUP = 112; + const int TE_CSQC_SVNOTICE = 113; + const int TE_CSQC_SHOCKWAVEPARTICLE = 114; ++const int TE_CSQC_SUPERBLASTPARTICLE = 115; + + const int RACE_NET_CHECKPOINT_HIT_QUALIFYING = 0; // byte checkpoint, short time, short recordtime, string recordholder + const int RACE_NET_CHECKPOINT_CLEAR = 1; + const int RACE_NET_CHECKPOINT_NEXT_QUALIFYING = 2; // byte nextcheckpoint, short recordtime, string recordholder + const int RACE_NET_CHECKPOINT_HIT_RACE = 3; // byte checkpoint, short delta, byte lapsdelta, string opponent + const int RACE_NET_CHECKPOINT_HIT_RACE_BY_OPPONENT = 4; // byte checkpoint, short delta, byte lapsdelta, string opponent + const int RACE_NET_CHECKPOINT_NEXT_SPEC_QUALIFYING = 5; // byte nextcheckpoint, float laptime, short recordtime, string recordholder + const int RACE_NET_PENALTY_RACE = 6; // byte penaltytime, string reason + const int RACE_NET_PENALTY_QUALIFYING = 7; // byte penaltytime, string reason + const int RACE_NET_SERVER_RECORD = 8; // server record, sent to client + const int RACE_NET_SPEED_AWARD = 9; // speed award, sent to client + const int RACE_NET_SPEED_AWARD_BEST = 10; // all time best speed award, sent to client + const int RACE_NET_SERVER_RANKINGS = 11; + const int RACE_NET_SERVER_STATUS = 12; + const int RANKINGS_CNT = 15; + + const int ENT_CLIENT = 0; + const int ENT_CLIENT_DEAD = 1; + const int ENT_CLIENT_ENTCS = 2; + const int ENT_CLIENT_SCORES_INFO = 3; + const int ENT_CLIENT_SCORES = 4; + const int ENT_CLIENT_TEAMSCORES = 5; + const int ENT_CLIENT_POINTPARTICLES = 6; + const int ENT_CLIENT_RAINSNOW = 7; + const int ENT_CLIENT_LASER = 8; + const int ENT_CLIENT_NAGGER = 9; // flags [votecalledvote] + const int ENT_CLIENT_WAYPOINT = 10; // flags origin [team displayrule] [spritename] [spritename2] [spritename3] [lifetime maxdistance hideable] + const int ENT_CLIENT_RADARLINK = 11; // flags [startorigin] [endorigin] [startcolor+16*endcolor] + const int ENT_CLIENT_PROJECTILE = 12; + const int ENT_CLIENT_GIBSPLASH = 13; + const int ENT_CLIENT_DAMAGEINFO = 14; + const int ENT_CLIENT_CASING = 15; + const int ENT_CLIENT_INIT = 16; + const int ENT_CLIENT_MAPVOTE = 17; + const int ENT_CLIENT_CLIENTDATA = 18; + const int ENT_CLIENT_RANDOMSEED = 19; + const int ENT_CLIENT_WALL = 20; + const int ENT_CLIENT_SPIDERBOT = 21; + const int ENT_CLIENT_MODELEFFECT = 22; + const int ENT_CLIENT_TUBANOTE = 23; + const int ENT_CLIENT_WARPZONE = 24; + const int ENT_CLIENT_WARPZONE_CAMERA = 25; + const int ENT_CLIENT_TRIGGER_MUSIC = 26; + const int ENT_CLIENT_HOOK = 27; + const int ENT_CLIENT_ARC_BEAM = 29; // WEAPONTODO: fix numbers + const int ENT_CLIENT_ACCURACY = 30; + const int ENT_CLIENT_SHOWNAMES = 31; + const int ENT_CLIENT_WARPZONE_TELEPORTED = 32; + const int ENT_CLIENT_MODEL = 33; + const int ENT_CLIENT_ITEM = 34; + const int ENT_CLIENT_BUMBLE_RAYGUN = 35; + const int ENT_CLIENT_SPAWNPOINT = 36; + const int ENT_CLIENT_SPAWNEVENT = 37; + const int ENT_CLIENT_NOTIFICATION = 38; + const int ENT_CLIENT_ELIMINATEDPLAYERS = 39; + const int ENT_CLIENT_TURRET = 40; + const int ENT_CLIENT_AUXILIARYXHAIR = 50; + const int ENT_CLIENT_VEHICLE = 60; ++const int ENT_CLIENT_GENERATOR = 70; ++const int ENT_CLIENT_CONTROLPOINT_ICON = 71; ++const int ENT_CLIENT_JAILCAMERA = 72; ++const int ENT_CLIENT_EFFECT = 73; ++const int ENT_CLIENT_CONQUEST_CONTROLPOINT = 74; ++const int ENT_CLIENT_MINIGAME = 75; + + const int ENT_CLIENT_HEALING_ORB = 80; + + const int SPRITERULE_DEFAULT = 0; + const int SPRITERULE_TEAMPLAY = 1; + + const int RADARICON_NONE = 0; + const int RADARICON_FLAG = 1; + const int RADARICON_FLAGCARRIER = 1; + const int RADARICON_HERE = 1; // TODO make these 3 and 4, and make images for them + const int RADARICON_DANGER = 1; + const int RADARICON_WAYPOINT = 1; + const int RADARICON_HELPME = 1; + const int RADARICON_CONTROLPOINT = 1; + const int RADARICON_GENERATOR = 1; + const int RADARICON_OBJECTIVE = 1; + const int RADARICON_DOMPOINT = 1; + const int RADARICON_POWERUP = 1; + const int RADARICON_TAGGED = 1; /////////////////////////// // keys pressed @@@ -151,8 -119,18 +126,8 @@@ const int CVAR_READONLY = 4 /////////////////////////// // csqc communication stuff - const float HUD_NORMAL = 0; - const float HUD_BUMBLEBEE_GUN = 25; -const int CTF_STATE_ATTACK = 1; -const int CTF_STATE_DEFEND = 2; -const int CTF_STATE_COMMANDER = 3; - + const int HUD_NORMAL = 0; -const int HUD_VEHICLE_FIRST = 10; -const int HUD_SPIDERBOT = 10; -const int HUD_WAKIZASHI = 11; -const int HUD_RAPTOR = 12; -const int HUD_BUMBLEBEE = 13; -const int HUD_BUMBLEBEE_GUN = 14; -const int HUD_VEHICLE_LAST = 14; ++const int HUD_BUMBLEBEE_GUN = 25; const vector eX = '1 0 0'; const vector eY = '0 1 0'; @@@ -200,33 -178,32 +175,33 @@@ const int SFL_SORT_PRIO_MASK = 12 /** * Score indices */ -#define MAX_SCORE 10 +#define MAX_SCORE 11 #define MAX_TEAMSCORE 2 - #define ST_SCORE 0 - #define SP_KILLS 0 - #define SP_DEATHS 1 - #define SP_SUICIDES 2 - #define SP_SCORE 3 - #define SP_FPS 10 + const int ST_SCORE = 0; + const int SP_KILLS = 0; + const int SP_DEATHS = 1; + const int SP_SUICIDES = 2; + const int SP_SCORE = 3; ++const int SP_FPS = 10; // game mode specific indices are not in common/, but in server/scores_rules.qc! - const float CH_INFO = 0; - const float CH_TRIGGER = -3; - const float CH_WEAPON_A = -1; - const float CH_WEAPON_SINGLE = 1; - const float CH_VOICE = -2; - const float CH_BGM_SINGLE = 8; - const float CH_AMBIENT = -9; - const float CH_TRIGGER_SINGLE = 3; - const float CH_SHOTS = -4; - const float CH_SHOTS_SINGLE = 4; - const float CH_WEAPON_B = -1; - const float CH_PAIN = -6; - const float CH_PAIN_SINGLE = 6; - const float CH_PLAYER = -7; - const float CH_PLAYER_SINGLE = 7; - const float CH_TUBA_SINGLE = 5; + const int CH_INFO = 0; + const int CH_TRIGGER = -3; + const int CH_WEAPON_A = -1; + const int CH_WEAPON_SINGLE = 1; + const int CH_VOICE = -2; + const int CH_BGM_SINGLE = 8; + const int CH_AMBIENT = -9; + const int CH_TRIGGER_SINGLE = 3; + const int CH_SHOTS = -4; + const int CH_SHOTS_SINGLE = 4; + const int CH_WEAPON_B = -1; + const int CH_PAIN = -6; + const int CH_PAIN_SINGLE = 6; + const int CH_PLAYER = -7; + const int CH_PLAYER_SINGLE = 7; + const int CH_TUBA_SINGLE = 5; const float ATTEN_NONE = 0; const float ATTEN_MIN = 0.015625; @@@ -236,63 -213,58 +211,63 @@@ const float ATTEN_IDLE = 2 const float ATTEN_STATIC = 3; const float ATTEN_MAX = 3.984375; - #define VOL_BASE 0.7 - #define VOL_BASEVOICE 1.0 + const float VOL_BASE = 0.7; + const float VOL_BASEVOICE = 1.0; // WEAPONTODO: move this into separate/new projectile handling code // this sets sounds and other properties of the projectiles in csqc - const float PROJECTILE_ELECTRO = 1; - const float PROJECTILE_ROCKET = 2; - const float PROJECTILE_TAG = 3; - const float PROJECTILE_CRYLINK = 5; - const float PROJECTILE_ELECTRO_BEAM = 6; - const float PROJECTILE_GRENADE = 7; - const float PROJECTILE_GRENADE_BOUNCING = 8; - const float PROJECTILE_MINE = 9; - const float PROJECTILE_BLASTER = 10; - const float PROJECTILE_HLAC = 11; - const float PROJECTILE_SEEKER = 12; - const float PROJECTILE_FLAC = 13; - const float PROJECTILE_PORTO_RED = 14; - const float PROJECTILE_PORTO_BLUE = 15; - const float PROJECTILE_HOOKBOMB = 16; - const float PROJECTILE_HAGAR = 17; - const float PROJECTILE_HAGAR_BOUNCING = 18; - const float PROJECTILE_CRYLINK_BOUNCING = 20; - const float PROJECTILE_FIREBALL = 21; - const float PROJECTILE_FIREMINE = 22; - - const float PROJECTILE_RAPTORCANNON = 24; - const float PROJECTILE_RAPTORBOMB = 25; - const float PROJECTILE_RAPTORBOMBLET = 26; - const float PROJECTILE_SPIDERROCKET = 27; - const float PROJECTILE_WAKIROCKET = 28; - const float PROJECTILE_WAKICANNON = 29; - - const float PROJECTILE_BUMBLE_GUN = 30; - const float PROJECTILE_BUMBLE_BEAM = 31; - - const float PROJECTILE_MAGE_SPIKE = 32; - const float PROJECTILE_SHAMBLER_LIGHTNING = 33; - const float PROJECTILE_SCRAG_SPIKE = 34; - - const float PROJECTILE_ROCKETMINSTA_LASER = 35; - const float PROJECTILE_SUPERROCKET = 36; - - const float PROJECTILE_RPC = 37; - const float PROJECTILE_CANNONBALL = 38; - - const float SPECIES_HUMAN = 0; - const float SPECIES_ROBOT_SOLID = 1; - const float SPECIES_ALIEN = 2; - const float SPECIES_ANIMAL = 3; - const float SPECIES_ROBOT_RUSTY = 4; - const float SPECIES_ROBOT_SHINY = 5; - const float SPECIES_RESERVED = 15; - - #define FRAGS_PLAYER 0 - #define FRAGS_SPECTATOR -666 - #define FRAGS_LMS_LOSER -616 - #define FRAGS_PLAYER_NONSOLID -616 + const int PROJECTILE_ELECTRO = 1; + const int PROJECTILE_ROCKET = 2; + const int PROJECTILE_TAG = 3; + const int PROJECTILE_CRYLINK = 5; + const int PROJECTILE_ELECTRO_BEAM = 6; + const int PROJECTILE_GRENADE = 7; + const int PROJECTILE_GRENADE_BOUNCING = 8; + const int PROJECTILE_MINE = 9; + const int PROJECTILE_BLASTER = 10; + const int PROJECTILE_HLAC = 11; + const int PROJECTILE_SEEKER = 12; + const int PROJECTILE_FLAC = 13; + const int PROJECTILE_PORTO_RED = 14; + const int PROJECTILE_PORTO_BLUE = 15; + const int PROJECTILE_HOOKBOMB = 16; + const int PROJECTILE_HAGAR = 17; + const int PROJECTILE_HAGAR_BOUNCING = 18; + const int PROJECTILE_CRYLINK_BOUNCING = 20; + const int PROJECTILE_FIREBALL = 21; + const int PROJECTILE_FIREMINE = 22; + + const int PROJECTILE_RAPTORCANNON = 24; + const int PROJECTILE_RAPTORBOMB = 25; + const int PROJECTILE_RAPTORBOMBLET = 26; + const int PROJECTILE_SPIDERROCKET = 27; + const int PROJECTILE_WAKIROCKET = 28; + const int PROJECTILE_WAKICANNON = 29; + + const int PROJECTILE_BUMBLE_GUN = 30; + const int PROJECTILE_BUMBLE_BEAM = 31; + + const int PROJECTILE_MAGE_SPIKE = 32; + const int PROJECTILE_SHAMBLER_LIGHTNING = 33; ++const int PROJECTILE_SCRAG_SPIKE = 34; ++ ++const int PROJECTILE_ROCKETMINSTA_LASER = 35; ++const int PROJECTILE_SUPERROCKET = 36; ++const int PROJECTILE_CANNONBALL = 38; + + const int PROJECTILE_RPC = 60; + + const int SPECIES_HUMAN = 0; + const int SPECIES_ROBOT_SOLID = 1; + const int SPECIES_ALIEN = 2; + const int SPECIES_ANIMAL = 3; + const int SPECIES_ROBOT_RUSTY = 4; + const int SPECIES_ROBOT_SHINY = 5; + const int SPECIES_RESERVED = 15; + + const int FRAGS_PLAYER = 0; + const int FRAGS_SPECTATOR = -666; + const int FRAGS_LMS_LOSER = -616; + const int FRAGS_PLAYER_NONSOLID = -616; // we can use this frags value for both // water levels diff --cc qcsrc/common/csqcmodel_settings.qh index 56f94dfde,fdc555e24..959372168 --- a/qcsrc/common/csqcmodel_settings.qh +++ b/qcsrc/common/csqcmodel_settings.qh @@@ -31,15 -34,15 +34,15 @@@ CSQCMODEL_PROPERTY(64, float, ReadByte, WriteByte, solid) \ CSQCMODEL_IF(!isplayer) \ CSQCMODEL_PROPERTY(128, TAG_ENTITY_TYPE, ReadShort, WriteEntity, TAG_ENTITY_NAME) \ - CSQCMODEL_PROPERTY_SCALED(256, float, ReadByte, WriteByte, glowmod_x, 255, 0, 255) \ - CSQCMODEL_PROPERTY_SCALED(256, float, ReadByte, WriteByte, glowmod_y, 255, 0, 255) \ - CSQCMODEL_PROPERTY_SCALED(256, float, ReadByte, WriteByte, glowmod_z, 255, 0, 255) \ - CSQCMODEL_PROPERTY_SCALED(256, float, ReadByte, WriteByte, colormod_x, 255, 0, 255) \ - CSQCMODEL_PROPERTY_SCALED(256, float, ReadByte, WriteByte, colormod_y, 255, 0, 255) \ - CSQCMODEL_PROPERTY_SCALED(256, float, ReadByte, WriteByte, colormod_z, 255, 0, 255) \ + CSQCMODEL_PROPERTY_SCALED(256, float, ReadByte, WriteByte, glowmod_x, 254, -1, 254) \ + CSQCMODEL_PROPERTY_SCALED(256, float, ReadByte, WriteByte, glowmod_y, 254, -1, 254) \ + CSQCMODEL_PROPERTY_SCALED(256, float, ReadByte, WriteByte, glowmod_z, 254, -1, 254) \ + CSQCMODEL_PROPERTY_SCALED(256, float, ReadByte, WriteByte, colormod_x, 254, -1, 254) \ + CSQCMODEL_PROPERTY_SCALED(256, float, ReadByte, WriteByte, colormod_y, 254, -1, 254) \ + CSQCMODEL_PROPERTY_SCALED(256, float, ReadByte, WriteByte, colormod_z, 254, -1, 254) \ CSQCMODEL_ENDIF \ CSQCMODEL_IF(isplayer) \ - CSQCMODEL_PROPERTY(128, float, ReadByte, WriteByte, anim_state) \ + CSQCMODEL_PROPERTY(128, int, ReadByte, WriteByte, anim_state) \ CSQCMODEL_PROPERTY(128, float, ReadApproxPastTime, WriteApproxPastTime, anim_time) \ CSQCMODEL_IF(!islocalplayer) \ CSQCMODEL_PROPERTY(256, float, ReadChar, WriteChar, anim_lower_action) \ diff --cc qcsrc/common/deathtypes.qh index 4f99fefdf,301d66576..b63dcc907 --- a/qcsrc/common/deathtypes.qh +++ b/qcsrc/common/deathtypes.qh @@@ -2,12 -7,20 +7,21 @@@ // Deathtypes, reworked by Samual // ================================ + int DEATH_SPECIAL_START; + int NORMAL_POS; + int DEATH_MONSTER_FIRST; + int DEATH_MONSTER_LAST; + int DEATH_TURRET_FIRST; + int DEATH_TURRET_LAST; + int DEATH_VHFIRST; + int DEATH_VHLAST; + #define DEATHTYPES \ DEATHTYPE(DEATH_AUTOTEAMCHANGE, DEATH_SELF_AUTOTEAMCHANGE, NO_MSG, DEATH_SPECIAL_START) \ - DEATHTYPE(DEATH_BUFF_VENGEANCE, NO_MSG, DEATH_MURDER_VENGEANCE, NORMAL_POS) \ + DEATHTYPE(DEATH_BUFF, NO_MSG, DEATH_MURDER_BUFF, NORMAL_POS) \ DEATHTYPE(DEATH_CAMP, DEATH_SELF_CAMP, NO_MSG, NORMAL_POS) \ DEATHTYPE(DEATH_CHEAT, DEATH_SELF_CHEAT, DEATH_MURDER_CHEAT, NORMAL_POS) \ + DEATHTYPE(DEATH_CRUSH, NO_MSG, DEATH_MURDER_CRUSH, NORMAL_POS) \ DEATHTYPE(DEATH_CUSTOM, DEATH_SELF_CUSTOM, NO_MSG, NORMAL_POS) \ DEATHTYPE(DEATH_DROWN, DEATH_SELF_DROWN, DEATH_MURDER_DROWN, NORMAL_POS) \ DEATHTYPE(DEATH_FALL, DEATH_SELF_FALL, DEATH_MURDER_FALL, NORMAL_POS) \ @@@ -131,10 -129,11 +144,11 @@@ string Deathtype_Name(int deathtype else { return ftos(deathtype); } } - const float DEATH_WEAPONMASK = 0xFF; - const float DEATH_HITTYPEMASK = 0x1F00; // which is WAY below 10000 used for normal deaths - const float HITTYPE_SECONDARY = 0x100; - const float HITTYPE_SPLASH = 0x200; // automatically set by RadiusDamage - const float HITTYPE_BOUNCE = 0x400; - const float HITTYPE_HEADSHOT = 0x800; // automatically set by Damage (if headshotbonus is set) - const float HITTYPE_RESERVED = 0x1000; // unused yet + const int DEATH_WEAPONMASK = 0xFF; + const int DEATH_HITTYPEMASK = 0x1F00; // which is WAY below 10000 used for normal deaths + const int HITTYPE_SECONDARY = 0x100; + const int HITTYPE_SPLASH = 0x200; // automatically set by RadiusDamage + const int HITTYPE_BOUNCE = 0x400; -const int HITTYPE_RESERVED2 = 0x800; ++const int HITTYPE_HEADSHOT = 0x800; // automatically set by Damage (if headshotbonus is set) + const int HITTYPE_RESERVED = 0x1000; // unused yet + #endif diff --cc qcsrc/common/effects.qc index 50b5d1386,000000000..e6f4711f8 mode 100644,000000..100644 --- a/qcsrc/common/effects.qc +++ b/qcsrc/common/effects.qc @@@ -1,109 -1,0 +1,109 @@@ +void Create_Effect_Entity(float eff_name, string eff_string, float eff_trail) +{ + entity eff; + effects_ent[eff_name - 1] = eff = spawn(); + + eff.classname = "effect_entity"; + eff.eent_net_name = eff_name; + eff.eent_eff_name = eff_string; + eff.eent_eff_trail = eff_trail; +} + +#ifdef CSQC +void Read_Effect(float is_new) +{ +#if EFFECTS_COUNT >= 255 + float net_name = ReadShort(); +#else + float net_name = ReadByte(); +#endif + + entity eff = effects_ent[net_name - 1]; + + vector v, vel = '0 0 0'; + float eff_cnt = 1; + float eff_trail = eff.eent_eff_trail; + v_x = ReadCoord(); + v_y = ReadCoord(); + v_z = ReadCoord(); + + float use_vel = ReadByte(); + if(use_vel) + { + vel_x = ReadCoord(); + vel_y = ReadCoord(); + vel_z = ReadCoord(); + } + + if(!eff_trail) + eff_cnt = ReadByte(); + + if(is_new) + if(eff_trail) + WarpZone_TrailParticles(world, particleeffectnum(eff.eent_eff_name), v, vel); + else + pointparticles(particleeffectnum(eff.eent_eff_name), v, vel, eff_cnt); +} +#endif + +#ifdef SVQC +float Net_Write_Effect(entity client, float sf) +{ + WriteByte(MSG_ENTITY, ENT_CLIENT_EFFECT); +#if EFFECTS_COUNT >= 255 + WriteShort(MSG_ENTITY, self.eent_net_name); +#else + WriteByte(MSG_ENTITY, self.eent_net_name); +#endif + WriteCoord(MSG_ENTITY, self.eent_net_location_x); + WriteCoord(MSG_ENTITY, self.eent_net_location_y); + WriteCoord(MSG_ENTITY, self.eent_net_location_z); + + // attempt to save a tiny bit more bandwidth by not sending velocity if it isn't set + if(self.eent_net_velocity) + { - WriteByte(MSG_ENTITY, TRUE); ++ WriteByte(MSG_ENTITY, true); + WriteCoord(MSG_ENTITY, self.eent_net_velocity_x); + WriteCoord(MSG_ENTITY, self.eent_net_velocity_y); + WriteCoord(MSG_ENTITY, self.eent_net_velocity_z); + } - else { WriteByte(MSG_ENTITY, FALSE); } ++ else { WriteByte(MSG_ENTITY, false); } + + if(!self.eent_eff_trail) { WriteByte(MSG_ENTITY, self.eent_net_count); } - return TRUE; ++ return true; +} + +// problem with this is, we might not have all the available effects for it +/*float Effect_NameToID(string eff_name) +{ + float i; + for(i = EFFECT_FIRST; i < EFFECT_MAX; ++i) + { + if((effects_ent[i - 1]).eent_eff_name == eff_name) + return (effects_ent[i - 1]).eent_net_name; + } + + return 0; +} */ + +void Send_Effect(float eff_name, vector eff_loc, vector eff_vel, float eff_cnt) +{ + entity eff = effects_ent[eff_name - 1]; + if(!eff) { return; } + if(!eff.eent_eff_trail && !eff_cnt) { return; } // effect has no count! + entity net_eff = spawn(); + net_eff.owner = eff; + net_eff.classname = "net_effect"; + //net_eff.eent_broadcast = broadcast; + net_eff.eent_net_name = eff_name; + net_eff.eent_net_velocity = eff_vel; + net_eff.eent_net_location = eff_loc; + net_eff.eent_net_count = eff_cnt; + net_eff.eent_eff_trail = eff.eent_eff_trail; + + net_eff.think = SUB_Remove; + net_eff.nextthink = time + 0.2; // don't need to keep this long + - Net_LinkEntity(net_eff, FALSE, 0, Net_Write_Effect); ++ Net_LinkEntity(net_eff, false, 0, Net_Write_Effect); +} +#endif diff --cc qcsrc/common/effects.qh index 9d7b05d38,000000000..69662c413 mode 100644,000000..100644 --- a/qcsrc/common/effects.qh +++ b/qcsrc/common/effects.qh @@@ -1,155 -1,0 +1,159 @@@ ++#ifndef EFFECTS_H ++#define EFFECTS_H +// Global list of effects, networked to CSQC by ID to save bandwidth and to use client particle numbers (allows mismatching effectinfos to some degree) +// Not too concerned about the order of this list, just keep the weapon effects together! + +// EFFECT(istrail, EFFECT_NAME, "effectinfo_string") +#define EFFECTS \ + EFFECT(0, EFFECT_EXPLOSION_SMALL, "explosion_small") \ + EFFECT(0, EFFECT_EXPLOSION_MEDIUM, "explosion_medium") \ + EFFECT(0, EFFECT_EXPLOSION_BIG, "explosion_big") \ + EFFECT(1, EFFECT_VAPORIZER_RED, "TE_TEI_G3RED") \ + EFFECT(1, EFFECT_VAPORIZER_RED_HIT, "TE_TEI_G3RED_HIT") \ + EFFECT(1, EFFECT_VAPORIZER_BLUE, "TE_TEI_G3BLUE") \ + EFFECT(1, EFFECT_VAPORIZER_BLUE_HIT, "TE_TEI_G3BLUE_HIT") \ + EFFECT(1, EFFECT_VAPORIZER_YELLOW, "TE_TEI_G3YELLOW") \ + EFFECT(1, EFFECT_VAPORIZER_YELLOW_HIT, "TE_TEI_G3YELLOW_HIT") \ + EFFECT(1, EFFECT_VAPORIZER_PINK, "TE_TEI_G3PINK") \ + EFFECT(1, EFFECT_VAPORIZER_PINK_HIT, "TE_TEI_G3PINK_HIT") \ + EFFECT(1, EFFECT_VAPORIZER_NEUTRAL, "TE_TEI_G3NEUTRAL") \ + EFFECT(1, EFFECT_VAPORIZER_NEUTRAL_HIT, "TE_TEI_G3NEUTRAL_HIT") \ + EFFECT(0, EFFECT_ELECTRO_COMBO, "electro_combo") \ + EFFECT(0, EFFECT_ELECTRO_IMPACT, "electro_impact") \ + EFFECT(0, EFFECT_ELECTRO_MUZZLEFLASH, "electro_muzzleflash") \ + EFFECT(0, EFFECT_HAGAR_BOUNCE, "hagar_bounce") \ + EFFECT(0, EFFECT_HAGAR_MUZZLEFLASH, "hagar_muzzleflash") \ + EFFECT(0, EFFECT_LASER_MUZZLEFLASH, "laser_muzzleflash") \ + EFFECT(0, EFFECT_MACHINEGUN_MUZZLEFLASH, "uzi_muzzleflash") \ + EFFECT(0, EFFECT_RIFLE_MUZZLEFLASH, "rifle_muzzleflash") \ + EFFECT(1, EFFECT_RIFLE, "tr_rifle") \ + EFFECT(1, EFFECT_RIFLE_WEAK, "tr_rifle_weak") \ + EFFECT(0, EFFECT_SEEKER_MUZZLEFLASH, "seeker_muzzleflash") \ + EFFECT(0, EFFECT_SHOTGUN_MUZZLEFLASH, "shotgun_muzzleflash") \ + EFFECT(0, EFFECT_GRENADE_MUZZLEFLASH, "grenadelauncher_muzzleflash") \ + EFFECT(0, EFFECT_GRENADE_EXPLODE, "grenade_explode") \ + EFFECT(0, EFFECT_CRYLINK_JOINEXPLODE, "crylink_joinexplode") \ + EFFECT(0, EFFECT_CRYLINK_MUZZLEFLASH, "crylink_muzzleflash") \ + EFFECT(0, EFFECT_VORTEX_MUZZLEFLASH, "nex_muzzleflash") \ + EFFECT(0, EFFECT_HOOK_MUZZLEFLASH, "grapple_muzzleflash") \ + EFFECT(0, EFFECT_HOOK_IMPACT, "grapple_impact") \ + EFFECT(0, EFFECT_ROCKET_EXPLODE, "rocket_explode") \ + EFFECT(0, EFFECT_ROCKET_GUIDE, "rocket_guide") \ + EFFECT(0, EFFECT_ROCKET_MUZZLEFLASH, "rocketlauncher_muzzleflash") \ + EFFECT(0, EFFECT_FIREBALL_LASER, "fireball_laser") \ + EFFECT(0, EFFECT_FIREBALL_EXPLODE, "fireball_explode") \ + EFFECT(0, EFFECT_FIREBALL_BFGDAMAGE, "fireball_bfgdamage") \ + EFFECT(0, EFFECT_FIREBALL_MUZZLEFLASH, "fireball_muzzleflash") \ + EFFECT(0, EFFECT_FIREBALL_PRE_MUZZLEFLASH, "fireball_preattack_muzzleflash") \ + EFFECT(0, EFFECT_TELEPORT, "teleport") \ + EFFECT(0, EFFECT_SPAWN_RED, "spawn_event_red") \ + EFFECT(0, EFFECT_SPAWN_BLUE, "spawn_event_blue") \ + EFFECT(0, EFFECT_SPAWN_YELLOW, "spawn_event_yellow") \ + EFFECT(0, EFFECT_SPAWN_PINK, "spawn_event_pink") \ + EFFECT(0, EFFECT_SPAWN_NEUTRAL, "spawn_event_neutral") \ + EFFECT(0, EFFECT_NADE_RED_EXPLODE, "nade_red_explode") \ + EFFECT(0, EFFECT_NADE_BLUE_EXPLODE, "nade_blue_explode") \ + EFFECT(0, EFFECT_NADE_YELLOW_EXPLODE, "nade_yellow_explode") \ + EFFECT(0, EFFECT_NADE_PINK_EXPLODE, "nade_pink_explode") \ + EFFECT(0, EFFECT_NADE_NEUTRAL_EXPLODE, "nade_neutral_explode") \ + EFFECT(0, EFFECT_ICEORGLASS, "iceorglass") \ + EFFECT(0, EFFECT_ICEFIELD, "icefield") \ + EFFECT(0, EFFECT_FIREFIELD, "firefield") \ + EFFECT(0, EFFECT_HEALING, "healing_fx") \ + EFFECT(1, EFFECT_LASER_BEAM_FAST, "nex242_misc_laser_beam_fast") \ + EFFECT(0, EFFECT_RESPAWN_GHOST, "respawn_ghost") \ + EFFECT(0, EFFECT_FLAG_RED_TOUCH, "redflag_touch") \ + EFFECT(0, EFFECT_FLAG_BLUE_TOUCH, "blueflag_touch") \ + EFFECT(0, EFFECT_FLAG_YELLOW_TOUCH, "yellowflag_touch") \ + EFFECT(0, EFFECT_FLAG_PINK_TOUCH, "pinkflag_touch") \ + EFFECT(0, EFFECT_FLAG_NEUTRAL_TOUCH, "neutralflag_touch") \ + EFFECT(1, EFFECT_RED_PASS, "red_pass") \ + EFFECT(1, EFFECT_BLUE_PASS, "blue_pass") \ + EFFECT(1, EFFECT_YELLOW_PASS, "yellow_pass") \ + EFFECT(1, EFFECT_PINK_PASS, "pink_pass") \ + EFFECT(1, EFFECT_NEUTRAL_PASS, "neutral_pass") \ + EFFECT(0, EFFECT_RED_CAP, "red_cap") \ + EFFECT(0, EFFECT_BLUE_CAP, "blue_cap") \ + EFFECT(0, EFFECT_YELLOW_CAP, "yellow_cap") \ + EFFECT(0, EFFECT_PINK_CAP, "pink_cap") \ + EFFECT(0, EFFECT_BALL_SPARKS, "kaball_sparks") \ + EFFECT(0, EFFECT_ELECTRIC_SPARKS, "electricity_sparks") \ + EFFECT(0, EFFECT_SPARKS, "sparks") \ + EFFECT(0, EFFECT_RAGE, "rage") \ + EFFECT(0, EFFECT_SMOKING, "smoking") \ + EFFECT(0, EFFECT_SMOKE_RING, "smoke_ring") \ + EFFECT(0, EFFECT_ITEM_PICKUP, "item_pickup") \ + EFFECT(0, EFFECT_ITEM_RESPAWN, "item_respawn") \ + EFFECT(0, EFFECT_JUMPPAD, "jumppad_activate") \ + EFFECT(1, EFFECT_BULLET, "tr_bullet") + + + + +// -------------------- +// -------------------------- +// ----------------------------------- +// ------------------------------------------| +// some stuff you don't need to care about...| +// ------------------------------------------| +// ----------------------------------- +// -------------------------- +// -------------------- + - .float eent_net_name; // id ++.int eent_net_name; // id +.vector eent_net_location; +.vector eent_net_velocity; - .float eent_eff_trail; ++.int eent_eff_trail; +.string eent_eff_name; - .float eent_net_count; ++.int eent_net_count; + +#ifdef CSQC - void Read_Effect(float is_new); ++void Read_Effect(bool is_new); +#endif + +#ifdef SVQC - void Send_Effect(float eff_name, vector eff_loc, vector eff_vel, float eff_cnt); ++void Send_Effect(int eff_name, vector eff_loc, vector eff_vel, float eff_cnt); +#endif + - #define EFFECT_FIRST 1 - float EFFECT_COUNT; ++const int EFFECT_FIRST = 1; ++int EFFECT_COUNT; + - #define MAX_EFFECTS 512 ++const int MAX_EFFECTS = 512; +entity effects_ent[MAX_EFFECTS]; + - void Create_Effect_Entity(float eff_name, string eff_string, float eff_trail); ++void Create_Effect_Entity(int eff_name, string eff_string, int eff_trail); + +#define EFFECT(istrail,name,realname) \ - float name; \ ++ int name; \ + void RegisterEffect_##name() \ + { \ + SET_FIELD_COUNT(name, EFFECT_FIRST, EFFECT_COUNT) \ + CHECK_MAX_COUNT(name, MAX_EFFECTS, EFFECT_COUNT, "EFFECT") \ + Create_Effect_Entity(name, realname, istrail); \ + } \ + ACCUMULATE_FUNCTION(RegisterEffects, RegisterEffect_##name); + +void RegisterEffects_First() +{ + #ifdef SVQC + #define dedi (server_is_dedicated ? "a dedicated " : "") + #else + #define dedi "" + #endif + + printf("Beginning effect initialization on %s%s program...\n", dedi, PROGNAME); + #undef dedi +} + +void RegisterEffects_Done() +{ + print("Effects initialization successful!\n"); +} + +// NOW we actually activate the declarations +ACCUMULATE_FUNCTION(RegisterEffects, RegisterEffects_First); +EFFECTS +ACCUMULATE_FUNCTION(RegisterEffects, RegisterEffects_Done); +#undef EFFECT ++ ++#endif diff --cc qcsrc/common/jeff.qh index f3da8ccb4,000000000..15bd633b7 mode 100644,000000..100644 --- a/qcsrc/common/jeff.qh +++ b/qcsrc/common/jeff.qh @@@ -1,49 -1,0 +1,52 @@@ - // sadly, we can't put this stuff inside an #ifdef, as it is always called ++#ifndef JEFF_H ++#define JEFF_H + +#define JEFF_ANCE_NOTIF \ + MSG_ANNCE_NOTIF(1, ANNCE_JEFF_BIOHAZARD, CH_INFO, "biohazard", VOL_BASEVOICE, ATTEN_NONE) \ + MSG_ANNCE_NOTIF(1, ANNCE_JEFF_BLAZEOFGLORY, CH_INFO, "blazeofglory", VOL_BASEVOICE, ATTEN_NONE) \ + MSG_ANNCE_NOTIF(1, ANNCE_JEFF_BLUESTREAK, CH_INFO, "bluestreak", VOL_BASEVOICE, ATTEN_NONE) \ + MSG_ANNCE_NOTIF(1, ANNCE_JEFF_BOTTOMFEEDER, CH_INFO, "bottomfeeder", VOL_BASEVOICE, ATTEN_NONE) \ + MSG_ANNCE_NOTIF(1, ANNCE_JEFF_BULLSEYE, CH_INFO, "bullseye", VOL_BASEVOICE, ATTEN_NONE) \ + MSG_ANNCE_NOTIF(1, ANNCE_JEFF_COMBOKING, CH_INFO, "comboking", VOL_BASEVOICE, ATTEN_NONE) \ + MSG_ANNCE_NOTIF(1, ANNCE_JEFF_DENIED, CH_INFO, "denied", VOL_BASEVOICE, ATTEN_NONE) \ + MSG_ANNCE_NOTIF(1, ANNCE_JEFF_DOUBLEKILL, CH_INFO, "doublekill", VOL_BASEVOICE, ATTEN_NONE) \ + MSG_ANNCE_NOTIF(1, ANNCE_JEFF_EAGLEEYE, CH_INFO, "eagleeye", VOL_BASEVOICE, ATTEN_NONE) \ + MSG_ANNCE_NOTIF(1, ANNCE_JEFF_FIRSTBLOOD, CH_INFO, "firstblood", VOL_BASEVOICE, ATTEN_NONE) \ + MSG_ANNCE_NOTIF(1, ANNCE_JEFF_FLAKMASTER, CH_INFO, "flakmaster", VOL_BASEVOICE, ATTEN_NONE) \ + MSG_ANNCE_NOTIF(1, ANNCE_JEFF_GAMEOVER, CH_INFO, "gameover", VOL_BASEVOICE, ATTEN_NONE) \ + MSG_ANNCE_NOTIF(1, ANNCE_JEFF_GODLIKE, CH_INFO, "godlike", VOL_BASEVOICE, ATTEN_NONE) \ + MSG_ANNCE_NOTIF(1, ANNCE_JEFF_GUNSLINGER, CH_INFO, "gunslinger", VOL_BASEVOICE, ATTEN_NONE) \ + MSG_ANNCE_NOTIF(1, ANNCE_JEFF_HATTRICK, CH_INFO, "hattrick", VOL_BASEVOICE, ATTEN_NONE) \ + MSG_ANNCE_NOTIF(1, ANNCE_JEFF_HIJACKED, CH_INFO, "hijacked", VOL_BASEVOICE, ATTEN_NONE) \ + MSG_ANNCE_NOTIF(1, ANNCE_JEFF_HITANDRUN, CH_INFO, "hitandrun", VOL_BASEVOICE, ATTEN_NONE) \ + MSG_ANNCE_NOTIF(1, ANNCE_JEFF_HOLY, CH_INFO, "holyshit", VOL_BASEVOICE, ATTEN_NONE) \ + MSG_ANNCE_NOTIF(1, ANNCE_JEFF_HUMILIATION, CH_INFO, "humiliation", VOL_BASEVOICE, ATTEN_NONE) \ + MSG_ANNCE_NOTIF(1, ANNCE_JEFF_JACKHAMMER, CH_INFO, "jackhammer", VOL_BASEVOICE, ATTEN_NONE) \ + MSG_ANNCE_NOTIF(1, ANNCE_JEFF_JUGGERNAUT, CH_INFO, "juggernaut", VOL_BASEVOICE, ATTEN_NONE) \ + MSG_ANNCE_NOTIF(1, ANNCE_JEFF_LUDICROUSKILL, CH_INFO, "ludicrouskill", VOL_BASEVOICE, ATTEN_NONE) \ + MSG_ANNCE_NOTIF(1, ANNCE_JEFF_MANSLAUGHTER, CH_INFO, "vehicularmanslaughter", VOL_BASEVOICE, ATTEN_NONE) \ + MSG_ANNCE_NOTIF(1, ANNCE_JEFF_MEGAKILL, CH_INFO, "megakill", VOL_BASEVOICE, ATTEN_NONE) \ + MSG_ANNCE_NOTIF(1, ANNCE_JEFF_MONSTERKILL, CH_INFO, "monsterkill", VOL_BASEVOICE, ATTEN_NONE) \ + MSG_ANNCE_NOTIF(1, ANNCE_JEFF_MULTIKILL, CH_INFO, "multikill", VOL_BASEVOICE, ATTEN_NONE) \ + MSG_ANNCE_NOTIF(1, ANNCE_JEFF_MUTDESTRUCT, CH_INFO, "mutualdestruction", VOL_BASEVOICE, ATTEN_NONE) \ + MSG_ANNCE_NOTIF(1, ANNCE_JEFF_NODEBUSTER, CH_INFO, "nodebuster", VOL_BASEVOICE, ATTEN_NONE) \ + MSG_ANNCE_NOTIF(1, ANNCE_JEFF_OUTSTANDING, CH_INFO, "outstanding", VOL_BASEVOICE, ATTEN_NONE) \ + MSG_ANNCE_NOTIF(1, ANNCE_JEFF_NUKEMHOLY, CH_INFO, "nukemholy", VOL_BASEVOICE, ATTEN_NONE) \ + MSG_ANNCE_NOTIF(1, ANNCE_JEFF_PANCAKE, CH_INFO, "pancake", VOL_BASEVOICE, ATTEN_NONE) \ + MSG_ANNCE_NOTIF(1, ANNCE_JEFF_PAYBACK, CH_INFO, "payback", VOL_BASEVOICE, ATTEN_NONE) \ + MSG_ANNCE_NOTIF(1, ANNCE_JEFF_REJECTED, CH_INFO, "rejected", VOL_BASEVOICE, ATTEN_NONE) \ + MSG_ANNCE_NOTIF(1, ANNCE_JEFF_RETRIBUTION, CH_INFO, "retribution", VOL_BASEVOICE, ATTEN_NONE) \ + MSG_ANNCE_NOTIF(1, ANNCE_JEFF_ROCKETSCIENTIST, CH_INFO, "rocketscientist", VOL_BASEVOICE, ATTEN_NONE) \ + MSG_ANNCE_NOTIF(1, ANNCE_JEFF_ROADKILL, CH_INFO, "roadkill", VOL_BASEVOICE, ATTEN_NONE) \ + MSG_ANNCE_NOTIF(1, ANNCE_JEFF_ROADRAGE, CH_INFO, "roadrage", VOL_BASEVOICE, ATTEN_NONE) \ + MSG_ANNCE_NOTIF(1, ANNCE_JEFF_ROADRAMPAGE, CH_INFO, "roadrampage", VOL_BASEVOICE, ATTEN_NONE) \ + MSG_ANNCE_NOTIF(1, ANNCE_JEFF_SHAFTMASTER, CH_INFO, "shaftmaster", VOL_BASEVOICE, ATTEN_NONE) \ + MSG_ANNCE_NOTIF(1, ANNCE_JEFF_SLACKER, CH_INFO, "slacker", VOL_BASEVOICE, ATTEN_NONE) \ + MSG_ANNCE_NOTIF(1, ANNCE_JEFF_SPEED, CH_INFO, "speed", VOL_BASEVOICE, ATTEN_NONE) \ + MSG_ANNCE_NOTIF(1, ANNCE_JEFF_TEAMKILLER, CH_INFO, "teamkiller", VOL_BASEVOICE, ATTEN_NONE) \ + MSG_ANNCE_NOTIF(1, ANNCE_JEFF_TOPGUN, CH_INFO, "topgun", VOL_BASEVOICE, ATTEN_NONE) \ + MSG_ANNCE_NOTIF(1, ANNCE_JEFF_ULTRAKILL, CH_INFO, "ultrakill", VOL_BASEVOICE, ATTEN_NONE) \ + MSG_ANNCE_NOTIF(1, ANNCE_JEFF_VENGEANCE, CH_INFO, "vengeance", VOL_BASEVOICE, ATTEN_NONE) \ + MSG_ANNCE_NOTIF(1, ANNCE_JEFF_WICKEDSICK, CH_INFO, "wickedsick", VOL_BASEVOICE, ATTEN_NONE) ++ ++#endif diff --cc qcsrc/common/mapinfo.qh index c431bf53b,6083350fa..adb9e19f8 --- a/qcsrc/common/mapinfo.qh +++ b/qcsrc/common/mapinfo.qh @@@ -37,72 -40,60 +40,72 @@@ entity MapInfo_Type_last #define IS_GAMETYPE(NAME) \ (MapInfo_LoadedGametype == MAPINFO_TYPE_##NAME) - REGISTER_GAMETYPE(_("Deathmatch"),dm,g_dm,DEATHMATCH,FALSE,"timelimit=20 pointlimit=30 leadlimit=0",_("Kill all enemies")); + REGISTER_GAMETYPE(_("Deathmatch"),dm,g_dm,DEATHMATCH,false,"timelimit=20 pointlimit=30 leadlimit=0",_("Kill all enemies")); #define g_dm IS_GAMETYPE(DEATHMATCH) - REGISTER_GAMETYPE(_("Last Man Standing"),lms,g_lms,LMS,FALSE,"timelimit=20 lives=9 leadlimit=0",_("Survive and kill until the enemies have no lives left")); + REGISTER_GAMETYPE(_("Last Man Standing"),lms,g_lms,LMS,false,"timelimit=20 lives=9 leadlimit=0",_("Survive and kill until the enemies have no lives left")); #define g_lms IS_GAMETYPE(LMS) - REGISTER_GAMETYPE(_("Race"),rc,g_race,RACE,FALSE,"timelimit=20 qualifying_timelimit=5 laplimit=7 teamlaplimit=15 leadlimit=0",_("Race against other players to the finish line")); + REGISTER_GAMETYPE(_("Race"),rc,g_race,RACE,false,"timelimit=20 qualifying_timelimit=5 laplimit=7 teamlaplimit=15 leadlimit=0",_("Race against other players to the finish line")); #define g_race IS_GAMETYPE(RACE) - REGISTER_GAMETYPE(_("Race CTS"),cts,g_cts,CTS,FALSE,"timelimit=20 skill=-1",_("Race for fastest time")); + REGISTER_GAMETYPE(_("Race CTS"),cts,g_cts,CTS,false,"timelimit=20 skill=-1",_("Race for fastest time")); #define g_cts IS_GAMETYPE(CTS) - REGISTER_GAMETYPE(_("Team Deathmatch"),tdm,g_tdm,TEAM_DEATHMATCH,TRUE,"timelimit=20 pointlimit=50 teams=2 leadlimit=0",_("Kill all enemy teammates")); + REGISTER_GAMETYPE(_("Team Deathmatch"),tdm,g_tdm,TEAM_DEATHMATCH,true,"timelimit=20 pointlimit=50 teams=2 leadlimit=0",_("Kill all enemy teammates")); #define g_tdm IS_GAMETYPE(TEAM_DEATHMATCH) - REGISTER_GAMETYPE(_("Capture the Flag"),ctf,g_ctf,CTF,TRUE,"timelimit=20 caplimit=10 leadlimit=6",_("Find and bring the enemy flag to your base to capture it")); + REGISTER_GAMETYPE(_("Capture the Flag"),ctf,g_ctf,CTF,true,"timelimit=20 caplimit=10 leadlimit=6",_("Find and bring the enemy flag to your base to capture it")); #define g_ctf IS_GAMETYPE(CTF) - REGISTER_GAMETYPE(_("Clan Arena"),ca,g_ca,CA,TRUE,"timelimit=20 pointlimit=10 teams=2 leadlimit=0",_("Kill all enemy teammates to win the round")); + REGISTER_GAMETYPE(_("Clan Arena"),ca,g_ca,CA,true,"timelimit=20 pointlimit=10 teams=2 leadlimit=0",_("Kill all enemy teammates to win the round")); #define g_ca IS_GAMETYPE(CA) - REGISTER_GAMETYPE(_("Domination"),dom,g_domination,DOMINATION,TRUE,"timelimit=20 pointlimit=200 teams=2 leadlimit=0",_("Capture all the control points to win")); + REGISTER_GAMETYPE(_("Domination"),dom,g_domination,DOMINATION,true,"timelimit=20 pointlimit=200 teams=2 leadlimit=0",_("Capture all the control points to win")); #define g_domination IS_GAMETYPE(DOMINATION) - REGISTER_GAMETYPE(_("Key Hunt"),kh,g_keyhunt,KEYHUNT,TRUE,"timelimit=20 pointlimit=10 teams=3 leadlimit=0",_("Gather all the keys to win the round")); -REGISTER_GAMETYPE(_("Key Hunt"),kh,g_keyhunt,KEYHUNT,true,"timelimit=20 pointlimit=1000 teams=3 leadlimit=0",_("Gather all the keys to win the round")); ++REGISTER_GAMETYPE(_("Key Hunt"),kh,g_keyhunt,KEYHUNT,true,"timelimit=20 pointlimit=10 teams=3 leadlimit=0",_("Gather all the keys to win the round")); #define g_keyhunt IS_GAMETYPE(KEYHUNT) - REGISTER_GAMETYPE(_("Assault"),as,g_assault,ASSAULT,TRUE,"timelimit=20",_("Destroy obstacles to find and destroy the enemy power core before time runs out")); + REGISTER_GAMETYPE(_("Assault"),as,g_assault,ASSAULT,true,"timelimit=20",_("Destroy obstacles to find and destroy the enemy power core before time runs out")); #define g_assault IS_GAMETYPE(ASSAULT) - REGISTER_GAMETYPE(_("Onslaught"),ons,g_onslaught,ONSLAUGHT,TRUE,"pointlimit=1 timelimit=30",_("Capture control points to reach and destroy the enemy generator")); -REGISTER_GAMETYPE(_("Onslaught"),ons,g_onslaught,ONSLAUGHT,true,"timelimit=20",_("Capture control points to reach and destroy the enemy generator")); ++REGISTER_GAMETYPE(_("Onslaught"),ons,g_onslaught,ONSLAUGHT,true,"pointlimit=1 timelimit=30",_("Capture control points to reach and destroy the enemy generator")); #define g_onslaught IS_GAMETYPE(ONSLAUGHT) - REGISTER_GAMETYPE(_("Nexball"),nb,g_nexball,NEXBALL,TRUE,"timelimit=20 pointlimit=5 leadlimit=0",_("XonSports")); + REGISTER_GAMETYPE(_("Nexball"),nb,g_nexball,NEXBALL,true,"timelimit=20 pointlimit=5 leadlimit=0",_("XonSports")); #define g_nexball IS_GAMETYPE(NEXBALL) - REGISTER_GAMETYPE(_("Freeze Tag"),ft,g_freezetag,FREEZETAG,TRUE,"timelimit=20 pointlimit=10 teams=2 leadlimit=0",_("Kill enemies to freeze them, stand next to teammates to revive them")); + REGISTER_GAMETYPE(_("Freeze Tag"),ft,g_freezetag,FREEZETAG,true,"timelimit=20 pointlimit=10 teams=2 leadlimit=0",_("Kill enemies to freeze them, stand next to teammates to revive them")); #define g_freezetag IS_GAMETYPE(FREEZETAG) - REGISTER_GAMETYPE(_("Keepaway"),ka,g_keepaway,KEEPAWAY,TRUE,"timelimit=20 pointlimit=30",_("Hold the ball to get points for kills")); + REGISTER_GAMETYPE(_("Keepaway"),ka,g_keepaway,KEEPAWAY,true,"timelimit=20 pointlimit=30",_("Hold the ball to get points for kills")); #define g_keepaway IS_GAMETYPE(KEEPAWAY) - REGISTER_GAMETYPE(_("VIP"),vip,g_vip,VIP,TRUE,"timelimit=20 pointlimit=10 leadlimit=0",_("A VIP is chosen on each team, when a VIP dies, the round is over")); -REGISTER_GAMETYPE(_("Invasion"),inv,g_invasion,INVASION,false,"pointlimit=50 teams=0",_("Survive against waves of monsters")); ++REGISTER_GAMETYPE(_("VIP"),vip,g_vip,VIP,true,"timelimit=20 pointlimit=10 leadlimit=0",_("A VIP is chosen on each team, when a VIP dies, the round is over")); +#define g_vip IS_GAMETYPE(VIP) + - REGISTER_GAMETYPE(_("Invasion"),inv,g_invasion,INVASION,TRUE,"pointlimit=50 teams=0",_("Survive against waves of monsters")); ++REGISTER_GAMETYPE(_("Invasion"),inv,g_invasion,INVASION,true,"pointlimit=50 teams=0",_("Survive against waves of monsters")); #define g_invasion IS_GAMETYPE(INVASION) - REGISTER_GAMETYPE(_("Conquest"),cq,g_conquest,CONQUEST,TRUE,"timelimit=20 pointlimit=1 teams=2",_("Capture all the spawnpoint control points to win")) ++REGISTER_GAMETYPE(_("Conquest"),cq,g_conquest,CONQUEST,true,"timelimit=20 pointlimit=1 teams=2",_("Capture all the spawnpoint control points to win")) +#define g_conquest IS_GAMETYPE(CONQUEST) + - REGISTER_GAMETYPE(_("Infection"),inf,g_infection,INFECTION,TRUE,"timelimit=20 pointlimit=10 leadlimit=0",_("Kill enemies to 'infect' them with your team color")); ++REGISTER_GAMETYPE(_("Infection"),inf,g_infection,INFECTION,true,"timelimit=20 pointlimit=10 leadlimit=0",_("Kill enemies to 'infect' them with your team color")); +#define g_infection IS_GAMETYPE(INFECTION) + - REGISTER_GAMETYPE(_("Jailbreak"),jb,g_jailbreak,JAILBREAK,TRUE,"teams=2 timelimit=20 pointlimit=5 leadlimit=0",_("Kill enemies to send them to jail, capture control points to release teammates")); ++REGISTER_GAMETYPE(_("Jailbreak"),jb,g_jailbreak,JAILBREAK,true,"teams=2 timelimit=20 pointlimit=5 leadlimit=0",_("Kill enemies to send them to jail, capture control points to release teammates")); +#define g_jailbreak IS_GAMETYPE(JAILBREAK) + - const float MAPINFO_FEATURE_WEAPONS = 1; // not defined for instagib-only maps - const float MAPINFO_FEATURE_VEHICLES = 2; - const float MAPINFO_FEATURE_TURRETS = 4; - const float MAPINFO_FEATURE_MONSTERS = 8; + const int MAPINFO_FEATURE_WEAPONS = 1; // not defined for instagib-only maps + const int MAPINFO_FEATURE_VEHICLES = 2; + const int MAPINFO_FEATURE_TURRETS = 4; + const int MAPINFO_FEATURE_MONSTERS = 8; - const float MAPINFO_FLAG_HIDDEN = 1; // not in lsmaps/menu/vcall/etc., can just be changed to manually - const float MAPINFO_FLAG_FORBIDDEN = 2; // don't even allow the map by a cvar setting that allows hidden maps - const float MAPINFO_FLAG_FRUSTRATING = 4; // this map is near impossible to play, enable at your own risk - const float MAPINFO_FLAG_NOAUTOMAPLIST = 8; // do not include when automatically building maplist (counts as hidden for maplist building purposes) + const int MAPINFO_FLAG_HIDDEN = 1; // not in lsmaps/menu/vcall/etc., can just be changed to manually + const int MAPINFO_FLAG_FORBIDDEN = 2; // don't even allow the map by a cvar setting that allows hidden maps + const int MAPINFO_FLAG_FRUSTRATING = 4; // this map is near impossible to play, enable at your own risk + const int MAPINFO_FLAG_NOAUTOMAPLIST = 8; // do not include when automatically building maplist (counts as hidden for maplist building purposes) float MapInfo_count; diff --cc qcsrc/common/minigames/cl_minigames.qc index cb7345260,000000000..f6af765b7 mode 100644,000000..100644 --- a/qcsrc/common/minigames/cl_minigames.qc +++ b/qcsrc/common/minigames/cl_minigames.qc @@@ -1,399 -1,0 +1,401 @@@ ++#include "cl_minigames.qh" ++ +// Draw a square in the center of the avaliable area +void minigame_hud_simpleboard(vector pos, vector mySize, string board_texture) +{ + if(panel.current_panel_bg != "0" && panel.current_panel_bg != "") + draw_BorderPicture(pos - '1 1 0' * panel_bg_border, + panel.current_panel_bg, + mySize + '1 1 0' * 2 * panel_bg_border, + panel_bg_color, panel_bg_alpha, + '1 1 0' * (panel_bg_border/BORDER_MULTIPLIER)); + drawpic(pos, board_texture, mySize, '1 1 1', panel_bg_alpha, DRAWFLAG_NORMAL); +} + +// De-normalize (2D vector) v from relative coordinate inside pos mySize +vector minigame_hud_denormalize(vector v, vector pos, vector mySize) +{ + v_x = pos_x + v_x * mySize_x; + v_y = pos_y + v_y * mySize_y; + return v; +} +// De-normalize (2D vector) v from relative size inside pos mySize +vector minigame_hud_denormalize_size(vector v, vector pos, vector mySize) +{ + v_x = v_x * mySize_x; + v_y = v_y * mySize_y; + return v; +} + +// Normalize (2D vector) v to relative coordinate inside pos mySize +vector minigame_hud_normalize(vector v, vector pos, vector mySize) +{ + v_x = ( v_x - pos_x ) / mySize_x; + v_y = ( v_y - pos_y ) / mySize_y; + return v; +} + +// Check if the mouse is inside the given area +float minigame_hud_mouse_in(vector pos, vector sz) +{ + return mousepos_x >= pos_x && mousepos_x < pos_x + sz_x && + mousepos_y >= pos_y && mousepos_y < pos_y + sz_y ; +} + +void initialize_minigames() +{ + entity last_minig = world; + entity minig; + #define MINIGAME(name,nicename) \ + minig = spawn(); \ + minig.classname = "minigame_descriptor"; \ + minig.netname = strzone(strtolower(#name)); \ + minig.message = nicename; \ + minig.minigame_hud_board = minigame_hud_board_##name; \ + minig.minigame_hud_status = minigame_hud_status_##name; \ + minig.minigame_event = minigame_event_##name; \ + if ( !last_minig ) minigame_descriptors = minig; \ + else last_minig.list_next = minig; \ + last_minig = minig; + + REGISTERED_MINIGAMES + + #undef MINIGAME +} + +string minigame_texture_skin(string skinname, string name) +{ + return sprintf("gfx/hud/%s/minigames/%s", skinname, name); +} +string minigame_texture(string name) +{ + string path = minigame_texture_skin(autocvar_menu_skin,name); + if ( precache_pic(path) == "" ) + path = minigame_texture_skin("default", name); + return path; +} + +#define FIELD(Flags, Type, Name) MSLE_CLEAN_##Type(self.Name) +#define MSLE_CLEAN_String(x) strunzone(x); +#define MSLE_CLEAN_Byte(x) +#define MSLE_CLEAN_Char(x) +#define MSLE_CLEAN_Short(x) +#define MSLE_CLEAN_Coord(x) +#define MSLE_CLEAN_Angle(x) +#define MSLE_CLEAN_Float(x) +#define MSLE_CLEAN_Vector(x) +#define MSLE_CLEAN_Vector2D(x) + +#define MSLE(Name,Fields) \ + void msle_entremove_##Name() { strunzone(self.netname); Fields } +MINIGAME_SIMPLELINKED_ENTITIES +#undef MSLE +#undef FIELD + +void minigame_autoclean_entity(entity e) +{ + dprint("CL Auto-cleaned: ",ftos(num_for_edict(e)), " (",e.classname,")\n"); + remove(e); +} + +void HUD_MinigameMenu_CurrentButton(); +float auto_close_minigamemenu; +void deactivate_minigame() +{ + if ( !active_minigame ) + return; + + active_minigame.minigame_event(active_minigame,"deactivate"); + entity e = world; + while( (e = findentity(e, owner, self)) ) + if ( e.minigame_autoclean ) + { + minigame_autoclean_entity(e); + } + + minigame_self = world; + active_minigame = world; + + if ( auto_close_minigamemenu ) + { + HUD_MinigameMenu_Close(); + auto_close_minigamemenu = 0; + } + else + HUD_MinigameMenu_CurrentButton(); +} + +void activate_minigame(entity minigame) +{ + if ( !minigame ) + { + deactivate_minigame(); + return; + } + + if ( !minigame.descriptor || minigame.classname != "minigame" ) + { + dprint("Trying to activate unregistered minigame ",minigame.netname," in client\n"); + return; + } + + if ( minigame == active_minigame ) + return; + + if ( active_minigame ) + { + entity olds = minigame_self; + deactivate_minigame(); + minigame_self = olds; + } + + if ( minigame_self.owner != minigame ) + minigame_self = world; + active_minigame = minigame; + active_minigame.minigame_event(active_minigame,"activate"); + + if ( HUD_MinigameMenu_IsOpened() ) + HUD_MinigameMenu_CurrentButton(); + else + { + auto_close_minigamemenu = 1; + HUD_MinigameMenu_Open(); + } +} + +void minigame_player_entremove() +{ + if ( self.owner == active_minigame && self.minigame_playerslot == player_localentnum ) + deactivate_minigame(); +} + +vector ReadVector2D() { vector v; v_x = ReadCoord(); v_y = ReadCoord(); v_z = 0; return v; } +vector ReadVector() { vector v; v_x = ReadCoord(); v_y = ReadCoord(); v_z = ReadCoord(); return v; } +string() ReadString_Raw = #366; +string ReadString_Zoned() { return strzone(ReadString_Raw()); } +#define ReadFloat ReadCoord +#define ReadString ReadString_Zoned +#define FIELD(Flags, Type,Name) if ( sf & (Flags) ) self.Name = Read##Type(); +#define MSLE(Name,Fields) \ + else if ( self.classname == #Name ) { \ + if ( sf & MINIG_SF_CREATE ) { \ + minigame_read_owner(); \ + self.entremove = msle_entremove_##Name; \ + } \ + minigame_ent = self.owner; \ + Fields \ + } +void minigame_read_owner() +{ + string owner_name = ReadString_Raw(); + self.owner = world; + do + self.owner = find(self.owner,netname,owner_name); + while ( self.owner && self.owner.classname != "minigame" ); + if ( !self.owner ) + dprint("Got a minigame entity without a minigame!\n"); +} +void ent_read_minigame() +{ + float sf = ReadByte(); + if ( sf & MINIG_SF_CREATE ) + { + self.classname = msle_classname(ReadShort()); + self.netname = ReadString_Zoned(); + } + + entity minigame_ent = world; + + if ( self.classname == "minigame" ) + { + minigame_ent = self; + + if ( sf & MINIG_SF_CREATE ) + { + self.entremove = deactivate_minigame; + self.descriptor = minigame_get_descriptor(ReadString_Raw()); + if ( !self.descriptor ) + dprint("Got a minigame without a client-side descriptor!\n"); + else + self.minigame_event = self.descriptor.minigame_event; + } + if ( sf & MINIG_SF_UPDATE ) + self.minigame_flags = ReadLong(); + } + else if ( self.classname == "minigame_player" ) + { + float activate = 0; + if ( sf & MINIG_SF_CREATE ) + { + self.entremove = minigame_player_entremove; + minigame_read_owner(); + float ent = ReadLong(); + self.minigame_playerslot = ent; + dprint("Player: ",GetPlayerName(ent-1),"\n"); + + activate = (ent == player_localnum+1 && self.owner && self.owner != active_minigame); + + } + minigame_ent = self.owner; + + if ( sf & MINIG_SF_UPDATE ) + self.team = ReadByte(); + + if ( activate ) + { + minigame_self = self; + activate_minigame(self.owner); + } + } + MINIGAME_SIMPLELINKED_ENTITIES + + if ( minigame_ent ) + minigame_ent.minigame_event(minigame_ent,"network_receive",self,sf); + + dprint("CL Reading entity: ",ftos(num_for_edict(self)), + " classname:",self.classname," enttype:",ftos(self.enttype) ); + dprint(" sf:",ftos(sf)," netname:",self.netname,"\n\n"); +} +#undef ReadFloat +#undef ReadString +#undef FIELD +#undef MSLE + +string minigame_getWrappedLine(float w, vector theFontSize, textLengthUpToWidth_widthFunction_t tw) +{ + float last_word; + string s; + float take_until; + float skip = 0; + + s = getWrappedLine_remaining; + + if(w <= 0) + { + getWrappedLine_remaining = string_null; + return s; // the line has no size ANYWAY, nothing would be displayed. + } + + take_until = textLengthUpToWidth(s, w, theFontSize, tw); + + if ( take_until > strlen(s) ) + take_until = strlen(s); + + float i; + for ( i = 0; i < take_until; i++ ) + if ( substring(s,i,1) == "\n" ) + { + take_until = i; + skip = 1; + break; + } + + if ( take_until > 0 || skip > 0 ) + { + if ( skip == 0 && take_until < strlen(s) ) + { + last_word = take_until; + while(last_word > 0 && substring(s, last_word, 1) != " ") + --last_word; + + if ( last_word != 0 ) + { + take_until = last_word; + skip = 1; + } + } + + getWrappedLine_remaining = substring(s, take_until+skip, strlen(s) - (take_until+skip)); + if(getWrappedLine_remaining == "") + getWrappedLine_remaining = string_null; + else if (tw("^7", theFontSize) == 0) + getWrappedLine_remaining = strcat(find_last_color_code(substring(s, 0, take_until)), getWrappedLine_remaining); + return substring(s, 0, take_until); + } + else + { + getWrappedLine_remaining = string_null; + return s; + } +} + +vector minigame_drawstring_wrapped( float maxwidth, vector pos, string text, + vector fontsize, vector color, float theAlpha, float drawflags, float align ) +{ + getWrappedLine_remaining = text; + vector mypos = pos; + while ( getWrappedLine_remaining ) + { + string line = minigame_getWrappedLine(maxwidth,fontsize,stringwidth_nocolors); + if ( line == "" ) + break; + mypos_x = pos_x + (maxwidth - stringwidth_nocolors(line, fontsize)) * align; + drawstring(mypos, line, fontsize, color, theAlpha, drawflags); + mypos_y += fontsize_y; + } + mypos_x = maxwidth; + mypos_y -= pos_y; + return mypos; +} + +vector minigame_drawcolorcodedstring_wrapped( float maxwidth, vector pos, + string text, vector fontsize, float theAlpha, float drawflags, float align ) +{ + getWrappedLine_remaining = text; + vector mypos = pos; + while ( getWrappedLine_remaining ) + { + string line = minigame_getWrappedLine(maxwidth,fontsize,stringwidth_colors); + if ( line == "" ) + break; + mypos_x = pos_x + (maxwidth - stringwidth_colors(line, fontsize)) * align; + drawcolorcodedstring(mypos, line, fontsize, theAlpha, drawflags); + mypos_y += fontsize_y; + } + mypos_x = maxwidth; + mypos_y -= pos_y; + return mypos; +} + +void minigame_drawstring_trunc(float maxwidth, vector pos, string text, + vector fontsize, vector color, float theAlpha, float drawflags ) +{ + string line = textShortenToWidth(text,maxwidth,fontsize,stringwidth_nocolors); + drawstring(pos, line, fontsize, color, theAlpha, drawflags); +} + +void minigame_drawcolorcodedstring_trunc(float maxwidth, vector pos, string text, + vector fontsize, float theAlpha, float drawflags ) +{ + string line = textShortenToWidth(text,maxwidth,fontsize,stringwidth_colors); + drawcolorcodedstring(pos, line, fontsize, theAlpha, drawflags); +} + +void minigame_drawpic_centered( vector pos, string texture, vector sz, + vector color, float thealpha, float drawflags ) +{ + drawpic( pos-sz/2, texture, sz, color, thealpha, drawflags ); +} + +// Workaround because otherwise variadic arguments won't work properly +// It could be a bug in the compiler or in darkplaces +void minigame_cmd_workaround(float dummy, string...cmdargc) +{ + string cmd; + cmd = "cmd minigame "; + float i; + for ( i = 0; i < cmdargc; i++ ) + cmd = strcat(cmd,...(i,string)); + localcmd(strcat(cmd,"\n")); +} + +// Prompt the player to play in the current minigame +// (ie: it's their turn and they should get back to the minigame) +void minigame_prompt() +{ + if ( active_minigame && ! HUD_MinigameMenu_IsOpened() ) + { + HUD_Notify_Push(sprintf("minigames/%s/icon_notif",active_minigame.descriptor.netname), + _("It's your turn"), ""); + } +} diff --cc qcsrc/common/minigames/cl_minigames.qh index 201b6ca6b,000000000..404f24a61 mode 100644,000000..100644 --- a/qcsrc/common/minigames/cl_minigames.qh +++ b/qcsrc/common/minigames/cl_minigames.qh @@@ -1,115 -1,0 +1,119 @@@ ++#ifndef CL_MINIGAMES_H ++#define CL_MINIGAMES_H ++ +// Get a square in the center of the avaliable area +// \note macro to pass by reference pos and mySize +#define minigame_hud_fitsqare(pos, mySize) \ + if ( mySize##_x > mySize##_y ) \ + { \ + pos##_x += (mySize##_x-mySize##_y)/2; \ + mySize##_x = mySize##_y; \ + } \ + else \ + { \ + pos##_y += (mySize##_y-mySize##_x)/2; \ + mySize##_x = mySize##_x; \ + } \ + if(panel_bg_padding) \ + { \ + pos += '1 1 0' * panel_bg_padding; \ + mySize -= '2 2 0' * panel_bg_padding; \ + } + +// Get position and size of a panel +// \note macro to pass by reference pos and mySize +#define minigame_hud_panelarea(pos, mySize, panelID) \ + pos = stov(cvar_string(strcat("hud_panel_", HUD_PANEL(panelID).panel_name, "_pos"))); \ + mySize = stov(cvar_string(strcat("hud_panel_", HUD_PANEL(panelID).panel_name, "_size"))); \ + pos##_x *= vid_conwidth; pos##_y *= vid_conheight; \ + mySize##_x *= vid_conwidth; mySize##_y *= vid_conheight; + +// draw a panel border and the given texture +void minigame_hud_simpleboard(vector pos, vector mySize, string board_texture); + +// Normalize (2D vector) v to relative coordinate inside pos mySize +vector minigame_hud_normalize(vector v, vector pos, vector mySize); + +// De-normalize (2D vector) v from relative coordinate inside pos mySize +vector minigame_hud_denormalize(vector v, vector pos, vector mySize); + +// De-normalize (2D vector) v from relative size inside pos mySize +vector minigame_hud_denormalize_size(vector v, vector pos, vector mySize); + +// Check if the mouse is inside the given area +float minigame_hud_mouse_in(vector pos, vector sz); + +// Like drawstring, but wrapping words to fit maxwidth +// returns the size of the drawn area +// align selects the string alignment (0 = left, 0.5 = center, 1 = right) +vector minigame_drawstring_wrapped( float maxwidth, vector pos, string text, + vector fontsize, vector color, float theAlpha, float drawflags, float align ); + +// Like drawcolorcodedstring, but wrapping words to fit maxwidth +// returns the size of the drawn area +// align selects the string alignment (0 = left, 0.5 = center, 1 = right) +vector minigame_drawcolorcodedstring_wrapped( float maxwidth, vector pos, + string text, vector fontsize, float theAlpha, float drawflags, float align ); + +// Like drawstring but truncates the text to fit maxwidth +void minigame_drawstring_trunc(float maxwidth, vector pos, string text, + vector fontsize, vector color, float theAlpha, float drawflags ); + +// Like drawcolorcodedstring but truncates the text to fit maxwidth +void minigame_drawcolorcodedstring_trunc(float maxwidth, vector pos, string text, + vector fontsize, float theAlpha, float drawflags ); + +// like drawpic but pos represent the center rather than the topleft corner +void minigame_drawpic_centered( vector pos, string texture, vector sz, + vector color, float thealpha, float drawflags ); + +// Get full path of a minigame texture +string minigame_texture(string name); + +// For minigame descriptors: hud function for the game board +.void(vector pos, vector size) minigame_hud_board; +// For minigame descriptors: hud function for the game status +.void(vector pos, vector size) minigame_hud_status; +// For minigame_player: player server slot, don't use for anything else +.float minigame_playerslot; + +// register all minigames +void initialize_minigames(); + +// client-side minigame session cleanup +void deactivate_minigame(); + +// Currently active minigame session +entity active_minigame; +// minigame_player representing this client +entity minigame_self; + +// Whethere there's an active minigame +float minigame_isactive() +{ + return active_minigame != world; +} + +// Execute a minigame command +#define minigame_cmd(...) minigame_cmd_workaround(0,__VA_ARGS__) +void minigame_cmd_workaround(float dummy, string...cmdargc); + +// Read a minigame entity from the server +void ent_read_minigame(); + +// Prompt the player to play in the current minigame +// (ie: it's their turn and they should get back to the minigame) +void minigame_prompt(); + +float HUD_MinigameMenu_IsOpened(); +void HUD_MinigameMenu_Close(); +float HUD_Minigame_Showpanels(); +// Adds a game-specific entry to the menu +void HUD_MinigameMenu_CustomEntry(entity parent, string message, string event_arg); + + +#define FOREACH_MINIGAME_ENTITY(entityvar) \ + entityvar=world; \ + while( (entityvar = findentity(entityvar,owner,active_minigame)) ) + ++#endif diff --cc qcsrc/common/minigames/cl_minigames_hud.qc index 0c4008511,000000000..7df760979 mode 100644,000000..100644 --- a/qcsrc/common/minigames/cl_minigames_hud.qc +++ b/qcsrc/common/minigames/cl_minigames_hud.qc @@@ -1,698 -1,0 +1,699 @@@ +#include "minigames.qh" ++#include "../../client/mapvoting.qh" + +float HUD_mouse_over(entity somepanel) +{ + vector pos = stov(cvar_string(strcat("hud_panel_", somepanel.panel_name, "_pos"))); + vector sz = stov(cvar_string(strcat("hud_panel_", somepanel.panel_name, "_size"))); + return mousepos_x >= pos_x*vid_conwidth && mousepos_x <= (pos_x+sz_x)*vid_conwidth && + mousepos_y >= pos_y*vid_conheight && mousepos_y <= (pos_y+sz_y)*vid_conheight ; +} + +// ==================================================================== +// Minigame Board +// ==================================================================== + +void HUD_MinigameBoard () +{ + entity hud_minigame = world; + + if(!autocvar__hud_configure) + hud_minigame = active_minigame.descriptor; + else + hud_minigame = minigame_get_descriptor("nmm"); + + if ( !hud_minigame ) + return; + + HUD_Panel_UpdateCvars(); + + + vector pos, mySize; + pos = panel_pos; + mySize = panel_size; + + hud_minigame.minigame_hud_board(pos,mySize); +} + +// ==================================================================== +// Minigame Status +// ==================================================================== +void HUD_MinigameStatus () +{ + entity hud_minigame = world; + + if(!autocvar__hud_configure) + hud_minigame = active_minigame.descriptor; + else + hud_minigame = minigame_get_descriptor("nmm"); + + if ( !hud_minigame ) + return; + + HUD_Panel_UpdateCvars(); + + + vector pos, mySize; + pos = panel_pos; + mySize = panel_size; + + if(panel_bg_padding) + { + pos += '1 1 0' * panel_bg_padding; + mySize -= '2 2 0' * panel_bg_padding; + } + + hud_minigame.minigame_hud_status(pos,mySize); +} + +// ==================================================================== +// Minigame Menu +// ==================================================================== + +// Minigame menu options: list head +entity HUD_MinigameMenu_entries; +// Minigame menu options: list tail +entity HUD_MinigameMenu_last_entry; + +// Minigame menu options: insert entry after the given location +void HUD_MinigameMenu_InsertEntry(entity new, entity prev) +{ + if ( !HUD_MinigameMenu_entries ) + { + HUD_MinigameMenu_entries = new; + HUD_MinigameMenu_last_entry = new; + return; + } + + new.list_prev = prev; + new.list_next = prev.list_next; + if ( prev.list_next ) + prev.list_next.list_prev = new; + else + HUD_MinigameMenu_last_entry = new; + prev.list_next = new; + +} + + +// minigame menu item uder the mouse +entity HUD_MinigameMenu_activeitem; + +// Click the given item +void HUD_MinigameMenu_Click(entity menuitem) +{ + if ( menuitem ) + { + entity e = self; + self = menuitem; + menuitem.use(); + self = e; + } +} + +// Minigame menu options: Remove the given entry +// Precondition: the given entry is actually in the list +void HUD_MinigameMenu_EraseEntry ( entity e ) +{ + // remove child items (if any) + if ( e.flags & 2 ) + { + HUD_MinigameMenu_Click(e); + } + + if ( e.list_prev ) + e.list_prev.list_next = e.list_next; + else + HUD_MinigameMenu_entries = e.list_next; + + if ( e.list_next ) + e.list_next.list_prev = e.list_prev; + else + HUD_MinigameMenu_last_entry = e.list_prev; + + if ( HUD_MinigameMenu_activeitem == e ) + HUD_MinigameMenu_activeitem = world; + + remove(e); +} + +// Minigame menu options: create entry +entity HUD_MinigameMenu_SpawnEntry(string s, vector offset, vector fontsize, vector color,void() click) +{ + entity entry = spawn(); + entry.message = s; + entry.origin = offset; + entry.size = fontsize; + entry.colormod = color; + entry.flags = 0; + entry.use = click; + panel_pos_y += fontsize_y; + return entry; +} + +// Spawn a child entry of a collapsable entry +entity HUD_MinigameMenu_SpawnSubEntry(string s, void() click, entity parent) +{ + vector item_fontsize = hud_fontsize*1.25; + vector item_offset = '1 0 0' * item_fontsize_x; + entity item = HUD_MinigameMenu_SpawnEntry( + s,item_offset,item_fontsize,'0.8 0.8 0.8', click ); + item.owner = parent; + return item; +} + +// Click action for Create sub-entries +void HUD_MinigameMenu_ClickCreate_Entry() +{ + minigame_cmd("create ",self.netname); +} + +// Helper click action for collapsible entries +// returns true when you have to create the sub-entries +float HUD_MinigameMenu_Click_ExpandCollapse() +{ + entity e; + if ( self.flags & 2 ) + { + if ( HUD_MinigameMenu_activeitem && + HUD_MinigameMenu_activeitem.owner == self ) + HUD_MinigameMenu_activeitem = world; + self.flags &= ~2; + for ( e = self.list_next; e != world && e.owner == self; e = self.list_next ) + { + if ( e.flags & 2 ) + HUD_MinigameMenu_Click(e); + self.list_next = e.list_next; + remove(e); + } + if ( self.list_next ) + self.list_next.list_prev = self; + } + else + { + for ( e = HUD_MinigameMenu_entries; e != world; e = e.list_next ) + { + if ( e.flags & 2 && e.origin_x == self.origin_x) + HUD_MinigameMenu_Click(e); + } + + self.flags |= 2; + + return true; + } + return false; +} + +// Click action for the Create menu +void HUD_MinigameMenu_ClickCreate() +{ + if ( HUD_MinigameMenu_Click_ExpandCollapse() ) + { + entity e; + entity curr; + entity prev = self; + for ( e = minigame_descriptors; e != world; e = e.list_next ) + { + curr = HUD_MinigameMenu_SpawnSubEntry( + e.message, HUD_MinigameMenu_ClickCreate_Entry, self ); + curr.netname = e.netname; + curr.model = strzone(minigame_texture(strcat(e.netname,"/icon"))); + HUD_MinigameMenu_InsertEntry( curr, prev ); + prev = curr; + } + } +} + +// Click action for Join sub-entries +void HUD_MinigameMenu_ClickJoin_Entry() +{ + minigame_cmd("join ",self.netname); + HUD_MinigameMenu_EraseEntry(self); +} + +// Click action for the Join menu +void HUD_MinigameMenu_ClickJoin() +{ + if ( HUD_MinigameMenu_Click_ExpandCollapse() ) + { + entity e = world; + entity curr; + entity prev = self; + while( (e = find(e,classname,"minigame")) ) + { + if ( e != active_minigame ) + { + curr = HUD_MinigameMenu_SpawnSubEntry( + e.netname, HUD_MinigameMenu_ClickJoin_Entry, self ); + curr.netname = e.netname; + curr.model = strzone(minigame_texture(strcat(e.descriptor.netname,"/icon"))); + HUD_MinigameMenu_InsertEntry( curr, prev ); + prev = curr; + } + } + } +} + +/*// Temporary placeholder for un-implemented Click actions +void HUD_MinigameMenu_ClickNoop() +{ + dprint("Placeholder for ",self.message,"\n"); +}*/ + +// Click action for Quit +void HUD_MinigameMenu_ClickQuit() +{ + minigame_cmd("end"); +} + +// Click action for Invite sub-entries +void HUD_MinigameMenu_ClickInvite_Entry() +{ + minigame_cmd("invite #",self.netname); +} + +// Click action for the Invite menu +void HUD_MinigameMenu_ClickInvite() +{ + if ( HUD_MinigameMenu_Click_ExpandCollapse() ) + { + float i; + entity e; + entity prev = self; + for(i = 0; i < maxclients; ++i) + { + if ( player_localnum != i && playerslots[i] && GetPlayerName(i) != "" && + !findfloat(world,minigame_playerslot,i+1) && playerslots[i].ping ) + { + e = HUD_MinigameMenu_SpawnSubEntry( + strzone(GetPlayerName(i)), HUD_MinigameMenu_ClickInvite_Entry, + self ); + e.flags |= 1; + e.netname = strzone(ftos(i+1)); + e.origin_x *= 2; + HUD_MinigameMenu_InsertEntry(e,prev); + prev = e; + } + } + } +} + +void HUD_MinigameMenu_ClickCustomEntry() +{ + if ( active_minigame ) + active_minigame.minigame_event(active_minigame,"menu_click",self.netname); +} + +// Adds a game-specific entry to the menu +void HUD_MinigameMenu_CustomEntry(entity parent, string menumessage, string event_arg) +{ + entity e = HUD_MinigameMenu_SpawnSubEntry( + menumessage, HUD_MinigameMenu_ClickCustomEntry, parent ); + e.netname = event_arg; + HUD_MinigameMenu_InsertEntry(e, parent); + dprint("CustomEntry ",ftos(num_for_edict(parent))," ",menumessage," ",event_arg,"\n"); +} + +// Click action for the Current Game menu +void HUD_MinigameMenu_ClickCurrentGame() +{ + if ( HUD_MinigameMenu_Click_ExpandCollapse() ) + { + HUD_MinigameMenu_InsertEntry( HUD_MinigameMenu_SpawnSubEntry( + _("Quit"), HUD_MinigameMenu_ClickQuit, self ), self); + + active_minigame.minigame_event(active_minigame,"menu_show",self); + + HUD_MinigameMenu_InsertEntry( HUD_MinigameMenu_SpawnSubEntry( + _("Invite"), HUD_MinigameMenu_ClickInvite, self), self); + } +} +// Whether the minigame menu panel is open +float HUD_MinigameMenu_IsOpened() +{ + return !!HUD_MinigameMenu_entries; +} + +// Close the minigame menu panel +void HUD_MinigameMenu_Close() +{ + if ( HUD_MinigameMenu_IsOpened() ) + { + entity e, p; + for ( e = HUD_MinigameMenu_entries; e != world; e = p ) + { + p = e.list_next; + remove(e); + } + HUD_MinigameMenu_entries = world; + HUD_MinigameMenu_last_entry = world; + HUD_MinigameMenu_activeitem = world; + if(autocvar_hud_cursormode) + if ( !autocvar__hud_configure ) + setcursormode(0); + } +} + +// toggle a button to manage the current game +void HUD_MinigameMenu_CurrentButton() +{ + entity e; + if ( active_minigame ) + { + for ( e = HUD_MinigameMenu_last_entry; e != world; e = e.list_prev ) + if ( e.classname == "hud_minigamemenu_exit" ) + { + HUD_MinigameMenu_EraseEntry(e); + break; + } + entity currb = HUD_MinigameMenu_SpawnEntry( + _("Current Game"), '0 0 0', hud_fontsize*1.5,'0.7 0.84 1', HUD_MinigameMenu_ClickCurrentGame ); + currb.classname = "hud_minigamemenu_current"; + currb.model = strzone(minigame_texture(strcat(active_minigame.descriptor.netname,"/icon"))); + HUD_MinigameMenu_InsertEntry(currb,HUD_MinigameMenu_last_entry); + HUD_MinigameMenu_Click(currb); + } + else + { + entity p; + for ( e = HUD_MinigameMenu_last_entry; e != world; e = p.list_prev ) + { + p = e; + if ( e.classname == "hud_minigamemenu_current" ) + { + p = e.list_next; + if ( !p ) + p = HUD_MinigameMenu_last_entry; + HUD_MinigameMenu_EraseEntry(e); + break; + } + } + for ( e = HUD_MinigameMenu_last_entry; e != world; e = e.list_prev ) + if ( e.classname == "hud_minigamemenu_exit" ) + return; + entity exit = HUD_MinigameMenu_SpawnEntry( + _("Exit Menu"),'0 0 0',hud_fontsize*1.5,'0.7 0.84 1', HUD_MinigameMenu_Close); + exit.classname = "hud_minigamemenu_exit"; + HUD_MinigameMenu_InsertEntry ( exit, HUD_MinigameMenu_last_entry ); + } +} + +// Open the minigame menu panel +void HUD_MinigameMenu_Open() +{ + if ( !HUD_MinigameMenu_IsOpened() ) + { + HUD_MinigameMenu_InsertEntry( HUD_MinigameMenu_SpawnEntry( + _("Create"), '0 0 0', hud_fontsize*1.5,'0.7 0.84 1', HUD_MinigameMenu_ClickCreate), + HUD_MinigameMenu_last_entry ); + HUD_MinigameMenu_InsertEntry ( HUD_MinigameMenu_SpawnEntry( + _("Join"),'0 0 0',hud_fontsize*1.5,'0.7 0.84 1', HUD_MinigameMenu_ClickJoin), + HUD_MinigameMenu_last_entry ); + HUD_MinigameMenu_CurrentButton(); + HUD_MinigameMenu_activeitem = world; + if(autocvar_hud_cursormode) + setcursormode(1); + } +} + +// Handles mouse input on to minigame menu panel +void HUD_MinigameMenu_MouseInput() +{ + panel = HUD_PANEL(MINIGAME_MENU); + - HUD_Panel_UpdateCvars() ++ HUD_Panel_UpdateCvars(); + + if(panel_bg_padding) + { + panel_pos += '1 1 0' * panel_bg_padding; + panel_size -= '2 2 0' * panel_bg_padding; + } + + entity e; + + panel_pos_y += hud_fontsize_y*2; + + HUD_MinigameMenu_activeitem = world; + vector sz; + for ( e = HUD_MinigameMenu_entries; e != world; e = e.list_next ) + { + sz = eX*panel_size_x + eY*e.size_y; + if ( e.model ) + sz_y = 22; + if ( !HUD_MinigameMenu_activeitem && mousepos_y >= panel_pos_y && mousepos_y <= panel_pos_y + sz_y ) + { + HUD_MinigameMenu_activeitem = e; + } + panel_pos_y += sz_y; + } +} + +// Draw a menu entry +void HUD_MinigameMenu_DrawEntry(vector pos, string s, vector fontsize, vector color) +{ + minigame_drawstring_trunc(panel_size_x-pos_x+panel_pos_x, pos, s, + fontsize, color, panel_fg_alpha, DRAWFLAG_NORMAL); +} +// Draw a color-coded menu +void HUD_MinigameMenu_DrawColoredEntry(vector pos, string s, vector fontsize) +{ + minigame_drawcolorcodedstring_trunc(panel_size_x-pos_x+panel_pos_x, pos, s, + fontsize, panel_fg_alpha, DRAWFLAG_NORMAL); +} + +// minigame menu panel UI +void HUD_MinigameMenu () +{ + if ( !HUD_MinigameMenu_IsOpened() ) + return; + + HUD_Panel_UpdateCvars(); + + HUD_Panel_DrawBg(1); + + if(panel_bg_padding) + { + panel_pos += '1 1 0' * panel_bg_padding; + panel_size -= '2 2 0' * panel_bg_padding; + } + + HUD_MinigameMenu_DrawEntry(panel_pos,_("Minigames"),hud_fontsize*2,'0.25 0.47 0.72'); + panel_pos_y += hud_fontsize_y*2; + + entity e; + vector color; + vector offset; + float itemh; + vector imgsz = '22 22 0'; // NOTE: if changed, edit where HUD_MinigameMenu_activeitem is selected + for ( e = HUD_MinigameMenu_entries; e != world; e = e.list_next ) + { + color = e.colormod; + + offset = e.origin; + itemh = e.size_y; + + if ( e.model ) + itemh = imgsz_y; + + if ( e.flags & 2 ) + { + drawfill(panel_pos, eX*panel_size_x + eY*itemh, e.colormod, + panel_fg_alpha, DRAWFLAG_NORMAL); + color = '0 0 0'; + } + + if ( e.model ) + { + drawpic( panel_pos+offset, e.model, imgsz, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL ); + offset_x += imgsz_x; + offset_y = (imgsz_y-e.size_y) / 2; + } + + if ( e.flags & 1 ) + HUD_MinigameMenu_DrawColoredEntry(panel_pos+offset,e.message,e.size); + else + HUD_MinigameMenu_DrawEntry(panel_pos+offset,e.message,e.size,color); + + if ( e == HUD_MinigameMenu_activeitem ) + drawfill(panel_pos, eX*panel_size_x + eY*itemh,'1 1 1', 0.25, DRAWFLAG_ADDITIVE); + + panel_pos_y += itemh; + } +} + +// ==================================================================== +// Minigame Help Panel +// ==================================================================== + +void HUD_MinigameHelp() +{ + string help_message; + + if(!autocvar__hud_configure) + help_message = active_minigame.message; + else + help_message = "Minigame message"; + + if ( !help_message ) + return; + + HUD_Panel_UpdateCvars(); + + + vector pos, mySize; + pos = panel_pos; + mySize = panel_size; + + if(panel_bg_padding) + { + pos += '1 1 0' * panel_bg_padding; + mySize -= '2 2 0' * panel_bg_padding; + } + + minigame_drawcolorcodedstring_wrapped( mySize_x, pos, help_message, + hud_fontsize, panel_fg_alpha, DRAWFLAG_NORMAL, 0.5 ); +} + +// ==================================================================== +// Minigame Panel Input +// ==================================================================== +float HUD_Minigame_InputEvent(float bInputType, float nPrimary, float nSecondary) +{ + + if( !HUD_MinigameMenu_IsOpened() || autocvar__hud_configure ) + return false; + + if(bInputType == 3) + { + mousepos_x = nPrimary; + mousepos_y = nSecondary; + if ( minigame_isactive() && HUD_mouse_over(HUD_PANEL(MINIGAME_BOARD)) ) + active_minigame.minigame_event(active_minigame,"mouse_moved",mousepos); + return true; + + } + else + { + + if(bInputType == 0) { + if(nPrimary == K_ALT) hudShiftState |= S_ALT; + if(nPrimary == K_CTRL) hudShiftState |= S_CTRL; + if(nPrimary == K_SHIFT) hudShiftState |= S_SHIFT; + if(nPrimary == K_MOUSE1) mouseClicked |= S_MOUSE1; + if(nPrimary == K_MOUSE2) mouseClicked |= S_MOUSE2; + } + else if(bInputType == 1) { + if(nPrimary == K_ALT) hudShiftState -= (hudShiftState & S_ALT); + if(nPrimary == K_CTRL) hudShiftState -= (hudShiftState & S_CTRL); + if(nPrimary == K_SHIFT) hudShiftState -= (hudShiftState & S_SHIFT); + if(nPrimary == K_MOUSE1) mouseClicked -= (mouseClicked & S_MOUSE1); + if(nPrimary == K_MOUSE2) mouseClicked -= (mouseClicked & S_MOUSE2); + } + + // allow some binds + string con_keys; + float keys; + float i; + con_keys = findkeysforcommand("toggleconsole", 0); + keys = tokenize(con_keys); // findkeysforcommand returns data for this + for (i = 0; i < keys; ++i) + { + if(nPrimary == stof(argv(i))) + return false; + } + + if ( minigame_isactive() && ( bInputType == 0 || bInputType == 1 ) ) + { + string device = ""; + string action = bInputType == 0 ? "pressed" : "released"; + if ( nPrimary >= K_MOUSE1 && nPrimary <= K_MOUSE16 ) + { + if ( HUD_mouse_over(HUD_PANEL(MINIGAME_BOARD)) ) + device = "mouse"; + } + else + device = "key"; + + if ( device && active_minigame.minigame_event( + active_minigame,strcat(device,"_",action),nPrimary) ) + return true; + + /// TODO: bInputType == 2? + } + + if ( bInputType == 0 ) + { + if ( nPrimary == K_MOUSE1 && HUD_MinigameMenu_activeitem && + HUD_mouse_over(HUD_PANEL(MINIGAME_MENU)) ) + { + HUD_MinigameMenu_Click(HUD_MinigameMenu_activeitem); + return true; + } + if ( nPrimary == K_UPARROW || nPrimary == K_KP_UPARROW ) + { + if ( HUD_MinigameMenu_activeitem && HUD_MinigameMenu_activeitem.list_prev ) + HUD_MinigameMenu_activeitem = HUD_MinigameMenu_activeitem.list_prev; + else + HUD_MinigameMenu_activeitem = HUD_MinigameMenu_last_entry; + return true; + } + else if ( nPrimary == K_DOWNARROW || nPrimary == K_KP_DOWNARROW ) + { + if ( HUD_MinigameMenu_activeitem && HUD_MinigameMenu_activeitem.list_next ) + HUD_MinigameMenu_activeitem = HUD_MinigameMenu_activeitem.list_next; + else + HUD_MinigameMenu_activeitem = HUD_MinigameMenu_entries; + return true; + } + else if ( nPrimary == K_HOME || nPrimary == K_KP_HOME ) + { + HUD_MinigameMenu_activeitem = HUD_MinigameMenu_entries; + return true; + } + else if ( nPrimary == K_END || nPrimary == K_KP_END ) + { + HUD_MinigameMenu_activeitem = HUD_MinigameMenu_entries; + return true; + } + else if ( nPrimary == K_KP_ENTER || nPrimary == K_ENTER || nPrimary == K_SPACE ) + { + HUD_MinigameMenu_Click(HUD_MinigameMenu_activeitem); + return true; + } + else if ( nPrimary == K_ESCAPE ) + { + HUD_MinigameMenu_Close(); + return true; + } + } + } + + return false; + +} + +void HUD_Minigame_Mouse() +{ + if( !HUD_MinigameMenu_IsOpened() || autocvar__hud_configure || mv_active ) + return; + + if(!autocvar_hud_cursormode) + { + mousepos = mousepos + getmousepos() * autocvar_menu_mouse_speed; + + mousepos_x = bound(0, mousepos_x, vid_conwidth); + mousepos_y = bound(0, mousepos_y, vid_conheight); + } + + if ( HUD_MinigameMenu_IsOpened() && HUD_mouse_over(HUD_PANEL(MINIGAME_MENU)) ) + HUD_MinigameMenu_MouseInput(); + + vector cursorsize = '32 32 0'; + drawpic(mousepos-'8 4 0', strcat("gfx/menu/", autocvar_menu_skin, "/cursor.tga"), + cursorsize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL); +} + +float HUD_Minigame_Showpanels() +{ + return HUD_MinigameMenu_IsOpened() && ( autocvar__hud_configure || minigame_isactive() ); +} diff --cc qcsrc/common/minigames/minigame/all.qh index 1330648cc,000000000..e550ff468 mode 100644,000000..100644 --- a/qcsrc/common/minigames/minigame/all.qh +++ b/qcsrc/common/minigames/minigame/all.qh @@@ -1,99 -1,0 +1,105 @@@ ++#if defined(SVQC) ++#include "../sv_minigames.qh" ++#elif defined(CSQC) ++#include "../cl_minigames.qh" ++#endif ++ +/** + +How to create a minigame +======================== + +Create a file for your minigame in this directory and #include it here. +(ttt.qc implements tic tac toe and can be used as an example) +and add your minigame to REGISTERED_MINIGAMES (see below) + +Required functions +------------------ + +SVQC: + float minigame_event_(entity minigame, string event, ...count) + see ../minigames.qh for a detailed explanation +CSQC: + void minigame_hud_board_(vector pos, vector mySize) + draws the main game board inside the rectangle defined by pos and mySize + (That rectangle is expressed in window coordinates) + void minigame_hud_status_(vector pos, vector mySize) + draws the game status panel inside the rectangle defined by pos and mySize + (That rectangle is expressed in window coordinates) + This panel shows eg scores, captured pieces and so on + float minigame_event_(entity minigame, string event, ...count) + see ../minigames.qh for a detailed explanation + +Managing entities +----------------- + +You can link entities without having to worry about them if their classname +has been defined in MINIGAME_SIMPLELINKED_ENTITIES (see below) +Such entities can be spawned with msle_spawn and the system +will handle networking and cleanup automatically. +You'll still need to set .SendFlags according to what you specified in FIELD +in order for them to be sent, ../minigames.qh defines some constants to be +used as send flags for minigame entities: + +* MINIG_SF_CREATE + Used when creating a new object, you can use this to define fields that don't change +* MINIG_SF_UPDATE + A miscellaneous update, can be safely used if the entity has just a few fields +* MINIG_SF_CUSTOM + Starting value for custom flags, since there are bit-wise flags, + the following values shall be MINIG_SF_CUSTOM*2, MINIG_SF_CUSTOM*4 and MINIG_SF_CUSTOM*8. +* MINIG_SF_MAX + Maximum flag value that will be networked +* MINIG_SF_ALL + Mask matching all possible flags + +Note: As of now, flags are sent as a single byte + +Even for non-networked entities, the system provides a system to remove +automatically unneeded entities when the minigame is over, the requirement is +that .owner is set to the minigame session entity and .minigame_autoclean is true. +*/ + +#include "nmm.qc" +#include "ttt.qc" + +/** + * Registration: + * MINIGAME(id,"Name") + * id (QuakeC symbol) Game identifier, used to find the functions explained above + * "Name"(String) Human readable name for the game, shown in the UI + */ +#define REGISTERED_MINIGAMES \ + MINIGAME(nmm, "Nine Men's Morris") \ + MINIGAME(ttt, "Tic Tac Toe") \ + /*empty line*/ + +/** + * Set up automatic entity read/write functionality + * To ensure that everything is handled automatically, spawn on the server using msle_spawn + * Syntax: + * MSLE(classname,Field...) \ + * classname: Identifier used to recognize the type of the entity + * (must be set as .classname on the sent entities) + * Field... : List of FIELD calls + * FIELD(sendflags, Type, field) + * sendflags: Send flags that signal when this field has to be sent + * Type : Type of the entity field. Used to determine WriteX/ReadX functions. + * Follows a list of accepted values + * Byte + * Char + * Short + * Long + * Coord + * Angle + * String Note: strzoned on client + * Float Note: implemented as Write/Read Coord + * Vector Note: implemented as Write/Read Coord on _x _y _z + * Vector2D Note: implemented as Write/Read Coord on _x _y + * Note: + * classname and netname are always sent + * MSLE stands for Minigame Simple Linked Entity + */ +#define MINIGAME_SIMPLELINKED_ENTITIES \ + MSLE(minigame_board_piece,FIELD(MINIG_SF_CREATE,Byte,team) FIELD(MINIG_SF_UPDATE, Short, minigame_flags) FIELD(MINIG_SF_UPDATE, Vector2D,origin)) \ + /*empty line*/ diff --cc qcsrc/common/minigames/minigame/ttt.qc index 85b97276a,000000000..e2dc0a06a mode 100644,000000..100644 --- a/qcsrc/common/minigames/minigame/ttt.qc +++ b/qcsrc/common/minigames/minigame/ttt.qc @@@ -1,688 -1,0 +1,688 @@@ +const float TTT_TURN_PLACE = 0x0100; // player has to place a piece on the board +const float TTT_TURN_WIN = 0x0200; // player has won +const float TTT_TURN_DRAW = 0x0400; // no moves are possible +const float TTT_TURN_NEXT = 0x0800; // a player wants to start a new match +const float TTT_TURN_TYPE = 0x0f00; // turn type mask + +const float TTT_TURN_TEAM1 = 0x0001; +const float TTT_TURN_TEAM2 = 0x0002; +const float TTT_TURN_TEAM = 0x000f; // turn team mask + +// send flags +const float TTT_SF_PLAYERSCORE = MINIG_SF_CUSTOM; // send minigame_player scores (won matches) +const float TTT_SF_SINGLEPLAYER = MINIG_SF_CUSTOM<<1;// send minigame.ttt_ai + +.float ttt_npieces; // (minigame) number of pieces on the board (simplifies checking a draw) +.float ttt_nexteam; // (minigame) next team (used to change the starting team on following matches) +.float ttt_ai; // (minigame) when non-zero, singleplayer vs AI + +// find tic tac toe piece given its tile name +entity ttt_find_piece(entity minig, string tile) +{ + entity e = world; + while ( ( e = findentity(e,owner,minig) ) ) + if ( e.classname == "minigame_board_piece" && e.netname == tile ) + return e; + return world; +} + +// Checks if the given piece completes a row +float ttt_winning_piece(entity piece) +{ + float number = minigame_tile_number(piece.netname); + float letter = minigame_tile_letter(piece.netname); + + if ( ttt_find_piece(piece.owner,minigame_tile_buildname(0,number)).team == piece.team ) + if ( ttt_find_piece(piece.owner,minigame_tile_buildname(1,number)).team == piece.team ) + if ( ttt_find_piece(piece.owner,minigame_tile_buildname(2,number)).team == piece.team ) + return 1; + + if ( ttt_find_piece(piece.owner,minigame_tile_buildname(letter,0)).team == piece.team ) + if ( ttt_find_piece(piece.owner,minigame_tile_buildname(letter,1)).team == piece.team ) + if ( ttt_find_piece(piece.owner,minigame_tile_buildname(letter,2)).team == piece.team ) + return 1; + + if ( number == letter ) + if ( ttt_find_piece(piece.owner,minigame_tile_buildname(0,0)).team == piece.team ) + if ( ttt_find_piece(piece.owner,minigame_tile_buildname(1,1)).team == piece.team ) + if ( ttt_find_piece(piece.owner,minigame_tile_buildname(2,2)).team == piece.team ) + return 1; + + if ( number == 2-letter ) + if ( ttt_find_piece(piece.owner,minigame_tile_buildname(0,2)).team == piece.team ) + if ( ttt_find_piece(piece.owner,minigame_tile_buildname(1,1)).team == piece.team ) + if ( ttt_find_piece(piece.owner,minigame_tile_buildname(2,0)).team == piece.team ) + return 1; + + return 0; +} + +// check if the tile name is valid (3x3 grid) +float ttt_valid_tile(string tile) +{ + if ( !tile ) + return 0; + float number = minigame_tile_number(tile); + float letter = minigame_tile_letter(tile); + return 0 <= number && number < 3 && 0 <= letter && letter < 3; +} + +// make a move +void ttt_move(entity minigame, entity player, string pos ) +{ + if ( minigame.minigame_flags & TTT_TURN_PLACE ) + if ( pos && player.team == (minigame.minigame_flags & TTT_TURN_TEAM) ) + { + if ( ttt_valid_tile(pos) ) + if ( !ttt_find_piece(minigame,pos) ) + { + entity piece = msle_spawn(minigame,"minigame_board_piece"); + piece.team = player.team; + piece.netname = strzone(pos); + minigame_server_sendflags(piece,MINIG_SF_ALL); + minigame_server_sendflags(minigame,MINIG_SF_UPDATE); + minigame.ttt_npieces++; + minigame.ttt_nexteam = minigame_next_team(player.team,2); + if ( ttt_winning_piece(piece) ) + { + player.minigame_flags++; + minigame_server_sendflags(player, TTT_SF_PLAYERSCORE); + minigame.minigame_flags = TTT_TURN_WIN | player.team; + } + else if ( minigame.ttt_npieces >= 9 ) + minigame.minigame_flags = TTT_TURN_DRAW; + else + minigame.minigame_flags = TTT_TURN_PLACE | minigame.ttt_nexteam; + } + } +} + +// request a new match +void ttt_next_match(entity minigame, entity player) +{ +#ifdef SVQC + // on multiplayer matches, wait for both players to agree + if ( minigame.minigame_flags & (TTT_TURN_WIN|TTT_TURN_DRAW) ) + { + minigame.minigame_flags = TTT_TURN_NEXT | player.team; + minigame.SendFlags |= MINIG_SF_UPDATE; + } + else if ( (minigame.minigame_flags & TTT_TURN_NEXT) && + !( minigame.minigame_flags & player.team ) ) +#endif + { + minigame.minigame_flags = TTT_TURN_PLACE | minigame.ttt_nexteam; + minigame_server_sendflags(minigame,MINIG_SF_UPDATE); + minigame.ttt_npieces = 0; + entity e = world; + while ( ( e = findentity(e,owner,minigame) ) ) + if ( e.classname == "minigame_board_piece" ) + remove(e); + } +} + +#ifdef SVQC + + +// required function, handle server side events +float minigame_event_ttt(entity minigame, string event, ...) +{ + switch(event) + { + case "start": + { + minigame.minigame_flags = (TTT_TURN_PLACE | TTT_TURN_TEAM1); - return TRUE; ++ return true; + } + case "end": + { + entity e = world; + while( (e = findentity(e, owner, minigame)) ) + if(e.classname == "minigame_board_piece") + { + if(e.netname) { strunzone(e.netname); } + remove(e); + } - return FALSE; ++ return false; + } + case "join": + { + float pl_num = minigame_count_players(minigame); + + // Don't allow joining a single player match + if ( (minigame.ttt_ai) && pl_num > 0 ) - return FALSE; ++ return false; + + // Don't allow more than 2 players - if(pl_num >= 2) { return FALSE; } ++ if(pl_num >= 2) { return false; } + + // Get the right team + if(minigame.minigame_players) + return minigame_next_team(minigame.minigame_players.team, 2); + + // Team 1 by default + return 1; + } + case "cmd": + { + switch(argv(0)) + { + case "move": + ttt_move(minigame, ...(0,entity), ...(1,float) == 2 ? argv(1) : string_null ); - return TRUE; ++ return true; + case "next": + ttt_next_match(minigame,...(0,entity)); - return TRUE; ++ return true; + case "singleplayer": + if ( minigame_count_players(minigame) == 1 ) + { + minigame.ttt_ai = minigame_next_team(minigame.minigame_players.team, 2); + minigame.SendFlags = TTT_SF_SINGLEPLAYER; + } - return TRUE; ++ return true; + } + - return FALSE; ++ return false; + } + case "network_send": + { + entity sent = ...(0,entity); + float sf = ...(1,float); + if ( sent.classname == "minigame_player" && (sf & TTT_SF_PLAYERSCORE ) ) + { + WriteByte(MSG_ENTITY,sent.minigame_flags); + } + else if ( sent.classname == "minigame" && (sf & TTT_SF_SINGLEPLAYER) ) + { + WriteByte(MSG_ENTITY,sent.ttt_ai); + } - return FALSE; ++ return false; + } + } + - return FALSE; ++ return false; +} + + +#elif defined(CSQC) + +string ttt_curr_pos; // identifier of the tile under the mouse +vector ttt_boardpos; // HUD board position +vector ttt_boardsize;// HUD board size +.float ttt_checkwin; // Used to optimize checks to display a win + +// Required function, draw the game board +void minigame_hud_board_ttt(vector pos, vector mySize) +{ + minigame_hud_fitsqare(pos, mySize); + ttt_boardpos = pos; + ttt_boardsize = mySize; + + minigame_hud_simpleboard(pos,mySize,minigame_texture("ttt/board")); + + vector tile_size = minigame_hud_denormalize_size('1 1 0'/3,pos,mySize); + vector tile_pos; + + if ( (active_minigame.minigame_flags & TTT_TURN_TEAM) == minigame_self.team ) + if ( ttt_valid_tile(ttt_curr_pos) ) + { + tile_pos = minigame_tile_pos(ttt_curr_pos,3,3); + tile_pos = minigame_hud_denormalize(tile_pos,pos,mySize); + minigame_drawpic_centered( tile_pos, + minigame_texture(strcat("ttt/piece",ftos(minigame_self.team))), + tile_size, '1 1 1', panel_fg_alpha/2, DRAWFLAG_NORMAL ); + } + + entity e; + FOREACH_MINIGAME_ENTITY(e) + { + if ( e.classname == "minigame_board_piece" ) + { + tile_pos = minigame_tile_pos(e.netname,3,3); + tile_pos = minigame_hud_denormalize(tile_pos,pos,mySize); + + if ( active_minigame.minigame_flags & TTT_TURN_WIN ) + if ( !e.ttt_checkwin ) + e.ttt_checkwin = ttt_winning_piece(e) ? 1 : -1; + + float icon_color = 1; + if ( e.ttt_checkwin == -1 ) + icon_color = 0.4; + else if ( e.ttt_checkwin == 1 ) + { + icon_color = 2; + minigame_drawpic_centered( tile_pos, minigame_texture("ttt/winglow"), + tile_size, '1 1 1', panel_fg_alpha, DRAWFLAG_ADDITIVE ); + } + + minigame_drawpic_centered( tile_pos, + minigame_texture(strcat("ttt/piece",ftos(e.team))), + tile_size, '1 1 1'*icon_color, panel_fg_alpha, DRAWFLAG_NORMAL ); + } + } +} + + +// Required function, draw the game status panel +void minigame_hud_status_ttt(vector pos, vector mySize) +{ + HUD_Panel_DrawBg(1); + vector ts; + ts = minigame_drawstring_wrapped(mySize_x,pos,active_minigame.descriptor.message, + hud_fontsize * 2, '0.25 0.47 0.72', panel_fg_alpha, DRAWFLAG_NORMAL,0.5); + + pos_y += ts_y; + mySize_y -= ts_y; + + vector player_fontsize = hud_fontsize * 1.75; + ts_y = ( mySize_y - 2*player_fontsize_y ) / 2; + ts_x = mySize_x; + vector mypos; + vector tile_size = '48 48 0'; + + entity e; + FOREACH_MINIGAME_ENTITY(e) + { + if ( e.classname == "minigame_player" ) + { + mypos = pos; + if ( e.team == 2 ) + mypos_y += player_fontsize_y + ts_y; + minigame_drawcolorcodedstring_trunc(mySize_x,mypos, + (e.minigame_playerslot ? GetPlayerName(e.minigame_playerslot-1) : _("AI")), + player_fontsize, panel_fg_alpha, DRAWFLAG_NORMAL); + + mypos_y += player_fontsize_y; + drawpic( mypos, + minigame_texture(strcat("ttt/piece",ftos(e.team))), + tile_size, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL ); + + mypos_x += tile_size_x; + + drawstring(mypos,ftos(e.minigame_flags),tile_size, + '0.7 0.84 1', panel_fg_alpha, DRAWFLAG_NORMAL); + } + } +} + +// Turn a set of flags into a help message +string ttt_turn_to_string(float turnflags) +{ + if ( turnflags & TTT_TURN_DRAW ) + return _("Draw"); + + if ( turnflags & TTT_TURN_WIN ) + { + if ( (turnflags&TTT_TURN_TEAM) != minigame_self.team ) + return _("You lost the game!\nSelect \"^1Next Match^7\" on the menu for a rematch!"); + return _("You win!\nSelect \"^1Next Match^7\" on the menu to start a new match!"); + } + + if ( turnflags & TTT_TURN_NEXT ) + { + if ( (turnflags&TTT_TURN_TEAM) != minigame_self.team ) + return _("Select \"^1Next Match^7\" on the menu to start a new match!"); + return _("Wait for your opponent to confirm the rematch"); + } + + if ( (turnflags & TTT_TURN_TEAM) != minigame_self.team ) + return _("Wait for your opponent to make their move"); + + if ( turnflags & TTT_TURN_PLACE ) + return _("Click on the game board to place your piece"); + + return ""; +} + +const float TTT_AI_POSFLAG_A1 = 0x0001; +const float TTT_AI_POSFLAG_A2 = 0x0002; +const float TTT_AI_POSFLAG_A3 = 0x0004; +const float TTT_AI_POSFLAG_B1 = 0x0008; +const float TTT_AI_POSFLAG_B2 = 0x0010; +const float TTT_AI_POSFLAG_B3 = 0x0020; +const float TTT_AI_POSFLAG_C1 = 0x0040; +const float TTT_AI_POSFLAG_C2 = 0x0080; +const float TTT_AI_POSFLAG_C3 = 0x0100; + +// convert a flag to a position +string ttt_ai_piece_flag2pos(float pieceflag) +{ + switch(pieceflag) + { + case TTT_AI_POSFLAG_A1: + return "a1"; + case TTT_AI_POSFLAG_A2: + return "a2"; + case TTT_AI_POSFLAG_A3: + return "a3"; + + case TTT_AI_POSFLAG_B1: + return "b1"; + case TTT_AI_POSFLAG_B2: + return "b2"; + case TTT_AI_POSFLAG_B3: + return "b3"; + + case TTT_AI_POSFLAG_C1: + return "c1"; + case TTT_AI_POSFLAG_C2: + return "c2"; + case TTT_AI_POSFLAG_C3: + return "c3"; + + default: + return string_null; + } +} + +float ttt_ai_checkmask(float piecemask, float checkflags) +{ + return checkflags && (piecemask & checkflags) == checkflags; +} + +// get the third flag if the mask matches two of them +float ttt_ai_1of3(float piecemask, float flag1, float flag2, float flag3) +{ + if ( ttt_ai_checkmask(piecemask,flag1|flag2|flag3) ) + return 0; + + if ( ttt_ai_checkmask(piecemask,flag1|flag2) ) + return flag3; + + if ( ttt_ai_checkmask(piecemask,flag3|flag2) ) + return flag1; + + if ( ttt_ai_checkmask(piecemask,flag3|flag1) ) + return flag2; + + return 0; +} + +// Select a random flag in the mask +float ttt_ai_random(float piecemask) +{ + if ( !piecemask ) + return 0; + + float i; + float f = 1; + + RandomSelection_Init(); + + for ( i = 0; i < 9; i++ ) + { + if ( piecemask & f ) + RandomSelection_Add(world, f, string_null, 1, 1); + f <<= 1; + } + + dprint(sprintf("TTT AI: selected %x from %x\n", + RandomSelection_chosen_float, piecemask) ); + return RandomSelection_chosen_float; +} + +// Block/complete a 3 i na row +float ttt_ai_block3 ( float piecemask, float piecemask_free ) +{ + float r = 0; + + r |= ttt_ai_1of3(piecemask,TTT_AI_POSFLAG_A1,TTT_AI_POSFLAG_A2,TTT_AI_POSFLAG_A3); + r |= ttt_ai_1of3(piecemask,TTT_AI_POSFLAG_B1,TTT_AI_POSFLAG_B2,TTT_AI_POSFLAG_B3); + r |= ttt_ai_1of3(piecemask,TTT_AI_POSFLAG_C1,TTT_AI_POSFLAG_C2,TTT_AI_POSFLAG_C3); + r |= ttt_ai_1of3(piecemask,TTT_AI_POSFLAG_A1,TTT_AI_POSFLAG_B1,TTT_AI_POSFLAG_C1); + r |= ttt_ai_1of3(piecemask,TTT_AI_POSFLAG_A2,TTT_AI_POSFLAG_B2,TTT_AI_POSFLAG_C2); + r |= ttt_ai_1of3(piecemask,TTT_AI_POSFLAG_A3,TTT_AI_POSFLAG_B3,TTT_AI_POSFLAG_C3); + r |= ttt_ai_1of3(piecemask,TTT_AI_POSFLAG_A1,TTT_AI_POSFLAG_B2,TTT_AI_POSFLAG_C3); + r |= ttt_ai_1of3(piecemask,TTT_AI_POSFLAG_A3,TTT_AI_POSFLAG_B2,TTT_AI_POSFLAG_C1); + dprint(sprintf("TTT AI: possible 3 in a rows in %x: %x (%x)\n",piecemask,r, r&piecemask_free)); + r &= piecemask_free; + return ttt_ai_random(r); +} + +// Simple AI +// 1) tries to win the game if possible +// 2) tries to block the opponent if they have 2 in a row +// 3) places a piece randomly +string ttt_ai_choose_simple(float piecemask_self, float piecemask_opponent, float piecemask_free ) +{ + float move = 0; + + dprint("TTT AI: checking winning move\n"); + if (( move = ttt_ai_block3(piecemask_self,piecemask_free) )) + return ttt_ai_piece_flag2pos(move); // place winning move + + dprint("TTT AI: checking opponent's winning move\n"); + if (( move = ttt_ai_block3(piecemask_opponent,piecemask_free) )) + return ttt_ai_piece_flag2pos(move); // block opponent + + dprint("TTT AI: random move\n"); + return ttt_ai_piece_flag2pos(ttt_ai_random(piecemask_free)); +} + +// AI move (if it's AI's turn) +void ttt_aimove(entity minigame) +{ + if ( minigame.minigame_flags == (TTT_TURN_PLACE|minigame.ttt_ai) ) + { + entity aiplayer = world; + while ( ( aiplayer = findentity(aiplayer,owner,minigame) ) ) + if ( aiplayer.classname == "minigame_player" && !aiplayer.minigame_playerslot ) + break; + + /* + * Build bit masks for the board pieces + * .---.---.---. + * | 4 | 32|256| 3 + * |---+---+---| + * | 2 | 16|128| 2 + * |---+---+---| + * | 1 | 8 | 64| 1 + * '---'---'---' + * A B C + */ + float piecemask_self = 0; + float piecemask_opponent = 0; + float piecemask_free = 0; + float pieceflag = 1; + string pos; + + float i,j; + for ( i = 0; i < 3; i++ ) + for ( j = 0; j < 3; j++ ) + { + pos = minigame_tile_buildname(i,j); + entity piece = ttt_find_piece(minigame,pos); + if ( piece ) + { + if ( piece.team == aiplayer.team ) + piecemask_self |= pieceflag; + else + piecemask_opponent |= pieceflag; + } + else + piecemask_free |= pieceflag; + pieceflag <<= 1; + } + + // TODO multiple AI difficulties + dprint(sprintf("TTT AI: self: %x opponent: %x free: %x\n", + piecemask_self, piecemask_opponent, piecemask_free)); + pos = ttt_ai_choose_simple(piecemask_self, piecemask_opponent, piecemask_free); + dprint("TTT AI: chosen move: ",pos,"\n\n"); + if ( !pos ) + dprint("Tic Tac Toe AI has derped!\n"); + else + ttt_move(minigame,aiplayer,pos); + } + minigame.message = ttt_turn_to_string(minigame.minigame_flags); +} + +// Make the correct move +void ttt_make_move(entity minigame) +{ + if ( minigame.minigame_flags == (TTT_TURN_PLACE|minigame_self.team) ) + { + if ( minigame.ttt_ai ) + { + ttt_move(minigame, minigame_self, ttt_curr_pos ); + ttt_aimove(minigame); + } + else + minigame_cmd("move ",ttt_curr_pos); + } +} + +void ttt_set_curr_pos(string s) +{ + if ( ttt_curr_pos ) + strunzone(ttt_curr_pos); + if ( s ) + s = strzone(s); + ttt_curr_pos = s; +} + +// Required function, handle client events +float minigame_event_ttt(entity minigame, string event, ...) +{ + switch(event) + { + case "activate": + { + ttt_set_curr_pos(""); + minigame.message = ttt_turn_to_string(minigame.minigame_flags); - return FALSE; ++ return false; + } + case "key_pressed": + { + if((minigame.minigame_flags & TTT_TURN_TEAM) == minigame_self.team) + { + switch ( ...(0,float) ) + { + case K_RIGHTARROW: + case K_KP_RIGHTARROW: + if ( ! ttt_curr_pos ) + ttt_set_curr_pos("a3"); + else + ttt_set_curr_pos(minigame_relative_tile(ttt_curr_pos,1,0,3,3)); - return TRUE; ++ return true; + case K_LEFTARROW: + case K_KP_LEFTARROW: + if ( ! ttt_curr_pos ) + ttt_set_curr_pos("c3"); + else + ttt_set_curr_pos(minigame_relative_tile(ttt_curr_pos,-1,0,3,3)); - return TRUE; ++ return true; + case K_UPARROW: + case K_KP_UPARROW: + if ( ! ttt_curr_pos ) + ttt_set_curr_pos("a1"); + else + ttt_set_curr_pos(minigame_relative_tile(ttt_curr_pos,0,1,3,3)); - return TRUE; ++ return true; + case K_DOWNARROW: + case K_KP_DOWNARROW: + if ( ! ttt_curr_pos ) + ttt_set_curr_pos("a3"); + else + ttt_set_curr_pos(minigame_relative_tile(ttt_curr_pos,0,-1,3,3)); - return TRUE; ++ return true; + case K_ENTER: + case K_KP_ENTER: + case K_SPACE: + ttt_make_move(minigame); - return TRUE; ++ return true; + } + } + - return FALSE; ++ return false; + } + case "mouse_pressed": + { + if(...(0,float) == K_MOUSE1) + { + ttt_make_move(minigame); - return TRUE; ++ return true; + } + - return FALSE; ++ return false; + } + case "mouse_moved": + { + vector mouse_pos = minigame_hud_normalize(mousepos,ttt_boardpos,ttt_boardsize); + if ( minigame.minigame_flags == (TTT_TURN_PLACE|minigame_self.team) ) + ttt_set_curr_pos(minigame_tile_name(mouse_pos,3,3)); + if ( ! ttt_valid_tile(ttt_curr_pos) ) + ttt_set_curr_pos(""); + - return TRUE; ++ return true; + } + case "network_receive": + { + entity sent = ...(0,entity); + float sf = ...(1,float); + if ( sent.classname == "minigame" ) + { + if ( sf & MINIG_SF_UPDATE ) + { + sent.message = ttt_turn_to_string(sent.minigame_flags); + if ( sent.minigame_flags & minigame_self.team ) + minigame_prompt(); + } + + if ( (sf & TTT_SF_SINGLEPLAYER) ) + { + float ai = ReadByte(); + float spawnai = ai && !sent.ttt_ai; + sent.ttt_ai = ai; + + if ( spawnai ) + { + entity aiplayer = spawn(); + aiplayer.classname = "minigame_player"; + aiplayer.owner = minigame; + aiplayer.team = ai; + aiplayer.minigame_playerslot = 0; + aiplayer.minigame_autoclean = 1; + ttt_aimove(minigame); + } + + } + } + else if ( sent.classname == "minigame_player" && (sf & TTT_SF_PLAYERSCORE ) ) + { + sent.minigame_flags = ReadByte(); + } + - return FALSE; ++ return false; + } + case "menu_show": + { + HUD_MinigameMenu_CustomEntry(...(0,entity),_("Next Match"),"next"); + HUD_MinigameMenu_CustomEntry(...(0,entity),_("Single Player"),"singleplayer"); - return FALSE; ++ return false; + } + case "menu_click": + { + if(...(0,string) == "next") + { + if ( minigame.ttt_ai ) + { + ttt_next_match(minigame,minigame_self); + ttt_aimove(minigame); + } + else + minigame_cmd("next"); + } + else if ( ...(0,string) == "singleplayer" && !minigame.ttt_ai ) + { + if ( minigame_count_players(minigame) == 1 ) + minigame_cmd("singleplayer"); + } - return FALSE; ++ return false; + } + } + - return FALSE; ++ return false; +} + +#endif diff --cc qcsrc/common/minigames/minigames.qc index 6b8dbf355,000000000..46f0a9c57 mode 100644,000000..100644 --- a/qcsrc/common/minigames/minigames.qc +++ b/qcsrc/common/minigames/minigames.qc @@@ -1,129 -1,0 +1,131 @@@ ++#include "minigames.qh" ++ +entity minigame_get_descriptor(string id) +{ + entity e; + for ( e = minigame_descriptors; e != world; e = e.list_next ) + if ( e.netname == id ) + return e; + return world; +} + +// Get letter index of a tile name +float minigame_tile_letter(string id) +{ + return str2chr(substring(id,0,1),0)-'a'; +} + +// Get number index of a tile name +// Note: this is 0 based, useful for mathematical operations +// Note: Since the tile notation starts from the bottom left, +// you may want to do number_of_rows - what_this_function_returns or something +float minigame_tile_number(string id) +{ + return stof(substring(id,1,-1)) -1 ; +} + +// Get relative position of the center of a given tile +vector minigame_tile_pos(string id, float rows, float columns) +{ + return eX*(minigame_tile_letter(id)+0.5)/columns + + eY - eY*(minigame_tile_number(id)+0.5)/rows; +} + +// Get a tile name from indices +string minigame_tile_buildname(float letter, float number) +{ + return strcat(chr2str('a'+letter),ftos(number+1)); +} + +// Get the id of a tile relative to the given one +string minigame_relative_tile(string start_id, float dx, float dy, float rows, float columns) +{ + float letter = minigame_tile_letter(start_id); + float number = minigame_tile_number(start_id); + letter = (letter+dx) % columns; + number = (number+dy) % rows; + if ( letter < 0 ) + letter = columns + letter; + if ( number < 0 ) + number = rows + number; + return minigame_tile_buildname(letter, number); +} + +// Get tile name from a relative position (matches the tile covering a square area) +string minigame_tile_name(vector pos, float rows, float columns) +{ + if ( pos_x < 0 || pos_x > 1 || pos_y < 0 || pos_y > 1 ) + return ""; // no tile + + float letter = floor(pos_x * columns); + float number = floor((1-pos_y) * rows); + return minigame_tile_buildname(letter, number); +} + +// Get the next team number (note: team numbers are between 1 and n_teams, inclusive) +float minigame_next_team(float curr_team, float n_teams) +{ + return curr_team % n_teams + 1; +} + +// set send flags only when on server +// (for example in game logic which can be used both in client and server +void minigame_server_sendflags(entity ent, float mgflags) +{ + #ifdef SVQC + ent.SendFlags |= mgflags; + #endif +} + +// Spawn linked entity on the server or local entity on the client +// This entity will be removed automatically when the minigame ends +entity msle_spawn(entity minigame_session, string class_name) +{ + entity e = spawn(); + e.classname = class_name; + e.owner = minigame_session; + e.minigame_autoclean = 1; + #ifdef SVQC + e.customizeentityforclient = minigame_CheckSend; - Net_LinkEntity(e, FALSE, 0, minigame_SendEntity); ++ Net_LinkEntity(e, false, 0, minigame_SendEntity); + #endif + return e; +} + +const float msle_base_id = 2; +float msle_id(string class_name) +{ + if ( class_name == "minigame" ) return 1; + if ( class_name == "minigame_player" ) return 2; + float i = msle_base_id; +#define MSLE(Name, Fields) i++; if ( class_name == #Name ) return i; + MINIGAME_SIMPLELINKED_ENTITIES +#undef MSLE + return 0; +} + +string msle_classname(float id) +{ + if ( id == 1 ) return "minigame"; + if ( id == 2 ) return "minigame_player"; + float i = msle_base_id; +#define MSLE(Name, Fields) i++; if ( id == i ) return #Name; + MINIGAME_SIMPLELINKED_ENTITIES +#undef MSLE + return ""; +} + +float minigame_count_players(entity minigame) +{ + float pl_num = 0; + entity e; +#ifdef SVQC + for(e = minigame.minigame_players; e; e = e.list_next) +#elif defined(CSQC) + e = world; + while( (e = findentity(e,owner,minigame)) ) + if ( e.classname == "minigame_player" ) +#endif + pl_num++; + return pl_num; +} diff --cc qcsrc/common/minigames/minigames.qh index a9aa92272,000000000..3b8bbafb4 mode 100644,000000..100644 --- a/qcsrc/common/minigames/minigames.qh +++ b/qcsrc/common/minigames/minigames.qh @@@ -1,119 -1,0 +1,124 @@@ ++#ifndef MINIGAMES_H ++#define MINIGAMES_H ++ +entity minigame_descriptors; + +// previous node in a doubly linked list +.entity list_prev; +// next node in a linked list +.entity list_next; + +entity minigame_get_descriptor(string id); + +// Get letter index of a tile name +float minigame_tile_letter(string id); + +// Get number index of a tile name +// Note: this is 0 based, useful for mathematical operations +// Note: Since the tile notation starts from the bottom left, +// you may want to do number_of_rows - what_this_function_returns or something +float minigame_tile_number(string id); + +// Get relative position of the center of a given tile +vector minigame_tile_pos(string id, float rows, float columns); + +// Get a tile name from indices +string minigame_tile_buildname(float letter, float number); + +// Get the id of a tile relative to the given one +string minigame_relative_tile(string start_id, float dx, float dy, float rows, float columns); + +// Get tile name from a relative position (matches the tile covering a square area) +string minigame_tile_name(vector pos, float rows, float columns); + +// Get the next team number (note: team numbers are between 1 and n_teams, inclusive) +float minigame_next_team(float curr_team, float n_teams); + +// set send flags only when on server +// (for example in game logic which can be used both in client and server +void minigame_server_sendflags(entity ent, float mgflags); + +// count the number of players in a minigame session +float minigame_count_players(entity minigame); + +/// For minigame sessions: minigame descriptor object +.entity descriptor; + +/// For minigame sessions/descriptors: execute the given event +/// Client events: +/// mouse_moved(vector mouse_pos) +/// return 1 to handle input, 0 to discard +/// mouse_pressed/released(float K_Keycode) +/// return 1 to handle input, 0 to discard +/// note: see dpdefs/keycodes.qc for values +/// key_pressed/released(float K_Keycode) +/// return 1 to handle input, 0 to discard +/// note: see dpdefs/keycodes.qc for values +/// activate() +/// executed when the minigame is activated for the current client +/// deactivate() +/// executed when the minigame is deactivated for the current client +/// network_receive(entity received,float flags) +/// executed each time a networked entity is received +/// note: when this is called self == ...(0,entity) +/// You can use the MINIG_SF_ constants to check the send flags +/// IMPORTANT: always read in client everything you send from the server! +/// menu_show(entity parent_menu_item) +/// executed when the Current Game menu is shown, used to add custom entries +/// Call HUD_MinigameMenu_CustomEntry to do so (pass ...(0,entity) as first argument) +/// menu_click(string arg) +/// executed when a custom menu entry is clicked +/// Server events: +/// start() +/// executed when the minigame session is starting +/// end() +/// executed when the minigame session is shutting down +/// join(entity player) +/// executed when a player wants to join the session +/// return the player team number to accept the new player, 0 to discard +/// part(entity player) +/// executed when a player is going to leave the session +/// network_send(entity sent,float flags) +/// executed each time a networked entity is sent +/// note: when this is called self == ...(0,entity) +/// You can use the MINIG_SF_ constants to check the send flags +/// IMPORTANT: always read in client everything you send from the server! +/// cmd(entity minigame_player, float argc, string command) +/// self = client entity triggering this +/// argv(n) = console token +/// argc: number of console tokens +/// command: full command string +/// triggered when a player does "cmd minigame ..." with some unrecognized command +/// return 1 if the minigame has handled the command +/// impulse(entity minigame_player,float impulse) +/// self = client entity triggering this +/// triggered when a player does "impulse ..." +/// return 1 if the minigame has handled the impulse +.float(entity,string,...) minigame_event; + +// For run-time gameplay entities: Whether to be removed when the game is deactivated +.float minigame_autoclean; + +// For run-time gameplay entities: some place to store flags safely +.float minigame_flags; + +// Send flags, set to .SendFlags on networked entities to send entity information +// Flag values for customized events must be powers of 2 in the range +// [MINIG_SF_CUSTOM, MINIG_SF_MAX] (inclusive) +const float MINIG_SF_CREATE = 0x01; // Create a new object +const float MINIG_SF_UPDATE = 0x02; // miscellaneous entity update +const float MINIG_SF_CUSTOM = 0x10; // a customized networked event +const float MINIG_SF_MAX = 0x80; // maximum flag value sent over the network +const float MINIG_SF_ALL = 0xff; // use to resend everything + + +// Spawn linked entity on the server or local entity on the client +// This entity will be removed automatically when the minigame ends +entity msle_spawn(entity minigame_session, string class_name); + +#include "minigame/all.qh" + +float msle_id(string class_name); - string msle_classname(float id); ++string msle_classname(float id); ++ ++#endif diff --cc qcsrc/common/minigames/sv_minigames.qc index 0683f6aa6,000000000..1953e9674 mode 100644,000000..100644 --- a/qcsrc/common/minigames/sv_minigames.qc +++ b/qcsrc/common/minigames/sv_minigames.qc @@@ -1,428 -1,0 +1,430 @@@ ++#include "minigames.qh" ++ +void player_clear_minigame(entity player) +{ + player.active_minigame = world; + if ( IS_PLAYER(player) ) + player.movetype = MOVETYPE_WALK; + else + player.movetype = MOVETYPE_FLY_WORLDONLY; + player.team_forced = 0; +} + +void minigame_rmplayer(entity minigame_session, entity player) +{ + entity e; + entity p = minigame_session.minigame_players; + + if ( p.minigame_players == player ) + { + if ( p.list_next == world ) + { + end_minigame(minigame_session); + return; + } + minigame_session.minigame_event(minigame_session,"part",player); + GameLogEcho(strcat(":minigame:part:",minigame_session.netname,":", + ftos(num_for_edict(player)),":",player.netname)); + minigame_session.minigame_players = p.list_next; + remove ( p ); + player_clear_minigame(player); + } + else + { + for ( e = p.list_next; e != world; e = e.list_next ) + { + if ( e.minigame_players == player ) + { + minigame_session.minigame_event(minigame_session,"part",player); + GameLogEcho(strcat(":minigame:part:",minigame_session.netname,":", + ftos(num_for_edict(player)),":",player.netname)); + p.list_next = e.list_next; + remove(e); + player_clear_minigame(player); + return; + } + p = e; + } + } +} + + +#define FIELD(Flags, Type,Name) if ( sf & (Flags) ) Write##Type(MSG_ENTITY, self.Name); +#define WriteVector(to,Name) WriteCoord(to,Name##_x); WriteCoord(to,Name##_y); WriteCoord(to,Name##_z) +#define WriteVector2D(to,Name) WriteCoord(to,Name##_x); WriteCoord(to,Name##_y) +#define WriteFloat WriteCoord +#define MSLE(Name,Fields) \ + else if ( self.classname == #Name ) { \ + if ( sf & MINIG_SF_CREATE ) WriteString(MSG_ENTITY,self.owner.netname); \ + Fields } + +// Send an entity to a client +// only use on minigame entities or entities with a minigame owner +float minigame_SendEntity(entity to, float sf) +{ + WriteByte(MSG_ENTITY, ENT_CLIENT_MINIGAME); + WriteByte(MSG_ENTITY, sf); + + if ( sf & MINIG_SF_CREATE ) + { + WriteShort(MSG_ENTITY,msle_id(self.classname)); + WriteString(MSG_ENTITY,self.netname); + } + + entity minigame_ent = self.owner; + + if ( self.classname == "minigame" ) + { + minigame_ent = self; + + if ( sf & MINIG_SF_CREATE ) + WriteString(MSG_ENTITY,self.descriptor.netname); + + if ( sf & MINIG_SF_UPDATE ) + WriteLong(MSG_ENTITY,self.minigame_flags); + } + else if ( self.classname == "minigame_player" ) + { + if ( sf & MINIG_SF_CREATE ) + { + WriteString(MSG_ENTITY,self.owner.netname); + WriteLong(MSG_ENTITY,num_for_edict(self.minigame_players)); + } + if ( sf & MINIG_SF_UPDATE ) + WriteByte(MSG_ENTITY,self.team); + } + MINIGAME_SIMPLELINKED_ENTITIES + + minigame_ent.minigame_event(minigame_ent,"network_send",self,sf); + + return 1; + +} +#undef FIELD +#undef MSLE +#undef WriteFloat + +// Force resend all minigame entities +void minigame_resend(entity minigame) +{ + minigame.SendFlags = MINIG_SF_ALL; + entity e = world; + while (( e = findentity(e,owner,minigame) )) + { + e.SendFlags = MINIG_SF_ALL; + } +} + +float minigame_CheckSend() +{ + entity e; + for ( e = self.owner.minigame_players; e != world; e = e.list_next ) + if ( e.minigame_players == other ) + return 1; + return 0; +} + +float minigame_addplayer(entity minigame_session, entity player) +{ + if ( player.active_minigame ) + { + if ( player.active_minigame == minigame_session ) + return 0; + minigame_rmplayer(player.active_minigame,player); + } + + float mgteam = minigame_session.minigame_event(minigame_session,"join",player); + + if ( mgteam ) + { + entity player_pointer = spawn(); + player_pointer.classname = "minigame_player"; + player_pointer.owner = minigame_session; + player_pointer.minigame_players = player; + player_pointer.team = mgteam; + player_pointer.list_next = minigame_session.minigame_players; + minigame_session.minigame_players = player_pointer; + player.active_minigame = minigame_session; + player_pointer.customizeentityforclient = minigame_CheckSend; - Net_LinkEntity(player_pointer, FALSE, 0, minigame_SendEntity); ++ Net_LinkEntity(player_pointer, false, 0, minigame_SendEntity); + + if ( !IS_OBSERVER(player) && autocvar_sv_minigames_observer ) + { + entity e = self; + self = player; + PutObserverInServer(); + self = e; + } + if ( autocvar_sv_minigames_observer == 2 ) + player.team_forced = -1; + + minigame_resend(minigame_session); + } + GameLogEcho(strcat(":minigame:join",(mgteam?"":"fail"),":",minigame_session.netname,":", + ftos(num_for_edict(player)),":",player.netname)); + + return mgteam; +} + +entity start_minigame(entity player, string minigame ) +{ + if ( !autocvar_sv_minigames || !IS_REAL_CLIENT(player) ) + return world; + + entity e = minigame_get_descriptor(minigame); + if ( e ) + { + entity minig = spawn(); + minig.classname = "minigame"; + minig.netname = strzone(strcat(e.netname,"_",ftos(num_for_edict(minig)))); + minig.descriptor = e; + minig.minigame_event = e.minigame_event; + minig.minigame_event(minig,"start"); + GameLogEcho(strcat(":minigame:start:",minig.netname)); + if ( ! minigame_addplayer(minig,player) ) + { + dprint("Minigame ",minig.netname," rejected the first player join!\n"); + end_minigame(minig); + return world; + } - Net_LinkEntity(minig, FALSE, 0, minigame_SendEntity); ++ Net_LinkEntity(minig, false, 0, minigame_SendEntity); + + if ( !minigame_sessions ) + minigame_sessions = minig; + else + { + minigame_sessions.owner = minig; + minig.list_next = minigame_sessions; + minigame_sessions = minig; + } + return minig; + } + + return world; +} + +entity join_minigame(entity player, string game_id ) +{ + if ( !autocvar_sv_minigames || !IS_REAL_CLIENT(player) ) + return world; + + entity minig; + for ( minig = minigame_sessions; minig != world; minig = minig.list_next ) + { + if ( minig.netname == game_id ) + if ( minigame_addplayer(minig,player) ) + return minig; + } + + return world; +} + +void part_minigame(entity player ) +{ + entity minig = player.active_minigame; + + if ( minig && minig.classname == "minigame" ) + minigame_rmplayer(minig,player); +} + +void end_minigame(entity minigame_session) +{ + if ( minigame_session.owner ) + minigame_session.owner.list_next = minigame_session.list_next; + else + minigame_sessions = minigame_session.list_next; + + minigame_session.minigame_event(minigame_session,"end"); + GameLogEcho(strcat(":minigame:end:",minigame_session.netname)); + + + entity e = world; + while( (e = findentity(e, owner, minigame_session)) ) + if ( e.minigame_autoclean ) + { + dprint("SV Auto-cleaned: ",ftos(num_for_edict(e)), " (",e.classname,")\n"); + remove(e); + } + + entity p; + for ( e = minigame_session.minigame_players; e != world; e = p ) + { + p = e.list_next; + player_clear_minigame(e.minigame_players); + remove(e); + } + + strunzone(minigame_session.netname); + remove(minigame_session); +} + +void end_minigames() +{ + while ( minigame_sessions ) + { + end_minigame(minigame_sessions); + } +} + +void initialize_minigames() +{ + entity last_minig = world; + entity minig; + #define MINIGAME(name,nicename) \ + minig = spawn(); \ + minig.classname = "minigame_descriptor"; \ + minig.netname = #name; \ + minig.message = nicename; \ + minig.minigame_event = minigame_event_##name; \ + if ( !last_minig ) minigame_descriptors = minig; \ + else last_minig.list_next = minig; \ + last_minig = minig; + + REGISTERED_MINIGAMES + + #undef MINIGAME +} + +string invite_minigame(entity inviter, entity player) +{ + if ( !inviter || !inviter.active_minigame ) + return "Invalid minigame"; - if ( !VerifyClientEntity(player, TRUE, FALSE) ) ++ if ( !VerifyClientEntity(player, true, false) ) + return "Invalid player"; + if ( inviter == player ) + return "You can't invite yourself"; + if ( player.active_minigame == inviter.active_minigame ) + return strcat(player.netname," is already playing"); + + Send_Notification(NOTIF_ONE, player, MSG_INFO, INFO_MINIGAME_INVITE, + inviter.active_minigame.netname, inviter.netname ); + + GameLogEcho(strcat(":minigame:invite:",inviter.active_minigame.netname,":", + ftos(num_for_edict(player)),":",player.netname)); + + return ""; +} + +entity minigame_find_player(entity client) +{ + if ( ! client.active_minigame ) + return world; + entity e; + for ( e = client.active_minigame.minigame_players; e; e = e.list_next ) + if ( e.minigame_players == client ) + return e; + return world; +} + +float MinigameImpulse(float imp) +{ + entity e = minigame_find_player(self); + if ( imp && self.active_minigame && e ) + { + return self.active_minigame.minigame_event(self.active_minigame,"impulse",e,imp); + } + return 0; +} + + + +void ClientCommand_minigame(float request, float argc, string command) +{ + if ( !autocvar_sv_minigames ) + { + sprint(self,"Minigames are not enabled!\n"); + return; + } + + if (request == CMD_REQUEST_COMMAND ) + { + string minig_cmd = argv(1); + if ( minig_cmd == "create" && argc > 2 ) + { + entity minig = start_minigame(self, argv(2)); + if ( minig ) + sprint(self,"Created minigame session: ",minig.netname,"\n"); + else + sprint(self,"Cannot start minigame session!\n"); + return; + } + else if ( minig_cmd == "join" && argc > 2 ) + { + entity minig = join_minigame(self, argv(2)); + if ( minig ) + sprint(self,"Joined: ",minig.netname,"\n"); + else + { + Send_Notification(NOTIF_ONE, self, MSG_CENTER, CENTER_JOIN_PREVENT_MINIGAME); + sprint(self,"Cannot join given minigame session!\n"); + } + return; + } + else if ( minig_cmd == "list" ) + { + entity e; + for ( e = minigame_descriptors; e != world; e = e.list_next ) + sprint(self,e.netname," (",e.message,") ","\n"); + return; + } + else if ( minig_cmd == "list-sessions" ) + { + entity e; + for ( e = minigame_sessions; e != world; e = e.list_next ) + sprint(self,e.netname,"\n"); + return; + } + else if ( minig_cmd == "end" || minig_cmd == "part" ) + { + if ( self.active_minigame ) + { + part_minigame(self); + sprint(self,"Left minigame session\n"); + } + else + sprint(self,"You aren't playing any minigame...\n"); + return; + } + else if ( minig_cmd == "invite" && argc > 2 ) + { + if ( self.active_minigame ) + { + entity client = GetIndexedEntity(argc, 2); + string error = invite_minigame(self,client); + if ( error == "" ) + { + sprint(self,"You have invited ",client.netname, + " to join your game of ", self.active_minigame.descriptor.message, "\n"); + } + else + sprint(self,"Could not invite: ", error, ".\n"); + } + else + sprint(self,"You aren't playing any minigame...\n"); + return; + } + else if ( self.active_minigame ) + { + entity e = minigame_find_player(self); + string subcommand = substring(command,argv_end_index(0),-1); + float arg_c = tokenize_console(subcommand); + if ( self.active_minigame.minigame_event(self.active_minigame,"cmd",e,arg_c,subcommand) ) + return; + + } + else sprint(self,strcat("Wrong command:^1 ",command,"\n")); + } + + sprint(self, "\nUsage:^3 cmd minigame create \n"); + sprint(self, " Start a new minigame session\n"); + sprint(self, "Usage:^3 cmd minigame join \n"); + sprint(self, " Join an exising minigame session\n"); + sprint(self, "Usage:^3 cmd minigame list\n"); + sprint(self, " List available minigames\n"); + sprint(self, "Usage:^3 cmd minigame list-sessions\n"); + sprint(self, " List available minigames sessions\n"); + sprint(self, "Usage:^3 cmd minigame part|end\n"); + sprint(self, " Leave the current minigame\n"); + sprint(self, "Usage:^3 cmd minigame invite \n"); + sprint(self, " Invite the given player to join you in a minigame\n"); +} diff --cc qcsrc/common/minigames/sv_minigames.qh index a8d7d6f7a,000000000..c9591bb13 mode 100644,000000..100644 --- a/qcsrc/common/minigames/sv_minigames.qh +++ b/qcsrc/common/minigames/sv_minigames.qh @@@ -1,51 -1,0 +1,54 @@@ - ++#ifndef SV_MINIGAMES_H ++#define SV_MINIGAMES_H + +/// Initialize the minigame system +void initialize_minigames(); + +/// Create a new minigame session +/// \return minigame session entity +entity start_minigame(entity player, string minigame ); + +/// Join an existing minigame session +/// \return minigame session entity +entity join_minigame(entity player, string game_id ); + +/// Invite a player to join in a minigame +/// \return Error string +string invite_minigame(entity inviter, entity player); + +// Part minigame session +void part_minigame(entity player); + +// Ends a minigame session +void end_minigame(entity minigame_session); + +// Ends all minigame sessions +void end_minigames(); + +// Only sends entities to players who joined the minigame +// Use on customizeentityforclient for gameplay entities +float minigame_CheckSend(); + +// Check for minigame impulses +float MinigameImpulse(float imp); + +// Parse a client command ( cmd minigame ... ) +void ClientCommand_minigame(float request, float argc, string command); + +// Find the minigame_player entity for the given client entity +entity minigame_find_player(entity client); + +/// For players: Minigame being played +.entity active_minigame; + +/// For minigame sessions: list of players +/// For minigame_player: client entity +.entity minigame_players; + +entity minigame_sessions; + +float minigame_SendEntity(entity to, float sf); + +var void remove(entity e); ++ ++#endif diff --cc qcsrc/common/monsters/all.qh index d827d45d8,c79a4e656..ec47d753e --- a/qcsrc/common/monsters/all.qh +++ b/qcsrc/common/monsters/all.qh @@@ -1,19 -1,11 +1,29 @@@ + // TODO: include once + //#ifndef MONSTERS_ALL_H + //#define MONSTERS_ALL_H + ++#ifdef SVQC ++#include "sv_monsters.qh" ++#endif ++ #include "monster/zombie.qc" #include "monster/spider.qc" #include "monster/mage.qc" -#include "monster/wyvern.qc" +#ifndef MONSTERS_EXTRA - #include "monster/wyvern.qc" ++ #include "monster/wyvern.qc" +#endif #include "monster/shambler.qc" +#include "monster/creeper.qc" +#ifdef MONSTERS_EXTRA - #include "monster/demon.qc" - #include "monster/rotfish.qc" - #include "monster/ogre.qc" - #include "monster/spawn.qc" - #include "monster/scrag.qc" - #include "monster/rottweiler.qc" - #include "monster/vore.qc" - #include "monster/afrit.qc" - #include "monster/enforcer.qc" ++ #include "monster/demon.qc" ++ #include "monster/rotfish.qc" ++ #include "monster/ogre.qc" ++ #include "monster/spawn.qc" ++ #include "monster/scrag.qc" ++ #include "monster/rottweiler.qc" ++ #include "monster/vore.qc" ++ #include "monster/afrit.qc" ++ #include "monster/enforcer.qc" +#endif + + //#endif diff --cc qcsrc/common/monsters/monster/afrit.qc index 5d9c01d91,000000000..0db2af618 mode 100644,000000..100644 --- a/qcsrc/common/monsters/monster/afrit.qc +++ b/qcsrc/common/monsters/monster/afrit.qc @@@ -1,192 -1,0 +1,194 @@@ +#ifdef REGISTER_MONSTER +REGISTER_MONSTER( +/* MON_##id */ AFRIT, +/* functions */ M_Afrit, M_Afrit_Attack, +/* spawnflags */ MONSTER_SIZE_BROKEN | MON_FLAG_RANGED | MONSTER_TYPE_FLY, +/* mins,maxs */ '-16 -16 -24', '16 16 24', +/* model */ "afrit.mdl", +/* netname */ "afrit", +/* fullname */ _("Afrit") +); + +#else +#ifdef SVQC ++#include "../../server/mutators/base.qh" ++ +float autocvar_g_monster_afrit_health; +var float autocvar_g_monster_afrit_damageforcescale = 0.3; +float autocvar_g_monster_afrit_attack_fireball_damage; +float autocvar_g_monster_afrit_attack_fireball_edgedamage; +float autocvar_g_monster_afrit_attack_fireball_damagetime; +float autocvar_g_monster_afrit_attack_fireball_force; +float autocvar_g_monster_afrit_attack_fireball_radius; +float autocvar_g_monster_afrit_attack_fireball_speed; +float autocvar_g_monster_afrit_speed_stop; +float autocvar_g_monster_afrit_speed_run; +float autocvar_g_monster_afrit_speed_walk; + +const float afrit_anim_sleep = 0; +const float afrit_anim_getup = 1; +const float afrit_anim_fly = 2; +const float afrit_anim_pain = 3; +const float afrit_anim_attack = 4; +const float afrit_anim_die1 = 5; +const float afrit_anim_die2 = 6; + +.float afrit_targetrange_backup; +.float afrit_targetcheck_delay; + +void M_Afrit_Attack_Fireball_Explode() +{ + entity e; + if(self) + { + pointparticles(particleeffectnum("fireball_explode"), self.origin, '0 0 0', 1); + + RadiusDamage(self, self.realowner, (autocvar_g_monster_afrit_attack_fireball_damage), (autocvar_g_monster_afrit_attack_fireball_edgedamage), + (autocvar_g_monster_afrit_attack_fireball_force), world, world, (autocvar_g_monster_afrit_attack_fireball_radius), self.projectiledeathtype, world); + + for(e = world; (e = findfloat(e, takedamage, DAMAGE_AIM)); ) if(vlen(e.origin - self.origin) <= (autocvar_g_monster_afrit_attack_fireball_radius)) + Fire_AddDamage(e, self, 5 * MONSTER_SKILLMOD(self), (autocvar_g_monster_afrit_attack_fireball_damagetime), self.projectiledeathtype); + + remove(self); + } +} + +void M_Afrit_Attack_Fireball_Touch() +{ + PROJECTILE_TOUCH; + + M_Afrit_Attack_Fireball_Explode(); +} + +void M_Afrit_Attack_Fireball() +{ + entity missile = spawn(); + vector dir = normalize((self.enemy.origin + '0 0 10') - self.origin); + + monster_makevectors(self.enemy); + + missile.owner = missile.realowner = self; + missile.solid = SOLID_TRIGGER; + missile.movetype = MOVETYPE_FLYMISSILE; + missile.projectiledeathtype = DEATH_MONSTER_AFRIT; + setsize(missile, '-6 -6 -6', '6 6 6'); + setorigin(missile, self.origin + '0 0 13' + v_forward * 10); + missile.flags = FL_PROJECTILE; + missile.velocity = dir * (autocvar_g_monster_afrit_attack_fireball_speed); + missile.avelocity = '300 300 300'; + missile.nextthink = time + 5; + missile.think = M_Afrit_Attack_Fireball_Explode; + missile.enemy = self.enemy; + missile.touch = M_Afrit_Attack_Fireball_Touch; - CSQCProjectile(missile, TRUE, PROJECTILE_FIREMINE, TRUE); ++ CSQCProjectile(missile, true, PROJECTILE_FIREMINE, true); +} + +float M_Afrit_Attack(float attack_type) +{ + switch(attack_type) + { + case MONSTER_ATTACK_MELEE: + case MONSTER_ATTACK_RANGED: + { + self.attack_finished_single = time + 2; + self.anim_finished = time + 1.3; + self.frame = afrit_anim_attack; + + Monster_Delay(2, 0.3, 0.7, M_Afrit_Attack_Fireball); + - return TRUE; ++ return true; + } + } + - return FALSE; ++ return false; +} + +void spawnfunc_monster_afrit() { Monster_Spawn(MON_AFRIT); } + +float M_Afrit(float req) +{ + switch(req) + { + case MR_THINK: + { + if(self.target_range < 0) + { + if(time >= self.afrit_targetcheck_delay) + { + self.target_range = self.afrit_targetrange_backup; + if(Monster_FindTarget(self) != world) + { + self.frame = afrit_anim_getup; + self.anim_finished = time + 2.3; + } + else + { + self.target_range = -1; + self.afrit_targetcheck_delay = time + 1; + } + } + } + self.fire_endtime = 0; // never burns + + if(self.flags & FL_ONGROUND) + self.m_anim_idle = afrit_anim_sleep; + else + self.m_anim_idle = afrit_anim_fly; + + if(self.frame == afrit_anim_sleep) + self.armorvalue = 0.95; // almost invincible + else + self.armorvalue = autocvar_g_monsters_armor_blockpercent; - return TRUE; ++ return true; + } + case MR_PAIN: + { + if(frag_deathtype == DEATH_FIRE || frag_deathtype == DEATH_LAVA) + frag_damage = 0; // afrit doesn't burn + else + { + self.pain_finished = time + 0.6; + self.frame = afrit_anim_pain; + } - return TRUE; ++ return true; + } + case MR_DEATH: + { + self.frame = (random() >= 0.5) ? afrit_anim_die1 : afrit_anim_die2; + self.superweapons_finished = time + 20; - return TRUE; ++ return true; + } + case MR_SETUP: + { + if(!self.health) self.health = (autocvar_g_monster_afrit_health); + if(!self.speed) { self.speed = (autocvar_g_monster_afrit_speed_walk); } + if(!self.speed2) { self.speed2 = (autocvar_g_monster_afrit_speed_run); } + if(!self.stopspeed) { self.stopspeed = (autocvar_g_monster_afrit_speed_stop); } + if(!self.damageforcescale) { self.damageforcescale = (autocvar_g_monster_afrit_damageforcescale); } + + self.m_anim_walk = afrit_anim_fly; + self.m_anim_run = afrit_anim_fly; + self.m_anim_idle = afrit_anim_fly; + + if(!self.target_range) { self.afrit_targetrange_backup = autocvar_g_monsters_target_range; } + else { self.afrit_targetrange_backup = self.target_range; } + self.target_range = -1; // special handler + + self.weapon = WEP_FIREBALL; // hehehe + self.effects |= EF_FLAME; + - self.noalign = FALSE; // starts on ground ++ self.noalign = false; // starts on ground + - return TRUE; ++ return true; + } + case MR_PRECACHE: + { - return TRUE; ++ return true; + } + } + - return TRUE; ++ return true; +} + +#endif // SVQC +#endif // REGISTER_MONSTER diff --cc qcsrc/common/monsters/monster/creeper.qc index 1db708ce0,000000000..93228efe3 mode 100644,000000..100644 --- a/qcsrc/common/monsters/monster/creeper.qc +++ b/qcsrc/common/monsters/monster/creeper.qc @@@ -1,125 -1,0 +1,125 @@@ +#ifdef REGISTER_MONSTER +REGISTER_MONSTER( +/* MON_##id */ CREEPER, +/* functions */ M_Creeper, M_Creeper_Attack, +/* spawnflags */ MON_FLAG_MELEE | MON_FLAG_RIDE, +/* mins,maxs */ '-18 -18 -25', '18 18 40', +/* model */ "creeper.dpm", +/* netname */ "creeper", +/* fullname */ _("Creeper") +); + +#else +#ifdef SVQC +float autocvar_g_monster_creeper_health; +var float autocvar_g_monster_creeper_damageforcescale = 0.0001; // hehehe +float autocvar_g_monster_creeper_attack_explode_damage; +float autocvar_g_monster_creeper_attack_explode_edgedamage; +float autocvar_g_monster_creeper_attack_explode_radius; +float autocvar_g_monster_creeper_attack_explode_force; +float autocvar_g_monster_creeper_attack_explode_prime_delay; +float autocvar_g_monster_creeper_speed_stop; +float autocvar_g_monster_creeper_speed_walk; +float autocvar_g_monster_creeper_speed_run; + +const float creeper_anim_idle = 0; +const float creeper_anim_walk = 1; +const float creeper_anim_die = 2; + +.float creeper_primed; + +float M_Creeper_Attack(float attack_type) +{ + switch(attack_type) + { + case MONSTER_ATTACK_MELEE: + { + if(self.creeper_primed) + { + pointparticles(particleeffectnum("explosion_medium"), self.origin, '0 0 0', 1); + sound(self, CH_SHOTS, W_Sound("rocket_impact"), VOL_BASE, ATTEN_NORM); + + RadiusDamage (self, self, (autocvar_g_monster_creeper_attack_explode_damage), (autocvar_g_monster_creeper_attack_explode_edgedamage), (autocvar_g_monster_creeper_attack_explode_radius), world, world, (autocvar_g_monster_creeper_attack_explode_force), DEATH_MONSTER_CREEPER, other); + - //Monster_Sound(monstersound_attack, 0, FALSE, CH_SHOTS); ++ //Monster_Sound(monstersound_attack, 0, false, CH_SHOTS); + Damage (self, world, world, self.health + self.max_health + 200, DEATH_KILL, self.origin, '0 0 0'); // killing monster should be reliable enough + self.event_damage = func_null; + self.frame = creeper_anim_idle; + } + else + { - Monster_Sound(monstersound_ranged, 0, FALSE, CH_VOICE); - self.creeper_primed = TRUE; ++ Monster_Sound(monstersound_ranged, 0, false, CH_VOICE); ++ self.creeper_primed = true; + + self.colormod = '1 0 0'; + self.frame = creeper_anim_idle; + self.velocity_x = 0; + self.velocity_y = 0; + self.state = MONSTER_ATTACK_MELEE; + self.attack_finished_single = time + (autocvar_g_monster_creeper_attack_explode_prime_delay); + } - return TRUE; ++ return true; + } + case MONSTER_ATTACK_RANGED: + { + // creeper has no ranged attacks - return FALSE; ++ return false; + } + } + - return FALSE; ++ return false; +} + +void spawnfunc_monster_creeper() { Monster_Spawn(MON_CREEPER); } + +float M_Creeper(float req) +{ + switch(req) + { + case MR_THINK: + { + if(self.creeper_primed) + if(vlen(self.origin - self.enemy.origin) >= self.attack_range * 2) - self.creeper_primed = FALSE; ++ self.creeper_primed = false; + if(self.health > 0 && time > self.attack_finished_single) { self.colormod = '1 1 1'; } - return TRUE; ++ return true; + } + case MR_PAIN: + { - return TRUE; ++ return true; + } + case MR_DEATH: + { + self.frame = creeper_anim_die; - return TRUE; ++ return true; + } + case MR_SETUP: + { + if(!self.health) self.health = (autocvar_g_monster_creeper_health); + if(!self.wander_delay) self.wander_delay = 10; + if(!self.wander_distance) self.wander_distance = 200; + if(!self.speed) { self.speed = (autocvar_g_monster_creeper_speed_walk); } + if(!self.speed2) { self.speed2 = (autocvar_g_monster_creeper_speed_run); } + if(!self.stopspeed) { self.stopspeed = (autocvar_g_monster_creeper_speed_stop); } + if(!self.damageforcescale) { self.damageforcescale = (autocvar_g_monster_creeper_damageforcescale); } + + self.m_anim_walk = creeper_anim_walk; + self.m_anim_run = creeper_anim_walk; + self.m_anim_idle = creeper_anim_idle; + self.monster_loot = spawnfunc_ammo_rockets; + self.frame = creeper_anim_idle; + self.colormod = '1 1 1'; + - return TRUE; ++ return true; + } + case MR_PRECACHE: + { - return TRUE; ++ return true; + } + } + - return TRUE; ++ return true; +} + +#endif // SVQC +#endif // REGISTER_MONSTER diff --cc qcsrc/common/monsters/monster/demon.qc index 19fb151aa,000000000..d403aa3e3 mode 100644,000000..100644 --- a/qcsrc/common/monsters/monster/demon.qc +++ b/qcsrc/common/monsters/monster/demon.qc @@@ -1,127 -1,0 +1,127 @@@ +#ifdef REGISTER_MONSTER +REGISTER_MONSTER( +/* MON_##id */ DEMON, +/* functions */ M_Demon, M_Demon_Attack, +/* spawnflags */ MON_FLAG_MELEE | MONSTER_SIZE_BROKEN | MON_FLAG_RIDE, +/* mins,maxs */ '-32 -32 -24', '32 32 24', +/* model */ "demon.mdl", +/* netname */ "demon", +/* fullname */ _("Demon") +); + +#else +#ifdef SVQC +float autocvar_g_monster_demon_health; +var float autocvar_g_monster_demon_damageforcescale = 0.35; +float autocvar_g_monster_demon_attack_melee_damage; +float autocvar_g_monster_demon_attack_melee_delay; +float autocvar_g_monster_demon_attack_leap_damage; +float autocvar_g_monster_demon_attack_leap_force; +float autocvar_g_monster_demon_attack_leap_speed; +float autocvar_g_monster_demon_attack_leap_delay; +float autocvar_g_monster_demon_attack_leap_mindist; +float autocvar_g_monster_demon_speed_stop; +float autocvar_g_monster_demon_speed_run; +float autocvar_g_monster_demon_speed_walk; + +const float demon_anim_stand = 0; +const float demon_anim_walk = 1; +const float demon_anim_run = 2; +const float demon_anim_leap = 3; +const float demon_anim_pain = 4; +const float demon_anim_death = 5; +const float demon_anim_attack = 6; + +void M_Demon_Attack_Leap_Touch() +{ + if (self.health <= 0) + return; + + vector angles_face; + + if(other.takedamage) + { + angles_face = vectoangles(self.moveto - self.origin); + angles_face = normalize(angles_face) * (autocvar_g_monster_demon_attack_leap_force); + Damage(other, self, self, (autocvar_g_monster_demon_attack_leap_damage) * MONSTER_SKILLMOD(self), DEATH_MONSTER_DEMON_JUMP, other.origin, angles_face); + self.touch = Monster_Touch; // instantly turn it off to stop damage spam + self.state = 0; + } + + if (trace_dphitcontents) + { + self.state = 0; + self.touch = Monster_Touch; + } +} + +float M_Demon_Attack(float attack_type) +{ + switch(attack_type) + { + case MONSTER_ATTACK_MELEE: + { - return Monster_Attack_Melee(self.enemy, (autocvar_g_monster_demon_attack_melee_damage), demon_anim_attack, self.attack_range, (autocvar_g_monster_demon_attack_melee_delay), DEATH_MONSTER_DEMON_MELEE, TRUE); ++ return Monster_Attack_Melee(self.enemy, (autocvar_g_monster_demon_attack_melee_damage), demon_anim_attack, self.attack_range, (autocvar_g_monster_demon_attack_melee_delay), DEATH_MONSTER_DEMON_MELEE, true); + } + case MONSTER_ATTACK_RANGED: + { - if(vlen(self.enemy.origin - self.origin) <= autocvar_g_monster_demon_attack_leap_mindist) { return FALSE; } ++ if(vlen(self.enemy.origin - self.origin) <= autocvar_g_monster_demon_attack_leap_mindist) { return false; } + makevectors(self.angles); + return Monster_Attack_Leap(demon_anim_leap, M_Demon_Attack_Leap_Touch, v_forward * (autocvar_g_monster_demon_attack_leap_speed) + '0 0 200', (autocvar_g_monster_demon_attack_leap_delay)); + } + } + - return FALSE; ++ return false; +} + +void spawnfunc_monster_demon() { Monster_Spawn(MON_DEMON); } +void spawnfunc_monster_demon1() { spawnfunc_monster_demon(); } +void spawnfunc_monster_animus() { spawnfunc_monster_demon(); } + +float M_Demon(float req) +{ + switch(req) + { + case MR_THINK: + { - return TRUE; ++ return true; + } + case MR_PAIN: + { + self.pain_finished = time + 0.5; + self.frame = demon_anim_pain; - return TRUE; ++ return true; + } + case MR_DEATH: + { + self.frame = demon_anim_death; - return TRUE; ++ return true; + } + case MR_SETUP: + { + if(!self.health) self.health = (autocvar_g_monster_demon_health); + if(!self.speed) { self.speed = (autocvar_g_monster_demon_speed_walk); } + if(!self.speed2) { self.speed2 = (autocvar_g_monster_demon_speed_run); } + if(!self.stopspeed) { self.stopspeed = (autocvar_g_monster_demon_speed_stop); } + if(!self.damageforcescale) { self.damageforcescale = (autocvar_g_monster_demon_damageforcescale); } + + self.m_anim_walk = demon_anim_walk; + self.m_anim_run = demon_anim_run; + self.m_anim_idle = demon_anim_stand; + + self.monster_loot = spawnfunc_item_health_large; + self.frame = demon_anim_stand; + - return TRUE; ++ return true; + } + case MR_PRECACHE: + { - return TRUE; ++ return true; + } + } + - return TRUE; ++ return true; +} + +#endif // SVQC +#endif // REGISTER_MONSTER diff --cc qcsrc/common/monsters/monster/enforcer.qc index 3143238c4,000000000..013de5242 mode 100644,000000..100644 --- a/qcsrc/common/monsters/monster/enforcer.qc +++ b/qcsrc/common/monsters/monster/enforcer.qc @@@ -1,178 -1,0 +1,178 @@@ +#ifdef REGISTER_MONSTER +REGISTER_MONSTER( +/* MON_##id */ ENFORCER, +/* functions */ M_Enforcer, M_Enforcer_Attack, +/* spawnflags */ MON_FLAG_MELEE | MONSTER_SIZE_BROKEN, +/* mins,maxs */ '-16 -16 -24', '16 16 24', +/* model */ "enforcer.mdl", +/* netname */ "enforcer", +/* fullname */ _("Enforcer") +); + +#else +#ifdef SVQC +float autocvar_g_monster_enforcer_health; +var float autocvar_g_monster_enforcer_damageforcescale = 0.7; +float autocvar_g_monster_enforcer_attack_plasma_damage; +float autocvar_g_monster_enforcer_attack_plasma_edgedamage; +float autocvar_g_monster_enforcer_attack_plasma_force; +float autocvar_g_monster_enforcer_attack_plasma_radius; +float autocvar_g_monster_enforcer_attack_plasma_spread; +float autocvar_g_monster_enforcer_attack_plasma_speed; +float autocvar_g_monster_enforcer_attack_plasma_lifetime; +float autocvar_g_monster_enforcer_attack_plasma_shots; +float autocvar_g_monster_enforcer_attack_plasma_delay; +float autocvar_g_monster_enforcer_speed_stop; +float autocvar_g_monster_enforcer_speed_run; +float autocvar_g_monster_enforcer_speed_walk; + +const float enforcer_anim_stand = 0; +const float enforcer_anim_walk = 1; +const float enforcer_anim_run = 2; +const float enforcer_anim_attack = 3; +const float enforcer_anim_death1 = 4; +const float enforcer_anim_death2 = 5; +const float enforcer_anim_pain1 = 6; +const float enforcer_anim_pain2 = 7; +const float enforcer_anim_pain3 = 8; +const float enforcer_anim_pain4 = 9; + +void M_Enforcer_Attack_Plasma_Explode() +{ + self.event_damage = func_null; + self.takedamage = DAMAGE_NO; + + if(self.movetype == MOVETYPE_NONE) + self.velocity = self.oldvelocity; + + sound (self, CH_WEAPON_A, W_Sound("tag_impact"), VOL_BASE, ATTEN_NORM); + pointparticles(particleeffectnum("electro_impact"), self.origin, '0 0 0', 1); + RadiusDamage (self, self.realowner, autocvar_g_monster_enforcer_attack_plasma_damage, autocvar_g_monster_enforcer_attack_plasma_edgedamage, + autocvar_g_monster_enforcer_attack_plasma_radius, world, world, autocvar_g_monster_enforcer_attack_plasma_force, self.projectiledeathtype, other); + + remove (self); +} + +void M_Enforcer_Attack_Plasma_Touch() +{ + PROJECTILE_TOUCH; + M_Enforcer_Attack_Plasma_Explode(); +} + +void M_Enforcer_Attack_Plasma() +{ + entity gren; + + sound (self, CH_WEAPON_A, W_Sound("lasergun_fire"), VOL_BASE, ATTEN_NORM); + + vector dir = normalize((self.enemy.origin + '0 0 10') - self.origin); + vector org = self.origin + v_forward * 14 + '0 0 5' + v_right * -14; + + makevectors(self.angles); + + pointparticles(particleeffectnum("electro_muzzleflash"), org, dir * 1000, 1); + + gren = spawn (); + gren.owner = gren.realowner = self; + gren.classname = "grenade"; - gren.bot_dodge = TRUE; ++ gren.bot_dodge = true; + gren.bot_dodgerating = autocvar_g_monster_enforcer_attack_plasma_damage; + gren.movetype = MOVETYPE_FLY; + PROJECTILE_MAKETRIGGER(gren); + gren.projectiledeathtype = DEATH_MONSTER_OGRE_GRENADE; + setorigin(gren, org); + setsize(gren, '-3 -3 -3', '3 3 3'); + + gren.nextthink = time + autocvar_g_monster_enforcer_attack_plasma_lifetime; + gren.think = adaptor_think2use_hittype_splash; + gren.use = M_Enforcer_Attack_Plasma_Explode; + gren.touch = M_Enforcer_Attack_Plasma_Touch; + + gren.missile_flags = MIF_SPLASH; - W_SetupProjVelocity_Explicit(gren, dir, v_up, autocvar_g_monster_enforcer_attack_plasma_speed, 0, 0, autocvar_g_monster_enforcer_attack_plasma_spread, FALSE); ++ W_SetupProjVelocity_Explicit(gren, dir, v_up, autocvar_g_monster_enforcer_attack_plasma_speed, 0, 0, autocvar_g_monster_enforcer_attack_plasma_spread, false); + + gren.flags = FL_PROJECTILE; + - CSQCProjectile(gren, TRUE, PROJECTILE_ELECTRO, TRUE); ++ CSQCProjectile(gren, true, PROJECTILE_ELECTRO, true); + + other = gren; MUTATOR_CALLHOOK(EditProjectile); + + self.attack_finished_single = time + autocvar_g_monster_enforcer_attack_plasma_delay; +} + +float M_Enforcer_Attack(float attack_type) +{ + switch(attack_type) + { + case MONSTER_ATTACK_MELEE: + case MONSTER_ATTACK_RANGED: + { + self.frame = enforcer_anim_attack; + self.attack_finished_single = time + 0.7; + self.anim_finished = time + 0.7 * autocvar_g_monster_enforcer_attack_plasma_shots; + self.state = MONSTER_ATTACK_RANGED; + Monster_Delay(autocvar_g_monster_enforcer_attack_plasma_shots - 1, 0.3, 0.4, M_Enforcer_Attack_Plasma); - return TRUE; ++ return true; + } + } + - return FALSE; ++ return false; +} + +void spawnfunc_monster_enforcer() { Monster_Spawn(MON_ENFORCER); } + +float M_Enforcer(float req) +{ + switch(req) + { + case MR_THINK: + { - return TRUE; ++ return true; + } + case MR_PAIN: + { + switch(floor(random() * 5)) + { + default: + case 1: self.frame = enforcer_anim_pain1; self.pain_finished = time + 0.3; break; + case 2: self.frame = enforcer_anim_pain2; self.pain_finished = time + 0.4; break; + case 3: self.frame = enforcer_anim_pain3; self.pain_finished = time + 0.7; break; + case 4: self.frame = enforcer_anim_pain4; self.pain_finished = time + 1; break; + } - return TRUE; ++ return true; + } + case MR_DEATH: + { + self.frame = ((random() > 0.5) ? enforcer_anim_death1 : enforcer_anim_death2); - return TRUE; ++ return true; + } + case MR_SETUP: + { + if(!self.health) self.health = (autocvar_g_monster_enforcer_health); + if(!self.speed) { self.speed = (autocvar_g_monster_enforcer_speed_walk); } + if(!self.speed2) { self.speed2 = (autocvar_g_monster_enforcer_speed_run); } + if(!self.stopspeed) { self.stopspeed = (autocvar_g_monster_enforcer_speed_stop); } + if(!self.damageforcescale) { self.damageforcescale = (autocvar_g_monster_enforcer_damageforcescale); } + + self.m_anim_walk = enforcer_anim_walk; + self.m_anim_run = enforcer_anim_run; + self.m_anim_idle = enforcer_anim_stand; + + self.monster_loot = spawnfunc_item_cells; + self.weapon = WEP_CRYLINK; + - return TRUE; ++ return true; + } + case MR_PRECACHE: + { + precache_sound(W_Sound("tag_impact")); + precache_sound(W_Sound("lasergun_fire")); - return TRUE; ++ return true; + } + } + - return TRUE; ++ return true; +} + +#endif // SVQC +#endif // REGISTER_MONSTER diff --cc qcsrc/common/monsters/monster/mage.qc index 907bb5101,1aab262fd..c4c5bad61 --- a/qcsrc/common/monsters/monster/mage.qc +++ b/qcsrc/common/monsters/monster/mage.qc @@@ -11,8 -11,7 +11,10 @@@ REGISTER_MONSTER #else #ifdef SVQC ++#include "../../effects.qh" ++ float autocvar_g_monster_mage_health; +var float autocvar_g_monster_mage_damageforcescale = 0.5; float autocvar_g_monster_mage_attack_spike_damage; float autocvar_g_monster_mage_attack_spike_radius; float autocvar_g_monster_mage_attack_spike_delay; @@@ -47,25 -46,26 +49,25 @@@ const float mage_anim_pain = 3 const float mage_anim_death = 4; const float mage_anim_run = 5; -void() mage_heal; -void() mage_shield; +void() M_Mage_Defend_Heal; +void() M_Mage_Defend_Shield; .entity mage_spike; -.float shield_ltime; +.float mage_shield_delay; +.float mage_shield_time; -float friend_needshelp(entity e) +float M_Mage_Defend_Heal_Check(entity e) { if(e == world) - return FALSE; + return false; if(e.health <= 0) - return FALSE; + return false; - if(DIFF_TEAM(e, self) && e != self.monster_owner) + if(DIFF_TEAM(e, self) && e != self.monster_follow) - return FALSE; + return false; if(e.frozen) - return FALSE; + return false; if(!IS_PLAYER(e)) - return ((e.flags & FL_MONSTER) && e.health < e.max_health); - if(e.items & IT_INVINCIBLE) - return false; + return (IS_MONSTER(e) && e.health < e.max_health); switch(self.skin) { @@@ -75,10 -75,10 +77,10 @@@ case 3: return (e.health > 0); } - return FALSE; + return false; } -void mage_spike_explode() +void M_Mage_Attack_Spike_Explode() { self.event_damage = func_null; @@@ -187,17 -187,17 +189,17 @@@ void M_Mage_Attack_Spike( self.mage_spike = missile; - CSQCProjectile(missile, TRUE, PROJECTILE_MAGE_SPIKE, TRUE); + CSQCProjectile(missile, true, PROJECTILE_MAGE_SPIKE, true); } -void mage_heal() +void M_Mage_Defend_Heal() { entity head; - float washealed = FALSE; + float washealed = false; - for(head = findradius(self.origin, (autocvar_g_monster_mage_heal_range)); head; head = head.chain) if(friend_needshelp(head)) + for(head = findradius(self.origin, (autocvar_g_monster_mage_heal_range)); head; head = head.chain) if(M_Mage_Defend_Heal_Check(head)) { - washealed = TRUE; + washealed = true; string fx = ""; if(IS_PLAYER(head)) { @@@ -299,11 -299,11 +301,11 @@@ float M_Mage_Attack(float attack_type { if(random() <= 0.7) { - mage_push(); + M_Mage_Attack_Push(); - return TRUE; + return true; } - return FALSE; + return false; } case MONSTER_ATTACK_RANGED: { @@@ -311,16 -311,15 +313,16 @@@ { if(random() <= 0.4) { - mage_teleport(); + M_Mage_Attack_Teleport(); - return TRUE; + return true; } else { self.frame = mage_anim_attack; self.attack_finished_single = time + (autocvar_g_monster_mage_attack_spike_delay); - defer(0.2, mage_attack_spike); + self.anim_finished = time + 1; + Monster_Delay(1, 0, 0.2, M_Mage_Attack_Spike); - return TRUE; + return true; } } @@@ -331,32 -330,33 +333,32 @@@ } } - return FALSE; + return false; } -void spawnfunc_monster_mage() -{ - self.classname = "monster_mage"; - - if(!monster_initialize(MON_MAGE)) { remove(self); return; } -} +void spawnfunc_monster_mage() { Monster_Spawn(MON_MAGE); } +// extra monsters contains original shalrath +#ifndef MONSTERS_EXTRA // compatibility with old spawns void spawnfunc_monster_shalrath() { spawnfunc_monster_mage(); } +#endif -float m_mage(float req) +float M_Mage(float req) { switch(req) { case MR_THINK: { entity head; - float need_help = FALSE; + float need_help = false; - for(head = world; (head = findfloat(head, iscreature, TRUE)); ) - for(head = findradius(self.origin, (autocvar_g_monster_mage_heal_range)); head; head = head.chain) ++ for(head = world; (head = findfloat(head, iscreature, true)); ) if(head != self) - if(friend_needshelp(head)) + if(vlen(head.origin - self.origin) <= (autocvar_g_monster_mage_heal_range)) + if(M_Mage_Defend_Heal_Check(head)) { - need_help = TRUE; + need_help = true; break; } @@@ -370,15 -370,12 +372,15 @@@ if(self.enemy) if(self.health < self.max_health) - if(time >= self.lastshielded) + if(time >= self.mage_shield_delay) if(random() < 0.5) - mage_shield(); + M_Mage_Defend_Shield(); - return TRUE; - monster_move((autocvar_g_monster_mage_speed_run), (autocvar_g_monster_mage_speed_walk), (autocvar_g_monster_mage_speed_stop), mage_anim_walk, mage_anim_run, mage_anim_idle); ++ return true; + } + case MR_PAIN: + { - return TRUE; + return true; } case MR_DEATH: { @@@ -388,25 -385,19 +390,25 @@@ case MR_SETUP: { if(!self.health) self.health = (autocvar_g_monster_mage_health); + if(!self.speed) { self.speed = (autocvar_g_monster_mage_speed_walk); } + if(!self.speed2) { self.speed2 = (autocvar_g_monster_mage_speed_run); } + if(!self.stopspeed) { self.stopspeed = (autocvar_g_monster_mage_speed_stop); } + if(!self.damageforcescale) { self.damageforcescale = (autocvar_g_monster_mage_damageforcescale); } + + self.m_anim_walk = mage_anim_walk; + self.m_anim_run = mage_anim_run; + self.m_anim_idle = mage_anim_idle; self.monster_loot = spawnfunc_item_health_large; - self.monster_attackfunc = mage_attack; self.frame = mage_anim_walk; - return TRUE; + return true; } case MR_PRECACHE: { - precache_model("models/monsters/mage.dpm"); - precache_sound ("weapons/grenade_impact.wav"); - precache_sound ("weapons/tagexp1.wav"); + precache_sound (W_Sound("grenade_impact")); + precache_sound (W_Sound("tagexp1")); - return TRUE; + return true; } } diff --cc qcsrc/common/monsters/monster/ogre.qc index fae371c42,000000000..87ca4bea6 mode 100644,000000..100644 --- a/qcsrc/common/monsters/monster/ogre.qc +++ b/qcsrc/common/monsters/monster/ogre.qc @@@ -1,370 -1,0 +1,372 @@@ +#ifdef REGISTER_MONSTER +REGISTER_MONSTER( +/* MON_##id */ OGRE, +/* functions */ M_Ogre, M_Ogre_Attack, +/* spawnflags */ MON_FLAG_MELEE | MON_FLAG_RANGED | MONSTER_SIZE_BROKEN, +/* mins,maxs */ '-36 -36 -20', '36 36 50', +/* model */ "ogre.mdl", +/* netname */ "ogre", +/* fullname */ _("Ogre") +); + +#else +#ifdef SVQC ++#include "../../effects.qh" ++ +float autocvar_g_monster_ogre_health; +var float autocvar_g_monster_ogre_damageforcescale = 0.4; +float autocvar_g_monster_ogre_attack_machinegun_spread; +float autocvar_g_monster_ogre_attack_machinegun_solidpenetration; +float autocvar_g_monster_ogre_attack_machinegun_damage; +float autocvar_g_monster_ogre_attack_machinegun_force; +float autocvar_g_monster_ogre_attack_grenade_damage; +float autocvar_g_monster_ogre_attack_grenade_edgedamage; +float autocvar_g_monster_ogre_attack_grenade_radius; +float autocvar_g_monster_ogre_attack_grenade_speed; +float autocvar_g_monster_ogre_attack_grenade_speed_up; +float autocvar_g_monster_ogre_attack_grenade_speed_z; +float autocvar_g_monster_ogre_attack_grenade_spread; +float autocvar_g_monster_ogre_attack_grenade_force; +float autocvar_g_monster_ogre_attack_grenade_bouncefactor; +float autocvar_g_monster_ogre_attack_grenade_bouncestop; +float autocvar_g_monster_ogre_attack_grenade_lifetime; +float autocvar_g_monster_ogre_attack_grenade_health; +float autocvar_g_monster_ogre_attack_grenade_damageforcescale; +float autocvar_g_monster_ogre_attack_melee_damage; +float autocvar_g_monster_ogre_attack_melee_nonplayerdamage; +float autocvar_g_monster_ogre_attack_melee_delay; +float autocvar_g_monster_ogre_attack_melee_time; +float autocvar_g_monster_ogre_attack_melee_range; +float autocvar_g_monster_ogre_attack_melee_traces; +float autocvar_g_monster_ogre_attack_melee_swing_up; +float autocvar_g_monster_ogre_attack_melee_swing_side; +float autocvar_g_monster_ogre_speed_stop; +float autocvar_g_monster_ogre_speed_run; +float autocvar_g_monster_ogre_speed_walk; + +const float ogre_anim_idle = 0; +const float ogre_anim_walk = 1; +const float ogre_anim_run = 2; +const float ogre_anim_swing = 3; +const float ogre_anim_smash = 4; +const float ogre_anim_shoot = 5; +const float ogre_anim_pain1 = 6; +const float ogre_anim_pain2 = 7; +const float ogre_anim_pain3 = 8; +const float ogre_anim_pain4 = 9; +const float ogre_anim_pain5 = 10; +const float ogre_anim_death1 = 11; +const float ogre_anim_death2 = 12; +const float ogre_anim_pull = 13; + +void M_Ogre_Attack_MachineGun() +{ + vector dir = normalize(self.enemy.origin - self.origin); + vector org = self.origin + self.view_ofs + v_forward * 14; + sound (self, CH_WEAPON_A, W_Sound("uzi_fire"), VOL_BASE, ATTEN_NORM); + + fireBullet(org, dir, autocvar_g_monster_ogre_attack_machinegun_spread, autocvar_g_monster_ogre_attack_machinegun_solidpenetration, autocvar_g_monster_ogre_attack_machinegun_damage, autocvar_g_monster_ogre_attack_machinegun_force, DEATH_MONSTER_OGRE_MACHINEGUN, 0); + + Send_Effect(EFFECT_MACHINEGUN_MUZZLEFLASH, org, dir * 1000, 1); + + // casing code + if (autocvar_g_casings >= 2) + SpawnCasing (((random () * 50 + 50) * v_right) - (v_forward * (random () * 25 + 25)) - ((random () * 5 - 70) * v_up), 2, vectoangles(v_forward),'0 250 0', 100, 3, self); +} + +void M_Ogre_Attack_Grenade_Explode() +{ + self.event_damage = func_null; + self.takedamage = DAMAGE_NO; + + if(self.movetype == MOVETYPE_NONE) + self.velocity = self.oldvelocity; + + sound (self, CH_WEAPON_A, W_Sound("grenade_impact"), VOL_BASE, ATTEN_NORM); + pointparticles(particleeffectnum("grenade_explode"), self.origin, '0 0 0', 1); + RadiusDamage (self, self.realowner, autocvar_g_monster_ogre_attack_grenade_damage, autocvar_g_monster_ogre_attack_grenade_edgedamage, autocvar_g_monster_ogre_attack_grenade_radius, world, world, autocvar_g_monster_ogre_attack_grenade_force, self.projectiledeathtype, other); + + remove (self); +} + +void M_Ogre_Attack_Grenade_Damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force) +{ + if (self.health <= 0) + return; + + if (!W_CheckProjectileDamage(inflictor.realowner, self.realowner, deathtype, -1)) // no exceptions + return; // g_projectiles_damage says to halt + + self.health -= damage; + + if (self.health <= 0) + W_PrepareExplosionByDamage(attacker, self.use); +} + +void M_Ogre_Attack_Grenade_Touch() +{ + PROJECTILE_TOUCH; + + if (other.takedamage == DAMAGE_AIM) + { + self.use(); + return; + } + + float r; + r = random() * 6; + if(r < 1) + spamsound (self, CH_SHOTS, W_Sound("grenade_bounce1"), VOL_BASE, ATTEN_NORM); + else if(r < 2) + spamsound (self, CH_SHOTS, W_Sound("grenade_bounce2"), VOL_BASE, ATTEN_NORM); + else if(r < 3) + spamsound (self, CH_SHOTS, W_Sound("grenade_bounce3"), VOL_BASE, ATTEN_NORM); + else if(r < 4) + spamsound (self, CH_SHOTS, W_Sound("grenade_bounce4"), VOL_BASE, ATTEN_NORM); + else if(r < 5) + spamsound (self, CH_SHOTS, W_Sound("grenade_bounce5"), VOL_BASE, ATTEN_NORM); + else + spamsound (self, CH_SHOTS, W_Sound("grenade_bounce6"), VOL_BASE, ATTEN_NORM); + self.projectiledeathtype |= HITTYPE_BOUNCE; +} + +void M_Ogre_Attack_Grenade() +{ + entity gren; + + sound (self, CH_WEAPON_A, W_Sound("grenade_fire"), VOL_BASE, ATTEN_NORM); + + vector dir = normalize((self.enemy.origin + '0 0 10') - self.origin); + vector org = self.origin + v_forward * 14 + '0 0 30' + v_right * -14; + + makevectors(self.angles); + + Send_Effect(EFFECT_GRENADE_MUZZLEFLASH, org, dir * 1000, 1); + + gren = spawn (); + gren.owner = gren.realowner = self; + gren.classname = "grenade"; - gren.bot_dodge = TRUE; ++ gren.bot_dodge = true; + gren.bot_dodgerating = autocvar_g_monster_ogre_attack_grenade_damage; + gren.movetype = MOVETYPE_BOUNCE; + gren.bouncefactor = autocvar_g_monster_ogre_attack_grenade_bouncefactor; + gren.bouncestop = autocvar_g_monster_ogre_attack_grenade_bouncestop; + PROJECTILE_MAKETRIGGER(gren); + gren.projectiledeathtype = DEATH_MONSTER_OGRE_GRENADE; + setorigin(gren, org); + setsize(gren, '-3 -3 -3', '3 3 3'); + + gren.nextthink = time + autocvar_g_monster_ogre_attack_grenade_lifetime; + gren.think = adaptor_think2use_hittype_splash; + gren.use = M_Ogre_Attack_Grenade_Explode; + gren.touch = M_Ogre_Attack_Grenade_Touch; + + gren.takedamage = DAMAGE_YES; + gren.health = autocvar_g_monster_ogre_attack_grenade_health; + gren.damageforcescale = autocvar_g_monster_ogre_attack_grenade_damageforcescale; + gren.event_damage = M_Ogre_Attack_Grenade_Damage; - gren.damagedbycontents = TRUE; ++ gren.damagedbycontents = true; + gren.missile_flags = MIF_SPLASH | MIF_ARC; - W_SetupProjVelocity_Explicit(gren, dir, v_up, autocvar_g_monster_ogre_attack_grenade_speed, autocvar_g_monster_ogre_attack_grenade_speed_up, autocvar_g_monster_ogre_attack_grenade_speed_z, autocvar_g_monster_ogre_attack_grenade_spread, FALSE); ++ W_SetupProjVelocity_Explicit(gren, dir, v_up, autocvar_g_monster_ogre_attack_grenade_speed, autocvar_g_monster_ogre_attack_grenade_speed_up, autocvar_g_monster_ogre_attack_grenade_speed_z, autocvar_g_monster_ogre_attack_grenade_spread, false); + + gren.angles = vectoangles (gren.velocity); + gren.flags = FL_PROJECTILE; + - CSQCProjectile(gren, TRUE, PROJECTILE_GRENADE_BOUNCING, TRUE); ++ CSQCProjectile(gren, true, PROJECTILE_GRENADE_BOUNCING, true); + + other = gren; MUTATOR_CALLHOOK(EditProjectile); + + self.attack_finished_single = time + 0.7; + self.state = 0; +} + +.float ogre_swing_prev; +.entity ogre_swing_alreadyhit; +void M_Ogre_Attack_Chainsaw() +{ + // declarations + float i, f, swing, swing_factor, swing_damage, meleetime, is_player, is_monster; + entity target_victim; + vector targpos; + + if(!self.cnt) // set start time of melee + { + self.cnt = time; + } + + makevectors(self.realowner.angles); // update values for v_* vectors + + // calculate swing percentage based on time + meleetime = autocvar_g_monster_ogre_attack_melee_time; + swing = bound(0, (self.cnt + meleetime - time) / meleetime, 10); + f = ((1 - swing) * autocvar_g_monster_ogre_attack_melee_traces); + + // check to see if we can still continue, otherwise give up now + if(self.realowner.deadflag != DEAD_NO) + { + remove(self); + return; + } + + // if okay, perform the traces needed for this frame + for(i=self.ogre_swing_prev; i < f; ++i) + { + swing_factor = ((1 - (i / autocvar_g_monster_ogre_attack_melee_traces)) * 2 - 1); + + targpos = (self.realowner.origin + self.realowner.view_ofs + + (v_forward * autocvar_g_monster_ogre_attack_melee_range) + + (v_up * swing_factor * autocvar_g_monster_ogre_attack_melee_swing_up) + + (v_right * swing_factor * autocvar_g_monster_ogre_attack_melee_swing_side)); + - WarpZone_traceline_antilag(self, self.realowner.origin + self.realowner.view_ofs, targpos, FALSE, self, 0); ++ WarpZone_traceline_antilag(self, self.realowner.origin + self.realowner.view_ofs, targpos, false, self, 0); + + // draw lightning beams for debugging + te_lightning2(world, targpos, self.realowner.origin + self.realowner.view_ofs + v_forward * 5 - v_up * 5); + te_customflash(targpos, 40, 2, '1 1 1'); + + is_player = (IS_PLAYER(trace_ent) || trace_ent.classname == "body"); + is_monster = IS_MONSTER(trace_ent); + + if((trace_fraction < 1) // if trace is good, apply the damage and remove self + && (trace_ent.takedamage == DAMAGE_AIM) + && (trace_ent != self.ogre_swing_alreadyhit) + && ((is_player || is_monster) || autocvar_g_monster_ogre_attack_melee_nonplayerdamage)) + { + target_victim = trace_ent; // so it persists through other calls + + if(is_player || is_monster) // this allows us to be able to nerf the non-player damage done in e.g. assault or onslaught. + swing_damage = (autocvar_g_monster_ogre_attack_melee_damage * min(1, swing_factor + 1)); + else + swing_damage = (autocvar_g_monster_ogre_attack_melee_nonplayerdamage * min(1, swing_factor + 1)); + + //print(strcat(self.realowner.netname, " hitting ", target_victim.netname, " with ", strcat(ftos(swing_damage), " damage (factor: ", ftos(swing_factor), ") at "), ftos(time), " seconds.\n")); + + Damage(target_victim, self.realowner, self.realowner, + swing_damage, DEATH_MONSTER_OGRE_MELEE, + self.realowner.origin + self.realowner.view_ofs, + v_forward * 1); + + // draw large red flash for debugging + //te_customflash(targpos, 200, 2, '15 0 0'); + + self.ogre_swing_alreadyhit = target_victim; + continue; // move along to next trace + } + } + + if(time >= self.cnt + meleetime) + { + // melee is finished + self.realowner.frame = ogre_anim_idle; + remove(self); + return; + } + else + { + // set up next frame + self.ogre_swing_prev = i; + self.nextthink = time; + } +} + +float M_Ogre_Attack(float attack_type) +{ + switch(attack_type) + { + case MONSTER_ATTACK_MELEE: + { + vector vdir = normalize(self.enemy.origin - self.origin); + + if(vdir_z > 0.7) + { + self.attack_finished_single = time + 1.2; + self.frame = ogre_anim_shoot; + self.state = MONSTER_ATTACK_RANGED; + Monster_Delay(2, 0.1, 0.4, M_Ogre_Attack_MachineGun); + return 2; + } + entity meleetemp; + meleetemp = spawn(); + meleetemp.realowner = self; + meleetemp.think = M_Ogre_Attack_Chainsaw; + meleetemp.nextthink = time + autocvar_g_monster_ogre_attack_melee_delay; + self.attack_finished_single = time + autocvar_g_monster_ogre_attack_melee_time + autocvar_g_monster_ogre_attack_melee_delay + 0.7; + self.anim_finished = self.attack_finished_single; + self.state = MONSTER_ATTACK_MELEE; + self.frame = ogre_anim_swing; + - return TRUE; ++ return true; + } + case MONSTER_ATTACK_RANGED: + { + Monster_Delay(0, 0, 0.5, M_Ogre_Attack_Grenade); + self.state = MONSTER_ATTACK_RANGED; + self.attack_finished_single = time + 1; + self.anim_finished = time + 0.5; + self.frame = ogre_anim_shoot; - return TRUE; ++ return true; + } + } + - return FALSE; ++ return false; +} + +void spawnfunc_monster_ogre() { Monster_Spawn(MON_OGRE); } + +float M_Ogre(float req) +{ + switch(req) + { + case MR_THINK: + { - return TRUE; ++ return true; + } + case MR_PAIN: + { + switch(floor(random() * 6)) + { + default: + case 1: self.frame = ogre_anim_pain1; self.anim_finished = time + 0.4; break; + case 2: self.frame = ogre_anim_pain2; self.anim_finished = time + 0.2; break; + case 3: self.frame = ogre_anim_pain3; self.anim_finished = time + 0.5; break; + case 4: self.frame = ogre_anim_pain4; self.anim_finished = time + 1.5; break; + case 5: self.frame = ogre_anim_pain5; self.anim_finished = time + 1.4; break; + } - return TRUE; ++ return true; + } + case MR_DEATH: + { + self.frame = ((random() >= 0.5) ? ogre_anim_death1 : ogre_anim_death2); - return TRUE; ++ return true; + } + case MR_SETUP: + { + if(!self.health) self.health = (autocvar_g_monster_ogre_health); + if(!self.speed) { self.speed = (autocvar_g_monster_ogre_speed_walk); } + if(!self.speed2) { self.speed2 = (autocvar_g_monster_ogre_speed_run); } + if(!self.stopspeed) { self.stopspeed = (autocvar_g_monster_ogre_speed_stop); } + if(!self.damageforcescale) { self.damageforcescale = (autocvar_g_monster_ogre_damageforcescale); } + + self.m_anim_walk = ogre_anim_walk; + self.m_anim_run = ogre_anim_run; + self.m_anim_idle = ogre_anim_idle; + + self.monster_loot = spawnfunc_item_rockets; + self.weapon = WEP_MACHINEGUN; + self.frame = ogre_anim_pull; + self.spawn_time = time + 1; + self.spawnshieldtime = self.spawn_time; + - return TRUE; ++ return true; + } + case MR_PRECACHE: + { - return TRUE; ++ return true; + } + } + - return TRUE; ++ return true; +} + +#endif // SVQC +#endif // REGISTER_MONSTER diff --cc qcsrc/common/monsters/monster/rotfish.qc index 8d2a3953a,000000000..a4b847948 mode 100644,000000..100644 --- a/qcsrc/common/monsters/monster/rotfish.qc +++ b/qcsrc/common/monsters/monster/rotfish.qc @@@ -1,97 -1,0 +1,97 @@@ +#ifdef REGISTER_MONSTER +REGISTER_MONSTER( +/* MON_##id */ ROTFISH, +/* functions */ M_Rotfish, M_Rotfish_Attack, +/* spawnflags */ MON_FLAG_MELEE | MONSTER_SIZE_BROKEN | MONSTER_TYPE_SWIM, +/* mins,maxs */ '-20 -20 -31', '20 20 20', +/* model */ "fish.mdl", +/* netname */ "rotfish", +/* fullname */ _("Rotfish") +); + +#else +#ifdef SVQC +float autocvar_g_monster_rotfish_health; +var float autocvar_g_monster_rotfish_damageforcescale = 0.8; +float autocvar_g_monster_rotfish_attack_range; +float autocvar_g_monster_rotfish_attack_melee_damage; +float autocvar_g_monster_rotfish_attack_melee_delay; +float autocvar_g_monster_rotfish_speed_stop; +float autocvar_g_monster_rotfish_speed_run; +float autocvar_g_monster_rotfish_speed_walk; + +const float rotfish_anim_attack = 0; +const float rotfish_anim_death = 1; +const float rotfish_anim_swim = 2; +const float rotfish_anim_pain = 3; + + +float M_Rotfish_Attack(float attack_type) +{ + switch(attack_type) + { + case MONSTER_ATTACK_MELEE: + { - return Monster_Attack_Melee(self.enemy, (autocvar_g_monster_rotfish_attack_melee_damage), rotfish_anim_attack, self.attack_range, (autocvar_g_monster_rotfish_attack_melee_delay), DEATH_MONSTER_ROTFISH_MELEE, TRUE); ++ return Monster_Attack_Melee(self.enemy, (autocvar_g_monster_rotfish_attack_melee_damage), rotfish_anim_attack, self.attack_range, (autocvar_g_monster_rotfish_attack_melee_delay), DEATH_MONSTER_ROTFISH_MELEE, true); + } + case MONSTER_ATTACK_RANGED: + { + // rotfish has no ranged attack yet - return FALSE; ++ return false; + } + } + - return FALSE; ++ return false; +} + +void spawnfunc_monster_rotfish() { Monster_Spawn(MON_ROTFISH); } +void spawnfunc_monster_fish() { spawnfunc_monster_rotfish(); } + +float M_Rotfish(float req) +{ + switch(req) + { + case MR_THINK: + { - return TRUE; ++ return true; + } + case MR_PAIN: + { + self.pain_finished = 0.8; + self.frame = rotfish_anim_pain; - return TRUE; ++ return true; + } + case MR_DEATH: + { + self.frame = rotfish_anim_death; - return TRUE; ++ return true; + } + case MR_SETUP: + { + if(!self.health) self.health = (autocvar_g_monster_rotfish_health); + if(!self.speed) { self.speed = (autocvar_g_monster_rotfish_speed_walk); } + if(!self.speed2) { self.speed2 = (autocvar_g_monster_rotfish_speed_run); } + if(!self.stopspeed) { self.stopspeed = (autocvar_g_monster_rotfish_speed_stop); } + if(!self.attack_range) { self.attack_range = autocvar_g_monster_rotfish_attack_range; } + if(!self.damageforcescale) { self.damageforcescale = (autocvar_g_monster_rotfish_damageforcescale); } + + self.m_anim_walk = rotfish_anim_swim; + self.m_anim_run = rotfish_anim_swim; + self.m_anim_idle = rotfish_anim_swim; + + self.monster_loot = spawnfunc_item_armor_small; + self.frame = rotfish_anim_swim; + - return TRUE; ++ return true; + } + case MR_PRECACHE: + { - return TRUE; ++ return true; + } + } + - return TRUE; ++ return true; +} + +#endif // SVQC +#endif // REGISTER_MONSTER diff --cc qcsrc/common/monsters/monster/rottweiler.qc index c4ed126c1,000000000..cda2a6c47 mode 100644,000000..100644 --- a/qcsrc/common/monsters/monster/rottweiler.qc +++ b/qcsrc/common/monsters/monster/rottweiler.qc @@@ -1,101 -1,0 +1,101 @@@ +#ifdef REGISTER_MONSTER +REGISTER_MONSTER( +/* MON_##id */ ROTTWEILER, +/* functions */ M_Rottweiler, M_Rottweiler_Attack, +/* spawnflags */ MON_FLAG_MELEE | MONSTER_SIZE_BROKEN | MON_FLAG_RIDE, +/* mins,maxs */ '-16 -16 -24', '16 16 12', +/* model */ "dog.mdl", +/* netname */ "rottweiler", +/* fullname */ _("Rottweiler") +); + +#else +#ifdef SVQC +float autocvar_g_monster_rottweiler_health; +var float autocvar_g_monster_rottweiler_damageforcescale = 0.7; +float autocvar_g_monster_rottweiler_attack_melee_damage; +float autocvar_g_monster_rottweiler_attack_melee_delay; +float autocvar_g_monster_rottweiler_speed_stop; +float autocvar_g_monster_rottweiler_speed_run; +float autocvar_g_monster_rottweiler_speed_walk; + +const float rottweiler_anim_attack1 = 0; +const float rottweiler_anim_death1 = 1; +const float rottweiler_anim_death2 = 2; +const float rottweiler_anim_attack2 = 3; +const float rottweiler_anim_pain = 4; +const float rottweiler_anim_run = 5; +const float rottweiler_anim_leap = 6; +const float rottweiler_anim_idle = 7; +const float rottweiler_anim_walk = 8; + +float M_Rottweiler_Attack(float attack_type) +{ + switch(attack_type) + { + case MONSTER_ATTACK_MELEE: + { - return Monster_Attack_Melee(self.enemy, (autocvar_g_monster_rottweiler_attack_melee_damage), ((random() >= 0.5) ? rottweiler_anim_attack1 : rottweiler_anim_attack2), self.attack_range, (autocvar_g_monster_rottweiler_attack_melee_delay), DEATH_MONSTER_ROTTWEILER, TRUE); ++ return Monster_Attack_Melee(self.enemy, (autocvar_g_monster_rottweiler_attack_melee_damage), ((random() >= 0.5) ? rottweiler_anim_attack1 : rottweiler_anim_attack2), self.attack_range, (autocvar_g_monster_rottweiler_attack_melee_delay), DEATH_MONSTER_ROTTWEILER, true); + } + case MONSTER_ATTACK_RANGED: + { + // rottweiler has no ranged attack yet! - return FALSE; ++ return false; + } + } + - return FALSE; ++ return false; +} + +void spawnfunc_monster_rottweiler() { Monster_Spawn(MON_ROTTWEILER); } + +float M_Rottweiler(float req) +{ + switch(req) + { + case MR_THINK: + { - return TRUE; ++ return true; + } + case MR_PAIN: + { + if(random() <= 0.3) + { + self.pain_finished = time + 1.5; + self.frame = rottweiler_anim_pain; + } - return TRUE; ++ return true; + } + case MR_DEATH: + { + self.frame = (random() >= 0.5) ? rottweiler_anim_death1 : rottweiler_anim_death2; - return TRUE; ++ return true; + } + case MR_SETUP: + { + if(!self.health) self.health = (autocvar_g_monster_rottweiler_health); + if(!self.speed) { self.speed = (autocvar_g_monster_rottweiler_speed_walk); } + if(!self.speed2) { self.speed2 = (autocvar_g_monster_rottweiler_speed_run); } + if(!self.stopspeed) { self.stopspeed = (autocvar_g_monster_rottweiler_speed_stop); } + if(!self.damageforcescale) { self.damageforcescale = (autocvar_g_monster_rottweiler_damageforcescale); } + + self.m_anim_walk = rottweiler_anim_walk; + self.m_anim_run = rottweiler_anim_run; + self.m_anim_idle = rottweiler_anim_idle; + + self.monster_loot = spawnfunc_item_health_small; + self.frame = rottweiler_anim_idle; + - return TRUE; ++ return true; + } + case MR_PRECACHE: + { - return TRUE; ++ return true; + } + } + - return TRUE; ++ return true; +} + +#endif // SVQC +#endif // REGISTER_MONSTER diff --cc qcsrc/common/monsters/monster/scrag.qc index 32ceb6ff8,000000000..ffb5879ab mode 100644,000000..100644 --- a/qcsrc/common/monsters/monster/scrag.qc +++ b/qcsrc/common/monsters/monster/scrag.qc @@@ -1,161 -1,0 +1,161 @@@ +#ifdef REGISTER_MONSTER +REGISTER_MONSTER( +/* MON_##id */ SCRAG, +/* functions */ M_Scrag, M_Scrag_Attack, +/* spawnflags */ MONSTER_TYPE_FLY | MONSTER_SIZE_BROKEN | MON_FLAG_RANGED, +/* mins,maxs */ '-20 -20 -58', '20 20 20', +/* model */ "scrag.mdl", +/* netname */ "scrag", +/* fullname */ _("Scrag") +); + +#else +#ifdef SVQC +float autocvar_g_monster_scrag_health; +var float autocvar_g_monster_scrag_damageforcescale = 0.55; +float autocvar_g_monster_scrag_attack_spike_damage; +float autocvar_g_monster_scrag_attack_spike_edgedamage; +float autocvar_g_monster_scrag_attack_spike_force; +float autocvar_g_monster_scrag_attack_spike_radius; +float autocvar_g_monster_scrag_attack_spike_speed; +float autocvar_g_monster_scrag_speed_stop; +float autocvar_g_monster_scrag_speed_run; +float autocvar_g_monster_scrag_speed_walk; + +const float scrag_anim_hover = 0; +const float scrag_anim_fly = 1; +const float scrag_anim_magic = 2; +const float scrag_anim_pain = 3; +const float scrag_anim_death = 4; + +void M_Scrag_Attack_Spike_Explode() +{ + if(self) + { + pointparticles(particleeffectnum("TE_WIZSPIKE"), self.origin, '0 0 0', 1); + + RadiusDamage(self, self.realowner, (autocvar_g_monster_scrag_attack_spike_damage), (autocvar_g_monster_scrag_attack_spike_edgedamage), (autocvar_g_monster_scrag_attack_spike_force), + self.owner, world, (autocvar_g_monster_scrag_attack_spike_radius), self.projectiledeathtype, world); + + remove(self); + } +} + +void M_Scrag_Attack_Spike_Touch() +{ + PROJECTILE_TOUCH; + if(other == self.owner) { return; } + + M_Scrag_Attack_Spike_Explode(); +} + +void M_Scrag_Attack_Spike() +{ + entity missile = spawn(); + vector viewofs = self.view_ofs; + vector dir = normalize(self.enemy.origin - self.origin); + + monster_makevectors(self.enemy); + + missile.owner = missile.realowner = self; + missile.solid = SOLID_TRIGGER; + missile.movetype = MOVETYPE_FLYMISSILE; + missile.projectiledeathtype = DEATH_MONSTER_SCRAG; + setsize(missile, '-6 -6 -6', '6 6 6'); + setorigin(missile, self.origin + viewofs + v_forward * 14); + missile.flags = FL_PROJECTILE; + missile.velocity = dir * (autocvar_g_monster_scrag_attack_spike_speed); + missile.avelocity = '300 300 300'; + missile.nextthink = time + 5; + missile.think = M_Scrag_Attack_Spike_Explode; + missile.enemy = self.enemy; + missile.touch = M_Scrag_Attack_Spike_Touch; - CSQCProjectile(missile, TRUE, PROJECTILE_SCRAG_SPIKE, TRUE); ++ CSQCProjectile(missile, true, PROJECTILE_SCRAG_SPIKE, true); +} + +float M_Scrag_Attack(float attack_type) +{ + switch(attack_type) + { + case MONSTER_ATTACK_MELEE: + case MONSTER_ATTACK_RANGED: + { + self.attack_finished_single = time + ((random() >= 0.8) ? 1.3 : 0.15); + self.anim_finished = self.attack_finished_single; + + self.frame = scrag_anim_magic; + + M_Scrag_Attack_Spike(); + + if(random() <= 0.1 || self.monster_moveto) + { + makevectors(self.angles); + self.monster_moveto = self.origin + '1 1 0' * (random() >= 0.5) ? (v_right * 300) : (v_right * -300); + self.monster_face = self.enemy.origin; // but still look at enemy + if(random() <= 0.4) + self.monster_moveto = self.monster_face = '0 0 0'; + } + - return TRUE; ++ return true; + } + } + - return FALSE; ++ return false; +} + +void spawnfunc_monster_scrag() { Monster_Spawn(MON_SCRAG); } + +// compatibility with old spawns +void spawnfunc_monster_wizard() { spawnfunc_monster_scrag(); } +void spawnfunc_monster_wyvern() { spawnfunc_monster_scrag(); } + +float M_Scrag(float req) +{ + switch(req) + { + case MR_THINK: + { - return TRUE; ++ return true; + } + case MR_PAIN: + { + self.pain_finished = time + 0.3; + self.frame = scrag_anim_pain; - return TRUE; ++ return true; + } + case MR_DEATH: + { + self.frame = scrag_anim_death; + self.velocity_x = -200 + 400 * random(); + self.velocity_y = -200 + 400 * random(); + self.velocity_z = 100 + 100 * random(); - return TRUE; ++ return true; + } + case MR_SETUP: + { + if(!self.health) self.health = (autocvar_g_monster_scrag_health); + if(!self.speed) { self.speed = (autocvar_g_monster_scrag_speed_walk); } + if(!self.speed2) { self.speed2 = (autocvar_g_monster_scrag_speed_run); } + if(!self.stopspeed) { self.stopspeed = (autocvar_g_monster_scrag_speed_stop); } + if(!self.damageforcescale) { self.damageforcescale = (autocvar_g_monster_scrag_damageforcescale); } + + self.m_anim_walk = scrag_anim_hover; + self.m_anim_run = scrag_anim_fly; + self.m_anim_idle = scrag_anim_hover; + + self.monster_loot = spawnfunc_item_shells; + self.frame = scrag_anim_hover; + - return TRUE; ++ return true; + } + case MR_PRECACHE: + { - return TRUE; ++ return true; + } + } + - return TRUE; ++ return true; +} + +#endif // SVQC +#endif // REGISTER_MONSTER diff --cc qcsrc/common/monsters/monster/shambler.qc index 1383463dd,12ff5dc89..7dac71421 --- a/qcsrc/common/monsters/monster/shambler.qc +++ b/qcsrc/common/monsters/monster/shambler.qc @@@ -11,10 -11,8 +11,12 @@@ REGISTER_MONSTER #else #ifdef SVQC ++#include "../../effects.qh" ++ float autocvar_g_monster_shambler_health; +var float autocvar_g_monster_shambler_damageforcescale = 0.1; float autocvar_g_monster_shambler_attack_smash_damage; +float autocvar_g_monster_shambler_attack_smash_range; float autocvar_g_monster_shambler_attack_claw_damage; float autocvar_g_monster_shambler_attack_lightning_damage; float autocvar_g_monster_shambler_attack_lightning_force; @@@ -38,29 -36,26 +40,29 @@@ const float shambler_anim_death = 8 .float shambler_lastattack; // delay attacks separately -void shambler_smash() +void M_Shambler_Attack_Smash() { makevectors(self.angles); - pointparticles(particleeffectnum("explosion_medium"), (self.origin + (v_forward * 150)) - ('0 0 1' * self.maxs.z), '0 0 0', 1); - sound(self, CH_SHOTS, "weapons/rocket_impact.wav", VOL_BASE, ATTEN_NORM); + Send_Effect(EFFECT_EXPLOSION_MEDIUM, (self.origin + (v_forward * 150)) - ('0 0 1' * self.maxs_z), '0 0 0', 1); + sound(self, CH_SHOTS, W_Sound("rocket_impact"), VOL_BASE, ATTEN_NORM); + + // RadiusDamage does NOT support custom starting location, which means we must use this hack... - tracebox(self.origin + v_forward * 50, self.mins * 0.5, self.maxs * 0.5, self.origin + v_forward * 500, MOVE_NORMAL, self); + tracebox(self.origin + v_forward * 50, self.mins * 0.5, self.maxs * 0.5, self.origin + v_forward * autocvar_g_monster_shambler_attack_smash_range, MOVE_NORMAL, self); if(trace_ent.takedamage) - Damage(trace_ent, self, self, (autocvar_g_monster_shambler_attack_smash_damage) * Monster_SkillModifier(), DEATH_MONSTER_SHAMBLER_SMASH, trace_ent.origin, normalize(trace_ent.origin - self.origin)); + Damage(trace_ent, self, self, (autocvar_g_monster_shambler_attack_smash_damage) * MONSTER_SKILLMOD(self), DEATH_MONSTER_SHAMBLER_SMASH, trace_ent.origin, normalize(trace_ent.origin - self.origin)); } -void shambler_swing() +void M_Shambler_Attack_Swing() { float r = (random() < 0.5); - Monster_Attack_Melee(self.enemy, (autocvar_g_monster_shambler_attack_claw_damage), ((r) ? shambler_anim_swingr : shambler_anim_swingl), self.attack_range, 0.8, DEATH_MONSTER_SHAMBLER_CLAW, TRUE); - monster_melee(self.enemy, (autocvar_g_monster_shambler_attack_claw_damage), ((r) ? shambler_anim_swingr : shambler_anim_swingl), self.attack_range, 0.8, DEATH_MONSTER_SHAMBLER_CLAW, true); ++ Monster_Attack_Melee(self.enemy, (autocvar_g_monster_shambler_attack_claw_damage), ((r) ? shambler_anim_swingr : shambler_anim_swingl), self.attack_range, 0.8, DEATH_MONSTER_SHAMBLER_CLAW, true); if(r) { - defer(0.5, shambler_swing); + Monster_Delay(1, 0, 0.5, M_Shambler_Attack_Swing); self.attack_finished_single += 0.5; + self.anim_finished = self.attack_finished_single; } } @@@ -150,75 -145,69 +152,75 @@@ void M_Shambler_Attack_Lightning( gren.takedamage = DAMAGE_YES; gren.health = 50; gren.damageforcescale = 0; - gren.event_damage = shambler_lightning_damage; + gren.event_damage = M_Shambler_Attack_Lightning_Damage; - gren.damagedbycontents = TRUE; + gren.damagedbycontents = true; gren.missile_flags = MIF_SPLASH | MIF_ARC; - W_SetupProjVelocity_Explicit(gren, v_forward, v_up, (autocvar_g_monster_shambler_attack_lightning_speed), (autocvar_g_monster_shambler_attack_lightning_speed_up), 0, 0, FALSE); + W_SetupProjVelocity_Explicit(gren, v_forward, v_up, (autocvar_g_monster_shambler_attack_lightning_speed), (autocvar_g_monster_shambler_attack_lightning_speed_up), 0, 0, false); gren.angles = vectoangles (gren.velocity); gren.flags = FL_PROJECTILE; - CSQCProjectile(gren, TRUE, PROJECTILE_SHAMBLER_LIGHTNING, TRUE); + CSQCProjectile(gren, true, PROJECTILE_SHAMBLER_LIGHTNING, true); } -float shambler_attack(float attack_type) +float M_Shambler_Attack(float attack_type) { switch(attack_type) { case MONSTER_ATTACK_MELEE: { - shambler_swing(); + M_Shambler_Attack_Swing(); - return TRUE; + return true; } case MONSTER_ATTACK_RANGED: { + float randomness = random(), enemy_len = vlen(self.enemy.origin - self.origin); + if(time >= self.shambler_lastattack) // shambler doesn't attack much if(self.flags & FL_ONGROUND) - if(random() <= 0.5 && vlen(self.enemy.origin - self.origin) <= 500) + if(randomness <= 0.5 && enemy_len <= autocvar_g_monster_shambler_attack_smash_range) { self.frame = shambler_anim_smash; - defer(0.7, shambler_smash); + Monster_Delay(1, 0, 0.7, M_Shambler_Attack_Smash); self.attack_finished_single = time + 1.1; - self.shambler_lastattack = time + 3; + self.anim_finished = time + 1.1; + self.state = MONSTER_ATTACK_MELEE; // kinda a melee attack + self.shambler_lastattack = time + 3 + random() * 1.5; - return TRUE; + return true; } - else if(random() <= 0.1) // small chance, don't want this spammed + else if(randomness <= 0.1 && enemy_len >= autocvar_g_monster_shambler_attack_smash_range * 1.5) // small chance, don't want this spammed { self.frame = shambler_anim_magic; + self.state = MONSTER_ATTACK_MELEE; // maybe we should rename this to something more general self.attack_finished_single = time + 1.1; - self.shambler_lastattack = time + 3; - defer(0.6, shambler_lightning); + self.anim_finished = 1.1; + self.shambler_lastattack = time + 3 + random() * 1.5; + Monster_Delay(1, 0, 0.6, M_Shambler_Attack_Lightning); - return TRUE; + return true; } - return FALSE; + return false; } } - return FALSE; + return false; } -void spawnfunc_monster_shambler() -{ - self.classname = "monster_shambler"; - - if(!monster_initialize(MON_SHAMBLER)) { remove(self); return; } -} +void spawnfunc_monster_shambler() { Monster_Spawn(MON_SHAMBLER); } -float m_shambler(float req) +float M_Shambler(float req) { switch(req) { case MR_THINK: { - return TRUE; - monster_move((autocvar_g_monster_shambler_speed_run), (autocvar_g_monster_shambler_speed_walk), (autocvar_g_monster_shambler_speed_stop), shambler_anim_run, shambler_anim_walk, shambler_anim_stand); ++ return true; + } + case MR_PAIN: + { + self.pain_finished = time + 0.5; + self.frame = shambler_anim_pain; - return TRUE; + return true; } case MR_DEATH: { @@@ -229,28 -218,18 +231,28 @@@ { if(!self.health) self.health = (autocvar_g_monster_shambler_health); if(!self.attack_range) self.attack_range = 150; + if(!self.speed) { self.speed = (autocvar_g_monster_shambler_speed_walk); } + if(!self.speed2) { self.speed2 = (autocvar_g_monster_shambler_speed_run); } + if(!self.stopspeed) { self.stopspeed = (autocvar_g_monster_shambler_speed_stop); } + if(!self.damageforcescale) { self.damageforcescale = (autocvar_g_monster_shambler_damageforcescale); } + + self.m_anim_walk = shambler_anim_walk; + self.m_anim_run = shambler_anim_run; + self.m_anim_idle = shambler_anim_stand; self.monster_loot = spawnfunc_item_health_mega; - self.monster_attackfunc = shambler_attack; self.frame = shambler_anim_stand; - self.weapon = WEP_VORTEX; + self.weapon = WEP_ELECTRO; // matches attacks better than WEP_VORTEX + + self.frame = shambler_anim_magic; + self.spawn_time = time + 1.1; + self.spawnshieldtime = self.spawn_time; - return TRUE; + return true; } case MR_PRECACHE: { - return TRUE; - precache_model("models/monsters/shambler.mdl"); + return true; } } diff --cc qcsrc/common/monsters/monster/spawn.qc index 7040f3c88,000000000..fa6a8915f mode 100644,000000..100644 --- a/qcsrc/common/monsters/monster/spawn.qc +++ b/qcsrc/common/monsters/monster/spawn.qc @@@ -1,136 -1,0 +1,138 @@@ +#ifdef REGISTER_MONSTER +REGISTER_MONSTER( +/* MON_##id */ SPAWN, +/* functions */ M_Spawn, M_Spawn_Attack, +/* spawnflags */ MON_FLAG_MELEE | MONSTER_SIZE_BROKEN, +/* mins,maxs */ '-16 -16 -24', '16 16 16', +/* model */ "tarbaby.mdl", +/* netname */ "spawn", +/* fullname */ _("Spawn") +); + +#else +#ifdef SVQC ++#include "../../effects.qh" ++ +float autocvar_g_monster_spawn_health; +var float autocvar_g_monster_spawn_damageforcescale = 0.6; +float autocvar_g_monster_spawn_attack_explode_damage; +float autocvar_g_monster_spawn_attack_explode_edgedamage; +float autocvar_g_monster_spawn_attack_explode_radius; +float autocvar_g_monster_spawn_attack_explode_force; +float autocvar_g_monster_spawn_attack_leap_speed; +float autocvar_g_monster_spawn_attack_leap_delay; +float autocvar_g_monster_spawn_speed_stop; +float autocvar_g_monster_spawn_speed_run; +float autocvar_g_monster_spawn_speed_walk; + +const float spawn_anim_walk = 0; +const float spawn_anim_run = 1; +const float spawn_anim_jump = 2; +const float spawn_anim_fly = 3; +const float spawn_anim_explode = 4; + +void M_Spawn_Attack_Explode() +{ + Send_Effect(EFFECT_GRENADE_EXPLODE, self.origin, '0 0 0', 1); + sound(self, CH_SHOTS, W_Sound("rocket_impact"), VOL_BASE, ATTEN_NORM); + + RadiusDamage (self, self, (autocvar_g_monster_spawn_attack_explode_damage), (autocvar_g_monster_spawn_attack_explode_edgedamage), (autocvar_g_monster_spawn_attack_explode_radius), world, world, (autocvar_g_monster_spawn_attack_explode_force), DEATH_MONSTER_CREEPER, other); + + self.think = SUB_Remove; + self.nextthink = time + 0.1; +} + +void M_Spawn_Attack_Leap_Touch() +{ + if(self.health <= 0) { return; } + + if(other.takedamage) + if(vlen(self.velocity) > 200) + { + Damage (self, world, world, self.health + self.max_health + 200, DEATH_KILL, self.origin, '0 0 0'); + self.touch = Monster_Touch; // instantly turn it off to stop damage spam + } + + if (trace_dphitcontents) + { + self.movetype = MOVETYPE_WALK; + self.touch = Monster_Touch; + } +} + +float M_Spawn_Attack(float attack_type) +{ + switch(attack_type) + { + case MONSTER_ATTACK_MELEE: + case MONSTER_ATTACK_RANGED: + { + makevectors(self.angles); + float leap_success = Monster_Attack_Leap(spawn_anim_jump, M_Spawn_Attack_Leap_Touch, v_forward * (autocvar_g_monster_spawn_attack_leap_speed) + '0 0 200', (autocvar_g_monster_spawn_attack_leap_delay)); + if(leap_success) + { + self.movetype = MOVETYPE_BOUNCE; + self.velocity_z += random() * 150; + } + return leap_success; + } + } + - return FALSE; ++ return false; +} + +void spawnfunc_monster_spawn() { Monster_Spawn(MON_SPAWN); } + +float M_Spawn(float req) +{ + switch(req) + { + case MR_THINK: + { + // prevent standard code from breaking everything + if(self.flags & FL_ONGROUND) + if(self.movetype == MOVETYPE_BOUNCE) + if(time >= self.attack_finished_single) + self.movetype = MOVETYPE_WALK; - return TRUE; ++ return true; + } + case MR_PAIN: + { - return TRUE; ++ return true; + } + case MR_DEATH: + { + // KABOOM! + self.frame = 0; + defer(0.05, M_Spawn_Attack_Explode); // simply defer to prevent recursion - return TRUE; ++ return true; + } + case MR_SETUP: + { + if(!self.health) self.health = (autocvar_g_monster_spawn_health); + if(!self.speed) { self.speed = (autocvar_g_monster_spawn_speed_walk); } + if(!self.speed2) { self.speed2 = (autocvar_g_monster_spawn_speed_run); } + if(!self.stopspeed) { self.stopspeed = (autocvar_g_monster_spawn_speed_stop); } + if(!self.attack_range) { self.attack_range = autocvar_g_monsters_target_range * 0.8; } // bounce from almost any distance + if(!self.damageforcescale) { self.damageforcescale = (autocvar_g_monster_spawn_damageforcescale); } + + self.m_anim_walk = spawn_anim_walk; + self.m_anim_run = spawn_anim_run; + self.m_anim_idle = spawn_anim_walk; + + self.monster_loot = spawnfunc_item_rockets; + self.frame = spawn_anim_walk; + - return TRUE; ++ return true; + } + case MR_PRECACHE: + { - return TRUE; ++ return true; + } + } + - return TRUE; ++ return true; +} + +#endif // SVQC +#endif // REGISTER_MONSTER diff --cc qcsrc/common/monsters/monster/spider.qc index 5ef819fd2,427f63107..59f9e08b1 --- a/qcsrc/common/monsters/monster/spider.qc +++ b/qcsrc/common/monsters/monster/spider.qc @@@ -11,8 -11,7 +11,10 @@@ REGISTER_MONSTER #else #ifdef SVQC ++#include "../../effects.qh" ++ float autocvar_g_monster_spider_health; +var float autocvar_g_monster_spider_damageforcescale = 0.6; float autocvar_g_monster_spider_attack_bite_damage; float autocvar_g_monster_spider_attack_bite_delay; float autocvar_g_monster_spider_attack_web_damagetime; @@@ -61,9 -60,9 +63,9 @@@ void M_Spider_Attack_Web( entity proj = spawn (); proj.classname = "plasma"; proj.owner = proj.realowner = self; - proj.use = spider_web_touch; + proj.use = M_Spider_Attack_Web_Explode; proj.think = adaptor_think2use_hittype_splash; - proj.bot_dodge = TRUE; + proj.bot_dodge = true; proj.bot_dodgerating = 0; proj.nextthink = time + 5; PROJECTILE_MAKETRIGGER(proj); @@@ -73,8 -72,8 +75,8 @@@ //proj.glow_size = 50; //proj.glow_color = 45; proj.movetype = MOVETYPE_BOUNCE; - W_SetupProjVelocity_Explicit(proj, v_forward, v_up, (autocvar_g_monster_spider_attack_web_speed), (autocvar_g_monster_spider_attack_web_speed_up), 0, 0, FALSE); + W_SetupProjVelocity_Explicit(proj, v_forward, v_up, (autocvar_g_monster_spider_attack_web_speed), (autocvar_g_monster_spider_attack_web_speed_up), 0, 0, false); - proj.touch = spider_web_touch; + proj.touch = M_Spider_Attack_Web_Touch; setsize(proj, '-4 -4 -4', '4 4 4'); proj.takedamage = DAMAGE_NO; proj.damageforcescale = 0; @@@ -87,16 -86,16 +89,16 @@@ proj.bouncestop = 0.05; proj.missile_flags = MIF_SPLASH | MIF_ARC; - CSQCProjectile(proj, TRUE, PROJECTILE_ELECTRO, TRUE); + CSQCProjectile(proj, true, PROJECTILE_ELECTRO, true); } -float spider_attack(float attack_type) +float M_Spider_Attack(float attack_type) { switch(attack_type) { case MONSTER_ATTACK_MELEE: { - return Monster_Attack_Melee(self.enemy, (autocvar_g_monster_spider_attack_bite_damage), ((random() > 0.5) ? spider_anim_attack : spider_anim_attack2), self.attack_range, (autocvar_g_monster_spider_attack_bite_delay), DEATH_MONSTER_SPIDER, TRUE); - return monster_melee(self.enemy, (autocvar_g_monster_spider_attack_bite_damage), ((random() > 0.5) ? spider_anim_attack : spider_anim_attack2), self.attack_range, (autocvar_g_monster_spider_attack_bite_delay), DEATH_MONSTER_SPIDER, true); ++ return Monster_Attack_Melee(self.enemy, (autocvar_g_monster_spider_attack_bite_damage), ((random() > 0.5) ? spider_anim_attack : spider_anim_attack2), self.attack_range, (autocvar_g_monster_spider_attack_bite_delay), DEATH_MONSTER_SPIDER, true); } case MONSTER_ATTACK_RANGED: { @@@ -104,32 -103,33 +106,32 @@@ { self.frame = spider_anim_attack2; self.attack_finished_single = time + (autocvar_g_monster_spider_attack_web_delay); - spider_shootweb(); + self.anim_finished = time + 1; + M_Spider_Attack_Web(); self.spider_web_delay = time + 3; - return TRUE; + return true; } - return FALSE; + return false; } } - return FALSE; + return false; } -void spawnfunc_monster_spider() -{ - self.classname = "monster_spider"; +void spawnfunc_monster_spider() { Monster_Spawn(MON_SPIDER); } - if(!monster_initialize(MON_SPIDER)) { remove(self); return; } -} - -float m_spider(float req) +float M_Spider(float req) { switch(req) { case MR_THINK: { - return TRUE; - monster_move((autocvar_g_monster_spider_speed_run), (autocvar_g_monster_spider_speed_walk), (autocvar_g_monster_spider_speed_stop), spider_anim_walk, spider_anim_walk, spider_anim_idle); ++ return true; + } + case MR_PAIN: + { - return TRUE; + return true; } case MR_DEATH: { @@@ -140,24 -140,18 +142,24 @@@ case MR_SETUP: { if(!self.health) self.health = (autocvar_g_monster_spider_health); + if(!self.speed) { self.speed = (autocvar_g_monster_spider_speed_walk); } + if(!self.speed2) { self.speed2 = (autocvar_g_monster_spider_speed_run); } + if(!self.stopspeed) { self.stopspeed = (autocvar_g_monster_spider_speed_stop); } + if(!self.damageforcescale) { self.damageforcescale = (autocvar_g_monster_spider_damageforcescale); } + + self.m_anim_walk = spider_anim_walk; + self.m_anim_run = spider_anim_walk; + self.m_anim_idle = spider_anim_idle; self.monster_loot = spawnfunc_item_health_medium; - self.monster_attackfunc = spider_attack; self.frame = spider_anim_idle; - return TRUE; + return true; } case MR_PRECACHE: { - precache_model("models/monsters/spider.dpm"); - precache_sound ("weapons/electro_fire2.wav"); + precache_sound (W_Sound("electro_fire2")); - return TRUE; + return true; } } diff --cc qcsrc/common/monsters/monster/vore.qc index 768826fd2,000000000..b0e9ba4a3 mode 100644,000000..100644 --- a/qcsrc/common/monsters/monster/vore.qc +++ b/qcsrc/common/monsters/monster/vore.qc @@@ -1,232 -1,0 +1,234 @@@ +#ifdef REGISTER_MONSTER +REGISTER_MONSTER( +/* MON_##id */ VORE, +/* functions */ M_Vore, M_Vore_Attack, +/* spawnflags */ MON_FLAG_MELEE | MON_FLAG_RANGED, +/* mins,maxs */ '-32 -32 -24', '32 32 32', +/* model */ "shalrath.mdl", +/* netname */ "vore", +/* fullname */ _("Vore") +); + +#else +#ifdef SVQC ++#include "../../effects.qh" ++ +float autocvar_g_monster_vore_health; +var float autocvar_g_monster_vore_damageforcescale = 0.4; +float autocvar_g_monster_vore_attack_spike_damage; +float autocvar_g_monster_vore_attack_spike_radius; +float autocvar_g_monster_vore_attack_spike_delay; +float autocvar_g_monster_vore_attack_spike_accel; +float autocvar_g_monster_vore_attack_spike_decel; +float autocvar_g_monster_vore_attack_spike_turnrate; +float autocvar_g_monster_vore_attack_spike_speed_max; +float autocvar_g_monster_vore_attack_spike_smart; +float autocvar_g_monster_vore_attack_spike_smart_trace_min; +float autocvar_g_monster_vore_attack_spike_smart_trace_max; +float autocvar_g_monster_vore_attack_spike_smart_mindist; +float autocvar_g_monster_vore_attack_melee_damage; +float autocvar_g_monster_vore_attack_melee_delay; +float autocvar_g_monster_vore_speed_stop; +float autocvar_g_monster_vore_speed_run; +float autocvar_g_monster_vore_speed_walk; + +const float vore_anim_attack = 0; +const float vore_anim_pain = 1; +const float vore_anim_death = 2; +const float vore_anim_walk = 3; + +.entity vore_spike; + +void M_Vore_Attack_Spike_Explode() +{ + self.event_damage = func_null; + + sound(self, CH_SHOTS, W_Sound("grenade_impact"), VOL_BASE, ATTEN_NORM); + + self.realowner.vore_spike = world; + + Send_Effect(EFFECT_EXPLOSION_SMALL, self.origin, '0 0 0', 1); + RadiusDamage (self, self.realowner, (autocvar_g_monster_vore_attack_spike_damage), (autocvar_g_monster_vore_attack_spike_damage) * 0.5, (autocvar_g_monster_vore_attack_spike_radius), world, world, 0, DEATH_MONSTER_VORE, other); + + remove (self); +} + +void M_Vore_Attack_Spike_Touch() +{ + PROJECTILE_TOUCH; + + M_Vore_Attack_Spike_Explode(); +} + +// copied from W_Seeker_Think +void M_Vore_Attack_Spike_Think() +{ + entity e; + vector desireddir, olddir, newdir, eorg; + float turnrate; + float dist; + float spd; + + if (time > self.ltime || self.enemy.health <= 0 || self.owner.health <= 0) + { + self.projectiledeathtype |= HITTYPE_SPLASH; + M_Vore_Attack_Spike_Explode(); + } + + spd = vlen(self.velocity); + spd = bound( + spd - (autocvar_g_monster_vore_attack_spike_decel) * frametime, + (autocvar_g_monster_vore_attack_spike_speed_max), + spd + (autocvar_g_monster_vore_attack_spike_accel) * frametime + ); + + if (self.enemy != world) + if (self.enemy.takedamage != DAMAGE_AIM || self.enemy.deadflag != DEAD_NO) + self.enemy = world; + + if (self.enemy != world) + { + e = self.enemy; + eorg = 0.5 * (e.absmin + e.absmax); + turnrate = (autocvar_g_monster_vore_attack_spike_turnrate); // how fast to turn + desireddir = normalize(eorg - self.origin); + olddir = normalize(self.velocity); // get my current direction + dist = vlen(eorg - self.origin); + + // Do evasive maneuvers for world objects? ( this should be a cpu hog. :P ) + if ((autocvar_g_monster_vore_attack_spike_smart) && (dist > (autocvar_g_monster_vore_attack_spike_smart_mindist))) + { + // Is it a better idea (shorter distance) to trace to the target itself? + if ( vlen(self.origin + olddir * self.wait) < dist) - traceline(self.origin, self.origin + olddir * self.wait, FALSE, self); ++ traceline(self.origin, self.origin + olddir * self.wait, false, self); + else - traceline(self.origin, eorg, FALSE, self); ++ traceline(self.origin, eorg, false, self); + + // Setup adaptive tracelength + self.wait = bound((autocvar_g_monster_vore_attack_spike_smart_trace_min), vlen(self.origin - trace_endpos), self.wait = (autocvar_g_monster_vore_attack_spike_smart_trace_max)); + + // Calc how important it is that we turn and add this to the desierd (enemy) dir. + desireddir = normalize(((trace_plane_normal * (1 - trace_fraction)) + (desireddir * trace_fraction)) * 0.5); + } + + newdir = normalize(olddir + desireddir * turnrate); // take the average of the 2 directions; not the best method but simple & easy + self.velocity = newdir * spd; // make me fly in the new direction at my flight speed + } + else + dist = 0; + + /////////////// + + //self.angles = vectoangles(self.velocity); // turn model in the new flight direction + self.nextthink = time;// + 0.05; // csqc projectiles + UpdateCSQCProjectile(self); +} + +void M_Vore_Attack_Spike() +{ + entity missile; + vector dir = normalize((self.enemy.origin + '0 0 10') - self.origin); + + makevectors(self.angles); + + missile = spawn (); + missile.owner = missile.realowner = self; + missile.think = M_Vore_Attack_Spike_Think; + missile.ltime = time + 7; + missile.nextthink = time; + missile.solid = SOLID_BBOX; + missile.movetype = MOVETYPE_FLYMISSILE; + missile.flags = FL_PROJECTILE; + setorigin(missile, self.origin + v_forward * 14 + '0 0 30' + v_right * -14); + setsize (missile, '0 0 0', '0 0 0'); + missile.velocity = dir * 400; + missile.avelocity = '300 300 300'; + missile.enemy = self.enemy; + missile.touch = M_Vore_Attack_Spike_Touch; + + self.vore_spike = missile; + - CSQCProjectile(missile, TRUE, PROJECTILE_MAGE_SPIKE, TRUE); ++ CSQCProjectile(missile, true, PROJECTILE_MAGE_SPIKE, true); +} + +float M_Vore_Attack(float attack_type) +{ + switch(attack_type) + { + case MONSTER_ATTACK_MELEE: + { - return Monster_Attack_Melee(self.enemy, (autocvar_g_monster_vore_attack_melee_damage), vore_anim_attack, self.attack_range, (autocvar_g_monster_vore_attack_melee_delay), DEATH_MONSTER_VORE_MELEE, TRUE); ++ return Monster_Attack_Melee(self.enemy, (autocvar_g_monster_vore_attack_melee_damage), vore_anim_attack, self.attack_range, (autocvar_g_monster_vore_attack_melee_delay), DEATH_MONSTER_VORE_MELEE, true); + } + case MONSTER_ATTACK_RANGED: + { + if(!self.vore_spike) + { + self.frame = vore_anim_attack; + self.attack_finished_single = time + (autocvar_g_monster_vore_attack_spike_delay); + self.anim_finished = time + 1; + Monster_Delay(1, 0, 0.2, M_Vore_Attack_Spike); - return TRUE; ++ return true; + } + - return (self.vore_spike) ? 2 : FALSE; ++ return (self.vore_spike) ? 2 : false; + } + } + - return FALSE; ++ return false; +} + +void spawnfunc_monster_vore() { Monster_Spawn(MON_VORE); } + +// compatibility with old spawns +void spawnfunc_monster_shalrath() { spawnfunc_monster_vore(); } + +float M_Vore(float req) +{ + switch(req) + { + case MR_THINK: + { - return TRUE; ++ return true; + } + case MR_PAIN: + { + self.pain_finished = time + 0.4; + self.frame = vore_anim_pain; - return TRUE; ++ return true; + } + case MR_DEATH: + { + self.frame = vore_anim_death; - return TRUE; ++ return true; + } + case MR_SETUP: + { + if(!self.health) self.health = (autocvar_g_monster_vore_health); + if(!self.speed) { self.speed = (autocvar_g_monster_vore_speed_walk); } + if(!self.speed2) { self.speed2 = (autocvar_g_monster_vore_speed_run); } + if(!self.stopspeed) { self.stopspeed = (autocvar_g_monster_vore_speed_stop); } + if(!self.damageforcescale) { self.damageforcescale = (autocvar_g_monster_vore_damageforcescale); } + + self.m_anim_walk = vore_anim_walk; + self.m_anim_run = vore_anim_walk; + self.m_anim_idle = vore_anim_walk; + + self.monster_loot = spawnfunc_ammo_cells; + self.weapon = WEP_MACHINEGUN; + self.frame = vore_anim_walk; + - return TRUE; ++ return true; + } + case MR_PRECACHE: + { + precache_sound (W_Sound("grenade_impact")); + precache_sound (W_Sound("tagexp1")); - return TRUE; ++ return true; + } + } + - return TRUE; ++ return true; +} + +#endif // SVQC +#endif // REGISTER_MONSTER diff --cc qcsrc/common/monsters/monster/wyvern.qc index 0ce51e78b,b6736caa4..2acc1c06e --- a/qcsrc/common/monsters/monster/wyvern.qc +++ b/qcsrc/common/monsters/monster/wyvern.qc @@@ -11,8 -11,7 +11,10 @@@ REGISTER_MONSTER #else #ifdef SVQC ++#include "../../effects.qh" ++ float autocvar_g_monster_wyvern_health; +var float autocvar_g_monster_wyvern_damageforcescale = 0.6; float autocvar_g_monster_wyvern_attack_fireball_damage; float autocvar_g_monster_wyvern_attack_fireball_edgedamage; float autocvar_g_monster_wyvern_attack_fireball_damagetime; @@@ -69,13 -68,13 +71,13 @@@ void M_Wyvern_Attack_Fireball( missile.velocity = dir * (autocvar_g_monster_wyvern_attack_fireball_speed); missile.avelocity = '300 300 300'; missile.nextthink = time + 5; - missile.think = wyvern_fireball_explode; + missile.think = M_Wyvern_Attack_Fireball_Explode; missile.enemy = self.enemy; - missile.touch = wyvern_fireball_touch; + missile.touch = M_Wyvern_Attack_Fireball_Touch; - CSQCProjectile(missile, TRUE, PROJECTILE_FIREMINE, TRUE); + CSQCProjectile(missile, true, PROJECTILE_FIREMINE, true); } -float wyvern_attack(float attack_type) +float M_Wyvern_Attack(float attack_type) { switch(attack_type) { @@@ -83,35 -82,34 +85,35 @@@ case MONSTER_ATTACK_RANGED: { self.attack_finished_single = time + 1.2; + self.anim_finished = time + 1.2; - wyvern_fireball(); + M_Wyvern_Attack_Fireball(); - return TRUE; + return true; } } - return FALSE; + return false; } -void spawnfunc_monster_wyvern() -{ - self.classname = "monster_wyvern"; - - if(!monster_initialize(MON_WYVERN)) { remove(self); return; } -} +void spawnfunc_monster_wyvern() { Monster_Spawn(MON_WYVERN); } +#ifndef MONSTERS_EXTRA // compatibility with old spawns void spawnfunc_monster_wizard() { spawnfunc_monster_wyvern(); } +#endif // extra monsters include original wizard -float m_wyvern(float req) +float M_Wyvern(float req) { switch(req) { case MR_THINK: { - return TRUE; - monster_move((autocvar_g_monster_wyvern_speed_run), (autocvar_g_monster_wyvern_speed_walk), (autocvar_g_monster_wyvern_speed_stop), wyvern_anim_fly, wyvern_anim_hover, wyvern_anim_hover); ++ return true; + } + case MR_PAIN: + { - return TRUE; + return true; } case MR_DEATH: { @@@ -124,23 -122,17 +126,23 @@@ case MR_SETUP: { if(!self.health) self.health = (autocvar_g_monster_wyvern_health); + if(!self.speed) { self.speed = (autocvar_g_monster_wyvern_speed_walk); } + if(!self.speed2) { self.speed2 = (autocvar_g_monster_wyvern_speed_run); } + if(!self.stopspeed) { self.stopspeed = (autocvar_g_monster_wyvern_speed_stop); } + if(!self.damageforcescale) { self.damageforcescale = (autocvar_g_monster_wyvern_damageforcescale); } + + self.m_anim_walk = wyvern_anim_hover; + self.m_anim_run = wyvern_anim_fly; + self.m_anim_idle = wyvern_anim_hover; self.monster_loot = spawnfunc_item_cells; - self.monster_attackfunc = wyvern_attack; self.frame = wyvern_anim_hover; - return TRUE; + return true; } case MR_PRECACHE: { - return TRUE; - precache_model("models/monsters/wizard.mdl"); + return true; } } diff --cc qcsrc/common/monsters/monster/zombie.qc index d8f9dc916,411e1c686..ec798681a --- a/qcsrc/common/monsters/monster/zombie.qc +++ b/qcsrc/common/monsters/monster/zombie.qc @@@ -84,23 -79,24 +84,23 @@@ void M_Zombie_Defend_Block_End( return; self.frame = zombie_anim_blockend; - self.armorvalue = 0; - self.m_armor_blockpercent = autocvar_g_monsters_armor_blockpercent; + self.armorvalue = autocvar_g_monsters_armor_blockpercent; } -float zombie_block() +float M_Zombie_Defend_Block() { self.frame = zombie_anim_blockstart; - self.armorvalue = 100; - self.m_armor_blockpercent = 0.9; - self.state = MONSTER_STATE_ATTACK_MELEE; // freeze monster + self.armorvalue = 0.9; + self.state = MONSTER_ATTACK_MELEE; // freeze monster self.attack_finished_single = time + 2.1; + self.anim_finished = self.attack_finished_single; - defer(2, zombie_blockend); + Monster_Delay(1, 0, 2, M_Zombie_Defend_Block_End); - return TRUE; + return true; } -float zombie_attack(float attack_type) +float M_Zombie_Attack(float attack_type) { switch(attack_type) { @@@ -116,9 -112,9 +116,9 @@@ chosen_anim = zombie_anim_attackstanding3; if(random() < 0.3 && self.health < 75 && self.enemy.health > 10) - return zombie_block(); + return M_Zombie_Defend_Block(); - return Monster_Attack_Melee(self.enemy, (autocvar_g_monster_zombie_attack_melee_damage), chosen_anim, self.attack_range, (autocvar_g_monster_zombie_attack_melee_delay), DEATH_MONSTER_ZOMBIE_MELEE, TRUE); - return monster_melee(self.enemy, (autocvar_g_monster_zombie_attack_melee_damage), chosen_anim, self.attack_range, (autocvar_g_monster_zombie_attack_melee_delay), DEATH_MONSTER_ZOMBIE_MELEE, true); ++ return Monster_Attack_Melee(self.enemy, (autocvar_g_monster_zombie_attack_melee_damage), chosen_anim, self.attack_range, (autocvar_g_monster_zombie_attack_melee_delay), DEATH_MONSTER_ZOMBIE_MELEE, true); } case MONSTER_ATTACK_RANGED: { @@@ -127,32 -123,31 +127,32 @@@ } } - return FALSE; + return false; } -void spawnfunc_monster_zombie() -{ - self.classname = "monster_zombie"; - - if(!monster_initialize(MON_ZOMBIE)) { remove(self); return; } -} +void spawnfunc_monster_zombie() { Monster_Spawn(MON_ZOMBIE); } -float m_zombie(float req) +float M_Zombie(float req) { switch(req) { case MR_THINK: { - monster_move((autocvar_g_monster_zombie_speed_run), (autocvar_g_monster_zombie_speed_walk), (autocvar_g_monster_zombie_speed_stop), zombie_anim_runforward, zombie_anim_runforward, zombie_anim_idle); + if(time >= self.spawn_time) + self.damageforcescale = autocvar_g_monster_zombie_damageforcescale; - return TRUE; ++ return true; + } + case MR_PAIN: + { + self.pain_finished = time + 0.34; + self.frame = (random() >= 0.5) ? zombie_anim_painfront1 : zombie_anim_painfront2; - return TRUE; + return true; } case MR_DEATH: { - self.armorvalue = 0; - self.m_armor_blockpercent = autocvar_g_monsters_armor_blockpercent; + self.armorvalue = autocvar_g_monsters_armor_blockpercent; self.frame = ((random() > 0.5) ? zombie_anim_deathback1 : zombie_anim_deathfront1); - return TRUE; + return true; } case MR_SETUP: { @@@ -175,13 -164,13 +175,13 @@@ self.spawn_time = time + 2.1; self.spawnshieldtime = self.spawn_time; self.respawntime = 0.2; + self.damageforcescale = 0.0001; // no push while spawning - return TRUE; + return true; } case MR_PRECACHE: { - return TRUE; - precache_model("models/monsters/zombie.dpm"); + return true; } } diff --cc qcsrc/common/monsters/monsters.qc index a63c0f842,096c2e5ea..fb4b30aab --- a/qcsrc/common/monsters/monsters.qc +++ b/qcsrc/common/monsters/monsters.qc @@@ -4,11 -6,9 +6,11 @@@ entity monster_info[MON_MAXCOUNT]; entity dummy_monster_info; - void register_monster(float id, float(float) func, float(float) attackfunc, float monsterflags, vector min_s, vector max_s, string modelname, string shortname, string mname) -void register_monster(int id, float(float) func, int monsterflags, vector min_s, vector max_s, string modelname, string shortname, string mname) ++void register_monster(int id, float(float) func, int(float) attackfunc, int monsterflags, vector min_s, vector max_s, string modelname, string shortname, string mname) { entity e; + string noext = substring(modelname, 0, strlen(modelname) - 4); + string ext = substring(modelname, strlen(modelname) - 4, strlen(modelname)); monster_info[id - 1] = e = spawn(); e.classname = "monster_info"; e.monsterid = id; diff --cc qcsrc/common/monsters/monsters.qh index dc68b0a2f,6fa9dfaf7..1a2bdeea8 --- a/qcsrc/common/monsters/monsters.qh +++ b/qcsrc/common/monsters/monsters.qh @@@ -1,32 -1,32 +1,35 @@@ + #ifndef MONSTERS_H + #define MONSTERS_H + // monster requests - #define MR_SETUP 1 // (SERVER) setup monster data - #define MR_THINK 2 // (SERVER) logic to run every frame - #define MR_DEATH 3 // (SERVER) called when monster dies - #define MR_PRECACHE 4 // (BOTH) precaches models/sounds used by this monster - #define MR_PAIN 5 // (SERVER) called when monster is damaged + const int MR_SETUP = 1; // (SERVER) setup monster data + const int MR_THINK = 2; // (SERVER) logic to run every frame + const int MR_DEATH = 3; // (SERVER) called when monster dies + const int MR_PRECACHE = 4; // (BOTH) precaches models/sounds used by this monster ++const int MR_PAIN = 5; // (SERVER) called when monster is damaged -// functions: +// functions entity get_monsterinfo(float id); // special spawn flags - const float MONSTER_RESPAWN_DEATHPOINT = 16; // re-spawn where we died - const float MONSTER_TYPE_FLY = 32; - const float MONSTER_TYPE_SWIM = 64; - const float MONSTER_SIZE_BROKEN = 128; // TODO: remove when bad models are replaced - const float MON_FLAG_SUPERMONSTER = 256; // incredibly powerful monster - const float MON_FLAG_RANGED = 512; // monster shoots projectiles - const float MON_FLAG_MELEE = 1024; - const float MON_FLAG_RIDE = 2048; // monster can be mounted + const int MONSTER_RESPAWN_DEATHPOINT = 16; // re-spawn where we died + const int MONSTER_TYPE_FLY = 32; + const int MONSTER_TYPE_SWIM = 64; + const int MONSTER_SIZE_BROKEN = 128; // TODO: remove when bad models are replaced + const int MON_FLAG_SUPERMONSTER = 256; // incredibly powerful monster + const int MON_FLAG_RANGED = 512; // monster shoots projectiles + const int MON_FLAG_MELEE = 1024; ++const int MON_FLAG_RIDE = 2048; // monster can be mounted -// entity properties of monsterinfo: +// entity properties of monsterinfo .float monsterid; // MON_... .string netname; // short name .string monster_name; // human readable name -.float(float) monster_func; // m_... +.float(float) monster_func; // M_... +.float(float attack_type) monster_attackfunc; // attack function .string mdl; // currently a copy of the model .string model; // full name of model - .float spawnflags; + .int spawnflags; .vector mins, maxs; // monster hitbox size // other useful macros @@@ -36,22 -37,18 +39,22 @@@ // Monster Registration // ===================== +// enable for testing +//#define MONSTERS_EXTRA + float m_null(float dummy); -void register_monster(float id, float(float) func, float monsterflags, vector min_s, vector max_s, string modelname, string shortname, string mname); +void register_monster(float id, float(float) func, float(float) attackfunc, float monsterflags, vector min_s, vector max_s, string modelname, string shortname, string mname); void register_monsters_done(); - const float MON_MAXCOUNT = 24; // increase as necessary, limit is infinite, but keep loops small! - #define MON_FIRST 1 - float MON_COUNT; - float MON_LAST; -const int MON_MAXCOUNT = 24; ++const int MON_MAXCOUNT = 24; // increase as necessary, limit is infinite, but keep loops small! + const int MON_FIRST = 1; + int MON_COUNT; + int MON_LAST; -#define REGISTER_MONSTER_2(id,func,monsterflags,min_s,max_s,modelname,shortname,mname) \ - int id; \ +#define REGISTER_MONSTER_2(id,func,attackfunc,monsterflags,min_s,max_s,modelname,shortname,mname) \ + float id; \ float func(float); \ + float attackfunc(float); \ void RegisterMonsters_##id() \ { \ MON_LAST = (id = MON_FIRST + MON_COUNT); \ diff --cc qcsrc/common/monsters/spawn.qc index 42c19edf9,8aee54b98..20dd0f567 --- a/qcsrc/common/monsters/spawn.qc +++ b/qcsrc/common/monsters/spawn.qc @@@ -1,7 -1,21 +1,18 @@@ + #if defined(CSQC) + #elif defined(MENUQC) + #elif defined(SVQC) + #include "../../dpdefs/progsdefs.qh" + #include "../util.qh" + #include "monsters.qh" + #include "sv_monsters.qh" + #include "spawn.qh" + #include "../../server/autocvars.qh" + #include "../../server/defs.qh" + #endif entity spawnmonster (string monster, float monster_id, entity spawnedby, entity own, vector orig, float respwn, float invincible, float moveflag) { - // ensure spawnfunc database is initialized - //initialize_field_db(); - + float i; entity e = spawn(); - float i; e.spawnflags = MONSTERFLAG_SPAWNED; diff --cc qcsrc/common/monsters/sv_monsters.qc index 5fa87e526,28430e961..a0089eead --- a/qcsrc/common/monsters/sv_monsters.qc +++ b/qcsrc/common/monsters/sv_monsters.qc @@@ -1,13 -1,33 +1,35 @@@ - // ========== - // Monsters - // ========== - + #if defined(CSQC) + #elif defined(MENUQC) + #elif defined(SVQC) + #include "../../dpdefs/progsdefs.qh" + #include "../../dpdefs/dpextensions.qh" + #include "../../warpzonelib/common.qh" + #include "../constants.qh" + #include "../teams.qh" + #include "../util.qh" + #include "monsters.qh" + #include "sv_monsters.qh" + #include "../weapons/weapons.qh" + #include "../../server/autocvars.qh" + #include "../../server/defs.qh" + #include "../deathtypes.qh" + #include "../../server/mutators/mutators_include.qh" - #include "../../server/tturrets/include/turrets_early.qh" - #include "../../server/vehicles/vehicles_def.qh" ++ #include "../turrets/sv_turrets.qh" ++ #include "../turrets/util.qh" ++ #include "../vehicles/sv_vehicles.qh" ++ #include "../effects.qh" + #include "../../server/campaign.qh" + #include "../../server/command/common.qh" + #include "../../server/command/cmd.qh" + #include "../../csqcmodellib/sv_model.qh" + #include "../../server/round_handler.qh" - #include "../../server/tturrets/include/turrets.qh" + #endif -// ========================= -// SVQC Monster Properties -// ========================= - +void monsters_setstatus() +{ + self.stat_monsters_total = monsters_total; + self.stat_monsters_killed = monsters_killed; +} void monster_dropitem() { @@@ -40,71 -60,91 +62,71 @@@ } } -float Monster_SkillModifier() -{ - float t = 0.5+self.monster_skill*((1.2-0.3)/10); - - return t; -} - -float monster_isvalidtarget (entity targ, entity ent) +void monster_makevectors(entity e) { - if(!targ || !ent) - return false; // someone doesn't exist - - if(targ == ent) - return false; // don't attack ourselves - - //traceline(ent.origin, targ.origin, MOVE_NORMAL, ent); - - //if(trace_ent != targ) - //return false; - - if(targ.vehicle_flags & VHF_ISVEHICLE) - if(!((get_monsterinfo(ent.monsterid)).spawnflags & MON_FLAG_RANGED)) - return false; // melee attacks are useless against vehicles - - if(time < game_starttime) - return false; // monsters do nothing before the match has started - - if(targ.takedamage == DAMAGE_NO) - return false; // enemy can't be damaged - - if(targ.items & IT_INVISIBILITY) - return false; // enemy is invisible - - if(substring(targ.classname, 0, 10) == "onslaught_") - return false; // don't attack onslaught targets - - if(IS_SPEC(targ) || IS_OBSERVER(targ)) - return false; // enemy is a spectator - - if(!(targ.vehicle_flags & VHF_ISVEHICLE)) - if(targ.deadflag != DEAD_NO || ent.deadflag != DEAD_NO || targ.health <= 0 || ent.health <= 0) - return false; // enemy/self is dead + vector v; - if(ent.monster_owner == targ) - return false; // don't attack our master + v = e.origin + (e.mins + e.maxs) * 0.5; + self.v_angle = vectoangles(v - (self.origin + self.view_ofs)); + self.v_angle_x = -self.v_angle_x; - if(targ.monster_owner == ent) - return false; // don't attack our pet + makevectors(self.v_angle); +} - if(!(targ.vehicle_flags & VHF_ISVEHICLE)) - if(targ.flags & FL_NOTARGET) - return false; // enemy can't be targeted +// =============== +// Target handling +// =============== - float Monster_ValidTarget(entity mon, entity player) - if(!autocvar_g_monsters_typefrag) - if(targ.BUTTON_CHAT) - return false; // no typefragging! ++bool Monster_ValidTarget(entity mon, entity player) +{ + // ensure we're not checking nonexistent monster/target - if(!mon || !player) { return FALSE; } ++ if(!mon || !player) { return false; } + + if((player == mon) + || (autocvar_g_monsters_lineofsight && !checkpvs(mon.origin + mon.view_ofs, player)) // enemy cannot be seen + || (IS_VEHICLE(player) && !((get_monsterinfo(mon.monsterid)).spawnflags & MON_FLAG_RANGED)) // melee vs vehicle is useless + || (time < game_starttime) // monsters do nothing before match has started + || (player.takedamage == DAMAGE_NO) + || (player.items & IT_INVISIBILITY) + || (IS_SPEC(player) || IS_OBSERVER(player)) // don't attack spectators + || (!IS_VEHICLE(player) && (player.deadflag != DEAD_NO || mon.deadflag != DEAD_NO || player.health <= 0 || mon.health <= 0)) + || (mon.monster_follow == player || player.monster_follow == mon) + || (!IS_VEHICLE(player) && (player.flags & FL_NOTARGET)) + || (!autocvar_g_monsters_typefrag && player.BUTTON_CHAT) + || (SAME_TEAM(player, mon)) + || (player.frozen) + || (player.alpha != 0 && player.alpha < 0.5) + ) + { + // if any of the above checks fail, target is not valid - return FALSE; ++ return false; + } - if(SAME_TEAM(targ, ent)) - return false; // enemy is on our team + traceline(mon.origin + self.view_ofs, player.origin, 0, mon); - if (targ.frozen) - return false; // ignore frozen + if((trace_fraction < 1) && (trace_ent != player)) - return FALSE; ++ return false; - if(autocvar_g_monsters_target_infront || (ent.spawnflags & MONSTERFLAG_INFRONT)) - if(ent.enemy != targ) + if(autocvar_g_monsters_target_infront || (mon.spawnflags & MONSTERFLAG_INFRONT)) + if(mon.enemy != player) { float dot; - makevectors (ent.angles); - dot = normalize (targ.origin - ent.origin) * v_forward; + makevectors (mon.angles); + dot = normalize (player.origin - mon.origin) * v_forward; - if(dot <= 0.3) { return FALSE; } - if(dot <= 0.3) - return false; ++ if(dot <= 0.3) { return false; } } - return TRUE; // this target is valid! - return true; ++ return true; // this target is valid! } -entity FindTarget (entity ent) +entity Monster_FindTarget(entity mon) { - if(MUTATOR_CALLHOOK(MonsterFindTarget)) { return ent.enemy; } // Handled by a mutator + if(MUTATOR_CALLHOOK(MonsterFindTarget)) { return mon.enemy; } // Handled by a mutator entity head, closest_target = world; - head = findradius(ent.origin, ent.target_range); - //head = WarpZone_FindRadius(ent.origin, ent.target_range, true); + head = findradius(mon.origin, mon.target_range); while(head) // find the closest acceptable target to pass to { @@@ -130,84 -175,17 +152,84 @@@ return closest_target; } -void MonsterTouch () +void monster_setupcolors(entity mon) { - if(other == world) - return; + if(IS_PLAYER(mon.realowner)) + mon.colormap = mon.realowner.colormap; + else if(teamplay && mon.team) + mon.colormap = 1024 + (mon.team - 1) * 17; + else + { + if(mon.monster_skill <= MONSTER_SKILL_EASY) + mon.colormap = 1029; + else if(mon.monster_skill <= MONSTER_SKILL_MEDIUM) + mon.colormap = 1027; + else if(mon.monster_skill <= MONSTER_SKILL_HARD) + mon.colormap = 1038; + else if(mon.monster_skill <= MONSTER_SKILL_INSANE) + mon.colormap = 1028; + else if(mon.monster_skill <= MONSTER_SKILL_NIGHTMARE) + mon.colormap = 1032; + else + mon.colormap = 1024; + } +} - if(self.enemy != other) - if(!(other.flags & FL_MONSTER)) - if(monster_isvalidtarget(other, self)) - self.enemy = other; +void monster_changeteam(entity ent, float newteam) +{ + if(!teamplay) { return; } + + ent.team = newteam; - ent.monster_attack = TRUE; // new team, activate attacking ++ ent.monster_attack = true; // new team, activate attacking + monster_setupcolors(ent); + + if(ent.sprite) + { + WaypointSprite_UpdateTeamRadar(ent.sprite, RADARICON_DANGER, ((newteam) ? Team_ColorRGB(newteam) : '1 0 0')); + + ent.sprite.team = newteam; + ent.sprite.SendFlags |= 1; + } } +void Monster_Delay_Action() +{ + entity oldself = self; + self = self.owner; + if(Monster_ValidTarget(self, self.enemy)) { oldself.use(); } + + if(oldself.cnt > 0) + { + oldself.cnt -= 1; + oldself.think = Monster_Delay_Action; + oldself.nextthink = time + oldself.respawn_time; + } + else + { + oldself.think = SUB_Remove; + oldself.nextthink = time; + } +} + +void Monster_Delay(float repeat_count, float repeat_defer, float defer_amnt, void() func) +{ + // deferred attacking, checks if monster is still alive and target is still valid before attacking + entity e = spawn(); + + e.think = Monster_Delay_Action; + e.nextthink = time + defer_amnt; + e.count = defer_amnt; + e.owner = self; + e.use = func; + e.cnt = repeat_count; + e.respawn_time = repeat_defer; +} + + +// ============== +// Monster sounds +// ============== + string get_monster_model_datafilename(string m, float sk, string fil) { if(m) @@@ -288,7 -266,7 +310,7 @@@ float Monster_Sounds_Load(string f, flo if(fh < 0) { dprint("Monster sound file not found: ", f, "\n"); - return FALSE; - return 0; ++ return false; } while((s = fgets(fh))) { @@@ -302,21 -280,25 +324,21 @@@ self.field = strzone(strcat(argv(1), " ", argv(2))); } fclose(fh); - return TRUE; - return 1; ++ return true; } - .float skin_for_monstersound; + .int skin_for_monstersound; -void UpdateMonsterSounds() +void Monster_Sounds_Update() { - entity mon = get_monsterinfo(self.monsterid); + if(self.skin == self.skin_for_monstersound) { return; } - if(self.skin == self.skin_for_monstersound) - return; self.skin_for_monstersound = self.skin; - ClearMonsterSounds(); - //LoadMonsterSounds("sound/monsters/default.sounds", 1); - if(!autocvar_g_debug_defaultsounds) - if(!LoadMonsterSounds(get_monster_model_datafilename(mon.model, self.skin, "sounds"), 0)) - LoadMonsterSounds(get_monster_model_datafilename(mon.model, 0, "sounds"), 0); + Monster_Sounds_Clear(); + if(!Monster_Sounds_Load(get_monster_model_datafilename(self.model, self.skin, "sounds"), 0)) + Monster_Sounds_Load(get_monster_model_datafilename(self.model, 0, "sounds"), 0); } -void MonsterSound(.string samplefield, float sound_delay, float delaytoo, float chan) +void Monster_Sound(.string samplefield, float sound_delay, float delaytoo, float chan) { if(!autocvar_g_monsters_sounds) { return; } @@@ -328,130 -310,45 +350,130 @@@ self.msound_delay = time + sound_delay; } -void monster_makevectors(entity e) + +// ======================= +// Monster attack handlers +// ======================= + +float Monster_Attack_Melee(entity targ, float damg, float anim, float er, float animtime, float deathtype, float dostop) { - vector v; + if(dostop) { self.state = MONSTER_ATTACK_MELEE; } - v = e.origin + (e.mins + e.maxs) * 0.5; - self.v_angle = vectoangles(v - (self.origin + self.view_ofs)); - self.v_angle_x = -self.v_angle.x; + self.frame = anim; - makevectors(self.v_angle); + if(animtime > 0) { self.attack_finished_single = self.anim_finished = time + animtime; } + + monster_makevectors(targ); + + traceline(self.origin + self.view_ofs, self.origin + v_forward * er, 0, self); + + if(trace_ent.takedamage) + Damage(trace_ent, self, self, damg * MONSTER_SKILLMOD(self), deathtype, trace_ent.origin, normalize(trace_ent.origin - self.origin)); + - return TRUE; ++ return true; +} + +float Monster_Attack_Leap_Check(vector vel) +{ + if(self.state) - return FALSE; // already attacking ++ return false; // already attacking + if(!(self.flags & FL_ONGROUND)) - return FALSE; // not on the ground ++ return false; // not on the ground + if(self.health <= 0) - return FALSE; // called when dead? ++ return false; // called when dead? + if(time < self.attack_finished_single) - return FALSE; // still attacking ++ return false; // still attacking + + vector old = self.velocity; + + self.velocity = vel; + tracetoss(self, self); + self.velocity = old; + if (trace_ent != self.enemy) - return FALSE; ++ return false; + - return TRUE; ++ return true; +} + - float Monster_Attack_Leap(float anm, void() touchfunc, vector vel, float animtime) ++bool Monster_Attack_Leap(int anm, void() touchfunc, vector vel, float animtime) +{ + if(!Monster_Attack_Leap_Check(vel)) - return FALSE; ++ return false; + + self.frame = anm; + self.state = MONSTER_ATTACK_RANGED; + self.touch = touchfunc; + self.origin_z += 1; + self.velocity = vel; + self.flags &= ~FL_ONGROUND; + + self.attack_finished_single = time + animtime; + self.anim_finished = self.attack_finished_single; // TODO: make these frame based + - return TRUE; ++ return true; } -float monster_melee(entity targ, float damg, float anim, float er, float anim_finished, float deathtype, float dostop) +void Monster_Attack_Check(entity e, entity targ) { - if (self.health <= 0) - return false; // attacking while dead?! + if((e == world || targ == world) + || (!e.monster_attackfunc) + || (time < e.attack_finished_single) + ) { return; } - if(dostop) + float targ_vlen = vlen(targ.origin - e.origin); + + if(targ_vlen <= e.attack_range) { - self.velocity_x = 0; - self.velocity_y = 0; - self.state = MONSTER_STATE_ATTACK_MELEE; + float attack_success = e.monster_attackfunc(MONSTER_ATTACK_MELEE); + if(attack_success == 1) - Monster_Sound(monstersound_melee, 0, FALSE, CH_VOICE); ++ Monster_Sound(monstersound_melee, 0, false, CH_VOICE); + else if(attack_success > 0) + return; } - self.frame = anim; + if(targ_vlen > e.attack_range) + { + float attack_success = e.monster_attackfunc(MONSTER_ATTACK_RANGED); + if(attack_success == 1) - Monster_Sound(monstersound_melee, 0, FALSE, CH_VOICE); ++ Monster_Sound(monstersound_melee, 0, false, CH_VOICE); + else if(attack_success > 0) + return; + } +} - if(anim_finished != 0) - self.attack_finished_single = time + anim_finished; - monster_makevectors(targ); +// ====================== +// Main monster functions +// ====================== - traceline(self.origin + self.view_ofs, self.origin + v_forward * er, 0, self); +void Monster_Skin_Check() +{ + vector oldmin = self.mins, oldmax = self.maxs; + entity mon = get_monsterinfo(self.monsterid); + string trymodel = sprintf("%s_%d%s", substring(mon.model, 0, strlen(mon.model) - 4), self.skin, substring(mon.model, strlen(mon.model) - 4, strlen(mon.model))); - if(trace_ent.takedamage) - Damage(trace_ent, self, self, damg * Monster_SkillModifier(), deathtype, trace_ent.origin, normalize(trace_ent.origin - self.origin)); + if(fexists(trymodel)) + { + precache_model(trymodel); + setmodel(self, trymodel); + setsize(self, oldmin, oldmax); + CSQCMODEL_AUTOUPDATE(); // do a quick update + } - return true; + self.oldskin = self.skin; } -void Monster_CheckMinibossFlag () +void Monster_Touch() +{ + if(other == world) { return; } + + if(self.enemy != other) + if(!IS_MONSTER(other)) + if(Monster_ValidTarget(self, other)) + self.enemy = other; +} + +void Monster_Miniboss_Check() { if(MUTATOR_CALLHOOK(MonsterCheckBossFlag)) return; @@@ -468,22 -365,34 +490,22 @@@ } } -float Monster_CanRespawn(entity ent) +float Monster_Respawn_Check() { - if(self.spawnflags & MONSTERFLAG_NORESPAWN) { return FALSE; } - if(!autocvar_g_monsters_respawn) { return FALSE; } - other = ent; - if(ent.deadflag == DEAD_DEAD) // don't call when monster isn't dead - if(MUTATOR_CALLHOOK(MonsterRespawn)) - return true; // enabled by a mutator - - if(ent.spawnflags & MONSTERFLAG_NORESPAWN) - return false; - - if(!autocvar_g_monsters_respawn) - return false; ++ if(self.spawnflags & MONSTERFLAG_NORESPAWN) { return false; } ++ if(!autocvar_g_monsters_respawn) { return false; } - return TRUE; + return true; } -void monster_respawn() -{ - // is this function really needed? - monster_initialize(self.monsterid); -} +void Monster_Respawn() { Monster_Spawn(self.monsterid); } -void Monster_Fade () +void Monster_Dead_Fade() { - if(Monster_CanRespawn(self)) + if(Monster_Respawn_Check()) { self.spawnflags |= MONSTERFLAG_RESPAWNED; - self.think = monster_respawn; + self.think = Monster_Respawn; self.nextthink = time + self.respawntime; self.monster_lifetime = 0; self.deadflag = DEAD_RESPAWNING; @@@ -541,15 -523,12 +563,15 @@@ vector Monster_Move_Target(entity targ print("Trace origin: ", vtos(targ_origin), "\n"); print("Target origin: ", vtos(self.enemy.origin), "\n"); print("My origin: ", vtos(self.origin), "\n"); */ - + self.monster_movestate = MONSTER_MOVE_ENEMY; self.last_trace = time + 1.2; - return targ_origin; + if(self.monster_moveto) + return self.monster_moveto; // assumes code is properly setting this when monster has an enemy + else + return targ_origin; } - + /*makevectors(self.angles); self.monster_movestate = MONSTER_MOVE_ENEMY; self.last_trace = time + 1.2; @@@ -621,11 -587,11 +643,11 @@@ } } -void monster_CalculateVelocity(entity mon, vector to, vector from, float turnrate, float movespeed) +void Monster_CalculateVelocity(entity mon, vector to, vector from, float turnrate, float movespeed) { - float current_distance = vlen((('1 0 0' * to_x) + ('0 1 0' * to_y)) - (('1 0 0' * from_x) + ('0 1 0' * from_y))); // for the sake of this check, exclude Z axis + float current_distance = vlen((('1 0 0' * to.x) + ('0 1 0' * to.y)) - (('1 0 0' * from.x) + ('0 1 0' * from.y))); // for the sake of this check, exclude Z axis float initial_height = 0; //min(50, (targ_distance * tanh(20))); - float current_height = (initial_height * min(1, (current_distance / self.pass_distance))); + float current_height = (initial_height * min(1, (self.pass_distance) ? (current_distance / self.pass_distance) : current_distance)); //print("current_height = ", ftos(current_height), ", initial_height = ", ftos(initial_height), ".\n"); vector targpos; @@@ -791,53 -753,56 +813,53 @@@ void Monster_Move(float runspeed, floa WarpZone_RefSys_Copy(self.enemy, self); WarpZone_RefSys_AddInverse(self.enemy, self); // wz1^-1 ... wzn^-1 receiver self.moveto = WarpZone_RefSys_TransformOrigin(self.enemy, self, (0.5 * (self.enemy.absmin + self.enemy.absmax))); - - self.pass_distance = vlen((('1 0 0' * self.enemy.origin.x) + ('0 1 0' * self.enemy.origin.y)) - (('1 0 0' * self.origin.x) + ('0 1 0' * self.origin.y))); - MonsterSound(monstersound_sight, 0, false, CH_VOICE); + self.monster_moveto = '0 0 0'; + self.monster_face = '0 0 0'; + + self.pass_distance = vlen((('1 0 0' * self.enemy.origin_x) + ('0 1 0' * self.enemy.origin_y)) - (('1 0 0' * self.origin_x) + ('0 1 0' * self.origin_y))); - Monster_Sound(monstersound_sight, 0, FALSE, CH_VOICE); ++ Monster_Sound(monstersound_sight, 0, false, CH_VOICE); } } self.last_enemycheck = time + 1; // check for enemies every second } - if(self.state == MONSTER_STATE_ATTACK_MELEE && time >= self.attack_finished_single) + if(self.state == MONSTER_ATTACK_RANGED && (self.flags & FL_ONGROUND)) + { self.state = 0; + self.touch = Monster_Touch; + } - if(self.state != MONSTER_STATE_ATTACK_MELEE) // don't move if set - if(time >= self.last_trace || self.enemy) // update enemy instantly - self.moveto = monster_pickmovetarget(targ); + if(self.state && time >= self.attack_finished_single) + self.state = 0; // attack is over - if(!self.enemy) - MonsterSound(monstersound_idle, 7, true, CH_VOICE); + if(self.state != MONSTER_ATTACK_MELEE) // don't move if set + if(time >= self.last_trace || self.enemy || self.piggybacker) // update enemy or rider instantly + self.moveto = Monster_Move_Target(targ); - if(self.state == MONSTER_STATE_ATTACK_LEAP && (self.flags & FL_ONGROUND)) - { - self.state = 0; - self.touch = MonsterTouch; - } + if(!self.enemy) - Monster_Sound(monstersound_idle, 7, TRUE, CH_VOICE); ++ Monster_Sound(monstersound_idle, 7, true, CH_VOICE); - if(self.state == MONSTER_STATE_ATTACK_MELEE) + if(self.state == MONSTER_ATTACK_MELEE) self.moveto = self.origin; if(self.enemy && self.enemy.vehicle) runspeed = 0; - if(!(((self.flags & FL_FLY) && (self.spawnflags & MONSTERFLAG_FLY_VERTICAL)) || (self.flags & FL_SWIM))) - //v_forward = normalize(self.moveto - self.origin); - //else - self.moveto_z = self.origin.z; + if(!(self.spawnflags & MONSTERFLAG_FLY_VERTICAL) && !(self.flags & FL_SWIM)) + self.moveto_z = self.origin_z; - if(vlen(self.origin - self.moveto) > 64) + if(vlen(self.origin - self.moveto) > 100) { + float do_run = (enemy || (self.piggybacker && self.monster_moveto)); if((self.flags & FL_ONGROUND) || ((self.flags & FL_FLY) || (self.flags & FL_SWIM))) - Monster_CalculateVelocity(self, self.moveto, self.origin, TRUE, ((do_run) ? runspeed : walkspeed)); - monster_CalculateVelocity(self, self.moveto, self.origin, true, ((self.enemy) ? runspeed : walkspeed)); ++ Monster_CalculateVelocity(self, self.moveto, self.origin, true, ((do_run) ? runspeed : walkspeed)); - /*&if(self.flags & FL_FLY || self.flags & FL_SWIM) - movelib_move_simple(v_forward, ((self.enemy) ? runspeed : walkspeed), 0.6); - else - movelib_move_simple_gravity(v_forward, ((self.enemy) ? runspeed : walkspeed), 0.6); */ - - if(time > self.pain_finished) - if(time > self.attack_finished_single) + if(time > self.pain_finished) // TODO: use anim_finished instead! + if(!self.state) + if(time > self.anim_finished) if(vlen(self.velocity) > 10) - self.frame = ((self.enemy) ? manim_run : manim_walk); + self.frame = ((do_run) ? manim_run : manim_walk); else self.frame = manim_idle; } @@@ -901,13 -878,13 +923,13 @@@ void Monster_Appear( { self.enemy = activator; self.spawnflags &= ~MONSTERFLAG_APPEAR; // otherwise, we get an endless loop - monster_initialize(self.monsterid); + Monster_Spawn(self.monsterid); } -float Monster_CheckAppearFlags(entity ent, float monster_id) +float Monster_Appear_Check(entity ent, float monster_id) { if(!(ent.spawnflags & MONSTERFLAG_APPEAR)) - return FALSE; + return false; ent.think = func_null; ent.monsterid = monster_id; // set so this monster is properly registered (otherwise, normal initialization is used) @@@ -915,10 -892,10 +937,10 @@@ ent.use = Monster_Appear; ent.flags = FL_MONSTER; // set so this monster can get butchered - return TRUE; + return true; } -void monsters_reset() +void Monster_Reset() { setorigin(self, self.pos1); self.angles = self.pos2; @@@ -966,7 -943,7 +988,7 @@@ void Monster_Dead(entity attacker, floa monster_dropitem(); - Monster_Sound(monstersound_death, 0, FALSE, CH_VOICE); - MonsterSound(monstersound_death, 0, false, CH_VOICE); ++ Monster_Sound(monstersound_death, 0, false, CH_VOICE); if(!(self.spawnflags & MONSTERFLAG_SPAWNED) && !(self.spawnflags & MONSTERFLAG_RESPAWNED)) monsters_killed += 1; @@@ -1025,24 -999,13 +1047,24 @@@ void Monster_Damage(entity inflictor, e vector v; float take, save; - v = healtharmor_applydamage(self.armorvalue, self.m_armor_blockpercent, deathtype, damage); - take = v.x; - save = v.y; + v = healtharmor_applydamage(100, self.armorvalue / 100, deathtype, damage, autocvar_g_balance_armor_block_bycount); + take = v_x; + save = v_y; - self.health -= take; + damage_take = take; + frag_attacker = attacker; + frag_deathtype = deathtype; + MON_ACTION(self.monsterid, MR_PAIN); + take = damage_take; - WaypointSprite_UpdateHealth(self.sprite, self.health); + if(take) + { + self.health -= take; - Monster_Sound(monstersound_pain, 1.2, TRUE, CH_PAIN); ++ Monster_Sound(monstersound_pain, 1.2, true, CH_PAIN); + } + + if(self.sprite) + WaypointSprite_UpdateHealth(self.sprite, self.health); self.dmg_time = time; @@@ -1143,55 -1128,52 +1165,55 @@@ float Monster_Spawn_Setup( if(!self.wander_delay) { self.wander_delay = 2; } if(!self.wander_distance) { self.wander_distance = 600; } - precache_monstersounds(); - UpdateMonsterSounds(); + Monster_Sounds_Precache(); + Monster_Sounds_Update(); if(teamplay) - self.monster_attack = TRUE; // we can have monster enemies in team games + self.monster_attack = true; // we can have monster enemies in team games - Monster_Sound(monstersound_spawn, 0, FALSE, CH_VOICE); - MonsterSound(monstersound_spawn, 0, false, CH_VOICE); ++ Monster_Sound(monstersound_spawn, 0, false, CH_VOICE); - WaypointSprite_Spawn(M_NAME(self.monsterid), 0, 1024, self, '0 0 1' * (self.maxs.z + 15), world, self.team, self, sprite, true, RADARICON_DANGER, ((self.team) ? Team_ColorRGB(self.team) : '1 0 0')); - if(!(self.spawnflags & MONSTERFLAG_INVINCIBLE)) + if(autocvar_g_monsters_healthbars) { - WaypointSprite_UpdateMaxHealth(self.sprite, self.max_health); - WaypointSprite_UpdateHealth(self.sprite, self.health); + WaypointSprite_Spawn(self.monster_name, 0, 1024, self, '0 0 1' * (self.maxs_z + 15), world, self.team, - self, sprite, TRUE, RADARICON_DANGER, ((self.team) ? Team_ColorRGB(self.team) : '1 0 0')); ++ self, sprite, true, RADARICON_DANGER, ((self.team) ? Team_ColorRGB(self.team) : '1 0 0')); + + if(!(self.spawnflags & MONSTERFLAG_INVINCIBLE)) + { + WaypointSprite_UpdateMaxHealth(self.sprite, self.max_health); + WaypointSprite_UpdateHealth(self.sprite, self.health); + } } - self.think = monster_think; + self.think = Monster_Think; self.nextthink = time + self.ticrate; if(MUTATOR_CALLHOOK(MonsterSpawn)) - return FALSE; + return false; - return TRUE; + return true; } - float Monster_Spawn(float mon_id) -float monster_initialize(float mon_id) ++bool Monster_Spawn(int mon_id) { - if(!autocvar_g_monsters) { return false; } - if(!(self.spawnflags & MONSTERFLAG_RESPAWNED)) { MON_ACTION(mon_id, MR_PRECACHE); } - if(Monster_CheckAppearFlags(self, mon_id)) { return true; } // return true so the monster isn't removed - + // setup the basic required properties for a monster entity mon = get_monsterinfo(mon_id); - if(!mon.monsterid) { return FALSE; } // invalid monster ++ if(!mon.monsterid) { return false; } // invalid monster + - if(!autocvar_g_monsters) { Monster_Remove(self); return FALSE; } ++ if(!autocvar_g_monsters) { Monster_Remove(self); return false; } + + self.mdl = mon.model; + if(!(self.spawnflags & MONSTERFLAG_RESPAWNED)) { precache_model(self.mdl); } + if(!(self.spawnflags & MONSTERFLAG_RESPAWNED)) { MON_ACTION(mon_id, MR_PRECACHE); } - if(Monster_Appear_Check(self, mon_id)) { return TRUE; } // return true so the monster isn't removed ++ if(Monster_Appear_Check(self, mon_id)) { return true; } // return true so the monster isn't removed if(!self.monster_skill) self.monster_skill = cvar("g_monsters_skill"); // support for quake style removing monsters based on skill - if(self.monster_skill == MONSTER_SKILL_EASY) if(self.spawnflags & MONSTERSKILL_NOTEASY) { Monster_Remove(self); return FALSE; } - if(self.monster_skill == MONSTER_SKILL_MEDIUM) if(self.spawnflags & MONSTERSKILL_NOTMEDIUM) { Monster_Remove(self); return FALSE; } - if(self.monster_skill == MONSTER_SKILL_HARD) if(self.spawnflags & MONSTERSKILL_NOTHARD) { Monster_Remove(self); return FALSE; } - if(self.monster_skill == MONSTER_SKILL_EASY) if(self.spawnflags & MONSTERSKILL_NOTEASY) { return false; } - if(self.monster_skill == MONSTER_SKILL_MEDIUM) if(self.spawnflags & MONSTERSKILL_NOTMEDIUM) { return false; } - if(self.monster_skill == MONSTER_SKILL_HARD) if(self.spawnflags & MONSTERSKILL_NOTHARD) { return false; } ++ if(self.monster_skill == MONSTER_SKILL_EASY) if(self.spawnflags & MONSTERSKILL_NOTEASY) { Monster_Remove(self); return false; } ++ if(self.monster_skill == MONSTER_SKILL_MEDIUM) if(self.spawnflags & MONSTERSKILL_NOTMEDIUM) { Monster_Remove(self); return false; } ++ if(self.monster_skill == MONSTER_SKILL_HARD) if(self.spawnflags & MONSTERSKILL_NOTHARD) { Monster_Remove(self); return false; } if(self.team && !teamplay) self.team = 0; @@@ -1200,18 -1182,19 +1222,18 @@@ if(!(self.spawnflags & MONSTERFLAG_RESPAWNED)) // don't count re-spawning monsters either monsters_total += 1; - setmodel(self, mon.model); - //setsize(self, mon.mins, mon.maxs); + setmodel(self, self.mdl); self.flags = FL_MONSTER; + self.classname = "monster"; self.takedamage = DAMAGE_AIM; - self.bot_attack = TRUE; - self.iscreature = TRUE; - self.teleportable = TRUE; - self.damagedbycontents = TRUE; + self.bot_attack = true; + self.iscreature = true; + self.teleportable = true; + self.damagedbycontents = true; self.monsterid = mon_id; - self.damageforcescale = 0; - self.event_damage = monsters_damage; - self.touch = MonsterTouch; - self.use = monster_use; + self.event_damage = Monster_Damage; + self.touch = Monster_Touch; + self.use = Monster_Use; self.solid = SOLID_BBOX; self.movetype = MOVETYPE_WALK; self.spawnshieldtime = time + autocvar_g_monsters_spawnshieldtime; @@@ -1220,12 -1203,11 +1242,12 @@@ self.moveto = self.origin; self.pos1 = self.origin; self.pos2 = self.angles; - self.reset = monsters_reset; + self.reset = Monster_Reset; self.netname = mon.netname; - self.monster_name = M_NAME(mon_id); + self.monster_attackfunc = mon.monster_attackfunc; + self.monster_name = mon.monster_name; - self.candrop = TRUE; + self.candrop = true; - self.view_ofs = '0 0 1' * (self.maxs.z * 0.5); + self.view_ofs = '0 0 0.7' * (self.maxs_z * 0.5); self.oldtarget2 = self.target2; self.pass_distance = 0; self.deadflag = DEAD_NO; @@@ -1255,13 -1244,22 +1277,13 @@@ setsize(self, mon.mins * self.scale, mon.maxs * self.scale); - if(!self.ticrate) - self.ticrate = autocvar_g_monsters_think_delay; - - self.ticrate = bound(sys_frametime, self.ticrate, 60); - - if(!self.m_armor_blockpercent) - self.m_armor_blockpercent = 0.5; - - if(!self.target_range) - self.target_range = autocvar_g_monsters_target_range; - - if(!self.respawntime) - self.respawntime = autocvar_g_monsters_respawn_delay; + self.ticrate = bound(sys_frametime, ((!self.ticrate) ? autocvar_g_monsters_think_delay : self.ticrate), 60); - if(!self.monster_moveflags) - self.monster_moveflags = MONSTER_MOVE_WANDER; + if(!Monster_Spawn_Setup()) + { + Monster_Remove(self); - return FALSE; ++ return false; + } if(!self.noalign) { diff --cc qcsrc/common/monsters/sv_monsters.qh index 05b71b10d,f6b149816..81769fcc2 --- a/qcsrc/common/monsters/sv_monsters.qh +++ b/qcsrc/common/monsters/sv_monsters.qh @@@ -1,85 -1,55 +1,98 @@@ - // ===================== - // Monster definitions - // For server (SVQC) - // ===================== - + #ifndef SV_MONSTERS_H + #define SV_MONSTERS_H -.string spawnmob; -.float monster_attack; +// stats networking - .float stat_monsters_killed; - .float stat_monsters_total; - float monsters_total; - float monsters_killed; ++.int stat_monsters_killed; ++.int stat_monsters_total; ++int monsters_total; ++int monsters_killed; + +// monster properties - .float monster_movestate; // move target priority ++.int monster_movestate; // move target priority +.entity monster_follow; // follow target +.float wander_delay; // logic delay between moving while idle +.float wander_distance; // distance to move between wander delays +.float monster_lifetime; // monster dies instantly after this delay, set from spawn +.float attack_range; // melee attack if closer, ranged attack if further away (TODO: separate ranged attack range?) +.float spawn_time; // delay monster thinking until spawn animation has completed - .float candrop; // toggle to allow disabling monster item drops - .float monster_movestate; // will be phased out - .float monster_moveflags; ++.bool candrop; // toggle to allow disabling monster item drops ++.int monster_movestate; // will be phased out ++.int monster_moveflags; +.string oldtarget2; // a copy of the original follow target string +.float last_trace; // logic delay between target tracing +.float last_enemycheck; // for checking enemy +.float anim_finished; // will be phased out when we have proper animations system +.vector monster_moveto; // custom destination for monster (reset to '0 0 0' when you're done!) +.vector monster_face; // custom looking direction for monster (reset to '0 0 0' when you're done!) +.float speed2; // run speed +.float stopspeed; - .float m_anim_run; - .float m_anim_walk; - .float m_anim_idle; - .float oldskin; ++.int m_anim_run; ++.int m_anim_walk; ++.int m_anim_idle; ++.int oldskin; + +#define MONSTER_SKILLMOD(mon) (0.5 + mon.monster_skill * ((1.2 - 0.3) / 10)) + +// other properties - .float monster_attack; // indicates whether an entity can be attacked by monsters ++.bool monster_attack; // indicates whether an entity can be attacked by monsters +.float spider_slowness; // effect time of slowness inflicted by spiders + +// monster state declarations - const float MONSTER_MOVE_FOLLOW = 1; // monster will follow if in range, or stand still - const float MONSTER_MOVE_WANDER = 2; // monster will ignore owner & wander around - const float MONSTER_MOVE_SPAWNLOC = 3; // monster will move to its spawn location when not attacking - const float MONSTER_MOVE_NOMOVE = 4; // monster simply stands still - const float MONSTER_MOVE_ENEMY = 5; // used only as a movestate - const float MONSTER_ATTACK_MELEE = 6; - const float MONSTER_ATTACK_RANGED = 7; ++const int MONSTER_MOVE_FOLLOW = 1; // monster will follow if in range, or stand still ++const int MONSTER_MOVE_WANDER = 2; // monster will ignore owner & wander around ++const int MONSTER_MOVE_SPAWNLOC = 3; // monster will move to its spawn location when not attacking ++const int MONSTER_MOVE_NOMOVE = 4; // monster simply stands still ++const int MONSTER_MOVE_ENEMY = 5; // used only as a movestate ++const int MONSTER_ATTACK_MELEE = 6; ++const int MONSTER_ATTACK_RANGED = 7; -.entity monster_owner; // new monster owner entity, fixes non-solid monsters +// skill declarations - const float MONSTER_SKILL_EASY = 1; - const float MONSTER_SKILL_MEDIUM = 3; - const float MONSTER_SKILL_HARD = 5; - const float MONSTER_SKILL_INSANE = 7; - const float MONSTER_SKILL_NIGHTMARE = 10; ++const int MONSTER_SKILL_EASY = 1; ++const int MONSTER_SKILL_MEDIUM = 3; ++const int MONSTER_SKILL_HARD = 5; ++const int MONSTER_SKILL_INSANE = 7; ++const int MONSTER_SKILL_NIGHTMARE = 10; - const float MONSTERSKILL_NOTEASY = 256; // monster will not spawn on skill <= 1 - const float MONSTERSKILL_NOTMEDIUM = 512; // monster will not spawn on skill 2 - const float MONSTERSKILL_NOTHARD = 1024; // monster will not spawn on skill >= 3 -.float stat_monsters_killed; // stats -.float stat_monsters_total; -float monsters_total; -float monsters_killed; -void monsters_setstatus(); // monsters.qc -.float monster_moveflags; // checks where to move when not attacking ++const int MONSTERSKILL_NOTEASY = 256; // monster will not spawn on skill <= 1 ++const int MONSTERSKILL_NOTMEDIUM = 512; // monster will not spawn on skill 2 ++const int MONSTERSKILL_NOTHARD = 1024; // monster will not spawn on skill >= 3 -.float wander_delay; -.float wander_distance; +// spawn flags - const float MONSTERFLAG_APPEAR = 2; // delay spawn until triggered - const float MONSTERFLAG_NORESPAWN = 4; - const float MONSTERFLAG_FLY_VERTICAL = 8; // fly/swim vertically - const float MONSTERFLAG_INFRONT = 32; // only check for enemies infront of us - const float MONSTERFLAG_MINIBOSS = 64; // monster spawns as mini-boss (also has a chance of naturally becoming one) - const float MONSTERFLAG_INVINCIBLE = 128; // monster doesn't take damage (may be used for map objects & temporary monsters) - const float MONSTERFLAG_SPAWNED = 16384; // flag for spawned monsters - const float MONSTERFLAG_RESPAWNED = 32768; // flag for re-spawned monsters ++const int MONSTERFLAG_APPEAR = 2; // delay spawn until triggered ++const int MONSTERFLAG_NORESPAWN = 4; ++const int MONSTERFLAG_FLY_VERTICAL = 8; // fly/swim vertically ++const int MONSTERFLAG_INFRONT = 32; // only check for enemies infront of us ++const int MONSTERFLAG_MINIBOSS = 64; // monster spawns as mini-boss (also has a chance of naturally becoming one) ++const int MONSTERFLAG_INVINCIBLE = 128; // monster doesn't take damage (may be used for map objects & temporary monsters) ++const int MONSTERFLAG_SPAWNED = 16384; // flag for spawned monsters ++const int MONSTERFLAG_RESPAWNED = 32768; // flag for re-spawned monsters +// compatibility with old maps (soon to be removed) - #define monster_lifetime lifetime - #define monster_skill skill + .float monster_lifetime; ++.int monster_skill; -.float spider_slowness; // special spider timer +// functions used elsewhere - void Monster_Remove(entity mon); // removes a monster ++void Monster_Remove(entity mon); + -void monster_remove(entity mon); // removes a monster +void monsters_setstatus(); - float Monster_Spawn(float mon_id); + -.float(float attack_type) monster_attackfunc; -const int MONSTER_ATTACK_MELEE = 1; -const int MONSTER_ATTACK_RANGED = 2; ++bool Monster_Spawn(int mon_id); + -.float monster_skill; -const float MONSTER_SKILL_EASY = 1; -const float MONSTER_SKILL_MEDIUM = 3; -const float MONSTER_SKILL_HARD = 5; -const float MONSTER_SKILL_INSANE = 7; -const float MONSTER_SKILL_NIGHTMARE = 10; - -.float fish_wasdrowning; // used to reset a drowning fish's angles if it reaches water again ++void monster_setupcolors(entity mon); + -.float candrop; ++void Monster_Touch(); + -.float attack_range; ++void Monster_Delay(float repeat_count, float repeat_defer, float defer_amnt, void() func); + -.float spawn_time; // stop monster from moving around right after spawning ++float Monster_Attack_Melee(entity targ, float damg, float anim, float er, float animtime, float deathtype, float dostop); + -.string oldtarget2; -.float lastshielded; ++bool Monster_Attack_Leap(int anm, void() touchfunc, vector vel, float animtime); + -.vector oldangles; ++void monster_makevectors(entity e); + -.float m_armor_blockpercent; ++void Monster_Sound(.string samplefield, float sound_delay, float delaytoo, float chan); // monster sounds -// copied from player sounds .float msound_delay; // temporary antilag system #define ALLMONSTERSOUNDS \ _MSOUND(death) \ @@@ -96,3 -65,37 +109,4 @@@ ALLMONSTERSOUND #undef _MSOUND float GetMonsterSoundSampleField_notFound; - -const int MONSTERSKILL_NOTEASY = 256; // monster will not spawn on skill <= 1 -const int MONSTERSKILL_NOTMEDIUM = 512; // monster will not spawn on skill 2 -const int MONSTERSKILL_NOTHARD = 1024; // monster will not spawn on skill >= 3 - -// new flags -const int MONSTERFLAG_APPEAR = 2; // delay spawn until triggered -const int MONSTERFLAG_NORESPAWN = 4; -const int MONSTERFLAG_FLY_VERTICAL = 8; // fly/swim vertically -const int MONSTERFLAG_INFRONT = 32; // only check for enemies infront of us -const int MONSTERFLAG_MINIBOSS = 64; // monster spawns as mini-boss (also has a chance of naturally becoming one) -const int MONSTERFLAG_INVINCIBLE = 128; // monster doesn't take damage (may be used for map objects & temporary monsters) -const int MONSTERFLAG_SPAWNED = 16384; // flag for spawned monsters -const int MONSTERFLAG_RESPAWNED = 32768; // flag for re-spawned monsters - -.int monster_movestate; // used to tell what the monster is currently doing -const int MONSTER_MOVE_OWNER = 1; // monster will move to owner if in range, or stand still -const int MONSTER_MOVE_WANDER = 2; // monster will ignore owner & wander around -const int MONSTER_MOVE_SPAWNLOC = 3; // monster will move to its spawn location when not attacking -const int MONSTER_MOVE_NOMOVE = 4; // monster simply stands still -const int MONSTER_MOVE_ENEMY = 5; // used only as a movestate - -const int MONSTER_STATE_ATTACK_LEAP = 1; -const int MONSTER_STATE_ATTACK_MELEE = 2; - -float monster_initialize(float mon_id); -float monster_leap (float anm, void() touchfunc, vector vel, float anim_finished); -void monster_makevectors(entity e); -float monster_melee(entity targ, float damg, float anim, float er, float anim_finished, float deathtype, float dostop); -void monster_move(float runspeed, float walkspeed, float stopspeed, float manim_run, float manim_walk, float manim_idle); -void monster_setupcolors(entity mon); -float Monster_SkillModifier(); -void MonsterTouch (); + #endif diff --cc qcsrc/common/nades.qc index 03b15525c,4225a19f0..f4e863ef8 --- a/qcsrc/common/nades.qc +++ b/qcsrc/common/nades.qc @@@ -1,8 -1,22 +1,22 @@@ - .float healer_lifetime; - .float healer_radius; + #if defined(CSQC) + #include "../dpdefs/csprogsdefs.qh" + #include "../client/defs.qh" + #include "nades.qh" + #include "buffs.qh" + #include "../client/movetypes.qh" - #include "../server/tturrets/include/turrets_early.qh" + #include "../client/main.qh" + #include "../csqcmodellib/cl_model.qh" + #elif defined(MENUQC) + #elif defined(SVQC) + #include "../dpdefs/progsdefs.qh" + #include "constants.qh" + #include "../server/constants.qh" ++ #include "../common/turrets/sv_turrets.qh" + #endif + #ifdef SVQC - float healer_send(entity to, float sf) + float healer_send(entity to, int sf) { WriteByte(MSG_ENTITY, ENT_CLIENT_HEALING_ORB); WriteByte(MSG_ENTITY, sf); diff --cc qcsrc/common/notifications.qc index f24d130d0,b48daec7b..b6cc0a6f8 --- a/qcsrc/common/notifications.qc +++ b/qcsrc/common/notifications.qc @@@ -1,3 -1,18 +1,19 @@@ + #if defined(CSQC) ++ #include "../client/announcer.qh" + #elif defined(MENUQC) + #elif defined(SVQC) + #include "../dpdefs/progsdefs.qh" + #include "../dpdefs/dpextensions.qh" + #include "constants.qh" + #include "teams.qh" + #include "counting.qh" + #include "../server/autocvars.qh" + #include "../server/constants.qh" + #include "../server/defs.qh" + #include "notifications.qh" + #include "../server/mutators/mutators_include.qh" + #endif + // ================================================ // Unified notification system, written by Samual // Last updated: August, 2013 diff --cc qcsrc/common/notifications.qh index 8710079fb,1cb1adf51..8a4865c3a --- a/qcsrc/common/notifications.qh +++ b/qcsrc/common/notifications.qh @@@ -1,3 -1,9 +1,10 @@@ + #ifndef NOTIFICATIONS_H + #define NOTIFICATIONS_H + + #include "constants.qh" + #include "teams.qh" ++#include "jeff.qh" + // ================================================ // Unified notification system, written by Samual // Last updated: March, 2013 @@@ -1152,13 -1017,11 +1159,13 @@@ float autocvar_notification_show_sprees item_centime: amount of time to display weapon message in centerprint item_buffname: return full name of a buff from buffid death_team: show the full name of the team a player is switching from + minigame1_name: return human readable name of a minigame from its id(s1) + minigame1_d: return descriptor name of a minigame from its id(s1) */ - #define NOTIF_MAX_ARGS 7 - #define NOTIF_MAX_HUDARGS 2 - #define NOTIF_MAX_DURCNT 2 + const float NOTIF_MAX_ARGS = 7; + const float NOTIF_MAX_HUDARGS = 2; + const float NOTIF_MAX_DURCNT = 2; string arg_slot[NOTIF_MAX_ARGS]; @@@ -1210,11 -1072,11 +1217,13 @@@ const float ARG_DC = 6; // unique resul ARG_CASE(ARG_CS_SV, "item_wepammo", (s1 != "" ? sprintf(_(" with %s"), s1) : "")) \ ARG_CASE(ARG_DC, "item_centime", ftos(autocvar_notification_item_centerprinttime)) \ ARG_CASE(ARG_SV, "death_team", Team_ColoredFullName(f1)) \ - ARG_CASE(ARG_CS, "death_team", Team_ColoredFullName(f1 - 1)) + ARG_CASE(ARG_CS, "death_team", Team_ColoredFullName(f1 - 1)) \ + ARG_CASE(ARG_CS_SV_HA, "minigame1_name",find(world,netname,s1).descriptor.message) \ + ARG_CASE(ARG_CS_SV_HA, "minigame1_d", find(world,netname,s1).descriptor.netname) - #define NOTIF_HIT_MAX(count,funcname) if(sel_num == count) { backtrace(sprintf("%s: Hit maximum arguments!\n", funcname)); break; } + #define NOTIF_HIT_MAX(count,funcname) do { \ + if(sel_num == count) { backtrace(sprintf("%s: Hit maximum arguments!\n", funcname)); break; } \ + } while(0) #define NOTIF_HIT_UNKNOWN(token,funcname) { backtrace(sprintf("%s: Hit unknown token in selected string! '%s'\n", funcname, selected)); break; } #define KILL_SPREE_LIST \ @@@ -1417,21 -1277,52 +1426,62 @@@ string notif_arg_spree_inf(float type, // Initialization/Create Declarations // ==================================== + enum { + NO_CPID + , CPID_ASSAULT_ROLE ++, CPID_CONQUEST + , CPID_ROUND + , CPID_CAMPCHECK + , CPID_CTF_CAPSHIELD + , CPID_CTF_LOWPRIO + , CPID_CTF_PASS ++, CPID_FREEZE + , CPID_STALEMATE + , CPID_NADES + , CPID_IDLING + , CPID_ITEM + , CPID_PREVENT_JOIN ++, CPID_JAILBREAK + , CPID_KEEPAWAY + , CPID_KEEPAWAY_WARN + , CPID_KEYHUNT -, CPID_KEYHUNT_OTHER ++, CPID_KEYHUNT_LOWPRIO ++, CPID_KEYHUNT_PASS + , CPID_LMS + , CPID_MISSING_TEAMS + , CPID_MISSING_PLAYERS + , CPID_INSTAGIB_FINDAMMO + , CPID_MOTD + , CPID_NIX + , CPID_ONSLAUGHT ++, CPID_ONS_CAPSHIELD ++, CPID_OVERKILL + , CPID_OVERTIME ++, CPID_PIGGYBACK + , CPID_POWERUP + , CPID_RACE_FINISHLAP + , CPID_TEAMCHANGE + , CPID_TIMEOUT ++, CPID_VEHICLES ++, CPID_VEHICLES_OTHER ++, CPID_VIP + // always last + , NOTIF_CPID_COUNT + }; // notification counts - #define NOTIF_FIRST 1 + const float NOTIF_FIRST = 1; float NOTIF_ANNCE_COUNT; float NOTIF_INFO_COUNT; float NOTIF_CENTER_COUNT; float NOTIF_MULTI_COUNT; float NOTIF_CHOICE_COUNT; - float NOTIF_CPID_COUNT; // notification limits -- INCREASE AS NECESSARY - #define NOTIF_ANNCE_MAX 200 - #define NOTIF_INFO_MAX 400 - #define NOTIF_CENTER_MAX 350 - #define NOTIF_MULTI_MAX 200 - #define NOTIF_CHOICE_MAX 50 -const float NOTIF_ANNCE_MAX = 100; -const float NOTIF_INFO_MAX = 300; -const float NOTIF_CENTER_MAX = 200; -const float NOTIF_MULTI_MAX = 200; -const float NOTIF_CHOICE_MAX = 20; ++const int NOTIF_ANNCE_MAX = 200; ++const int NOTIF_INFO_MAX = 400; ++const int NOTIF_CENTER_MAX = 350; ++const int NOTIF_MULTI_MAX = 200; ++const int NOTIF_CHOICE_MAX = 50; // notification entities entity msg_annce_notifs[NOTIF_ANNCE_MAX]; diff --cc qcsrc/common/playerstats.qc index 54afbd024,5890fb89e..2f9f1eb65 --- a/qcsrc/common/playerstats.qc +++ b/qcsrc/common/playerstats.qc @@@ -14,7 -29,7 +29,7 @@@ void PlayerStats_GameReport_AddPlayer(e if((e.crypto_idfp != "") && (e.cvar_cl_allow_uidtracking == 1)) { s = e.crypto_idfp; } else if(IS_BOT_CLIENT(e)) -- { s = sprintf("bot#%g#%s", skill, e.cleanname); } ++ { s = sprintf("bot#%g#%s", bot_skill, e.cleanname); } if((s == "") || find(world, playerstats_id, s)) // already have one of the ID - next one can't be tracked then! { diff --cc qcsrc/common/stats.qh index cd141c83b,481713cc9..2c6a1cd82 --- a/qcsrc/common/stats.qh +++ b/qcsrc/common/stats.qh @@@ -34,101 -37,111 +37,104 @@@ const int STAT_VIEWZOOM // 29 empty? // 30 empty? // 31 empty? - const float STAT_KH_KEYS = 32; - const float STAT_CTF_STATE = 33; + const int STAT_KH_KEYS = 32; + const int STAT_CTF_STATE = 33; // 34 empty? - const float STAT_WEAPONS = 35; - const float STAT_SWITCHWEAPON = 36; - const float STAT_GAMESTARTTIME = 37; - // 38 empty? - // 39 empty? + const int STAT_WEAPONS = 35; + const int STAT_SWITCHWEAPON = 36; + const int STAT_GAMESTARTTIME = 37; + const int STAT_STRENGTH_FINISHED = 38; + const int STAT_INVINCIBLE_FINISHED = 39; // 40 empty? - const float STAT_ARC_HEAT = 41; - const float STAT_PRESSED_KEYS = 42; - const float STAT_ALLOW_OLDVORTEXBEAM = 43; // this stat could later contain some other bits of info, like, more server-side particle config - const float STAT_FUEL = 44; - const float STAT_NB_METERSTART = 45; - const float STAT_SHOTORG = 46; // compressShotOrigin - const float STAT_LEADLIMIT = 47; - const float STAT_WEAPON_CLIPLOAD = 48; - const float STAT_WEAPON_CLIPSIZE = 49; - const float STAT_VORTEX_CHARGE = 50; - const float STAT_LAST_PICKUP = 51; - const float STAT_HUD = 52; - const float STAT_VORTEX_CHARGEPOOL = 53; - const float STAT_HIT_TIME = 54; - const float STAT_DAMAGE_DEALT_TOTAL = 55; - const float STAT_TYPEHIT_TIME = 56; - const float STAT_LAYED_MINES = 57; - const float STAT_HAGAR_LOAD = 58; - const float STAT_SWITCHINGWEAPON = 59; - const float STAT_SUPERWEAPONS_FINISHED = 60; - const float STAT_VEHICLESTAT_HEALTH = 61; - const float STAT_VEHICLESTAT_SHIELD = 62; - const float STAT_VEHICLESTAT_ENERGY = 63; - const float STAT_VEHICLESTAT_AMMO1 = 64; - const float STAT_VEHICLESTAT_RELOAD1 = 65; - const float STAT_VEHICLESTAT_AMMO2 = 66; - const float STAT_VEHICLESTAT_RELOAD2 = 67; - const float STAT_VEHICLESTAT_W2MODE = 68; - const float STAT_NADE_TIMER = 69; - const float STAT_SECRETS_TOTAL = 70; - const float STAT_SECRETS_FOUND = 71; - const float STAT_RESPAWN_TIME = 72; - const float STAT_ROUNDSTARTTIME = 73; - const float STAT_WEAPONS2 = 74; - const float STAT_WEAPONS3 = 75; - const float STAT_MONSTERS_TOTAL = 76; - const float STAT_MONSTERS_KILLED = 77; - const float STAT_OK_AMMO_CHARGE = 78; - const float STAT_OK_AMMO_CHARGEPOOL = 79; - const float STAT_NADE_BONUS = 80; - const float STAT_NADE_BONUS_TYPE = 81; - const float STAT_NADE_BONUS_SCORE = 82; - const float STAT_HEALING_ORB = 83; - const float STAT_HEALING_ORB_ALPHA = 84; - const float STAT_WEAPONSINMAP = 90; - const float STAT_WEAPONSINMAP2 = 91; - const float STAT_WEAPONSINMAP3 = 92; - const float STAT_SUPERCELLS = 93; - const float STAT_CAPTURE_PROGRESS = 94; - const float STAT_PRISONED = 95; - const float STAT_ROUNDLOST = 96; - const float STAT_CTF_FLAGSTATUS = 97; - const float STAT_KH_KEYSTATUS = 97; - const float STAT_BUFFS = 98; - const float STAT_DISCO_MODE = 99; + const int STAT_ARC_HEAT = 41; + const int STAT_PRESSED_KEYS = 42; + const int STAT_ALLOW_OLDVORTEXBEAM = 43; // this stat could later contain some other bits of info, like, more server-side particle config + const int STAT_FUEL = 44; + const int STAT_NB_METERSTART = 45; + const int STAT_SHOTORG = 46; // compressShotOrigin + const int STAT_LEADLIMIT = 47; + const int STAT_WEAPON_CLIPLOAD = 48; + const int STAT_WEAPON_CLIPSIZE = 49; + const int STAT_VORTEX_CHARGE = 50; + const int STAT_LAST_PICKUP = 51; + const int STAT_HUD = 52; + const int STAT_VORTEX_CHARGEPOOL = 53; + const int STAT_HIT_TIME = 54; + const int STAT_DAMAGE_DEALT_TOTAL = 55; + const int STAT_TYPEHIT_TIME = 56; + const int STAT_LAYED_MINES = 57; + const int STAT_HAGAR_LOAD = 58; + const int STAT_SWITCHINGWEAPON = 59; + const int STAT_SUPERWEAPONS_FINISHED = 60; + const int STAT_VEHICLESTAT_HEALTH = 61; + const int STAT_VEHICLESTAT_SHIELD = 62; + const int STAT_VEHICLESTAT_ENERGY = 63; + const int STAT_VEHICLESTAT_AMMO1 = 64; + const int STAT_VEHICLESTAT_RELOAD1 = 65; + const int STAT_VEHICLESTAT_AMMO2 = 66; + const int STAT_VEHICLESTAT_RELOAD2 = 67; + const int STAT_VEHICLESTAT_W2MODE = 68; + const int STAT_NADE_TIMER = 69; + const int STAT_SECRETS_TOTAL = 70; + const int STAT_SECRETS_FOUND = 71; + const int STAT_RESPAWN_TIME = 72; + const int STAT_ROUNDSTARTTIME = 73; + const int STAT_WEAPONS2 = 74; + const int STAT_WEAPONS3 = 75; + const int STAT_MONSTERS_TOTAL = 76; + const int STAT_MONSTERS_KILLED = 77; + const int STAT_BUFFS = 78; + const int STAT_NADE_BONUS = 79; + const int STAT_NADE_BONUS_TYPE = 80; + const int STAT_NADE_BONUS_SCORE = 81; + const int STAT_HEALING_ORB = 82; + const int STAT_HEALING_ORB_ALPHA = 83; + const int STAT_PLASMA = 84; + const int STAT_OK_AMMO_CHARGE = 85; + const int STAT_OK_AMMO_CHARGEPOOL = 86; -// 87 empty? -// 88 empty? -// 89 empty? -// 90 empty? -// 91 empty? -// 92 empty? -// 93 empty? -// 94 empty? -// 95 empty? ++const int STAT_WEAPONSINMAP = 87; ++const int STAT_WEAPONSINMAP2 = 88; ++const int STAT_WEAPONSINMAP3 = 89; ++const int STAT_SUPERCELLS = 90; ++const int STAT_CAPTURE_PROGRESS = 91; ++const int STAT_PRISONED = 92; ++const int STAT_ROUNDLOST = 93; ++const int STAT_CTF_FLAGSTATUS = 94; ++const int STAT_KH_KEYSTATUS = 95; + // 96 empty? -// 97 empty? -// 98 empty? -// 99 empty? ++const int STAT_DISCO_MODE = 97; ++const int STAT_FROZEN = 98; ++const int STAT_REVIVE_PROGRESS = 99; /* The following stats change depending on the gamemode, so can share the same ID */ // IDs 100 to 104 reserved for gamemodes // freeze tag, clan arena, jailbreak - const float STAT_REDALIVE = 100; - const float STAT_BLUEALIVE = 101; - const float STAT_YELLOWALIVE = 102; - const float STAT_PINKALIVE = 103; + const int STAT_REDALIVE = 100; + const int STAT_BLUEALIVE = 101; + const int STAT_YELLOWALIVE = 102; + const int STAT_PINKALIVE = 103; // domination - const float STAT_DOM_TOTAL_PPS = 100; - const float STAT_DOM_PPS_RED = 101; - const float STAT_DOM_PPS_BLUE = 102; - const float STAT_DOM_PPS_YELLOW = 103; - const float STAT_DOM_PPS_PINK = 104; + const int STAT_DOM_TOTAL_PPS = 100; + const int STAT_DOM_PPS_RED = 101; + const int STAT_DOM_PPS_BLUE = 102; + const int STAT_DOM_PPS_YELLOW = 103; + const int STAT_DOM_PPS_PINK = 104; // vip - const float STAT_VIP = 100; - const float STAT_VIP_RED = 101; - const float STAT_VIP_BLUE = 102; - const float STAT_VIP_YELLOW = 103; - const float STAT_VIP_PINK = 104; + const int STAT_VIP = 100; + const int STAT_VIP_RED = 101; + const int STAT_VIP_BLUE = 102; + const int STAT_VIP_YELLOW = 103; + const int STAT_VIP_PINK = 104; -// key hunt -const int STAT_KH_REDKEY_TEAM = 100; -const int STAT_KH_BLUEKEY_TEAM = 101; -const int STAT_KH_YELLOWKEY_TEAM = 102; -const int STAT_KH_PINKKEY_TEAM = 103; - /* Gamemode-specific stats end here */ -- - const float STAT_FROZEN = 105; - const float STAT_REVIVE_PROGRESS = 106; - const float STAT_PLASMA = 107; -const int STAT_FROZEN = 105; -const int STAT_REVIVE_PROGRESS = 106; ++// 105 empty? ++// 106 empty? + // 107 empty? // 108 empty? // 109 empty? // 110 empty? diff --cc qcsrc/common/turrets/all.qh index 04bb10f6a,000000000..6196ce8fc mode 100644,000000..100644 --- a/qcsrc/common/turrets/all.qh +++ b/qcsrc/common/turrets/all.qh @@@ -1,12 -1,0 +1,20 @@@ ++#ifdef SVQC ++#include "sv_turrets.qh" ++#include "util.qh" ++#include "../../server/autocvars.qh" ++#elif defined(CSQC) ++#include "../../server/movelib.qh" ++#endif ++ +#include "unit/ewheel.qc" +#include "unit/flac.qc" +#include "unit/fusionreactor.qc" +#include "unit/hellion.qc" +#include "unit/hk.qc" +#include "unit/machinegun.qc" +#include "unit/mlrs.qc" +#include "unit/phaser.qc" +#include "unit/plasma.qc" +#include "unit/plasma_dual.qc" +#include "unit/tesla.qc" +#include "unit/walker.qc" diff --cc qcsrc/common/turrets/cl_turrets.qc index 3365b9202,000000000..2f8e77d2d mode 100644,000000..100644 --- a/qcsrc/common/turrets/cl_turrets.qc +++ b/qcsrc/common/turrets/cl_turrets.qc @@@ -1,446 -1,0 +1,445 @@@ +void turret_remove() +{ + remove(self.tur_head); + //remove(self.enemy); + self.tur_head = world; +} + +.vector glowmod; +void turret_changeteam() +{ + self.glowmod = Team_ColorRGB(self.team - 1) * 2; + self.teamradar_color = Team_ColorRGB(self.team - 1); + + if(self.team) + self.colormap = 1024 + (self.team - 1) * 17; + + self.tur_head.colormap = self.colormap; + self.tur_head.glowmod = self.glowmod; + +} + +void turret_head_draw() +{ + self.drawmask = MASK_NORMAL; +} + +void turret_draw() +{ + float dt; + + dt = time - self.move_time; + self.move_time = time; + if(dt <= 0) + return; + + self.tur_head.angles += dt * self.tur_head.move_avelocity; + + if (self.health < 127) + { + dt = random(); + + if(dt < 0.03) + te_spark(self.origin + '0 0 40', randomvec() * 256 + '0 0 256', 16); + } + + if(self.health < 85) + if(dt < 0.01) + pointparticles(particleeffectnum("smoke_large"), (self.origin + (randomvec() * 80)), '0 0 0', 1); + + if(self.health < 32) + if(dt < 0.015) + pointparticles(particleeffectnum("smoke_small"), (self.origin + (randomvec() * 80)), '0 0 0', 1); + +} + +void turret_draw2d() +{ + if(self.netname == "") + return; + + if(!autocvar_g_waypointsprite_turrets) + return; + + if(autocvar_cl_hidewaypoints) + return; + + float dist = vlen(self.origin - view_origin); + float t = (GetPlayerColor(player_localnum) + 1); + + vector o; + string txt; + + if(autocvar_cl_vehicles_hud_tactical) + if(dist < 10240 && t != self.team) + { + // TODO: Vehicle tactical hud + o = project_3d_to_2d(self.origin + '0 0 32'); + if(o_z < 0 + || o_x < (vid_conwidth * waypointsprite_edgeoffset_left) + || o_y < (vid_conheight * waypointsprite_edgeoffset_top) + || o_x > (vid_conwidth - (vid_conwidth * waypointsprite_edgeoffset_right)) + || o_y > (vid_conheight - (vid_conheight * waypointsprite_edgeoffset_bottom))) + return; // Dont draw wp's for turrets out of view + o_z = 0; + if(hud != HUD_NORMAL) + { + if((get_turretinfo(self.turretid)).spawnflags & TUR_FLAG_MOVE) + txt = "gfx/vehicles/vth-mover.tga"; + else + txt = "gfx/vehicles/vth-stationary.tga"; + + vector pz = drawgetimagesize(txt) * 0.25; + drawpic(o - pz * 0.5, txt, pz , '1 1 1', 0.75, DRAWFLAG_NORMAL); + } + } + + if(dist > self.maxdistance) + return; + + string spriteimage = self.netname; + float a = self.alpha * autocvar_hud_panel_fg_alpha; + vector rgb = spritelookupcolor(spriteimage, self.teamradar_color); + + + if(self.maxdistance > waypointsprite_normdistance) + a *= pow(bound(0, (self.maxdistance - dist) / (self.maxdistance - waypointsprite_normdistance), 1), waypointsprite_distancealphaexponent); + else if(self.maxdistance > 0) + a *= pow(bound(0, (waypointsprite_fadedistance - dist) / (waypointsprite_fadedistance - waypointsprite_normdistance), 1), waypointsprite_distancealphaexponent) * (1 - waypointsprite_minalpha) + waypointsprite_minalpha; + + if(rgb == '0 0 0') + { + self.teamradar_color = '1 0 1'; + printf("WARNING: sprite of name %s has no color, using pink so you notice it\n", spriteimage); + } + + txt = self.netname; + if(autocvar_g_waypointsprite_spam && waypointsprite_count >= autocvar_g_waypointsprite_spam) + txt = _("Spam"); + else + txt = spritelookuptext(spriteimage); + + if(time - floor(time) > 0.5 && t == self.team) + { + if(self.helpme && time < self.helpme) + { + a *= SPRITE_HELPME_BLINK; + txt = sprintf(_("%s under attack!"), txt); + } + else + a *= spritelookupblinkvalue(spriteimage); + } + + if(autocvar_g_waypointsprite_uppercase) + txt = strtoupper(txt); + + if(a > 1) + { + rgb *= a; + a = 1; + } + + if(a <= 0) + return; + + rgb = fixrgbexcess(rgb); + + o = project_3d_to_2d(self.origin + '0 0 64'); + if(o_z < 0 + || o_x < (vid_conwidth * waypointsprite_edgeoffset_left) + || o_y < (vid_conheight * waypointsprite_edgeoffset_top) + || o_x > (vid_conwidth - (vid_conwidth * waypointsprite_edgeoffset_right)) + || o_y > (vid_conheight - (vid_conheight * waypointsprite_edgeoffset_bottom))) + return; // Dont draw wp's for turrets out of view + + o_z = 0; + + float edgedistance_min, crosshairdistance; + edgedistance_min = min((o_y - (vid_conheight * waypointsprite_edgeoffset_top)), + (o_x - (vid_conwidth * waypointsprite_edgeoffset_left)), + (vid_conwidth - (vid_conwidth * waypointsprite_edgeoffset_right)) - o_x, + (vid_conheight - (vid_conheight * waypointsprite_edgeoffset_bottom)) - o_y); + + float vidscale = max(vid_conwidth / vid_width, vid_conheight / vid_height); + + crosshairdistance = sqrt( pow(o_x - vid_conwidth/2, 2) + pow(o_y - vid_conheight/2, 2) ); + + t = waypointsprite_scale * vidscale; + a *= waypointsprite_alpha; + + { + a = a * (1 - (1 - waypointsprite_distancefadealpha) * (bound(0, dist/waypointsprite_distancefadedistance, 1))); + t = t * (1 - (1 - waypointsprite_distancefadescale) * (bound(0, dist/waypointsprite_distancefadedistance, 1))); + } + if (edgedistance_min < waypointsprite_edgefadedistance) { + a = a * (1 - (1 - waypointsprite_edgefadealpha) * (1 - bound(0, edgedistance_min/waypointsprite_edgefadedistance, 1))); + t = t * (1 - (1 - waypointsprite_edgefadescale) * (1 - bound(0, edgedistance_min/waypointsprite_edgefadedistance, 1))); + } + if(crosshairdistance < waypointsprite_crosshairfadedistance) { + a = a * (1 - (1 - waypointsprite_crosshairfadealpha) * (1 - bound(0, crosshairdistance/waypointsprite_crosshairfadedistance, 1))); + t = t * (1 - (1 - waypointsprite_crosshairfadescale) * (1 - bound(0, crosshairdistance/waypointsprite_crosshairfadedistance, 1))); + } + + o = drawspritearrow(o, M_PI, rgb, a, SPRITE_ARROW_SCALE * t); + o = drawspritetext(o, M_PI, (SPRITE_HEALTHBAR_WIDTH + 2 * SPRITE_HEALTHBAR_BORDER) * t, rgb, a, waypointsprite_fontsize * '1 1 0', txt); + drawhealthbar( + o, + 0, + self.health / 255, + '0 0 0', + '0 0 0', + 0.5 * SPRITE_HEALTHBAR_WIDTH * t, + 0.5 * SPRITE_HEALTHBAR_HEIGHT * t, + SPRITE_HEALTHBAR_MARGIN * t + 0.5 * waypointsprite_fontsize, + SPRITE_HEALTHBAR_BORDER * t, + 0, + rgb, + a * SPRITE_HEALTHBAR_BORDERALPHA, + rgb, + a * SPRITE_HEALTHBAR_HEALTHALPHA, + DRAWFLAG_NORMAL + ); +} + - void(entity e, entity tagentity, string tagname) setattachment = #443; +void turret_construct() +{ + entity tur = get_turretinfo(self.turretid); + + if(self.tur_head == world) + self.tur_head = spawn(); + + self.netname = TUR_NAME(self.turretid); + + setorigin(self, self.origin); + setmodel(self, tur.model); + setmodel(self.tur_head, tur.head_model); + setsize(self, tur.mins, tur.maxs); + setsize(self.tur_head, '0 0 0', '0 0 0'); + + if(self.turretid == TUR_EWHEEL) + setattachment(self.tur_head, self, ""); + else + setattachment(self.tur_head, self, "tag_head"); + + self.tur_head.classname = "turret_head"; + self.tur_head.owner = self; + self.tur_head.move_movetype = MOVETYPE_NOCLIP; + self.move_movetype = MOVETYPE_NOCLIP; + self.tur_head.angles = self.angles; + self.health = 255; + self.solid = SOLID_BBOX; + self.tur_head.solid = SOLID_NOT; + self.movetype = MOVETYPE_NOCLIP; + self.tur_head.movetype = MOVETYPE_NOCLIP; + self.draw = turret_draw; + self.entremove = turret_remove; + self.drawmask = MASK_NORMAL; + self.tur_head.drawmask = MASK_NORMAL; + self.anim_start_time = 0; + self.draw2d = turret_draw2d; + self.maxdistance = autocvar_g_waypointsprite_turrets_maxdist; + self.teamradar_color = '1 0 0'; + self.alpha = 1; + + TUR_ACTION(self.turretid, TR_SETUP); +} + +entity turret_gibtoss(string _model, vector _from, vector _to, vector _cmod, float _explode); +void turret_gibboom(); +void turret_gib_draw() +{ + Movetype_Physics_MatchTicrate(autocvar_cl_gibs_ticrate, autocvar_cl_gibs_sloppy); + + self.drawmask = MASK_NORMAL; + + if(self.cnt) + { + if(time >= self.nextthink) + { + turret_gibboom(); + remove(self); + } + } + else + { + self.alpha = bound(0, self.nextthink - time, 1); + if(self.alpha < ALPHA_MIN_VISIBLE) + remove(self); + } +} + +void turret_gibboom() +{ + float i; + + sound (self, CH_SHOTS, "weapons/rocket_impact.wav", VOL_BASE, ATTEN_NORM); + pointparticles(particleeffectnum("rocket_explode"), self.origin, '0 0 0', 1); + + for (i = 1; i < 5; i = i + 1) - turret_gibtoss(strcat("models/turrets/head-gib", ftos(i), ".md3"), self.origin + '0 0 2', self.velocity + randomvec() * 700, '0 0 0', FALSE); ++ turret_gibtoss(strcat("models/turrets/head-gib", ftos(i), ".md3"), self.origin + '0 0 2', self.velocity + randomvec() * 700, '0 0 0', false); +} + +entity turret_gibtoss(string _model, vector _from, vector _to, vector _cmod, float _explode) +{ + entity gib; + + traceline(_from, _to, MOVE_NOMONSTERS, world); + if(trace_startsolid) + return world; + + gib = spawn(); + setorigin(gib, _from); + setmodel(gib, _model); + gib.colormod = _cmod; + gib.solid = SOLID_CORPSE; + gib.draw = turret_gib_draw; + gib.cnt = _explode; + setsize(gib, '-1 -1 -1', '1 1 1'); + if(_explode) + { + gib.nextthink = time + 0.2 * (autocvar_cl_gibs_lifetime * (1 + prandom() * 0.15)); + gib.effects = EF_FLAME; + } + else + gib.nextthink = time + autocvar_cl_gibs_lifetime * (1 + prandom() * 0.15); + + gib.gravity = 1; + gib.move_movetype = MOVETYPE_BOUNCE; + gib.move_origin = _from; + setorigin(gib, _from); + gib.move_velocity = _to; + gib.move_avelocity = prandomvec() * 32; + gib.move_time = time; + gib.damageforcescale = 1; + gib.classname = "turret_gib"; + + return gib; +} + +void turret_die() +{ + sound (self, CH_SHOTS, "weapons/rocket_impact.wav", VOL_BASE, ATTEN_NORM); + pointparticles(particleeffectnum("rocket_explode"), self.origin, '0 0 0', 1); + if (!autocvar_cl_nogibs) + { + // Base + if(self.turretid == TUR_EWHEEL) - turret_gibtoss((get_turretinfo(self.turretid)).model, self.origin + '0 0 18', self.velocity + '0 0 400' + '0.1 0.1 1' * (random() * 400), '-1 -1 -1', TRUE); ++ turret_gibtoss((get_turretinfo(self.turretid)).model, self.origin + '0 0 18', self.velocity + '0 0 400' + '0.1 0.1 1' * (random() * 400), '-1 -1 -1', true); + else if (self.turretid == TUR_WALKER) - turret_gibtoss((get_turretinfo(self.turretid)).model, self.origin + '0 0 18', self.velocity + '0 0 300' + '0.1 0.1 1' * (random() * 200), '-1 -1 -1', TRUE); ++ turret_gibtoss((get_turretinfo(self.turretid)).model, self.origin + '0 0 18', self.velocity + '0 0 300' + '0.1 0.1 1' * (random() * 200), '-1 -1 -1', true); + else if (self.turretid == TUR_TESLA) - turret_gibtoss((get_turretinfo(self.turretid)).model, self.origin + '0 0 18', '0 0 200', '-1 -1 -1', FALSE); ++ turret_gibtoss((get_turretinfo(self.turretid)).model, self.origin + '0 0 18', '0 0 200', '-1 -1 -1', false); + else + { + if (random() > 0.5) + { - turret_gibtoss("models/turrets/base-gib2.md3", self.origin + '0 0 8', '0 0 50' + randomvec() * 150, '0 0 0', FALSE); - turret_gibtoss("models/turrets/base-gib3.md3", self.origin + '0 0 8', '0 0 50' + randomvec() * 150, '0 0 0', FALSE); - turret_gibtoss("models/turrets/base-gib4.md3", self.origin + '0 0 8', '0 0 50' + randomvec() * 150, '0 0 0', FALSE); ++ turret_gibtoss("models/turrets/base-gib2.md3", self.origin + '0 0 8', '0 0 50' + randomvec() * 150, '0 0 0', false); ++ turret_gibtoss("models/turrets/base-gib3.md3", self.origin + '0 0 8', '0 0 50' + randomvec() * 150, '0 0 0', false); ++ turret_gibtoss("models/turrets/base-gib4.md3", self.origin + '0 0 8', '0 0 50' + randomvec() * 150, '0 0 0', false); + } + else - turret_gibtoss("models/turrets/base-gib1.md3", self.origin + '0 0 8', '0 0 0', '0 0 0', TRUE); ++ turret_gibtoss("models/turrets/base-gib1.md3", self.origin + '0 0 8', '0 0 0', '0 0 0', true); + - entity headgib = turret_gibtoss((get_turretinfo(self.turretid)).head_model, self.origin + '0 0 32', '0 0 200' + randomvec() * 200, '-1 -1 -1', TRUE); ++ entity headgib = turret_gibtoss((get_turretinfo(self.turretid)).head_model, self.origin + '0 0 32', '0 0 200' + randomvec() * 200, '-1 -1 -1', true); + if(headgib) + { + headgib.angles = headgib.move_angles = self.tur_head.angles; + headgib.avelocity = headgib.move_avelocity = self.tur_head.move_avelocity + randomvec() * 45; + headgib.avelocity_y = headgib.move_avelocity_y = headgib.move_avelocity_y * 5; + headgib.gravity = 0.5; + } + } + } + + setmodel(self, "null"); + setmodel(self.tur_head, "null"); +} + +void ent_turret() +{ + float sf; + sf = ReadByte(); + + if(sf & TNSF_SETUP) + { + self.turretid = ReadByte(); + + self.origin_x = ReadCoord(); + self.origin_y = ReadCoord(); + self.origin_z = ReadCoord(); + setorigin(self, self.origin); + + self.angles_x = ReadAngle(); + self.angles_y = ReadAngle(); + + turret_construct(); + self.colormap = 1024; + self.glowmod = '0 1 1'; + self.tur_head.colormap = self.colormap; + self.tur_head.glowmod = self.glowmod; + } + + if(sf & TNSF_ANG) + { + if(self.tur_head == world) // aparenly this can happpen before TNSF_SETUP. great. + self.tur_head = spawn(); + + self.tur_head.move_angles_x = ReadShort(); + self.tur_head.move_angles_y = ReadShort(); + //self.tur_head.angles = self.angles + self.tur_head.move_angles; + self.tur_head.angles = self.tur_head.move_angles; + } + + if(sf & TNSF_AVEL) + { + if(self.tur_head == world) // aparenly this can happpen before TNSF_SETUP. great. + self.tur_head = spawn(); + + self.tur_head.move_avelocity_x = ReadShort(); + self.tur_head.move_avelocity_y = ReadShort(); + } + + if(sf & TNSF_MOVE) + { + self.origin_x = ReadShort(); + self.origin_y = ReadShort(); + self.origin_z = ReadShort(); + setorigin(self, self.origin); + + self.velocity_x = ReadShort(); + self.velocity_y = ReadShort(); + self.velocity_z = ReadShort(); + + self.move_angles_y = ReadShort(); + + self.move_time = time; + self.move_velocity = self.velocity; + self.move_origin = self.origin; + } + + if(sf & TNSF_ANIM) + { + self.frame1time = ReadCoord(); + self.frame = ReadByte(); + } + + if(sf & TNSF_STATUS) + { + float _tmp; + _tmp = ReadByte(); + if(_tmp != self.team) + { + self.team = _tmp; + turret_changeteam(); + } + + _tmp = ReadByte(); + if(_tmp == 0 && self.health != 0) + turret_die(); + else if(self.health && self.health != _tmp) + self.helpme = servertime + 10; + + self.health = _tmp; + } + //self.enemy.health = self.health / 255; +} diff --cc qcsrc/common/turrets/cl_turrets.qh index b61678218,000000000..0f8ff9485 mode 100644,000000..100644 --- a/qcsrc/common/turrets/cl_turrets.qh +++ b/qcsrc/common/turrets/cl_turrets.qh @@@ -1,1 -1,0 +1,6 @@@ ++#ifndef CL_TURRETS_H ++#define CL_TURRETS_H ++ +void ent_turret(); ++ ++#endif diff --cc qcsrc/common/turrets/sv_turrets.qc index c5bb1fbf1,000000000..c2ebbe211 mode 100644,000000..100644 --- a/qcsrc/common/turrets/sv_turrets.qc +++ b/qcsrc/common/turrets/sv_turrets.qc @@@ -1,1394 -1,0 +1,1392 @@@ - // ========================= - // SVQC Turret Properties - // ========================= - ++#ifdef SVQC ++#include "../../server/autocvars.qh" + +// Generic aiming +vector turret_aim_generic() +{ + + vector pre_pos, prep; + float distance, impact_time = 0, i, mintime; + + turret_tag_fire_update(); + + if(self.aim_flags & TFL_AIM_SIMPLE) + return real_origin(self.enemy); + + mintime = max(self.attack_finished_single - time,0) + sys_frametime; + + // Baseline + pre_pos = real_origin(self.enemy); + + // Lead? + if (self.aim_flags & TFL_AIM_LEAD) + { + if (self.aim_flags & TFL_AIM_SHOTTIMECOMPENSATE) // Need to conpensate for shot traveltime + { + prep = pre_pos; + + distance = vlen(prep - self.tur_shotorg); + impact_time = distance / self.shot_speed; + + prep = pre_pos + (self.enemy.velocity * (impact_time + mintime)); + + if(self.aim_flags & TFL_AIM_ZPREDICT) + if(!(self.enemy.flags & FL_ONGROUND)) + if(self.enemy.movetype == MOVETYPE_WALK || self.enemy.movetype == MOVETYPE_TOSS || self.enemy.movetype == MOVETYPE_BOUNCE) + { + float vz; + prep_z = pre_pos_z; + vz = self.enemy.velocity_z; + for(i = 0; i < impact_time; i += sys_frametime) + { + vz = vz - (autocvar_sv_gravity * sys_frametime); + prep_z = prep_z + vz * sys_frametime; + } + } + pre_pos = prep; + } + else + pre_pos = pre_pos + self.enemy.velocity * mintime; + } + + if(self.aim_flags & TFL_AIM_SPLASH) + { + //tracebox(pre_pos + '0 0 32',self.enemy.mins,self.enemy.maxs,pre_pos -'0 0 64',MOVE_WORLDONLY,self.enemy); + traceline(pre_pos + '0 0 32',pre_pos -'0 0 64',MOVE_WORLDONLY,self.enemy); + if(trace_fraction != 1.0) + pre_pos = trace_endpos; + } + + return pre_pos; +} + +float turret_targetscore_support(entity _turret,entity _target) +{ + float score; // Total score + float s_score = 0, d_score; + + if (_turret.enemy == _target) s_score = 1; + + d_score = min(_turret.target_range_optimal,tvt_dist) / max(_turret.target_range_optimal,tvt_dist); + + score = (d_score * _turret.target_select_rangebias) + + (s_score * _turret.target_select_samebias); + + return score; +} + +/* +* Generic bias aware score system. +*/ +float turret_targetscore_generic(entity _turret, entity _target) +{ + float d_dist; // Defendmode Distance + float score; // Total score + float d_score; // Distance score + float a_score; // Angular score + float m_score = 0; // missile score + float p_score = 0; // player score + float ikr; // ideal kill range + + if (_turret.tur_defend) + { + d_dist = vlen(real_origin(_target) - _turret.tur_defend.origin); + ikr = vlen(_turret.origin - _turret.tur_defend.origin); + d_score = 1 - d_dist / _turret.target_range; + } + else + { + // Make a normlized value base on the targets distance from our optimal killzone + ikr = _turret.target_range_optimal; + d_score = min(ikr, tvt_dist) / max(ikr, tvt_dist); + } + + a_score = 1 - tvt_thadf / _turret.aim_maxrotate; + + if ((_turret.target_select_missilebias > 0) && (_target.flags & FL_PROJECTILE)) + m_score = 1; + + if ((_turret.target_select_playerbias > 0) && IS_CLIENT(_target)) + p_score = 1; + + d_score = max(d_score, 0); + a_score = max(a_score, 0); + m_score = max(m_score, 0); + p_score = max(p_score, 0); + + score = (d_score * _turret.target_select_rangebias) + + (a_score * _turret.target_select_anglebias) + + (m_score * _turret.target_select_missilebias) + + (p_score * _turret.target_select_playerbias); + + if(_turret.target_range < vlen(_turret.tur_shotorg - real_origin(_target))) + { + //dprint("Wtf?\n"); + score *= 0.001; + } + +#ifdef TURRET_DEBUG + string sd,sa,sm,sp,ss; + string sdt,sat,smt,spt; + + sd = ftos(d_score); + d_score *= _turret.target_select_rangebias; + sdt = ftos(d_score); + + //sv = ftos(v_score); + //v_score *= _turret.target_select_samebias; + //svt = ftos(v_score); + + sa = ftos(a_score); + a_score *= _turret.target_select_anglebias; + sat = ftos(a_score); + + sm = ftos(m_score); + m_score *= _turret.target_select_missilebias; + smt = ftos(m_score); + + sp = ftos(p_score); + p_score *= _turret.target_select_playerbias; + spt = ftos(p_score); + + + ss = ftos(score); + bprint("^3Target scores^7 \[ ",_turret.netname, " \] ^3for^7 \[ ", _target.netname," \]\n"); + bprint("^5Range:\[ ",sd, " \]^2+bias:\[ ",sdt," \]\n"); + bprint("^5Angle:\[ ",sa, " \]^2+bias:\[ ",sat," \]\n"); + bprint("^5Missile:\[ ",sm," \]^2+bias:\[ ",smt," \]\n"); + bprint("^5Player:\[ ",sp, " \]^2+bias:\[ ",spt," \]\n"); + bprint("^3Total (w/bias):\[^1",ss,"\]\n"); + +#endif + + return score; +} + +// Generic damage handling - void() turret_respawn; +void turret_hide() +{ + self.effects |= EF_NODRAW; + self.nextthink = time + self.respawntime - 0.2; + self.think = turret_respawn; +} + +void turret_die() +{ + self.deadflag = DEAD_DEAD; + self.tur_head.deadflag = self.deadflag; + +// Unsolidify and hide real parts + self.solid = SOLID_NOT; + self.tur_head.solid = self.solid; + + self.event_damage = func_null; + self.takedamage = DAMAGE_NO; + + self.health = 0; + +// Go boom + //RadiusDamage (self,self, min(self.ammo,50),min(self.ammo,50) * 0.25,250,world,min(self.ammo,50)*5,DEATH_TURRET,world); + + if(self.damage_flags & TFL_DMG_DEATH_NORESPAWN) + { + TUR_ACTION(self.turretid, TR_DEATH); + + remove(self.tur_head); + remove(self); + } + else + { + // Setup respawn + self.SendFlags |= TNSF_STATUS; + self.nextthink = time + 0.2; + self.think = turret_hide; + + TUR_ACTION(self.turretid, TR_DEATH); + } +} + +void turret_damage (entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector vforce) +{ + // Enougth allready! + if(self.deadflag == DEAD_DEAD) + return; + + // Inactive turrets take no damage. (hm..) + if(!self.active) + return; + + if(SAME_TEAM(self, attacker)) + { + if(autocvar_g_friendlyfire) + damage = damage * autocvar_g_friendlyfire; + else + return; + } + + self.health -= damage; + + // thorw head slightly off aim when hit? + if (self.damage_flags & TFL_DMG_HEADSHAKE) + { + self.tur_head.angles_x = self.tur_head.angles_x + (-0.5 + random()) * damage; + self.tur_head.angles_y = self.tur_head.angles_y + (-0.5 + random()) * damage; + + self.SendFlags |= TNSF_ANG; + } + + if (self.turret_flags & TUR_FLAG_MOVE) + self.velocity = self.velocity + vforce; + + if (self.health <= 0) + { + self.event_damage = func_null; + self.tur_head.event_damage = func_null; + self.takedamage = DAMAGE_NO; + self.nextthink = time; + self.think = turret_die; + } + + self.SendFlags |= TNSF_STATUS; +} + +void() turret_think; +void turret_respawn() +{ + // Make sure all parts belong to the same team since + // this function doubles as "teamchange" function. + self.tur_head.team = self.team; + self.effects &= ~EF_NODRAW; + self.deadflag = DEAD_NO; + self.effects = EF_LOWPRECISION; + self.solid = SOLID_BBOX; + self.takedamage = DAMAGE_AIM; + self.event_damage = turret_damage; + self.avelocity = '0 0 0'; + self.tur_head.avelocity = self.avelocity; + self.tur_head.angles = self.idle_aim; + self.health = self.max_health; + self.enemy = world; + self.volly_counter = self.shot_volly; + self.ammo = self.ammo_max; + + self.nextthink = time + self.ticrate; + self.think = turret_think; + + self.SendFlags = TNSF_FULL_UPDATE; + + TUR_ACTION(self.turretid, TR_SETUP); +} + + +// Main functions +#define cvar_base "g_turrets_unit_" +.float clientframe; +void turrets_setframe(float _frame, float client_only) +{ + if((client_only ? self.clientframe : self.frame ) != _frame) + { + self.SendFlags |= TNSF_ANIM; + self.anim_start_time = time; + } + + if(client_only) + self.clientframe = _frame; + else + self.frame = _frame; + +} + +float turret_send(entity to, float sf) +{ + + WriteByte(MSG_ENTITY, ENT_CLIENT_TURRET); + WriteByte(MSG_ENTITY, sf); + if(sf & TNSF_SETUP) + { + WriteByte(MSG_ENTITY, self.turretid); + + WriteCoord(MSG_ENTITY, self.origin_x); + WriteCoord(MSG_ENTITY, self.origin_y); + WriteCoord(MSG_ENTITY, self.origin_z); + + WriteAngle(MSG_ENTITY, self.angles_x); + WriteAngle(MSG_ENTITY, self.angles_y); + } + + if(sf & TNSF_ANG) + { + WriteShort(MSG_ENTITY, rint(self.tur_head.angles_x)); + WriteShort(MSG_ENTITY, rint(self.tur_head.angles_y)); + } + + if(sf & TNSF_AVEL) + { + WriteShort(MSG_ENTITY, rint(self.tur_head.avelocity_x)); + WriteShort(MSG_ENTITY, rint(self.tur_head.avelocity_y)); + } + + if(sf & TNSF_MOVE) + { + WriteShort(MSG_ENTITY, rint(self.origin_x)); + WriteShort(MSG_ENTITY, rint(self.origin_y)); + WriteShort(MSG_ENTITY, rint(self.origin_z)); + + WriteShort(MSG_ENTITY, rint(self.velocity_x)); + WriteShort(MSG_ENTITY, rint(self.velocity_y)); + WriteShort(MSG_ENTITY, rint(self.velocity_z)); + + WriteShort(MSG_ENTITY, rint(self.angles_y)); + } + + if(sf & TNSF_ANIM) + { + WriteCoord(MSG_ENTITY, self.anim_start_time); + WriteByte(MSG_ENTITY, self.frame); + } + + if(sf & TNSF_STATUS) + { + WriteByte(MSG_ENTITY, self.team); + + if(self.health <= 0) + WriteByte(MSG_ENTITY, 0); + else + WriteByte(MSG_ENTITY, ceil((self.health / self.max_health) * 255)); + } + - return TRUE; ++ return true; +} + +void load_unit_settings(entity ent, string unitname, float is_reload) +{ + string sbase; + + if (ent == world) + return; + + if(!ent.turret_scale_damage) ent.turret_scale_damage = 1; + if(!ent.turret_scale_range) ent.turret_scale_range = 1; + if(!ent.turret_scale_refire) ent.turret_scale_refire = 1; + if(!ent.turret_scale_ammo) ent.turret_scale_ammo = 1; + if(!ent.turret_scale_aim) ent.turret_scale_aim = 1; + if(!ent.turret_scale_health) ent.turret_scale_health = 1; + if(!ent.turret_scale_respawn) ent.turret_scale_respawn = 1; + + sbase = strcat(cvar_base,unitname); + if (is_reload) + { + ent.enemy = world; + ent.tur_head.avelocity = '0 0 0'; + + ent.tur_head.angles = '0 0 0'; + } + + ent.health = cvar(strcat(sbase,"_health")) * ent.turret_scale_health; + ent.respawntime = cvar(strcat(sbase,"_respawntime")) * ent.turret_scale_respawn; + + ent.shot_dmg = cvar(strcat(sbase,"_shot_dmg")) * ent.turret_scale_damage; + ent.shot_refire = cvar(strcat(sbase,"_shot_refire")) * ent.turret_scale_refire; + ent.shot_radius = cvar(strcat(sbase,"_shot_radius")) * ent.turret_scale_damage; + ent.shot_speed = cvar(strcat(sbase,"_shot_speed")); + ent.shot_spread = cvar(strcat(sbase,"_shot_spread")); + ent.shot_force = cvar(strcat(sbase,"_shot_force")) * ent.turret_scale_damage; + ent.shot_volly = cvar(strcat(sbase,"_shot_volly")); + ent.shot_volly_refire = cvar(strcat(sbase,"_shot_volly_refire")) * ent.turret_scale_refire; + + ent.target_range = cvar(strcat(sbase,"_target_range")) * ent.turret_scale_range; + ent.target_range_min = cvar(strcat(sbase,"_target_range_min")) * ent.turret_scale_range; + ent.target_range_optimal = cvar(strcat(sbase,"_target_range_optimal")) * ent.turret_scale_range; + //ent.target_range_fire = cvar(strcat(sbase,"_target_range_fire")) * ent.turret_scale_range; + + ent.target_select_rangebias = cvar(strcat(sbase,"_target_select_rangebias")); + ent.target_select_samebias = cvar(strcat(sbase,"_target_select_samebias")); + ent.target_select_anglebias = cvar(strcat(sbase,"_target_select_anglebias")); + ent.target_select_playerbias = cvar(strcat(sbase,"_target_select_playerbias")); + //ent.target_select_fov = cvar(cvar_gets(sbase,"_target_select_fov")); + + ent.ammo_max = cvar(strcat(sbase,"_ammo_max")) * ent.turret_scale_ammo; + ent.ammo_recharge = cvar(strcat(sbase,"_ammo_recharge")) * ent.turret_scale_ammo; + + ent.aim_firetolerance_dist = cvar(strcat(sbase,"_aim_firetolerance_dist")); + ent.aim_speed = cvar(strcat(sbase,"_aim_speed")) * ent.turret_scale_aim; + ent.aim_maxrotate = cvar(strcat(sbase,"_aim_maxrot")); + ent.aim_maxpitch = cvar(strcat(sbase,"_aim_maxpitch")); + + ent.track_type = cvar(strcat(sbase,"_track_type")); + ent.track_accel_pitch = cvar(strcat(sbase,"_track_accel_pitch")); + ent.track_accel_rotate = cvar(strcat(sbase,"_track_accel_rot")); + ent.track_blendrate = cvar(strcat(sbase,"_track_blendrate")); + + if(is_reload) + TUR_ACTION(self.turretid, TR_SETUP); +} + +void turret_projectile_explode() +{ + + self.takedamage = DAMAGE_NO; + self.event_damage = func_null; +#ifdef TURRET_DEBUG + float d; + d = RadiusDamage (self, self.owner, self.owner.shot_dmg, 0, self.owner.shot_radius, self, world, self.owner.shot_force, self.totalfrags, world); + self.owner.tur_debug_dmg_t_h = self.owner.tur_debug_dmg_t_h + d; + self.owner.tur_debug_dmg_t_f = self.owner.tur_debug_dmg_t_f + self.owner.shot_dmg; +#else + RadiusDamage (self, self.realowner, self.owner.shot_dmg, 0, self.owner.shot_radius, self, world, self.owner.shot_force, self.totalfrags, world); +#endif + remove(self); +} + +void turret_projectile_touch() +{ + PROJECTILE_TOUCH; + turret_projectile_explode(); +} + +void turret_projectile_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector vforce) +{ + self.velocity += vforce; + self.health -= damage; + //self.realowner = attacker; // Dont change realowner, it does not make much sense for turrets + if(self.health <= 0) + W_PrepareExplosionByDamage(self.owner, turret_projectile_explode); +} + +entity turret_projectile(string _snd, float _size, float _health, float _death, float _proj_type, float _cull, float _cli_anim) +{ + entity proj; + + sound (self, CH_WEAPON_A, _snd, VOL_BASE, ATTEN_NORM); + proj = spawn (); + setorigin(proj, self.tur_shotorg); + setsize(proj, '-0.5 -0.5 -0.5' * _size, '0.5 0.5 0.5' * _size); + proj.owner = self; + proj.realowner = self; - proj.bot_dodge = TRUE; ++ proj.bot_dodge = true; + proj.bot_dodgerating = self.shot_dmg; + proj.think = turret_projectile_explode; + proj.touch = turret_projectile_touch; + proj.nextthink = time + 9; + proj.movetype = MOVETYPE_FLYMISSILE; + proj.velocity = normalize(self.tur_shotdir_updated + randomvec() * self.shot_spread) * self.shot_speed; + proj.flags = FL_PROJECTILE; + proj.enemy = self.enemy; + proj.totalfrags = _death; + PROJECTILE_MAKETRIGGER(proj); + if(_health) + { + proj.health = _health; + proj.takedamage = DAMAGE_YES; + proj.event_damage = turret_projectile_damage; + } + else + proj.flags |= FL_NOTARGET; + + CSQCProjectile(proj, _cli_anim, _proj_type, _cull); + + return proj; +} + +/** +** updates enemy distances, predicted impact point/time +** and updated aim<->predict impact distance. +**/ +void turret_do_updates(entity t_turret) +{ + vector enemy_pos; + entity oldself; + + oldself = self; + self = t_turret; + + enemy_pos = real_origin(self.enemy); + + turret_tag_fire_update(); + + self.tur_shotdir_updated = v_forward; + self.tur_dist_enemy = vlen(self.tur_shotorg - enemy_pos); + self.tur_dist_aimpos = vlen(self.tur_shotorg - self.tur_aimpos); + + /*if((self.firecheck_flags & TFL_FIRECHECK_VERIFIED) && (self.enemy)) + { + oldpos = self.enemy.origin; + setorigin(self.enemy, self.tur_aimpos); + tracebox(self.tur_shotorg, '-1 -1 -1', '1 1 1', self.tur_shotorg + (self.tur_shotdir_updated * self.tur_dist_aimpos), MOVE_NORMAL,self); + setorigin(self.enemy, oldpos); + + if(trace_ent == self.enemy) + self.tur_dist_impact_to_aimpos = 0; + else + self.tur_dist_impact_to_aimpos = vlen(trace_endpos - self.tur_aimpos); + } + else*/ + tracebox(self.tur_shotorg, '-1 -1 -1','1 1 1', self.tur_shotorg + (self.tur_shotdir_updated * self.tur_dist_aimpos), MOVE_NORMAL,self); + + self.tur_dist_impact_to_aimpos = vlen(trace_endpos - self.tur_aimpos) - (vlen(self.enemy.maxs - self.enemy.mins) * 0.5); + self.tur_impactent = trace_ent; + self.tur_impacttime = vlen(self.tur_shotorg - trace_endpos) / self.shot_speed; + + self = oldself; +} + +/** +** Handles head rotation according to +** the units .track_type and .track_flags +**/ +.float turret_framecounter; +void turret_track() +{ + vector target_angle; // This is where we want to aim + vector move_angle; // This is where we can aim + float f_tmp; + vector v1, v2; + v1 = self.tur_head.angles; + v2 = self.tur_head.avelocity; + + if (self.track_flags == TFL_TRACK_NO) + return; + + if(!self.active) + target_angle = self.idle_aim - ('1 0 0' * self.aim_maxpitch); + else if (self.enemy == world) + { + if(time > self.lip) + target_angle = self.idle_aim + self.angles; + else + target_angle = vectoangles(normalize(self.tur_aimpos - self.tur_shotorg)); + } + else + { + target_angle = vectoangles(normalize(self.tur_aimpos - self.tur_shotorg)); + } + + self.tur_head.angles_x = anglemods(self.tur_head.angles_x); + self.tur_head.angles_y = anglemods(self.tur_head.angles_y); + + // Find the diffrence between where we currently aim and where we want to aim + //move_angle = target_angle - (self.angles + self.tur_head.angles); + //move_angle = shortangle_vxy(move_angle,(self.angles + self.tur_head.angles)); + + move_angle = AnglesTransform_ToAngles(AnglesTransform_LeftDivide(AnglesTransform_FromAngles(self.angles), AnglesTransform_FromAngles(target_angle))) - self.tur_head.angles; + move_angle = shortangle_vxy(move_angle, self.tur_head.angles); + + switch(self.track_type) + { + case TFL_TRACKTYPE_STEPMOTOR: + f_tmp = self.aim_speed * self.ticrate; // dgr/sec -> dgr/tic + if (self.track_flags & TFL_TRACK_PITCH) + { + self.tur_head.angles_x += bound(-f_tmp,move_angle_x, f_tmp); + if(self.tur_head.angles_x > self.aim_maxpitch) + self.tur_head.angles_x = self.aim_maxpitch; + + if(self.tur_head.angles_x < -self.aim_maxpitch) + self.tur_head.angles_x = self.aim_maxpitch; + } + + if (self.track_flags & TFL_TRACK_ROTATE) + { + self.tur_head.angles_y += bound(-f_tmp, move_angle_y, f_tmp); + if(self.tur_head.angles_y > self.aim_maxrotate) + self.tur_head.angles_y = self.aim_maxrotate; + + if(self.tur_head.angles_y < -self.aim_maxrotate) + self.tur_head.angles_y = self.aim_maxrotate; + } + + // CSQC + self.SendFlags |= TNSF_ANG; + + return; + + case TFL_TRACKTYPE_FLUIDINERTIA: + f_tmp = self.aim_speed * self.ticrate; // dgr/sec -> dgr/tic + move_angle_x = bound(-self.aim_speed, move_angle_x * self.track_accel_pitch * f_tmp, self.aim_speed); + move_angle_y = bound(-self.aim_speed, move_angle_y * self.track_accel_rotate * f_tmp, self.aim_speed); + move_angle = (self.tur_head.avelocity * self.track_blendrate) + (move_angle * (1 - self.track_blendrate)); + break; + + case TFL_TRACKTYPE_FLUIDPRECISE: + + move_angle_y = bound(-self.aim_speed, move_angle_y, self.aim_speed); + move_angle_x = bound(-self.aim_speed, move_angle_x, self.aim_speed); + + break; + } + + // pitch + if (self.track_flags & TFL_TRACK_PITCH) + { + self.tur_head.avelocity_x = move_angle_x; + if((self.tur_head.angles_x + self.tur_head.avelocity_x * self.ticrate) > self.aim_maxpitch) + { + self.tur_head.avelocity_x = 0; + self.tur_head.angles_x = self.aim_maxpitch; + + self.SendFlags |= TNSF_ANG; + } + + if((self.tur_head.angles_x + self.tur_head.avelocity_x * self.ticrate) < -self.aim_maxpitch) + { + self.tur_head.avelocity_x = 0; + self.tur_head.angles_x = -self.aim_maxpitch; + + self.SendFlags |= TNSF_ANG; + } + } + + // rot + if (self.track_flags & TFL_TRACK_ROTATE) + { + self.tur_head.avelocity_y = move_angle_y; + + if((self.tur_head.angles_y + self.tur_head.avelocity_y * self.ticrate) > self.aim_maxrotate) + { + self.tur_head.avelocity_y = 0; + self.tur_head.angles_y = self.aim_maxrotate; + + self.SendFlags |= TNSF_ANG; + } + + if((self.tur_head.angles_y + self.tur_head.avelocity_y * self.ticrate) < -self.aim_maxrotate) + { + self.tur_head.avelocity_y = 0; + self.tur_head.angles_y = -self.aim_maxrotate; + + self.SendFlags |= TNSF_ANG; + } + } + + self.SendFlags |= TNSF_AVEL; + + // Force a angle update every 10'th frame + self.turret_framecounter += 1; + if(self.turret_framecounter >= 10) + { + self.SendFlags |= TNSF_ANG; + self.turret_framecounter = 0; + } +} + +/* + + TFL_TARGETSELECT_NO + + TFL_TARGETSELECT_LOS + + TFL_TARGETSELECT_PLAYERS + + TFL_TARGETSELECT_MISSILES + - TFL_TARGETSELECT_TRIGGERTARGET + + TFL_TARGETSELECT_ANGLELIMITS + + TFL_TARGETSELECT_RANGELIMITS + + TFL_TARGETSELECT_TEAMCHECK + - TFL_TARGETSELECT_NOBUILTIN + + TFL_TARGETSELECT_OWNTEAM +*/ + +/** +** Evaluate a entity for target valitity based on validate_flags +** NOTE: the caller must check takedamage before calling this, to inline this check. +**/ +float turret_validate_target(entity e_turret, entity e_target, float validate_flags) +{ + vector v_tmp; + + //if(!validate_flags & TFL_TARGETSELECT_NOBUILTIN) + // return -0.5; + + if(!e_target) + return -2; + + if(e_target.owner == e_turret) + return -0.5; + + if(!checkpvs(e_target.origin, e_turret)) + return -1; + + if(e_target.alpha <= 0.3) + return -1; + + if(g_onslaught) + if (substring(e_target.classname, 0, 10) == "onslaught_") // don't attack onslaught targets, that's the player's job! + return - 3; + + if (validate_flags & TFL_TARGETSELECT_NO) + return -4; + + // If only this was used more.. + if (e_target.flags & FL_NOTARGET) + return -5; + + // Cant touch this + if(IS_VEHICLE(e_target)) + { + if (e_target.vehicle_health <= 0) + return -6; + } + else if(Player_Trapped(e_target)) + return -6; + + // player + if (IS_CLIENT(e_target)) + { + if(!(validate_flags & TFL_TARGETSELECT_PLAYERS)) + return -7; + + if (e_target.deadflag != DEAD_NO) + return -8; + } + + // enemy turrets + if(validate_flags & TFL_TARGETSELECT_NOTURRETS) + if(e_target.owner.tur_head == e_target) + if(e_target.team != e_turret.team) // Dont break support units. + return -9; + + // Missile + if (e_target.flags & FL_PROJECTILE) + if(!(validate_flags & TFL_TARGETSELECT_MISSILES)) + return -10; + + if (validate_flags & TFL_TARGETSELECT_MISSILESONLY) + if(!(e_target.flags & FL_PROJECTILE)) + return -10.5; + + // Team check + if (validate_flags & TFL_TARGETSELECT_TEAMCHECK) + { + if (validate_flags & TFL_TARGETSELECT_OWNTEAM) + { + if (e_target.team != e_turret.team) + return -11; + + if (e_turret.team != e_target.owner.team) + return -12; + } + else + { + if (e_target.team == e_turret.team) + return -13; + + if (e_turret.team == e_target.owner.team) + return -14; + } + } + + // Range limits? + tvt_dist = vlen(e_turret.origin - real_origin(e_target)); + if (validate_flags & TFL_TARGETSELECT_RANGELIMITS) + { + if (tvt_dist < e_turret.target_range_min) + return -15; + + if (tvt_dist > e_turret.target_range) + return -16; + } + + // Can we even aim this thing? + tvt_thadv = angleofs3(e_turret.tur_head.origin, e_turret.angles + e_turret.tur_head.angles, e_target); + tvt_tadv = shortangle_vxy(angleofs(e_turret, e_target), e_turret.angles); + tvt_thadf = vlen(tvt_thadv); + tvt_tadf = vlen(tvt_tadv); + + /* + if(validate_flags & TFL_TARGETSELECT_FOV) + { + if(e_turret.target_select_fov < tvt_thadf) + return -21; + } + */ + + if (validate_flags & TFL_TARGETSELECT_ANGLELIMITS) + { + if (fabs(tvt_tadv_x) > e_turret.aim_maxpitch) + return -17; + + if (fabs(tvt_tadv_y) > e_turret.aim_maxrotate) + return -18; + } + + // Line of sight? + if (validate_flags & TFL_TARGETSELECT_LOS) + { + v_tmp = real_origin(e_target) + ((e_target.mins + e_target.maxs) * 0.5); + + traceline(e_turret.origin + '0 0 16', v_tmp, 0, e_turret); + + if (e_turret.aim_firetolerance_dist < vlen(v_tmp - trace_endpos)) + return -19; + } + + if (e_target.classname == "grapplinghook") + return -20; + + /* + if (e_target.classname == "func_button") + return -21; + */ + +#ifdef TURRET_DEBUG_TARGETSELECT + dprint("Target:",e_target.netname," is a valid target for ",e_turret.netname,"\n"); +#endif + + return 1; +} + +entity turret_select_target() +{ + entity e; // target looper entity + float score; // target looper entity score + entity e_enemy; // currently best scoreing target + float m_score; // currently best scoreing target's score + + m_score = 0; + if(self.enemy && self.enemy.takedamage && turret_validate_target(self,self.enemy,self.target_validate_flags) > 0) + { + e_enemy = self.enemy; + m_score = self.turret_score_target(self,e_enemy) * self.target_select_samebias; + } + else + e_enemy = self.enemy = world; + + e = findradius(self.origin, self.target_range); + + // Nothing to aim at? + if (!e) + return world; + + while (e) + { + if(e.takedamage) + { + float f = turret_validate_target(self, e, self.target_select_flags); + //dprint("F is: ", ftos(f), "\n"); + if ( f > 0) + { + score = self.turret_score_target(self,e); + if ((score > m_score) && (score > 0)) + { + e_enemy = e; + m_score = score; + } + } + } + e = e.chain; + } + + return e_enemy; +} + + +/* + + = implemented + - = not implemented + + + TFL_FIRECHECK_NO + + TFL_FIRECHECK_WORLD + + TFL_FIRECHECK_DEAD + + TFL_FIRECHECK_DISTANCES + - TFL_FIRECHECK_LOS + + TFL_FIRECHECK_AIMDIST + + TFL_FIRECHECK_REALDIST + - TFL_FIRECHECK_ANGLEDIST + - TFL_FIRECHECK_TEAMCECK + + TFL_FIRECHECK_AFF + + TFL_FIRECHECK_AMMO_OWN + + TFL_FIRECHECK_AMMO_OTHER + + TFL_FIRECHECK_REFIRE +*/ + +/** +** Preforms pre-fire checks based on the uints firecheck_flags +**/ +float turret_firecheck() +{ + // This one just dont care =) + if (self.firecheck_flags & TFL_FIRECHECK_NO) + return 1; + + if (self.enemy == world) + return 0; + + // Ready? + if (self.firecheck_flags & TFL_FIRECHECK_REFIRE) + if (self.attack_finished_single > time) return 0; + + // Special case: volly fire turret that has to fire a full volly if a shot was fired. + if (self.shoot_flags & TFL_SHOOT_VOLLYALWAYS) + if (self.volly_counter != self.shot_volly) + if(self.ammo >= self.shot_dmg) + return 1; + + // Lack of zombies makes shooting dead things unnecessary :P + if (self.firecheck_flags & TFL_FIRECHECK_DEAD) + if (self.enemy.deadflag != DEAD_NO) + return 0; + + // Own ammo? + if (self.firecheck_flags & TFL_FIRECHECK_AMMO_OWN) + if (self.ammo < self.shot_dmg) + return 0; + + // Other's ammo? (support-supply units) + if (self.firecheck_flags & TFL_FIRECHECK_AMMO_OTHER) + if (self.enemy.ammo >= self.enemy.ammo_max) + return 0; + + // Target of opertunity? + if(turret_validate_target(self, self.tur_impactent, self.target_validate_flags) > 0) + { + self.enemy = self.tur_impactent; + return 1; + } + + if (self.firecheck_flags & TFL_FIRECHECK_DISTANCES) + { + // To close? + if (self.tur_dist_aimpos < self.target_range_min) + if(turret_validate_target(self, self.tur_impactent, self.target_validate_flags) > 0) + return 1; // Target of opertunity? + else + return 0; + } + + // Try to avoid FF? + if (self.firecheck_flags & TFL_FIRECHECK_AFF) + if (self.tur_impactent.team == self.team) + return 0; + + // aim<->predicted impact + if (self.firecheck_flags & TFL_FIRECHECK_AIMDIST) + if (self.tur_dist_impact_to_aimpos > self.aim_firetolerance_dist) + return 0; + + // Volly status + if (self.shot_volly > 1) + if (self.volly_counter == self.shot_volly) + if (self.ammo < (self.shot_dmg * self.shot_volly)) + return 0; + + /*if(self.firecheck_flags & TFL_FIRECHECK_VERIFIED) + if(self.tur_impactent != self.enemy) + return 0;*/ + + return 1; +} + +void turret_fire() +{ + if (autocvar_g_turrets_nofire != 0) + return; + + TUR_ACTION(self.turretid, TR_ATTACK); + + self.attack_finished_single = time + self.shot_refire; + self.ammo -= self.shot_dmg; + self.volly_counter = self.volly_counter - 1; + + if (self.volly_counter <= 0) + { + self.volly_counter = self.shot_volly; + + if (self.shoot_flags & TFL_SHOOT_CLEARTARGET) + self.enemy = world; + + if (self.shot_volly > 1) + self.attack_finished_single = time + self.shot_volly_refire; + } + +#ifdef TURRET_DEBUG + if (self.enemy) paint_target3(self.tur_aimpos, 64, self.tur_debug_rvec, self.tur_impacttime + 0.25); +#endif +} + +void turret_think() +{ + entity e; + + self.nextthink = time + self.ticrate; + + // ONS uses somewhat backwards linking. + if (teamplay) + { + if (g_onslaught) + if (self.target) + { + e = find(world, targetname,self.target); + if (e != world) + self.team = e.team; + } + + if (self.team != self.tur_head.team) + turret_respawn(); + } + +#ifdef TURRET_DEBUG + if (self.tur_debug_tmr1 < time) + { + if (self.enemy) paint_target (self.enemy,128,self.tur_debug_rvec,0.9); + paint_target(self,256,self.tur_debug_rvec,0.9); + self.tur_debug_tmr1 = time + 1; + } +#endif + + // Handle ammo + if (!(self.spawnflags & TSF_NO_AMMO_REGEN)) + if (self.ammo < self.ammo_max) + self.ammo = min(self.ammo + self.ammo_recharge, self.ammo_max); + + // Inactive turrets needs to run the think loop, + // So they can handle animation and wake up if need be. + if(!self.active) + { + turret_track(); + return; + } + + // This is typicaly used for zaping every target in range + // turret_fusionreactor uses this to recharge friendlys. + if (self.shoot_flags & TFL_SHOOT_HITALLVALID) + { + // Do a self.turret_fire for every valid target. + e = findradius(self.origin,self.target_range); + while (e) + { + if(e.takedamage) + { + if (turret_validate_target(self,e,self.target_validate_flags)) + { + self.enemy = e; + + turret_do_updates(self); + + if (self.turret_firecheckfunc()) + turret_fire(); + } + } + + e = e.chain; + } + self.enemy = world; + } + else if(self.shoot_flags & TFL_SHOOT_CUSTOM) + { + // This one is doing something.. oddball. assume its handles what needs to be handled. + + // Predict? + if(!(self.aim_flags & TFL_AIM_NO)) + self.tur_aimpos = turret_aim_generic(); + + // Turn & pitch? + if(!(self.track_flags & TFL_TRACK_NO)) + turret_track(); + + turret_do_updates(self); + + // Fire? + if (self.turret_firecheckfunc()) + turret_fire(); + } + else + { + // Special case for volly always. if it fired once it must compleate the volly. + if(self.shoot_flags & TFL_SHOOT_VOLLYALWAYS) + if(self.volly_counter != self.shot_volly) + { + // Predict or whatnot + if(!(self.aim_flags & TFL_AIM_NO)) + self.tur_aimpos = turret_aim_generic(); + + // Turn & pitch + if(!(self.track_flags & TFL_TRACK_NO)) + turret_track(); + + turret_do_updates(self); + + // Fire! + if (self.turret_firecheckfunc() != 0) + turret_fire(); + + TUR_ACTION(self.turretid, TR_THINK); + + return; + } + + // Check if we have a vailid enemy, and try to find one if we dont. + + // g_turrets_targetscan_maxdelay forces a target re-scan at least this often + float do_target_scan = 0; + if((self.target_select_time + autocvar_g_turrets_targetscan_maxdelay) < time) + do_target_scan = 1; + + // Old target (if any) invalid? + if(self.target_validate_time < time) + if (turret_validate_target(self, self.enemy, self.target_validate_flags) <= 0) + { + self.enemy = world; + self.target_validate_time = time + 0.5; + do_target_scan = 1; + } + + // But never more often then g_turrets_targetscan_mindelay! + if (self.target_select_time + autocvar_g_turrets_targetscan_mindelay > time) + do_target_scan = 0; + + if(do_target_scan) + { + self.enemy = turret_select_target(); + self.target_select_time = time; + } + + // No target, just go to idle, do any custom stuff and bail. + if (self.enemy == world) + { + // Turn & pitch + if(!(self.track_flags & TFL_TRACK_NO)) + turret_track(); + + TUR_ACTION(self.turretid, TR_THINK); + + // And bail. + return; + } + else + self.lip = time + autocvar_g_turrets_aimidle_delay; // Keep track of the last time we had a target. + + // Predict? + if(!(self.aim_flags & TFL_AIM_NO)) + self.tur_aimpos = turret_aim_generic(); + + // Turn & pitch? + if(!(self.track_flags & TFL_TRACK_NO)) + turret_track(); + + turret_do_updates(self); + + // Fire? + if (self.turret_firecheckfunc()) + turret_fire(); + } + + TUR_ACTION(self.turretid, TR_THINK); +} + +/* + When .used a turret switch team to activator.team. + If activator is world, the turret go inactive. +*/ +void turret_use() +{ + dprint("Turret ",self.netname, " used by ", activator.classname, "\n"); + + self.team = activator.team; + + if(self.team == 0) + self.active = ACTIVE_NOT; + else + self.active = ACTIVE_ACTIVE; + +} + +void turret_link() +{ - Net_LinkEntity(self, TRUE, 0, turret_send); ++ Net_LinkEntity(self, true, 0, turret_send); + self.think = turret_think; + self.nextthink = time; + self.tur_head.effects = EF_NODRAW; +} + +void turrets_manager_think() +{ + self.nextthink = time + 1; + + entity e; + if (autocvar_g_turrets_reloadcvars == 1) + { + e = nextent(world); + while (e) + { + if (IS_TURRET(e)) + { + load_unit_settings(e,e.cvar_basename,1); + TUR_ACTION(self.turretid, TR_THINK); + } + + e = nextent(e); + } + cvar_set("g_turrets_reloadcvars","0"); + } +} + +float turret_initialize(float tur_id) +{ + if(!autocvar_g_turrets) - return FALSE; ++ return false; + + entity e; + entity tur = get_turretinfo(tur_id); + if(tur.turretid == 0) - return FALSE; // invalid turret ++ return false; // invalid turret + + if(!self.tur_head) { TUR_ACTION(tur_id, TR_PRECACHE); } // if tur_head exists, we can assume this turret re-spawned + + e = find(world, classname, "turret_manager"); + if(!e) + { + e = spawn(); + e.classname = "turret_manager"; + e.think = turrets_manager_think; + e.nextthink = time + 2; + } + + if(!(self.spawnflags & TSF_SUSPENDED)) + builtin_droptofloor(); + + self.cvar_basename = tur.cvar_basename; + load_unit_settings(self, self.cvar_basename, 0); + + if(!self.team || !teamplay) { self.team = MAX_SHOT_DISTANCE; } + if(!self.ticrate) { self.ticrate = ((self.turret_flags & TUR_FLAG_SUPPORT) ? 0.2 : 0.1); } + if(!self.health) { self.health = 1000; } + if(!self.shot_refire) { self.shot_refire = 1; } + if(!self.tur_shotorg) { self.tur_shotorg = '50 0 50'; } + if(!self.turret_flags) { self.turret_flags = TUR_FLAG_SPLASH | TUR_FLAG_MEDPROJ | TUR_FLAG_PLAYER; } + if(!self.damage_flags) { self.damage_flags = TFL_DMG_YES | TFL_DMG_RETALIATE | TFL_DMG_AIMSHAKE; } + if(!self.aim_flags) { self.aim_flags = TFL_AIM_LEAD | TFL_AIM_SHOTTIMECOMPENSATE; } + if(!self.track_type) { self.track_type = TFL_TRACKTYPE_STEPMOTOR; } + if(!self.track_flags) { self.track_flags = TFL_TRACK_PITCH | TFL_TRACK_ROTATE; } + if(!self.ammo_flags) { self.ammo_flags = TFL_AMMO_ENERGY | TFL_AMMO_RECHARGE; } + if(!self.target_select_flags) { self.target_select_flags = TFL_TARGETSELECT_LOS | TFL_TARGETSELECT_TEAMCHECK | TFL_TARGETSELECT_RANGELIMITS | TFL_TARGETSELECT_ANGLELIMITS; } + if(!self.firecheck_flags) { self.firecheck_flags = TFL_FIRECHECK_DEAD | TFL_FIRECHECK_DISTANCES | TFL_FIRECHECK_LOS + | TFL_FIRECHECK_AIMDIST | TFL_FIRECHECK_TEAMCHECK | TFL_FIRECHECK_AMMO_OWN | TFL_FIRECHECK_REFIRE; } + + if(self.track_type != TFL_TRACKTYPE_STEPMOTOR) + { + // Fluid / Ineria mode. Looks mutch nicer. + // Can reduce aim preformance alot, needs a bit diffrent aimspeed + + self.aim_speed = bound(0.1, ((!self.aim_speed) ? 180 : self.aim_speed), 1000); + + if(!self.track_accel_pitch) { self.track_accel_pitch = 0.5; } + if(!self.track_accel_rotate) { self.track_accel_rotate = 0.5; } + if(!self.track_blendrate) { self.track_blendrate = 0.35; } + } + + self.respawntime = max(-1, ((!self.respawntime) ? 60 : self.respawntime)); + self.shot_refire = bound(0.01, ((!self.shot_refire) ? 1 : self.shot_refire), 9999); + self.shot_dmg = max(1, ((!self.shot_dmg) ? self.shot_refire * 50 : self.shot_dmg)); + self.shot_radius = max(1, ((!self.shot_radius) ? self.shot_dmg * 0.5 : self.shot_radius)); + self.shot_speed = max(1, ((!self.shot_speed) ? 2500 : self.shot_speed)); + self.shot_spread = bound(0.0001, ((!self.shot_spread) ? 0.0125 : self.shot_spread), 500); + self.shot_force = bound(0.001, ((!self.shot_force) ? self.shot_dmg * 0.5 + self.shot_radius * 0.5 : self.shot_force), 5000); + self.shot_volly = bound(1, ((!self.shot_volly) ? 1 : self.shot_volly), floor(self.ammo_max / self.shot_dmg)); + self.shot_volly_refire = bound(self.shot_refire, ((!self.shot_volly_refire) ? self.shot_refire * self.shot_volly : self.shot_volly_refire), 60); + self.target_range = bound(0, ((!self.target_range) ? self.shot_speed * 0.5 : self.target_range), MAX_SHOT_DISTANCE); + self.target_range_min = bound(0, ((!self.target_range_min) ? self.shot_radius * 2 : self.target_range_min), MAX_SHOT_DISTANCE); + self.target_range_optimal = bound(0, ((!self.target_range_optimal) ? self.target_range * 0.5 : self.target_range_optimal), MAX_SHOT_DISTANCE); + self.aim_maxrotate = bound(0, ((!self.aim_maxrotate) ? 90 : self.aim_maxrotate), 360); + self.aim_maxpitch = bound(0, ((!self.aim_maxpitch) ? 20 : self.aim_maxpitch), 90); + self.aim_speed = bound(0.1, ((!self.aim_speed) ? 36 : self.aim_speed), 1000); + self.aim_firetolerance_dist = bound(0.1, ((!self.aim_firetolerance_dist) ? 5 + (self.shot_radius * 2) : self.aim_firetolerance_dist), MAX_SHOT_DISTANCE); + self.target_select_rangebias = bound(-10, ((!self.target_select_rangebias) ? 1 : self.target_select_rangebias), 10); + self.target_select_samebias = bound(-10, ((!self.target_select_samebias) ? 1 : self.target_select_samebias), 10); + self.target_select_anglebias = bound(-10, ((!self.target_select_anglebias) ? 1 : self.target_select_anglebias), 10); + self.target_select_missilebias = bound(-10, ((!self.target_select_missilebias) ? 1 : self.target_select_missilebias), 10); + self.target_select_playerbias = bound(-10, ((!self.target_select_playerbias) ? 1 : self.target_select_playerbias), 10); + self.ammo_max = max(self.shot_dmg, ((!self.ammo_max) ? self.shot_dmg * 10 : self.ammo_max)); + self.ammo_recharge = max(0, ((!self.ammo_recharge) ? self.shot_dmg * 0.5 : self.ammo_recharge)); + + self.turret_flags = TUR_FLAG_ISTURRET | (tur.spawnflags); + + if(self.turret_flags & TUR_FLAG_SPLASH) + self.aim_flags |= TFL_AIM_SPLASH; + + if(self.turret_flags & TUR_FLAG_MISSILE) + self.target_select_flags |= TFL_TARGETSELECT_MISSILES; + + if(self.turret_flags & TUR_FLAG_PLAYER) + self.target_select_flags |= TFL_TARGETSELECT_PLAYERS; + + if(self.spawnflags & TSL_NO_RESPAWN) + self.damage_flags |= TFL_DMG_DEATH_NORESPAWN; + + if (self.turret_flags & TUR_FLAG_SUPPORT) + self.turret_score_target = turret_targetscore_support; + else + self.turret_score_target = turret_targetscore_generic; + + ++turret_count; + + setmodel(self, tur.model); + setsize(self, tur.mins, tur.maxs); + + self.turretid = tur_id; + self.classname = "turret_main"; + self.active = ACTIVE_ACTIVE; + self.effects = EF_NODRAW; + self.netname = TUR_NAME(tur_id); + self.ticrate = bound(sys_frametime, self.ticrate, 60); + self.max_health = self.health; + self.target_validate_flags = self.target_select_flags; + self.ammo = self.ammo_max; + self.ammo_recharge *= self.ticrate; + self.solid = SOLID_BBOX; + self.takedamage = DAMAGE_AIM; + self.movetype = MOVETYPE_NOCLIP; + self.view_ofs = '0 0 0'; + self.turret_firecheckfunc = turret_firecheck; + self.event_damage = turret_damage; + self.use = turret_use; - self.bot_attack = TRUE; ++ self.bot_attack = true; + self.nextthink = time + 1; + self.nextthink += turret_count * sys_frametime; + + self.tur_head = spawn(); + setmodel(self.tur_head, tur.head_model); + setsize(self.tur_head, '0 0 0', '0 0 0'); + setorigin(self.tur_head, '0 0 0'); + setattachment(self.tur_head, self, "tag_head"); + + self.tur_head.netname = self.tur_head.classname = "turret_head"; + self.tur_head.team = self.team; + self.tur_head.owner = self; + self.tur_head.takedamage = DAMAGE_NO; + self.tur_head.solid = SOLID_NOT; + self.tur_head.movetype = self.movetype; + + if(!self.tur_defend) + if(self.target != "") + { + self.tur_defend = find(world, targetname, self.target); + if (self.tur_defend == world) + { + self.target = ""; + dprint("Turret has invalid defendpoint!\n"); + } + } + + if (self.tur_defend) + self.idle_aim = self.tur_head.angles + angleofs(self.tur_head, self.tur_defend); + else + self.idle_aim = '0 0 0'; + +#ifdef TURRET_DEBUG + self.tur_debug_start = self.nextthink; + while (vlen(self.tur_debug_rvec) < 2) + self.tur_debug_rvec = randomvec() * 4; + + self.tur_debug_rvec_x = fabs(self.tur_debug_rvec_x); + self.tur_debug_rvec_y = fabs(self.tur_debug_rvec_y); + self.tur_debug_rvec_z = fabs(self.tur_debug_rvec_z); +#endif + + turret_link(); + turret_respawn(); + turret_tag_fire_update(); + + TUR_ACTION(tur_id, TR_SETUP); + + if(MUTATOR_CALLHOOK(TurretSpawn)) - return FALSE; ++ return false; + - return TRUE; - } ++ return true; ++} ++#endif diff --cc qcsrc/common/turrets/sv_turrets.qh index f89ef1581,000000000..c4ff13e91 mode 100644,000000..100644 --- a/qcsrc/common/turrets/sv_turrets.qh +++ b/qcsrc/common/turrets/sv_turrets.qh @@@ -1,106 -1,0 +1,113 @@@ ++#ifndef SV_TURRETS_H ++#define SV_TURRETS_H ++ +// turret fields +.float ticrate; // interal ai think rate +.vector aim_idle; // where to aim while idle +.entity tur_head; // top part of the turret +.entity tur_defend; // defend this entity +.vector tur_shotorg; // shot origin +.vector tur_aimpos; // aiming location +.float tur_impacttime; // predicted projectile impact time +.entity tur_impactent; // entity the projectile hit +.float tur_dist_enemy; // distance to enemy +.float tur_dist_aimpos; // distance to aim location +.float tur_dist_impact_to_aimpos; // distance impact<->aim +.float volly_counter; // decrement counter from .shot_volly to 0 + +.float shot_refire; // attack refire +.float shot_speed; // projectile speed +.float shot_spread; // inaccuracy +.float shot_dmg; // core damage of projectile +.float shot_radius; // projectile damage radius +.float shot_force; // projectile damage force +.float shot_volly; // smaller than 1 = shoot # times at target +.float shot_volly_refire; // refire after completed volly + +.float target_range; +.float target_range_min; +.float target_range_optimal; + +.float target_select_rangebias; +.float target_select_samebias; +.float target_select_anglebias; +.float target_select_missilebias; +.float target_select_playerbias; +.float target_select_time; // last time turret had a valid target +.float target_validate_time; // throttle re-validation of current target + +.float aim_firetolerance_dist; +.float aim_speed; +.float aim_maxpitch; +.float aim_maxrotate; + +.float ammo; // current ammo +.float ammo_recharge; // recharge rate +.float ammo_max; // maximum ammo + +.vector idle_aim; + +/// Map time control over pain inflicted +.float turret_scale_damage; +/// Map time control targetting range +.float turret_scale_range; +/// Map time control refire +.float turret_scale_refire; +/// Map time control ammo held and recharged +.float turret_scale_ammo; +/// Map time control aim speed +.float turret_scale_aim; +/// Map time control health +.float turret_scale_health; +/// Map time control respawn time +.float turret_scale_respawn; + +// tracking type +.float track_type; +const float TFL_TRACKTYPE_STEPMOTOR = 1; // hard angle increments, ugly for fast turning with best accuracy +const float TFL_TRACKTYPE_FLUIDPRECISE = 2; // smooth absolute movement, looks OK with fair accuracy +const float TFL_TRACKTYPE_FLUIDINERTIA = 3; // simulated inertia ("wobbly" mode), worst accuracy, depends on below flags +.float track_accel_pitch; +.float track_accel_rotate; +.float track_blendrate; + ++void() turret_respawn; ++ +/// updates aim org, shot org, shot dir and enemy org for selected turret +void turret_do_updates(entity e_turret); +.vector tur_shotdir_updated; + +.float() turret_firecheckfunc; // TODO: deprecate! + +/// Function to use for target evaluation. usualy turret_targetscore_generic +.float(entity _turret, entity _target) turret_score_target; + +.float(entity e_target,entity e_sender) turret_addtarget; + +.entity pathcurrent; + +float turret_count; + +// debugging +// Uncomment below to enable various debug output. +//#define TURRET_DEBUG +//#define TURRET_DEBUG_TARGETVALIDATE +//#define TURRET_DEBUG_TARGETSELECT +#ifdef TURRET_DEBUG +.float tur_debug_dmg_t_h; // total damage that hit something (can be more than tur_debug_dmg_t_f since it should count radius damage) +.float tur_debug_dmg_t_f; // total damage +.float tur_debug_start; // turret initialization time +.float tur_debug_tmr1; // random timer +.float tur_debug_tmr2; // random timer +.float tur_debug_tmr3; // random timer +.vector tur_debug_rvec; // random vector +#endif + +// aiming +vector tvt_thadv; // turret head angle diff vector, updated by a successful call to turret_validate_target +vector tvt_tadv; // turret angle diff vector, updated by a successful call to turret_validate_target +float tvt_thadf; // turret head angle diff float, updated by a successful call to turret_validate_target +float tvt_tadf; // turret angle diff float, updated by a successful call to turret_validate_target +float tvt_dist; // turret distance, updated by a successful call to turret_validate_target ++ ++#endif diff --cc qcsrc/common/turrets/turrets.qh index 5491746e9,000000000..b3736aff9 mode 100644,000000..100644 --- a/qcsrc/common/turrets/turrets.qh +++ b/qcsrc/common/turrets/turrets.qh @@@ -1,197 -1,0 +1,202 @@@ ++#ifndef TURRETS_H ++#define TURRETS_H ++ +// turret requests +#define TR_SETUP 1 // (BOTH) setup turret data +#define TR_THINK 2 // (SERVER) logic to run every frame +#define TR_DEATH 3 // (SERVER) called when turret dies +#define TR_PRECACHE 4 // (BOTH) precaches models/sounds used by this turret +#define TR_ATTACK 5 // (SERVER) called when turret attacks +#define TR_CONFIG 6 // (ALL) + +// functions: - entity get_turretinfo(float id); ++entity get_turretinfo(int id); + +// fields: +.entity tur_head; + +// target selection flags - .float target_select_flags; - .float target_validate_flags; - const float TFL_TARGETSELECT_NO = 2; // don't automatically find targets - const float TFL_TARGETSELECT_LOS = 4; // require line of sight to find targets - const float TFL_TARGETSELECT_PLAYERS = 8; // target players - const float TFL_TARGETSELECT_MISSILES = 16; // target projectiles - const float TFL_TARGETSELECT_TRIGGERTARGET = 32; // respond to turret_trigger_target events - const float TFL_TARGETSELECT_ANGLELIMITS = 64; // apply extra angular limits to target selection - const float TFL_TARGETSELECT_RANGELIMITS = 128; // limit target selection range - const float TFL_TARGETSELECT_TEAMCHECK = 256; // don't attack teammates - const float TFL_TARGETSELECT_NOBUILTIN = 512; // only attack targets when triggered - const float TFL_TARGETSELECT_OWNTEAM = 1024; // only attack teammates - const float TFL_TARGETSELECT_NOTURRETS = 2048; // don't attack other turrets - const float TFL_TARGETSELECT_FOV = 4096; // extra limits to attack range - const float TFL_TARGETSELECT_MISSILESONLY = 8192; // only attack missiles ++.int target_select_flags; ++.int target_validate_flags; ++const int TFL_TARGETSELECT_NO = 2; // don't automatically find targets ++const int TFL_TARGETSELECT_LOS = 4; // require line of sight to find targets ++const int TFL_TARGETSELECT_PLAYERS = 8; // target players ++const int TFL_TARGETSELECT_MISSILES = 16; // target projectiles ++const int TFL_TARGETSELECT_TRIGGERTARGET = 32; // respond to turret_trigger_target events ++const int TFL_TARGETSELECT_ANGLELIMITS = 64; // apply extra angular limits to target selection ++const int TFL_TARGETSELECT_RANGELIMITS = 128; // limit target selection range ++const int TFL_TARGETSELECT_TEAMCHECK = 256; // don't attack teammates ++const int TFL_TARGETSELECT_NOBUILTIN = 512; // only attack targets when triggered ++const int TFL_TARGETSELECT_OWNTEAM = 1024; // only attack teammates ++const int TFL_TARGETSELECT_NOTURRETS = 2048; // don't attack other turrets ++const int TFL_TARGETSELECT_FOV = 4096; // extra limits to attack range ++const int TFL_TARGETSELECT_MISSILESONLY = 8192; // only attack missiles + +// aim flags - .float aim_flags; - const float TFL_AIM_NO = 1; // no aiming - const float TFL_AIM_SPLASH = 2; // aim for ground around the target's feet - const float TFL_AIM_LEAD = 4; // try to predict target movement - const float TFL_AIM_SHOTTIMECOMPENSATE = 8; // compensate for shot traveltime when leading - const float TFL_AIM_ZPREDICT = 16; // predict target's z position at impact - const float TFL_AIM_SIMPLE = 32; // aim at player's current location ++.int aim_flags; ++const int TFL_AIM_NO = 1; // no aiming ++const int TFL_AIM_SPLASH = 2; // aim for ground around the target's feet ++const int TFL_AIM_LEAD = 4; // try to predict target movement ++const int TFL_AIM_SHOTTIMECOMPENSATE = 8; // compensate for shot traveltime when leading ++const int TFL_AIM_ZPREDICT = 16; // predict target's z position at impact ++const int TFL_AIM_SIMPLE = 32; // aim at player's current location + +// tracking flags - .float track_flags; - const float TFL_TRACK_NO = 2; // don't move head - const float TFL_TRACK_PITCH = 4; // pitch head - const float TFL_TRACK_ROTATE = 8; // rotate head ++.int track_flags; ++const int TFL_TRACK_NO = 2; // don't move head ++const int TFL_TRACK_PITCH = 4; // pitch head ++const int TFL_TRACK_ROTATE = 8; // rotate head + +// prefire checks - .float firecheck_flags; - const float TFL_FIRECHECK_DEAD = 4; // don't attack dead targets (zombies?) - const float TFL_FIRECHECK_DISTANCES = 8; // another range check - const float TFL_FIRECHECK_LOS = 16; // line of sight - const float TFL_FIRECHECK_AIMDIST = 32; // consider distance impactpoint<->aimspot - const float TFL_FIRECHECK_REALDIST = 64; // consider enemy origin<->impactpoint - const float TFL_FIRECHECK_ANGLEDIST = 128; // consider angular diff head<->aimspot - const float TFL_FIRECHECK_TEAMCHECK = 256; // don't attack teammates - const float TFL_FIRECHECK_AFF = 512; // try to avoid any friendly fire - const float TFL_FIRECHECK_AMMO_OWN = 1024; // own ammo needs to be larger than damage dealt - const float TFL_FIRECHECK_AMMO_OTHER = 2048; // target's ammo needs to be less than max - const float TFL_FIRECHECK_REFIRE = 4096; // check single attack finished delays - const float TFL_FIRECHECK_NO = 16384; // no prefire checks ++.int firecheck_flags; ++const int TFL_FIRECHECK_DEAD = 4; // don't attack dead targets (zombies?) ++const int TFL_FIRECHECK_DISTANCES = 8; // another range check ++const int TFL_FIRECHECK_LOS = 16; // line of sight ++const int TFL_FIRECHECK_AIMDIST = 32; // consider distance impactpoint<->aimspot ++const int TFL_FIRECHECK_REALDIST = 64; // consider enemy origin<->impactpoint ++const int TFL_FIRECHECK_ANGLEDIST = 128; // consider angular diff head<->aimspot ++const int TFL_FIRECHECK_TEAMCHECK = 256; // don't attack teammates ++const int TFL_FIRECHECK_AFF = 512; // try to avoid any friendly fire ++const int TFL_FIRECHECK_AMMO_OWN = 1024; // own ammo needs to be larger than damage dealt ++const int TFL_FIRECHECK_AMMO_OTHER = 2048; // target's ammo needs to be less than max ++const int TFL_FIRECHECK_REFIRE = 4096; // check single attack finished delays ++const int TFL_FIRECHECK_NO = 16384; // no prefire checks + +// attack flags - .float shoot_flags; - const float TFL_SHOOT_NO = 64; // no attacking - const float TFL_SHOOT_VOLLY = 2; // fire in vollies - const float TFL_SHOOT_VOLLYALWAYS = 4; // always do a full volly, even if target is lost - const float TFL_SHOOT_HITALLVALID = 8; // loop through all valid targets - const float TFL_SHOOT_CLEARTARGET = 16; // lose target after attack (after volly is done if in volly mode) - const float TFL_SHOOT_CUSTOM = 32; // custom attacking ++.int shoot_flags; ++const int TFL_SHOOT_NO = 64; // no attacking ++const int TFL_SHOOT_VOLLY = 2; // fire in vollies ++const int TFL_SHOOT_VOLLYALWAYS = 4; // always do a full volly, even if target is lost ++const int TFL_SHOOT_HITALLVALID = 8; // loop through all valid targets ++const int TFL_SHOOT_CLEARTARGET = 16; // lose target after attack (after volly is done if in volly mode) ++const int TFL_SHOOT_CUSTOM = 32; // custom attacking + +// turret capabilities - .float turret_flags; - const float TUR_FLAG_NONE = 0; // no abilities - const float TUR_FLAG_SNIPER = 2; // sniping turret - const float TUR_FLAG_SPLASH = 4; // can deal splash damage - const float TUR_FLAG_HITSCAN = 8; // hit scan - const float TUR_FLAG_MULTIGUN = 16; // multiple guns - const float TUR_FLAG_GUIDED = 32; // laser guided projectiles - const float TUR_FLAG_SLOWPROJ = 64; // turret fires slow projectiles - const float TUR_FLAG_MEDPROJ = 128; // turret fires medium projectiles - const float TUR_FLAG_FASTPROJ = 256; // turret fires fast projectiles - const float TUR_FLAG_PLAYER = 512; // can damage players - const float TUR_FLAG_MISSILE = 1024; // can damage missiles - const float TUR_FLAG_SUPPORT = 2048; // supports other units - const float TUR_FLAG_AMMOSOURCE = 4096; // can provide ammunition - const float TUR_FLAG_RECIEVETARGETS = 8192; // can recieve targets from external sources - const float TUR_FLAG_MOVE = 16384; // can move - const float TUR_FLAG_ROAM = 32768; // roams around if not attacking - const float TUR_FLAG_ISTURRET = 65536; // identifies this unit as a turret ++.int turret_flags; ++const int TUR_FLAG_NONE = 0; // no abilities ++const int TUR_FLAG_SNIPER = 2; // sniping turret ++const int TUR_FLAG_SPLASH = 4; // can deal splash damage ++const int TUR_FLAG_HITSCAN = 8; // hit scan ++const int TUR_FLAG_MULTIGUN = 16; // multiple guns ++const int TUR_FLAG_GUIDED = 32; // laser guided projectiles ++const int TUR_FLAG_SLOWPROJ = 64; // turret fires slow projectiles ++const int TUR_FLAG_MEDPROJ = 128; // turret fires medium projectiles ++const int TUR_FLAG_FASTPROJ = 256; // turret fires fast projectiles ++const int TUR_FLAG_PLAYER = 512; // can damage players ++const int TUR_FLAG_MISSILE = 1024; // can damage missiles ++const int TUR_FLAG_SUPPORT = 2048; // supports other units ++const int TUR_FLAG_AMMOSOURCE = 4096; // can provide ammunition ++const int TUR_FLAG_RECIEVETARGETS = 8192; // can recieve targets from external sources ++const int TUR_FLAG_MOVE = 16384; // can move ++const int TUR_FLAG_ROAM = 32768; // roams around if not attacking ++const int TUR_FLAG_ISTURRET = 65536; // identifies this unit as a turret + +// ammo types +#define ammo_flags currentammo - const float TFL_AMMO_NONE = 64; // doesn't use ammo - const float TFL_AMMO_ENERGY = 2; // uses power - const float TFL_AMMO_BULLETS = 4; // uses bullets - const float TFL_AMMO_ROCKETS = 8; // uses explosives - const float TFL_AMMO_RECHARGE = 16; // regenerates ammo - const float TFL_AMMO_RECIEVE = 32; // can recieve ammo from support units ++const int TFL_AMMO_NONE = 64; // doesn't use ammo ++const int TFL_AMMO_ENERGY = 2; // uses power ++const int TFL_AMMO_BULLETS = 4; // uses bullets ++const int TFL_AMMO_ROCKETS = 8; // uses explosives ++const int TFL_AMMO_RECHARGE = 16; // regenerates ammo ++const int TFL_AMMO_RECIEVE = 32; // can recieve ammo from support units + +// damage flags - .float damage_flags; - const float TFL_DMG_NO = 256; // doesn't take damage - const float TFL_DMG_YES = 2; // can be damaged - const float TFL_DMG_TEAM = 4; // can be damaged by teammates - const float TFL_DMG_RETALIATE = 8; // target attackers - const float TFL_DMG_RETALIATE_TEAM = 16; // target attackers, even if on same team - const float TFL_DMG_TARGETLOSS = 32; // loses target when damaged - const float TFL_DMG_AIMSHAKE = 64; // damage throws off aim - const float TFL_DMG_HEADSHAKE = 128; // damage shakes head - const float TFL_DMG_DEATH_NORESPAWN = 256; // no re-spawning ++.int damage_flags; ++const int TFL_DMG_NO = 256; // doesn't take damage ++const int TFL_DMG_YES = 2; // can be damaged ++const int TFL_DMG_TEAM = 4; // can be damaged by teammates ++const int TFL_DMG_RETALIATE = 8; // target attackers ++const int TFL_DMG_RETALIATE_TEAM = 16; // target attackers, even if on same team ++const int TFL_DMG_TARGETLOSS = 32; // loses target when damaged ++const int TFL_DMG_AIMSHAKE = 64; // damage throws off aim ++const int TFL_DMG_HEADSHAKE = 128; // damage shakes head ++const int TFL_DMG_DEATH_NORESPAWN = 256; // no re-spawning + +// spawn flags - const float TSF_SUSPENDED = 1; - const float TSF_TERRAINBASE = 2; // currently unused - const float TSF_NO_AMMO_REGEN = 4; // disable builtin ammo regeneration - const float TSF_NO_PATHBREAK = 8; // don't break path to chase enemies, will still fire at them if possible - const float TSL_NO_RESPAWN = 16; // don't re-spawn - const float TSL_ROAM = 32; // roam while idle ++const int TSF_SUSPENDED = 1; ++const int TSF_TERRAINBASE = 2; // currently unused ++const int TSF_NO_AMMO_REGEN = 4; // disable builtin ammo regeneration ++const int TSF_NO_PATHBREAK = 8; // don't break path to chase enemies, will still fire at them if possible ++const int TSL_NO_RESPAWN = 16; // don't re-spawn ++const int TSL_ROAM = 32; // roam while idle + +// send flags - const float TNSF_UPDATE = 2; - const float TNSF_STATUS = 4; - const float TNSF_SETUP = 8; - const float TNSF_ANG = 16; - const float TNSF_AVEL = 32; - const float TNSF_MOVE = 64; ++const int TNSF_UPDATE = 2; ++const int TNSF_STATUS = 4; ++const int TNSF_SETUP = 8; ++const int TNSF_ANG = 16; ++const int TNSF_AVEL = 32; ++const int TNSF_MOVE = 64; +.float anim_start_time; - const float TNSF_ANIM = 128; ++const int TNSF_ANIM = 128; + - const float TNSF_FULL_UPDATE = 16777215; ++const int TNSF_FULL_UPDATE = 16777215; + + +// entity properties of turretinfo: - .float turretid; // TUR_... ++.int turretid; // TUR_... +.string netname; // short name +.string turret_name; // human readable name +.float(float) turret_func; // m_... +.string mdl; // currently a copy of the model +.string model; // full name of model +.string head_model; // full name of tur_head model +.string cvar_basename; // TODO: deprecate! +.float spawnflags; +.vector mins, maxs; // turret hitbox size + +// other useful macros +#define TUR_ACTION(turrettype,mrequest) (get_turretinfo(turrettype)).turret_func(mrequest) +#define TUR_NAME(turrettype) (get_turretinfo(turrettype)).turret_name + +// ===================== +// Turret Registration +// ===================== + +float t_null(float dummy); +void register_turret(float id, float(float) func, float turretflags, vector min_s, vector max_s, string modelname, string headmodelname, string shortname, string mname); +void register_turrets_done(); + +const float TUR_MAXCOUNT = 24; +#define TUR_FIRST 1 +float TUR_COUNT; +float TUR_LAST; + +#define REGISTER_TURRET_2(id,func,turretflags,min_s,max_s,modelname,headmodelname,shortname,mname) \ + float id; \ + float func(float); \ + void RegisterTurrets_##id() \ + { \ + TUR_LAST = (id = TUR_FIRST + TUR_COUNT); \ + ++TUR_COUNT; \ + register_turret(id,func,turretflags,min_s,max_s,modelname,headmodelname,shortname,mname); \ + } \ + ACCUMULATE_FUNCTION(RegisterTurrets, RegisterTurrets_##id) +#ifdef MENUQC +#define REGISTER_TURRET(id,func,turretflags,min_s,max_s,modelname,headmodelname,shortname,mname) \ + REGISTER_TURRET_2(TUR_##id,t_null,turretflags,min_s,max_s,modelname,headmodelname,shortname,mname) +#else +#define REGISTER_TURRET(id,func,turretflags,min_s,max_s,modelname,headmodelname,shortname,mname) \ + REGISTER_TURRET_2(TUR_##id,func,turretflags,min_s,max_s,modelname,headmodelname,shortname,mname) +#endif + +#define TUR_DUPECHECK(dupecheck,cvar) \ + #ifndef dupecheck \ + #define dupecheck \ + float cvar; \ + #else \ + #error DUPLICATE TURRET CVAR: cvar \ + #endif + +#define TUR_ADD_CVAR(turret,name) \ + TUR_DUPECHECK(TUR_CVAR_##turret##_##name, autocvar_g_turrets_unit_##turret##_##name) + +#define TUR_CVAR(turret,name) autocvar_g_turrets_unit_##turret##_##name + +#include "all.qh" + +#undef TUR_ADD_CVAR +#undef REGISTER_TURRET +ACCUMULATE_FUNCTION(RegisterTurrets, register_turrets_done); ++ ++#endif diff --cc qcsrc/common/turrets/unit/ewheel.qc index fea43eb36,000000000..229677431 mode 100644,000000..100644 --- a/qcsrc/common/turrets/unit/ewheel.qc +++ b/qcsrc/common/turrets/unit/ewheel.qc @@@ -1,311 -1,0 +1,313 @@@ +#ifdef REGISTER_TURRET +REGISTER_TURRET( +/* TUR_##id */ EWHEEL, +/* function */ t_ewheel, +/* spawnflags */ TUR_FLAG_PLAYER | TUR_FLAG_MOVE | TUR_FLAG_ROAM, +/* mins,maxs */ '-32 -32 0', '32 32 48', +/* model */ "ewheel-base2.md3", +/* head_model */ "ewheel-gun1.md3", +/* netname */ "ewheel", +/* fullname */ _("eWheel Turret") +); +#else +#ifdef SVQC ++#include "../../effects.qh" ++ +float autocvar_g_turrets_unit_ewheel_speed_fast; +float autocvar_g_turrets_unit_ewheel_speed_slow; +float autocvar_g_turrets_unit_ewheel_speed_slower; +float autocvar_g_turrets_unit_ewheel_speed_stop; +float autocvar_g_turrets_unit_ewheel_turnrate; + +const float ewheel_anim_stop = 0; +const float ewheel_anim_fwd_slow = 1; +const float ewheel_anim_fwd_fast = 2; +const float ewheel_anim_bck_slow = 3; +const float ewheel_anim_bck_fast = 4; + +//#define EWHEEL_FANCYPATH +void ewheel_move_path() +{ +#ifdef EWHEEL_FANCYPATH + // Are we close enougth to a path node to switch to the next? + if (vlen(self.origin - self.pathcurrent.origin) < 64) + if (self.pathcurrent.path_next == world) + { + // Path endpoint reached + pathlib_deletepath(self.pathcurrent.owner); + self.pathcurrent = world; + + if (self.pathgoal) + { + if (self.pathgoal.use) + self.pathgoal.use(); + + if (self.pathgoal.enemy) + { + self.pathcurrent = pathlib_astar(self.pathgoal.origin,self.pathgoal.enemy.origin); + self.pathgoal = self.pathgoal.enemy; + } + } + else + self.pathgoal = world; + } + else + self.pathcurrent = self.pathcurrent.path_next; + +#else + if (vlen(self.origin - self.pathcurrent.origin) < 64) + self.pathcurrent = self.pathcurrent.enemy; +#endif + + if (self.pathcurrent) + { + + self.moveto = self.pathcurrent.origin; + self.steerto = steerlib_attract2(self.moveto, 0.5, 500, 0.95); + + movelib_move_simple(v_forward, (autocvar_g_turrets_unit_ewheel_speed_fast), 0.4); + } +} + +void ewheel_move_enemy() +{ + float newframe; + + self.steerto = steerlib_arrive(self.enemy.origin,self.target_range_optimal); + + self.moveto = self.origin + self.steerto * 128; + + if (self.tur_dist_enemy > self.target_range_optimal) + { + if ( self.tur_head.spawnshieldtime < 1 ) + { + newframe = ewheel_anim_fwd_fast; + movelib_move_simple(v_forward, (autocvar_g_turrets_unit_ewheel_speed_fast), 0.4); + } + else if (self.tur_head.spawnshieldtime < 2) + { + + newframe = ewheel_anim_fwd_slow; + movelib_move_simple(v_forward, (autocvar_g_turrets_unit_ewheel_speed_slow), 0.4); + } + else + { + newframe = ewheel_anim_fwd_slow; + movelib_move_simple(v_forward, (autocvar_g_turrets_unit_ewheel_speed_slower), 0.4); + } + } + else if (self.tur_dist_enemy < self.target_range_optimal * 0.5) + { + newframe = ewheel_anim_bck_slow; + movelib_move_simple(v_forward * -1, (autocvar_g_turrets_unit_ewheel_speed_slow), 0.4); + } + else + { + newframe = ewheel_anim_stop; + movelib_beak_simple((autocvar_g_turrets_unit_ewheel_speed_stop)); + } + - turrets_setframe(newframe, FALSE); ++ turrets_setframe(newframe, false); +} + +void ewheel_move_idle() +{ + if(self.frame != 0) + { + self.SendFlags |= TNSF_ANIM; + self.anim_start_time = time; + } + + self.frame = 0; + if (vlen(self.velocity)) + movelib_beak_simple((autocvar_g_turrets_unit_ewheel_speed_stop)); +} + +void spawnfunc_turret_ewheel() { if(!turret_initialize(TUR_EWHEEL)) remove(self); } + +float t_ewheel(float req) +{ + switch(req) + { + case TR_ATTACK: + { + float i; + entity _mis; + + for (i = 0; i < 1; ++i) + { + turret_do_updates(self); + - _mis = turret_projectile(W_Sound("lasergun_fire"), 1, 0, DEATH_TURRET_EWHEEL, PROJECTILE_BLASTER, TRUE, TRUE); ++ _mis = turret_projectile(W_Sound("lasergun_fire"), 1, 0, DEATH_TURRET_EWHEEL, PROJECTILE_BLASTER, true, true); + _mis.missile_flags = MIF_SPLASH; + + Send_Effect(EFFECT_LASER_MUZZLEFLASH, self.tur_shotorg, self.tur_shotdir_updated * 1000, 1); + + self.tur_head.frame += 2; + + if (self.tur_head.frame > 3) + self.tur_head.frame = 0; + } + - return TRUE; ++ return true; + } + case TR_THINK: + { + float vz; + vector wish_angle, real_angle; + + vz = self.velocity_z; + + self.angles_x = anglemods(self.angles_x); + self.angles_y = anglemods(self.angles_y); + + fixedmakevectors(self.angles); + + wish_angle = normalize(self.steerto); + wish_angle = vectoangles(wish_angle); + real_angle = wish_angle - self.angles; + real_angle = shortangle_vxy(real_angle, self.tur_head.angles); + + self.tur_head.spawnshieldtime = fabs(real_angle_y); + real_angle_y = bound(-self.tur_head.aim_speed, real_angle_y, self.tur_head.aim_speed); + self.angles_y = (self.angles_y + real_angle_y); + + if(self.enemy) + ewheel_move_enemy(); + else if(self.pathcurrent) + ewheel_move_path(); + else + ewheel_move_idle(); + + self.velocity_z = vz; + + if(vlen(self.velocity)) + self.SendFlags |= TNSF_MOVE; + - return TRUE; ++ return true; + } + case TR_DEATH: + { + self.velocity = '0 0 0'; + +#ifdef EWHEEL_FANCYPATH + if (self.pathcurrent) + pathlib_deletepath(self.pathcurrent.owner); +#endif + self.pathcurrent = world; + - return TRUE; ++ return true; + } + case TR_SETUP: + { + entity e; + + if(self.movetype == MOVETYPE_WALK) + { + self.velocity = '0 0 0'; + self.enemy = world; + + setorigin(self, self.pos1); + + if (self.target != "") + { + e = find(world, targetname, self.target); + if (!e) + { + dprint("Initital waypoint for ewheel does NOT exsist, fix your map!\n"); + self.target = ""; + } + + if (e.classname != "turret_checkpoint") + dprint("Warning: not a turrret path\n"); + else + { + +#ifdef EWHEEL_FANCYPATH + self.pathcurrent = WALKER_PATH(self.origin,e.origin); + self.pathgoal = e; +#else + self.pathcurrent = e; +#endif + } + } + } + - self.iscreature = TRUE; ++ self.iscreature = true; + self.teleportable = TELEPORT_NORMAL; - self.damagedbycontents = TRUE; ++ self.damagedbycontents = true; + self.movetype = MOVETYPE_WALK; + self.solid = SOLID_SLIDEBOX; + self.takedamage = DAMAGE_AIM; + self.idle_aim = '0 0 0'; + self.pos1 = self.origin; + self.target_select_flags = TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_RANGELIMITS | TFL_TARGETSELECT_TEAMCHECK | TFL_TARGETSELECT_LOS; + self.target_validate_flags = TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_RANGELIMITS | TFL_TARGETSELECT_TEAMCHECK | TFL_TARGETSELECT_LOS; + self.frame = self.tur_head.frame = 1; + self.ammo_flags = TFL_AMMO_ENERGY | TFL_AMMO_RECHARGE | TFL_AMMO_RECIEVE; + + // Convert from dgr / sec to dgr / tic + self.tur_head.aim_speed = (autocvar_g_turrets_unit_ewheel_turnrate); + self.tur_head.aim_speed = self.tur_head.aim_speed / (1 / self.ticrate); + - return TRUE; ++ return true; + } + case TR_PRECACHE: + { + precache_model ("models/turrets/ewheel-base2.md3"); + precache_model ("models/turrets/ewheel-gun1.md3"); - return TRUE; ++ return true; + } + } + - return TRUE; ++ return true; +} + +#endif // SVQC +#ifdef CSQC + +void ewheel_draw() +{ + float dt; + + dt = time - self.move_time; + self.move_time = time; + if(dt <= 0) + return; + + fixedmakevectors(self.angles); + setorigin(self, self.origin + self.velocity * dt); + self.tur_head.angles += dt * self.tur_head.move_avelocity; + self.angles_y = self.move_angles_y; + + if (self.health < 127) + if(random() < 0.05) + te_spark(self.origin + '0 0 40', randomvec() * 256 + '0 0 256', 16); +} + +float t_ewheel(float req) +{ + switch(req) + { + case TR_SETUP: + { + self.gravity = 1; + self.movetype = MOVETYPE_BOUNCE; + self.move_movetype = MOVETYPE_BOUNCE; + self.move_origin = self.origin; + self.move_time = time; + self.draw = ewheel_draw; + - return TRUE; ++ return true; + } + case TR_PRECACHE: + { - return TRUE; ++ return true; + } + } + - return TRUE; ++ return true; +} + +#endif // CSQC +#endif // REGISTER_TURRET diff --cc qcsrc/common/turrets/unit/flac.qc index 02bd7103e,000000000..1797f4924 mode 100644,000000..100644 --- a/qcsrc/common/turrets/unit/flac.qc +++ b/qcsrc/common/turrets/unit/flac.qc @@@ -1,103 -1,0 +1,105 @@@ +#ifdef REGISTER_TURRET +REGISTER_TURRET( +/* TUR_##id */ FLAC, +/* function */ t_flac, +/* spawnflags */ TUR_FLAG_SPLASH | TUR_FLAG_FASTPROJ | TUR_FLAG_MISSILE, +/* mins,maxs */ '-32 -32 0', '32 32 64', +/* model */ "base.md3", +/* head_model */ "flac.md3", +/* netname */ "flac", +/* fullname */ _("FLAC Cannon") +); +#else +#ifdef SVQC ++#include "../../effects.qh" ++ +void turret_flac_projectile_think_explode() +{ + if(self.enemy != world) + if(vlen(self.origin - self.enemy.origin) < self.owner.shot_radius * 3) + setorigin(self,self.enemy.origin + randomvec() * self.owner.shot_radius); + +#ifdef TURRET_DEBUG + float d; + d = RadiusDamage (self, self.owner, self.owner.shot_dmg, self.owner.shot_dmg, self.owner.shot_radius, self, world, self.owner.shot_force, self.totalfrags, world); + self.owner.tur_dbg_dmg_t_h = self.owner.tur_dbg_dmg_t_h + d; + self.owner.tur_dbg_dmg_t_f = self.owner.tur_dbg_dmg_t_f + self.owner.shot_dmg; +#else + RadiusDamage (self, self.realowner, self.owner.shot_dmg, self.owner.shot_dmg, self.owner.shot_radius, self, world, self.owner.shot_force, self.totalfrags, world); +#endif + remove(self); +} + +void spawnfunc_turret_flac() { if(!turret_initialize(TUR_FLAC)) remove(self); } + +float t_flac(float req) +{ + switch(req) + { + case TR_ATTACK: + { + entity proj; + + turret_tag_fire_update(); + - proj = turret_projectile(W_Sound("hagar_fire"), 5, 0, DEATH_TURRET_FLAC, PROJECTILE_HAGAR, TRUE, TRUE); ++ proj = turret_projectile(W_Sound("hagar_fire"), 5, 0, DEATH_TURRET_FLAC, PROJECTILE_HAGAR, true, true); + Send_Effect(EFFECT_LASER_MUZZLEFLASH, self.tur_shotorg, self.tur_shotdir_updated * 1000, 1); + proj.think = turret_flac_projectile_think_explode; + proj.nextthink = time + self.tur_impacttime + (random() * 0.01 - random() * 0.01); + proj.missile_flags = MIF_SPLASH | MIF_PROXY; + + self.tur_head.frame = self.tur_head.frame + 1; + if (self.tur_head.frame >= 4) + self.tur_head.frame = 0; + - return TRUE; ++ return true; + } + case TR_THINK: + { - return TRUE; ++ return true; + } + case TR_DEATH: + { - return TRUE; ++ return true; + } + case TR_SETUP: + { + self.ammo_flags = TFL_AMMO_ROCKETS | TFL_AMMO_RECHARGE; + self.aim_flags = TFL_AIM_LEAD | TFL_AIM_SHOTTIMECOMPENSATE; + self.damage_flags |= TFL_DMG_HEADSHAKE; + self.target_select_flags |= TFL_TARGETSELECT_NOTURRETS | TFL_TARGETSELECT_MISSILESONLY; + - return TRUE; ++ return true; + } + case TR_PRECACHE: + { + precache_model ("models/turrets/base.md3"); + precache_model ("models/turrets/flac.md3"); - return TRUE; ++ return true; + } + } + - return TRUE; ++ return true; +} + +#endif // SVQC +#ifdef CSQC +float t_flac(float req) +{ + switch(req) + { + case TR_SETUP: + { - return TRUE; ++ return true; + } + case TR_PRECACHE: + { - return TRUE; ++ return true; + } + } + - return TRUE; ++ return true; +} + +#endif // CSQC +#endif // REGISTER_TURRET diff --cc qcsrc/common/turrets/unit/fusionreactor.qc index d2a3c408e,000000000..db4974566 mode 100644,000000..100644 --- a/qcsrc/common/turrets/unit/fusionreactor.qc +++ b/qcsrc/common/turrets/unit/fusionreactor.qc @@@ -1,116 -1,0 +1,116 @@@ +#ifdef REGISTER_TURRET +REGISTER_TURRET( +/* TUR_##id */ FUSIONREACTOR, +/* function */ t_fusionreactor, +/* spawnflags */ TUR_FLAG_SUPPORT | TUR_FLAG_AMMOSOURCE, +/* mins,maxs */ '-34 -34 0', '34 34 90', +/* model */ "base.md3", +/* head_model */ "reactor.md3", +/* netname */ "fusionreactor", +/* fullname */ _("Fusion Reactor") +); +#else +#ifdef SVQC +float turret_fusionreactor_firecheck() +{ + if (self.attack_finished_single > time) + return 0; + + if (self.enemy.deadflag != DEAD_NO) + return 0; + + if (self.enemy == world) + return 0; + + if (self.ammo < self.shot_dmg) + return 0; + + if (self.enemy.ammo >= self.enemy.ammo_max) + return 0; + + if (vlen(self.enemy.origin - self.origin) > self.target_range) + return 0; + + if(self.team != self.enemy.team) + return 0; + + if(!(self.enemy.ammo_flags & TFL_AMMO_ENERGY)) + return 0; + + return 1; +} + +void spawnfunc_turret_fusionreactor() { if(!turret_initialize(TUR_FUSIONREACTOR)) remove(self); } + +float t_fusionreactor(float req) +{ + switch(req) + { + case TR_ATTACK: + { + vector fl_org; + + self.enemy.ammo = min(self.enemy.ammo + self.shot_dmg,self.enemy.ammo_max); + fl_org = 0.5 * (self.enemy.absmin + self.enemy.absmax); + te_smallflash(fl_org); + - return TRUE; ++ return true; + } + case TR_THINK: + { + self.tur_head.avelocity = '0 250 0' * (self.ammo / self.ammo_max); + - return TRUE; ++ return true; + } + case TR_DEATH: + { - return TRUE; ++ return true; + } + case TR_SETUP: + { + self.ammo_flags = TFL_AMMO_ENERGY | TFL_AMMO_RECHARGE; + self.target_select_flags = TFL_TARGETSELECT_TEAMCHECK | TFL_TARGETSELECT_OWNTEAM | TFL_TARGETSELECT_RANGELIMITS; + self.firecheck_flags = TFL_FIRECHECK_AMMO_OWN | TFL_FIRECHECK_AMMO_OTHER | TFL_FIRECHECK_DISTANCES | TFL_FIRECHECK_DEAD; + self.shoot_flags = TFL_SHOOT_HITALLVALID; + self.aim_flags = TFL_AIM_NO; + self.track_flags = TFL_TRACK_NO; + + self.tur_head.scale = 0.75; + self.tur_head.avelocity = '0 50 0'; + + self.turret_firecheckfunc = turret_fusionreactor_firecheck; + - return TRUE; ++ return true; + } + case TR_PRECACHE: + { + precache_model ("models/turrets/base.md3"); + precache_model ("models/turrets/reactor.md3"); - return TRUE; ++ return true; + } + } + - return TRUE; ++ return true; +} + +#endif // SVQC +#ifdef CSQC +float t_fusionreactor(float req) +{ + switch(req) + { + case TR_SETUP: + { - return TRUE; ++ return true; + } + case TR_PRECACHE: + { - return TRUE; ++ return true; + } + } + - return TRUE; ++ return true; +} + +#endif // CSQC +#endif // REGISTER_TURRET diff --cc qcsrc/common/turrets/unit/hellion.qc index b097cae70,000000000..7db39d80a mode 100644,000000..100644 --- a/qcsrc/common/turrets/unit/hellion.qc +++ b/qcsrc/common/turrets/unit/hellion.qc @@@ -1,160 -1,0 +1,160 @@@ +#ifdef REGISTER_TURRET +REGISTER_TURRET( +/* TUR_##id */ HELLION, +/* function */ t_hellion, +/* spawnflags */ TUR_FLAG_SPLASH | TUR_FLAG_FASTPROJ | TUR_FLAG_PLAYER | TUR_FLAG_MISSILE, +/* mins,maxs */ '-32 -32 0', '32 32 64', +/* model */ "base.md3", +/* head_model */ "hellion.md3", +/* netname */ "hellion", +/* fullname */ _("Hellion Missile Turret") +); +#else +#ifdef SVQC +float autocvar_g_turrets_unit_hellion_shot_speed_gain; +float autocvar_g_turrets_unit_hellion_shot_speed_max; + +void turret_hellion_missile_think() +{ + vector olddir,newdir; + vector pre_pos; + float itime; + + self.nextthink = time + 0.05; + + olddir = normalize(self.velocity); + + if(self.max_health < time) + turret_projectile_explode(); + + // Enemy dead? just keep on the current heading then. + if ((self.enemy == world) || (self.enemy.deadflag != DEAD_NO)) + { + + // Make sure we dont return to tracking a respawned player + self.enemy = world; + + // Turn model + self.angles = vectoangles(self.velocity); + + if ( (vlen(self.origin - self.owner.origin)) > (self.owner.shot_radius * 5) ) + turret_projectile_explode(); + + // Accelerate + self.velocity = olddir * min(vlen(self.velocity) * (autocvar_g_turrets_unit_hellion_shot_speed_gain), (autocvar_g_turrets_unit_hellion_shot_speed_max)); + + UpdateCSQCProjectile(self); + + return; + } + + // Enemy in range? + if (vlen(self.origin - self.enemy.origin) < self.owner.shot_radius * 0.2) + turret_projectile_explode(); + + // Predict enemy position + itime = vlen(self.enemy.origin - self.origin) / vlen(self.velocity); + pre_pos = self.enemy.origin + self.enemy.velocity * itime; + + pre_pos = (pre_pos + self.enemy.origin) * 0.5; + + // Find out the direction to that place + newdir = normalize(pre_pos - self.origin); + + // Turn + newdir = normalize(olddir + newdir * 0.35); + + // Turn model + self.angles = vectoangles(self.velocity); + + // Accelerate + self.velocity = newdir * min(vlen(self.velocity) * (autocvar_g_turrets_unit_hellion_shot_speed_gain), (autocvar_g_turrets_unit_hellion_shot_speed_max)); + + if (itime < 0.05) + self.think = turret_projectile_explode; + + UpdateCSQCProjectile(self); +} + +void spawnfunc_turret_hellion() { if(!turret_initialize(TUR_HELLION)) remove(self); } + +float t_hellion(float req) +{ + switch(req) + { + case TR_ATTACK: + { + entity missile; + + if(self.tur_head.frame != 0) + self.tur_shotorg = gettaginfo(self.tur_head, gettagindex(self.tur_head, "tag_fire")); + else + self.tur_shotorg = gettaginfo(self.tur_head, gettagindex(self.tur_head, "tag_fire2")); + - missile = turret_projectile(W_Sound("rocket_fire"), 6, 10, DEATH_TURRET_HELLION, PROJECTILE_ROCKET, FALSE, FALSE); ++ missile = turret_projectile(W_Sound("rocket_fire"), 6, 10, DEATH_TURRET_HELLION, PROJECTILE_ROCKET, false, false); + te_explosion (missile.origin); + missile.think = turret_hellion_missile_think; + missile.nextthink = time; + missile.flags = FL_PROJECTILE; + missile.max_health = time + 9; + missile.tur_aimpos = randomvec() * 128; + missile.missile_flags = MIF_SPLASH | MIF_PROXY | MIF_GUIDED_HEAT; + self.tur_head.frame += 1; + - return TRUE; ++ return true; + } + case TR_THINK: + { + if (self.tur_head.frame != 0) + self.tur_head.frame += 1; + + if (self.tur_head.frame >= 7) + self.tur_head.frame = 0; + - return TRUE; ++ return true; + } + case TR_DEATH: + { - return TRUE; ++ return true; + } + case TR_SETUP: + { + self.aim_flags = TFL_AIM_SIMPLE; + self.target_select_flags = TFL_TARGETSELECT_LOS | TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_RANGELIMITS | TFL_TARGETSELECT_TEAMCHECK ; + self.firecheck_flags = TFL_FIRECHECK_DEAD | TFL_FIRECHECK_DISTANCES | TFL_FIRECHECK_TEAMCHECK | TFL_FIRECHECK_REFIRE | TFL_FIRECHECK_AFF | TFL_FIRECHECK_AMMO_OWN; + self.ammo_flags = TFL_AMMO_ROCKETS | TFL_AMMO_RECHARGE; + - return TRUE; ++ return true; + } + case TR_PRECACHE: + { + precache_model ("models/turrets/base.md3"); + precache_model ("models/turrets/hellion.md3"); - return TRUE; ++ return true; + } + } + - return TRUE; ++ return true; +} + +#endif // SVQC +#ifdef CSQC +float t_hellion(float req) +{ + switch(req) + { + case TR_SETUP: + { - return TRUE; ++ return true; + } + case TR_PRECACHE: + { - return TRUE; ++ return true; + } + } + - return TRUE; ++ return true; +} + +#endif // CSQC +#endif // REGISTER_TURRET diff --cc qcsrc/common/turrets/unit/hk.qc index 4fc8dcbe5,000000000..f4a36f907 mode 100644,000000..100644 --- a/qcsrc/common/turrets/unit/hk.qc +++ b/qcsrc/common/turrets/unit/hk.qc @@@ -1,361 -1,0 +1,361 @@@ +#ifdef REGISTER_TURRET +REGISTER_TURRET( +/* TUR_##id */ HK, +/* function */ t_hk, +/* spawnflags */ TUR_FLAG_SPLASH | TUR_FLAG_MEDPROJ | TUR_FLAG_PLAYER | TUR_FLAG_RECIEVETARGETS, +/* mins,maxs */ '-32 -32 0', '32 32 64', +/* model */ "base.md3", +/* head_model */ "hk.md3", +/* netname */ "hk", +/* fullname */ _("Hunter-Killer Turret") +); +#else +#ifdef SVQC +float autocvar_g_turrets_unit_hk_shot_speed; +float autocvar_g_turrets_unit_hk_shot_speed_accel; +float autocvar_g_turrets_unit_hk_shot_speed_accel2; +float autocvar_g_turrets_unit_hk_shot_speed_decel; +float autocvar_g_turrets_unit_hk_shot_speed_max; +float autocvar_g_turrets_unit_hk_shot_speed_turnrate; + +//#define TURRET_DEBUG_HK + +#ifdef TURRET_DEBUG_HK +.float atime; +#endif + +float hk_is_valid_target(entity e_target) +{ + if (e_target == world) + return 0; + + // If only this was used more.. + if (e_target.flags & FL_NOTARGET) + return 0; + + // Cant touch this + if ((e_target.takedamage == DAMAGE_NO) || (e_target.health < 0)) + return 0; + + // player + if (IS_CLIENT(e_target)) + { + if (self.owner.target_select_playerbias < 0) + return 0; + + if (e_target.deadflag != DEAD_NO) + return 0; + } + + // Missile + if ((e_target.flags & FL_PROJECTILE) && (self.owner.target_select_missilebias < 0)) + return 0; + + // Team check + if ((e_target.team == self.owner.team) || (self.owner.team == e_target.owner.team)) + return 0; + + return 1; +} + +void turret_hk_missile_think() +{ + vector vu, vd, vf, vl, vr, ve; // Vector (direction) + float fu, fd, ff, fl, fr, fe; // Fraction to solid + vector olddir,wishdir,newdir; // Final direction + float lt_for; // Length of Trace FORwrad + float lt_seek; // Length of Trace SEEK (left, right, up down) + float pt_seek; // Pitch of Trace SEEK (How mutch to angele left, right up, down trace towards v_forward) + vector pre_pos; + float myspeed; + entity e; + float ad,edist; + + self.nextthink = time + self.ticrate; + + //if (self.cnt < time) + // turret_hk_missile_explode(); + + if (self.enemy.deadflag != DEAD_NO) + self.enemy = world; + + // Pick the closest valid target. + if (!self.enemy) + { + e = findradius(self.origin, 5000); + while (e) + { + if (hk_is_valid_target(e)) + { + if (!self.enemy) + self.enemy = e; + else + if (vlen(self.origin - e.origin) < vlen(self.origin - self.enemy.origin)) + self.enemy = e; + } + e = e.chain; + } + } + + self.angles = vectoangles(self.velocity); + self.angles_x = self.angles_x * -1; + makevectors(self.angles); + self.angles_x = self.angles_x * -1; + + if (self.enemy) + { + edist = vlen(self.origin - self.enemy.origin); + // Close enougth to do decent damage? + if ( edist <= (self.owner.shot_radius * 0.25) ) + { + turret_projectile_explode(); + return; + } + + // Get data on enemy position + pre_pos = self.enemy.origin + + self.enemy.velocity * + min((vlen(self.enemy.origin - self.origin) / vlen(self.velocity)),0.5); + - traceline(self.origin, pre_pos,TRUE,self.enemy); ++ traceline(self.origin, pre_pos,true,self.enemy); + ve = normalize(pre_pos - self.origin); + fe = trace_fraction; + + } + else + { + edist = 0; + ve = '0 0 0'; + fe = 0; + } + + if ((fe != 1) || (self.enemy == world) || (edist > 1000)) + { + myspeed = vlen(self.velocity); + + lt_for = myspeed * 3; + lt_seek = myspeed * 2.95; + + // Trace forward - traceline(self.origin, self.origin + v_forward * lt_for,FALSE,self); ++ traceline(self.origin, self.origin + v_forward * lt_for,false,self); + vf = trace_endpos; + ff = trace_fraction; + + // Find angular offset + ad = vlen(vectoangles(normalize(self.enemy.origin - self.origin)) - self.angles); + + // To close to something, Slow down! + if ( ((ff < 0.7) || (ad > 4)) && (myspeed > (autocvar_g_turrets_unit_hk_shot_speed)) ) + myspeed = max(myspeed * (autocvar_g_turrets_unit_hk_shot_speed_decel), (autocvar_g_turrets_unit_hk_shot_speed)); + + // Failry clear, accelerate. + if ( (ff > 0.7) && (myspeed < (autocvar_g_turrets_unit_hk_shot_speed_max)) ) + myspeed = min(myspeed * (autocvar_g_turrets_unit_hk_shot_speed_accel), (autocvar_g_turrets_unit_hk_shot_speed_max)); + + // Setup trace pitch + pt_seek = 1 - ff; + pt_seek = bound(0.15,pt_seek,0.8); + if (ff < 0.5) pt_seek = 1; + + // Trace left - traceline(self.origin, self.origin + (-1 * (v_right * pt_seek) + (v_forward * ff)) * lt_seek,FALSE,self); ++ traceline(self.origin, self.origin + (-1 * (v_right * pt_seek) + (v_forward * ff)) * lt_seek,false,self); + vl = trace_endpos; + fl = trace_fraction; + + // Trace right - traceline(self.origin, self.origin + ((v_right * pt_seek) + (v_forward * ff)) * lt_seek ,FALSE,self); ++ traceline(self.origin, self.origin + ((v_right * pt_seek) + (v_forward * ff)) * lt_seek ,false,self); + vr = trace_endpos; + fr = trace_fraction; + + // Trace up - traceline(self.origin, self.origin + ((v_up * pt_seek) + (v_forward * ff)) * lt_seek ,FALSE,self); ++ traceline(self.origin, self.origin + ((v_up * pt_seek) + (v_forward * ff)) * lt_seek ,false,self); + vu = trace_endpos; + fu = trace_fraction; + + // Trace down - traceline(self.origin, self.origin + (-1 * (v_up * pt_seek) + (v_forward * ff)) * lt_seek ,FALSE,self); ++ traceline(self.origin, self.origin + (-1 * (v_up * pt_seek) + (v_forward * ff)) * lt_seek ,false,self); + vd = trace_endpos; + fd = trace_fraction; + + vl = normalize(vl - self.origin); + vr = normalize(vr - self.origin); + vu = normalize(vu - self.origin); + vd = normalize(vd - self.origin); + + // Panic tresh passed, find a single direction and turn as hard as we can + if (pt_seek == 1) + { + wishdir = v_right; + if (fl > fr) wishdir = -1 * v_right; + if (fu > fl) wishdir = v_up; + if (fd > fu) wishdir = -1 * v_up; + } + else + { + // Normalize our trace vectors to make a smooth path + wishdir = normalize( (vl * fl) + (vr * fr) + (vu * fu) + (vd * fd) ); + } + + if (self.enemy) + { + if (fe < 0.1) fe = 0.1; // Make sure we always try to move sligtly towards our target + wishdir = (wishdir * (1 - fe)) + (ve * fe); + } + } + else + { + // Got a clear path to target, speed up fast (if not at full speed) and go straight for it. + myspeed = vlen(self.velocity); + if (myspeed < (autocvar_g_turrets_unit_hk_shot_speed_max)) + myspeed = min(myspeed * (autocvar_g_turrets_unit_hk_shot_speed_accel2),(autocvar_g_turrets_unit_hk_shot_speed_max)); + + wishdir = ve; + } + + if ((myspeed > (autocvar_g_turrets_unit_hk_shot_speed)) && (self.cnt > time)) + myspeed = min(myspeed * (autocvar_g_turrets_unit_hk_shot_speed_accel2),(autocvar_g_turrets_unit_hk_shot_speed_max)); + + // Ranoutagazfish? + if (self.cnt < time) + { + self.cnt = time + 0.25; + self.nextthink = 0; + self.movetype = MOVETYPE_BOUNCE; + return; + } + + // Calculate new heading + olddir = normalize(self.velocity); + newdir = normalize(olddir + wishdir * (autocvar_g_turrets_unit_hk_shot_speed_turnrate)); + + // Set heading & speed + self.velocity = newdir * myspeed; + + // Align model with new heading + self.angles = vectoangles(self.velocity); + + +#ifdef TURRET_DEBUG_HK + //if(self.atime < time) { + if ((fe <= 0.99)||(edist > 1000)) + { + te_lightning2(world,self.origin, self.origin + vr * lt_seek); + te_lightning2(world,self.origin, self.origin + vl * lt_seek); + te_lightning2(world,self.origin, self.origin + vu * lt_seek); + te_lightning2(world,self.origin, self.origin + vd * lt_seek); + te_lightning2(world,self.origin, vf); + } + else + { + te_lightning2(world,self.origin, self.enemy.origin); + } + bprint("Speed: ", ftos(rint(myspeed)), "\n"); + bprint("Trace to solid: ", ftos(rint(ff * 100)), "%\n"); + bprint("Trace to target:", ftos(rint(fe * 100)), "%\n"); + self.atime = time + 0.2; + //} +#endif + + UpdateCSQCProjectile(self); +} + +float turret_hk_addtarget(entity e_target,entity e_sender) +{ + if (e_target) + { + if (turret_validate_target(self,e_target,self.target_validate_flags) > 0) + { + self.enemy = e_target; + return 1; + } + } + + return 0; +} + +void spawnfunc_turret_hk() { if(!turret_initialize(TUR_HK)) remove(self); } + +float t_hk(float req) +{ + switch(req) + { + case TR_ATTACK: + { + entity missile; + - missile = turret_projectile(W_Sound("rocket_fire"), 6, 10, DEATH_TURRET_HK, PROJECTILE_ROCKET, FALSE, FALSE); ++ missile = turret_projectile(W_Sound("rocket_fire"), 6, 10, DEATH_TURRET_HK, PROJECTILE_ROCKET, false, false); + te_explosion (missile.origin); + + missile.think = turret_hk_missile_think; + missile.nextthink = time + 0.25; + missile.movetype = MOVETYPE_BOUNCEMISSILE; + missile.velocity = self.tur_shotdir_updated * (self.shot_speed * 0.75); + missile.angles = vectoangles(missile.velocity); + missile.cnt = time + 30; + missile.ticrate = max(autocvar_sys_ticrate, 0.05); + missile.missile_flags = MIF_SPLASH | MIF_PROXY | MIF_GUIDED_AI; + + if (self.tur_head.frame == 0) + self.tur_head.frame = self.tur_head.frame + 1; + - return TRUE; ++ return true; + } + case TR_THINK: + { + if (self.tur_head.frame != 0) + self.tur_head.frame = self.tur_head.frame + 1; + + if (self.tur_head.frame > 5) + self.tur_head.frame = 0; + - return TRUE; ++ return true; + } + case TR_DEATH: + { - return TRUE; ++ return true; + } + case TR_SETUP: + { + self.ammo_flags = TFL_AMMO_ROCKETS | TFL_AMMO_RECHARGE; + self.aim_flags = TFL_AIM_SIMPLE; + self.target_select_flags = TFL_TARGETSELECT_LOS | TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_TRIGGERTARGET | TFL_TARGETSELECT_RANGELIMITS | TFL_TARGETSELECT_TEAMCHECK; + self.firecheck_flags = TFL_FIRECHECK_DEAD | TFL_FIRECHECK_TEAMCHECK | TFL_FIRECHECK_REFIRE | TFL_FIRECHECK_AFF; + self.shoot_flags = TFL_SHOOT_CLEARTARGET; + self.target_validate_flags = TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_TEAMCHECK; + + self.turret_addtarget = turret_hk_addtarget; + - return TRUE; ++ return true; + } + case TR_PRECACHE: + { + precache_model ("models/turrets/base.md3"); + precache_model ("models/turrets/hk.md3"); - return TRUE; ++ return true; + } + } + - return TRUE; ++ return true; +} + +#endif // SVQC +#ifdef CSQC +float t_hk(float req) +{ + switch(req) + { + case TR_SETUP: + { - return TRUE; ++ return true; + } + case TR_PRECACHE: + { - return TRUE; ++ return true; + } + } + - return TRUE; ++ return true; +} + +#endif // CSQC +#endif // REGISTER_TURRET diff --cc qcsrc/common/turrets/unit/machinegun.qc index 57267ef86,000000000..a66fb0723 mode 100644,000000..100644 --- a/qcsrc/common/turrets/unit/machinegun.qc +++ b/qcsrc/common/turrets/unit/machinegun.qc @@@ -1,79 -1,0 +1,81 @@@ +#ifdef REGISTER_TURRET +REGISTER_TURRET( +/* TUR_##id */ MACHINEGUN, +/* function */ t_machinegun, +/* spawnflags */ TUR_FLAG_PLAYER, +/* mins,maxs */ '-32 -32 0', '32 32 64', +/* model */ "base.md3", +/* head_model */ "machinegun.md3", +/* netname */ "machinegun", +/* fullname */ _("Machinegun Turret") +); +#else +#ifdef SVQC ++#include "../../vehicles/vehicles.qh" ++ +void spawnfunc_turret_machinegun() { if(!turret_initialize(TUR_MACHINEGUN)) remove(self); } + +float t_machinegun(float req) +{ + switch(req) + { + case TR_ATTACK: + { + fireBullet (self.tur_shotorg, self.tur_shotdir_updated, self.shot_spread, 0, self.shot_dmg, self.shot_force, DEATH_TURRET_MACHINEGUN, 0); + + W_MachineGun_MuzzleFlash(); + setattachment(self.muzzle_flash, self.tur_head, "tag_fire"); + - return TRUE; ++ return true; + } + case TR_THINK: + { - return TRUE; ++ return true; + } + case TR_DEATH: + { - return TRUE; ++ return true; + } + case TR_SETUP: + { + self.damage_flags |= TFL_DMG_HEADSHAKE; + self.target_select_flags = TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_RANGELIMITS | TFL_TARGETSELECT_TEAMCHECK; + self.ammo_flags = TFL_AMMO_BULLETS | TFL_AMMO_RECHARGE | TFL_AMMO_RECIEVE; + self.aim_flags = TFL_AIM_LEAD | TFL_AIM_SHOTTIMECOMPENSATE; + self.turret_flags |= TUR_FLAG_HITSCAN; + - return TRUE; ++ return true; + } + case TR_PRECACHE: + { + precache_model ("models/turrets/base.md3"); + precache_model ("models/turrets/machinegun.md3"); + precache_sound (W_Sound("uzi_fire")); - return TRUE; ++ return true; + } + } + - return TRUE; ++ return true; +} + +#endif // SVQC +#ifdef CSQC +float t_machinegun(float req) +{ + switch(req) + { + case TR_SETUP: + { - return TRUE; ++ return true; + } + case TR_PRECACHE: + { - return TRUE; ++ return true; + } + } + - return TRUE; ++ return true; +} + +#endif // CSQC +#endif // REGISTER_TURRET diff --cc qcsrc/common/turrets/unit/mlrs.qc index 1c979961c,000000000..9bc20f64d mode 100644,000000..100644 --- a/qcsrc/common/turrets/unit/mlrs.qc +++ b/qcsrc/common/turrets/unit/mlrs.qc @@@ -1,90 -1,0 +1,90 @@@ +#ifdef REGISTER_TURRET +REGISTER_TURRET( +/* TUR_##id */ MLRS, +/* function */ t_mlrs, +/* spawnflags */ TUR_FLAG_SPLASH | TUR_FLAG_MEDPROJ | TUR_FLAG_PLAYER, +/* mins,maxs */ '-32 -32 0', '32 32 64', +/* model */ "base.md3", +/* head_model */ "mlrs.md3", +/* netname */ "mlrs", +/* fullname */ _("MLRS Turret") +); +#else +#ifdef SVQC +void spawnfunc_turret_mlrs() { if(!turret_initialize(TUR_MLRS)) remove(self); } + +float t_mlrs(float req) +{ + switch(req) + { + case TR_ATTACK: + { + entity missile; + + turret_tag_fire_update(); - missile = turret_projectile(W_Sound("rocket_fire"), 6, 10, DEATH_TURRET_MLRS, PROJECTILE_ROCKET, TRUE, TRUE); ++ missile = turret_projectile(W_Sound("rocket_fire"), 6, 10, DEATH_TURRET_MLRS, PROJECTILE_ROCKET, true, true); + missile.nextthink = time + max(self.tur_impacttime,(self.shot_radius * 2) / self.shot_speed); + missile.missile_flags = MIF_SPLASH; + te_explosion (missile.origin); + - return TRUE; ++ return true; + } + case TR_THINK: + { + // 0 = full, 6 = empty + self.tur_head.frame = bound(0, 6 - floor(0.1 + self.ammo / self.shot_dmg), 6); + if(self.tur_head.frame < 0) + { + dprint("ammo:",ftos(self.ammo),"\n"); + dprint("shot_dmg:",ftos(self.shot_dmg),"\n"); + } + - return TRUE; ++ return true; + } + case TR_DEATH: + { - return TRUE; ++ return true; + } + case TR_SETUP: + { + self.ammo_flags = TFL_AMMO_ROCKETS | TFL_AMMO_RECHARGE; + self.aim_flags = TFL_AIM_LEAD | TFL_AIM_SHOTTIMECOMPENSATE; + + self.damage_flags |= TFL_DMG_HEADSHAKE; + self.shoot_flags |= TFL_SHOOT_VOLLYALWAYS; + self.volly_counter = self.shot_volly; + - return TRUE; ++ return true; + } + case TR_PRECACHE: + { + precache_model ("models/turrets/base.md3"); + precache_model ("models/turrets/mlrs.md3"); - return TRUE; ++ return true; + } + } + - return TRUE; ++ return true; +} + +#endif // SVQC +#ifdef CSQC +float t_mlrs(float req) +{ + switch(req) + { + case TR_SETUP: + { - return TRUE; ++ return true; + } + case TR_PRECACHE: + { - return TRUE; ++ return true; + } + } + - return TRUE; ++ return true; +} + +#endif // CSQC +#endif // REGISTER_TURRET diff --cc qcsrc/common/turrets/unit/phaser.qc index 4fd0a6581,000000000..7a066472e mode 100644,000000..100644 --- a/qcsrc/common/turrets/unit/phaser.qc +++ b/qcsrc/common/turrets/unit/phaser.qc @@@ -1,173 -1,0 +1,173 @@@ +#ifdef REGISTER_TURRET +REGISTER_TURRET( +/* TUR_##id */ PHASER, +/* function */ t_phaser, +/* spawnflags */ TUR_FLAG_SNIPER | TUR_FLAG_HITSCAN | TUR_FLAG_PLAYER, +/* mins,maxs */ '-32 -32 0', '32 32 64', +/* model */ "base.md3", +/* head_model */ "phaser.md3", +/* netname */ "phaser", +/* fullname */ _("Phaser Cannon") +); +#else +#ifdef SVQC +.float fireflag; + +float turret_phaser_firecheck() +{ + if (self.fireflag != 0) return 0; + return turret_firecheck(); +} + +void beam_think() +{ + if ((time > self.cnt) || (self.owner.deadflag != DEAD_NO)) + { + self.owner.attack_finished_single = time + self.owner.shot_refire; + self.owner.fireflag = 2; + self.owner.tur_head.frame = 10; + sound (self, CH_SHOTS_SINGLE, "misc/null.wav", VOL_BASE, ATTEN_NORM); + remove(self); + return; + } + + turret_do_updates(self.owner); + + if (time - self.shot_spread > 0) + { + self.shot_spread = time + 2; + sound (self, CH_SHOTS_SINGLE, "turrets/phaser.wav", VOL_BASE, ATTEN_NORM); + } + + + self.nextthink = time + self.ticrate; + + self.owner.attack_finished_single = time + frametime; + entity oldself; + oldself = self; + self = self.owner; + FireImoBeam ( self.tur_shotorg, + self.tur_shotorg + self.tur_shotdir_updated * self.target_range, + '-1 -1 -1' * self.shot_radius, + '1 1 1' * self.shot_radius, + self.shot_force, + oldself.shot_dmg, + 0.75, + DEATH_TURRET_PHASER); + self = oldself; + self.scale = vlen(self.owner.tur_shotorg - trace_endpos) / 256; + +} + +void spawnfunc_turret_phaser() { if(!turret_initialize(TUR_PHASER)) remove(self); } + +float t_phaser(float req) +{ + switch(req) + { + case TR_ATTACK: + { + entity beam; + + beam = spawn(); + beam.ticrate = 0.1; //autocvar_sys_ticrate; + setmodel(beam,"models/turrets/phaser_beam.md3"); + beam.effects = EF_LOWPRECISION; + beam.solid = SOLID_NOT; + beam.think = beam_think; + beam.cnt = time + self.shot_speed; + beam.shot_spread = time + 2; + beam.nextthink = time; + beam.owner = self; + beam.shot_dmg = self.shot_dmg / (self.shot_speed / beam.ticrate); + beam.scale = self.target_range / 256; + beam.movetype = MOVETYPE_NONE; + beam.enemy = self.enemy; - beam.bot_dodge = TRUE; ++ beam.bot_dodge = true; + beam.bot_dodgerating = beam.shot_dmg; + sound (beam, CH_SHOTS_SINGLE, "turrets/phaser.wav", VOL_BASE, ATTEN_NORM); + self.fireflag = 1; + + beam.attack_finished_single = self.attack_finished_single; + self.attack_finished_single = time; // + autocvar_sys_ticrate; + + setattachment(beam,self.tur_head,"tag_fire"); + + soundat (self, trace_endpos, CH_SHOTS, W_Sound("neximpact"), VOL_BASE, ATTEN_NORM); + + if (self.tur_head.frame == 0) + self.tur_head.frame = 1; + - return TRUE; ++ return true; + } + case TR_THINK: + { + if (self.tur_head.frame != 0) + { + if (self.fireflag == 1) + { + if (self.tur_head.frame == 10) + self.tur_head.frame = 1; + else + self.tur_head.frame = self.tur_head.frame +1; + } + else if (self.fireflag == 2 ) + { + self.tur_head.frame = self.tur_head.frame +1; + if (self.tur_head.frame == 15) + { + self.tur_head.frame = 0; + self.fireflag = 0; + } + } + } + - return TRUE; ++ return true; + } + case TR_DEATH: + { - return TRUE; ++ return true; + } + case TR_SETUP: + { + self.ammo_flags = TFL_AMMO_ENERGY | TFL_AMMO_RECHARGE | TFL_AMMO_RECIEVE; + self.aim_flags = TFL_AIM_LEAD; + + self.turret_firecheckfunc = turret_phaser_firecheck; + - return TRUE; ++ return true; + } + case TR_PRECACHE: + { + precache_model ("models/turrets/base.md3"); + precache_model ("models/turrets/phaser.md3"); + precache_model ("models/turrets/phaser_beam.md3"); + precache_sound ("turrets/phaser.wav"); - return TRUE; ++ return true; + } + } + - return TRUE; ++ return true; +} + +#endif // SVQC +#ifdef CSQC +float t_phaser(float req) +{ + switch(req) + { + case TR_SETUP: + { - return TRUE; ++ return true; + } + case TR_PRECACHE: + { - return TRUE; ++ return true; + } + } + - return TRUE; ++ return true; +} + +#endif // CSQC +#endif // REGISTER_TURRET diff --cc qcsrc/common/turrets/unit/plasma.qc index 8f925aa40,000000000..4d3977e1d mode 100644,000000..100644 --- a/qcsrc/common/turrets/unit/plasma.qc +++ b/qcsrc/common/turrets/unit/plasma.qc @@@ -1,118 -1,0 +1,120 @@@ +#ifdef REGISTER_TURRET +REGISTER_TURRET( +/* TUR_##id */ PLASMA, +/* function */ t_plasma, +/* spawnflags */ TUR_FLAG_SPLASH | TUR_FLAG_MEDPROJ | TUR_FLAG_PLAYER, +/* mins,maxs */ '-32 -32 0', '32 32 64', +/* model */ "base.md3", +/* head_model */ "plasma.md3", +/* netname */ "plasma", +/* fullname */ _("Plasma Cannon") +); +#else +#ifdef SVQC ++#include "../../effects.qh" ++ +void spawnfunc_turret_plasma() { if(!turret_initialize(TUR_PLASMA)) remove(self); } + +float t_plasma(float req) +{ + switch(req) + { + case TR_ATTACK: + { + if(g_instagib) + { + float flying; + flying = IsFlying(self); // do this BEFORE to make the trace values from FireRailgunBullet last + + FireRailgunBullet (self.tur_shotorg, self.tur_shotorg + self.tur_shotdir_updated * MAX_SHOT_DISTANCE, 10000000000, + 800, 0, 0, 0, 0, DEATH_TURRET_PLASMA); + + Send_Effect(EFFECT_VORTEX_MUZZLEFLASH, self.tur_shotorg, self.tur_shotdir_updated * 1000, 1); + + // teamcolor / hit beam effect + vector v; + float f; + v = WarpZone_UnTransformOrigin(WarpZone_trace_transform, trace_endpos); + switch(self.team) + { + case NUM_TEAM_1: f = (damage_goodhits) ? EFFECT_VAPORIZER_RED_HIT : EFFECT_VAPORIZER_RED; break; + case NUM_TEAM_2: f = (damage_goodhits) ? EFFECT_VAPORIZER_BLUE_HIT : EFFECT_VAPORIZER_BLUE; break; + case NUM_TEAM_3: f = (damage_goodhits) ? EFFECT_VAPORIZER_YELLOW_HIT : EFFECT_VAPORIZER_YELLOW; break; + case NUM_TEAM_4: f = (damage_goodhits) ? EFFECT_VAPORIZER_PINK_HIT : EFFECT_VAPORIZER_PINK; break; + default: f = (damage_goodhits) ? EFFECT_VAPORIZER_NEUTRAL_HIT : EFFECT_VAPORIZER_NEUTRAL; break; + } + + Send_Effect(f, self.tur_shotorg, v, 0); + + if (self.tur_head.frame == 0) + self.tur_head.frame = 1; + } + else + { - entity missile = turret_projectile(W_Sound("hagar_fire"), 1, 0, DEATH_TURRET_PLASMA, PROJECTILE_ELECTRO_BEAM, TRUE, TRUE); ++ entity missile = turret_projectile(W_Sound("hagar_fire"), 1, 0, DEATH_TURRET_PLASMA, PROJECTILE_ELECTRO_BEAM, true, true); + missile.missile_flags = MIF_SPLASH; + + Send_Effect(EFFECT_LASER_MUZZLEFLASH, self.tur_shotorg, self.tur_shotdir_updated * 1000, 1); + if (self.tur_head.frame == 0) + self.tur_head.frame = 1; + } + - return TRUE; ++ return true; + } + case TR_THINK: + { + if (self.tur_head.frame != 0) + self.tur_head.frame = self.tur_head.frame + 1; + + if (self.tur_head.frame > 5) + self.tur_head.frame = 0; + - return TRUE; ++ return true; + } + case TR_DEATH: + { - return TRUE; ++ return true; + } + case TR_SETUP: + { + self.ammo_flags = TFL_AMMO_ENERGY | TFL_AMMO_RECHARGE | TFL_AMMO_RECIEVE; + self.damage_flags |= TFL_DMG_HEADSHAKE; + self.firecheck_flags |= TFL_FIRECHECK_AFF; + self.aim_flags = TFL_AIM_LEAD | TFL_AIM_SHOTTIMECOMPENSATE | TFL_AIM_SPLASH; + + turret_do_updates(self); + - return TRUE; ++ return true; + } + case TR_PRECACHE: + { + precache_model ("models/turrets/base.md3"); + precache_model ("models/turrets/plasma.md3"); - return TRUE; ++ return true; + } + } + - return TRUE; ++ return true; +} + +#endif // SVQC +#ifdef CSQC +float t_plasma(float req) +{ + switch(req) + { + case TR_SETUP: + { - return TRUE; ++ return true; + } + case TR_PRECACHE: + { - return TRUE; ++ return true; + } + } + - return TRUE; ++ return true; +} + +#endif // CSQC +#endif // REGISTER_TURRET diff --cc qcsrc/common/turrets/unit/plasma_dual.qc index 22f272b21,000000000..110a9baf1 mode 100644,000000..100644 --- a/qcsrc/common/turrets/unit/plasma_dual.qc +++ b/qcsrc/common/turrets/unit/plasma_dual.qc @@@ -1,116 -1,0 +1,118 @@@ +#ifdef REGISTER_TURRET +REGISTER_TURRET( +/* TUR_##id */ PLASMA_DUAL, +/* function */ t_plasma_dual, +/* spawnflags */ TUR_FLAG_SPLASH | TUR_FLAG_MEDPROJ | TUR_FLAG_PLAYER, +/* mins,maxs */ '-32 -32 0', '32 32 64', +/* model */ "base.md3", +/* head_model */ "plasmad.md3", +/* netname */ "plasma_dual", +/* fullname */ _("Dual Plasma Cannon") +); +#else +#ifdef SVQC ++#include "../../effects.qh" ++ +void spawnfunc_turret_plasma_dual() { if(!turret_initialize(TUR_PLASMA_DUAL)) remove(self); } + +float t_plasma_dual(float req) +{ + switch(req) + { + case TR_ATTACK: + { + if(g_instagib) + { + float flying; + flying = IsFlying(self); // do this BEFORE to make the trace values from FireRailgunBullet last + + FireRailgunBullet (self.tur_shotorg, self.tur_shotorg + self.tur_shotdir_updated * MAX_SHOT_DISTANCE, 10000000000, + 800, 0, 0, 0, 0, DEATH_TURRET_PLASMA); + + + Send_Effect(EFFECT_VORTEX_MUZZLEFLASH, self.tur_shotorg, self.tur_shotdir_updated * 1000, 1); + + // teamcolor / hit beam effect + vector v; + v = WarpZone_UnTransformOrigin(WarpZone_trace_transform, trace_endpos); + float f; + switch(self.team) + { + case NUM_TEAM_1: f = (damage_goodhits) ? EFFECT_VAPORIZER_RED_HIT : EFFECT_VAPORIZER_RED; break; + case NUM_TEAM_2: f = (damage_goodhits) ? EFFECT_VAPORIZER_BLUE_HIT : EFFECT_VAPORIZER_BLUE; break; + case NUM_TEAM_3: f = (damage_goodhits) ? EFFECT_VAPORIZER_YELLOW_HIT : EFFECT_VAPORIZER_YELLOW; break; + case NUM_TEAM_4: f = (damage_goodhits) ? EFFECT_VAPORIZER_PINK_HIT : EFFECT_VAPORIZER_PINK; break; + default: f = (damage_goodhits) ? EFFECT_VAPORIZER_NEUTRAL_HIT : EFFECT_VAPORIZER_NEUTRAL; break; + } + + Send_Effect(f, self.tur_shotorg, v, 0); + + self.tur_head.frame += 1; + } + else + { - entity missile = turret_projectile(W_Sound("hagar_fire"), 1, 0, DEATH_TURRET_PLASMA, PROJECTILE_ELECTRO_BEAM, TRUE, TRUE); ++ entity missile = turret_projectile(W_Sound("hagar_fire"), 1, 0, DEATH_TURRET_PLASMA, PROJECTILE_ELECTRO_BEAM, true, true); + missile.missile_flags = MIF_SPLASH; + Send_Effect(EFFECT_LASER_MUZZLEFLASH, self.tur_shotorg, self.tur_shotdir_updated * 1000, 1); + self.tur_head.frame += 1; + } + - return TRUE; ++ return true; + } + case TR_THINK: + { + if ((self.tur_head.frame != 0) && (self.tur_head.frame != 3)) + self.tur_head.frame = self.tur_head.frame + 1; + + if (self.tur_head.frame > 6) + self.tur_head.frame = 0; + - return TRUE; ++ return true; + } + case TR_DEATH: + { - return TRUE; ++ return true; + } + case TR_SETUP: + { + self.ammo_flags = TFL_AMMO_ENERGY | TFL_AMMO_RECHARGE | TFL_AMMO_RECIEVE; + self.damage_flags |= TFL_DMG_HEADSHAKE; + self.firecheck_flags |= TFL_FIRECHECK_AFF; + self.aim_flags = TFL_AIM_LEAD | TFL_AIM_SHOTTIMECOMPENSATE | TFL_AIM_SPLASH; + + turret_do_updates(self); + - return TRUE; ++ return true; + } + case TR_PRECACHE: + { + precache_model ("models/turrets/base.md3"); + precache_model ("models/turrets/plasmad.md3"); - return TRUE; ++ return true; + } + } + - return TRUE; ++ return true; +} + +#endif // SVQC +#ifdef CSQC +float t_plasma_dual(float req) +{ + switch(req) + { + case TR_SETUP: + { - return TRUE; ++ return true; + } + case TR_PRECACHE: + { - return TRUE; ++ return true; + } + } + - return TRUE; ++ return true; +} + +#endif // CSQC +#endif // REGISTER_TURRET diff --cc qcsrc/common/turrets/unit/tesla.qc index 2878c3187,000000000..61a9f5e30 mode 100644,000000..100644 --- a/qcsrc/common/turrets/unit/tesla.qc +++ b/qcsrc/common/turrets/unit/tesla.qc @@@ -1,217 -1,0 +1,217 @@@ +#ifdef REGISTER_TURRET +REGISTER_TURRET( +/* TUR_##id */ TESLA, +/* function */ t_tesla, +/* spawnflags */ TUR_FLAG_HITSCAN | TUR_FLAG_PLAYER | TUR_FLAG_MISSILE, +/* mins,maxs */ '-60 -60 0', '60 60 128', +/* model */ "tesla_base.md3", +/* head_model */ "tesla_head.md3", +/* netname */ "tesla", +/* fullname */ _("Tesla Coil") +); +#else +#ifdef SVQC +entity toast(entity from, float range, float damage) +{ + entity e; + entity etarget = world; + float d,dd; + float r; + + dd = range + 1; + + e = findradius(from.origin,range); + while (e) + { + if ((e.railgunhit != 1) && (e != from)) + { + r = turret_validate_target(self,e,self.target_validate_flags); + if (r > 0) + { + traceline(from.origin,0.5 * (e.absmin + e.absmax),MOVE_WORLDONLY,from); + if (trace_fraction == 1.0) + { + d = vlen(e.origin - from.origin); + if (d < dd) + { + dd = d; + etarget = e; + } + } + } + } + e = e.chain; + } + + if (etarget) + { + te_csqc_lightningarc(from.origin,etarget.origin); + Damage(etarget, self, self, damage, DEATH_TURRET_TESLA, etarget.origin, '0 0 0'); + etarget.railgunhit = 1; + } + + return etarget; +} + +float turret_tesla_firecheck() +{ + // g_turrets_targetscan_maxdelay forces a target re-scan at least this often + float do_target_scan = 0; + + if((self.target_select_time + autocvar_g_turrets_targetscan_maxdelay) < time) + do_target_scan = 1; + + // Old target (if any) invalid? + if(self.target_validate_time < time) + if (turret_validate_target(self, self.enemy, self.target_validate_flags) <= 0) + { + self.enemy = world; + self.target_validate_time = time + 0.5; + do_target_scan = 1; + } + + // But never more often then g_turrets_targetscan_mindelay! + if (self.target_select_time + autocvar_g_turrets_targetscan_mindelay > time) + do_target_scan = 0; + + if(do_target_scan) + { + self.enemy = turret_select_target(); + self.target_select_time = time; + } + + if(!turret_firecheck()) + return 0; + + if(self.enemy) + return 1; + + return 0; +} + +void spawnfunc_turret_tesla() { if(!turret_initialize(TUR_TESLA)) remove(self); } + +float t_tesla(float req) +{ + switch(req) + { + case TR_ATTACK: + { + entity e, t; + float d, r, i; + + d = self.shot_dmg; + r = self.target_range; + e = spawn(); + setorigin(e,self.tur_shotorg); + + self.target_validate_flags = TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_MISSILES | TFL_TARGETSELECT_RANGELIMITS | TFL_TARGETSELECT_TEAMCHECK; + + t = toast(e,r,d); + remove(e); + - if (t == world) return TRUE; ++ if (t == world) return true; + + self.target_validate_flags = TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_MISSILES | TFL_TARGETSELECT_TEAMCHECK; + + self.attack_finished_single = time + self.shot_refire; + for (i = 0; i < 10; ++i) + { + d *= 0.75; + r *= 0.85; + t = toast(t, r, d); + if (t == world) break; + + } + + e = findchainfloat(railgunhit, 1); + while (e) + { + e.railgunhit = 0; + e = e.chain; + } + - return TRUE; ++ return true; + } + case TR_THINK: + { + if(!self.active) + { + self.tur_head.avelocity = '0 0 0'; - return TRUE; ++ return true; + } + + if(self.ammo < self.shot_dmg) + { + self.tur_head.avelocity = '0 45 0' * (self.ammo / self.shot_dmg); + } + else + { + self.tur_head.avelocity = '0 180 0' * (self.ammo / self.shot_dmg); + + if(self.attack_finished_single > time) - return TRUE; ++ return true; + + float f; + f = (self.ammo / self.ammo_max); + f = f * f; + if(f > random()) + if(random() < 0.1) + te_csqc_lightningarc(self.tur_shotorg,self.tur_shotorg + (randomvec() * 350)); + } + - return TRUE; ++ return true; + } + case TR_DEATH: + { - return TRUE; ++ return true; + } + case TR_SETUP: + { + self.target_validate_flags = TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_MISSILES | + TFL_TARGETSELECT_RANGELIMITS | TFL_TARGETSELECT_TEAMCHECK; + + self.turret_firecheckfunc = turret_tesla_firecheck; + self.target_select_flags = TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_MISSILES | + TFL_TARGETSELECT_RANGELIMITS | TFL_TARGETSELECT_TEAMCHECK; + + self.firecheck_flags = TFL_FIRECHECK_REFIRE | TFL_FIRECHECK_AMMO_OWN; + self.shoot_flags = TFL_SHOOT_CUSTOM; + self.ammo_flags = TFL_AMMO_ENERGY | TFL_AMMO_RECHARGE | TFL_AMMO_RECIEVE; + self.aim_flags = TFL_AIM_NO; + self.track_flags = TFL_TRACK_NO; + - return TRUE; ++ return true; + } + case TR_PRECACHE: + { + precache_model ("models/turrets/tesla_base.md3"); + precache_model ("models/turrets/tesla_head.md3"); - return TRUE; ++ return true; + } + } + - return TRUE; ++ return true; +} + +#endif // SVQC +#ifdef CSQC +float t_tesla(float req) +{ + switch(req) + { + case TR_SETUP: + { - return TRUE; ++ return true; + } + case TR_PRECACHE: + { - return TRUE; ++ return true; + } + } + - return TRUE; ++ return true; +} + +#endif // CSQC +#endif // REGISTER_TURRET diff --cc qcsrc/common/turrets/unit/walker.qc index 644db2c71,000000000..82be1ddfb mode 100644,000000..100644 --- a/qcsrc/common/turrets/unit/walker.qc +++ b/qcsrc/common/turrets/unit/walker.qc @@@ -1,696 -1,0 +1,698 @@@ +#ifdef REGISTER_TURRET +REGISTER_TURRET( +/* TUR_##id */ WALKER, +/* function */ t_walker, +/* spawnflags */ TUR_FLAG_PLAYER | TUR_FLAG_MOVE, +/* mins,maxs */ '-70 -70 0', '70 70 95', +/* model */ "walker_body.md3", +/* head_model */ "walker_head_minigun.md3", +/* netname */ "walker", +/* fullname */ _("Walker Turret") +); +#else +#ifdef SVQC ++#include "../../effects.qh" ++ +float autocvar_g_turrets_unit_walker_melee_damage; +float autocvar_g_turrets_unit_walker_melee_force; +float autocvar_g_turrets_unit_walker_melee_range; +float autocvar_g_turrets_unit_walker_rocket_damage; +float autocvar_g_turrets_unit_walker_rocket_radius; +float autocvar_g_turrets_unit_walker_rocket_force; +float autocvar_g_turrets_unit_walker_rocket_speed; +float autocvar_g_turrets_unit_walker_rocket_range; +float autocvar_g_turrets_unit_walker_rocket_range_min; +float autocvar_g_turrets_unit_walker_rocket_refire; +float autocvar_g_turrets_unit_walker_rocket_turnrate; +float autocvar_g_turrets_unit_walker_speed_stop; +float autocvar_g_turrets_unit_walker_speed_walk; +float autocvar_g_turrets_unit_walker_speed_run; +float autocvar_g_turrets_unit_walker_speed_jump; +float autocvar_g_turrets_unit_walker_speed_swim; +float autocvar_g_turrets_unit_walker_speed_roam; +float autocvar_g_turrets_unit_walker_turn; +float autocvar_g_turrets_unit_walker_turn_walk; +float autocvar_g_turrets_unit_walker_turn_strafe; +float autocvar_g_turrets_unit_walker_turn_swim; +float autocvar_g_turrets_unit_walker_turn_run; + +#define ANIM_NO 0 +#define ANIM_TURN 1 +#define ANIM_WALK 2 +#define ANIM_RUN 3 +#define ANIM_STRAFE_L 4 +#define ANIM_STRAFE_R 5 +#define ANIM_JUMP 6 +#define ANIM_LAND 7 +#define ANIM_PAIN 8 +#define ANIM_MELEE 9 +#define ANIM_SWIM 10 +#define ANIM_ROAM 11 + +.float animflag; +.float idletime; + +#define WALKER_PATH(s,e) pathlib_astar(s,e) + +float walker_firecheck() +{ + if (self.animflag == ANIM_MELEE) + return 0; + + return turret_firecheck(); +} + +void walker_melee_do_dmg() +{ + vector where; + entity e; + + makevectors(self.angles); + where = self.origin + v_forward * 128; + + e = findradius(where,32); + while (e) + { + if (turret_validate_target(self, e, self.target_validate_flags)) + if (e != self && e.owner != self) + Damage(e, self, self, (autocvar_g_turrets_unit_walker_melee_damage), DEATH_TURRET_WALK_MELEE, '0 0 0', v_forward * (autocvar_g_turrets_unit_walker_melee_force)); + + e = e.chain; + } +} + +void walker_setnoanim() +{ - turrets_setframe(ANIM_NO, FALSE); ++ turrets_setframe(ANIM_NO, false); + self.animflag = self.frame; +} +void walker_rocket_explode() +{ + RadiusDamage (self, self.owner, (autocvar_g_turrets_unit_walker_rocket_damage), 0, (autocvar_g_turrets_unit_walker_rocket_radius), self, world, (autocvar_g_turrets_unit_walker_rocket_force), DEATH_TURRET_WALK_ROCKET, world); + remove (self); +} + +void walker_rocket_damage (entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector vforce) +{ + self.health = self.health - damage; + self.velocity = self.velocity + vforce; + + if (self.health <= 0) + W_PrepareExplosionByDamage(self.owner, walker_rocket_explode); +} + +#define WALKER_ROCKET_MOVE movelib_move_simple(newdir, (autocvar_g_turrets_unit_walker_rocket_speed), (autocvar_g_turrets_unit_walker_rocket_turnrate)); UpdateCSQCProjectile(self) +void walker_rocket_loop(); +void walker_rocket_think() +{ + vector newdir; + float edist; + float itime; + float m_speed; + + self.nextthink = time; + + edist = vlen(self.enemy.origin - self.origin); + + // Simulate crude guidance + if (self.cnt < time) + { + if (edist < 1000) + self.tur_shotorg = randomvec() * min(edist, 64); + else + self.tur_shotorg = randomvec() * min(edist, 256); + + self.cnt = time + 0.5; + } + + if (edist < 128) + self.tur_shotorg = '0 0 0'; + + if (self.max_health < time) + { + self.think = walker_rocket_explode; + self.nextthink = time; + return; + } + + if (self.shot_dmg != 1337 && random() < 0.01) + { + walker_rocket_loop(); + return; + } + + m_speed = vlen(self.velocity); + + // Enemy dead? just keep on the current heading then. + if (self.enemy == world || self.enemy.deadflag != DEAD_NO) + self.enemy = world; + + if (self.enemy) + { + itime = max(edist / m_speed, 1); + newdir = steerlib_pull(self.enemy.origin + self.tur_shotorg); + } + else + newdir = normalize(self.velocity); + + WALKER_ROCKET_MOVE; +} + +void walker_rocket_loop3() +{ + vector newdir; + self.nextthink = time; + + if (self.max_health < time) + { + self.think = walker_rocket_explode; + return; + } + + if (vlen(self.origin - self.tur_shotorg) < 100 ) + { + self.think = walker_rocket_think; + return; + } + + newdir = steerlib_pull(self.tur_shotorg); + WALKER_ROCKET_MOVE; + + self.angles = vectoangles(self.velocity); +} + +void walker_rocket_loop2() +{ + vector newdir; + + self.nextthink = time; + + if (self.max_health < time) + { + self.think = walker_rocket_explode; + return; + } + + if (vlen(self.origin - self.tur_shotorg) < 100 ) + { + self.tur_shotorg = self.origin - '0 0 200'; + self.think = walker_rocket_loop3; + return; + } + + newdir = steerlib_pull(self.tur_shotorg); + WALKER_ROCKET_MOVE; +} + +void walker_rocket_loop() +{ + self.nextthink = time; + self.tur_shotorg = self.origin + '0 0 300'; + self.think = walker_rocket_loop2; + self.shot_dmg = 1337; +} + +void walker_fire_rocket(vector org) +{ + entity rocket; + + fixedmakevectors(self.angles); + + te_explosion (org); + + rocket = spawn (); + setorigin(rocket, org); + + sound (self, CH_WEAPON_A, W_Sound("hagar_fire"), VOL_BASE, ATTEN_NORM); + setsize (rocket, '-3 -3 -3', '3 3 3'); // give it some size so it can be shot + + rocket.classname = "walker_rocket"; + rocket.owner = self; - rocket.bot_dodge = TRUE; ++ rocket.bot_dodge = true; + rocket.bot_dodgerating = 50; + rocket.takedamage = DAMAGE_YES; + rocket.damageforcescale = 2; + rocket.health = 25; + rocket.tur_shotorg = randomvec() * 512; + rocket.cnt = time + 1; + rocket.enemy = self.enemy; + + if (random() < 0.01) + rocket.think = walker_rocket_loop; + else + rocket.think = walker_rocket_think; + + rocket.event_damage = walker_rocket_damage; + + rocket.nextthink = time; + rocket.movetype = MOVETYPE_FLY; + rocket.velocity = normalize((v_forward + v_up * 0.5) + (randomvec() * 0.2)) * (autocvar_g_turrets_unit_walker_rocket_speed); + rocket.angles = vectoangles(rocket.velocity); + rocket.touch = walker_rocket_explode; + rocket.flags = FL_PROJECTILE; + rocket.solid = SOLID_BBOX; + rocket.max_health = time + 9; + rocket.missile_flags = MIF_SPLASH | MIF_PROXY | MIF_GUIDED_HEAT; + - CSQCProjectile(rocket, FALSE, PROJECTILE_ROCKET, FALSE); // no culling, has fly sound ++ CSQCProjectile(rocket, false, PROJECTILE_ROCKET, false); // no culling, has fly sound +} + +.vector enemy_last_loc; +.float enemy_last_time; +void walker_move_to(vector _target, float _dist) +{ + switch (self.waterlevel) + { + case WATERLEVEL_NONE: + if (_dist > 500) + self.animflag = ANIM_RUN; + else + self.animflag = ANIM_WALK; + case WATERLEVEL_WETFEET: + case WATERLEVEL_SWIMMING: + if (self.animflag != ANIM_SWIM) + self.animflag = ANIM_WALK; + else + self.animflag = ANIM_SWIM; + break; + case WATERLEVEL_SUBMERGED: + self.animflag = ANIM_SWIM; + } + + self.moveto = _target; + self.steerto = steerlib_attract2(self.moveto, 0.5, 500, 0.95); + + if(self.enemy) + { + self.enemy_last_loc = _target; + self.enemy_last_time = time; + } +} + +//#define WALKER_FANCYPATHING + +void walker_move_path() +{ +#ifdef WALKER_FANCYPATHING + // Are we close enougth to a path node to switch to the next? + if (vlen(self.origin - self.pathcurrent.origin) < 64) + if (self.pathcurrent.path_next == world) + { + // Path endpoint reached + pathlib_deletepath(self.pathcurrent.owner); + self.pathcurrent = world; + + if (self.pathgoal) + { + if (self.pathgoal.use) + self.pathgoal.use(); + + if (self.pathgoal.enemy) + { + self.pathcurrent = WALKER_PATH(self.pathgoal.origin,self.pathgoal.enemy.origin); + self.pathgoal = self.pathgoal.enemy; + } + } + else + self.pathgoal = world; + } + else + self.pathcurrent = self.pathcurrent.path_next; + + self.moveto = self.pathcurrent.origin; + self.steerto = steerlib_attract2(self.moveto,0.5,500,0.95); + walker_move_to(self.moveto, 0); + +#else + if (vlen(self.origin - self.pathcurrent.origin) < 64) + self.pathcurrent = self.pathcurrent.enemy; + + if(!self.pathcurrent) + return; + + self.moveto = self.pathcurrent.origin; + self.steerto = steerlib_attract2(self.moveto, 0.5, 500, 0.95); + walker_move_to(self.moveto, 0); +#endif +} + +void spawnfunc_turret_walker() { if(!turret_initialize(TUR_WALKER)) remove(self); } + +float t_walker(float req) +{ + switch(req) + { + case TR_ATTACK: + { + sound (self, CH_WEAPON_A, W_Sound("uzi_fire"), VOL_BASE, ATTEN_NORM); + fireBullet (self.tur_shotorg, self.tur_shotdir_updated, self.shot_spread, 0, self.shot_dmg, self.shot_force, DEATH_TURRET_WALK_GUN, 0); + Send_Effect(EFFECT_LASER_MUZZLEFLASH, self.tur_shotorg, self.tur_shotdir_updated * 1000, 1); + - return TRUE; ++ return true; + } + case TR_THINK: + { + fixedmakevectors(self.angles); + + if (self.spawnflags & TSF_NO_PATHBREAK && self.pathcurrent) + walker_move_path(); + else if (self.enemy == world) + { + if(self.pathcurrent) + walker_move_path(); + else + { + if(self.enemy_last_time != 0) + { + if(vlen(self.origin - self.enemy_last_loc) < 128 || time - self.enemy_last_time > 10) + self.enemy_last_time = 0; + else + walker_move_to(self.enemy_last_loc, 0); + } + else + { + if(self.animflag != ANIM_NO) + { + traceline(self.origin + '0 0 64', self.origin + '0 0 64' + v_forward * 128, MOVE_NORMAL, self); + + if(trace_fraction != 1.0) + self.tur_head.idletime = -1337; + else + { + traceline(trace_endpos, trace_endpos - '0 0 256', MOVE_NORMAL, self); + if(trace_fraction == 1.0) + self.tur_head.idletime = -1337; + } + + if(self.tur_head.idletime == -1337) + { + self.moveto = self.origin + randomvec() * 256; + self.tur_head.idletime = 0; + } + + self.moveto = self.moveto * 0.9 + ((self.origin + v_forward * 500) + randomvec() * 400) * 0.1; + self.moveto_z = self.origin_z + 64; + walker_move_to(self.moveto, 0); + } + + if(self.idletime < time) + { + if(random() < 0.5 || !(self.spawnflags & TSL_ROAM)) + { + self.idletime = time + 1 + random() * 5; + self.moveto = self.origin; + self.animflag = ANIM_NO; + } + else + { + self.animflag = ANIM_WALK; + self.idletime = time + 4 + random() * 2; + self.moveto = self.origin + randomvec() * 256; + self.tur_head.moveto = self.moveto; + self.tur_head.idletime = 0; + } + } + } + } + } + else + { + if (self.tur_dist_enemy < (autocvar_g_turrets_unit_walker_melee_range) && self.animflag != ANIM_MELEE) + { + vector wish_angle; + + wish_angle = angleofs(self, self.enemy); + if (self.animflag != ANIM_SWIM) + if (fabs(wish_angle_y) < 15) + { + self.moveto = self.enemy.origin; + self.steerto = steerlib_attract2(self.moveto, 0.5, 500, 0.95); + self.animflag = ANIM_MELEE; + } + } + else if (self.tur_head.attack_finished_single < time) + { + if(self.tur_head.shot_volly) + { + self.animflag = ANIM_NO; + + self.tur_head.shot_volly = self.tur_head.shot_volly -1; + if(self.tur_head.shot_volly == 0) + self.tur_head.attack_finished_single = time + (autocvar_g_turrets_unit_walker_rocket_refire); + else + self.tur_head.attack_finished_single = time + 0.2; + + if(self.tur_head.shot_volly > 1) + walker_fire_rocket(gettaginfo(self, gettagindex(self, "tag_rocket01"))); + else + walker_fire_rocket(gettaginfo(self, gettagindex(self, "tag_rocket02"))); + } + else + { + if (self.tur_dist_enemy > (autocvar_g_turrets_unit_walker_rocket_range_min)) + if (self.tur_dist_enemy < (autocvar_g_turrets_unit_walker_rocket_range)) + self.tur_head.shot_volly = 4; + } + } + else + { + if (self.animflag != ANIM_MELEE) + walker_move_to(self.enemy.origin, self.tur_dist_enemy); + } + } + + { + vector real_angle; + float turny = 0, turnx = 0; + float vz; + + real_angle = vectoangles(self.steerto) - self.angles; + vz = self.velocity_z; + + switch (self.animflag) + { + case ANIM_NO: + movelib_beak_simple((autocvar_g_turrets_unit_walker_speed_stop)); + break; + + case ANIM_TURN: + turny = (autocvar_g_turrets_unit_walker_turn); + movelib_beak_simple((autocvar_g_turrets_unit_walker_speed_stop)); + break; + + case ANIM_WALK: + turny = (autocvar_g_turrets_unit_walker_turn_walk); + movelib_move_simple(v_forward, (autocvar_g_turrets_unit_walker_speed_walk), 0.6); + break; + + case ANIM_RUN: + turny = (autocvar_g_turrets_unit_walker_turn_run); + movelib_move_simple(v_forward, (autocvar_g_turrets_unit_walker_speed_run), 0.6); + break; + + case ANIM_STRAFE_L: + turny = (autocvar_g_turrets_unit_walker_turn_strafe); + movelib_move_simple(v_right * -1, (autocvar_g_turrets_unit_walker_speed_walk), 0.8); + break; + + case ANIM_STRAFE_R: + turny = (autocvar_g_turrets_unit_walker_turn_strafe); + movelib_move_simple(v_right, (autocvar_g_turrets_unit_walker_speed_walk), 0.8); + break; + + case ANIM_JUMP: + self.velocity += '0 0 1' * (autocvar_g_turrets_unit_walker_speed_jump); + break; + + case ANIM_LAND: + break; + + case ANIM_PAIN: + if(self.frame != ANIM_PAIN) + defer(0.25, walker_setnoanim); + + break; + + case ANIM_MELEE: + if(self.frame != ANIM_MELEE) + { + defer(0.41, walker_setnoanim); + defer(0.21, walker_melee_do_dmg); + } + + movelib_beak_simple((autocvar_g_turrets_unit_walker_speed_stop)); + break; + + case ANIM_SWIM: + turny = (autocvar_g_turrets_unit_walker_turn_swim); + turnx = (autocvar_g_turrets_unit_walker_turn_swim); + + self.angles_x += bound(-10, shortangle_f(real_angle_x, self.angles_x), 10); + movelib_move_simple(v_forward, (autocvar_g_turrets_unit_walker_speed_swim), 0.3); + vz = self.velocity_z + sin(time * 4) * 8; + break; + + case ANIM_ROAM: + turny = (autocvar_g_turrets_unit_walker_turn_walk); + movelib_move_simple(v_forward ,(autocvar_g_turrets_unit_walker_speed_roam), 0.5); + break; + } + + if(turny) + { + turny = bound( turny * -1, shortangle_f(real_angle_y, self.angles_y), turny ); + self.angles_y += turny; + } + + if(turnx) + { + turnx = bound( turnx * -1, shortangle_f(real_angle_x, self.angles_x), turnx ); + self.angles_x += turnx; + } + + self.velocity_z = vz; + } + + + if(self.origin != self.oldorigin) + self.SendFlags |= TNSF_MOVE; + + self.oldorigin = self.origin; - turrets_setframe(self.animflag, FALSE); ++ turrets_setframe(self.animflag, false); + - return TRUE; ++ return true; + } + case TR_DEATH: + { +#ifdef WALKER_FANCYPATHING + if (self.pathcurrent) + pathlib_deletepath(self.pathcurrent.owner); +#endif + self.pathcurrent = world; + - return TRUE; ++ return true; + } + case TR_SETUP: + { + self.ticrate = 0.05; + + entity e; + + // Respawn is called & first spawn to, to set team. need to make sure we do not move the initial spawn. + if(self.movetype == MOVETYPE_WALK) + { + if(self.pos1) + setorigin(self, self.pos1); + if(self.pos2) + self.angles = self.pos2; + } + + self.ammo_flags = TFL_AMMO_BULLETS | TFL_AMMO_RECHARGE | TFL_AMMO_RECIEVE; + self.aim_flags = TFL_AIM_LEAD; + self.turret_flags |= TUR_FLAG_HITSCAN; + + self.target_select_flags = TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_RANGELIMITS | TFL_TARGETSELECT_TEAMCHECK | TFL_TARGETSELECT_LOS; + self.target_validate_flags = TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_RANGELIMITS | TFL_TARGETSELECT_TEAMCHECK | TFL_TARGETSELECT_LOS; - self.iscreature = TRUE; ++ self.iscreature = true; + self.teleportable = TELEPORT_NORMAL; - self.damagedbycontents = TRUE; ++ self.damagedbycontents = true; + self.solid = SOLID_SLIDEBOX; + self.takedamage = DAMAGE_AIM; + if(self.movetype != MOVETYPE_WALK) + { + setorigin(self, self.origin); + tracebox(self.origin + '0 0 128', self.mins, self.maxs, self.origin - '0 0 10000', MOVE_NORMAL, self); + setorigin(self, trace_endpos + '0 0 4'); + self.pos1 = self.origin; + self.pos2 = self.angles; + } + self.movetype = MOVETYPE_WALK; + self.idle_aim = '0 0 0'; + self.turret_firecheckfunc = walker_firecheck; + + if (self.target != "") + { + e = find(world, targetname, self.target); + if (!e) + { + dprint("Initital waypoint for walker does NOT exsist, fix your map!\n"); + self.target = ""; + } + + if (e.classname != "turret_checkpoint") + dprint("Warning: not a turrret path\n"); + else + { +#ifdef WALKER_FANCYPATHING + self.pathcurrent = WALKER_PATH(self.origin, e.origin); + self.pathgoal = e; +#else + self.pathcurrent = e; +#endif + } + } + - return TRUE; ++ return true; + } + case TR_PRECACHE: + { + precache_model ("models/turrets/walker_body.md3"); + precache_model ("models/turrets/walker_head_minigun.md3"); + precache_model ("models/turrets/rocket.md3"); + precache_sound (W_Sound("rocket_impact")); - return TRUE; ++ return true; + } + } + - return TRUE; ++ return true; +} + +#endif // SVQC +#ifdef CSQC + +void walker_draw() +{ + float dt; + + dt = time - self.move_time; + self.move_time = time; + if(dt <= 0) + return; + + fixedmakevectors(self.angles); + movelib_groundalign4point(300, 100, 0.25, 45); + setorigin(self, self.origin + self.velocity * dt); + self.tur_head.angles += dt * self.tur_head.move_avelocity; + self.angles_y = self.move_angles_y; + + if (self.health < 127) + if(random() < 0.15) + te_spark(self.origin + '0 0 40', randomvec() * 256 + '0 0 256', 16); +} + +float t_walker(float req) +{ + switch(req) + { + case TR_SETUP: + { + self.gravity = 1; + self.movetype = MOVETYPE_BOUNCE; + self.move_movetype = MOVETYPE_BOUNCE; + self.move_origin = self.origin; + self.move_time = time; + self.draw = walker_draw; + - return TRUE; ++ return true; + } + case TR_PRECACHE: + { - return TRUE; ++ return true; + } + } + - return TRUE; ++ return true; +} + +#endif // CSQC +#endif // REGISTER_TURRET diff --cc qcsrc/common/turrets/util.qc index 6de59027e,000000000..9020a5793 mode 100644,000000..100644 --- a/qcsrc/common/turrets/util.qc +++ b/qcsrc/common/turrets/util.qc @@@ -1,331 -1,0 +1,330 @@@ +/* +* Return a angle within +/- 360. +*/ +float anglemods(float v) +{ + v = v - 360 * floor(v / 360); + + if(v >= 180) + return v - 360; + else if(v <= -180) + return v + 360; + else + return v; +} + +/* +* Return the short angle +*/ +float shortangle_f(float ang1, float ang2) +{ + if(ang1 > ang2) + { + if(ang1 > 180) + return ang1 - 360; + } + else + { + if(ang1 < -180) + return ang1 + 360; + } + + return ang1; +} + +vector shortangle_v(vector ang1, vector ang2) +{ + vector vtmp; + + vtmp_x = shortangle_f(ang1_x,ang2_x); + vtmp_y = shortangle_f(ang1_y,ang2_y); + vtmp_z = shortangle_f(ang1_z,ang2_z); + + return vtmp; +} + +vector shortangle_vxy(vector ang1, vector ang2) +{ + vector vtmp = '0 0 0'; + + vtmp_x = shortangle_f(ang1_x,ang2_x); + vtmp_y = shortangle_f(ang1_y,ang2_y); + + return vtmp; +} + + +/* +* Get "real" origin, in worldspace, even if ent is attached to something else. +*/ +vector real_origin(entity ent) +{ + entity e; + vector v = ((ent.absmin + ent.absmax) * 0.5); + + e = ent.tag_entity; + while(e) + { + v = v + ((e.absmin + e.absmax) * 0.5); + e = e.tag_entity; + } + + return v; +} + +/* +* Return the angle between two enteties +*/ +vector angleofs(entity from, entity to) +{ + vector v_res; + + v_res = normalize(to.origin - from.origin); + v_res = vectoangles(v_res); + v_res = v_res - from.angles; + + if (v_res_x < 0) v_res_x += 360; + if (v_res_x > 180) v_res_x -= 360; + + if (v_res_y < 0) v_res_y += 360; + if (v_res_y > 180) v_res_y -= 360; + + return v_res; +} + +vector angleofs3(vector from, vector from_a, entity to) +{ + vector v_res; + + v_res = normalize(to.origin - from); + v_res = vectoangles(v_res); + v_res = v_res - from_a; + + if (v_res_x < 0) v_res_x += 360; + if (v_res_x > 180) v_res_x -= 360; + + if (v_res_y < 0) v_res_y += 360; + if (v_res_y > 180) v_res_y -= 360; + + return v_res; +} + +/* +* Update self.tur_shotorg by getting up2date bone info +* NOTICE this func overwrites the global v_forward, v_right and v_up vectors. +*/ - #define turret_tag_fire_update() self.tur_shotorg = gettaginfo(self.tur_head, gettagindex(self.tur_head, "tag_fire"));v_forward = normalize(v_forward) +float turret_tag_fire_update_s() +{ + if(!self.tur_head) + { + error("Call to turret_tag_fire_update with self.tur_head missing!\n"); + self.tur_shotorg = '0 0 0'; - return FALSE; ++ return false; + } + + self.tur_shotorg = gettaginfo(self.tur_head, gettagindex(self.tur_head, "tag_fire")); + v_forward = normalize(v_forward); + - return TRUE; ++ return true; +} + +/* +* Railgun-like beam, but has thickness and suppots slowing of target +*/ +void FireImoBeam (vector start, vector end, vector smin, vector smax, + float bforce, float f_dmg, float f_velfactor, float deathtype) + +{ + vector hitloc, force, endpoint, dir; + entity ent; + + dir = normalize(end - start); + force = dir * bforce; + + // go a little bit into the wall because we need to hit this wall later + end = end + dir; + + // trace multiple times until we hit a wall, each obstacle will be made unsolid. + // note down which entities were hit so we can damage them later + while (1) + { - tracebox(start, smin, smax, end, FALSE, self); ++ tracebox(start, smin, smax, end, false, self); + + // if it is world we can't hurt it so stop now + if (trace_ent == world || trace_fraction == 1) + break; + + if (trace_ent.solid == SOLID_BSP) + break; + + // make the entity non-solid so we can hit the next one - trace_ent.railgunhit = TRUE; ++ trace_ent.railgunhit = true; + trace_ent.railgunhitloc = end; + trace_ent.railgunhitsolidbackup = trace_ent.solid; + + // stop if this is a wall + + // make the entity non-solid + trace_ent.solid = SOLID_NOT; + } + + endpoint = trace_endpos; + + // find all the entities the railgun hit and restore their solid state - ent = findfloat(world, railgunhit, TRUE); ++ ent = findfloat(world, railgunhit, true); + while (ent) + { + // restore their solid type + ent.solid = ent.railgunhitsolidbackup; - ent = findfloat(ent, railgunhit, TRUE); ++ ent = findfloat(ent, railgunhit, true); + } + + // find all the entities the railgun hit and hurt them - ent = findfloat(world, railgunhit, TRUE); ++ ent = findfloat(world, railgunhit, true); + while (ent) + { + // get the details we need to call the damage function + hitloc = ent.railgunhitloc; + ent.railgunhitloc = '0 0 0'; + ent.railgunhitsolidbackup = SOLID_NOT; - ent.railgunhit = FALSE; ++ ent.railgunhit = false; + + // apply the damage + if (ent.takedamage) + { + Damage (ent, self, self, f_dmg, deathtype, hitloc, force); + ent.velocity = ent.velocity * f_velfactor; + //ent.alpha = 0.25 + random() * 0.75; + } + + // advance to the next entity - ent = findfloat(ent, railgunhit, TRUE); ++ ent = findfloat(ent, railgunhit, true); + } + trace_endpos = endpoint; +} + +#ifdef TURRET_DEBUG +void SUB_Remove(); +void marker_think() +{ + if(self.cnt) + if(self.cnt < time) + { + self.think = SUB_Remove; + self.nextthink = time; + return; + } + + self.frame += 1; + if(self.frame > 29) + self.frame = 0; + + self.nextthink = time; +} + +void mark_error(vector where,float lifetime) +{ + entity err; + + err = spawn(); + err.classname = "error_marker"; + setmodel(err,"models/marker.md3"); + setorigin(err,where); + err.movetype = MOVETYPE_NONE; + err.think = marker_think; + err.nextthink = time; + err.skin = 0; + if(lifetime) + err.cnt = lifetime + time; +} + +void mark_info(vector where,float lifetime) +{ + entity err; + + err = spawn(); + err.classname = "info_marker"; + setmodel(err,"models/marker.md3"); + setorigin(err,where); + err.movetype = MOVETYPE_NONE; + err.think = marker_think; + err.nextthink = time; + err.skin = 1; + if(lifetime) + err.cnt = lifetime + time; +} + +entity mark_misc(vector where,float lifetime) +{ + entity err; + + err = spawn(); + err.classname = "mark_misc"; + setmodel(err,"models/marker.md3"); + setorigin(err,where); + err.movetype = MOVETYPE_NONE; + err.think = marker_think; + err.nextthink = time; + err.skin = 3; + if(lifetime) + err.cnt = lifetime + time; + return err; +} + +/* +* Paint a v_color colord circle on target onwho +* that fades away over f_time +*/ +void paint_target(entity onwho, float f_size, vector v_color, float f_time) +{ + entity e; + + e = spawn(); + setmodel(e, "models/turrets/c512.md3"); // precision set above + e.scale = (f_size/512); + //setsize(e, '0 0 0', '0 0 0'); + //setattachment(e,onwho,""); + setorigin(e,onwho.origin + '0 0 1'); + e.alpha = 0.15; + e.movetype = MOVETYPE_FLY; + + e.velocity = (v_color * 32); // + '0 0 1' * 64; + + e.colormod = v_color; + SUB_SetFade(e,time,f_time); +} + +void paint_target2(entity onwho, float f_size, vector v_color, float f_time) +{ + entity e; + + e = spawn(); + setmodel(e, "models/turrets/c512.md3"); // precision set above + e.scale = (f_size/512); + setsize(e, '0 0 0', '0 0 0'); + + setorigin(e,onwho.origin + '0 0 1'); + e.alpha = 0.15; + e.movetype = MOVETYPE_FLY; + + e.velocity = (v_color * 32); // + '0 0 1' * 64; + e.avelocity_x = -128; + + e.colormod = v_color; + SUB_SetFade(e,time,f_time); +} + +void paint_target3(vector where, float f_size, vector v_color, float f_time) +{ + entity e; + e = spawn(); + setmodel(e, "models/turrets/c512.md3"); // precision set above + e.scale = (f_size/512); + setsize(e, '0 0 0', '0 0 0'); + setorigin(e,where+ '0 0 1'); + e.movetype = MOVETYPE_NONE; + e.velocity = '0 0 0'; + e.colormod = v_color; + SUB_SetFade(e,time,f_time); +} +#endif diff --cc qcsrc/common/turrets/util.qh index 000000000,000000000..80cb670c9 new file mode 100644 --- /dev/null +++ b/qcsrc/common/turrets/util.qh @@@ -1,0 -1,0 +1,22 @@@ ++#ifndef TURRETS_UTIL_H ++#define TURRETS_UTIL_H ++ ++#define turret_tag_fire_update() self.tur_shotorg = gettaginfo(self.tur_head, gettagindex(self.tur_head, "tag_fire"));v_forward = normalize(v_forward) ++ ++float shortangle_f(float ang1, float ang2); ++ ++float anglemods(float v); ++ ++vector shortangle_v(vector ang1, vector ang2); ++ ++vector shortangle_vxy(vector ang1, vector ang2); ++ ++vector real_origin(entity ent); ++ ++vector angleofs(entity from, entity to); ++ ++vector angleofs3(vector from, vector from_a, entity to); ++ ++void FireImoBeam (vector start, vector end, vector smin, vector smax, float bforce, float f_dmg, float f_velfactor, float deathtype); ++ ++#endif diff --cc qcsrc/common/util.qc index 451a092af,99fecda0b..81d09c79d --- a/qcsrc/common/util.qc +++ b/qcsrc/common/util.qc @@@ -1863,7 -1880,7 +1884,7 @@@ vector healtharmor_maxdamage(float h, f return v; } - vector healtharmor_applydamage(float a, float armorblock, float deathtype, float damage, float bycount) -vector healtharmor_applydamage(float a, float armorblock, float deathtype, float damage) ++vector healtharmor_applydamage(float a, float armorblock, int deathtype, float damage, bool bycount) { vector v; if (deathtype == DEATH_DROWN) // Why should armor help here... diff --cc qcsrc/common/util.qh index 4e59c5846,4de610f26..741133bab --- a/qcsrc/common/util.qh +++ b/qcsrc/common/util.qh @@@ -1,40 -1,7 +1,36 @@@ + #ifndef COMMON_UTIL_H + #define COMMON_UTIL_H + +#ifndef MENUQC +void FixSize(entity e) +{ + e.mins_x = rint(e.mins_x); + e.mins_y = rint(e.mins_y); + e.mins_z = rint(e.mins_z); + + e.maxs_x = rint(e.maxs_x); + e.maxs_y = rint(e.maxs_y); + e.maxs_z = rint(e.maxs_z); +} + +vector randompos(vector m1, vector m2) +{ + vector v; + m2 = m2 - m1; + v_x = m2_x * random() + m1_x; + v_y = m2_y * random() + m1_y; + v_z = m2_z * random() + m1_z; + return v; +} + +void setmodel_fixsize(entity e, string m) +{ + setmodel(e, m); + FixSize(e); +} +#endif + - // commonly used, but better make them macros - #define TRUE 1 - #define FALSE 0 - - // a dummy macro that prevents the "hanging ;" warning - #define ENDS_WITH_CURLY_BRACE - - #ifdef GMQCC + #ifdef QCC_SUPPORT_ACCUMULATE # define ACCUMULATE_FUNCTION(func,otherfunc) \ [[accumulate]] void func() { otherfunc(); } # define CALL_ACCUMULATED_FUNCTION(func) \ @@@ -482,3 -445,4 +478,5 @@@ vector bezier_quadratic_getderivative(v // Returns the correct difference between two always increasing numbers #define COMPARE_INCREASING(to,from) (to < from ? from + to + 2 : to - from) ++ + #endif diff --cc qcsrc/common/vehicles/cl_vehicles.qc index 9a2598f82,000000000..8c4bd6044 mode 100644,000000..100644 --- a/qcsrc/common/vehicles/cl_vehicles.qc +++ b/qcsrc/common/vehicles/cl_vehicles.qc @@@ -1,126 -1,0 +1,126 @@@ +#define hud_bg "gfx/vehicles/frame.tga" +#define hud_sh "gfx/vehicles/vh-shield.tga" + +#define hud_hp_bar "gfx/vehicles/bar_up_left.tga" +#define hud_hp_ico "gfx/vehicles/health.tga" +#define hud_sh_bar "gfx/vehicles/bar_dwn_left.tga" +#define hud_sh_ico "gfx/vehicles/shield.tga" + +#define hud_ammo1_bar "gfx/vehicles/bar_up_right.tga" +#define hud_ammo1_ico "gfx/vehicles/bullets.tga" +#define hud_ammo2_bar "gfx/vehicles/bar_dwn_right.tga" +#define hud_ammo2_ico "gfx/vehicles/rocket.tga" +#define hud_energy "gfx/vehicles/energy.tga" + +entity dropmark; - var float autocvar_cl_vehicles_hudscale = 0.5; - var float autocvar_cl_vehicles_hudalpha = 0.75; ++float autocvar_cl_vehicles_hudscale = 0.5; ++float autocvar_cl_vehicles_hudalpha = 0.75; + +const float MAX_AXH = 4; +entity AuxiliaryXhair[MAX_AXH]; + +.string axh_image; +.float axh_fadetime; +.float axh_drawflag; +.float axh_scale; + +float alarm1time; +float alarm2time; + +void vehicle_alarm(entity e, float ch, string s0und) +{ + if(!autocvar_cl_vehicles_alarm) + return; + + sound(e, ch, s0und, VOL_BASEVOICE, ATTEN_NONE); +} + +void AuxiliaryXhair_Draw2D() +{ + vector loc, psize; + + psize = self.axh_scale * draw_getimagesize(self.axh_image); + loc = project_3d_to_2d(self.move_origin) - 0.5 * psize; + if(!(loc_z < 0 || loc_x < 0 || loc_y < 0 || loc_x > vid_conwidth || loc_y > vid_conheight)) + { + loc_z = 0; + psize_z = 0; + drawpic(loc, self.axh_image, psize, self.colormod, self.alpha, self.axh_drawflag); + } + + if(time - self.cnt > self.axh_fadetime) + self.draw2d = func_null; +} + - void Net_AuXair2(float bIsNew) ++void Net_AuXair2(bool bIsNew) +{ - float axh_id = bound(0, ReadByte(), MAX_AXH); ++ int axh_id = bound(0, ReadByte(), MAX_AXH); + entity axh = AuxiliaryXhair[axh_id]; + + if(axh == world || wasfreed(axh)) // MADNESS? THIS IS QQQQCCCCCCCCC (wasfreed, why do you exsist?) + { + axh = spawn(); + axh.draw2d = func_null; + axh.drawmask = MASK_NORMAL; + axh.axh_drawflag = DRAWFLAG_ADDITIVE; + axh.axh_fadetime = 0.1; + axh.axh_image = "gfx/vehicles/axh-ring.tga"; + axh.axh_scale = 1; + axh.alpha = 1; + AuxiliaryXhair[axh_id] = axh; + } + + axh.move_origin_x = ReadCoord(); + axh.move_origin_y = ReadCoord(); + axh.move_origin_z = ReadCoord(); + axh.colormod_x = ReadByte() / 255; + axh.colormod_y = ReadByte() / 255; + axh.colormod_z = ReadByte() / 255; + axh.cnt = time; + axh.draw2d = AuxiliaryXhair_Draw2D; +} + +void Net_VehicleSetup() +{ + float i; + + float hud_id = ReadByte(); + + // hud_id == 0 means we exited a vehicle, so stop alarm sound/s + if(hud_id == 0) + { + sound(self, CH_TRIGGER_SINGLE, "misc/null.wav", VOL_BASEVOICE, ATTEN_NONE); + sound(self, CH_PAIN_SINGLE, "misc/null.wav", VOL_BASEVOICE, ATTEN_NONE); + return; + } + + // Init auxiliary crosshairs + entity axh; + for(i = 0; i < MAX_AXH; ++i) + { + axh = AuxiliaryXhair[i]; + if(axh != world && !wasfreed(axh)) // MADNESS? THIS IS QQQQCCCCCCCCC (wasfreed, why do you exsist?) + remove(axh); + + axh = spawn(); + axh.draw2d = func_null; + axh.drawmask = MASK_NORMAL; + axh.axh_drawflag = DRAWFLAG_NORMAL; + axh.axh_fadetime = 0.1; + axh.axh_image = "gfx/vehicles/axh-ring.tga"; + axh.axh_scale = 1; + axh.alpha = 1; + AuxiliaryXhair[i] = axh; + } + + if(hud_id == HUD_BUMBLEBEE_GUN) + { + // Plasma cannons + AuxiliaryXhair[0].axh_image = "gfx/vehicles/axh-bracket.tga"; + AuxiliaryXhair[0].axh_scale = 0.25; + // Raygun + AuxiliaryXhair[1].axh_image = "gfx/vehicles/axh-bracket.tga"; + AuxiliaryXhair[1].axh_scale = 0.25; + } + else { VEH_ACTION(hud_id, VR_SETUP); } +} diff --cc qcsrc/common/vehicles/cl_vehicles.qh index 67701e418,000000000..f91add3f1 mode 100644,000000..100644 --- a/qcsrc/common/vehicles/cl_vehicles.qh +++ b/qcsrc/common/vehicles/cl_vehicles.qh @@@ -1,15 -1,0 +1,24 @@@ ++#ifndef CL_VEHICLES_H ++#define CL_VEHICLES_H ++ +// vehicle cvars - var float autocvar_cl_vehicles_alarm = 1; - var float autocvar_cl_vehicles_hud_tactical = 1; ++bool autocvar_cl_vehicles_alarm = 1; ++bool autocvar_cl_vehicles_hud_tactical = 1; ++ ++void Net_AuXair2(float bIsNew); ++ ++void Net_VehicleSetup(); + +void RaptorCBShellfragDraw(); +void RaptorCBShellfragToss(vector _org, vector _vel, vector _ang); + +#define HUD_GETVEHICLESTATS \ - local noref float vh_health = getstati(STAT_VEHICLESTAT_HEALTH); \ - local noref float shield = getstati(STAT_VEHICLESTAT_SHIELD); \ - local noref float energy = getstati(STAT_VEHICLESTAT_ENERGY); \ - local noref float ammo1 = getstati(STAT_VEHICLESTAT_AMMO1); \ - local noref float reload1 = getstati(STAT_VEHICLESTAT_RELOAD1); \ - local noref float ammo2 = getstati(STAT_VEHICLESTAT_AMMO2); \ - local noref float reload2 = getstati(STAT_VEHICLESTAT_RELOAD2); ++ int vh_health = getstati(STAT_VEHICLESTAT_HEALTH); \ ++ float shield = getstati(STAT_VEHICLESTAT_SHIELD); \ ++ noref int energy = getstati(STAT_VEHICLESTAT_ENERGY); \ ++ noref float ammo1 = getstati(STAT_VEHICLESTAT_AMMO1); \ ++ noref float reload1 = getstati(STAT_VEHICLESTAT_RELOAD1); \ ++ noref int ammo2 = getstati(STAT_VEHICLESTAT_AMMO2); \ ++ noref int reload2 = getstati(STAT_VEHICLESTAT_RELOAD2); ++ ++#endif diff --cc qcsrc/common/vehicles/sv_vehicles.qc index 25e622a48,000000000..4747ce460 mode 100644,000000..100644 --- a/qcsrc/common/vehicles/sv_vehicles.qc +++ b/qcsrc/common/vehicles/sv_vehicles.qc @@@ -1,1264 -1,0 +1,1264 @@@ - // ========================= - // SVQC Vehicle Properties - // ========================= - ++#include "../effects.qh" ++#include "vehicles.qh" ++#include "sv_vehicles.qh" ++#include "../../server/jeff.qh" + +float SendAuxiliaryXhair(entity to, float sf) +{ + + WriteByte(MSG_ENTITY, ENT_CLIENT_AUXILIARYXHAIR); + + WriteByte(MSG_ENTITY, self.cnt); + + WriteCoord(MSG_ENTITY, self.origin_x); + WriteCoord(MSG_ENTITY, self.origin_y); + WriteCoord(MSG_ENTITY, self.origin_z); + + WriteByte(MSG_ENTITY, rint(self.colormod_x * 255)); + WriteByte(MSG_ENTITY, rint(self.colormod_y * 255)); + WriteByte(MSG_ENTITY, rint(self.colormod_z * 255)); + - return TRUE; ++ return true; +} + +void UpdateAuxiliaryXhair(entity own, vector loc, vector clr, float axh_id) +{ + if(!IS_REAL_CLIENT(own)) + return; + + entity axh; + + axh_id = bound(0, axh_id, MAX_AXH); + axh = own.(AuxiliaryXhair[axh_id]); + + if(axh == world || wasfreed(axh)) // MADNESS? THIS IS QQQQCCCCCCCCC (wasfreed, why do you exsist?) + { + axh = spawn(); + axh.cnt = axh_id; + axh.drawonlytoclient = own; + axh.owner = own; - Net_LinkEntity(axh, FALSE, 0, SendAuxiliaryXhair); ++ Net_LinkEntity(axh, false, 0, SendAuxiliaryXhair); + } + + setorigin(axh, loc); + axh.colormod = clr; + axh.SendFlags = 0x01; + own.(AuxiliaryXhair[axh_id]) = axh; +} + +void CSQCVehicleSetup(entity own, float vehicle_id) +{ + if(!IS_REAL_CLIENT(own)) + return; + + msg_entity = own; + + WriteByte(MSG_ONE, SVC_TEMPENTITY); + WriteByte(MSG_ONE, TE_CSQC_VEHICLESETUP); + WriteByte(MSG_ONE, vehicle_id); +} + +vector targetdrone_getnewspot() +{ + vector spot; + float i; + for(i = 0; i < 100; ++i) + { + spot = self.origin + randomvec() * 1024; + tracebox(spot, self.mins, self.maxs, spot, MOVE_NORMAL, self); + if(trace_fraction == 1.0 && trace_startsolid == 0 && trace_allsolid == 0) + return spot; + } + return self.origin; +} + +void vehicles_locktarget(float incr, float decr, float _lock_time) +{ + if(self.lock_target && self.lock_target.deadflag != DEAD_NO) + { + self.lock_target = world; + self.lock_strength = 0; + self.lock_time = 0; + } + + if(self.lock_time > time) + { + if(self.lock_target) + if(self.lock_soundtime < time) + { + self.lock_soundtime = time + 0.5; + play2(self.owner, "vehicles/locked.wav"); + } + + return; + } + + if(trace_ent != world) + { + if(SAME_TEAM(trace_ent, self)) + trace_ent = world; + + if(trace_ent.deadflag != DEAD_NO) + trace_ent = world; + + if(!(IS_VEHICLE(trace_ent) || IS_TURRET(trace_ent))) + trace_ent = world; + + if(trace_ent.alpha <= 0.5 && trace_ent.alpha != 0) + trace_ent = world; // invisible + } + + if(self.lock_target == world && trace_ent != world) + self.lock_target = trace_ent; + + if(self.lock_target && trace_ent == self.lock_target) + { + if(self.lock_strength != 1 && self.lock_strength + incr >= 1) + { + play2(self.owner, "vehicles/lock.wav"); + self.lock_soundtime = time + 0.8; + } + else if (self.lock_strength != 1 && self.lock_soundtime < time) + { + play2(self.owner, "vehicles/locking.wav"); + self.lock_soundtime = time + 0.3; + } + } + + // Have a locking target + // Trace hit current target + if(trace_ent == self.lock_target && trace_ent != world) + { + self.lock_strength = min(self.lock_strength + incr, 1); + if(self.lock_strength == 1) + self.lock_time = time + _lock_time; + } + else + { + if(trace_ent) + self.lock_strength = max(self.lock_strength - decr * 2, 0); + else + self.lock_strength = max(self.lock_strength - decr, 0); + + if(self.lock_strength == 0) + self.lock_target = world; + } +} + +vector vehicles_force_fromtag_hover(string tag_name, float spring_length, float max_power) +{ + force_fromtag_origin = gettaginfo(self, gettagindex(self, tag_name)); + v_forward = normalize(v_forward) * -1; + traceline(force_fromtag_origin, force_fromtag_origin - (v_forward * spring_length), MOVE_NORMAL, self); + + force_fromtag_power = (1 - trace_fraction) * max_power; + force_fromtag_normpower = force_fromtag_power / max_power; + + return v_forward * force_fromtag_power; +} + +vector vehicles_force_fromtag_maglev(string tag_name, float spring_length, float max_power) +{ + + force_fromtag_origin = gettaginfo(self, gettagindex(self, tag_name)); + v_forward = normalize(v_forward) * -1; + traceline(force_fromtag_origin, force_fromtag_origin - (v_forward * spring_length), MOVE_NORMAL, self); + + // TODO - this may NOT be compatible with wall/celing movement, unhardcode 0.25 (engine count multiplier) + if(trace_fraction == 1.0) + { + force_fromtag_normpower = -0.25; + return '0 0 -200'; + } + + force_fromtag_power = ((1 - trace_fraction) - trace_fraction) * max_power; + force_fromtag_normpower = force_fromtag_power / max_power; + + return v_forward * force_fromtag_power; +} + +// projectile handling +void vehicles_projectile_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force) +{ + // Ignore damage from oterh projectiles from my owner (dont mess up volly's) + if(inflictor.owner == self.owner) + return; + + self.health -= damage; + self.velocity += force; + if(self.health < 1) + { + self.takedamage = DAMAGE_NO; + self.event_damage = func_null; + self.think = self.use; + self.nextthink = time; + } +} + +void vehicles_projectile_explode() +{ + if(self.owner && other != world) + { + if(other == self.owner.vehicle) + return; + + if(other == self.owner.vehicle.tur_head) + return; + } + + PROJECTILE_TOUCH; + + self.event_damage = func_null; + RadiusDamage (self, self.realowner, self.shot_dmg, 0, self.shot_radius, self, world, self.shot_force, self.totalfrags, other); + + remove (self); +} + +entity vehicles_projectile(string _mzlfx, string _mzlsound, + vector _org, vector _vel, + float _dmg, float _radi, float _force, float _size, + float _deahtype, float _projtype, float _health, + float _cull, float _clianim, entity _owner) +{ + entity proj; + + proj = spawn(); + + PROJECTILE_MAKETRIGGER(proj); + setorigin(proj, _org); + + proj.shot_dmg = _dmg; + proj.shot_radius = _radi; + proj.shot_force = _force; + proj.totalfrags = _deahtype; + proj.solid = SOLID_BBOX; + proj.movetype = MOVETYPE_FLYMISSILE; + proj.flags = FL_PROJECTILE; - proj.bot_dodge = TRUE; ++ proj.bot_dodge = true; + proj.bot_dodgerating = _dmg; + proj.velocity = _vel; + proj.touch = vehicles_projectile_explode; + proj.use = vehicles_projectile_explode; + proj.owner = self; + proj.realowner = _owner; + proj.think = SUB_Remove; + proj.nextthink = time + 30; + + if(_health) + { + proj.takedamage = DAMAGE_AIM; + proj.event_damage = vehicles_projectile_damage; + proj.health = _health; + } + else + proj.flags = FL_PROJECTILE | FL_NOTARGET; + + if(_mzlsound) + sound (self, CH_WEAPON_A, _mzlsound, VOL_BASE, ATTEN_NORM); + + if(_mzlfx) + pointparticles(particleeffectnum(_mzlfx), proj.origin, proj.velocity, 1); + + + setsize (proj, '-1 -1 -1' * _size, '1 1 1' * _size); + + CSQCProjectile(proj, _clianim, _projtype, _cull); + + return proj; +} + +void vehicles_gib_explode() +{ + sound (self, CH_SHOTS, W_Sound("rocket_impact"), VOL_BASE, ATTEN_NORM); + Send_Effect(EFFECT_EXPLOSION_SMALL, randomvec() * 80 + (self.origin + '0 0 100'), '0 0 0', 1); + Send_Effect(EFFECT_EXPLOSION_SMALL, self.wp00.origin + '0 0 64', '0 0 0', 1); + remove(self); +} + +void vehicles_gib_think() +{ + self.alpha -= 0.1; + if(self.cnt >= time) + remove(self); + else + self.nextthink = time + 0.1; +} + +entity vehicle_tossgib(entity _template, vector _vel, string _tag, float _burn, float _explode, float _maxtime, vector _rot) +{ + entity _gib = spawn(); + setmodel(_gib, _template.model); + setorigin(_gib, gettaginfo(self, gettagindex(self, _tag))); + _gib.velocity = _vel; + _gib.movetype = MOVETYPE_TOSS; + _gib.solid = SOLID_CORPSE; + _gib.colormod = '-0.5 -0.5 -0.5'; + _gib.effects = EF_LOWPRECISION; + _gib.avelocity = _rot; + + if(_burn) + _gib.effects |= EF_FLAME; + + if(_explode) + { + _gib.think = vehicles_gib_explode; + _gib.nextthink = time + random() * _explode; + _gib.touch = vehicles_gib_explode; + } + else + { + _gib.cnt = time + _maxtime; + _gib.think = vehicles_gib_think; + _gib.nextthink = time + _maxtime - 1; + _gib.alpha = 1; + } + return _gib; +} + +float vehicle_addplayerslot( entity _owner, + entity _slot, + float _hud, + string _hud_model, + float() _framefunc, + void(float) _exitfunc, float() _enterfunc) +{ + if(!(_owner.vehicle_flags & VHF_MULTISLOT)) + _owner.vehicle_flags |= VHF_MULTISLOT; + + _slot.PlayerPhysplug = _framefunc; + _slot.vehicle_exit = _exitfunc; + _slot.vehicle_enter = _enterfunc; + _slot.hud = _hud; + _slot.vehicle_flags = VHF_PLAYERSLOT; + _slot.vehicle_viewport = spawn(); + _slot.vehicle_hudmodel = spawn(); + _slot.vehicle_hudmodel.viewmodelforclient = _slot; + _slot.vehicle_viewport.effects = (EF_ADDITIVE | EF_DOUBLESIDED | EF_FULLBRIGHT | EF_NODEPTHTEST | EF_NOGUNBOB | EF_NOSHADOW | EF_LOWPRECISION | EF_SELECTABLE | EF_TELEPORT_BIT); + + setmodel(_slot.vehicle_hudmodel, _hud_model); + setmodel(_slot.vehicle_viewport, "null"); + + setattachment(_slot.vehicle_hudmodel, _slot, ""); + setattachment(_slot.vehicle_viewport, _slot.vehicle_hudmodel, ""); + - return TRUE; ++ return true; +} + +vector vehicle_aimturret(entity _vehic, vector _target, entity _turrret, string _tagname, + float _pichlimit_min, float _pichlimit_max, + float _rotlimit_min, float _rotlimit_max, float _aimspeed) +{ + vector vtmp, vtag; + float ftmp; + vtag = gettaginfo(_turrret, gettagindex(_turrret, _tagname)); + vtmp = vectoangles(normalize(_target - vtag)); + vtmp = AnglesTransform_ToAngles(AnglesTransform_LeftDivide(AnglesTransform_FromAngles(_vehic.angles), AnglesTransform_FromAngles(vtmp))) - _turrret.angles; - vtmp = AnglesTransform_Normalize(vtmp, TRUE); ++ vtmp = AnglesTransform_Normalize(vtmp, true); + ftmp = _aimspeed * frametime; + vtmp_y = bound(-ftmp, vtmp_y, ftmp); + vtmp_x = bound(-ftmp, vtmp_x, ftmp); + _turrret.angles_y = bound(_rotlimit_min, _turrret.angles_y + vtmp_y, _rotlimit_max); + _turrret.angles_x = bound(_pichlimit_min, _turrret.angles_x + vtmp_x, _pichlimit_max); + return vtag; +} + +void vehicles_reset_colors() +{ + entity e; + float _effects = 0, _colormap; + vector _glowmod, _colormod; + + if(autocvar_g_nodepthtestplayers) + _effects |= EF_NODEPTHTEST; + + if(autocvar_g_fullbrightplayers) + _effects |= EF_FULLBRIGHT; + + if(self.team) + _colormap = 1024 + (self.team - 1) * 17; + else + _colormap = 1024; + + _glowmod = '0 0 0'; + _colormod = '0 0 0'; + + // Find all ents attacked to main model and setup effects, colormod etc. + e = findchainentity(tag_entity, self); + while(e) + { + if(e != self.vehicle_shieldent) + { + e.effects = _effects; // | EF_LOWPRECISION; + e.colormod = _colormod; + e.colormap = _colormap; + e.alpha = 1; + } + e = e.chain; + } + // Also check head tags + e = findchainentity(tag_entity, self.tur_head); + while(e) + { + if(e != self.vehicle_shieldent) + { + e.effects = _effects; // | EF_LOWPRECISION; + e.colormod = _colormod; + e.colormap = _colormap; + e.alpha = 1; + } + e = e.chain; + } + + self.vehicle_hudmodel.effects = self.effects = _effects; // | EF_LOWPRECISION; + self.vehicle_hudmodel.colormod = self.colormod = _colormod; + self.vehicle_hudmodel.colormap = self.colormap = _colormap; + self.vehicle_viewport.effects = (EF_ADDITIVE | EF_DOUBLESIDED | EF_FULLBRIGHT | EF_NODEPTHTEST | EF_NOGUNBOB | EF_NOSHADOW | EF_LOWPRECISION | EF_SELECTABLE | EF_TELEPORT_BIT); + + self.alpha = 1; + self.avelocity = '0 0 0'; + self.velocity = '0 0 0'; + self.effects = _effects; +} + +void vehicles_clearreturn(entity veh) +{ + entity ret; + // Remove "return helper", if any. + ret = findchain(classname, "vehicle_return"); + while(ret) + { + if(ret.wp00 == veh) + { + ret.classname = ""; + ret.think = SUB_Remove; + ret.nextthink = time + 0.1; + + if(ret.waypointsprite_attached) + WaypointSprite_Kill(ret.waypointsprite_attached); + + return; + } + ret = ret.chain; + } +} + +void vehicles_spawn(); +void vehicles_return() +{ + Send_Effect(EFFECT_TELEPORT, self.wp00.origin + '0 0 64', '0 0 0', 1); + + self.wp00.think = vehicles_spawn; + self.wp00.nextthink = time; + + if(self.waypointsprite_attached) + WaypointSprite_Kill(self.waypointsprite_attached); + + remove(self); +} + +void vehicles_showwp_goaway() +{ + if(self.waypointsprite_attached) + WaypointSprite_Kill(self.waypointsprite_attached); + + remove(self); + +} + +void vehicles_showwp() +{ + entity oldself = world; + vector rgb; + + if(self.cnt) + { + self.think = vehicles_return; + self.nextthink = self.cnt; + } + else + { + self.think = vehicles_return; + self.nextthink = time +1; + + oldself = self; + self = spawn(); + setmodel(self, "null"); + self.team = oldself.wp00.team; + self.wp00 = oldself.wp00; + setorigin(self, oldself.wp00.pos1); + + self.nextthink = time + 5; + self.think = vehicles_showwp_goaway; + } + + if(teamplay && self.team) + rgb = Team_ColorRGB(self.team); + else + rgb = '1 1 1'; - WaypointSprite_Spawn("vehicle", 0, 0, self, '0 0 64', world, 0, self, waypointsprite_attached, TRUE, RADARICON_POWERUP, rgb); ++ WaypointSprite_Spawn("vehicle", 0, 0, self, '0 0 64', world, 0, self, waypointsprite_attached, true, RADARICON_POWERUP, rgb); + if(self.waypointsprite_attached) + { + WaypointSprite_UpdateRule(self.waypointsprite_attached, self.wp00.team, SPRITERULE_DEFAULT); + if(oldself == world) + WaypointSprite_UpdateBuildFinished(self.waypointsprite_attached, self.nextthink); + WaypointSprite_Ping(self.waypointsprite_attached); + } + + if(oldself != world) + self = oldself; +} + +void vehicles_setreturn(entity veh) +{ + entity ret; + + vehicles_clearreturn(veh); + + ret = spawn(); + ret.classname = "vehicle_return"; + ret.wp00 = veh; + ret.team = veh.team; + ret.think = vehicles_showwp; + + if(veh.deadflag != DEAD_NO) + { + ret.cnt = time + veh.respawntime; + ret.nextthink = min(time + veh.respawntime, time + veh.respawntime - 5); + } + else + { + ret.nextthink = min(time + veh.respawntime, time + veh.respawntime - 1); + } + + setmodel(ret, "null"); + setorigin(ret, veh.pos1 + '0 0 96'); + +} + +void vehicle_use() +{ + dprint("vehicle ",self.netname, " used by ", activator.classname, "\n"); + + self.tur_head.team = activator.team; + + if(self.tur_head.team == 0) + self.active = ACTIVE_NOT; + else + self.active = ACTIVE_ACTIVE; + + if(self.active == ACTIVE_ACTIVE && self.deadflag == DEAD_NO && !gameover) + { + dprint("Respawning vehicle: ", self.netname, "\n"); + if(self.effects & EF_NODRAW) + { + self.think = vehicles_spawn; + self.nextthink = time + 3; + } + else + { + vehicles_setreturn(self); + vehicles_reset_colors(); + } + } +} + +void vehicles_regen(float timer, .float regen_field, float field_max, float rpause, float regen, float delta_time, float _healthscale) +{ + if(self.regen_field < field_max) + if(timer + rpause < time) + { + if(_healthscale) + regen = regen * (self.vehicle_health / self.max_health); + + self.regen_field = min(self.regen_field + regen * delta_time, field_max); + + if(self.owner) + self.owner.regen_field = (self.regen_field / field_max) * 100; + } +} + +void shieldhit_think() +{ + self.alpha -= 0.1; + if (self.alpha <= 0) + { + //setmodel(self, ""); + self.alpha = -1; + self.effects |= EF_NODRAW; + } + else + { + self.nextthink = time + 0.1; + } +} + +void vehicles_painframe() +{ + if(self.owner.vehicle_health <= 50) + if(self.pain_frame < time) + { + float _ftmp; + _ftmp = self.owner.vehicle_health / 50; + self.pain_frame = time + 0.1 + (random() * 0.5 * _ftmp); + pointparticles(particleeffectnum("smoke_small"), (self.origin + (randomvec() * 80)), '0 0 0', 1); + + if(self.vehicle_flags & VHF_DMGSHAKE) + self.velocity += randomvec() * 30; + + if(self.vehicle_flags & VHF_DMGROLL) + if(self.vehicle_flags & VHF_DMGHEADROLL) + self.tur_head.angles += randomvec(); + else + self.angles += randomvec(); + + } +} + +void vehicles_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force) +{ + self.dmg_time = time; + + // WEAPONTODO + if(DEATH_ISWEAPON(deathtype, WEP_VORTEX)) + damage *= autocvar_g_vehicles_vortex_damagerate; + + if(DEATH_ISWEAPON(deathtype, WEP_MACHINEGUN)) + damage *= autocvar_g_vehicles_machinegun_damagerate; + + if(DEATH_ISWEAPON(deathtype, WEP_RIFLE)) + damage *= autocvar_g_vehicles_rifle_damagerate; + + if(DEATH_ISWEAPON(deathtype, WEP_VAPORIZER)) + damage *= autocvar_g_vehicles_vaporizer_damagerate; + + if(DEATH_ISWEAPON(deathtype, WEP_SEEKER)) + damage *= autocvar_g_vehicles_tag_damagerate; + + self.enemy = attacker; + + self.pain_finished = time; + + if((self.vehicle_flags & VHF_HASSHIELD) && (self.vehicle_shield > 0)) + { + if (wasfreed(self.vehicle_shieldent) || self.vehicle_shieldent == world) + { + self.vehicle_shieldent = spawn(); + self.vehicle_shieldent.effects = EF_LOWPRECISION; + + setmodel(self.vehicle_shieldent, "models/vhshield.md3"); + setattachment(self.vehicle_shieldent, self, ""); + setorigin(self.vehicle_shieldent, real_origin(self) - self.origin); + self.vehicle_shieldent.scale = 256 / vlen(self.maxs - self.mins); + self.vehicle_shieldent.think = shieldhit_think; + } + + self.vehicle_shieldent.colormod = '1 1 1'; + self.vehicle_shieldent.alpha = 0.45; + self.vehicle_shieldent.angles = vectoangles(normalize(hitloc - (self.origin + self.vehicle_shieldent.origin))) - self.angles; + self.vehicle_shieldent.nextthink = time; + self.vehicle_shieldent.effects &= ~EF_NODRAW; + + self.vehicle_shield -= damage; + + if(self.vehicle_shield < 0) + { + self.vehicle_health -= fabs(self.vehicle_shield); + self.vehicle_shieldent.colormod = '2 0 0'; + self.vehicle_shield = 0; + self.vehicle_shieldent.alpha = 0.75; + + if(sound_allowed(MSG_BROADCAST, attacker)) + spamsound (self, CH_PAIN, "onslaught/ons_hit2.wav", VOL_BASE, ATTEN_NORM); // FIXME: PLACEHOLDER + } + else + if(sound_allowed(MSG_BROADCAST, attacker)) + spamsound (self, CH_PAIN, "onslaught/electricity_explode.wav", VOL_BASE, ATTEN_NORM); // FIXME: PLACEHOLDER + + } + else + { + self.vehicle_health -= damage; + + if(sound_allowed(MSG_BROADCAST, attacker)) + spamsound (self, CH_PAIN, "onslaught/ons_hit2.wav", VOL_BASE, ATTEN_NORM); // FIXME: PLACEHOLDER + } + + if(self.damageforcescale < 1 && self.damageforcescale > 0) + self.velocity += force * self.damageforcescale; + else + self.velocity += force; + + if(self.vehicle_health <= 0) + { + if(self.owner) + if(self.vehicle_flags & VHF_DEATHEJECT) + vehicles_exit(VHEF_EJECT); + else + vehicles_exit(VHEF_RELEASE); + + + antilag_clear(self); + + VEH_ACTION(self.vehicleid, VR_DEATH); + vehicles_setreturn(self); + } +} + +float vehicles_crushable(entity e) +{ + if(IS_PLAYER(e)) - return TRUE; ++ return true; + + if(IS_MONSTER(e)) - return TRUE; ++ return true; + - return FALSE; ++ return false; +} + +void vehicles_impact(float _minspeed, float _speedfac, float _maxpain) +{ + if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT) + return; + + if(self.play_time < time) + { + float wc = vlen(self.velocity - self.oldvelocity); + //dprint("oldvel: ", vtos(self.oldvelocity), "\n"); + //dprint("vel: ", vtos(self.velocity), "\n"); + if(_minspeed < wc) + { + float take = min(_speedfac * wc, _maxpain); + Damage (self, world, world, take, DEATH_FALL, self.origin, '0 0 0'); + self.play_time = time + 0.25; + + //dprint("wc: ", ftos(wc), "\n"); + //dprint("take: ", ftos(take), "\n"); + } + } +} + +// vehicle enter/exit handling +vector vehicles_findgoodexit(vector prefer_spot) +{ + //vector exitspot; + float mysize; + + tracebox(self.origin + '0 0 32', PL_MIN, PL_MAX, prefer_spot, MOVE_NORMAL, self.owner); + if(trace_fraction == 1.0 && !trace_startsolid && !trace_allsolid) + return prefer_spot; + + mysize = 1.5 * vlen(self.maxs - self.mins); + float i; + vector v, v2; + v2 = 0.5 * (self.absmin + self.absmax); + for(i = 0; i < 100; ++i) + { + v = randomvec(); + v_z = 0; + v = v2 + normalize(v) * mysize; + tracebox(v2, PL_MIN, PL_MAX, v, MOVE_NORMAL, self.owner); + if(trace_fraction == 1.0 && !trace_startsolid && !trace_allsolid) + return v; + } + + /* + exitspot = (self.origin + '0 0 48') + v_forward * mysize; + tracebox(self.origin + '0 0 32', PL_MIN, PL_MAX, exitspot, MOVE_NORMAL, self.owner); + if(trace_fraction == 1.0 && !trace_startsolid && !trace_allsolid) + return exitspot; + + exitspot = (self.origin + '0 0 48') - v_forward * mysize; + tracebox(self.origin + '0 0 32', PL_MIN, PL_MAX, exitspot, MOVE_NORMAL, self.owner); + if(trace_fraction == 1.0 && !trace_startsolid && !trace_allsolid) + return exitspot; + + exitspot = (self.origin + '0 0 48') + v_right * mysize; + tracebox(self.origin + '0 0 32', PL_MIN, PL_MAX, exitspot, MOVE_NORMAL, self.owner); + if(trace_fraction == 1.0 && !trace_startsolid && !trace_allsolid) + return exitspot; + + exitspot = (self.origin + '0 0 48') - v_right * mysize; + tracebox(self.origin + '0 0 32', PL_MIN, PL_MAX, exitspot, MOVE_NORMAL, self.owner); + if(trace_fraction == 1.0 && !trace_startsolid && !trace_allsolid) + return exitspot; + */ + + return self.origin; +} + +void vehicles_exit(float eject) +{ + entity _vehicle; + entity _player; + entity _oldself = self; + + if(vehicles_exit_running) + { + dprint("^1vehicles_exit allready running! this is not good..\n"); + return; + } + - vehicles_exit_running = TRUE; ++ vehicles_exit_running = true; + if(IS_CLIENT(self)) + { + _vehicle = self.vehicle; + + if (_vehicle.vehicle_flags & VHF_PLAYERSLOT) + { + _vehicle.vehicle_exit(eject); + self = _oldself; - vehicles_exit_running = FALSE; ++ vehicles_exit_running = false; + return; + } + } + else + _vehicle = self; + + _player = _vehicle.owner; + + self = _vehicle; + + if (_player) + { + if (IS_REAL_CLIENT(_player)) + { + msg_entity = _player; + WriteByte (MSG_ONE, SVC_SETVIEWPORT); + WriteEntity( MSG_ONE, _player); + + WriteByte (MSG_ONE, SVC_SETVIEWANGLES); + WriteAngle(MSG_ONE, 0); + WriteAngle(MSG_ONE, _vehicle.angles_y); + WriteAngle(MSG_ONE, 0); + } + + setsize(_player, PL_MIN,PL_MAX); + + _player.takedamage = DAMAGE_AIM; + _player.solid = SOLID_SLIDEBOX; + _player.movetype = MOVETYPE_WALK; + _player.effects &= ~EF_NODRAW; + _player.teleportable = TELEPORT_NORMAL; + _player.alpha = 1; + _player.PlayerPhysplug = func_null; + _player.vehicle = world; + _player.view_ofs = PL_VIEW_OFS; + _player.event_damage = PlayerDamage; + _player.hud = HUD_NORMAL; + _player.switchweapon = _vehicle.switchweapon; + _player.last_vehiclecheck = time + 3; + + CSQCVehicleSetup(_player, HUD_NORMAL); + } + _vehicle.flags |= FL_NOTARGET; + + if(_vehicle.deadflag == DEAD_NO) + _vehicle.avelocity = '0 0 0'; + + _vehicle.tur_head.nodrawtoclient = world; + + if(!teamplay) + _vehicle.team = 0; + + Kill_Notification(NOTIF_ONE, _player, MSG_CENTER_CPID, CPID_VEHICLES); + Kill_Notification(NOTIF_ONE, _player, MSG_CENTER_CPID, CPID_VEHICLES_OTHER); // kill all vehicle notifications when exiting a vehicle? + + WaypointSprite_Kill(_vehicle.wps_intruder); + + vh_player = _player; + vh_vehicle = _vehicle; + MUTATOR_CALLHOOK(VehicleExit); + _player = vh_player; + _vehicle = vh_vehicle; + + _vehicle.team = _vehicle.tur_head.team; + + sound (_vehicle, CH_TRIGGER_SINGLE, "misc/null.wav", 1, ATTEN_NORM); + _vehicle.vehicle_hudmodel.viewmodelforclient = _vehicle; + _vehicle.phase = time + 1; + + _vehicle.vehicle_exit(eject); + + vehicles_setreturn(_vehicle); + vehicles_reset_colors(); + _vehicle.owner = world; + + CSQCMODEL_AUTOINIT(); + + self = _oldself; + - vehicles_exit_running = FALSE; ++ vehicles_exit_running = false; +} + +void vehicles_touch() +{ + if(MUTATOR_CALLHOOK(VehicleTouch)) + return; + + // Vehicle currently in use + if(self.owner) + { + if(!forbidWeaponUse(self.owner)) + if(other != world) + if((self.origin_z + self.maxs_z) > (other.origin_z)) + if(vehicles_crushable(other)) + { + if(vlen(self.velocity) != 0) + Damage(other, self, self.owner, autocvar_g_vehicles_crush_dmg, DEATH_VH_CRUSH, '0 0 0', normalize(other.origin - self.origin) * autocvar_g_vehicles_crush_force); + + return; // Dont do selfdamage when hitting "soft targets". + } + + if(self.play_time < time) + VEH_ACTION(self.vehicleid, VR_IMPACT); + + return; + } + + if(autocvar_g_vehicles_enter) + return; + + vehicles_enter(other, self); +} + +void vehicles_enter(entity pl, entity veh) +{ + // Remove this when bots know how to use vehicles + if (IS_BOT_CLIENT(pl)) + if (autocvar_g_vehicles_allow_bots) + dprint("Bot enters vehicle\n"); // This is where we need to disconnect (some, all?) normal bot AI and hand over to vehicle's _aiframe() + else + return; + + if(!IS_PLAYER(pl)) + return; + + if(veh.phase > time) + return; + + if(pl.frozen) + return; + + if(pl.deadflag != DEAD_NO) + return; + + if(pl.vehicle) + return; + + if(autocvar_g_vehicles_enter) // skip if we're using regular touch code + if(veh.vehicle_flags & VHF_MULTISLOT) + if(veh.owner) + { + entity oldself = self; + self = veh; + other = pl; // TODO: fix + + if(!veh.gunner1) + if(veh.gun1.phase <= time) + if(veh.gun1.vehicle_enter) + if(veh.gun1.vehicle_enter()) + { + self = oldself; + return; + } + + if(!veh.gunner2) + if(veh.gun2.phase <= time) + if(veh.gun2.vehicle_enter) + if(veh.gun2.vehicle_enter()) + { + self = oldself; + return; + } + + self = oldself; + } + + if(teamplay) + if(veh.team) + if(DIFF_TEAM(pl, veh)) + if(autocvar_g_vehicles_steal) + { + entity head; + FOR_EACH_PLAYER(head) if(SAME_TEAM(head, veh)) + Send_Notification(NOTIF_ONE, head, MSG_CENTER, CENTER_VEHICLE_STEAL); + + Send_Notification(NOTIF_ONE, pl, MSG_CENTER, CENTER_VEHICLE_STEAL_SELF); + + if(autocvar_g_vehicles_steal_show_waypoint) - WaypointSprite_Spawn("intruder", 0, 0, pl, '0 0 68', world, veh.team, veh, wps_intruder, TRUE, RADARICON_DANGER, Team_ColorRGB(pl.team)); ++ WaypointSprite_Spawn("intruder", 0, 0, pl, '0 0 68', world, veh.team, veh, wps_intruder, true, RADARICON_DANGER, Team_ColorRGB(pl.team)); + } + else return; + + jeff_Announcer_VehicleEnter(pl, veh); + + RemoveGrapplingHook(pl); + + veh.vehicle_ammo1 = 0; + veh.vehicle_ammo2 = 0; + veh.vehicle_reload1 = 0; + veh.vehicle_reload2 = 0; + veh.vehicle_energy = 0; + + veh.owner = pl; + pl.vehicle = veh; + + // .viewmodelforclient works better. + //veh.vehicle_hudmodel.drawonlytoclient = veh.owner; + + veh.vehicle_hudmodel.viewmodelforclient = pl; + - tracebox(pl.origin, PL_MIN, PL_MAX, pl.origin, FALSE, pl); - pl.crouch = FALSE; ++ tracebox(pl.origin, PL_MIN, PL_MAX, pl.origin, false, pl); ++ pl.crouch = false; + pl.view_ofs = PL_VIEW_OFS; + setsize (pl, PL_MIN, PL_MAX); + + veh.event_damage = vehicles_damage; + veh.nextthink = 0; + pl.angles = veh.angles; + pl.takedamage = DAMAGE_NO; + pl.solid = SOLID_NOT; + pl.movetype = MOVETYPE_NOCLIP; - pl.teleportable = FALSE; ++ pl.teleportable = false; + pl.alpha = -1; + pl.event_damage = func_null; + pl.view_ofs = '0 0 0'; + veh.colormap = pl.colormap; + if(veh.tur_head) + veh.tur_head.colormap = pl.colormap; + veh.switchweapon = pl.switchweapon; + pl.hud = veh.vehicleid; + pl.PlayerPhysplug = veh.PlayerPhysplug; + + pl.vehicle_ammo1 = veh.vehicle_ammo1; + pl.vehicle_ammo2 = veh.vehicle_ammo2; + pl.vehicle_reload1 = veh.vehicle_reload1; + pl.vehicle_reload2 = veh.vehicle_reload2; + + // Cant do this, hides attached objects too. + //veh.exteriormodeltoclient = veh.owner; + //veh.tur_head.exteriormodeltoclient = veh.owner; + + pl.flags &= ~FL_ONGROUND; + veh.flags &= ~FL_ONGROUND; + + veh.team = pl.team; + veh.flags -= FL_NOTARGET; + + if (IS_REAL_CLIENT(pl)) + { + Send_Notification(NOTIF_ONE, pl, MSG_CENTER, CENTER_VEHICLE_ENTER); + + msg_entity = pl; + WriteByte (MSG_ONE, SVC_SETVIEWPORT); + WriteEntity(MSG_ONE, veh.vehicle_viewport); + + WriteByte (MSG_ONE, SVC_SETVIEWANGLES); + if(veh.tur_head) + { + WriteAngle(MSG_ONE, veh.tur_head.angles_x + veh.angles_x); // tilt + WriteAngle(MSG_ONE, veh.tur_head.angles_y + veh.angles_y); // yaw + WriteAngle(MSG_ONE, 0); // roll + } + else + { + WriteAngle(MSG_ONE, veh.angles_x * -1); // tilt + WriteAngle(MSG_ONE, veh.angles_y); // yaw + WriteAngle(MSG_ONE, 0); // roll + } + } + + vehicles_clearreturn(veh); + + CSQCVehicleSetup(pl, veh.vehicleid); + + vh_player = pl; + vh_vehicle = veh; + MUTATOR_CALLHOOK(VehicleEnter); + + entity oldself = self; + self = veh; + CSQCModel_UnlinkEntity(); + VEH_ACTION(veh.vehicleid, VR_ENTER); + self = oldself; + + antilag_clear(pl); +} + +void vehicles_think() +{ + self.nextthink = time; + + if(self.owner) + self.owner.vehicle_weapon2mode = self.vehicle_weapon2mode; + + VEH_ACTION(self.vehicleid, VR_THINK); + + CSQCMODEL_AUTOUPDATE(); +} + +// initialization +void vehicles_spawn() +{ + dprint("Spawning vehicle: ", self.classname, "\n"); + + // disown & reset + self.vehicle_hudmodel.viewmodelforclient = self; + + self.owner = world; + self.touch = vehicles_touch; + self.event_damage = vehicles_damage; - self.iscreature = TRUE; - self.teleportable = FALSE; // no teleporting for vehicles, too buggy - self.damagedbycontents = TRUE; ++ self.iscreature = true; ++ self.teleportable = false; // no teleporting for vehicles, too buggy ++ self.damagedbycontents = true; + self.movetype = MOVETYPE_WALK; + self.solid = SOLID_SLIDEBOX; + self.takedamage = DAMAGE_AIM; + self.deadflag = DEAD_NO; - self.bot_attack = TRUE; ++ self.bot_attack = true; + self.flags = FL_NOTARGET; + self.avelocity = '0 0 0'; + self.velocity = '0 0 0'; + self.think = vehicles_think; + self.nextthink = time; + + // Reset locking + self.lock_strength = 0; + self.lock_target = world; + self.misc_bulletcounter = 0; + + // Return to spawn + self.angles = self.pos2; + setorigin(self, self.pos1); + // Show it + Send_Effect(EFFECT_TELEPORT, self.origin + '0 0 64', '0 0 0', 1); + + if(self.vehicle_controller) + self.team = self.vehicle_controller.team; + + entity head; // remove hooks (if any) + FOR_EACH_PLAYER(head) + if(head.hook.aiment == self) + RemoveGrapplingHook(head); + + vehicles_reset_colors(); + + VEH_ACTION(self.vehicleid, VR_SPAWN); + + CSQCMODEL_AUTOINIT(); +} + +float vehicle_initialize(float vehicle_id, float nodrop) +{ + if(!autocvar_g_vehicles) - return FALSE; ++ return false; + + entity veh = get_vehicleinfo(vehicle_id); + + if(!veh.vehicleid) - return FALSE; ++ return false; + + if(!veh.tur_head) { VEH_ACTION(vehicle_id, VR_PRECACHE); } + + if(self.targetname && self.targetname != "") + { + self.vehicle_controller = find(world, target, self.targetname); + if(!self.vehicle_controller) + { + bprint("^1WARNING: ^7Vehicle with invalid .targetname\n"); + self.active = ACTIVE_ACTIVE; + } + else + { + self.team = self.vehicle_controller.team; + self.use = vehicle_use; + + if(teamplay) + { + if(self.vehicle_controller.team == 0) + self.active = ACTIVE_NOT; + else + self.active = ACTIVE_ACTIVE; + } + } + } + else { self.active = ACTIVE_ACTIVE; } + + if(self.team && (!teamplay || !autocvar_g_vehicles_teams)) + self.team = 0; + + self.vehicle_flags |= VHF_ISVEHICLE; + + setmodel(self, veh.model); + + self.vehicle_viewport = spawn(); + self.vehicle_hudmodel = spawn(); + self.tur_head = spawn(); + self.tur_head.owner = self; + self.takedamage = DAMAGE_NO; - self.bot_attack = TRUE; - self.iscreature = TRUE; - self.teleportable = FALSE; // no teleporting for vehicles, too buggy - self.damagedbycontents = TRUE; ++ self.bot_attack = true; ++ self.iscreature = true; ++ self.teleportable = false; // no teleporting for vehicles, too buggy ++ self.damagedbycontents = true; + self.vehicleid = vehicle_id; + self.PlayerPhysplug = veh.PlayerPhysplug; + self.event_damage = func_null; + self.touch = vehicles_touch; + self.think = vehicles_spawn; + self.nextthink = time; + self.effects = EF_NODRAW; + self.dphitcontentsmask = DPCONTENTS_BODY | DPCONTENTS_SOLID; + + if(autocvar_g_playerclip_collisions) + self.dphitcontentsmask |= DPCONTENTS_PLAYERCLIP; + + if(autocvar_g_nodepthtestplayers) + self.effects |= EF_NODEPTHTEST; + + if(autocvar_g_fullbrightplayers) + self.effects |= EF_FULLBRIGHT; + + setmodel(self.vehicle_hudmodel, veh.hud_model); + setmodel(self.vehicle_viewport, "null"); + + if(veh.head_model != "") + { + setmodel(self.tur_head, veh.head_model); + setattachment(self.tur_head, self, veh.tag_head); + setattachment(self.vehicle_hudmodel, self.tur_head, veh.tag_hud); + setattachment(self.vehicle_viewport, self.vehicle_hudmodel, veh.tag_view); + } + else + { + setattachment(self.tur_head, self, ""); + setattachment(self.vehicle_hudmodel, self, veh.tag_hud); + setattachment(self.vehicle_viewport, self.vehicle_hudmodel, veh.tag_view); + } + + setsize(self, veh.mins, veh.maxs); + + if(!nodrop) + { + setorigin(self, self.origin); + tracebox(self.origin + '0 0 100', veh.mins, veh.maxs, self.origin - '0 0 10000', MOVE_WORLDONLY, self); + setorigin(self, trace_endpos); + } + + self.pos1 = self.origin; + self.pos2 = self.angles; + self.tur_head.team = self.team; + + VEH_ACTION(vehicle_id, VR_SETUP); + + if(self.active == ACTIVE_NOT) + self.nextthink = 0; // wait until activated + else if(autocvar_g_vehicles_delayspawn) + self.nextthink = time + self.respawntime + (random() * autocvar_g_vehicles_delayspawn_jitter); + else + self.nextthink = time + game_starttime; + + if(MUTATOR_CALLHOOK(VehicleSpawn)) - return FALSE; ++ return false; + - return TRUE; ++ return true; +} diff --cc qcsrc/common/vehicles/sv_vehicles.qh index 2e8844eef,000000000..4e339e460 mode 100644,000000..100644 --- a/qcsrc/common/vehicles/sv_vehicles.qh +++ b/qcsrc/common/vehicles/sv_vehicles.qh @@@ -1,104 -1,0 +1,112 @@@ ++#ifndef VEHICLES_DEF_H ++#define VEHICLES_DEF_H ++#ifdef SVQC ++ ++#include "../turrets/sv_turrets.qh" ++#include "sv_vehicles.qh" ++ +// #define VEHICLES_USE_ODE + +// vehicle cvars +float autocvar_g_vehicles; +float autocvar_g_vehicles_enter; +float autocvar_g_vehicles_enter_radius; +float autocvar_g_vehicles_extra; +float autocvar_g_vehicles_steal; +float autocvar_g_vehicles_steal_show_waypoint; +float autocvar_g_vehicles_crush_dmg; +float autocvar_g_vehicles_crush_force; +float autocvar_g_vehicles_delayspawn; +float autocvar_g_vehicles_delayspawn_jitter; +float autocvar_g_vehicles_allow_bots; +float autocvar_g_vehicles_teams; +float autocvar_g_vehicles_teleportable; +var float autocvar_g_vehicles_vortex_damagerate = 0.5; +var float autocvar_g_vehicles_machinegun_damagerate = 0.5; +var float autocvar_g_vehicles_rifle_damagerate = 0.75; +var float autocvar_g_vehicles_vaporizer_damagerate = 0.001; +var float autocvar_g_vehicles_tag_damagerate = 5; + ++// flags: ++.int vehicle_flags; ++ +// vehicle definitions +.entity gun1; +.entity gun2; +.entity gun3; +.entity vehicle_shieldent; /// Entity to disply the shild effect on damage +.entity vehicle; +.entity vehicle_viewport; +.entity vehicle_hudmodel; +.entity vehicle_controller; + +.entity gunner1; +.entity gunner2; + +.float vehicle_health; /// If self is player this is 0..100 indicating precentage of health left on vehicle. If self is vehile, this is the real health value. +.float vehicle_energy; /// If self is player this is 0..100 indicating precentage of energy left on vehicle. If self is vehile, this is the real energy value. +.float vehicle_shield; /// If self is player this is 0..100 indicating precentage of shield left on vehicle. If self is vehile, this is the real shield value. + +.float vehicle_ammo1; /// If self is player this field's use depends on the individual vehile. If self is vehile, this is the real ammo1 value. +.float vehicle_reload1; /// If self is player this field's use depends on the individual vehile. If self is vehile, this is the real reload1 value. +.float vehicle_ammo2; /// If self is player this field's use depends on the individual vehile. If self is vehile, this is the real ammo2 value. +.float vehicle_reload2; /// If self is player this field's use depends on the individual vehile. If self is vehile, this is the real reload2 value. + +.float sound_nexttime; - #define VOL_VEHICLEENGINE 1 ++const float VOL_VEHICLEENGINE = 1; + +const float SVC_SETVIEWPORT = 5; // Net.Protocol 0x05 +const float SVC_SETVIEWANGLES = 10; // Net.Protocol 0x0A +const float SVC_UPDATEENTITY = 128; // Net.Protocol 0x80 + - #define VHSF_NORMAL 0 - #define VHSF_FACTORY 2 ++const float VHSF_NORMAL = 0; ++const float VHSF_FACTORY = 2; + - .float hud; ++.int hud; +.float dmg_time; + - .float volly_counter; ++.int volly_counter; + - const float MAX_AXH = 4; ++const int MAX_AXH = 4; +.entity AuxiliaryXhair[MAX_AXH]; + +.entity wps_intruder; + +.entity lock_target; +.float lock_strength; +.float lock_time; +.float lock_soundtime; +const float DAMAGE_TARGETDRONE = 10; + ++// vehicle functions ++.void(int _spawnflag) vehicle_spawn; /// Vehicles custom fucntion to be efecuted when vehicle (re)spawns ++.bool(int _imp) vehicles_impulse; ++.int vehicle_weapon2mode; ++.void(int exit_flags) vehicle_exit; ++.bool() vehicle_enter; ++const int VHEF_NORMAL = 0; /// User pressed exit key ++const int VHEF_EJECT = 1; /// User pressed exit key 3 times fast (not implemented) or vehile is dying ++const int VHEF_RELEASE = 2; /// Release ownership, client possibly allready dissconnected / went spec / changed team / used "kill" (not implemented) ++ +float force_fromtag_power; +float force_fromtag_normpower; +vector force_fromtag_origin; + +float vehicles_exit_running; + ++// macros ++#define VEHICLE_UPDATE_PLAYER(ply,fld,vhname) \ ++ ply.vehicle_##fld = (self.vehicle_##fld / autocvar_g_vehicle_##vhname##_##fld) * 100 ++ +#ifdef VEHICLES_USE_ODE +void(entity e, float physics_enabled) physics_enable = #540; // enable or disable physics on object +void(entity e, vector force, vector force_pos) physics_addforce = #541; // apply a force from certain origin, length of force vector is power of force +void(entity e, vector torque) physics_addtorque = #542; // add relative torque +#endif // VEHICLES_USE_ODE + - // functions used outside the vehicle code +void vehicles_exit(float eject); - void vehicles_enter(entity pl, entity veh); - - // vehicle functions - .void(float _spawnflag) vehicle_spawn; /// Vehicles custom fucntion to be efecuted when vehicle (re)spawns - .float(float _imp) vehicles_impulse; - .float vehicle_weapon2mode; - .void(float exit_flags) vehicle_exit; - .float() vehicle_enter; - const float VHEF_NORMAL = 0; /// User pressed exit key - const float VHEF_EJECT = 1; /// User pressed exit key 3 times fast (not implemented) or vehile is dying - const float VHEF_RELEASE = 2; /// Release ownership, client possibly allready dissconnected / went spec / changed team / used "kill" (not implemented) ++float vehicle_initialize(float vehicle_id, float nodrop); + - // macros - #define VEHICLE_UPDATE_PLAYER(ply,fld,vhname) \ - ply.vehicle_##fld = (self.vehicle_##fld / autocvar_g_vehicle_##vhname##_##fld) * 100 ++#endif + - #define vehicles_sweap_collision(orig,vel,dt,acm,mult) \ - traceline(orig, orig + vel * dt, MOVE_NORMAL, self); \ - if(trace_fraction != 1) \ - acm += normalize(self.origin - trace_endpos) * (vlen(vel) * mult) ++#endif diff --cc qcsrc/common/vehicles/unit/bumblebee.qc index cd76b30d8,000000000..8bc60818b mode 100644,000000..100644 --- a/qcsrc/common/vehicles/unit/bumblebee.qc +++ b/qcsrc/common/vehicles/unit/bumblebee.qc @@@ -1,1388 -1,0 +1,1388 @@@ +#ifdef REGISTER_VEHICLE +REGISTER_VEHICLE( +/* VEH_##id */ BUMBLEBEE, +/* function */ v_bumblebee, +/* spawnflags */ VHF_DMGSHAKE, +/* mins,maxs */ '-245 -130 -130', '230 130 130', +/* model */ "models/vehicles/bumblebee_body.dpm", +/* head_model */ "", +/* hud_model */ "models/vehicles/spiderbot_cockpit.dpm", +/* tags */ "", "", "tag_viewport", +/* netname */ "bumblebee", +/* fullname */ _("Bumblebee") +); +#else + +const float BRG_SETUP = 2; +const float BRG_START = 4; +const float BRG_END = 8; + +#ifdef SVQC ++#include "../../effects.qh" ++ +float autocvar_g_vehicle_bumblebee_speed_forward; +float autocvar_g_vehicle_bumblebee_speed_strafe; +float autocvar_g_vehicle_bumblebee_speed_up; +float autocvar_g_vehicle_bumblebee_speed_down; +float autocvar_g_vehicle_bumblebee_turnspeed; +float autocvar_g_vehicle_bumblebee_pitchspeed; +float autocvar_g_vehicle_bumblebee_pitchlimit; +float autocvar_g_vehicle_bumblebee_friction; + +float autocvar_g_vehicle_bumblebee_energy; +float autocvar_g_vehicle_bumblebee_energy_regen; +float autocvar_g_vehicle_bumblebee_energy_regen_pause; + +float autocvar_g_vehicle_bumblebee_health; +float autocvar_g_vehicle_bumblebee_health_regen; +float autocvar_g_vehicle_bumblebee_health_regen_pause; + +float autocvar_g_vehicle_bumblebee_shield; +float autocvar_g_vehicle_bumblebee_shield_regen; +float autocvar_g_vehicle_bumblebee_shield_regen_pause; + +float autocvar_g_vehicle_bumblebee_cannon_cost; +float autocvar_g_vehicle_bumblebee_cannon_damage; +float autocvar_g_vehicle_bumblebee_cannon_radius; +float autocvar_g_vehicle_bumblebee_cannon_refire; +float autocvar_g_vehicle_bumblebee_cannon_speed; +float autocvar_g_vehicle_bumblebee_cannon_spread; +float autocvar_g_vehicle_bumblebee_cannon_force; + +float autocvar_g_vehicle_bumblebee_cannon_ammo; +float autocvar_g_vehicle_bumblebee_cannon_ammo_regen; +float autocvar_g_vehicle_bumblebee_cannon_ammo_regen_pause; + - var float autocvar_g_vehicle_bumblebee_cannon_lock = 0; ++float autocvar_g_vehicle_bumblebee_cannon_lock = 0; + +float autocvar_g_vehicle_bumblebee_cannon_turnspeed; +float autocvar_g_vehicle_bumblebee_cannon_pitchlimit_down; +float autocvar_g_vehicle_bumblebee_cannon_pitchlimit_up; +float autocvar_g_vehicle_bumblebee_cannon_turnlimit_in; +float autocvar_g_vehicle_bumblebee_cannon_turnlimit_out; + + +float autocvar_g_vehicle_bumblebee_raygun_turnspeed; +float autocvar_g_vehicle_bumblebee_raygun_pitchlimit_down; +float autocvar_g_vehicle_bumblebee_raygun_pitchlimit_up; +float autocvar_g_vehicle_bumblebee_raygun_turnlimit_sides; + +float autocvar_g_vehicle_bumblebee_raygun_range; +float autocvar_g_vehicle_bumblebee_raygun_dps; +float autocvar_g_vehicle_bumblebee_raygun_aps; +float autocvar_g_vehicle_bumblebee_raygun_fps; + +float autocvar_g_vehicle_bumblebee_raygun; +float autocvar_g_vehicle_bumblebee_healgun_hps; +float autocvar_g_vehicle_bumblebee_healgun_hmax; +float autocvar_g_vehicle_bumblebee_healgun_aps; +float autocvar_g_vehicle_bumblebee_healgun_amax; +float autocvar_g_vehicle_bumblebee_healgun_sps; +float autocvar_g_vehicle_bumblebee_healgun_locktime; + +float autocvar_g_vehicle_bumblebee_respawntime; + +float autocvar_g_vehicle_bumblebee_blowup_radius; +float autocvar_g_vehicle_bumblebee_blowup_coredamage; +float autocvar_g_vehicle_bumblebee_blowup_edgedamage; +float autocvar_g_vehicle_bumblebee_blowup_forceintensity; - var vector autocvar_g_vehicle_bumblebee_bouncepain; - - var float autocvar_g_vehicle_bumblebee = 0; ++vector autocvar_g_vehicle_bumblebee_bouncepain; + ++bool autocvar_g_vehicle_bumblebee = 0; + +float bumble_raygun_send(entity to, float sf); + +void bumblebee_fire_cannon(entity _gun, string _tagname, entity _owner) +{ + vector v = gettaginfo(_gun, gettagindex(_gun, _tagname)); + vehicles_projectile("bigplasma_muzzleflash", W_Sound("flacexp3"), + v, normalize(v_forward + randomvec() * autocvar_g_vehicle_bumblebee_cannon_spread) * autocvar_g_vehicle_bumblebee_cannon_speed, + autocvar_g_vehicle_bumblebee_cannon_damage, autocvar_g_vehicle_bumblebee_cannon_radius, autocvar_g_vehicle_bumblebee_cannon_force, 0, - DEATH_VH_BUMB_GUN, PROJECTILE_BUMBLE_GUN, 0, TRUE, TRUE, _owner); ++ DEATH_VH_BUMB_GUN, PROJECTILE_BUMBLE_GUN, 0, true, true, _owner); +} + +float bumblebee_gunner_frame() +{ + entity vehic = self.vehicle.owner; + entity gun = self.vehicle; + entity gunner = self; + self = vehic; + + vehic.solid = SOLID_NOT; + //setorigin(gunner, vehic.origin); + gunner.velocity = vehic.velocity; + + float _in, _out; + vehic.angles_x *= -1; + makevectors(vehic.angles); + vehic.angles_x *= -1; + if((gun == vehic.gun1)) + { + _in = autocvar_g_vehicle_bumblebee_cannon_turnlimit_in; + _out = autocvar_g_vehicle_bumblebee_cannon_turnlimit_out; + setorigin(gunner, vehic.origin + v_up * -16 + v_forward * -16 + v_right * 128); + } + else + { + _in = autocvar_g_vehicle_bumblebee_cannon_turnlimit_out; + _out = autocvar_g_vehicle_bumblebee_cannon_turnlimit_in; + setorigin(gunner, vehic.origin + v_up * -16 + v_forward * -16 + v_right * -128); + } + + crosshair_trace(gunner); + vector _ct = trace_endpos; + vector ad; + + if(autocvar_g_vehicle_bumblebee_cannon_lock) + { + if(gun.lock_time < time) + gun.enemy = world; + + if(trace_ent) + if(trace_ent.movetype) + if(trace_ent.takedamage) + if(!trace_ent.deadflag) + { + if(teamplay) + { + if(trace_ent.team != gunner.team) + { + gun.enemy = trace_ent; + gun.lock_time = time + 5; + } + } + else + { + gun.enemy = trace_ent; + gun.lock_time = time + 5; + } + } + } + + if(gun.enemy) + { + float distance, impact_time; + + vector vf = real_origin(gun.enemy); + vector _vel = gun.enemy.velocity; + if(gun.enemy.movetype == MOVETYPE_WALK) - _vel_z *= 0.1; ++ _vel.z *= 0.1; + + + ad = vf; + distance = vlen(ad - gunner.origin); + impact_time = distance / autocvar_g_vehicle_bumblebee_cannon_speed; + ad = vf + _vel * impact_time; + trace_endpos = ad; + + + UpdateAuxiliaryXhair(gunner, ad, '1 0 1', 1); + vehicle_aimturret(vehic, trace_endpos, gun, "fire", + autocvar_g_vehicle_bumblebee_cannon_pitchlimit_down * -1, autocvar_g_vehicle_bumblebee_cannon_pitchlimit_up, + _out * -1, _in, autocvar_g_vehicle_bumblebee_cannon_turnspeed); + + } + else + vehicle_aimturret(vehic, _ct, gun, "fire", + autocvar_g_vehicle_bumblebee_cannon_pitchlimit_down * -1, autocvar_g_vehicle_bumblebee_cannon_pitchlimit_up, + _out * -1, _in, autocvar_g_vehicle_bumblebee_cannon_turnspeed); + + if(!forbidWeaponUse(gunner)) + if(gunner.BUTTON_ATCK) + if(time > gun.attack_finished_single) + if(gun.vehicle_energy >= autocvar_g_vehicle_bumblebee_cannon_cost) + { + gun.vehicle_energy -= autocvar_g_vehicle_bumblebee_cannon_cost; + bumblebee_fire_cannon(gun, "fire", gunner); + gun.delay = time; + gun.attack_finished_single = time + autocvar_g_vehicle_bumblebee_cannon_refire; + } + + VEHICLE_UPDATE_PLAYER(gunner, health, bumblebee); + + if(vehic.vehicle_flags & VHF_HASSHIELD) + VEHICLE_UPDATE_PLAYER(gunner, shield, bumblebee); + + ad = gettaginfo(gun, gettagindex(gun, "fire")); + traceline(ad, ad + v_forward * MAX_SHOT_DISTANCE, MOVE_NORMAL, gun); + + UpdateAuxiliaryXhair(gunner, trace_endpos, ('1 0 0' * gunner.vehicle_reload1) + ('0 1 0' *(1 - gunner.vehicle_reload1)), 0); + + if(vehic.owner) + UpdateAuxiliaryXhair(vehic.owner, trace_endpos, ('1 0 0' * gunner.vehicle_reload1) + ('0 1 0' *(1 - gunner.vehicle_reload1)), ((gunner == vehic.gunner1) ? 1 : 2)); + + vehic.solid = SOLID_BBOX; + gunner.BUTTON_ATCK = gunner.BUTTON_ATCK2 = gunner.BUTTON_CROUCH = 0; + gunner.vehicle_energy = (gun.vehicle_energy / autocvar_g_vehicle_bumblebee_cannon_ammo) * 100; + + self = gunner; + return 1; +} + +void bumblebee_gunner_exit(float _exitflag) +{ + if(IS_REAL_CLIENT(self)) + { + msg_entity = self; + WriteByte(MSG_ONE, SVC_SETVIEWPORT); + WriteEntity(MSG_ONE, self); + + WriteByte(MSG_ONE, SVC_SETVIEWANGLES); + WriteAngle(MSG_ONE, 0); - WriteAngle(MSG_ONE, self.vehicle.angles_y); ++ WriteAngle(MSG_ONE, self.vehicle.angles.y); + WriteAngle(MSG_ONE, 0); + } + + CSQCVehicleSetup(self, HUD_NORMAL); + setsize(self, PL_MIN, PL_MAX); + + self.takedamage = DAMAGE_AIM; + self.solid = SOLID_SLIDEBOX; + self.movetype = MOVETYPE_WALK; + self.effects &= ~EF_NODRAW; + self.alpha = 1; + self.PlayerPhysplug = func_null; + self.view_ofs = PL_VIEW_OFS; + self.event_damage = PlayerDamage; + self.hud = HUD_NORMAL; + self.teleportable = TELEPORT_NORMAL; + self.switchweapon = self.vehicle.switchweapon; + + vh_player = self; + vh_vehicle = self.vehicle; + MUTATOR_CALLHOOK(VehicleExit); + self = vh_player; + self.vehicle = vh_vehicle; + + self.vehicle.vehicle_hudmodel.viewmodelforclient = self.vehicle; + + fixedmakevectors(self.vehicle.owner.angles); + + if(self == self.vehicle.owner.gunner1) + { + self.vehicle.owner.gunner1 = world; + } + else if(self == self.vehicle.owner.gunner2) + { + self.vehicle.owner.gunner2 = world; + v_right *= -1; + } + else + dprint("^1self != gunner1 or gunner2, this is a BIG PROBLEM, tell tZork this happend.\n"); + + vector spot = self.vehicle.owner.origin + + v_up * 128 + v_right * 300; + spot = vehicles_findgoodexit(spot); + //setorigin(self , spot); + + self.velocity = 0.75 * self.vehicle.owner.velocity + normalize(spot - self.vehicle.owner.origin) * 200; + self.velocity_z += 10; + + self.vehicle.phase = time + 5; + self.vehicle = world; +} + +float bumblebee_gunner_enter() +{ + RemoveGrapplingHook(other); + entity _gun, _gunner; + if(!self.gunner1) + { + _gun = self.gun1; + _gunner = self.gunner1; + self.gunner1 = other; + } + else if(!self.gunner2) + { + _gun = self.gun2; + _gunner = self.gunner2; + self.gunner2 = other; + } + else + { + dprint("^1ERROR:^7Tried to enter a fully occupied vehicle!\n"); - return FALSE; ++ return false; + } + + _gunner = other; + _gunner.vehicle = _gun; + _gun.switchweapon = other.switchweapon; + _gun.vehicle_exit = bumblebee_gunner_exit; + + other.angles = self.angles; + other.takedamage = DAMAGE_NO; + other.solid = SOLID_NOT; + other.movetype = MOVETYPE_NOCLIP; + other.alpha = -1; + other.event_damage = func_null; + other.view_ofs = '0 0 0'; + other.hud = _gun.hud; - other.teleportable = FALSE; ++ other.teleportable = false; + other.PlayerPhysplug = _gun.PlayerPhysplug; + other.vehicle_ammo1 = self.vehicle_ammo1; + other.vehicle_ammo2 = self.vehicle_ammo2; + other.vehicle_reload1 = self.vehicle_reload1; + other.vehicle_reload2 = self.vehicle_reload2; + other.vehicle_energy = self.vehicle_energy; + other.PlayerPhysplug = bumblebee_gunner_frame; + other.flags &= ~FL_ONGROUND; + + if(IS_REAL_CLIENT(other)) + { + msg_entity = other; + WriteByte(MSG_ONE, SVC_SETVIEWPORT); + WriteEntity(MSG_ONE, _gun.vehicle_viewport); + WriteByte(MSG_ONE, SVC_SETVIEWANGLES); + WriteAngle(MSG_ONE, _gun.angles_x + self.angles_x); // tilt + WriteAngle(MSG_ONE, _gun.angles_y + self.angles_y); // yaw + WriteAngle(MSG_ONE, 0); // roll + } - ++ + _gun.vehicle_hudmodel.viewmodelforclient = other; + + CSQCVehicleSetup(other, other.hud); + + vh_player = other; + vh_vehicle = _gun; + MUTATOR_CALLHOOK(VehicleEnter); + other = vh_player; + _gun = vh_vehicle; + - return TRUE; ++ return true; +} + +float vehicles_valid_pilot() +{ + if (!IS_PLAYER(other)) - return FALSE; ++ return false; + + if(other.deadflag != DEAD_NO) - return FALSE; ++ return false; + + if(other.vehicle != world) - return FALSE; ++ return false; + + if (!IS_REAL_CLIENT(other)) + if(!autocvar_g_vehicles_allow_bots) - return FALSE; ++ return false; + + if(teamplay && other.team != self.team) - return FALSE; ++ return false; + - return TRUE; ++ return true; +} + +void bumblebee_touch() +{ + if(autocvar_g_vehicles_enter) { return; } + + if(self.gunner1 != world && self.gunner2 != world) + { + vehicles_touch(); + return; + } + + if(vehicles_valid_pilot()) + { + if(self.gun1.phase <= time) + if(bumblebee_gunner_enter()) + return; + + if(self.gun2.phase <= time) + if(bumblebee_gunner_enter()) + return; + } + + vehicles_touch(); +} + +void bumblebee_regen() +{ + if(self.gun1.delay + autocvar_g_vehicle_bumblebee_cannon_ammo_regen_pause < time) + self.gun1.vehicle_energy = min(autocvar_g_vehicle_bumblebee_cannon_ammo, + self.gun1.vehicle_energy + autocvar_g_vehicle_bumblebee_cannon_ammo_regen * frametime); + + if(self.gun2.delay + autocvar_g_vehicle_bumblebee_cannon_ammo_regen_pause < time) + self.gun2.vehicle_energy = min(autocvar_g_vehicle_bumblebee_cannon_ammo, + self.gun2.vehicle_energy + autocvar_g_vehicle_bumblebee_cannon_ammo_regen * frametime); + + if(self.vehicle_flags & VHF_SHIELDREGEN) - vehicles_regen(self.dmg_time, vehicle_shield, autocvar_g_vehicle_bumblebee_shield, autocvar_g_vehicle_bumblebee_shield_regen_pause, autocvar_g_vehicle_bumblebee_shield_regen, frametime, TRUE); ++ vehicles_regen(self.dmg_time, vehicle_shield, autocvar_g_vehicle_bumblebee_shield, autocvar_g_vehicle_bumblebee_shield_regen_pause, autocvar_g_vehicle_bumblebee_shield_regen, frametime, true); + + if(self.vehicle_flags & VHF_HEALTHREGEN) - vehicles_regen(self.dmg_time, vehicle_health, autocvar_g_vehicle_bumblebee_health, autocvar_g_vehicle_bumblebee_health_regen_pause, autocvar_g_vehicle_bumblebee_health_regen, frametime, FALSE); ++ vehicles_regen(self.dmg_time, vehicle_health, autocvar_g_vehicle_bumblebee_health, autocvar_g_vehicle_bumblebee_health_regen_pause, autocvar_g_vehicle_bumblebee_health_regen, frametime, false); + + if(self.vehicle_flags & VHF_ENERGYREGEN) - vehicles_regen(self.wait, vehicle_energy, autocvar_g_vehicle_bumblebee_energy, autocvar_g_vehicle_bumblebee_energy_regen_pause, autocvar_g_vehicle_bumblebee_energy_regen, frametime, FALSE); ++ vehicles_regen(self.wait, vehicle_energy, autocvar_g_vehicle_bumblebee_energy, autocvar_g_vehicle_bumblebee_energy_regen_pause, autocvar_g_vehicle_bumblebee_energy_regen, frametime, false); + +} + +float bumblebee_pilot_frame() +{ + entity pilot, vehic; + vector newvel; + + if(intermission_running) + { + self.vehicle.velocity = '0 0 0'; + self.vehicle.avelocity = '0 0 0'; + return 1; + } + + pilot = self; + vehic = self.vehicle; + self = vehic; + + if(vehic.deadflag != DEAD_NO) + { + self = pilot; + pilot.BUTTON_ATCK = pilot.BUTTON_ATCK2 = 0; + return 1; + } + + bumblebee_regen(); + + crosshair_trace(pilot); + + vector vang; + float ftmp; + + vang = vehic.angles; + newvel = vectoangles(normalize(trace_endpos - self.origin + '0 0 32')); - vang_x *= -1; - newvel_x *= -1; - if(newvel_x > 180) newvel_x -= 360; - if(newvel_x < -180) newvel_x += 360; - if(newvel_y > 180) newvel_y -= 360; - if(newvel_y < -180) newvel_y += 360; - - ftmp = shortangle_f(pilot.v_angle_y - vang_y, vang_y); ++ vang.x *= -1; ++ newvel.x *= -1; ++ if(newvel.x > 180) newvel.x -= 360; ++ if(newvel.x < -180) newvel.x += 360; ++ if(newvel.y > 180) newvel.y -= 360; ++ if(newvel.y < -180) newvel.y += 360; ++ ++ ftmp = shortangle_f(pilot.v_angle.y - vang.y, vang.y); + if(ftmp > 180) ftmp -= 360; + if(ftmp < -180) ftmp += 360; - vehic.avelocity_y = bound(-autocvar_g_vehicle_bumblebee_turnspeed, ftmp + vehic.avelocity_y * 0.9, autocvar_g_vehicle_bumblebee_turnspeed); ++ vehic.avelocity_y = bound(-autocvar_g_vehicle_bumblebee_turnspeed, ftmp + vehic.avelocity.y * 0.9, autocvar_g_vehicle_bumblebee_turnspeed); + + // Pitch + ftmp = 0; - if(pilot.movement_x > 0 && vang_x < autocvar_g_vehicle_bumblebee_pitchlimit) ++ if(pilot.movement.x > 0 && vang.x < autocvar_g_vehicle_bumblebee_pitchlimit) + ftmp = 4; - else if(pilot.movement_x < 0 && vang_x > -autocvar_g_vehicle_bumblebee_pitchlimit) ++ else if(pilot.movement.x < 0 && vang.x > -autocvar_g_vehicle_bumblebee_pitchlimit) + ftmp = -8; + - newvel_x = bound(-autocvar_g_vehicle_bumblebee_pitchlimit, newvel_x , autocvar_g_vehicle_bumblebee_pitchlimit); - ftmp = vang_x - bound(-autocvar_g_vehicle_bumblebee_pitchlimit, newvel_x + ftmp, autocvar_g_vehicle_bumblebee_pitchlimit); - vehic.avelocity_x = bound(-autocvar_g_vehicle_bumblebee_pitchspeed, ftmp + vehic.avelocity_x * 0.9, autocvar_g_vehicle_bumblebee_pitchspeed); ++ newvel.x = bound(-autocvar_g_vehicle_bumblebee_pitchlimit, newvel.x , autocvar_g_vehicle_bumblebee_pitchlimit); ++ ftmp = vang.x - bound(-autocvar_g_vehicle_bumblebee_pitchlimit, newvel.x + ftmp, autocvar_g_vehicle_bumblebee_pitchlimit); ++ vehic.avelocity_x = bound(-autocvar_g_vehicle_bumblebee_pitchspeed, ftmp + vehic.avelocity.x * 0.9, autocvar_g_vehicle_bumblebee_pitchspeed); + - vehic.angles_x = anglemods(vehic.angles_x); - vehic.angles_y = anglemods(vehic.angles_y); - vehic.angles_z = anglemods(vehic.angles_z); ++ vehic.angles_x = anglemods(vehic.angles.x); ++ vehic.angles_y = anglemods(vehic.angles.y); ++ vehic.angles_z = anglemods(vehic.angles.z); + - makevectors('0 1 0' * vehic.angles_y); ++ makevectors('0 1 0' * vehic.angles.y); + newvel = vehic.velocity * -autocvar_g_vehicle_bumblebee_friction; + - if(pilot.movement_x != 0) ++ if(pilot.movement.x != 0) + { - if(pilot.movement_x > 0) ++ if(pilot.movement.x > 0) + newvel += v_forward * autocvar_g_vehicle_bumblebee_speed_forward; - else if(pilot.movement_x < 0) ++ else if(pilot.movement.x < 0) + newvel -= v_forward * autocvar_g_vehicle_bumblebee_speed_forward; + } + - if(pilot.movement_y != 0) ++ if(pilot.movement.y != 0) + { - if(pilot.movement_y < 0) ++ if(pilot.movement.y < 0) + newvel -= v_right * autocvar_g_vehicle_bumblebee_speed_strafe; - else if(pilot.movement_y > 0) ++ else if(pilot.movement.y > 0) + newvel += v_right * autocvar_g_vehicle_bumblebee_speed_strafe; + ftmp = newvel * v_right; + ftmp *= frametime * 0.1; - vehic.angles_z = bound(-15, vehic.angles_z + ftmp, 15); ++ vehic.angles_z = bound(-15, vehic.angles.z + ftmp, 15); + } + else + { + vehic.angles_z *= 0.95; - if(vehic.angles_z >= -1 && vehic.angles_z <= -1) ++ if(vehic.angles.z >= -1 && vehic.angles.z <= -1) + vehic.angles_z = 0; + } + + if(pilot.BUTTON_CROUCH) + newvel -= v_up * autocvar_g_vehicle_bumblebee_speed_down; + else if(pilot.BUTTON_JUMP) + newvel += v_up * autocvar_g_vehicle_bumblebee_speed_up; + + vehic.velocity += newvel * frametime; + pilot.velocity = pilot.movement = vehic.velocity; + + + if(autocvar_g_vehicle_bumblebee_healgun_locktime) + { + if(vehic.tur_head.lock_time < time || vehic.tur_head.enemy.deadflag) + vehic.tur_head.enemy = world; + + if(trace_ent) + if(trace_ent.movetype) + if(trace_ent.takedamage) + if(!trace_ent.deadflag) + { + if(teamplay) + { + if(trace_ent.team == pilot.team) + { + vehic.tur_head.enemy = trace_ent; + vehic.tur_head.lock_time = time + autocvar_g_vehicle_bumblebee_healgun_locktime; + } + } + else + { + vehic.tur_head.enemy = trace_ent; + vehic.tur_head.lock_time = time + autocvar_g_vehicle_bumblebee_healgun_locktime; + } + } + + if(vehic.tur_head.enemy) + { + trace_endpos = real_origin(vehic.tur_head.enemy); + UpdateAuxiliaryXhair(pilot, trace_endpos, '0 0.75 0', 0); + } + } + + vang = vehicle_aimturret(vehic, trace_endpos, self.gun3, "fire", + autocvar_g_vehicle_bumblebee_raygun_pitchlimit_down * -1, autocvar_g_vehicle_bumblebee_raygun_pitchlimit_up, + autocvar_g_vehicle_bumblebee_raygun_turnlimit_sides * -1, autocvar_g_vehicle_bumblebee_raygun_turnlimit_sides, autocvar_g_vehicle_bumblebee_raygun_turnspeed); + + if(!forbidWeaponUse(pilot)) + if((pilot.BUTTON_ATCK || pilot.BUTTON_ATCK2) && (vehic.vehicle_energy > autocvar_g_vehicle_bumblebee_raygun_dps * sys_frametime || autocvar_g_vehicle_bumblebee_raygun == 0)) + { + vehic.gun3.enemy.realowner = pilot; + vehic.gun3.enemy.effects &= ~EF_NODRAW; + + vehic.gun3.enemy.hook_start = gettaginfo(vehic.gun3, gettagindex(vehic.gun3, "fire")); + vehic.gun3.enemy.SendFlags |= BRG_START; + + traceline(vehic.gun3.enemy.hook_start, vehic.gun3.enemy.hook_start + v_forward * autocvar_g_vehicle_bumblebee_raygun_range, MOVE_NORMAL, vehic); + + if(trace_ent) + { + if(autocvar_g_vehicle_bumblebee_raygun) + { + Damage(trace_ent, vehic, pilot, autocvar_g_vehicle_bumblebee_raygun_dps * sys_frametime, DEATH_GENERIC, trace_endpos, v_forward * autocvar_g_vehicle_bumblebee_raygun_fps * sys_frametime); + vehic.vehicle_energy -= autocvar_g_vehicle_bumblebee_raygun_aps * sys_frametime; + } + else + { + if(trace_ent.deadflag == DEAD_NO) + if((teamplay && trace_ent.team == pilot.team) || !teamplay) + { + + if(trace_ent.vehicle_flags & VHF_ISVEHICLE) + { + if(autocvar_g_vehicle_bumblebee_healgun_sps && trace_ent.vehicle_health <= trace_ent.max_health) + trace_ent.vehicle_shield = min(trace_ent.vehicle_shield + autocvar_g_vehicle_bumblebee_healgun_sps * frametime, trace_ent.tur_head.max_health); + + if(autocvar_g_vehicle_bumblebee_healgun_hps) + trace_ent.vehicle_health = min(trace_ent.vehicle_health + autocvar_g_vehicle_bumblebee_healgun_hps * frametime, trace_ent.max_health); + } + else if(IS_CLIENT(trace_ent)) + { + if(trace_ent.health <= autocvar_g_vehicle_bumblebee_healgun_hmax && autocvar_g_vehicle_bumblebee_healgun_hps) + trace_ent.health = min(trace_ent.health + autocvar_g_vehicle_bumblebee_healgun_hps * frametime, autocvar_g_vehicle_bumblebee_healgun_hmax); + + if(trace_ent.armorvalue <= autocvar_g_vehicle_bumblebee_healgun_amax && autocvar_g_vehicle_bumblebee_healgun_aps) + trace_ent.armorvalue = min(trace_ent.armorvalue + autocvar_g_vehicle_bumblebee_healgun_aps * frametime, autocvar_g_vehicle_bumblebee_healgun_amax); + + trace_ent.health = min(trace_ent.health + autocvar_g_vehicle_bumblebee_healgun_hps * frametime, autocvar_g_vehicle_bumblebee_healgun_hmax); + } + else if(IS_TURRET(trace_ent)) + { + if(trace_ent.health <= trace_ent.max_health && autocvar_g_vehicle_bumblebee_healgun_hps) + trace_ent.health = min(trace_ent.health + autocvar_g_vehicle_bumblebee_healgun_hps * frametime, trace_ent.max_health); + //else ..hmmm what? ammo? + + trace_ent.SendFlags |= TNSF_STATUS; + } + } + } + } + + vehic.gun3.enemy.hook_end = trace_endpos; + setorigin(vehic.gun3.enemy, trace_endpos); + vehic.gun3.enemy.SendFlags |= BRG_END; + + vehic.wait = time + 1; + } + else + vehic.gun3.enemy.effects |= EF_NODRAW; + /*{ + if(vehic.gun3.enemy) + remove(vehic.gun3.enemy); + + vehic.gun3.enemy = world; + } + */ + + VEHICLE_UPDATE_PLAYER(pilot, health, bumblebee); + VEHICLE_UPDATE_PLAYER(pilot, energy, bumblebee); + + pilot.vehicle_ammo1 = (vehic.gun1.vehicle_energy / autocvar_g_vehicle_bumblebee_cannon_ammo) * 100; + pilot.vehicle_ammo2 = (vehic.gun2.vehicle_energy / autocvar_g_vehicle_bumblebee_cannon_ammo) * 100; + + if(vehic.vehicle_flags & VHF_HASSHIELD) + VEHICLE_UPDATE_PLAYER(pilot, shield, bumblebee); + + vehic.angles_x *= -1; + makevectors(vehic.angles); + vehic.angles_x *= -1; + setorigin(pilot, vehic.origin + v_up * 48 + v_forward * 160); + + pilot.BUTTON_ATCK = pilot.BUTTON_ATCK2 = pilot.BUTTON_CROUCH = 0; + self = pilot; + + return 1; +} + +void bumblebee_land() +{ + float hgt; + + hgt = raptor_altitude(512); + self.velocity = (self.velocity * 0.9) + ('0 0 -1800' * (hgt / 256) * sys_frametime); + self.angles_x *= 0.95; + self.angles_z *= 0.95; + + if(hgt < 16) + self.think = vehicles_think; + + self.nextthink = time; + + CSQCMODEL_AUTOUPDATE(); +} + +void bumblebee_exit(float eject) +{ + if(self.owner.vehicleid == VEH_BUMBLEBEE) + { + bumblebee_gunner_exit(eject); + return; + } + + self.touch = vehicles_touch; + + if(self.deadflag == DEAD_NO) + { + self.think = bumblebee_land; + self.nextthink = time; + } + + self.movetype = MOVETYPE_TOSS; + + if(!self.owner) + return; + + fixedmakevectors(self.angles); + vector spot; + if(vlen(self.velocity) > autocvar_g_vehicle_bumblebee_speed_forward * 0.5) + spot = self.origin + v_up * 128 + v_forward * 300; + else + spot = self.origin + v_up * 128 - v_forward * 300; + + spot = vehicles_findgoodexit(spot); + + // Hide beam + if(self.gun3.enemy || !wasfreed(self.gun3.enemy)) { + self.gun3.enemy.effects |= EF_NODRAW; + } + + self.owner.velocity = 0.75 * self.vehicle.velocity + normalize(spot - self.vehicle.origin) * 200; + self.owner.velocity_z += 10; + setorigin(self.owner, spot); + + antilag_clear(self.owner); + self.owner = world; +} + +void bumblebee_blowup() +{ + RadiusDamage(self, self.enemy, autocvar_g_vehicle_bumblebee_blowup_coredamage, + autocvar_g_vehicle_bumblebee_blowup_edgedamage, + autocvar_g_vehicle_bumblebee_blowup_radius, self, world, + autocvar_g_vehicle_bumblebee_blowup_forceintensity, + DEATH_VH_BUMB_DEATH, world); + + sound(self, CH_SHOTS, W_Sound("rocket_impact"), VOL_BASE, ATTEN_NORM); + Send_Effect(EFFECT_EXPLOSION_BIG, (self.origin + '0 0 100') + (randomvec() * 80), '0 0 0', 1); + + if(self.owner.deadflag == DEAD_DYING) + self.owner.deadflag = DEAD_DEAD; + + remove(self); +} + +void bumblebee_diethink() +{ + if(time >= self.wait) + self.think = bumblebee_blowup; + + if(random() < 0.1) + { + sound(self, CH_SHOTS, W_Sound("rocket_impact"), VOL_BASE, ATTEN_NORM); + Send_Effect(EFFECT_EXPLOSION_SMALL, (self.origin + '0 0 100') + (randomvec() * 80), '0 0 0', 1); + } + + self.nextthink = time + 0.1; +} + +float bumble_raygun_send(entity to, float sf) +{ + WriteByte(MSG_ENTITY, ENT_CLIENT_BUMBLE_RAYGUN); + + WriteByte(MSG_ENTITY, sf); + if(sf & BRG_SETUP) + { + WriteByte(MSG_ENTITY, num_for_edict(self.realowner)); + WriteByte(MSG_ENTITY, self.realowner.team); + WriteByte(MSG_ENTITY, self.cnt); + } + + if(sf & BRG_START) + { + WriteCoord(MSG_ENTITY, self.hook_start_x); + WriteCoord(MSG_ENTITY, self.hook_start_y); + WriteCoord(MSG_ENTITY, self.hook_start_z); + } + + if(sf & BRG_END) + { + WriteCoord(MSG_ENTITY, self.hook_end_x); + WriteCoord(MSG_ENTITY, self.hook_end_y); + WriteCoord(MSG_ENTITY, self.hook_end_z); + } + - return TRUE; ++ return true; +} + +void spawnfunc_vehicle_bumblebee() +{ + if(!autocvar_g_vehicle_bumblebee) { remove(self); return; } - if(!vehicle_initialize(VEH_BUMBLEBEE, FALSE)) { remove(self); return; } ++ if(!vehicle_initialize(VEH_BUMBLEBEE, false)) { remove(self); return; } +} + +float v_bumblebee(float req) +{ + switch(req) + { + case VR_IMPACT: + { + if(autocvar_g_vehicle_bumblebee_bouncepain) + vehicles_impact(autocvar_g_vehicle_bumblebee_bouncepain_x, autocvar_g_vehicle_bumblebee_bouncepain_y, autocvar_g_vehicle_bumblebee_bouncepain_z); + - return TRUE; ++ return true; + } + case VR_ENTER: + { + self.touch = bumblebee_touch; + self.nextthink = 0; + self.movetype = MOVETYPE_BOUNCEMISSILE; - return TRUE; ++ return true; + } + case VR_THINK: + { + self.angles_z *= 0.8; + self.angles_x *= 0.8; + + self.nextthink = time; + + if(!self.owner) + { + entity oldself = self; + if(self.gunner1) + { + self = self.gunner1; + oldself.gun1.vehicle_exit(VHEF_EJECT); + entity oldother = other; + other = self; + self = oldself; + self.phase = 0; + self.touch(); + other = oldother; - return TRUE; ++ return true; + } + + if(self.gunner2) + { + self = self.gunner2; + oldself.gun2.vehicle_exit(VHEF_EJECT); + entity oldother = other; + other = self; + self = oldself; + self.phase = 0; + self.touch(); + other = oldother; - return TRUE; ++ return true; + } + } + - return TRUE; ++ return true; + } + case VR_DEATH: + { + entity oldself = self; + + CSQCModel_UnlinkEntity(); + + // Hide beam + if(self.gun3.enemy || !wasfreed(self.gun3.enemy)) + self.gun3.enemy.effects |= EF_NODRAW; + + if(self.gunner1) + { + self = self.gunner1; + oldself.gun1.vehicle_exit(VHEF_EJECT); + self = oldself; + } + + if(self.gunner2) + { + self = self.gunner2; + oldself.gun2.vehicle_exit(VHEF_EJECT); + self = oldself; + } + + self.vehicle_exit(VHEF_EJECT); + + fixedmakevectors(self.angles); + vehicle_tossgib(self.gun1, self.velocity + v_right * 300 + v_up * 100 + randomvec() * 200, "cannon_right", rint(random()), rint(random()), 6, randomvec() * 200); + vehicle_tossgib(self.gun2, self.velocity + v_right * -300 + v_up * 100 + randomvec() * 200, "cannon_left", rint(random()), rint(random()), 6, randomvec() * 200); + vehicle_tossgib(self.gun3, self.velocity + v_forward * 300 + v_up * -100 + randomvec() * 200, "raygun", rint(random()), rint(random()), 6, randomvec() * 300); + + entity _body = vehicle_tossgib(self, self.velocity + randomvec() * 200, "", rint(random()), rint(random()), 6, randomvec() * 100); + + if(random() > 0.5) + _body.touch = bumblebee_blowup; + else + _body.touch = func_null; + + _body.think = bumblebee_diethink; + _body.nextthink = time; + _body.wait = time + 2 + (random() * 8); + _body.owner = self; + _body.enemy = self.enemy; + _body.scale = 1.5; + _body.angles = self.angles; + + Send_Effect(EFFECT_EXPLOSION_MEDIUM, findbetterlocation (self.origin, 16), '0 0 0', 1); + + self.health = 0; + self.event_damage = func_null; + self.solid = SOLID_NOT; + self.takedamage = DAMAGE_NO; + self.deadflag = DEAD_DYING; + self.movetype = MOVETYPE_NONE; + self.effects = EF_NODRAW; + self.colormod = '0 0 0'; + self.avelocity = '0 0 0'; + self.velocity = '0 0 0'; + self.touch = func_null; + self.nextthink = 0; + + setorigin(self, self.pos1); - return TRUE; ++ return true; + } + case VR_SPAWN: + { + if(!self.gun1) + { + // for some reason, autosizing of the shield entity refuses to work for this one so set it up in advance. + self.vehicle_shieldent = spawn(); + self.vehicle_shieldent.effects = EF_LOWPRECISION; + setmodel(self.vehicle_shieldent, "models/vhshield.md3"); + setattachment(self.vehicle_shieldent, self, ""); + setorigin(self.vehicle_shieldent, real_origin(self) - self.origin); + self.vehicle_shieldent.scale = 512 / vlen(self.maxs - self.mins); + self.vehicle_shieldent.think = shieldhit_think; + self.vehicle_shieldent.alpha = -1; + self.vehicle_shieldent.effects = EF_LOWPRECISION | EF_NODRAW; + + self.gun1 = spawn(); + self.gun2 = spawn(); + self.gun3 = spawn(); + + self.vehicle_flags |= VHF_MULTISLOT; + + self.gun1.owner = self; + self.gun2.owner = self; + self.gun3.owner = self; + + setmodel(self.gun1, "models/vehicles/bumblebee_plasma_right.dpm"); + setmodel(self.gun2, "models/vehicles/bumblebee_plasma_left.dpm"); + setmodel(self.gun3, "models/vehicles/bumblebee_ray.dpm"); + + setattachment(self.gun1, self, "cannon_right"); + setattachment(self.gun2, self, "cannon_left"); + + // Angled bones are no fun, messes up gun-aim; so work arround it. + self.gun3.pos1 = self.angles; + self.angles = '0 0 0'; + vector ofs = gettaginfo(self, gettagindex(self, "raygun")); + ofs -= self.origin; + setattachment(self.gun3, self, ""); + setorigin(self.gun3, ofs); + self.angles = self.gun3.pos1; + + vehicle_addplayerslot(self, self.gun1, HUD_BUMBLEBEE_GUN, "models/vehicles/wakizashi_cockpit.dpm", bumblebee_gunner_frame, bumblebee_gunner_exit, bumblebee_gunner_enter); + vehicle_addplayerslot(self, self.gun2, HUD_BUMBLEBEE_GUN, "models/vehicles/wakizashi_cockpit.dpm", bumblebee_gunner_frame, bumblebee_gunner_exit, bumblebee_gunner_enter); + + setorigin(self.vehicle_hudmodel, '50 0 -5'); // Move cockpit forward - down. + setorigin(self.vehicle_viewport, '5 0 2'); // Move camera forward up + + //fixme-model-bones + setorigin(self.gun1.vehicle_hudmodel, '90 -27 -23'); + setorigin(self.gun1.vehicle_viewport, '-85 0 50'); + //fixme-model-bones + setorigin(self.gun2.vehicle_hudmodel, '90 27 -23'); + setorigin(self.gun2.vehicle_viewport, '-85 0 50'); + + self.scale = 1.5; + + // Raygun beam + if(self.gun3.enemy == world) + { + self.gun3.enemy = spawn(); - Net_LinkEntity(self.gun3.enemy, TRUE, 0, bumble_raygun_send); ++ Net_LinkEntity(self.gun3.enemy, true, 0, bumble_raygun_send); + self.gun3.enemy.SendFlags = BRG_SETUP; + self.gun3.enemy.cnt = autocvar_g_vehicle_bumblebee_raygun; + self.gun3.enemy.effects = EF_NODRAW | EF_LOWPRECISION; + } + } + + self.vehicle_health = autocvar_g_vehicle_bumblebee_health; + self.vehicle_shield = autocvar_g_vehicle_bumblebee_shield; + self.solid = SOLID_BBOX; + self.movetype = MOVETYPE_TOSS; + self.damageforcescale = 0.025; + + self.PlayerPhysplug = bumblebee_pilot_frame; + + setorigin(self, self.origin + '0 0 25'); - return TRUE; ++ return true; + } + case VR_SETUP: + { + if(autocvar_g_vehicle_bumblebee_energy) + if(autocvar_g_vehicle_bumblebee_energy_regen) + self.vehicle_flags |= VHF_ENERGYREGEN; + + if(autocvar_g_vehicle_bumblebee_shield) + self.vehicle_flags |= VHF_HASSHIELD; + + if(autocvar_g_vehicle_bumblebee_shield_regen) + self.vehicle_flags |= VHF_SHIELDREGEN; + + if(autocvar_g_vehicle_bumblebee_health_regen) + self.vehicle_flags |= VHF_HEALTHREGEN; + + self.vehicle_exit = bumblebee_exit; + self.respawntime = autocvar_g_vehicle_bumblebee_respawntime; + self.vehicle_health = autocvar_g_vehicle_bumblebee_health; + self.max_health = self.vehicle_health; + self.vehicle_shield = autocvar_g_vehicle_bumblebee_shield; + - return TRUE; ++ return true; + } + case VR_PRECACHE: + { + precache_model("models/vehicles/bumblebee_body.dpm"); + precache_model("models/vehicles/bumblebee_plasma_left.dpm"); + precache_model("models/vehicles/bumblebee_plasma_right.dpm"); + precache_model("models/vehicles/bumblebee_ray.dpm"); + precache_model("models/vehicles/wakizashi_cockpit.dpm"); + precache_model("models/vehicles/spiderbot_cockpit.dpm"); + precache_model("models/vehicles/raptor_cockpit.dpm"); - return TRUE; ++ return true; + } + } + - return TRUE; ++ return true; +} + +#endif // SVQC +#ifdef CSQC + +#define bumb_ico "gfx/vehicles/bumb.tga" +#define bumb_lgun "gfx/vehicles/bumb_lgun.tga" +#define bumb_rgun "gfx/vehicles/bumb_rgun.tga" + +#define bumb_gun_ico "gfx/vehicles/bumb_side.tga" +#define bumb_gun_gun "gfx/vehicles/bumb_side_gun.tga" + +void CSQC_BUMBLE_GUN_HUD() +{ - + if(autocvar_r_letterbox) + return; + + vector picsize, hudloc = '0 0 0', pic2size, picloc; + + // Fetch health & ammo stats + HUD_GETVEHICLESTATS + + picsize = draw_getimagesize(hud_bg) * autocvar_cl_vehicles_hudscale; + hudloc_y = vid_conheight - picsize_y; + hudloc_x = vid_conwidth * 0.5 - picsize_x * 0.5; + + drawpic(hudloc, hud_bg, picsize, '1 1 1', autocvar_cl_vehicles_hudalpha, DRAWFLAG_NORMAL); + + shield *= 0.01; + vh_health *= 0.01; + energy *= 0.01; + reload1 *= 0.01; + + pic2size = draw_getimagesize(bumb_gun_ico) * (autocvar_cl_vehicles_hudscale * 0.8); + picloc = picsize * 0.5 - pic2size * 0.5; + + if(vh_health < 0.25) + drawpic(hudloc + picloc, bumb_gun_ico, pic2size, '1 0 0' + '0 1 1' * sin(time * 8), 1, DRAWFLAG_NORMAL); + else + drawpic(hudloc + picloc, bumb_gun_ico, pic2size, '1 1 1' * vh_health + '1 0 0' * (1 - vh_health), 1, DRAWFLAG_NORMAL); + + drawpic(hudloc + picloc, bumb_gun_gun, pic2size, '1 1 1' * energy + '1 0 0' * (1 - energy), 1, DRAWFLAG_NORMAL); + drawpic(hudloc + picloc, hud_sh, pic2size, '1 1 1', shield, DRAWFLAG_NORMAL); + +// Health bar + picsize = draw_getimagesize(hud_hp_bar) * autocvar_cl_vehicles_hudscale; + picloc = '69 69 0' * autocvar_cl_vehicles_hudscale; + drawsetcliparea(hudloc_x + picloc_x + (picsize_x * (1 - vh_health)), 0, vid_conwidth, vid_conheight); + drawpic(hudloc + picloc, hud_hp_bar, picsize, '1 1 1', 1 , DRAWFLAG_NORMAL); + drawresetcliparea(); +// .. and icon + picsize = draw_getimagesize(hud_hp_ico) * autocvar_cl_vehicles_hudscale; + picloc = '37 65 0' * autocvar_cl_vehicles_hudscale; + if(vh_health < 0.25) + { + if(alarm1time < time) + { + alarm1time = time + 2; + vehicle_alarm(self, CH_PAIN_SINGLE, "vehicles/alarm.wav"); + } + + drawpic(hudloc + picloc, hud_hp_ico, picsize, '1 0 0' + '0 1 1' * sin(time * 8), 1, DRAWFLAG_NORMAL); + } + else + { + drawpic(hudloc + picloc, hud_hp_ico, picsize, '1 1 1', 1, DRAWFLAG_NORMAL); + if(alarm1time) + { + vehicle_alarm(self, CH_PAIN_SINGLE, "misc/null.wav"); + alarm1time = 0; + } + } + +// Shield bar + picsize = draw_getimagesize(hud_sh_bar) * autocvar_cl_vehicles_hudscale; + picloc = '69 140 0' * autocvar_cl_vehicles_hudscale; + drawsetcliparea(hudloc_x + picloc_x + (picsize_x * (1 - shield)), 0, vid_conwidth, vid_conheight); + drawpic(hudloc + picloc, hud_sh_bar, picsize, '1 1 1', 1, DRAWFLAG_NORMAL); + drawresetcliparea(); +// .. and icon + picloc = '40 136 0' * autocvar_cl_vehicles_hudscale; + picsize = draw_getimagesize(hud_sh_ico) * autocvar_cl_vehicles_hudscale; + if(shield < 0.25) + { + if(alarm2time < time) + { + alarm2time = time + 1; + vehicle_alarm(self, CH_TRIGGER_SINGLE, "vehicles/alarm_shield.wav"); + } + drawpic(hudloc + picloc, hud_sh_ico, picsize, '1 0 0' + '0 1 1' * sin(time * 8), 1, DRAWFLAG_NORMAL); + } + else + { + drawpic(hudloc + picloc, hud_sh_ico, picsize, '1 1 1', 1, DRAWFLAG_NORMAL); + if(alarm2time) + { + vehicle_alarm(self, CH_TRIGGER_SINGLE, "misc/null.wav"); + alarm2time = 0; + } + } + +// Gun bar + picsize = draw_getimagesize(hud_ammo1_bar) * autocvar_cl_vehicles_hudscale; + picloc = '450 69 0' * autocvar_cl_vehicles_hudscale; + drawsetcliparea(hudloc_x + picloc_x, picloc_y, picsize_x * energy, vid_conheight); + drawpic(hudloc + picloc, hud_ammo1_bar, picsize, '1 1 1', 1, DRAWFLAG_NORMAL); + drawresetcliparea(); + +// .. and icon + picsize = 1.5 * draw_getimagesize(hud_energy) * autocvar_cl_vehicles_hudscale; + picloc = '664 60 0' * autocvar_cl_vehicles_hudscale; + if(energy < 0.2) + drawpic(hudloc + picloc, hud_energy, picsize, '1 0 0' + '0 1 1' * sin(time * 8), 1, DRAWFLAG_NORMAL); + else + drawpic(hudloc + picloc, hud_energy, picsize, '1 1 1', 1, DRAWFLAG_NORMAL); + + if (scoreboard_showscores) + HUD_DrawScoreboard(); + /* + else + { + picsize = draw_getimagesize(waki_xhair); + picsize_x *= 0.5; + picsize_y *= 0.5; + + + drawpic('0.5 0 0' * (vid_conwidth - picsize_x) + '0 0.5 0' * (vid_conheight - picsize_y), waki_xhair, picsize, '1 1 1', 1, DRAWFLAG_NORMAL); + } + */ +} + +void bumble_raygun_draw() +{ + float _len; + vector _dir; + vector _vtmp1, _vtmp2; + + _len = vlen(self.origin - self.move_origin); + _dir = normalize(self.move_origin - self.origin); + + if(self.total_damages < time) + { + boxparticles(self.traileffect, self, self.origin, self.origin + _dir * -64, _dir * -_len , _dir * -_len, 1, PARTICLES_USEALPHA); + boxparticles(self.lip, self, self.move_origin, self.move_origin + _dir * -64, _dir * -200 , _dir * -200, 1, PARTICLES_USEALPHA); + self.total_damages = time + 0.1; + } + + float i, df, sz, al; + for(i = -0.1; i < 0.2; i += 0.1) + { + df = DRAWFLAG_NORMAL; //((random() < 0.5) ? DRAWFLAG_ADDITIVE : DRAWFLAG_SCREEN); + sz = 5 + random() * 5; + al = 0.25 + random() * 0.5; + _vtmp1 = self.origin + _dir * _len * (0.25 + i); + _vtmp1 += (randomvec() * (_len * 0.2) * (frametime * 2)); //self.raygun_l1; + Draw_CylindricLine(self.origin, _vtmp1, sz, "gfx/colors/white.tga", 1, 1, self.colormod, al, df, view_origin); + + _vtmp2 = self.origin + _dir * _len * (0.5 + i); + _vtmp2 += (randomvec() * (_len * 0.2) * (frametime * 5)); //self.raygun_l2; + Draw_CylindricLine(_vtmp1, _vtmp2, sz, "gfx/colors/white.tga", 1, 1, self.colormod, al, df, view_origin); + + _vtmp1 = self.origin + _dir * _len * (0.75 + i); + _vtmp1 += randomvec() * (_len * 0.2) * (frametime * 10); //self.raygun_l3; + Draw_CylindricLine(_vtmp2, _vtmp1, sz, "gfx/colors/white.tga", 1, 1, self.colormod, al, df, view_origin); + + Draw_CylindricLine(_vtmp1, self.move_origin + randomvec() * 32, sz, "gfx/colors/white.tga", 1, 1, self.colormod, al, df, view_origin); + } +} + - void bumble_raygun_read(float bIsNew) ++void bumble_raygun_read(bool bIsNew) +{ - float sf = ReadByte(); ++ int sf = ReadByte(); + + if(sf & BRG_SETUP) + { + self.cnt = ReadByte(); + self.team = ReadByte(); + self.cnt = ReadByte(); + + if(self.cnt) + self.colormod = '1 0 0'; + else + self.colormod = '0 1 0'; + + self.traileffect = particleeffectnum("healray_muzzleflash"); + self.lip = particleeffectnum("healray_impact"); + + self.draw = bumble_raygun_draw; + } + + + if(sf & BRG_START) + { + self.origin_x = ReadCoord(); + self.origin_y = ReadCoord(); + self.origin_z = ReadCoord(); + setorigin(self, self.origin); + } + + if(sf & BRG_END) + { + self.move_origin_x = ReadCoord(); + self.move_origin_y = ReadCoord(); + self.move_origin_z = ReadCoord(); + } +} + +float v_bumblebee(float req) +{ + switch(req) + { + case VR_HUD: + { + if(autocvar_r_letterbox) - return TRUE; ++ return true; + + vector picsize, hudloc = '0 0 0', pic2size, picloc; + + // Fetch health & ammo stats + HUD_GETVEHICLESTATS + + picsize = draw_getimagesize(hud_bg) * autocvar_cl_vehicles_hudscale; + hudloc_y = vid_conheight - picsize_y; + hudloc_x = vid_conwidth * 0.5 - picsize_x * 0.5; + + drawpic(hudloc, hud_bg, picsize, '1 1 1', autocvar_cl_vehicles_hudalpha, DRAWFLAG_NORMAL); + + shield *= 0.01; + vh_health *= 0.01; + energy *= 0.01; + reload1 *= 0.01; + + pic2size = draw_getimagesize(bumb_ico) * (autocvar_cl_vehicles_hudscale * 0.8); + picloc = picsize * 0.5 - pic2size * 0.5; + + if(vh_health < 0.25) + drawpic(hudloc + picloc, bumb_ico, pic2size, '1 0 0' + '0 1 1' * sin(time * 8), 1, DRAWFLAG_NORMAL); + else + drawpic(hudloc + picloc, bumb_ico, pic2size, '1 1 1' * vh_health + '1 0 0' * (1 - vh_health), 1, DRAWFLAG_NORMAL); + + drawpic(hudloc + picloc, bumb_lgun, pic2size, '1 1 1' * energy + '1 0 0' * (1 - energy), 1, DRAWFLAG_NORMAL); + drawpic(hudloc + picloc, bumb_lgun, pic2size, '1 1 1' * energy + '1 0 0' * (1 - energy), 1, DRAWFLAG_NORMAL); + drawpic(hudloc + picloc, hud_sh, pic2size, '1 1 1', shield, DRAWFLAG_NORMAL); + + // Health bar + picsize = draw_getimagesize(hud_hp_bar) * autocvar_cl_vehicles_hudscale; + picloc = '69 69 0' * autocvar_cl_vehicles_hudscale; + drawsetcliparea(hudloc_x + picloc_x + (picsize_x * (1 - vh_health)), 0, vid_conwidth, vid_conheight); + drawpic(hudloc + picloc, hud_hp_bar, picsize, '1 1 1', 1 , DRAWFLAG_NORMAL); + drawresetcliparea(); + // .. and icon + picsize = draw_getimagesize(hud_hp_ico) * autocvar_cl_vehicles_hudscale; + picloc = '37 65 0' * autocvar_cl_vehicles_hudscale; + if(vh_health < 0.25) + { + if(alarm1time < time) + { + alarm1time = time + 2; + vehicle_alarm(self, CH_PAIN_SINGLE, "vehicles/alarm.wav"); + } + + drawpic(hudloc + picloc, hud_hp_ico, picsize, '1 0 0' + '0 1 1' * sin(time * 8), 1, DRAWFLAG_NORMAL); + } + else + { + drawpic(hudloc + picloc, hud_hp_ico, picsize, '1 1 1', 1, DRAWFLAG_NORMAL); + if(alarm1time) + { + vehicle_alarm(self, CH_PAIN_SINGLE, "misc/null.wav"); + alarm1time = 0; + } + } + + // Shield bar + picsize = draw_getimagesize(hud_sh_bar) * autocvar_cl_vehicles_hudscale; + picloc = '69 140 0' * autocvar_cl_vehicles_hudscale; + drawsetcliparea(hudloc_x + picloc_x + (picsize_x * (1 - shield)), 0, vid_conwidth, vid_conheight); + drawpic(hudloc + picloc, hud_sh_bar, picsize, '1 1 1', 1, DRAWFLAG_NORMAL); + drawresetcliparea(); + // .. and icon + picloc = '40 136 0' * autocvar_cl_vehicles_hudscale; + picsize = draw_getimagesize(hud_sh_ico) * autocvar_cl_vehicles_hudscale; + if(shield < 0.25) + { + if(alarm2time < time) + { + alarm2time = time + 1; + vehicle_alarm(self, CH_TRIGGER_SINGLE, "vehicles/alarm_shield.wav"); + } + drawpic(hudloc + picloc, hud_sh_ico, picsize, '1 0 0' + '0 1 1' * sin(time * 8), 1, DRAWFLAG_NORMAL); + } + else + { + drawpic(hudloc + picloc, hud_sh_ico, picsize, '1 1 1', 1, DRAWFLAG_NORMAL); + if(alarm2time) + { + vehicle_alarm(self, CH_TRIGGER_SINGLE, "misc/null.wav"); + alarm2time = 0; + } + } + + ammo1 *= 0.01; + ammo2 *= 0.01; + + // Gunner1 bar + picsize = draw_getimagesize(hud_ammo1_bar) * autocvar_cl_vehicles_hudscale; + picloc = '450 69 0' * autocvar_cl_vehicles_hudscale; + drawsetcliparea(hudloc_x + picloc_x, picloc_y, picsize_x * ammo1, vid_conheight); + drawpic(hudloc + picloc, hud_ammo1_bar, picsize, '1 1 1', 1, DRAWFLAG_NORMAL); + drawresetcliparea(); + + // Right gunner slot occupied? + if(!AuxiliaryXhair[1].draw2d) + { - shield = (picsize_x * 0.5) - (0.5 * stringwidth(_("No right gunner!"), FALSE, '1 0 0' * picsize_y + '0 1 0' * picsize_y)); ++ shield = (picsize_x * 0.5) - (0.5 * stringwidth(_("No right gunner!"), false, '1 0 0' * picsize_y + '0 1 0' * picsize_y)); + drawfill(hudloc + picloc - '0.2 0.2 0', picsize + '0.4 0.4 0', '0.25 0.25 0.25', 0.75, DRAWFLAG_NORMAL); + drawstring(hudloc + picloc + '1 0 0' * shield, _("No right gunner!"), '1 0 0' * picsize_y + '0 1 0' * picsize_y, '1 0 0' + '0 1 1' * sin(time * 10), 1, DRAWFLAG_NORMAL); + } + + // .. and icon + picsize = 1.5 * draw_getimagesize(hud_energy) * autocvar_cl_vehicles_hudscale; + picloc = '664 60 0' * autocvar_cl_vehicles_hudscale; + if(ammo1 < 0.2) + drawpic(hudloc + picloc, hud_energy, picsize, '1 0 0' + '0 1 1' * sin(time * 8), 1, DRAWFLAG_NORMAL); + else + drawpic(hudloc + picloc, hud_energy, picsize, '1 1 1', 1, DRAWFLAG_NORMAL); + + // Gunner2 bar + picsize = draw_getimagesize(hud_ammo2_bar) * autocvar_cl_vehicles_hudscale; + picloc = '450 140 0' * autocvar_cl_vehicles_hudscale; + drawsetcliparea(hudloc_x + picloc_x, picloc_y, picsize_x * ammo2, vid_conheight); + drawpic(hudloc + picloc, hud_ammo2_bar, picsize, '1 1 1', 1, DRAWFLAG_NORMAL); + drawresetcliparea(); + // Left gunner slot occupied? + if(!AuxiliaryXhair[2].draw2d) + { - shield = (picsize_x * 0.5) - (0.5 * stringwidth(_("No left gunner!"), FALSE, '1 0 0' * picsize_y + '0 1 0' * picsize_y)); ++ shield = (picsize_x * 0.5) - (0.5 * stringwidth(_("No left gunner!"), false, '1 0 0' * picsize_y + '0 1 0' * picsize_y)); + drawfill(hudloc + picloc - '0.2 0.2 0', picsize + '0.4 0.4 0', '0.25 0.25 0.25', 0.75, DRAWFLAG_NORMAL); + drawstring(hudloc + picloc + '1 0 0' * shield, _("No left gunner!"), '1 0 0' * picsize_y + '0 1 0' * picsize_y, '1 0 0' + '0 1 1' * sin(time * 10), 1, DRAWFLAG_NORMAL); + } + + // .. and icon + picsize = 1.5 * draw_getimagesize(hud_energy) * autocvar_cl_vehicles_hudscale; + picloc = '664 130 0' * autocvar_cl_vehicles_hudscale; + if(ammo2 < 0.2) + drawpic(hudloc + picloc, hud_energy, picsize, '1 0 0' + '0 1 1' * sin(time * 8), 1, DRAWFLAG_NORMAL); + else + drawpic(hudloc + picloc, hud_energy, picsize, '1 1 1', 1, DRAWFLAG_NORMAL); + + if (scoreboard_showscores) + HUD_DrawScoreboard(); + else + { + picsize = draw_getimagesize(waki_xhair); + picsize_x *= 0.5; + picsize_y *= 0.5; + drawpic('0.5 0 0' * (vid_conwidth - picsize_x) + '0 0.5 0' * (vid_conheight - picsize_y), waki_xhair, picsize, '1 1 1', 1, DRAWFLAG_NORMAL); + } - return TRUE; ++ return true; + } + case VR_SETUP: + { + // raygun-locked + AuxiliaryXhair[0].axh_image = "gfx/vehicles/axh-bracket.tga"; + AuxiliaryXhair[0].axh_scale = 0.5; + + // Gunner1 + AuxiliaryXhair[1].axh_image = "gfx/vehicles/axh-target.tga"; + AuxiliaryXhair[1].axh_scale = 0.75; + + // Gunner2 + AuxiliaryXhair[2].axh_image = "gfx/vehicles/axh-target.tga"; + AuxiliaryXhair[2].axh_scale = 0.75; - return TRUE; ++ return true; + } + case VR_PRECACHE: + { - return TRUE; ++ return true; + } + } + - return TRUE; ++ return true; +} + +#endif // CSQC +#endif // REGISTER_VEHICLE diff --cc qcsrc/common/vehicles/unit/bumblebee.qh index 000000000,000000000..08f9adf69 new file mode 100644 --- /dev/null +++ b/qcsrc/common/vehicles/unit/bumblebee.qh @@@ -1,0 -1,0 +1,6 @@@ ++#ifndef BUMBLEBEE_H ++#define BUMBLEBEE_H ++ ++void bumble_raygun_read(bool bIsNew); ++ ++#endif diff --cc qcsrc/common/vehicles/unit/racer.qc index dae5219d6,000000000..1a54f4aa3 mode 100644,000000..100644 --- a/qcsrc/common/vehicles/unit/racer.qc +++ b/qcsrc/common/vehicles/unit/racer.qc @@@ -1,909 -1,0 +1,911 @@@ +#ifdef REGISTER_VEHICLE +REGISTER_VEHICLE( +/* VEH_##id */ RACER, +/* function */ v_racer, +/* spawnflags */ VHF_DMGSHAKE | VHF_DMGROLL, +/* mins,maxs */ '-120 -120 -40' * 0.5, '120 120 40' * 0.5, +/* model */ "models/vehicles/wakizashi.dpm", +/* head_model */ "null", +/* hud_model */ "models/vehicles/wakizashi_cockpit.dpm", +/* tags */ "", "", "tag_viewport", +/* netname */ "racer", +/* fullname */ _("Racer") +); +#else +#ifdef SVQC ++#include "../../effects.qh" ++ +float autocvar_g_vehicle_racer; + +float autocvar_g_vehicle_racer_speed_afterburn; +float autocvar_g_vehicle_racer_afterburn_cost; + +float autocvar_g_vehicle_racer_waterburn_cost; +float autocvar_g_vehicle_racer_waterburn_speed; + +float autocvar_g_vehicle_racer_water_speed_forward; +float autocvar_g_vehicle_racer_water_speed_strafe; + +float autocvar_g_vehicle_racer_anglestabilizer; +float autocvar_g_vehicle_racer_downforce; + +float autocvar_g_vehicle_racer_speed_forward; +float autocvar_g_vehicle_racer_speed_strafe; +float autocvar_g_vehicle_racer_springlength; +float autocvar_g_vehicle_racer_upforcedamper; +float autocvar_g_vehicle_racer_friction; + +var float autocvar_g_vehicle_racer_water_time = 5; + +float autocvar_g_vehicle_racer_hovertype; +float autocvar_g_vehicle_racer_hoverpower; + +float autocvar_g_vehicle_racer_turnroll; +float autocvar_g_vehicle_racer_turnspeed; +float autocvar_g_vehicle_racer_pitchspeed; + +float autocvar_g_vehicle_racer_energy; +float autocvar_g_vehicle_racer_energy_regen; +float autocvar_g_vehicle_racer_energy_regen_pause; + +float autocvar_g_vehicle_racer_health; +float autocvar_g_vehicle_racer_health_regen; +float autocvar_g_vehicle_racer_health_regen_pause; + +float autocvar_g_vehicle_racer_shield; +float autocvar_g_vehicle_racer_shield_regen; +float autocvar_g_vehicle_racer_shield_regen_pause; + +float autocvar_g_vehicle_racer_cannon_cost; +float autocvar_g_vehicle_racer_cannon_damage; +float autocvar_g_vehicle_racer_cannon_radius; +float autocvar_g_vehicle_racer_cannon_refire; +float autocvar_g_vehicle_racer_cannon_speed; +float autocvar_g_vehicle_racer_cannon_spread; +float autocvar_g_vehicle_racer_cannon_force; + +float autocvar_g_vehicle_racer_rocket_accel; +float autocvar_g_vehicle_racer_rocket_damage; +float autocvar_g_vehicle_racer_rocket_radius; +float autocvar_g_vehicle_racer_rocket_force; +float autocvar_g_vehicle_racer_rocket_refire; +float autocvar_g_vehicle_racer_rocket_speed; +float autocvar_g_vehicle_racer_rocket_turnrate; + +float autocvar_g_vehicle_racer_rocket_locktarget; +float autocvar_g_vehicle_racer_rocket_locking_time; +float autocvar_g_vehicle_racer_rocket_locking_releasetime; +float autocvar_g_vehicle_racer_rocket_locked_time; +float autocvar_g_vehicle_racer_rocket_locked_maxangle; +float autocvar_g_vehicle_racer_rocket_climbspeed; + +float autocvar_g_vehicle_racer_respawntime; + +float autocvar_g_vehicle_racer_blowup_radius; +float autocvar_g_vehicle_racer_blowup_coredamage; +float autocvar_g_vehicle_racer_blowup_edgedamage; +float autocvar_g_vehicle_racer_blowup_forceintensity; + +float autocvar_g_vehicle_racer_bouncefactor; +float autocvar_g_vehicle_racer_bouncestop; +vector autocvar_g_vehicle_racer_bouncepain; + +var vector racer_force_from_tag(string tag_name, float spring_length, float max_power); + +void racer_align4point(float _delta) +{ + vector push_vector; + float fl_push, fr_push, bl_push, br_push; + + push_vector = racer_force_from_tag("tag_engine_fr", autocvar_g_vehicle_racer_springlength, autocvar_g_vehicle_racer_hoverpower); + fr_push = force_fromtag_normpower; + //vehicles_sweap_collision(force_fromtag_origin, self.velocity, _delta, v_add, autocvar_g_vehicle_racer_collision_multiplier); + + push_vector += racer_force_from_tag("tag_engine_fl", autocvar_g_vehicle_racer_springlength, autocvar_g_vehicle_racer_hoverpower); + fl_push = force_fromtag_normpower; + //vehicles_sweap_collision(force_fromtag_origin, self.velocity, _delta, v_add, autocvar_g_vehicle_racer_collision_multiplier); + + push_vector += racer_force_from_tag("tag_engine_br", autocvar_g_vehicle_racer_springlength, autocvar_g_vehicle_racer_hoverpower); + br_push = force_fromtag_normpower; + //vehicles_sweap_collision(force_fromtag_origin, self.velocity, _delta, v_add, autocvar_g_vehicle_racer_collision_multiplier); + + push_vector += racer_force_from_tag("tag_engine_bl", autocvar_g_vehicle_racer_springlength, autocvar_g_vehicle_racer_hoverpower); + bl_push = force_fromtag_normpower; + //vehicles_sweap_collision(force_fromtag_origin, self.velocity, _delta, v_add, autocvar_g_vehicle_racer_collision_multiplier); + + self.velocity += push_vector * _delta; + + if(pointcontents(self.origin - '0 0 64') == CONTENT_WATER) + if(self.owner.BUTTON_CROUCH && time < self.air_finished) + self.velocity_z += 30; + else + self.velocity_z += 200; + + // Anti ocilation + if(self.velocity_z > 0) + self.velocity_z *= 1 - autocvar_g_vehicle_racer_upforcedamper * _delta; + + push_vector_x = (fl_push - bl_push); + push_vector_x += (fr_push - br_push); + push_vector_x *= 360; + + push_vector_z = (fr_push - fl_push); + push_vector_z += (br_push - bl_push); + push_vector_z *= 360; + + // Apply angle diffrance + self.angles_z += push_vector_z * _delta; + self.angles_x += push_vector_x * _delta; + + // Apply stabilizer + self.angles_x *= 1 - (autocvar_g_vehicle_racer_anglestabilizer * _delta); + self.angles_z *= 1 - (autocvar_g_vehicle_racer_anglestabilizer * _delta); +} + +void racer_fire_cannon(string tagname) +{ + vector v; + entity bolt; + + v = gettaginfo(self, gettagindex(self, tagname)); + bolt = vehicles_projectile("wakizashi_gun_muzzleflash", W_Sound("lasergun_fire"), + v, normalize(v_forward + randomvec() * autocvar_g_vehicle_racer_cannon_spread) * autocvar_g_vehicle_racer_cannon_speed, + autocvar_g_vehicle_racer_cannon_damage, autocvar_g_vehicle_racer_cannon_radius, autocvar_g_vehicle_racer_cannon_force, 0, - DEATH_VH_WAKI_GUN, PROJECTILE_WAKICANNON, 0, TRUE, TRUE, self.owner); ++ DEATH_VH_WAKI_GUN, PROJECTILE_WAKICANNON, 0, true, true, self.owner); + + // Fix z-aim (for chase mode) + v = normalize(trace_endpos - bolt.origin); + v_forward_z = v_z * 0.5; + bolt.velocity = v_forward * autocvar_g_vehicle_racer_cannon_speed; +} + +void racer_rocket_groundhugger() +{ + vector olddir, newdir; + float oldvel, newvel; + + self.nextthink = time; + + if(self.owner.deadflag != DEAD_NO || self.cnt < time) + { + self.use(); + return; + } + + if(!self.realowner.vehicle) + { + UpdateCSQCProjectile(self); + return; + } + + olddir = normalize(self.velocity); + oldvel = vlen(self.velocity); + newvel = oldvel + self.lip; + + tracebox(self.origin, self.mins, self.maxs, self.origin + olddir * 64, MOVE_WORLDONLY,self); + if(trace_fraction <= 0.5) + { + // Hitting somethign soon, just speed ahead + self.velocity = olddir * newvel; + UpdateCSQCProjectile(self); + return; + } + + traceline(trace_endpos, trace_endpos - '0 0 64', MOVE_NORMAL, self); + if(trace_fraction != 1.0) + { + newdir = normalize(trace_endpos + '0 0 64' - self.origin) * autocvar_g_vehicle_racer_rocket_turnrate; + self.velocity = normalize(olddir + newdir) * newvel; + } + else + { + self.velocity = olddir * newvel; + self.velocity_z -= 1600 * sys_frametime; // 2x grav looks better for this one + } + + if(pointcontents(self.origin - '0 0 32') == CONTENT_WATER) + self.velocity_z += 200; + + UpdateCSQCProjectile(self); + return; +} + +void racer_rocket_tracker() +{ + vector olddir, newdir; + float oldvel, newvel; + + self.nextthink = time; + + if (self.owner.deadflag != DEAD_NO || self.cnt < time) + { + self.use(); + return; + } + + if(!self.realowner.vehicle) + { + UpdateCSQCProjectile(self); + return; + } + + olddir = normalize(self.velocity); + oldvel = vlen(self.velocity); + newvel = oldvel + self.lip; + makevectors(vectoangles(olddir)); + + float time_to_impact = min(vlen(self.enemy.origin - self.origin) / vlen(self.velocity), 1); + vector predicted_origin = self.enemy.origin + self.enemy.velocity * time_to_impact; + + traceline(self.origin, self.origin + v_forward * 64 - '0 0 32', MOVE_NORMAL, self); + newdir = normalize(predicted_origin - self.origin); + + //vector + float height_diff = predicted_origin_z - self.origin_z; + + if(vlen(newdir - v_forward) > autocvar_g_vehicle_racer_rocket_locked_maxangle) + { + //bprint("Target lost!\n"); + //dprint("OF:", ftos(vlen(newdir - v_forward)), "\n"); + self.think = racer_rocket_groundhugger; + return; + } + + if(trace_fraction != 1.0 && trace_ent != self.enemy) + newdir_z += 16 * sys_frametime; + + self.velocity = normalize(olddir + newdir * autocvar_g_vehicle_racer_rocket_turnrate) * newvel; + self.velocity_z -= 800 * sys_frametime; + self.velocity_z += max(height_diff, autocvar_g_vehicle_racer_rocket_climbspeed) * sys_frametime ; + + UpdateCSQCProjectile(self); + return; +} + +void racer_fire_rocket(string tagname, entity trg) +{ + vector v = gettaginfo(self, gettagindex(self, tagname)); + entity rocket = rocket = vehicles_projectile("wakizashi_rocket_launch", W_Sound("rocket_fire"), + v, v_forward * autocvar_g_vehicle_racer_rocket_speed, + autocvar_g_vehicle_racer_rocket_damage, autocvar_g_vehicle_racer_rocket_radius, autocvar_g_vehicle_racer_rocket_force, 3, - DEATH_VH_WAKI_ROCKET, PROJECTILE_WAKIROCKET, 20, FALSE, FALSE, self.owner); ++ DEATH_VH_WAKI_ROCKET, PROJECTILE_WAKIROCKET, 20, false, false, self.owner); + + rocket.lip = autocvar_g_vehicle_racer_rocket_accel * sys_frametime; + rocket.wait = autocvar_g_vehicle_racer_rocket_turnrate; + rocket.nextthink = time; + rocket.enemy = trg; + rocket.cnt = time + 15; + + if(trg) + rocket.think = racer_rocket_tracker; + else + rocket.think = racer_rocket_groundhugger; +} + +float racer_frame() +{ + entity player, racer; + vector df; + float ftmp; + + if(intermission_running) + { + self.vehicle.velocity = '0 0 0'; + self.vehicle.avelocity = '0 0 0'; + return 1; + } + + player = self; + racer = self.vehicle; + self = racer; + + vehicles_painframe(); + + if(pointcontents(racer.origin) != CONTENT_WATER) + racer.air_finished = time + autocvar_g_vehicle_racer_water_time; + + if(racer.deadflag != DEAD_NO) + { + self = player; + player.BUTTON_ATCK = player.BUTTON_ATCK2 = 0; + return 1; + } + + racer_align4point(frametime); + + player.BUTTON_ZOOM = player.BUTTON_CROUCH = 0; + + crosshair_trace(player); + + racer.angles_x *= -1; + + // Yaw + ftmp = autocvar_g_vehicle_racer_turnspeed * frametime; + ftmp = bound(-ftmp, shortangle_f(player.v_angle_y - racer.angles_y, racer.angles_y), ftmp); + racer.angles_y = anglemods(racer.angles_y + ftmp); + + // Roll + racer.angles_z += -ftmp * autocvar_g_vehicle_racer_turnroll * frametime; + + // Pitch + ftmp = autocvar_g_vehicle_racer_pitchspeed * frametime; + ftmp = bound(-ftmp, shortangle_f(player.v_angle_x - racer.angles_x, racer.angles_x), ftmp); + racer.angles_x = bound(-30, anglemods(racer.angles_x + ftmp), 30); + + makevectors(racer.angles); + racer.angles_x *= -1; + + //ftmp = racer.velocity_z; + df = racer.velocity * -autocvar_g_vehicle_racer_friction; + //racer.velocity_z = ftmp; + + if(vlen(player.movement) != 0) + { + if(pointcontents(racer.origin) == CONTENT_WATER) + { + if(player.movement_x) { df += v_forward * ((player.movement_x > 0) ? autocvar_g_vehicle_racer_water_speed_forward : -autocvar_g_vehicle_racer_water_speed_forward); } + if(player.movement_y) { df += v_right * ((player.movement_y > 0) ? autocvar_g_vehicle_racer_water_speed_strafe : -autocvar_g_vehicle_racer_water_speed_strafe); } + } + else + { + if(player.movement_x) { df += v_forward * ((player.movement_x > 0) ? autocvar_g_vehicle_racer_speed_forward : -autocvar_g_vehicle_racer_speed_forward); } + if(player.movement_y) { df += v_right * ((player.movement_y > 0) ? autocvar_g_vehicle_racer_speed_strafe : -autocvar_g_vehicle_racer_speed_strafe); } + } + + if(self.sound_nexttime < time || self.sounds != 1) + { + self.sounds = 1; + self.sound_nexttime = time + 10.922667; //soundlength("vehicles/racer_move.wav"); + sound (self, CH_TRIGGER_SINGLE, "vehicles/racer_move.wav", VOL_VEHICLEENGINE, ATTEN_NORM); + } + } + else + { + if(self.sound_nexttime < time || self.sounds != 0) + { + self.sounds = 0; + self.sound_nexttime = time + 11.888604; //soundlength("vehicles/racer_idle.wav"); + sound (self, CH_TRIGGER_SINGLE, "vehicles/racer_idle.wav", VOL_VEHICLEENGINE, ATTEN_NORM); + } + } + + // Afterburn + if (player.BUTTON_JUMP && racer.vehicle_energy >= (autocvar_g_vehicle_racer_afterburn_cost * frametime)) + { + if(time - racer.wait > 0.2) + pointparticles(particleeffectnum("wakizashi_booster_smoke"), self.origin - v_forward * 32, v_forward * vlen(self.velocity), 1); + + racer.wait = time; + + if(pointcontents(racer.origin) == CONTENT_WATER) + { + racer.vehicle_energy -= autocvar_g_vehicle_racer_waterburn_cost * frametime; + df += (v_forward * autocvar_g_vehicle_racer_waterburn_speed); + } + else + { + racer.vehicle_energy -= autocvar_g_vehicle_racer_afterburn_cost * frametime; + df += (v_forward * autocvar_g_vehicle_racer_speed_afterburn); + } + + if(racer.invincible_finished < time) + { + traceline(racer.origin, racer.origin - '0 0 256', MOVE_NORMAL, self); + if(trace_fraction != 1.0) + pointparticles(particleeffectnum("smoke_small"), trace_endpos, '0 0 0', 1); + + racer.invincible_finished = time + 0.1 + (random() * 0.1); + } + + if(racer.strength_finished < time) + { + racer.strength_finished = time + 10.922667; //soundlength("vehicles/racer_boost.wav"); + sound (racer.tur_head, CH_TRIGGER_SINGLE, "vehicles/racer_boost.wav", VOL_VEHICLEENGINE, ATTEN_NORM); + } + } + else + { + racer.strength_finished = 0; + sound (racer.tur_head, CH_TRIGGER_SINGLE, "misc/null.wav", VOL_VEHICLEENGINE, ATTEN_NORM); + } + + df -= v_up * (vlen(racer.velocity) * autocvar_g_vehicle_racer_downforce); + player.movement = racer.velocity += df * frametime; + + if(!forbidWeaponUse(player)) + if(player.BUTTON_ATCK) + if(time > racer.attack_finished_single) + if(racer.vehicle_energy >= autocvar_g_vehicle_racer_cannon_cost) + { + racer.vehicle_energy -= autocvar_g_vehicle_racer_cannon_cost; + racer.wait = time; + + crosshair_trace(player); + if(racer.cnt) + { + racer_fire_cannon("tag_fire1"); + racer.cnt = 0; + } + else + { + racer_fire_cannon("tag_fire2"); + racer.cnt = 1; + } + racer.attack_finished_single = time + autocvar_g_vehicle_racer_cannon_refire; + } + + if(autocvar_g_vehicle_racer_rocket_locktarget) + { + vehicles_locktarget((1 / autocvar_g_vehicle_racer_rocket_locking_time) * frametime, + (1 / autocvar_g_vehicle_racer_rocket_locking_releasetime) * frametime, + autocvar_g_vehicle_racer_rocket_locked_time); + + if(self.lock_target) + { + if(racer.lock_strength == 1) + UpdateAuxiliaryXhair(player, real_origin(self.lock_target), '1 0 0', 0); + else if(self.lock_strength > 0.5) + UpdateAuxiliaryXhair(player, real_origin(self.lock_target), '0 1 0', 0); + else if(self.lock_strength < 0.5) + UpdateAuxiliaryXhair(player, real_origin(self.lock_target), '0 0 1', 0); + } + } + + if(!forbidWeaponUse(player)) + if(time > racer.delay) + if(player.BUTTON_ATCK2) + { + racer.misc_bulletcounter += 1; + racer.delay = time + 0.3; + + if(racer.misc_bulletcounter == 1) + racer_fire_rocket("tag_rocket_r", (racer.lock_strength == 1 && racer.lock_target) ? racer.lock_target : world); + else if(racer.misc_bulletcounter == 2) + { + racer_fire_rocket("tag_rocket_l", (racer.lock_strength == 1 && racer.lock_target) ? racer.lock_target : world); + racer.lock_strength = 0; + racer.lock_target = world; + racer.misc_bulletcounter = 0; + + racer.delay = time + autocvar_g_vehicle_racer_rocket_refire; + racer.lip = time; + } + } + player.vehicle_reload1 = bound(0, 100 * ((time - racer.lip) / (racer.delay - racer.lip)), 100); + + if(racer.vehicle_flags & VHF_SHIELDREGEN) - vehicles_regen(racer.dmg_time, vehicle_shield, autocvar_g_vehicle_racer_shield, autocvar_g_vehicle_racer_shield_regen_pause, autocvar_g_vehicle_racer_shield_regen, frametime, TRUE); ++ vehicles_regen(racer.dmg_time, vehicle_shield, autocvar_g_vehicle_racer_shield, autocvar_g_vehicle_racer_shield_regen_pause, autocvar_g_vehicle_racer_shield_regen, frametime, true); + + if(racer.vehicle_flags & VHF_HEALTHREGEN) - vehicles_regen(racer.dmg_time, vehicle_health, autocvar_g_vehicle_racer_health, autocvar_g_vehicle_racer_health_regen_pause, autocvar_g_vehicle_racer_health_regen, frametime, FALSE); ++ vehicles_regen(racer.dmg_time, vehicle_health, autocvar_g_vehicle_racer_health, autocvar_g_vehicle_racer_health_regen_pause, autocvar_g_vehicle_racer_health_regen, frametime, false); + + if(racer.vehicle_flags & VHF_ENERGYREGEN) - vehicles_regen(racer.wait, vehicle_energy, autocvar_g_vehicle_racer_energy, autocvar_g_vehicle_racer_energy_regen_pause, autocvar_g_vehicle_racer_energy_regen, frametime, FALSE); ++ vehicles_regen(racer.wait, vehicle_energy, autocvar_g_vehicle_racer_energy, autocvar_g_vehicle_racer_energy_regen_pause, autocvar_g_vehicle_racer_energy_regen, frametime, false); + + + VEHICLE_UPDATE_PLAYER(player, health, racer); + VEHICLE_UPDATE_PLAYER(player, energy, racer); + + if(racer.vehicle_flags & VHF_HASSHIELD) + VEHICLE_UPDATE_PLAYER(player, shield, racer); + + player.BUTTON_ATCK = player.BUTTON_ATCK2 = 0; + setorigin(player,racer.origin + '0 0 32'); + player.velocity = racer.velocity; + + self = player; + return 1; +} + +void racer_think() +{ + self.nextthink = time; + + float pushdeltatime = time - self.lastpushtime; + if (pushdeltatime > 0.15) pushdeltatime = 0; + self.lastpushtime = time; + if(!pushdeltatime) return; + + tracebox(self.origin, self.mins, self.maxs, self.origin - ('0 0 1' * autocvar_g_vehicle_racer_springlength), MOVE_NOMONSTERS, self); + + vector df = self.velocity * -autocvar_g_vehicle_racer_friction; + df_z += (1 - trace_fraction) * autocvar_g_vehicle_racer_hoverpower + sin(time * 2) * (autocvar_g_vehicle_racer_springlength * 2); + + if(pointcontents(self.origin - '0 0 64') == CONTENT_WATER) + self.velocity_z += 200; + + self.velocity += df * pushdeltatime; + if(self.velocity_z > 0) + self.velocity_z *= 1 - autocvar_g_vehicle_racer_upforcedamper * pushdeltatime; + + self.angles_x *= 1 - (autocvar_g_vehicle_racer_anglestabilizer * pushdeltatime); + self.angles_z *= 1 - (autocvar_g_vehicle_racer_anglestabilizer * pushdeltatime); + + CSQCMODEL_AUTOUPDATE(); +} + +void racer_exit(float eject) +{ + vector spot; + + self.think = racer_think; + self.nextthink = time; + self.movetype = MOVETYPE_BOUNCE; + sound (self.tur_head, CH_TRIGGER_SINGLE, "misc/null.wav", VOL_VEHICLEENGINE, ATTEN_NORM); + + if(!self.owner) + return; + + makevectors(self.angles); + if(eject) + { + spot = self.origin + v_forward * 100 + '0 0 64'; + spot = vehicles_findgoodexit(spot); + setorigin(self.owner , spot); + self.owner.velocity = (v_up + v_forward * 0.25) * 750; + self.owner.oldvelocity = self.owner.velocity; + } + else + { + if(vlen(self.velocity) > 2 * autocvar_sv_maxairspeed) + { + self.owner.velocity = normalize(self.velocity) * autocvar_sv_maxairspeed * 2; + self.owner.velocity_z += 200; + spot = self.origin + v_forward * 32 + '0 0 32'; + spot = vehicles_findgoodexit(spot); + } + else + { + self.owner.velocity = self.velocity * 0.5; + self.owner.velocity_z += 10; + spot = self.origin - v_forward * 200 + '0 0 32'; + spot = vehicles_findgoodexit(spot); + } + self.owner.oldvelocity = self.owner.velocity; + setorigin(self.owner , spot); + } + antilag_clear(self.owner); + self.owner = world; +} + +void racer_blowup() +{ + self.deadflag = DEAD_DEAD; + self.vehicle_exit(VHEF_NORMAL); + + RadiusDamage (self, self.enemy, autocvar_g_vehicle_racer_blowup_coredamage, + autocvar_g_vehicle_racer_blowup_edgedamage, + autocvar_g_vehicle_racer_blowup_radius, world, world, + autocvar_g_vehicle_racer_blowup_forceintensity, + DEATH_VH_WAKI_DEATH, world); + + self.nextthink = time + autocvar_g_vehicle_racer_respawntime; + self.think = vehicles_spawn; + self.movetype = MOVETYPE_NONE; + self.effects = EF_NODRAW; + + self.colormod = '0 0 0'; + self.avelocity = '0 0 0'; + self.velocity = '0 0 0'; + + setorigin(self, self.pos1); +} + +void racer_blowup_think() +{ + self.nextthink = time; + + if(time >= self.delay) + racer_blowup(); + + CSQCMODEL_AUTOUPDATE(); +} + +void racer_deadtouch() +{ + self.avelocity_x *= 0.7; + self.cnt -= 1; + if(self.cnt <= 0) + racer_blowup(); +} + +void spawnfunc_vehicle_racer() +{ + if(!autocvar_g_vehicle_racer) { remove(self); return; } - if(!vehicle_initialize(VEH_RACER, FALSE)) { remove(self); return; } ++ if(!vehicle_initialize(VEH_RACER, false)) { remove(self); return; } +} + +float v_racer(float req) +{ + switch(req) + { + case VR_IMPACT: + { + if(autocvar_g_vehicle_racer_bouncepain) + vehicles_impact(autocvar_g_vehicle_racer_bouncepain_x, autocvar_g_vehicle_racer_bouncepain_y, autocvar_g_vehicle_racer_bouncepain_z); - return TRUE; ++ return true; + } + case VR_ENTER: + { + self.movetype = MOVETYPE_BOUNCE; + self.owner.vehicle_health = (self.vehicle_health / autocvar_g_vehicle_racer_health) * 100; + self.owner.vehicle_shield = (self.vehicle_shield / autocvar_g_vehicle_racer_shield) * 100; + + if(self.owner.flagcarried) + setorigin(self.owner.flagcarried, '-190 0 96'); + - return TRUE; ++ return true; + } + case VR_THINK: + { - return TRUE; ++ return true; + } + case VR_DEATH: + { + self.health = 0; + self.event_damage = func_null; + self.solid = SOLID_CORPSE; + self.takedamage = DAMAGE_NO; + self.deadflag = DEAD_DYING; + self.movetype = MOVETYPE_BOUNCE; + self.wait = time; + self.delay = 2 + time + random() * 3; + self.cnt = 1 + random() * 2; + self.touch = racer_deadtouch; + + Send_Effect(EFFECT_EXPLOSION_MEDIUM, self.origin, '0 0 0', 1); + + if(random() < 0.5) + self.avelocity_z = 32; + else + self.avelocity_z = -32; + + self.avelocity_x = -vlen(self.velocity) * 0.2; + self.velocity += '0 0 700'; + self.colormod = '-0.5 -0.5 -0.5'; + + self.think = racer_blowup_think; + self.nextthink = time; + - return TRUE; ++ return true; + } + case VR_SPAWN: + { + if(self.scale != 0.5) + { + if(autocvar_g_vehicle_racer_hovertype != 0) + racer_force_from_tag = vehicles_force_fromtag_maglev; + else + racer_force_from_tag = vehicles_force_fromtag_hover; + + // FIXME: this be hakkz, fix the models insted (scale body, add tag_viewport to the hudmodel). + self.scale = 0.5; + setattachment(self.vehicle_hudmodel, self, ""); + setattachment(self.vehicle_viewport, self, "tag_viewport"); + + self.mass = 900; + } + + self.think = racer_think; + self.nextthink = time; + self.vehicle_health = autocvar_g_vehicle_racer_health; + self.vehicle_shield = autocvar_g_vehicle_racer_shield; + + self.movetype = MOVETYPE_TOSS; + self.solid = SOLID_SLIDEBOX; + self.delay = time; + self.scale = 0.5; + + self.PlayerPhysplug = racer_frame; + + self.bouncefactor = autocvar_g_vehicle_racer_bouncefactor; + self.bouncestop = autocvar_g_vehicle_racer_bouncestop; + self.damageforcescale = 0.5; + self.vehicle_health = autocvar_g_vehicle_racer_health; + self.vehicle_shield = autocvar_g_vehicle_racer_shield; + - return TRUE; ++ return true; + } + case VR_SETUP: + { + if(autocvar_g_vehicle_racer_energy) + if(autocvar_g_vehicle_racer_energy_regen) + self.vehicle_flags |= VHF_ENERGYREGEN; + + if(autocvar_g_vehicle_racer_shield) + self.vehicle_flags |= VHF_HASSHIELD; + + if(autocvar_g_vehicle_racer_shield_regen) + self.vehicle_flags |= VHF_SHIELDREGEN; + + if(autocvar_g_vehicle_racer_health_regen) + self.vehicle_flags |= VHF_HEALTHREGEN; + + self.vehicle_exit = racer_exit; + self.respawntime = autocvar_g_vehicle_racer_respawntime; + self.vehicle_health = autocvar_g_vehicle_racer_health; + self.vehicle_shield = autocvar_g_vehicle_racer_shield; + self.max_health = self.vehicle_health; + - return TRUE; ++ return true; + } + case VR_PRECACHE: + { + precache_sound (W_Sound("lasergun_fire")); + precache_sound (W_Sound("rocket_fire")); + + precache_sound ("vehicles/racer_idle.wav"); + precache_sound ("vehicles/racer_move.wav"); + precache_sound ("vehicles/racer_boost.wav"); + + precache_model ("models/vhshield.md3"); + precache_model ("models/vehicles/wakizashi.dpm"); + precache_model ("models/vehicles/wakizashi_cockpit.dpm"); - return TRUE; ++ return true; + } + } + - return TRUE; ++ return true; +} + +#endif // SVQC +#ifdef CSQC + +#define waki_ico "gfx/vehicles/waki.tga" +#define waki_eng "gfx/vehicles/waki_e.tga" +#define waki_gun "gfx/vehicles/waki_guns.tga" +#define waki_rkt "gfx/vehicles/waki_rockets.tga" +#define waki_xhair "gfx/vehicles/axh-special1.tga" + +float v_racer(float req) +{ + switch(req) + { + case VR_HUD: + { + if(autocvar_r_letterbox) - return TRUE; ++ return true; + + vector picsize, hudloc = '0 0 0', pic2size, picloc; + + // Fetch health & ammo stats + HUD_GETVEHICLESTATS + + picsize = draw_getimagesize(hud_bg) * autocvar_cl_vehicles_hudscale; + hudloc_y = vid_conheight - picsize_y; + hudloc_x = vid_conwidth * 0.5 - picsize_x * 0.5; + + drawpic(hudloc, hud_bg, picsize, '1 1 1', autocvar_cl_vehicles_hudalpha, DRAWFLAG_NORMAL); + + shield *= 0.01; + vh_health *= 0.01; + energy *= 0.01; + reload1 *= 0.01; + + pic2size = draw_getimagesize(waki_ico) * (autocvar_cl_vehicles_hudscale * 0.8); + picloc = picsize * 0.5 - pic2size * 0.5; + if(vh_health < 0.25) + drawpic(hudloc + picloc, waki_ico, pic2size, '1 0 0' + '0 1 1' * sin(time * 8), 1, DRAWFLAG_NORMAL); + else + drawpic(hudloc + picloc, waki_ico, pic2size, '1 1 1' * vh_health + '1 0 0' * (1 - vh_health), 1, DRAWFLAG_NORMAL); + drawpic(hudloc + picloc, waki_eng, pic2size, '1 1 1' * energy + '1 0 0' * (1 - energy), 1, DRAWFLAG_NORMAL); + drawpic(hudloc + picloc, waki_gun, pic2size, '1 1 1' * energy + '1 0 0' * (1 - energy), 1, DRAWFLAG_NORMAL); + drawpic(hudloc + picloc, waki_rkt, pic2size, '1 1 1' * reload1 + '1 0 0' * (1 - reload1), 1, DRAWFLAG_NORMAL); + drawpic(hudloc + picloc, hud_sh, pic2size, '1 1 1', shield, DRAWFLAG_NORMAL); + + // Health bar + picsize = draw_getimagesize(hud_hp_bar) * autocvar_cl_vehicles_hudscale; + picloc = '69 69 0' * autocvar_cl_vehicles_hudscale; + drawsetcliparea(hudloc_x + picloc_x + (picsize_x * (1 - vh_health)), 0, vid_conwidth, vid_conheight); + drawpic(hudloc + picloc, hud_hp_bar, picsize, '1 1 1', 1 , DRAWFLAG_NORMAL); + drawresetcliparea(); + // .. and icon + picsize = draw_getimagesize(hud_hp_ico) * autocvar_cl_vehicles_hudscale; + picloc = '37 65 0' * autocvar_cl_vehicles_hudscale; + if(vh_health < 0.25) + { + if(alarm1time < time) + { + alarm1time = time + 2; + vehicle_alarm(self, CH_PAIN_SINGLE, "vehicles/alarm.wav"); + } + + drawpic(hudloc + picloc, hud_hp_ico, picsize, '1 0 0' + '0 1 1' * sin(time * 8), 1, DRAWFLAG_NORMAL); + } + else + { + drawpic(hudloc + picloc, hud_hp_ico, picsize, '1 1 1', 1, DRAWFLAG_NORMAL); + if(alarm1time) + { + vehicle_alarm(self, CH_PAIN_SINGLE, "misc/null.wav"); + alarm1time = 0; + } + } + + + // Shield bar + picsize = draw_getimagesize(hud_sh_bar) * autocvar_cl_vehicles_hudscale; + picloc = '69 140 0' * autocvar_cl_vehicles_hudscale; + drawsetcliparea(hudloc_x + picloc_x + (picsize_x * (1 - shield)), 0, vid_conwidth, vid_conheight); + drawpic(hudloc + picloc, hud_sh_bar, picsize, '1 1 1', 1, DRAWFLAG_NORMAL); + drawresetcliparea(); + // .. and icon + picloc = '40 136 0' * autocvar_cl_vehicles_hudscale; + picsize = draw_getimagesize(hud_sh_ico) * autocvar_cl_vehicles_hudscale; + if(shield < 0.25) + { + if(alarm2time < time) + { + alarm2time = time + 1; + vehicle_alarm(self, CH_TRIGGER_SINGLE, "vehicles/alarm_shield.wav"); + } + drawpic(hudloc + picloc, hud_sh_ico, picsize, '1 0 0' + '0 1 1' * sin(time * 8), 1, DRAWFLAG_NORMAL); + } + else + { + drawpic(hudloc + picloc, hud_sh_ico, picsize, '1 1 1', 1, DRAWFLAG_NORMAL); + if(alarm2time) + { + vehicle_alarm(self, CH_TRIGGER_SINGLE, "misc/null.wav"); + alarm2time = 0; + } + } + + // Gun bar + picsize = draw_getimagesize(hud_ammo1_bar) * autocvar_cl_vehicles_hudscale; + picloc = '450 69 0' * autocvar_cl_vehicles_hudscale; + drawsetcliparea(hudloc_x + picloc_x, picloc_y, picsize_x * energy, vid_conheight); + drawpic(hudloc + picloc, hud_ammo1_bar, picsize, '1 1 1', 1, DRAWFLAG_NORMAL); + drawresetcliparea(); + // .. and icon + picsize = draw_getimagesize(hud_ammo1_ico) * autocvar_cl_vehicles_hudscale; + picloc = '664 60 0' * autocvar_cl_vehicles_hudscale; + if(energy < 0.2) + drawpic(hudloc + picloc, hud_ammo1_ico, picsize, '1 0 0' + '0 1 1' * sin(time * 8), 1, DRAWFLAG_NORMAL); + else + drawpic(hudloc + picloc, hud_ammo1_ico, picsize, '1 1 1', 1, DRAWFLAG_NORMAL); + + // Bomb bar + picsize = draw_getimagesize(hud_ammo2_bar) * autocvar_cl_vehicles_hudscale; + picloc = '450 140 0' * autocvar_cl_vehicles_hudscale; + drawsetcliparea(hudloc_x + picloc_x, hudloc_y + picloc_y, picsize_x * reload1, vid_conheight); + drawpic(hudloc + picloc, hud_ammo2_bar, picsize, '1 1 1', 1, DRAWFLAG_NORMAL); + drawresetcliparea(); + // .. and icon + pic2size = draw_getimagesize(hud_ammo2_ico) * autocvar_cl_vehicles_hudscale; + picloc = '664 130 0' * autocvar_cl_vehicles_hudscale; + if(reload1 != 1) + drawpic(hudloc + picloc, hud_ammo2_ico, pic2size, '1 0 0' + '0 1 1' * sin(time * 8), 1, DRAWFLAG_NORMAL); + else + drawpic(hudloc + picloc, hud_ammo2_ico, pic2size, '1 1 1', 1, DRAWFLAG_NORMAL); + + if (scoreboard_showscores) + HUD_DrawScoreboard(); + else + { + picsize = draw_getimagesize(waki_xhair); + picsize_x *= 0.5; + picsize_y *= 0.5; + + + drawpic('0.5 0 0' * (vid_conwidth - picsize_x) + '0 0.5 0' * (vid_conheight - picsize_y), waki_xhair, picsize, '1 1 1', 1, DRAWFLAG_NORMAL); + } - return TRUE; ++ return true; + } + case VR_SETUP: + { + AuxiliaryXhair[0].axh_image = "gfx/vehicles/axh-bracket.tga"; + AuxiliaryXhair[0].axh_scale = 0.25; - return TRUE; ++ return true; + } + case VR_PRECACHE: + { - return TRUE; ++ return true; + } + } + - return TRUE; ++ return true; +} + +#endif // CSQC +#endif // REGISTER_VEHICLE diff --cc qcsrc/common/vehicles/unit/raptor.qc index ed5c00aa0,000000000..9333dc62c mode 100644,000000..100644 --- a/qcsrc/common/vehicles/unit/raptor.qc +++ b/qcsrc/common/vehicles/unit/raptor.qc @@@ -1,1222 -1,0 +1,1224 @@@ +#ifdef REGISTER_VEHICLE +REGISTER_VEHICLE( +/* VEH_##id */ RAPTOR, +/* function */ v_raptor, +/* spawnflags */ VHF_DMGSHAKE | VHF_DMGROLL, +/* mins,maxs */ '-80 -80 0', '80 80 70', +/* model */ "models/vehicles/raptor.dpm", +/* head_model */ "", +/* hud_model */ "models/vehicles/raptor_cockpit.dpm", +/* tags */ "", "tag_hud", "tag_camera", +/* netname */ "raptor", +/* fullname */ _("Raptor") +); +#else + +const float RSM_FIRST = 1; +const float RSM_BOMB = 1; +const float RSM_FLARE = 2; +const float RSM_LAST = 2; + +#ifdef SVQC ++#include "../../effects.qh" ++ +float autocvar_g_vehicle_raptor; + +float autocvar_g_vehicle_raptor_respawntime; +float autocvar_g_vehicle_raptor_takeofftime; + +float autocvar_g_vehicle_raptor_movestyle; +float autocvar_g_vehicle_raptor_turnspeed; +float autocvar_g_vehicle_raptor_pitchspeed; +float autocvar_g_vehicle_raptor_pitchlimit; + +float autocvar_g_vehicle_raptor_speed_forward; +float autocvar_g_vehicle_raptor_speed_strafe; +float autocvar_g_vehicle_raptor_speed_up; +float autocvar_g_vehicle_raptor_speed_down; +float autocvar_g_vehicle_raptor_friction; + +float autocvar_g_vehicle_raptor_bomblets; +float autocvar_g_vehicle_raptor_bomblet_alt; +float autocvar_g_vehicle_raptor_bomblet_time; +float autocvar_g_vehicle_raptor_bomblet_damage; +float autocvar_g_vehicle_raptor_bomblet_spread; +float autocvar_g_vehicle_raptor_bomblet_edgedamage; +float autocvar_g_vehicle_raptor_bomblet_radius; +float autocvar_g_vehicle_raptor_bomblet_force; +float autocvar_g_vehicle_raptor_bomblet_explode_delay; +float autocvar_g_vehicle_raptor_bombs_refire; + +float autocvar_g_vehicle_raptor_flare_refire; +float autocvar_g_vehicle_raptor_flare_lifetime; +float autocvar_g_vehicle_raptor_flare_chase; +float autocvar_g_vehicle_raptor_flare_range; + +float autocvar_g_vehicle_raptor_cannon_turnspeed; +float autocvar_g_vehicle_raptor_cannon_turnlimit; +float autocvar_g_vehicle_raptor_cannon_pitchlimit_up; +float autocvar_g_vehicle_raptor_cannon_pitchlimit_down; + +float autocvar_g_vehicle_raptor_cannon_locktarget; +float autocvar_g_vehicle_raptor_cannon_locking_time; +float autocvar_g_vehicle_raptor_cannon_locking_releasetime; +float autocvar_g_vehicle_raptor_cannon_locked_time; +float autocvar_g_vehicle_raptor_cannon_predicttarget; + +float autocvar_g_vehicle_raptor_cannon_cost; +float autocvar_g_vehicle_raptor_cannon_damage; +float autocvar_g_vehicle_raptor_cannon_radius; +float autocvar_g_vehicle_raptor_cannon_refire; +float autocvar_g_vehicle_raptor_cannon_speed; +float autocvar_g_vehicle_raptor_cannon_spread; +float autocvar_g_vehicle_raptor_cannon_force; + +float autocvar_g_vehicle_raptor_energy; +float autocvar_g_vehicle_raptor_energy_regen; +float autocvar_g_vehicle_raptor_energy_regen_pause; + +float autocvar_g_vehicle_raptor_health; +float autocvar_g_vehicle_raptor_health_regen; +float autocvar_g_vehicle_raptor_health_regen_pause; + +float autocvar_g_vehicle_raptor_shield; +float autocvar_g_vehicle_raptor_shield_regen; +float autocvar_g_vehicle_raptor_shield_regen_pause; + +float autocvar_g_vehicle_raptor_bouncefactor; +float autocvar_g_vehicle_raptor_bouncestop; +vector autocvar_g_vehicle_raptor_bouncepain; + +.entity bomb1; +.entity bomb2; + +float raptor_altitude(float amax) +{ + tracebox(self.origin, self.mins, self.maxs, self.origin - ('0 0 1' * amax), MOVE_WORLDONLY, self); + return vlen(self.origin - trace_endpos); +} + +void raptor_bomblet_boom() +{ + RadiusDamage (self, self.realowner, autocvar_g_vehicle_raptor_bomblet_damage, + autocvar_g_vehicle_raptor_bomblet_edgedamage, + autocvar_g_vehicle_raptor_bomblet_radius, world, world, + autocvar_g_vehicle_raptor_bomblet_force, DEATH_VH_RAPT_BOMB, world); + remove(self); +} + +void raptor_bomblet_touch() +{ + if(other == self.owner) + return; + + PROJECTILE_TOUCH; + self.think = raptor_bomblet_boom; + self.nextthink = time + random() * autocvar_g_vehicle_raptor_bomblet_explode_delay; +} + +void raptor_bomb_burst() +{ + if(self.cnt > time) + if(autocvar_g_vehicle_raptor_bomblet_alt) + { + self.nextthink = time; + traceline(self.origin, self.origin + (normalize(self.velocity) * autocvar_g_vehicle_raptor_bomblet_alt), MOVE_NORMAL, self); + if((trace_fraction == 1.0) || (vlen(self.origin - self.owner.origin) < autocvar_g_vehicle_raptor_bomblet_radius)) + { + UpdateCSQCProjectile(self); + return; + } + } + + entity bomblet; + float i; + + Damage_DamageInfo(self.origin, 0, 0, 0, '0 0 0', DEATH_VH_RAPT_FRAGMENT, 0, self); + + for(i = 0; i < autocvar_g_vehicle_raptor_bomblets; ++i) + { + bomblet = spawn(); + setorigin(bomblet, self.origin); + + bomblet.movetype = MOVETYPE_TOSS; + bomblet.touch = raptor_bomblet_touch; + bomblet.think = raptor_bomblet_boom; + bomblet.nextthink = time + 5; + bomblet.owner = self.owner; + bomblet.realowner = self.realowner; + bomblet.velocity = normalize(normalize(self.velocity) + (randomvec() * autocvar_g_vehicle_raptor_bomblet_spread)) * vlen(self.velocity); + + PROJECTILE_MAKETRIGGER(bomblet); - CSQCProjectile(bomblet, TRUE, PROJECTILE_RAPTORBOMBLET, TRUE); ++ CSQCProjectile(bomblet, true, PROJECTILE_RAPTORBOMBLET, true); + } + + remove(self); +} + +void raptor_bombdrop() +{ + entity bomb_1, bomb_2; + + bomb_1 = spawn(); + bomb_2 = spawn(); + + setorigin(bomb_1, gettaginfo(self, gettagindex(self, "bombmount_left"))); + setorigin(bomb_2, gettaginfo(self, gettagindex(self, "bombmount_right"))); + + bomb_1.movetype = bomb_2.movetype = MOVETYPE_BOUNCE; + bomb_1.velocity = bomb_2.velocity = self.velocity; + bomb_1.touch = bomb_2.touch = raptor_bomb_burst; + bomb_1.think = bomb_2.think = raptor_bomb_burst; + bomb_1.cnt = bomb_2.cnt = time + 10; + + if(autocvar_g_vehicle_raptor_bomblet_alt) + bomb_1.nextthink = bomb_2.nextthink = time; + else + bomb_1.nextthink = bomb_2.nextthink = time + autocvar_g_vehicle_raptor_bomblet_time; + + bomb_1.owner = bomb_2.owner = self; + bomb_1.realowner = bomb_2.realowner = self.owner; + bomb_1.solid = bomb_2.solid = SOLID_BBOX; + bomb_1.gravity = bomb_2.gravity = 1; + + PROJECTILE_MAKETRIGGER(bomb_1); + PROJECTILE_MAKETRIGGER(bomb_2); + - CSQCProjectile(bomb_1, TRUE, PROJECTILE_RAPTORBOMB, TRUE); - CSQCProjectile(bomb_2, TRUE, PROJECTILE_RAPTORBOMB, TRUE); ++ CSQCProjectile(bomb_1, true, PROJECTILE_RAPTORBOMB, true); ++ CSQCProjectile(bomb_2, true, PROJECTILE_RAPTORBOMB, true); +} + + +void raptor_fire_cannon(entity gun, string tagname) +{ + vehicles_projectile("raptor_cannon_muzzleflash", W_Sound("lasergun_fire"), + gettaginfo(gun, gettagindex(gun, tagname)), normalize(v_forward + randomvec() * autocvar_g_vehicle_raptor_cannon_spread) * autocvar_g_vehicle_raptor_cannon_speed, + autocvar_g_vehicle_raptor_cannon_damage, autocvar_g_vehicle_raptor_cannon_radius, autocvar_g_vehicle_raptor_cannon_force, 0, - DEATH_VH_RAPT_CANNON, PROJECTILE_RAPTORCANNON, 0, TRUE, TRUE, self.owner); ++ DEATH_VH_RAPT_CANNON, PROJECTILE_RAPTORCANNON, 0, true, true, self.owner); +} + +void raptor_land() +{ + float hgt; + + hgt = raptor_altitude(512); + self.velocity = (self.velocity * 0.9) + ('0 0 -1800' * (hgt / 256) * sys_frametime); + self.angles_x *= 0.95; + self.angles_z *= 0.95; + + if(hgt < 128) + if(hgt > 0) + self.frame = (hgt / 128) * 25; + + self.bomb1.gun1.avelocity_y = 90 + ((self.frame / 25) * 2000); + self.bomb1.gun2.avelocity_y = -self.bomb1.gun1.avelocity_y; + + if(hgt < 16) + { + self.movetype = MOVETYPE_TOSS; + self.think = vehicles_think; + self.frame = 0; + } + + self.nextthink = time; + + CSQCMODEL_AUTOUPDATE(); +} + +void raptor_exit(float eject) +{ + vector spot; + self.tur_head.exteriormodeltoclient = world; + + if(self.deadflag == DEAD_NO) + { + self.think = raptor_land; + self.nextthink = time; + } + + if(!self.owner) + return; + + makevectors(self.angles); + if(eject) + { + spot = self.origin + v_forward * 100 + '0 0 64'; + spot = vehicles_findgoodexit(spot); + setorigin(self.owner , spot); + self.owner.velocity = (v_up + v_forward * 0.25) * 750; + self.owner.oldvelocity = self.owner.velocity; + } + else + { + if(vlen(self.velocity) > 2 * autocvar_sv_maxairspeed) + { + self.owner.velocity = normalize(self.velocity) * autocvar_sv_maxairspeed * 2; + self.owner.velocity_z += 200; + spot = self.origin + v_forward * 32 + '0 0 64'; + spot = vehicles_findgoodexit(spot); + } + else + { + self.owner.velocity = self.velocity * 0.5; + self.owner.velocity_z += 10; + spot = self.origin - v_forward * 200 + '0 0 64'; + spot = vehicles_findgoodexit(spot); + } + self.owner.oldvelocity = self.owner.velocity; + setorigin(self.owner , spot); + } + + antilag_clear(self.owner); + self.owner = world; +} + +void raptor_flare_touch() +{ + remove(self); +} + +void raptor_flare_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force) +{ + self.health -= damage; + if(self.health <= 0) + remove(self); +} + +void raptor_flare_think() +{ + self.nextthink = time + 0.1; + entity _missile = findchainentity(enemy, self.owner); + while(_missile) + { + if(_missile.flags & FL_PROJECTILE) + if(vlen(self.origin - _missile.origin) < autocvar_g_vehicle_raptor_flare_range) + if(random() > autocvar_g_vehicle_raptor_flare_chase) + _missile.enemy = self; + _missile = _missile.chain; + } + + if(self.tur_impacttime < time) + remove(self); +} + +float raptor_frame() +{ + entity player, raptor; + float ftmp = 0; + vector df; + + if(intermission_running) + { + self.vehicle.velocity = '0 0 0'; + self.vehicle.avelocity = '0 0 0'; + return 1; + } + + player = self; + raptor = self.vehicle; + self = raptor; + + vehicles_painframe(); + /* + ftmp = vlen(self.velocity); + if(ftmp > autocvar_g_vehicle_raptor_speed_forward) + ftmp = 1; + else + ftmp = ftmp / autocvar_g_vehicle_raptor_speed_forward; + */ + + if(self.sound_nexttime < time) + { + self.sound_nexttime = time + 7.955812; + //sound (self.tur_head, CH_TRIGGER_SINGLE, "vehicles/raptor_fly.wav", 1 - ftmp, ATTEN_NORM ); + sound (self, CH_TRIGGER_SINGLE, "vehicles/raptor_speed.wav", 1, ATTEN_NORM); + self.wait = ftmp; + } + /* + else if(fabs(ftmp - self.wait) > 0.2) + { + sound (self.tur_head, CH_TRIGGER_SINGLE, "", 1 - ftmp, ATTEN_NORM ); + sound (self, CH_TRIGGER_SINGLE, "", ftmp, ATTEN_NORM); + self.wait = ftmp; + } + */ + + if(raptor.deadflag != DEAD_NO) + { + self = player; + player.BUTTON_ATCK = player.BUTTON_ATCK2 = 0; + return 1; + } + crosshair_trace(player); + + vector vang; + vang = raptor.angles; + df = vectoangles(normalize(trace_endpos - self.origin + '0 0 32')); + vang_x *= -1; + df_x *= -1; + if(df_x > 180) df_x -= 360; + if(df_x < -180) df_x += 360; + if(df_y > 180) df_y -= 360; + if(df_y < -180) df_y += 360; + + ftmp = shortangle_f(player.v_angle_y - vang_y, vang_y); + if(ftmp > 180) ftmp -= 360; if(ftmp < -180) ftmp += 360; + raptor.avelocity_y = bound(-autocvar_g_vehicle_raptor_turnspeed, ftmp + raptor.avelocity_y * 0.9, autocvar_g_vehicle_raptor_turnspeed); + + // Pitch + ftmp = 0; + if(player.movement_x > 0 && vang_x < autocvar_g_vehicle_raptor_pitchlimit) ftmp = 5; + else if(player.movement_x < 0 && vang_x > -autocvar_g_vehicle_raptor_pitchlimit) ftmp = -20; + + df_x = bound(-autocvar_g_vehicle_raptor_pitchlimit, df_x , autocvar_g_vehicle_raptor_pitchlimit); + ftmp = vang_x - bound(-autocvar_g_vehicle_raptor_pitchlimit, df_x + ftmp, autocvar_g_vehicle_raptor_pitchlimit); + raptor.avelocity_x = bound(-autocvar_g_vehicle_raptor_pitchspeed, ftmp + raptor.avelocity_x * 0.9, autocvar_g_vehicle_raptor_pitchspeed); + + raptor.angles_x = anglemods(raptor.angles_x); + raptor.angles_y = anglemods(raptor.angles_y); + raptor.angles_z = anglemods(raptor.angles_z); + + if(autocvar_g_vehicle_raptor_movestyle == 1) + makevectors('0 1 0' * raptor.angles_y); + else + makevectors(player.v_angle); + + df = raptor.velocity * -autocvar_g_vehicle_raptor_friction; + + if(player.movement_x != 0) + { + if(player.movement_x > 0) + df += v_forward * autocvar_g_vehicle_raptor_speed_forward; + else if(player.movement_x < 0) + df -= v_forward * autocvar_g_vehicle_raptor_speed_forward; + } + + if(player.movement_y != 0) + { + if(player.movement_y < 0) + df -= v_right * autocvar_g_vehicle_raptor_speed_strafe; + else if(player.movement_y > 0) + df += v_right * autocvar_g_vehicle_raptor_speed_strafe; + + raptor.angles_z = bound(-30,raptor.angles_z + (player.movement_y / autocvar_g_vehicle_raptor_speed_strafe),30); + } + else + { + raptor.angles_z *= 0.95; + if(raptor.angles_z >= -1 && raptor.angles_z <= -1) + raptor.angles_z = 0; + } + + if(player.BUTTON_CROUCH) + df -= v_up * autocvar_g_vehicle_raptor_speed_down; + else if (player.BUTTON_JUMP) + df += v_up * autocvar_g_vehicle_raptor_speed_up; + + raptor.velocity += df * frametime; + player.velocity = player.movement = raptor.velocity; + setorigin(player, raptor.origin + '0 0 32'); + + player.vehicle_weapon2mode = raptor.vehicle_weapon2mode; + + vector vf, ad; + // Target lock & predict + if(autocvar_g_vehicle_raptor_cannon_locktarget == 2) + { + if(raptor.gun1.lock_time < time || raptor.gun1.enemy.deadflag) + raptor.gun1.enemy = world; + + if(trace_ent) + if(trace_ent.movetype) + if(trace_ent.takedamage) + if(!trace_ent.deadflag) + { + if(teamplay) + { + if(trace_ent.team != player.team) + { + raptor.gun1.enemy = trace_ent; + raptor.gun1.lock_time = time + 5; + } + } + else + { + raptor.gun1.enemy = trace_ent; + raptor.gun1.lock_time = time + 0.5; + } + } + + if(raptor.gun1.enemy) + { + float distance, impact_time; + + vf = real_origin(raptor.gun1.enemy); + UpdateAuxiliaryXhair(player, vf, '1 0 0', 1); + vector _vel = raptor.gun1.enemy.velocity; + if(raptor.gun1.enemy.movetype == MOVETYPE_WALK) + _vel_z *= 0.1; + + if(autocvar_g_vehicle_raptor_cannon_predicttarget) + { + ad = vf; + distance = vlen(ad - player.origin); + impact_time = distance / autocvar_g_vehicle_raptor_cannon_speed; + ad = vf + _vel * impact_time; + trace_endpos = ad; + } + else + trace_endpos = vf; + } + } + else if(autocvar_g_vehicle_raptor_cannon_locktarget == 1) + { + + vehicles_locktarget((1 / autocvar_g_vehicle_raptor_cannon_locking_time) * frametime, + (1 / autocvar_g_vehicle_raptor_cannon_locking_releasetime) * frametime, + autocvar_g_vehicle_raptor_cannon_locked_time); + + if(self.lock_target != world) + if(autocvar_g_vehicle_raptor_cannon_predicttarget) + if(self.lock_strength == 1) + { + float i, distance, impact_time; + + vf = real_origin(raptor.lock_target); + ad = vf; + for(i = 0; i < 4; ++i) + { + distance = vlen(ad - raptor.origin); + impact_time = distance / autocvar_g_vehicle_raptor_cannon_speed; + ad = vf + raptor.lock_target.velocity * impact_time; + } + trace_endpos = ad; + } + + if(self.lock_target) + { + if(raptor.lock_strength == 1) + UpdateAuxiliaryXhair(player, real_origin(raptor.lock_target), '1 0 0', 1); + else if(self.lock_strength > 0.5) + UpdateAuxiliaryXhair(player, real_origin(raptor.lock_target), '0 1 0', 1); + else if(self.lock_strength < 0.5) + UpdateAuxiliaryXhair(player, real_origin(raptor.lock_target), '0 0 1', 1); + } + } + + + vehicle_aimturret(raptor, trace_endpos, raptor.gun1, "fire1", + autocvar_g_vehicle_raptor_cannon_pitchlimit_down * -1, autocvar_g_vehicle_raptor_cannon_pitchlimit_up, + autocvar_g_vehicle_raptor_cannon_turnlimit * -1, autocvar_g_vehicle_raptor_cannon_turnlimit, autocvar_g_vehicle_raptor_cannon_turnspeed); + + vehicle_aimturret(raptor, trace_endpos, raptor.gun2, "fire1", + autocvar_g_vehicle_raptor_cannon_pitchlimit_down * -1, autocvar_g_vehicle_raptor_cannon_pitchlimit_up, + autocvar_g_vehicle_raptor_cannon_turnlimit * -1, autocvar_g_vehicle_raptor_cannon_turnlimit, autocvar_g_vehicle_raptor_cannon_turnspeed); + + /* + ad = ad * 0.5; + v_forward = vf * 0.5; + traceline(ad, ad + v_forward * MAX_SHOT_DISTANCE, MOVE_NORMAL, raptor); + UpdateAuxiliaryXhair(player, trace_endpos, '0 1 0', 0); + */ + + if(!forbidWeaponUse(player)) + if(player.BUTTON_ATCK) + if(raptor.attack_finished_single <= time) + if(raptor.vehicle_energy > autocvar_g_vehicle_raptor_cannon_cost) + { + raptor.misc_bulletcounter += 1; + raptor.attack_finished_single = time + autocvar_g_vehicle_raptor_cannon_refire; + if(raptor.misc_bulletcounter <= 2) + raptor_fire_cannon(self.gun1, "fire1"); + else if(raptor.misc_bulletcounter == 3) + raptor_fire_cannon(self.gun2, "fire1"); + else + { + raptor.attack_finished_single = time + autocvar_g_vehicle_raptor_cannon_refire * 2; + raptor_fire_cannon(self.gun2, "fire1"); + raptor.misc_bulletcounter = 0; + } + raptor.vehicle_energy -= autocvar_g_vehicle_raptor_cannon_cost; + self.cnt = time; + } + + if(self.vehicle_flags & VHF_SHIELDREGEN) - vehicles_regen(raptor.dmg_time, vehicle_shield, autocvar_g_vehicle_raptor_shield, autocvar_g_vehicle_raptor_shield_regen_pause, autocvar_g_vehicle_raptor_shield_regen, frametime, TRUE); ++ vehicles_regen(raptor.dmg_time, vehicle_shield, autocvar_g_vehicle_raptor_shield, autocvar_g_vehicle_raptor_shield_regen_pause, autocvar_g_vehicle_raptor_shield_regen, frametime, true); + + if(self.vehicle_flags & VHF_HEALTHREGEN) - vehicles_regen(raptor.dmg_time, vehicle_health, autocvar_g_vehicle_raptor_health, autocvar_g_vehicle_raptor_health_regen_pause, autocvar_g_vehicle_raptor_health_regen, frametime, FALSE); ++ vehicles_regen(raptor.dmg_time, vehicle_health, autocvar_g_vehicle_raptor_health, autocvar_g_vehicle_raptor_health_regen_pause, autocvar_g_vehicle_raptor_health_regen, frametime, false); + + if(self.vehicle_flags & VHF_ENERGYREGEN) - vehicles_regen(raptor.cnt, vehicle_energy, autocvar_g_vehicle_raptor_energy, autocvar_g_vehicle_raptor_energy_regen_pause, autocvar_g_vehicle_raptor_energy_regen, frametime, FALSE); ++ vehicles_regen(raptor.cnt, vehicle_energy, autocvar_g_vehicle_raptor_energy, autocvar_g_vehicle_raptor_energy_regen_pause, autocvar_g_vehicle_raptor_energy_regen, frametime, false); + + if(!forbidWeaponUse(player)) + if(raptor.vehicle_weapon2mode == RSM_BOMB) + { + if(time > raptor.lip + autocvar_g_vehicle_raptor_bombs_refire) + if(player.BUTTON_ATCK2) + { + raptor_bombdrop(); + raptor.delay = time + autocvar_g_vehicle_raptor_bombs_refire; + raptor.lip = time; + } + } + else + { + if(time > raptor.lip + autocvar_g_vehicle_raptor_flare_refire) + if(player.BUTTON_ATCK2) + { + float i; + entity _flare; + + for(i = 0; i < 3; ++i) + { + _flare = spawn(); + setmodel(_flare, "models/runematch/rune.mdl"); + _flare.effects = EF_LOWPRECISION | EF_FLAME; + _flare.scale = 0.5; + setorigin(_flare, self.origin - '0 0 16'); + _flare.movetype = MOVETYPE_TOSS; + _flare.gravity = 0.15; + _flare.velocity = 0.25 * raptor.velocity + (v_forward + randomvec() * 0.25)* -500; + _flare.think = raptor_flare_think; + _flare.nextthink = time; + _flare.owner = raptor; + _flare.solid = SOLID_CORPSE; + _flare.takedamage = DAMAGE_YES; + _flare.event_damage = raptor_flare_damage; + _flare.health = 20; + _flare.tur_impacttime = time + autocvar_g_vehicle_raptor_flare_lifetime; + _flare.touch = raptor_flare_touch; + } + raptor.delay = time + autocvar_g_vehicle_raptor_flare_refire; + raptor.lip = time; + } + } + + raptor.bomb1.alpha = raptor.bomb2.alpha = (time - raptor.lip) / (raptor.delay - raptor.lip); + player.vehicle_reload2 = bound(0, raptor.bomb1.alpha * 100, 100); + + if(self.bomb1.cnt < time) + { + entity _missile = findchainentity(enemy, raptor); + float _incomming = 0; + while(_missile) + { + if(_missile.flags & FL_PROJECTILE) + if(MISSILE_IS_TRACKING(_missile)) + if(vlen(self.origin - _missile.origin) < 2 * autocvar_g_vehicle_raptor_flare_range) + ++_incomming; + + _missile = _missile.chain; + } + + if(_incomming) + sound(self, CH_PAIN_SINGLE, "vehicles/missile_alarm.wav", VOL_BASE, ATTEN_NONE); + + self.bomb1.cnt = time + 1; + } + + + VEHICLE_UPDATE_PLAYER(player, health, raptor); + VEHICLE_UPDATE_PLAYER(player, energy, raptor); + if(self.vehicle_flags & VHF_HASSHIELD) + VEHICLE_UPDATE_PLAYER(player, shield, raptor); + + player.BUTTON_ATCK = player.BUTTON_ATCK2 = player.BUTTON_CROUCH = 0; + + self = player; + return 1; +} + +float raptor_takeoff() +{ + entity player, raptor; + + player = self; + raptor = self.vehicle; + self = raptor; + + self.nextthink = time; + CSQCMODEL_AUTOUPDATE(); + self.nextthink = 0; // will this work? + + if(self.sound_nexttime < time) + { + self.sound_nexttime = time + 7.955812; //soundlength("vehicles/raptor_fly.wav"); + sound (self, CH_TRIGGER_SINGLE, "vehicles/raptor_speed.wav", VOL_VEHICLEENGINE, ATTEN_NORM); + } + + // Takeoff sequense + if(raptor.frame < 25) + { + raptor.frame += 25 / (autocvar_g_vehicle_raptor_takeofftime / sys_frametime); + raptor.velocity_z = min(raptor.velocity_z * 1.5, 256); + self.bomb1.gun1.avelocity_y = 90 + ((raptor.frame / 25) * 25000); + self.bomb1.gun2.avelocity_y = -self.bomb1.gun1.avelocity_y; + player.BUTTON_ATCK = player.BUTTON_ATCK2 = player.BUTTON_CROUCH = 0; + + setorigin(player, raptor.origin + '0 0 32'); + } + else + player.PlayerPhysplug = raptor_frame; + + if(self.vehicle_flags & VHF_SHIELDREGEN) - vehicles_regen(raptor.dmg_time, vehicle_shield, autocvar_g_vehicle_raptor_shield, autocvar_g_vehicle_raptor_shield_regen_pause, autocvar_g_vehicle_raptor_shield_regen, frametime, TRUE); ++ vehicles_regen(raptor.dmg_time, vehicle_shield, autocvar_g_vehicle_raptor_shield, autocvar_g_vehicle_raptor_shield_regen_pause, autocvar_g_vehicle_raptor_shield_regen, frametime, true); + + if(self.vehicle_flags & VHF_HEALTHREGEN) - vehicles_regen(raptor.dmg_time, vehicle_health, autocvar_g_vehicle_raptor_health, autocvar_g_vehicle_raptor_health_regen_pause, autocvar_g_vehicle_raptor_health_regen, frametime, FALSE); ++ vehicles_regen(raptor.dmg_time, vehicle_health, autocvar_g_vehicle_raptor_health, autocvar_g_vehicle_raptor_health_regen_pause, autocvar_g_vehicle_raptor_health_regen, frametime, false); + + if(self.vehicle_flags & VHF_ENERGYREGEN) - vehicles_regen(raptor.cnt, vehicle_energy, autocvar_g_vehicle_raptor_energy, autocvar_g_vehicle_raptor_energy_regen_pause, autocvar_g_vehicle_raptor_energy_regen, frametime, FALSE); ++ vehicles_regen(raptor.cnt, vehicle_energy, autocvar_g_vehicle_raptor_energy, autocvar_g_vehicle_raptor_energy_regen_pause, autocvar_g_vehicle_raptor_energy_regen, frametime, false); + + + raptor.bomb1.alpha = raptor.bomb2.alpha = (time - raptor.lip) / (raptor.delay - raptor.lip); + player.vehicle_reload2 = bound(0, raptor.bomb1.alpha * 100, 100); + + VEHICLE_UPDATE_PLAYER(player, health, raptor); + VEHICLE_UPDATE_PLAYER(player, energy, raptor); + if(self.vehicle_flags & VHF_HASSHIELD) + VEHICLE_UPDATE_PLAYER(player, shield, raptor); + + player.BUTTON_ATCK = player.BUTTON_ATCK2 = player.BUTTON_CROUCH = 0; + self = player; + return 1; +} + +void raptor_blowup() +{ + self.deadflag = DEAD_DEAD; + self.vehicle_exit(VHEF_NORMAL); + RadiusDamage (self, self.enemy, 250, 15, 250, world, world, 250, DEATH_VH_RAPT_DEATH, world); + + self.alpha = -1; + self.movetype = MOVETYPE_NONE; + self.effects = EF_NODRAW; + self.colormod = '0 0 0'; + self.avelocity = '0 0 0'; + self.velocity = '0 0 0'; + + setorigin(self, self.pos1); + self.touch = func_null; + self.nextthink = 0; +} + +void raptor_diethink() +{ + if(time >= self.wait) + self.think = raptor_blowup; + + if(random() < 0.05) + { + sound (self, CH_SHOTS, W_Sound("rocket_impact"), VOL_BASE, ATTEN_NORM); + Send_Effect(EFFECT_EXPLOSION_SMALL, randomvec() * 80 + (self.origin + '0 0 100'), '0 0 0', 1); + } + self.nextthink = time; + + CSQCMODEL_AUTOUPDATE(); +} + +// If we dont do this ever now and then, the raptors rotors +// stop working, presumably due to angle overflow. cute. +void raptor_rotor_anglefix() +{ + self.gun1.angles_y = anglemods(self.gun1.angles_y); + self.gun2.angles_y = anglemods(self.gun2.angles_y); + self.nextthink = time + 15; +} + +float raptor_impulse(float _imp) +{ + switch(_imp) + { + case 10: + case 15: + case 18: + self.vehicle.vehicle_weapon2mode += 1; + if(self.vehicle.vehicle_weapon2mode > RSM_LAST) + self.vehicle.vehicle_weapon2mode = RSM_FIRST; + + CSQCVehicleSetup(self, 0); - return TRUE; ++ return true; + case 12: + case 16: + case 19: + self.vehicle.vehicle_weapon2mode -= 1; + if(self.vehicle.vehicle_weapon2mode < RSM_FIRST) + self.vehicle.vehicle_weapon2mode = RSM_LAST; + + CSQCVehicleSetup(self, 0); - return TRUE; ++ return true; + + /* + case 17: // toss gun, could be used to exit? + break; + case 20: // Manual minigun reload? + break; + */ + } - return FALSE; ++ return false; +} + +void spawnfunc_vehicle_raptor() +{ + if(!autocvar_g_vehicle_raptor) { remove(self); return; } - if(!vehicle_initialize(VEH_RAPTOR, FALSE)) { remove(self); return; } ++ if(!vehicle_initialize(VEH_RAPTOR, false)) { remove(self); return; } +} + +float v_raptor(float req) +{ + switch(req) + { + case VR_IMPACT: + { + if(autocvar_g_vehicle_raptor_bouncepain) + vehicles_impact(autocvar_g_vehicle_raptor_bouncepain_x, autocvar_g_vehicle_raptor_bouncepain_y, autocvar_g_vehicle_raptor_bouncepain_z); + - return TRUE; ++ return true; + } + case VR_ENTER: + { + self.vehicle_weapon2mode = RSM_BOMB; + self.owner.PlayerPhysplug = raptor_takeoff; + self.movetype = MOVETYPE_BOUNCEMISSILE; + self.solid = SOLID_SLIDEBOX; + self.owner.vehicle_health = (self.vehicle_health / autocvar_g_vehicle_raptor_health) * 100; + self.owner.vehicle_shield = (self.vehicle_shield / autocvar_g_vehicle_raptor_shield) * 100; + self.velocity_z = 1; // Nudge upwards to takeoff sequense can work. + self.tur_head.exteriormodeltoclient = self.owner; + + self.delay = time + autocvar_g_vehicle_raptor_bombs_refire; + self.lip = time; + + if(self.owner.flagcarried) + setorigin(self.owner.flagcarried, '-20 0 96'); + + CSQCVehicleSetup(self.owner, 0); - return TRUE; ++ return true; + } + case VR_THINK: + { - return TRUE; ++ return true; + } + case VR_DEATH: + { + self.health = 0; + self.event_damage = func_null; + self.solid = SOLID_CORPSE; + self.takedamage = DAMAGE_NO; + self.deadflag = DEAD_DYING; + self.movetype = MOVETYPE_BOUNCE; + self.think = raptor_diethink; + self.nextthink = time; + self.wait = time + 5 + (random() * 5); + + Send_Effect(EFFECT_EXPLOSION_MEDIUM, findbetterlocation (self.origin, 16), '0 0 0', 1); + + self.velocity_z += 600; + + self.avelocity = '0 0.5 1' * (random() * 400); + self.avelocity -= '0 0.5 1' * (random() * 400); + + self.colormod = '-0.5 -0.5 -0.5'; + self.touch = raptor_blowup; - return TRUE; ++ return true; + } + case VR_SPAWN: + { + if(!self.gun1) + { + entity spinner; + vector ofs; + + //FIXME: Camera is in a bad place in HUD model. + //setorigin(self.vehicle_viewport, '25 0 5'); + + self.vehicles_impulse = raptor_impulse; + + self.frame = 0; + + self.bomb1 = spawn(); + self.bomb2 = spawn(); + self.gun1 = spawn(); + self.gun2 = spawn(); + + setmodel(self.bomb1,"models/vehicles/clusterbomb_folded.md3"); + setmodel(self.bomb2,"models/vehicles/clusterbomb_folded.md3"); + setmodel(self.gun1, "models/vehicles/raptor_gun.dpm"); + setmodel(self.gun2, "models/vehicles/raptor_gun.dpm"); + setmodel(self.tur_head, "models/vehicles/raptor_body.dpm"); + + setattachment(self.bomb1, self, "bombmount_left"); + setattachment(self.bomb2, self, "bombmount_right"); + setattachment(self.tur_head, self,"root"); + + // FIXMODEL Guns mounts to angled bones + self.bomb1.angles = self.angles; + self.angles = '0 0 0'; + // This messes up gun-aim, so work arround it. + //setattachment(self.gun1, self, "gunmount_left"); + ofs = gettaginfo(self, gettagindex(self, "gunmount_left")); + ofs -= self.origin; + setattachment(self.gun1, self, ""); + setorigin(self.gun1, ofs); + + //setattachment(self.gun2, self, "gunmount_right"); + ofs = gettaginfo(self, gettagindex(self, "gunmount_right")); + ofs -= self.origin; + setattachment(self.gun2, self, ""); + setorigin(self.gun2, ofs); + + self.angles = self.bomb1.angles; + self.bomb1.angles = '0 0 0'; + + spinner = spawn(); + spinner.owner = self; + setmodel(spinner,"models/vehicles/spinner.dpm"); + setattachment(spinner, self, "engine_left"); + spinner.movetype = MOVETYPE_NOCLIP; + spinner.avelocity = '0 90 0'; + self.bomb1.gun1 = spinner; + + spinner = spawn(); + spinner.owner = self; + setmodel(spinner,"models/vehicles/spinner.dpm"); + setattachment(spinner, self, "engine_right"); + spinner.movetype = MOVETYPE_NOCLIP; + spinner.avelocity = '0 -90 0'; + self.bomb1.gun2 = spinner; + + // Sigh. + self.bomb1.think = raptor_rotor_anglefix; + self.bomb1.nextthink = time; + + self.mass = 1 ; + } + + self.frame = 0; + self.vehicle_health = autocvar_g_vehicle_raptor_health; + self.vehicle_shield = autocvar_g_vehicle_raptor_shield; + self.movetype = MOVETYPE_TOSS; + self.solid = SOLID_SLIDEBOX; + self.vehicle_energy = 1; + + self.PlayerPhysplug = raptor_frame; + + self.bomb1.gun1.avelocity_y = 90; + self.bomb1.gun2.avelocity_y = -90; + + self.delay = time; + + self.bouncefactor = autocvar_g_vehicle_raptor_bouncefactor; + self.bouncestop = autocvar_g_vehicle_raptor_bouncestop; + self.damageforcescale = 0.25; + self.vehicle_health = autocvar_g_vehicle_raptor_health; + self.vehicle_shield = autocvar_g_vehicle_raptor_shield; - return TRUE; ++ return true; + } + case VR_SETUP: + { + if(autocvar_g_vehicle_raptor_shield) + self.vehicle_flags |= VHF_HASSHIELD; + + if(autocvar_g_vehicle_raptor_shield_regen) + self.vehicle_flags |= VHF_SHIELDREGEN; + + if(autocvar_g_vehicle_raptor_health_regen) + self.vehicle_flags |= VHF_HEALTHREGEN; + + if(autocvar_g_vehicle_raptor_energy_regen) + self.vehicle_flags |= VHF_ENERGYREGEN; + + self.vehicle_exit = raptor_exit; + self.respawntime = autocvar_g_vehicle_raptor_respawntime; + self.vehicle_health = autocvar_g_vehicle_raptor_health; + self.vehicle_shield = autocvar_g_vehicle_raptor_shield; + self.max_health = self.vehicle_health; + - return TRUE; ++ return true; + } + case VR_PRECACHE: + { + precache_model ("models/vehicles/raptor.dpm"); + precache_model ("models/vehicles/raptor_gun.dpm"); + precache_model ("models/vehicles/spinner.dpm"); + precache_model ("models/vehicles/raptor_cockpit.dpm"); + precache_model ("models/vehicles/clusterbomb_folded.md3"); + precache_model ("models/vehicles/raptor_body.dpm"); + + precache_sound ("vehicles/raptor_fly.wav"); + precache_sound ("vehicles/raptor_speed.wav"); + precache_sound ("vehicles/missile_alarm.wav"); + - return TRUE; ++ return true; + } + } + - return TRUE; ++ return true; +} + +#endif // SVQC +#ifdef CSQC +#define raptor_ico "gfx/vehicles/raptor.tga" +#define raptor_gun "gfx/vehicles/raptor_guns.tga" +#define raptor_bomb "gfx/vehicles/raptor_bombs.tga" +#define raptor_drop "gfx/vehicles/axh-dropcross.tga" + +void RaptorCBShellfragDraw() +{ + if(wasfreed(self)) + return; + + Movetype_Physics_MatchTicrate(autocvar_cl_gibs_ticrate, autocvar_cl_gibs_sloppy); + self.move_avelocity += randomvec() * 15; + self.renderflags = 0; + + if(self.cnt < time) + self.alpha = bound(0, self.nextthink - time, 1); + + if(self.alpha < ALPHA_MIN_VISIBLE) + remove(self); +} + +void RaptorCBShellfragToss(vector _org, vector _vel, vector _ang) +{ + entity sfrag; + + sfrag = spawn(); + setmodel(sfrag, "models/vehicles/clusterbomb_fragment.md3"); + setorigin(sfrag, _org); + + sfrag.move_movetype = MOVETYPE_BOUNCE; + sfrag.gravity = 0.15; + sfrag.solid = SOLID_CORPSE; + + sfrag.draw = RaptorCBShellfragDraw; + + sfrag.move_origin = sfrag.origin = _org; + sfrag.move_velocity = _vel; + sfrag.move_avelocity = prandomvec() * vlen(sfrag.move_velocity); + sfrag.angles = self.move_angles = _ang; + + sfrag.move_time = time; + sfrag.damageforcescale = 4; + + sfrag.nextthink = time + 3; + sfrag.cnt = time + 2; + sfrag.alpha = 1; + sfrag.drawmask = MASK_NORMAL; +} + +float v_raptor(float req) +{ + switch(req) + { + case VR_HUD: + { + if(autocvar_r_letterbox) - return TRUE; ++ return true; + + vector picsize, hudloc = '0 0 0', pic2size, picloc; + string raptor_xhair; + + // Fetch health & ammo stats + HUD_GETVEHICLESTATS + + picsize = draw_getimagesize(hud_bg) * autocvar_cl_vehicles_hudscale; + hudloc_y = vid_conheight - picsize_y; + hudloc_x = vid_conwidth * 0.5 - picsize_x * 0.5; + + drawpic(hudloc, hud_bg, picsize, '1 1 1', autocvar_cl_vehicles_hudalpha, DRAWFLAG_NORMAL); + + ammo1 *= 0.01; + ammo2 *= 0.01; + shield *= 0.01; + vh_health *= 0.01; + energy *= 0.01; + reload1 = reload2 * 0.01; + //reload2 *= 0.01; + + pic2size = draw_getimagesize(raptor_ico) * (autocvar_cl_vehicles_hudscale * 0.8); + picloc = picsize * 0.5 - pic2size * 0.5; + if(vh_health < 0.25) + drawpic(hudloc + picloc, raptor_ico, pic2size, '1 0 0' + '0 1 1' * sin(time * 8), 1, DRAWFLAG_NORMAL); + else + drawpic(hudloc + picloc, raptor_ico, pic2size, '1 1 1' * vh_health + '1 0 0' * (1 - vh_health), 1, DRAWFLAG_NORMAL); + drawpic(hudloc + picloc, raptor_bomb, pic2size, '1 1 1' * reload1 + '1 0 0' * (1 - reload1), 1, DRAWFLAG_NORMAL); + drawpic(hudloc + picloc, raptor_gun, pic2size, '1 1 1' * energy + '1 0 0' * (1 - energy), 1, DRAWFLAG_NORMAL); + drawpic(hudloc + picloc, hud_sh, pic2size, '1 1 1', shield, DRAWFLAG_NORMAL); + + // Health bar + picsize = draw_getimagesize(hud_hp_bar) * autocvar_cl_vehicles_hudscale; + picloc = '69 69 0' * autocvar_cl_vehicles_hudscale; + drawsetcliparea(hudloc_x + picloc_x + (picsize_x * (1 - vh_health)), 0, vid_conwidth, vid_conheight); + drawpic(hudloc + picloc, hud_hp_bar, picsize, '1 1 1', 1 , DRAWFLAG_NORMAL); + drawresetcliparea(); + // .. and icon + picsize = draw_getimagesize(hud_hp_ico) * autocvar_cl_vehicles_hudscale; + picloc = '37 65 0' * autocvar_cl_vehicles_hudscale; + if(vh_health < 0.25) + { + if(alarm1time < time) + { + alarm1time = time + 2; + vehicle_alarm(self, CH_PAIN_SINGLE, "vehicles/alarm.wav"); + } + + drawpic(hudloc + picloc, hud_hp_ico, picsize, '1 0 0' + '0 1 1' * sin(time * 8), 1, DRAWFLAG_NORMAL); + } + else + { + drawpic(hudloc + picloc, hud_hp_ico, picsize, '1 1 1', 1, DRAWFLAG_NORMAL); + if(alarm1time) + { + vehicle_alarm(self, CH_PAIN_SINGLE, "misc/null.wav"); + alarm1time = 0; + } + } + + // Shield bar + picsize = draw_getimagesize(hud_sh_bar) * autocvar_cl_vehicles_hudscale; + picloc = '69 140 0' * autocvar_cl_vehicles_hudscale; + drawsetcliparea(hudloc_x + picloc_x + (picsize_x * (1 - shield)), 0, vid_conwidth, vid_conheight); + drawpic(hudloc + picloc, hud_sh_bar, picsize, '1 1 1', 1, DRAWFLAG_NORMAL); + drawresetcliparea(); + // .. and icon + picloc = '40 136 0' * autocvar_cl_vehicles_hudscale; + picsize = draw_getimagesize(hud_sh_ico) * autocvar_cl_vehicles_hudscale; + if(shield < 0.25) + { + if(alarm2time < time) + { + alarm2time = time + 1; + vehicle_alarm(self, CH_TRIGGER_SINGLE, "vehicles/alarm_shield.wav"); + } + drawpic(hudloc + picloc, hud_sh_ico, picsize, '1 0 0' + '0 1 1' * sin(time * 8), 1, DRAWFLAG_NORMAL); + } + else + { + drawpic(hudloc + picloc, hud_sh_ico, picsize, '1 1 1', 1, DRAWFLAG_NORMAL); + if(alarm2time) + { + vehicle_alarm(self, CH_TRIGGER_SINGLE, "misc/null.wav"); + alarm2time = 0; + } + } + + // Gun bar + picsize = draw_getimagesize(hud_ammo1_bar) * autocvar_cl_vehicles_hudscale; + picloc = '450 69 0' * autocvar_cl_vehicles_hudscale; + drawsetcliparea(hudloc_x + picloc_x, picloc_y, picsize_x * energy, vid_conheight); + drawpic(hudloc + picloc, hud_ammo1_bar, picsize, '1 1 1', 1, DRAWFLAG_NORMAL); + drawresetcliparea(); + // .. and icon + picsize = draw_getimagesize(hud_ammo1_ico) * autocvar_cl_vehicles_hudscale; + picloc = '664 60 0' * autocvar_cl_vehicles_hudscale; + if(energy < 0.2) + drawpic(hudloc + picloc, hud_ammo1_ico, picsize, '1 0 0' + '0 1 1' * sin(time * 8), 1, DRAWFLAG_NORMAL); + else + drawpic(hudloc + picloc, hud_ammo1_ico, picsize, '1 1 1', 1, DRAWFLAG_NORMAL); + + // Bomb bar + picsize = draw_getimagesize(hud_ammo2_bar) * autocvar_cl_vehicles_hudscale; + picloc = '450 140 0' * autocvar_cl_vehicles_hudscale; + drawsetcliparea(hudloc_x + picloc_x, hudloc_y + picloc_y, picsize_x * reload1, vid_conheight); + drawpic(hudloc + picloc, hud_ammo2_bar, picsize, '1 1 1', 1, DRAWFLAG_NORMAL); + drawresetcliparea(); + // .. and icon + pic2size = draw_getimagesize(hud_ammo2_ico) * autocvar_cl_vehicles_hudscale; + picloc = '664 130 0' * autocvar_cl_vehicles_hudscale; + if(reload1 != 1) + drawpic(hudloc + picloc, hud_ammo2_ico, pic2size, '1 0 0' + '0 1 1' * sin(time * 8), 1, DRAWFLAG_NORMAL); + else + drawpic(hudloc + picloc, hud_ammo2_ico, pic2size, '1 1 1', 1, DRAWFLAG_NORMAL); + + if(getstati(STAT_VEHICLESTAT_W2MODE) == RSM_FLARE) + { + raptor_xhair = "gfx/vehicles/axh-bracket.tga"; + } + else + { + raptor_xhair = "gfx/vehicles/axh-ring.tga"; + + // Bombing crosshair + if(!dropmark) + { + dropmark = spawn(); + dropmark.owner = self; + dropmark.gravity = 1; + } + + if(reload2 == 100) + { + vector where; + + setorigin(dropmark, pmove_org); + dropmark.velocity = pmove_vel; + tracetoss(dropmark, self); + + where = project_3d_to_2d(trace_endpos); + + setorigin(dropmark, trace_endpos); + picsize = draw_getimagesize(raptor_drop) * 0.2; + + if(!(where_z < 0 || where_x < 0 || where_y < 0 || where_x > vid_conwidth || where_y > vid_conheight)) + { + where_x -= picsize_x * 0.5; + where_y -= picsize_y * 0.5; + where_z = 0; + drawpic(where, raptor_drop, picsize, '0 2 0', 1, DRAWFLAG_ADDITIVE); + } + dropmark.cnt = time + 5; + } + else + { + vector where; + if(dropmark.cnt > time) + { + where = project_3d_to_2d(dropmark.origin); + picsize = draw_getimagesize(raptor_drop) * 0.25; + + if(!(where_z < 0 || where_x < 0 || where_y < 0 || where_x > vid_conwidth || where_y > vid_conheight)) + { + where_x -= picsize_x * 0.5; + where_y -= picsize_y * 0.5; + where_z = 0; + drawpic(where, raptor_drop, picsize, '2 0 0', 1, DRAWFLAG_ADDITIVE); + } + } + } + } + + if (scoreboard_showscores) + HUD_DrawScoreboard(); + else + { + picsize = draw_getimagesize(raptor_xhair); + picsize_x *= 0.5; + picsize_y *= 0.5; + + drawpic('0.5 0 0' * (vid_conwidth - picsize_x) + '0 0.5 0' * (vid_conheight - picsize_y), raptor_xhair, picsize, '1 1 1', 1, DRAWFLAG_NORMAL); + } + - return TRUE; ++ return true; + } + case VR_SETUP: + { + AuxiliaryXhair[0].axh_image = "gfx/vehicles/axh-special2.tga"; + AuxiliaryXhair[0].axh_scale = 0.5; + + AuxiliaryXhair[1].axh_image = "gfx/vehicles/axh-bracket.tga"; + AuxiliaryXhair[1].axh_scale = 0.25; - return TRUE; ++ return true; + } + case VR_PRECACHE: + { - return TRUE; ++ return true; + } + } + - return TRUE; ++ return true; +} + +#endif // CSQC +#endif // REGISTER_VEHICLE diff --cc qcsrc/common/vehicles/unit/spiderbot.qc index 8ecdb36b7,000000000..85cd3e23a mode 100644,000000..100644 --- a/qcsrc/common/vehicles/unit/spiderbot.qc +++ b/qcsrc/common/vehicles/unit/spiderbot.qc @@@ -1,1080 -1,0 +1,1082 @@@ +#ifdef REGISTER_VEHICLE +REGISTER_VEHICLE( +/* VEH_##id */ SPIDERBOT, +/* function */ v_spiderbot, +/* spawnflags */ VHF_DMGSHAKE, +/* mins,maxs */ '-75 -75 10', '75 75 125', +/* model */ "models/vehicles/spiderbot.dpm", +/* head_model */ "models/vehicles/spiderbot_top.dpm", +/* hud_model */ "models/vehicles/spiderbot_cockpit.dpm", +/* tags */ "tag_head", "tag_hud", "", +/* netname */ "spiderbot", +/* fullname */ _("Spiderbot") +); +#else + +const float SBRM_FIRST = 1; +const float SBRM_VOLLY = 1; +const float SBRM_GUIDE = 2; +const float SBRM_ARTILLERY = 3; +const float SBRM_LAST = 3; + +#ifdef SVQC ++#include "../../effects.qh" ++ +float autocvar_g_vehicle_spiderbot; + +float autocvar_g_vehicle_spiderbot_respawntime; + +float autocvar_g_vehicle_spiderbot_speed_stop; +float autocvar_g_vehicle_spiderbot_speed_strafe; +float autocvar_g_vehicle_spiderbot_speed_walk; +float autocvar_g_vehicle_spiderbot_turnspeed; +float autocvar_g_vehicle_spiderbot_turnspeed_strafe; +float autocvar_g_vehicle_spiderbot_movement_inertia; + +float autocvar_g_vehicle_spiderbot_springlength; +float autocvar_g_vehicle_spiderbot_springup; +float autocvar_g_vehicle_spiderbot_springblend; +float autocvar_g_vehicle_spiderbot_tiltlimit; + +float autocvar_g_vehicle_spiderbot_head_pitchlimit_down; +float autocvar_g_vehicle_spiderbot_head_pitchlimit_up; +float autocvar_g_vehicle_spiderbot_head_turnlimit; +float autocvar_g_vehicle_spiderbot_head_turnspeed; + +float autocvar_g_vehicle_spiderbot_health; +float autocvar_g_vehicle_spiderbot_health_regen; +float autocvar_g_vehicle_spiderbot_health_regen_pause; + +float autocvar_g_vehicle_spiderbot_shield; +float autocvar_g_vehicle_spiderbot_shield_regen; +float autocvar_g_vehicle_spiderbot_shield_regen_pause; + +float autocvar_g_vehicle_spiderbot_minigun_damage; +float autocvar_g_vehicle_spiderbot_minigun_refire; +float autocvar_g_vehicle_spiderbot_minigun_spread; +float autocvar_g_vehicle_spiderbot_minigun_ammo_cost; +float autocvar_g_vehicle_spiderbot_minigun_ammo_max; +float autocvar_g_vehicle_spiderbot_minigun_ammo_regen; +float autocvar_g_vehicle_spiderbot_minigun_ammo_regen_pause; +float autocvar_g_vehicle_spiderbot_minigun_force; +float autocvar_g_vehicle_spiderbot_minigun_solidpenetration; + +float autocvar_g_vehicle_spiderbot_rocket_damage; +float autocvar_g_vehicle_spiderbot_rocket_force; +float autocvar_g_vehicle_spiderbot_rocket_radius; +float autocvar_g_vehicle_spiderbot_rocket_speed; +float autocvar_g_vehicle_spiderbot_rocket_spread; +float autocvar_g_vehicle_spiderbot_rocket_refire; +float autocvar_g_vehicle_spiderbot_rocket_refire2; +float autocvar_g_vehicle_spiderbot_rocket_reload; +float autocvar_g_vehicle_spiderbot_rocket_health; +float autocvar_g_vehicle_spiderbot_rocket_noise; +float autocvar_g_vehicle_spiderbot_rocket_turnrate; +float autocvar_g_vehicle_spiderbot_rocket_lifetime; + +vector autocvar_g_vehicle_spiderbot_bouncepain; + +void spiderbot_rocket_artillery() +{ + self.nextthink = time; + UpdateCSQCProjectile(self); +} + +void spiderbot_rocket_unguided() +{ + vector newdir, olddir; + + self.nextthink = time; + + olddir = normalize(self.velocity); + newdir = normalize(self.pos1 - self.origin) + randomvec() * autocvar_g_vehicle_spiderbot_rocket_noise; + self.velocity = normalize(olddir + newdir * autocvar_g_vehicle_spiderbot_rocket_turnrate) * autocvar_g_vehicle_spiderbot_rocket_speed; + + UpdateCSQCProjectile(self); + + if (self.owner.deadflag != DEAD_NO || self.cnt < time || vlen(self.pos1 - self.origin) < 16) + self.use(); +} + +void spiderbot_rocket_guided() +{ + vector newdir, olddir; + + self.nextthink = time; + + if(!self.realowner.vehicle) + self.think = spiderbot_rocket_unguided; + + crosshair_trace(self.realowner); + olddir = normalize(self.velocity); + newdir = normalize(trace_endpos - self.origin) + randomvec() * autocvar_g_vehicle_spiderbot_rocket_noise; + self.velocity = normalize(olddir + newdir * autocvar_g_vehicle_spiderbot_rocket_turnrate) * autocvar_g_vehicle_spiderbot_rocket_speed; + + UpdateCSQCProjectile(self); + + if (self.owner.deadflag != DEAD_NO || self.cnt < time) + self.use(); +} + +void spiderbot_guide_release() +{ + entity rkt; + rkt = findchainentity(realowner, self.owner); + if(!rkt) + return; + + crosshair_trace(self.owner); + while(rkt) + { + if(rkt.think == spiderbot_rocket_guided) + { + rkt.pos1 = trace_endpos; + rkt.think = spiderbot_rocket_unguided; + } + rkt = rkt.chain; + } +} + +float spiberbot_calcartillery_flighttime; +vector spiberbot_calcartillery(vector org, vector tgt, float ht) +{ + float grav, sdist, zdist, vs, vz, jumpheight; + vector sdir; + + grav = autocvar_sv_gravity; + zdist = tgt_z - org_z; + sdist = vlen(tgt - org - zdist * '0 0 1'); + sdir = normalize(tgt - org - zdist * '0 0 1'); + + // how high do we need to go? + jumpheight = fabs(ht); + if(zdist > 0) + jumpheight = jumpheight + zdist; + + // push so high... + vz = sqrt(2 * grav * jumpheight); // NOTE: sqrt(positive)! + + // we start with downwards velocity only if it's a downjump and the jump apex should be outside the jump! + if(ht < 0) + if(zdist < 0) + vz = -vz; + + vector solution; + solution = solve_quadratic(0.5 * grav, -vz, zdist); // equation "z(ti) = zdist" + // ALWAYS solvable because jumpheight >= zdist + if(!solution_z) + solution_y = solution_x; // just in case it is not solvable due to roundoff errors, assume two equal solutions at their center (this is mainly for the usual case with ht == 0) + if(zdist == 0) + solution_x = solution_y; // solution_x is 0 in this case, so don't use it, but rather use solution_y (which will be sqrt(0.5 * jumpheight / grav), actually) + + if(zdist < 0) + { + // down-jump + if(ht < 0) + { + // almost straight line type + // jump apex is before the jump + // we must take the larger one + spiberbot_calcartillery_flighttime = solution_y; + } + else + { + // regular jump + // jump apex is during the jump + // we must take the larger one too + spiberbot_calcartillery_flighttime = solution_y; + } + } + else + { + // up-jump + if(ht < 0) + { + // almost straight line type + // jump apex is after the jump + // we must take the smaller one + spiberbot_calcartillery_flighttime = solution_x; + } + else + { + // regular jump + // jump apex is during the jump + // we must take the larger one + spiberbot_calcartillery_flighttime = solution_y; + } + } + vs = sdist / spiberbot_calcartillery_flighttime; + + // finally calculate the velocity + return sdir * vs + '0 0 1' * vz; +} + +void spiderbot_rocket_do() +{ + vector v; + entity rocket = world; + + if (self.wait != -10) + { + if (self.owner.BUTTON_ATCK2 && self.vehicle_weapon2mode == SBRM_GUIDE) + { + if (self.wait == 1) + if (self.tur_head.frame == 9 || self.tur_head.frame == 1) + { + if(self.gun2.cnt < time && self.tur_head.frame == 9) + self.tur_head.frame = 1; + + return; + } + self.wait = 1; + } + else + { + if(self.wait) + spiderbot_guide_release(); + + self.wait = 0; + } + } + + if(self.gun2.cnt > time) + return; + + if (self.tur_head.frame >= 9) + { + self.tur_head.frame = 1; + self.wait = 0; + } + + if(self.wait != -10) + if(!self.owner.BUTTON_ATCK2) + return; + + if(forbidWeaponUse(self.owner)) + return; + + v = gettaginfo(self.tur_head,gettagindex(self.tur_head,"tag_fire")); + + switch(self.vehicle_weapon2mode) + { + case SBRM_VOLLY: + rocket = vehicles_projectile("spiderbot_rocket_launch", W_Sound("rocket_fire"), + v, normalize(randomvec() * autocvar_g_vehicle_spiderbot_rocket_spread + v_forward) * autocvar_g_vehicle_spiderbot_rocket_speed, + autocvar_g_vehicle_spiderbot_rocket_damage, autocvar_g_vehicle_spiderbot_rocket_radius, autocvar_g_vehicle_spiderbot_rocket_force, 1, - DEATH_VH_SPID_ROCKET, PROJECTILE_SPIDERROCKET, autocvar_g_vehicle_spiderbot_rocket_health, FALSE, TRUE, self.owner); ++ DEATH_VH_SPID_ROCKET, PROJECTILE_SPIDERROCKET, autocvar_g_vehicle_spiderbot_rocket_health, false, true, self.owner); + crosshair_trace(self.owner); + float _dist = (random() * autocvar_g_vehicle_spiderbot_rocket_radius) + vlen(v - trace_endpos); + _dist -= (random() * autocvar_g_vehicle_spiderbot_rocket_radius) ; + rocket.nextthink = time + (_dist / autocvar_g_vehicle_spiderbot_rocket_speed); + rocket.think = vehicles_projectile_explode; + + if(self.owner.BUTTON_ATCK2 && self.tur_head.frame == 1) + self.wait = -10; + break; + case SBRM_GUIDE: + rocket = vehicles_projectile("spiderbot_rocket_launch", W_Sound("rocket_fire"), + v, normalize(v_forward) * autocvar_g_vehicle_spiderbot_rocket_speed, + autocvar_g_vehicle_spiderbot_rocket_damage, autocvar_g_vehicle_spiderbot_rocket_radius, autocvar_g_vehicle_spiderbot_rocket_force, 1, - DEATH_VH_SPID_ROCKET, PROJECTILE_SPIDERROCKET, autocvar_g_vehicle_spiderbot_rocket_health, FALSE, FALSE, self.owner); ++ DEATH_VH_SPID_ROCKET, PROJECTILE_SPIDERROCKET, autocvar_g_vehicle_spiderbot_rocket_health, false, false, self.owner); + crosshair_trace(self.owner); + rocket.pos1 = trace_endpos; + rocket.nextthink = time; + rocket.think = spiderbot_rocket_guided; + + + break; + case SBRM_ARTILLERY: + rocket = vehicles_projectile("spiderbot_rocket_launch", W_Sound("rocket_fire"), + v, normalize(v_forward) * autocvar_g_vehicle_spiderbot_rocket_speed, + autocvar_g_vehicle_spiderbot_rocket_damage, autocvar_g_vehicle_spiderbot_rocket_radius, autocvar_g_vehicle_spiderbot_rocket_force, 1, - DEATH_VH_SPID_ROCKET, PROJECTILE_SPIDERROCKET, autocvar_g_vehicle_spiderbot_rocket_health, FALSE, TRUE, self.owner); ++ DEATH_VH_SPID_ROCKET, PROJECTILE_SPIDERROCKET, autocvar_g_vehicle_spiderbot_rocket_health, false, true, self.owner); + + crosshair_trace(self.owner); + + rocket.pos1 = trace_endpos + randomvec() * (0.75 * autocvar_g_vehicle_spiderbot_rocket_radius); + rocket.pos1_z = trace_endpos_z; + + traceline(v, v + '0 0 1' * MAX_SHOT_DISTANCE, MOVE_WORLDONLY, self); + float h1 = 0.75 * vlen(v - trace_endpos); + + //v = trace_endpos; + traceline(v , rocket.pos1 + '0 0 1' * MAX_SHOT_DISTANCE, MOVE_WORLDONLY, self); + float h2 = 0.75 * vlen(rocket.pos1 - v); + + rocket.velocity = spiberbot_calcartillery(v, rocket.pos1, ((h1 < h2) ? h1 : h2)); + rocket.movetype = MOVETYPE_TOSS; + rocket.gravity = 1; + //rocket.think = spiderbot_rocket_artillery; + break; + } + rocket.classname = "spiderbot_rocket"; + + rocket.cnt = time + autocvar_g_vehicle_spiderbot_rocket_lifetime; + + self.tur_head.frame += 1; + if (self.tur_head.frame == 9) + self.attack_finished_single = autocvar_g_vehicle_spiderbot_rocket_reload; + else + self.attack_finished_single = ((self.vehicle_weapon2mode == SBRM_VOLLY) ? autocvar_g_vehicle_spiderbot_rocket_refire2 : autocvar_g_vehicle_spiderbot_rocket_refire); + + self.gun2.cnt = time + self.attack_finished_single; +} + +float spiderbot_frame() +{ + vector ad, vf; + entity player, spider; + float ftmp; + + if(intermission_running) + { + self.vehicle.velocity = '0 0 0'; + self.vehicle.avelocity = '0 0 0'; + return 1; + } + + player = self; + spider = self.vehicle; + self = spider; + + vehicles_painframe(); + + player.BUTTON_ZOOM = 0; + player.BUTTON_CROUCH = 0; + player.switchweapon = 0; + player.vehicle_weapon2mode = spider.vehicle_weapon2mode; + + +#if 1 // 0 to enable per-gun impact aux crosshairs + // Avarage gun impact point's -> aux cross + ad = gettaginfo(spider.tur_head, gettagindex(spider.tur_head, "tag_hardpoint01")); + vf = v_forward; + ad += gettaginfo(spider.tur_head, gettagindex(spider.tur_head, "tag_hardpoint02")); + vf += v_forward; + ad = ad * 0.5; + v_forward = vf * 0.5; + traceline(ad, ad + v_forward * MAX_SHOT_DISTANCE, MOVE_NORMAL, spider); + UpdateAuxiliaryXhair(player, trace_endpos, ('1 0 0' * player.vehicle_reload1) + ('0 1 0' * (1 - player.vehicle_reload1)), 0); +#else + ad = gettaginfo(spider.gun1, gettagindex(spider.gun1, "barrels")); + traceline(ad, ad + v_forward * MAX_SHOT_DISTANCE, MOVE_NORMAL, spider); + UpdateAuxiliaryXhair(player, trace_endpos, ('1 0 0' * player.vehicle_reload1) + ('0 1 0' * (1 - player.vehicle_reload1)), 0); + vf = ad; + ad = gettaginfo(spider.gun2, gettagindex(spider.gun2, "barrels")); + traceline(ad, ad + v_forward * MAX_SHOT_DISTANCE, MOVE_NORMAL, spider); + UpdateAuxiliaryXhair(player, trace_endpos, ('1 0 0' * player.vehicle_reload1) + ('0 1 0' * (1 - player.vehicle_reload1)), 1); + ad = 0.5 * (ad + vf); +#endif + + crosshair_trace(player); + ad = vectoangles(normalize(trace_endpos - ad)); + ad = AnglesTransform_ToAngles(AnglesTransform_LeftDivide(AnglesTransform_FromAngles(spider.angles), AnglesTransform_FromAngles(ad))) - spider.tur_head.angles; - ad = AnglesTransform_Normalize(ad, TRUE); ++ ad = AnglesTransform_Normalize(ad, true); + //UpdateAuxiliaryXhair(player, trace_endpos, ('1 0 0' * player.vehicle_reload2) + ('0 1 0' * (1 - player.vehicle_reload2)), 2); + + // Rotate head + ftmp = autocvar_g_vehicle_spiderbot_head_turnspeed * sys_frametime; + ad_y = bound(-ftmp, ad_y, ftmp); + spider.tur_head.angles_y = bound(autocvar_g_vehicle_spiderbot_head_turnlimit * -1, spider.tur_head.angles_y + ad_y, autocvar_g_vehicle_spiderbot_head_turnlimit); + + // Pitch head + ad_x = bound(ftmp * -1, ad_x, ftmp); + spider.tur_head.angles_x = bound(autocvar_g_vehicle_spiderbot_head_pitchlimit_down, spider.tur_head.angles_x + ad_x, autocvar_g_vehicle_spiderbot_head_pitchlimit_up); + + + //fixedmakevectors(spider.angles); + makevectors(spider.angles + '-2 0 0' * spider.angles_x); + + movelib_groundalign4point(autocvar_g_vehicle_spiderbot_springlength, autocvar_g_vehicle_spiderbot_springup, autocvar_g_vehicle_spiderbot_springblend, autocvar_g_vehicle_spiderbot_tiltlimit); + + if(spider.flags & FL_ONGROUND) + { + if(spider.frame == 4 && self.tur_head.wait != 0) + { + sound (self, CH_TRIGGER_SINGLE, "vehicles/spiderbot_land.wav", VOL_VEHICLEENGINE, ATTEN_NORM); + spider.frame = 5; + } + + if(player.BUTTON_JUMP && self.tur_head.wait < time) + { + sound (self, CH_TRIGGER_SINGLE, "vehicles/spiderbot_jump.wav", VOL_VEHICLEENGINE, ATTEN_NORM); + //dprint("spiderbot_jump:", ftos(soundlength("vehicles/spiderbot_jump.wav")), "\n"); + self.delay = 0; + + self.tur_head.wait = time + 2; + player.BUTTON_JUMP = 0; + spider.velocity = v_forward * 700 + v_up * 600; + spider.frame = 4; + } + else + { + if(vlen(player.movement) == 0) + { + if(self.sound_nexttime < time || self.delay != 3) + { + self.delay = 3; + self.sound_nexttime = time + 6.486500; //soundlength("vehicles/spiderbot_idle.wav"); + //dprint("spiderbot_idle:", ftos(soundlength("vehicles/spiderbot_idle.wav")), "\n"); + sound (self, CH_TRIGGER_SINGLE, "vehicles/spiderbot_idle.wav", VOL_VEHICLEENGINE, ATTEN_NORM); + } + movelib_beak_simple(autocvar_g_vehicle_spiderbot_speed_stop); + spider.frame = 5; + } + else + { + // Turn Body + if(player.movement_x == 0 && player.movement_y != 0) + ftmp = autocvar_g_vehicle_spiderbot_turnspeed_strafe * sys_frametime; + else + ftmp = autocvar_g_vehicle_spiderbot_turnspeed * sys_frametime; + + ftmp = bound(-ftmp, spider.tur_head.angles_y, ftmp); + spider.angles_y = anglemods(spider.angles_y + ftmp); + spider.tur_head.angles_y -= ftmp; + + if(player.movement_x != 0) + { + if(player.movement_x > 0) + { + player.movement_x = 1; + spider.frame = 0; + } + else if(player.movement_x < 0) + { + player.movement_x = -1; + spider.frame = 1; + } + player.movement_y = 0; + movelib_move_simple(normalize(v_forward * player.movement_x),autocvar_g_vehicle_spiderbot_speed_walk,autocvar_g_vehicle_spiderbot_movement_inertia); + + if(self.sound_nexttime < time || self.delay != 1) + { + self.delay = 1; + self.sound_nexttime = time + 6.486500; //soundlength("vehicles/spiderbot_walk.wav"); + sound (self, CH_TRIGGER_SINGLE, "vehicles/spiderbot_walk.wav", VOL_VEHICLEENGINE, ATTEN_NORM); + //dprint("spiderbot_walk:", ftos(soundlength("vehicles/spiderbot_walk.wav")), "\n"); + } + } + else if(player.movement_y != 0) + { + if(player.movement_y < 0) + { + player.movement_y = -1; + spider.frame = 2; + } + else if(player.movement_y > 0) + { + player.movement_y = 1; + spider.frame = 3; + } + movelib_move_simple(normalize(v_right * player.movement_y),autocvar_g_vehicle_spiderbot_speed_strafe,autocvar_g_vehicle_spiderbot_movement_inertia); + if(self.sound_nexttime < time || self.delay != 2) + { + self.delay = 2; + self.sound_nexttime = time + 6.486500; //soundlength("vehicles/spiderbot_strafe.wav"); + sound (self, CH_TRIGGER_SINGLE, "vehicles/spiderbot_strafe.wav", VOL_VEHICLEENGINE, ATTEN_NORM); + //dprint("spiderbot_strafe:", ftos(soundlength("vehicles/spiderbot_strafe.wav")), "\n"); + } + } + } + } + } + + self.angles_x = bound(-autocvar_g_vehicle_spiderbot_tiltlimit, self.angles_x, autocvar_g_vehicle_spiderbot_tiltlimit); + self.angles_z = bound(-autocvar_g_vehicle_spiderbot_tiltlimit, self.angles_z, autocvar_g_vehicle_spiderbot_tiltlimit); + + if(!forbidWeaponUse(player)) + if(player.BUTTON_ATCK) + { + spider.cnt = time; + if(spider.vehicle_ammo1 >= autocvar_g_vehicle_spiderbot_minigun_ammo_cost && spider.tur_head.attack_finished_single <= time) + { + entity gun; + vector v; + spider.misc_bulletcounter += 1; + + self = player; + + gun = (spider.misc_bulletcounter % 2) ? spider.gun1 : spider.gun2; + + v = gettaginfo(gun, gettagindex(gun, "barrels")); + v_forward = normalize(v_forward); + v += v_forward * 50; + + fireBullet(v, v_forward, autocvar_g_vehicle_spiderbot_minigun_spread, autocvar_g_vehicle_spiderbot_minigun_solidpenetration, + autocvar_g_vehicle_spiderbot_minigun_damage, autocvar_g_vehicle_spiderbot_minigun_force, DEATH_VH_SPID_MINIGUN, 0); + + sound (gun, CH_WEAPON_A, W_Sound("uzi_fire"), VOL_BASE, ATTEN_NORM); + //trailparticles(self, particleeffectnum("spiderbot_minigun_trail"), v, trace_endpos); + pointparticles(particleeffectnum("spiderbot_minigun_muzzleflash"), v, v_forward * 2500, 1); + + self = spider; + + spider.vehicle_ammo1 -= autocvar_g_vehicle_spiderbot_minigun_ammo_cost; + spider.tur_head.attack_finished_single = time + autocvar_g_vehicle_spiderbot_minigun_refire; + player.vehicle_ammo1 = (spider.vehicle_ammo1 / autocvar_g_vehicle_spiderbot_minigun_ammo_max) * 100; + spider.gun1.angles_z += 45; + spider.gun2.angles_z -= 45; + if(spider.gun1.angles_z >= 360) + { + spider.gun1.angles_z = 0; + spider.gun2.angles_z = 0; + } + } + } + else + vehicles_regen(spider.cnt, vehicle_ammo1, autocvar_g_vehicle_spiderbot_minigun_ammo_max, + autocvar_g_vehicle_spiderbot_minigun_ammo_regen_pause, - autocvar_g_vehicle_spiderbot_minigun_ammo_regen, frametime, FALSE); ++ autocvar_g_vehicle_spiderbot_minigun_ammo_regen, frametime, false); + + + spiderbot_rocket_do(); + + if(self.vehicle_flags & VHF_SHIELDREGEN) - vehicles_regen(spider.dmg_time, vehicle_shield, autocvar_g_vehicle_spiderbot_shield, autocvar_g_vehicle_spiderbot_shield_regen_pause, autocvar_g_vehicle_spiderbot_shield_regen, frametime, TRUE); ++ vehicles_regen(spider.dmg_time, vehicle_shield, autocvar_g_vehicle_spiderbot_shield, autocvar_g_vehicle_spiderbot_shield_regen_pause, autocvar_g_vehicle_spiderbot_shield_regen, frametime, true); + + if(self.vehicle_flags & VHF_HEALTHREGEN) - vehicles_regen(spider.dmg_time, vehicle_health, autocvar_g_vehicle_spiderbot_health, autocvar_g_vehicle_spiderbot_health_regen_pause, autocvar_g_vehicle_spiderbot_health_regen, frametime, FALSE); ++ vehicles_regen(spider.dmg_time, vehicle_health, autocvar_g_vehicle_spiderbot_health, autocvar_g_vehicle_spiderbot_health_regen_pause, autocvar_g_vehicle_spiderbot_health_regen, frametime, false); + + player.BUTTON_ATCK = player.BUTTON_ATCK2 = 0; + player.vehicle_ammo2 = spider.tur_head.frame; + + if(spider.gun2.cnt <= time) + player.vehicle_reload2 = 100; + else + player.vehicle_reload2 = 100 - ((spider.gun2.cnt - time) / spider.attack_finished_single) * 100; + + setorigin(player, spider.origin + '0 0 1' * spider.maxs_z); + player.velocity = spider.velocity; + + VEHICLE_UPDATE_PLAYER(player, health, spiderbot); + + if(self.vehicle_flags & VHF_HASSHIELD) + VEHICLE_UPDATE_PLAYER(player, shield, spiderbot); + + self = player; + return 1; +} + +void spiderbot_exit(float eject) +{ + entity e; + vector spot; + + e = findchain(classname,"spiderbot_rocket"); + while(e) + { + if(e.owner == self.owner) + { + e.realowner = self.owner; + e.owner = world; + } + e = e.chain; + } + + self.think = vehicles_think; + self.nextthink = time; + self.frame = 5; + self.movetype = MOVETYPE_WALK; + + if(!self.owner) + return; + + makevectors(self.angles); + if(eject) + { + spot = self.origin + v_forward * 100 + '0 0 64'; + spot = vehicles_findgoodexit(spot); + setorigin(self.owner , spot); + self.owner.velocity = (v_up + v_forward * 0.25) * 750; + self.owner.oldvelocity = self.owner.velocity; + } + else + { + if(vlen(self.velocity) > autocvar_g_vehicle_spiderbot_speed_strafe) + { + self.owner.velocity = normalize(self.velocity) * vlen(self.velocity); + self.owner.velocity_z += 200; + spot = self.origin + v_forward * 128 + '0 0 64'; + spot = vehicles_findgoodexit(spot); + } + else + { + self.owner.velocity = self.velocity * 0.5; + self.owner.velocity_z += 10; + spot = self.origin + v_forward * 256 + '0 0 64'; + spot = vehicles_findgoodexit(spot); + } + self.owner.oldvelocity = self.owner.velocity; + setorigin(self.owner , spot); + } + + antilag_clear(self.owner); + self.owner = world; +} + +void spiderbot_headfade() +{ + self.think = spiderbot_headfade; + self.nextthink = self.fade_time; + self.alpha = 1 - (time - self.fade_time) * self.fade_rate; + + if(self.cnt < time || self.alpha < 0.1) + { + if(self.alpha > 0.1) + { + sound (self, CH_SHOTS, W_Sound("rocket_impact"), VOL_BASE, ATTEN_NORM); + Send_Effect(EFFECT_EXPLOSION_BIG, self.origin + '0 0 100', '0 0 0', 1); + } + remove(self); + } +} + +void spiderbot_blowup() +{ + if(self.cnt > time) + { + if(random() < 0.1) + { + sound (self, CH_SHOTS, W_Sound("rocket_impact"), VOL_BASE, ATTEN_NORM); + Send_Effect(EFFECT_EXPLOSION_SMALL, randomvec() * 80 + (self.origin + '0 0 100'), '0 0 0', 1); + } + self.nextthink = time + 0.1; + return; + } + + entity h, g1, g2, b; + b = spawn(); + h = spawn(); + g1 = spawn(); + g2 = spawn(); + + setmodel(b, "models/vehicles/spiderbot.dpm"); + setmodel(h, "models/vehicles/spiderbot_top.dpm"); + setmodel(g1, "models/vehicles/spiderbot_barrels.dpm"); + setmodel(g2, "models/vehicles/spiderbot_barrels.dpm"); + + setorigin(b, self.origin); + b.frame = 11; + b.angles = self.angles; + setsize(b, self.mins, self.maxs); + + setorigin(h, gettaginfo(self, gettagindex(self, "tag_head"))); + h.movetype = MOVETYPE_BOUNCE; + h.solid = SOLID_BBOX; + h.velocity = v_up * (500 + random() * 500) + randomvec() * 128; + h.modelflags = MF_ROCKET; + h.effects = EF_FLAME | EF_LOWPRECISION; + h.avelocity = randomvec() * 360; + + h.alpha = 1; + h.cnt = time + (3.5 * random()); + h.fade_rate = 1 / min(self.respawntime, 10); + h.fade_time = time; + h.think = spiderbot_headfade; + h.nextthink = time; + + setorigin(g1, gettaginfo(self.tur_head, gettagindex(self.tur_head, "tag_hardpoint01"))); + g1.movetype = MOVETYPE_TOSS; + g1.solid = SOLID_CORPSE; + g1.velocity = v_forward * 700 + (randomvec() * 32); + g1.avelocity = randomvec() * 180; + + setorigin(g2, gettaginfo(self.tur_head, gettagindex(self.tur_head, "tag_hardpoint02"))); + g2.movetype = MOVETYPE_TOSS; + g2.solid = SOLID_CORPSE; + g2.velocity = v_forward * 700 + (randomvec() * 32); + g2.avelocity = randomvec() * 180; + + h.colormod = b.colormod = g1.colormod = g2.colormod = '-2 -2 -2'; + + SUB_SetFade(b, time + 5, min(self.respawntime, 1)); + //SUB_SetFade(h, time, min(self.respawntime, 10)); + SUB_SetFade(g1, time, min(self.respawntime, 10)); + SUB_SetFade(g2, time, min(self.respawntime, 10)); + + RadiusDamage (self, self.enemy, 250, 15, 250, world, world, 250, DEATH_VH_SPID_DEATH, world); + + self.alpha = self.tur_head.alpha = self.gun1.alpha = self.gun2.alpha = -1; + self.movetype = MOVETYPE_NONE; + self.deadflag = DEAD_DEAD; + self.solid = SOLID_NOT; + self.tur_head.effects &= ~EF_FLAME; + self.vehicle_hudmodel.viewmodelforclient = self; +} + +float spiderbot_impulse(float _imp) +{ + switch(_imp) + { + case 10: + case 15: + case 18: + self.vehicle.vehicle_weapon2mode += 1; + if(self.vehicle.vehicle_weapon2mode > SBRM_LAST) + self.vehicle.vehicle_weapon2mode = SBRM_FIRST; + + //centerprint(self, strcat("Rocket mode is ", ftos(self.vehicle.vehicle_weapon2mode))); + CSQCVehicleSetup(self, 0); - return TRUE; ++ return true; + case 12: + case 16: + case 19: + self.vehicle.vehicle_weapon2mode -= 1; + if(self.vehicle.vehicle_weapon2mode < SBRM_FIRST) + self.vehicle.vehicle_weapon2mode = SBRM_LAST; + + //centerprint(self, strcat("Rocket mode is ", ftos(self.vehicle.vehicle_weapon2mode))); + CSQCVehicleSetup(self, 0); - return TRUE; ++ return true; + + /* + case 17: // toss gun, could be used to exit? + break; + case 20: // Manual minigun reload? + break; + */ + } - return FALSE; ++ return false; +} + +void spawnfunc_vehicle_spiderbot() +{ + if(!autocvar_g_vehicle_spiderbot) { remove(self); return; } - if(!vehicle_initialize(VEH_SPIDERBOT, FALSE)) { remove(self); return; } ++ if(!vehicle_initialize(VEH_SPIDERBOT, false)) { remove(self); return; } +} + +float v_spiderbot(float req) +{ + switch(req) + { + case VR_IMPACT: + { + if(autocvar_g_vehicle_spiderbot_bouncepain) + vehicles_impact(autocvar_g_vehicle_spiderbot_bouncepain_x, autocvar_g_vehicle_spiderbot_bouncepain_y, autocvar_g_vehicle_spiderbot_bouncepain_z); + - return TRUE; ++ return true; + } + case VR_ENTER: + { + self.vehicle_weapon2mode = SBRM_GUIDE; + self.movetype = MOVETYPE_WALK; + CSQCVehicleSetup(self.owner, 0); + self.owner.vehicle_health = (self.vehicle_health / autocvar_g_vehicle_spiderbot_health) * 100; + self.owner.vehicle_shield = (self.vehicle_shield / autocvar_g_vehicle_spiderbot_shield) * 100; + + if(self.owner.flagcarried) + { + setattachment(self.owner.flagcarried, self.tur_head, ""); + setorigin(self.owner.flagcarried, '-20 0 120'); + } + - return TRUE; ++ return true; + } + case VR_THINK: + { + if(self.flags & FL_ONGROUND) + movelib_beak_simple(autocvar_g_vehicle_spiderbot_speed_stop); + - return TRUE; ++ return true; + } + case VR_DEATH: + { + self.health = 0; + self.event_damage = func_null; + self.takedamage = DAMAGE_NO; + self.touch = func_null; + self.cnt = 3.4 + time + random() * 2; + self.think = spiderbot_blowup; + self.nextthink = time; + self.deadflag = DEAD_DYING; + self.frame = 5; + self.tur_head.effects |= EF_FLAME; + self.colormod = self.tur_head.colormod = '-1 -1 -1'; + self.frame = 10; + self.movetype = MOVETYPE_TOSS; + + CSQCModel_UnlinkEntity(); // networking the death scene would be a nightmare + - return TRUE; ++ return true; + } + case VR_SPAWN: + { + if(!self.gun1) + { + self.vehicles_impulse = spiderbot_impulse; + self.gun1 = spawn(); + self.gun2 = spawn(); + setmodel(self.gun1, "models/vehicles/spiderbot_barrels.dpm"); + setmodel(self.gun2, "models/vehicles/spiderbot_barrels.dpm"); + setattachment(self.gun1, self.tur_head, "tag_hardpoint01"); + setattachment(self.gun2, self.tur_head, "tag_hardpoint02"); + self.gravity = 2; + self.mass = 5000; + } + + self.frame = 5; + self.tur_head.frame = 1; + self.movetype = MOVETYPE_WALK; + self.solid = SOLID_SLIDEBOX; + self.alpha = self.tur_head.alpha = self.gun1.alpha = self.gun2.alpha = 1; + self.tur_head.angles = '0 0 0'; + self.vehicle_exit = spiderbot_exit; + + setorigin(self, self.pos1 + '0 0 128'); + self.angles = self.pos2; + self.damageforcescale = 0.03; + self.vehicle_health = autocvar_g_vehicle_spiderbot_health; + self.vehicle_shield = autocvar_g_vehicle_spiderbot_shield; + + self.PlayerPhysplug = spiderbot_frame; + - return TRUE; ++ return true; + } + case VR_SETUP: + { + if(autocvar_g_vehicle_spiderbot_shield) + self.vehicle_flags |= VHF_HASSHIELD; + + if(autocvar_g_vehicle_spiderbot_shield_regen) + self.vehicle_flags |= VHF_SHIELDREGEN; + + if(autocvar_g_vehicle_spiderbot_health_regen) + self.vehicle_flags |= VHF_HEALTHREGEN; + + self.respawntime = autocvar_g_vehicle_spiderbot_respawntime; + self.vehicle_health = autocvar_g_vehicle_spiderbot_health; + self.vehicle_shield = autocvar_g_vehicle_spiderbot_shield; + self.max_health = self.vehicle_health; - self.pushable = TRUE; // spiderbot can use jumppads ++ self.pushable = true; // spiderbot can use jumppads + - return TRUE; ++ return true; + } + case VR_PRECACHE: + { + precache_model ("models/vhshield.md3"); + precache_model ("models/vehicles/spiderbot.dpm"); + precache_model ("models/vehicles/spiderbot_top.dpm"); + precache_model ("models/vehicles/spiderbot_barrels.dpm"); + precache_model ("models/vehicles/spiderbot_cockpit.dpm"); + precache_model ( "models/uziflash.md3"); + + precache_sound (W_Sound("uzi_fire")); + precache_sound (W_Sound("rocket_impact")); + + precache_sound ("vehicles/spiderbot_die.wav"); + precache_sound ("vehicles/spiderbot_idle.wav"); + precache_sound ("vehicles/spiderbot_jump.wav"); + precache_sound ("vehicles/spiderbot_strafe.wav"); + precache_sound ("vehicles/spiderbot_walk.wav"); + precache_sound ("vehicles/spiderbot_land.wav"); - return TRUE; ++ return true; + } + } + - return TRUE; ++ return true; +} + +#endif // SVQC +#ifdef CSQC - var float autocvar_cl_vehicle_spiderbot_cross_alpha = 0.6; - var float autocvar_cl_vehicle_spiderbot_cross_size = 1; ++float autocvar_cl_vehicle_spiderbot_cross_alpha = 0.6; ++float autocvar_cl_vehicle_spiderbot_cross_size = 1; + +#define spider_ico "gfx/vehicles/sbot.tga" +#define spider_rkt "gfx/vehicles/sbot_rpods.tga" +#define spider_mgun "gfx/vehicles/sbot_mguns.tga" +string spider_xhair; // = "gfx/vehicles/axh-special1.tga"; + +float v_spiderbot(float req) +{ + switch(req) + { + case VR_HUD: + { + if(autocvar_r_letterbox) - return TRUE; ++ return true; + + vector picsize, hudloc = '0 0 0', pic2size, picloc; + float i; + + // Fetch health & ammo stats + HUD_GETVEHICLESTATS + + picsize = draw_getimagesize(hud_bg) * autocvar_cl_vehicles_hudscale; + hudloc_y = vid_conheight - picsize_y; + hudloc_x = vid_conwidth * 0.5 - picsize_x * 0.5; + + drawpic(hudloc, hud_bg, picsize, '1 1 1', autocvar_cl_vehicles_hudalpha, DRAWFLAG_NORMAL); + + ammo1 *= 0.01; + shield *= 0.01; + vh_health *= 0.01; + reload2 *= 0.01; + + pic2size = draw_getimagesize(spider_ico) * (autocvar_cl_vehicles_hudscale * 0.8); + picloc = picsize * 0.5 - pic2size * 0.5; + if(vh_health < 0.25) + drawpic(hudloc + picloc, spider_ico, pic2size, '1 0 0' + '0 1 1' * sin(time * 8), 1, DRAWFLAG_NORMAL); + else + drawpic(hudloc + picloc, spider_ico, pic2size, '1 1 1' * vh_health + '1 0 0' * (1 - vh_health), 1, DRAWFLAG_NORMAL); + drawpic(hudloc + picloc, spider_rkt, pic2size, '1 1 1' * reload2 + '1 0 0' * (1 - reload2), 1, DRAWFLAG_NORMAL); + drawpic(hudloc + picloc, spider_mgun, pic2size, '1 1 1' * ammo1 + '1 0 0' * (1 - ammo1), 1, DRAWFLAG_NORMAL); + drawpic(hudloc + picloc, hud_sh, pic2size, '1 1 1', shield, DRAWFLAG_NORMAL); + + // Health bar + picsize = draw_getimagesize(hud_hp_bar) * autocvar_cl_vehicles_hudscale; + picloc = '69 69 0' * autocvar_cl_vehicles_hudscale; + drawsetcliparea(hudloc_x + picloc_x + (picsize_x * (1 - vh_health)), 0, vid_conwidth, vid_conheight); + drawpic(hudloc + picloc, hud_hp_bar, picsize, '1 1 1', 1 , DRAWFLAG_NORMAL); + drawresetcliparea(); + // .. and icon + picsize = draw_getimagesize(hud_hp_ico) * autocvar_cl_vehicles_hudscale; + picloc = '37 65 0' * autocvar_cl_vehicles_hudscale; + if(vh_health < 0.25) + { + if(alarm1time < time) + { + alarm1time = time + 2; + vehicle_alarm(self, CH_PAIN_SINGLE, "vehicles/alarm.wav"); + } + drawpic(hudloc + picloc, hud_hp_ico, picsize, '1 0 0' + '0 1 1' * sin(time * 8), 1, DRAWFLAG_NORMAL); + } + else + { + drawpic(hudloc + picloc, hud_hp_ico, picsize, '1 1 1', 1, DRAWFLAG_NORMAL); + if(alarm1time) + { + vehicle_alarm(self, CH_PAIN_SINGLE, "misc/null.wav"); + alarm1time = 0; + } + } + // Shield bar + picsize = draw_getimagesize(hud_sh_bar) * autocvar_cl_vehicles_hudscale; + picloc = '69 140 0' * autocvar_cl_vehicles_hudscale; + drawsetcliparea(hudloc_x + picloc_x + (picsize_x * (1 - shield)), 0, vid_conwidth, vid_conheight); + drawpic(hudloc + picloc, hud_sh_bar, picsize, '1 1 1', 1, DRAWFLAG_NORMAL); + drawresetcliparea(); + // .. and icon + picloc = '40 136 0' * autocvar_cl_vehicles_hudscale; + picsize = draw_getimagesize(hud_sh_ico) * autocvar_cl_vehicles_hudscale; + if(shield < 0.25) + { + if(alarm2time < time) + { + alarm2time = time + 1; + vehicle_alarm(self, CH_PAIN_SINGLE, "vehicles/alarm_shield.wav"); + } + drawpic(hudloc + picloc, hud_sh_ico, picsize, '1 0 0' + '0 1 1' * sin(time * 8), 1, DRAWFLAG_NORMAL); + } + else + { + drawpic(hudloc + picloc, hud_sh_ico, picsize, '1 1 1', 1, DRAWFLAG_NORMAL); + if(alarm2time) + { + vehicle_alarm(self, CH_PAIN_SINGLE, "misc/null.wav"); + alarm2time = 0; + } + } + + // Minigun bar + picsize = draw_getimagesize(hud_ammo1_bar) * autocvar_cl_vehicles_hudscale; + picloc = '450 69 0' * autocvar_cl_vehicles_hudscale; + drawsetcliparea(hudloc_x + picloc_x, picloc_y, picsize_x * ammo1, vid_conheight); + drawpic(hudloc + picloc, hud_ammo1_bar, picsize, '1 1 1', 1, DRAWFLAG_NORMAL); + drawresetcliparea(); + // .. and icon + picsize = draw_getimagesize(hud_ammo1_ico) * autocvar_cl_vehicles_hudscale; + picloc = '664 60 0' * autocvar_cl_vehicles_hudscale; + if(ammo1 < 0.2) + drawpic(hudloc + picloc, hud_ammo1_ico, picsize, '1 0 0' + '0 1 1' * sin(time * 8), 1, DRAWFLAG_NORMAL); + else + drawpic(hudloc + picloc, hud_ammo1_ico, picsize, '1 1 1', 1, DRAWFLAG_NORMAL); + + // Rocket ammo bar + picsize = draw_getimagesize(hud_ammo2_bar) * autocvar_cl_vehicles_hudscale; + ammo1 = picsize_x / 8; + picloc = '450 140 0' * autocvar_cl_vehicles_hudscale; + drawsetcliparea(hudloc_x + picloc_x, hudloc_y + picloc_y, picsize_x * reload2, vid_conheight); + drawpic(hudloc + picloc, hud_ammo2_bar, picsize, '1 1 1', 1, DRAWFLAG_NORMAL); + drawresetcliparea(); + + // .. and icons + pic2size = 0.35 * draw_getimagesize(hud_ammo2_ico) * autocvar_cl_vehicles_hudscale; + picloc_x -= pic2size_x; + picloc_y += pic2size_y * 2.25; + if(ammo2 == 9) + { + for(i = 1; i < 9; ++i) + { + picloc_x += ammo1; + drawpic(hudloc + picloc, hud_ammo2_ico, pic2size, ((8 * reload2 <= i) ? '0 0 0' : '1 1 1'), 0.75, DRAWFLAG_NORMAL); + } + } + else + { + for(i = 1; i < 9; ++i) + { + picloc_x += ammo1; + drawpic(hudloc + picloc, hud_ammo2_ico, pic2size, ((i >= ammo2) ? '1 1 1' : '0 0 0'), 0.75, DRAWFLAG_NORMAL); + } + } + pic2size = draw_getimagesize(hud_ammo2_ico) * autocvar_cl_vehicles_hudscale; + picloc = '664 130 0' * autocvar_cl_vehicles_hudscale; + if(ammo2 == 9) + drawpic(hudloc + picloc, hud_ammo2_ico, pic2size, '1 0 0' + '0 1 1' * sin(time * 8), 1, DRAWFLAG_NORMAL); + else + drawpic(hudloc + picloc, hud_ammo2_ico, pic2size, '1 1 1', 1, DRAWFLAG_NORMAL); + + if (scoreboard_showscores) + HUD_DrawScoreboard(); + else + { + switch(getstati(STAT_VEHICLESTAT_W2MODE)) + { + case SBRM_VOLLY: + spider_xhair = "gfx/vehicles/axh-bracket.tga"; + break; + case SBRM_GUIDE: + spider_xhair = "gfx/vehicles/axh-cross.tga"; + break; + case SBRM_ARTILLERY: + spider_xhair = "gfx/vehicles/axh-tag.tga"; + break; + default: + spider_xhair= "gfx/vehicles/axh-tag.tga"; + } + + picsize = draw_getimagesize(spider_xhair); + picsize_x *= autocvar_cl_vehicle_spiderbot_cross_size; + picsize_y *= autocvar_cl_vehicle_spiderbot_cross_size; + + drawpic('0.5 0 0' * (vid_conwidth - picsize_x) + '0 0.5 0' * (vid_conheight - picsize_y), spider_xhair, picsize, '1 1 1', autocvar_cl_vehicle_spiderbot_cross_alpha, DRAWFLAG_ADDITIVE); + } + - return TRUE; ++ return true; + } + case VR_SETUP: + { + // Minigun1 + AuxiliaryXhair[0].axh_image = "gfx/vehicles/axh-ring.tga"; + AuxiliaryXhair[0].axh_scale = 0.25; + // Minigun2 + AuxiliaryXhair[1].axh_image = "gfx/vehicles/axh-ring.tga"; + AuxiliaryXhair[1].axh_scale = 0.25; + // Rocket + AuxiliaryXhair[2].axh_image = "gfx/vehicles/axh-special1.tga"; + AuxiliaryXhair[2].axh_scale = 0.5; + - return TRUE; ++ return true; + } + case VR_PRECACHE: + { - return TRUE; ++ return true; + } + } + - return TRUE; ++ return true; +} + +#endif // CSQC +#endif // REGISTER_VEHICLE diff --cc qcsrc/common/vehicles/unit/tankll48.qc index 94266d226,000000000..f79466b53 mode 100644,000000..100644 --- a/qcsrc/common/vehicles/unit/tankll48.qc +++ b/qcsrc/common/vehicles/unit/tankll48.qc @@@ -1,743 -1,0 +1,745 @@@ +#ifdef REGISTER_VEHICLE +REGISTER_VEHICLE( +/* VEH_##id */ TANKLL48, +/* function */ v_tankll48, +/* spawnflags */ VHF_DMGSHAKE, +/* mins,maxs */ '-185 -185 2', '185 185 136', +/* model */ "models/vehicles/tankll48.md3", +/* head_model */ "models/vehicles/tankll48_turret.md3", +/* hud_model */ "null", +/* tags */ "tag_turret", "tag_camera", "", +/* netname */ "tankll48", +/* fullname */ _("LL48") +); +#else +#ifdef SVQC ++#include "../../effects.qh" ++ +float autocvar_g_vehicle_tankll48; + +float autocvar_g_vehicle_tankll48_respawntime; + +float autocvar_g_vehicle_tankll48_speed_stop; +float autocvar_g_vehicle_tankll48_speed_strafe; +float autocvar_g_vehicle_tankll48_speed_walk; +float autocvar_g_vehicle_tankll48_turnspeed; +float autocvar_g_vehicle_tankll48_turnspeed_strafe; +float autocvar_g_vehicle_tankll48_movement_inertia; + +float autocvar_g_vehicle_tankll48_springlength; +float autocvar_g_vehicle_tankll48_springup; +float autocvar_g_vehicle_tankll48_springblend; +float autocvar_g_vehicle_tankll48_tiltlimit; + +float autocvar_g_vehicle_tankll48_head_pitchlimit_down; +float autocvar_g_vehicle_tankll48_head_pitchlimit_up; +float autocvar_g_vehicle_tankll48_head_turnlimit; +float autocvar_g_vehicle_tankll48_head_turnspeed; + +float autocvar_g_vehicle_tankll48_turret_turnlimit; + +float autocvar_g_vehicle_tankll48_health; +float autocvar_g_vehicle_tankll48_health_regen; +float autocvar_g_vehicle_tankll48_health_regen_pause; + +float autocvar_g_vehicle_tankll48_shield; +float autocvar_g_vehicle_tankll48_shield_regen; +float autocvar_g_vehicle_tankll48_shield_regen_pause; + +vector autocvar_g_vehicle_tankll48_bouncepain; + +float autocvar_g_vehicle_tankll48_cannon_damage; +float autocvar_g_vehicle_tankll48_cannon_ammo; +float autocvar_g_vehicle_tankll48_cannon_speed; +float autocvar_g_vehicle_tankll48_cannon_edgedamage; +float autocvar_g_vehicle_tankll48_cannon_force; +float autocvar_g_vehicle_tankll48_cannon_radius; +float autocvar_g_vehicle_tankll48_cannon_damage2; +float autocvar_g_vehicle_tankll48_cannon_speedaccel; +float autocvar_g_vehicle_tankll48_cannon_ammo_max; +float autocvar_g_vehicle_tankll48_cannon_ammo_regen; +float autocvar_g_vehicle_tankll48_cannon_ammo_regen_pause; + +void tankll48_cannon_explode() +{ + self.event_damage = func_null; + self.takedamage = DAMAGE_NO; + + RadiusDamage (self, self.realowner, autocvar_g_vehicle_tankll48_cannon_damage, autocvar_g_vehicle_tankll48_cannon_edgedamage, autocvar_g_vehicle_tankll48_cannon_radius, world, world, autocvar_g_vehicle_tankll48_cannon_force, self.projectiledeathtype, other); + + remove (self); +} + +void tankll48_cannon_touch() +{ + if(WarpZone_Projectile_Touch()) + if(wasfreed(self)) + return; + + //if(other.solid != SOLID_BSP) + // return; + + tankll48_cannon_explode(); +} + +void tankll48_cannon_think() +{ + if(self.cnt <= time) + { + remove(self); + return; + } + + self.cnt = vlen(self.velocity); + self.wait = self.cnt * sys_frametime; + self.pos1 = normalize(self.velocity); + + tracebox(self.origin, self.mins, self.maxs, self.origin + self.pos1 * (2 * self.wait), MOVE_NORMAL, self); + if(IS_PLAYER(trace_ent)) + Damage (trace_ent, self, self.realowner, autocvar_g_vehicle_tankll48_cannon_damage2, self.projectiledeathtype, self.origin, normalize(self.origin - other.origin) * autocvar_g_vehicle_tankll48_cannon_force); + + self.velocity = self.pos1 * (self.cnt + (autocvar_g_vehicle_tankll48_cannon_speedaccel * sys_frametime)); + + UpdateCSQCProjectile(self); + self.nextthink = time; +} + +float tankll48_frame() +{ + vector ad, vf; + entity player, tank; + float ftmp; + + if(intermission_running) + { + self.vehicle.velocity = '0 0 0'; + self.vehicle.avelocity = '0 0 0'; + return 1; + } + + player = self; + tank = self.vehicle; + self = tank; + + vehicles_painframe(); + + player.BUTTON_ZOOM = 0; + player.BUTTON_CROUCH = 0; + player.switchweapon = 0; + player.vehicle_weapon2mode = tank.vehicle_weapon2mode; + + +#if 1 // 0 to enable per-gun impact aux crosshairs + // Avarage gun impact point's -> aux cross + ad = gettaginfo(tank.tur_head, gettagindex(tank.tur_head, "tag_cannon_pivot")); + vf = v_forward; + ad += gettaginfo(tank.tur_head, gettagindex(tank.tur_head, "tag_cannon_pivot_0")); + vf += v_forward; + ad = ad * 0.5; + v_forward = vf * 0.5; + traceline(ad, ad + v_forward * MAX_SHOT_DISTANCE, MOVE_NORMAL, tank); + UpdateAuxiliaryXhair(player, trace_endpos, ('1 0 0' * player.vehicle_reload1) + ('0 1 0' * (1 - player.vehicle_reload1)), 0); +#else + ad = gettaginfo(tank.gun1, gettagindex(tank.gun1, "barrels")); + traceline(ad, ad + v_forward * MAX_SHOT_DISTANCE, MOVE_NORMAL, tank); + UpdateAuxiliaryXhair(player, trace_endpos, ('1 0 0' * player.vehicle_reload1) + ('0 1 0' * (1 - player.vehicle_reload1)), 0); + vf = ad; + ad = gettaginfo(tank.gun2, gettagindex(tank.gun2, "barrels")); + traceline(ad, ad + v_forward * MAX_SHOT_DISTANCE, MOVE_NORMAL, tank); + UpdateAuxiliaryXhair(player, trace_endpos, ('1 0 0' * player.vehicle_reload1) + ('0 1 0' * (1 - player.vehicle_reload1)), 1); + ad = 0.5 * (ad + vf); +#endif + + crosshair_trace(player); + ad = vectoangles(normalize(trace_endpos - ad)); + ad = AnglesTransform_ToAngles(AnglesTransform_LeftDivide(AnglesTransform_FromAngles(tank.angles), AnglesTransform_FromAngles(ad))) - tank.tur_head.angles; - ad = AnglesTransform_Normalize(ad, TRUE); ++ ad = AnglesTransform_Normalize(ad, true); + //UpdateAuxiliaryXhair(player, trace_endpos, ('1 0 0' * player.vehicle_reload2) + ('0 1 0' * (1 - player.vehicle_reload2)), 2); + + // rotate turret and head + ftmp = autocvar_g_vehicle_tankll48_head_turnspeed * sys_frametime; + ad_y = bound(-ftmp, ad_y, ftmp); + tank.gun3.angles_y = bound(autocvar_g_vehicle_tankll48_turret_turnlimit * -1, tank.gun3.angles_y + ad_y, autocvar_g_vehicle_tankll48_turret_turnlimit); + tank.tur_head.angles_y = bound(autocvar_g_vehicle_tankll48_head_turnlimit * -1, tank.tur_head.angles_y + ad_y, autocvar_g_vehicle_tankll48_head_turnlimit); + + + // Pitch head + ad_x = bound(ftmp * -1, ad_x, ftmp); + tank.tur_head.angles_x = bound(autocvar_g_vehicle_tankll48_head_pitchlimit_down, tank.tur_head.angles_x + ad_x, autocvar_g_vehicle_tankll48_head_pitchlimit_up); + + + //fixedmakevectors(tank.angles); + makevectors(tank.angles + '-2 0 0' * tank.angles_x); + + movelib_groundalign4point(autocvar_g_vehicle_tankll48_springlength, autocvar_g_vehicle_tankll48_springup, autocvar_g_vehicle_tankll48_springblend, autocvar_g_vehicle_tankll48_tiltlimit); + + if(tank.flags & FL_ONGROUND) + { + if(tank.frame == 4 && self.tur_head.wait != 0) + { + tank.frame = 5; + } + + if(vlen(player.movement) == 0) + { + if(self.sound_nexttime < time || self.delay != 3) + { + self.delay = 3; + self.sound_nexttime = time + 6.009; //soundlength("vehicles/tankll48_idle.wav"); + //dprint("tankll48_idle:", ftos(soundlength("vehicles/tankll48_idle.wav")), "\n"); + sound (self, CH_TRIGGER_SINGLE, "machines/generator_loop_pitchdown.wav", VOL_VEHICLEENGINE, ATTEN_NORM); + } + movelib_beak_simple(autocvar_g_vehicle_tankll48_speed_stop); + tank.frame = 5; + } + else + { + // Turn Body + if(player.movement_x == 0 && player.movement_y != 0) + ftmp = autocvar_g_vehicle_tankll48_turnspeed_strafe * sys_frametime; + else + ftmp = autocvar_g_vehicle_tankll48_turnspeed * sys_frametime; + + ftmp = bound(-ftmp, tank.tur_head.angles_y, ftmp); + tank.angles_y = anglemods(tank.angles_y + ftmp); + tank.tur_head.angles_y -= ftmp; + + if(player.movement_x != 0) + { + if(player.movement_x > 0) + { + player.movement_x = 1; + tank.frame = 0; + } + else if(player.movement_x < 0) + { + player.movement_x = -1; + tank.frame = 1; + } + player.movement_y = 0; + movelib_move_simple(normalize(v_forward * player.movement_x),autocvar_g_vehicle_tankll48_speed_walk,autocvar_g_vehicle_tankll48_movement_inertia); + + if(self.sound_nexttime < time || self.delay != 1) + { + self.delay = 1; + self.sound_nexttime = time + 3.991; //soundlength("vehicles/tankll48_walk.wav"); + sound (self, CH_TRIGGER_SINGLE, "machines/generator_loop_speedup_pitchdown.wav", VOL_VEHICLEENGINE, ATTEN_NORM); + //dprint("tankll48_walk:", ftos(soundlength("vehicles/tankll48_walk.wav")), "\n"); + } + } + else if(player.movement_y != 0) + { + if(player.movement_y < 0) + { + player.movement_y = -1; + tank.frame = 2; + } + else if(player.movement_y > 0) + { + player.movement_y = 1; + tank.frame = 3; + } + + movelib_move_simple(normalize(v_right * player.movement_y),autocvar_g_vehicle_tankll48_speed_strafe,autocvar_g_vehicle_tankll48_movement_inertia); + if(self.sound_nexttime < time || self.delay != 2) + { + self.delay = 2; + self.sound_nexttime = time + 3.991; //soundlength("vehicles/tankll48_strafe.wav"); + sound (self, CH_TRIGGER_SINGLE, "machines/generator_loop_speedup_pitchdown.wav", VOL_VEHICLEENGINE, ATTEN_NORM); + //dprint("tankll48_strafe:", ftos(soundlength("vehicles/tankll48_strafe.wav")), "\n"); + } + } + } + } + + self.angles_x = bound(-autocvar_g_vehicle_tankll48_tiltlimit, self.angles_x, autocvar_g_vehicle_tankll48_tiltlimit); + self.angles_z = bound(-autocvar_g_vehicle_tankll48_tiltlimit, self.angles_z, autocvar_g_vehicle_tankll48_tiltlimit); + + if(!forbidWeaponUse(player)) + if(player.BUTTON_ATCK && tank.vehicle_ammo1 >= autocvar_g_vehicle_tankll48_cannon_ammo && tank.tur_head.attack_finished_single <= time) + { + tank.cnt = time; + entity missile = spawn(); + vector v = gettaginfo(self.gun1, gettagindex(self.gun1, "barrels")); - W_SetupShot_ProjectileSize (player, '-3 -3 -3', '3 3 3', FALSE, 5, W_Sound("campingrifle_fire_morebass"), CH_WEAPON_A, autocvar_g_vehicle_tankll48_cannon_damage); ++ W_SetupShot_ProjectileSize (player, '-3 -3 -3', '3 3 3', false, 5, W_Sound("campingrifle_fire_morebass"), CH_WEAPON_A, autocvar_g_vehicle_tankll48_cannon_damage); + tank.vehicle_ammo1 -= autocvar_g_vehicle_tankll48_cannon_ammo; + w_shotorg = v; + + Send_Effect(EFFECT_ROCKET_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1); + PROJECTILE_MAKETRIGGER(missile); + + missile.owner = tank; + missile.realowner = player; - missile.bot_dodge = TRUE; ++ missile.bot_dodge = true; + missile.bot_dodgerating = autocvar_g_vehicle_tankll48_cannon_damage * 2; + + missile.takedamage = DAMAGE_NO; + missile.event_damage = func_null; - missile.damagedbycontents = TRUE; ++ missile.damagedbycontents = true; + missile.movetype = MOVETYPE_FLY; + + missile.projectiledeathtype = DEATH_VH_TANKLL48; + setsize (missile, '-3 -3 -3', '3 3 3'); // give it some size so it can be shot + + setorigin (missile, w_shotorg - v_forward * 3); // move it back so it hits the wall at the right point + W_SetupProjVelocity_Basic(missile, autocvar_g_vehicle_tankll48_cannon_speed, 0); + + missile.touch = tankll48_cannon_touch; + + missile.think = tankll48_cannon_think; + missile.cnt = time + 15; + missile.nextthink = time; + missile.flags = FL_PROJECTILE; + missile.pos1 = missile.velocity; + - CSQCProjectile(missile, TRUE, PROJECTILE_CANNONBALL, FALSE); ++ CSQCProjectile(missile, true, PROJECTILE_CANNONBALL, false); + } + else + vehicles_regen(tank.cnt, vehicle_ammo1, autocvar_g_vehicle_tankll48_cannon_ammo_max, + autocvar_g_vehicle_tankll48_cannon_ammo_regen_pause, - autocvar_g_vehicle_tankll48_cannon_ammo_regen, frametime, FALSE); ++ autocvar_g_vehicle_tankll48_cannon_ammo_regen, frametime, false); + + if(self.vehicle_flags & VHF_SHIELDREGEN) - vehicles_regen(tank.dmg_time, vehicle_shield, autocvar_g_vehicle_tankll48_shield, autocvar_g_vehicle_tankll48_shield_regen_pause, autocvar_g_vehicle_tankll48_shield_regen, frametime, TRUE); ++ vehicles_regen(tank.dmg_time, vehicle_shield, autocvar_g_vehicle_tankll48_shield, autocvar_g_vehicle_tankll48_shield_regen_pause, autocvar_g_vehicle_tankll48_shield_regen, frametime, true); + + if(self.vehicle_flags & VHF_HEALTHREGEN) - vehicles_regen(tank.dmg_time, vehicle_health, autocvar_g_vehicle_tankll48_health, autocvar_g_vehicle_tankll48_health_regen_pause, autocvar_g_vehicle_tankll48_health_regen, frametime, FALSE); ++ vehicles_regen(tank.dmg_time, vehicle_health, autocvar_g_vehicle_tankll48_health, autocvar_g_vehicle_tankll48_health_regen_pause, autocvar_g_vehicle_tankll48_health_regen, frametime, false); + + player.BUTTON_ATCK = player.BUTTON_ATCK2 = 0; + + if(tank.gun2.cnt <= time) + player.vehicle_reload2 = 100; + else + player.vehicle_reload2 = 100 - ((tank.gun2.cnt - time) / tank.attack_finished_single) * 100; + + setorigin(player, tank.origin + '0 0 1' * tank.maxs_z); + player.velocity = tank.velocity; + + VEHICLE_UPDATE_PLAYER(player, health, tankll48); + + if(self.vehicle_flags & VHF_HASSHIELD) + VEHICLE_UPDATE_PLAYER(player, shield, tankll48); + + self = player; + return 1; +} + +void tankll48_exit(float eject) +{ + entity e; + vector spot; + + e = findchain(classname,"tankll48_rocket"); + while(e) + { + if(e.owner == self.owner) + { + e.realowner = self.owner; + e.owner = world; + } + e = e.chain; + } + + self.think = vehicles_think; + self.nextthink = time; + self.frame = 5; + self.movetype = MOVETYPE_WALK; + + if(!self.owner) + return; + + makevectors(self.angles); + if(eject) + { + spot = self.origin + v_forward * 300 + '0 0 64'; + spot = vehicles_findgoodexit(spot); + setorigin(self.owner , spot); + self.owner.velocity = (v_up + v_forward * 0.25) * 750; + self.owner.oldvelocity = self.owner.velocity; + } + else + { + if(vlen(self.velocity) > autocvar_g_vehicle_tankll48_speed_strafe) + { + self.owner.velocity = normalize(self.velocity) * vlen(self.velocity); + self.owner.velocity_z += 200; + spot = self.origin + v_forward * 328 + '0 0 64'; + spot = vehicles_findgoodexit(spot); + } + else + { + self.owner.velocity = self.velocity * 0.5; + self.owner.velocity_z += 10; + spot = self.origin + v_forward * 356 + '0 0 64'; + spot = vehicles_findgoodexit(spot); + } + self.owner.oldvelocity = self.owner.velocity; + setorigin(self.owner , spot); + } + + antilag_clear(self.owner); + self.owner = world; +} + +void tankll48_headfade() +{ + self.think = tankll48_headfade; + self.nextthink = self.fade_time; + self.alpha = 1 - (time - self.fade_time) * self.fade_rate; + + if(self.cnt < time || self.alpha < 0.1) + { + if(self.alpha > 0.1) + { + sound (self, CH_SHOTS, W_Sound("rocket_impact"), VOL_BASE, ATTEN_NORM); + Send_Effect(EFFECT_EXPLOSION_BIG, self.origin + '0 0 100', '0 0 0', 1); + } + remove(self); + } +} + +void tankll48_blowup() +{ + if(self.cnt > time) + { + if(random() < 0.1) + { + sound (self, CH_SHOTS, W_Sound("rocket_impact"), VOL_BASE, ATTEN_NORM); + Send_Effect(EFFECT_EXPLOSION_SMALL, randomvec() * 80 + (self.origin + '0 0 100'), '0 0 0', 1); + } + self.nextthink = time + 0.1; + return; + } + + entity h, g1, b; + b = spawn(); + h = spawn(); + g1 = spawn(); + + setmodel(b, "models/vehicles/tankll48.md3"); + setmodel(h, "models/vehicles/tankll48_turret.md3"); + setmodel(g1, "models/vehicles/tankll48_cannon.md3"); + + setorigin(b, self.origin); + b.frame = 11; + b.angles = self.angles; + setsize(b, self.mins, self.maxs); + + setorigin(h, gettaginfo(self, gettagindex(self, "tag_head"))); + h.movetype = MOVETYPE_BOUNCE; + h.solid = SOLID_BBOX; + h.velocity = v_up * (500 + random() * 500) + randomvec() * 128; + h.modelflags = MF_ROCKET; + h.effects = EF_FLAME | EF_LOWPRECISION; + h.avelocity = randomvec() * 360; + + h.alpha = 1; + h.cnt = time + (3.5 * random()); + h.fade_rate = 1 / min(self.respawntime, 10); + h.fade_time = time; + h.think = tankll48_headfade; + h.nextthink = time; + + setorigin(g1, gettaginfo(self.tur_head, gettagindex(self.tur_head, "tag_hardpoint01"))); + g1.movetype = MOVETYPE_TOSS; + g1.solid = SOLID_CORPSE; + g1.velocity = v_forward * 700 + (randomvec() * 32); + g1.avelocity = randomvec() * 180; + + h.colormod = b.colormod = g1.colormod = '-2 -2 -2'; + + SUB_SetFade(b, time + 5, min(self.respawntime, 1)); + //SUB_SetFade(h, time, min(self.respawntime, 10)); + SUB_SetFade(g1, time, min(self.respawntime, 10)); + + RadiusDamage (self, self.enemy, 250, 15, 250, world, world, 250, DEATH_VH_TANK_DEATH, world); + + self.alpha = self.tur_head.alpha = self.gun1.alpha = self.gun2.alpha = -1; + self.movetype = MOVETYPE_NONE; + self.deadflag = DEAD_DEAD; + self.solid = SOLID_NOT; + self.tur_head.effects &= ~EF_FLAME; + self.vehicle_hudmodel.viewmodelforclient = self; +} + +void spawnfunc_vehicle_tankll48() +{ + if(!autocvar_g_vehicles_extra) { remove(self); return; } + if(!autocvar_g_vehicle_tankll48) { remove(self); return; } - if(!vehicle_initialize(VEH_TANKLL48, FALSE)) { remove(self); return; } ++ if(!vehicle_initialize(VEH_TANKLL48, false)) { remove(self); return; } +} + +float v_tankll48(float req) +{ + switch(req) + { + case VR_IMPACT: + { + if(autocvar_g_vehicle_tankll48_bouncepain) + vehicles_impact(autocvar_g_vehicle_tankll48_bouncepain_x, autocvar_g_vehicle_tankll48_bouncepain_y, autocvar_g_vehicle_tankll48_bouncepain_z); + - return TRUE; ++ return true; + } + case VR_ENTER: + { + self.movetype = MOVETYPE_WALK; + CSQCVehicleSetup(self.owner, 0); + self.owner.vehicle_health = (self.vehicle_health / autocvar_g_vehicle_tankll48_health) * 100; + self.owner.vehicle_shield = (self.vehicle_shield / autocvar_g_vehicle_tankll48_shield) * 100; + self.gun2.colormap = self.colormap; + + if(self.owner.flagcarried) + { + setattachment(self.owner.flagcarried, self.tur_head, ""); + setorigin(self.owner.flagcarried, '-20 0 120'); + } + - return TRUE; ++ return true; + } + case VR_THINK: + { + if(self.flags & FL_ONGROUND) + movelib_beak_simple(autocvar_g_vehicle_tankll48_speed_stop); + - return TRUE; ++ return true; + } + case VR_DEATH: + { + self.health = 0; + self.event_damage = func_null; + self.takedamage = DAMAGE_NO; + self.touch = func_null; + self.cnt = 3.4 + time + random() * 2; + self.think = tankll48_blowup; + self.nextthink = time; + self.deadflag = DEAD_DYING; + self.frame = 5; + self.tur_head.effects |= EF_FLAME; + self.colormod = self.tur_head.colormod = self.gun1.colormod = self.gun2.colormod = '-1 -1 -1'; + self.frame = 10; + self.movetype = MOVETYPE_TOSS; + + CSQCModel_UnlinkEntity(); // networking the death scene would be a nightmare + - return TRUE; ++ return true; + } + case VR_SPAWN: + { + if(!self.gun1) + { + self.gun3 = spawn(); // Will be an angle stabilizer for the rotating turret + self.gun3.alpha = -1; + setmodel(self.gun3, "null"); + setattachment(self.gun3, self.tur_head, "tag_cannon_pivot"); + + self.gun1 = spawn(); + self.gun2 = spawn(); + setmodel(self.gun1, "null"); + setmodel(self.gun2, "models/vehicles/tankll48_cannon.md3"); + setattachment(self.gun1, self.tur_head, "tag_gunpivot2"); + setattachment(self.gun2, self.tur_head, "tag_cannon_pivot"); + self.gravity = 2; + self.mass = 5000; + } + + self.frame = 5; + self.tur_head.frame = 1; + self.movetype = MOVETYPE_WALK; + self.solid = SOLID_SLIDEBOX; + self.alpha = self.tur_head.alpha = self.gun1.alpha = self.gun2.alpha = 1; + self.colormod = self.tur_head.colormod = self.gun1.colormod = self.gun2.colormod = '1 1 1'; + self.gun2.colormap = self.colormap; + self.tur_head.angles = '0 0 0'; + self.vehicle_exit = tankll48_exit; + + setorigin(self, self.pos1 + '0 0 128'); + self.angles = self.pos2; + self.damageforcescale = 0.03; + self.vehicle_health = autocvar_g_vehicle_tankll48_health; + self.vehicle_shield = autocvar_g_vehicle_tankll48_shield; + + self.PlayerPhysplug = tankll48_frame; + - return TRUE; ++ return true; + } + case VR_SETUP: + { + if(autocvar_g_vehicle_tankll48_shield) + self.vehicle_flags |= VHF_HASSHIELD; + + if(autocvar_g_vehicle_tankll48_shield_regen) + self.vehicle_flags |= VHF_SHIELDREGEN; + + if(autocvar_g_vehicle_tankll48_health_regen) + self.vehicle_flags |= VHF_HEALTHREGEN; + + self.respawntime = autocvar_g_vehicle_tankll48_respawntime; + self.vehicle_health = autocvar_g_vehicle_tankll48_health; + self.vehicle_shield = autocvar_g_vehicle_tankll48_shield; + self.max_health = self.vehicle_health; - self.pushable = TRUE; // tankll48 can use jumppads ++ self.pushable = true; // tankll48 can use jumppads + setorigin(self.tur_head, '0 0 110'); + setorigin(self.vehicle_hudmodel, '0 0 50'); + - return TRUE; ++ return true; + } + case VR_PRECACHE: + { + precache_model ("models/vhshield.md3"); + precache_model ("models/vehicles/tankll48.md3"); + precache_model ("models/vehicles/tankll48_turret.md3"); + precache_model ( "models/uziflash.md3"); + + precache_model ( "models/vehicles/tankll48_cannon.md3"); + + precache_sound (W_Sound("rocket_impact")); + precache_sound (W_Sound("campingrifle_fire_morebass")); + + precache_sound ( "machines/generator_loop_speedup_pitchdown.wav"); + precache_sound ( "machines/generator_loop_pitchdown.wav"); + - return TRUE; ++ return true; + } + } + - return TRUE; ++ return true; +} + +#endif // SVQC +#ifdef CSQC +#define tank_ico "gfx/vehicles/tankll48.tga" + +float v_tankll48(float req) +{ + switch(req) + { + case VR_HUD: + { + if(autocvar_r_letterbox) - return TRUE; ++ return true; + + vector picsize, hudloc = '0 0 0', pic2size, picloc; + + // Fetch health & ammo stats + HUD_GETVEHICLESTATS + + picsize = draw_getimagesize(hud_bg) * autocvar_cl_vehicles_hudscale; + hudloc_y = vid_conheight - picsize_y; + hudloc_x = vid_conwidth * 0.5 - picsize_x * 0.5; + + drawpic(hudloc, hud_bg, picsize, '1 1 1', autocvar_cl_vehicles_hudalpha, DRAWFLAG_NORMAL); + + ammo1 *= 0.01; + shield *= 0.01; + vh_health *= 0.01; + reload2 *= 0.01; + + pic2size = draw_getimagesize(tank_ico) * (autocvar_cl_vehicles_hudscale * 0.8); + picloc = picsize * 0.5 - pic2size * 0.5; + if(vh_health < 0.25) + drawpic(hudloc + picloc, tank_ico, pic2size, '1 0 0' + '0 1 1' * sin(time * 8), 1, DRAWFLAG_NORMAL); + else + drawpic(hudloc + picloc, tank_ico, pic2size, '1 1 1' * vh_health + '1 0 0' * (1 - vh_health), 1, DRAWFLAG_NORMAL); + //drawpic(hudloc + picloc, tank_rkt, pic2size, '1 1 1' * reload2 + '1 0 0' * (1 - reload2), 1, DRAWFLAG_NORMAL); + //drawpic(hudloc + picloc, tank_mgun, pic2size, '1 1 1' * ammo1 + '1 0 0' * (1 - ammo1), 1, DRAWFLAG_NORMAL); + drawpic(hudloc + picloc, hud_sh, pic2size, '1 1 1', shield, DRAWFLAG_NORMAL); + + // Health bar + picsize = draw_getimagesize(hud_hp_bar) * autocvar_cl_vehicles_hudscale; + picloc = '69 69 0' * autocvar_cl_vehicles_hudscale; + drawsetcliparea(hudloc_x + picloc_x + (picsize_x * (1 - vh_health)), 0, vid_conwidth, vid_conheight); + drawpic(hudloc + picloc, hud_hp_bar, picsize, '1 1 1', 1 , DRAWFLAG_NORMAL); + drawresetcliparea(); + // .. and icon + picsize = draw_getimagesize(hud_hp_ico) * autocvar_cl_vehicles_hudscale; + picloc = '37 65 0' * autocvar_cl_vehicles_hudscale; + if(vh_health < 0.25) + { + if(alarm1time < time) + { + alarm1time = time + 2; + vehicle_alarm(self, CH_PAIN_SINGLE, "vehicles/alarm.wav"); + } + drawpic(hudloc + picloc, hud_hp_ico, picsize, '1 0 0' + '0 1 1' * sin(time * 8), 1, DRAWFLAG_NORMAL); + } + else + { + drawpic(hudloc + picloc, hud_hp_ico, picsize, '1 1 1', 1, DRAWFLAG_NORMAL); + if(alarm1time) + { + vehicle_alarm(self, CH_PAIN_SINGLE, "misc/null.wav"); + alarm1time = 0; + } + } + // Shield bar + picsize = draw_getimagesize(hud_sh_bar) * autocvar_cl_vehicles_hudscale; + picloc = '69 140 0' * autocvar_cl_vehicles_hudscale; + drawsetcliparea(hudloc_x + picloc_x + (picsize_x * (1 - shield)), 0, vid_conwidth, vid_conheight); + drawpic(hudloc + picloc, hud_sh_bar, picsize, '1 1 1', 1, DRAWFLAG_NORMAL); + drawresetcliparea(); + // .. and icon + picloc = '40 136 0' * autocvar_cl_vehicles_hudscale; + picsize = draw_getimagesize(hud_sh_ico) * autocvar_cl_vehicles_hudscale; + if(shield < 0.25) + { + if(alarm2time < time) + { + alarm2time = time + 1; + vehicle_alarm(self, CH_PAIN_SINGLE, "vehicles/alarm_shield.wav"); + } + drawpic(hudloc + picloc, hud_sh_ico, picsize, '1 0 0' + '0 1 1' * sin(time * 8), 1, DRAWFLAG_NORMAL); + } + else + { + drawpic(hudloc + picloc, hud_sh_ico, picsize, '1 1 1', 1, DRAWFLAG_NORMAL); + if(alarm2time) + { + vehicle_alarm(self, CH_PAIN_SINGLE, "misc/null.wav"); + alarm2time = 0; + } + } + + // Minigun bar + picsize = draw_getimagesize(hud_ammo1_bar) * autocvar_cl_vehicles_hudscale; + picloc = '450 69 0' * autocvar_cl_vehicles_hudscale; + drawsetcliparea(hudloc_x + picloc_x, picloc_y, picsize_x * ammo1, vid_conheight); + drawpic(hudloc + picloc, hud_ammo1_bar, picsize, '1 1 1', 1, DRAWFLAG_NORMAL); + drawresetcliparea(); + // .. and icon + picsize = draw_getimagesize(hud_ammo2_ico) * autocvar_cl_vehicles_hudscale; + picloc = '664 60 0' * autocvar_cl_vehicles_hudscale; + if(ammo1 < 0.2) + drawpic(hudloc + picloc, hud_ammo2_ico, picsize, '1 0 0' + '0 1 1' * sin(time * 8), 1, DRAWFLAG_NORMAL); + else + drawpic(hudloc + picloc, hud_ammo2_ico, picsize, '1 1 1', 1, DRAWFLAG_NORMAL); + + if (scoreboard_showscores) + HUD_DrawScoreboard(); + - return TRUE; ++ return true; + } + case VR_SETUP: + { + // Minigun1 + AuxiliaryXhair[0].axh_image = "gfx/vehicles/axh-ring.tga"; + AuxiliaryXhair[0].axh_scale = 0.25; + // Minigun2 + AuxiliaryXhair[1].axh_image = "gfx/vehicles/axh-ring.tga"; + AuxiliaryXhair[1].axh_scale = 0.25; + // Rocket + AuxiliaryXhair[2].axh_image = "gfx/vehicles/axh-special1.tga"; + AuxiliaryXhair[2].axh_scale = 0.5; + - return TRUE; ++ return true; + } + case VR_PRECACHE: + { - return TRUE; ++ return true; + } + } + - return TRUE; ++ return true; +} + +#endif // CSQC +#endif // REGISTER_VEHICLE diff --cc qcsrc/common/vehicles/unit/yugo.qc index 7facf7596,000000000..634b37096 mode 100644,000000..100644 --- a/qcsrc/common/vehicles/unit/yugo.qc +++ b/qcsrc/common/vehicles/unit/yugo.qc @@@ -1,661 -1,0 +1,663 @@@ +#ifdef REGISTER_VEHICLE +REGISTER_VEHICLE( +/* VEH_##id */ YUGO, +/* function */ v_yugo, +/* spawnflags */ 0, +/* mins,maxs */ '-50 -50 -50', '50 50 50', +/* model */ "models/vehicles/yugo.dpm", +/* head_model */ "null", +/* hud_model */ "null", +/* tags */ "", "", "tag_viewport", +/* netname */ "yugo", +/* fullname */ _("Yugo") +); +#else +#ifdef SVQC ++#include "../../effects.qh" ++ +float autocvar_g_vehicle_yugo; + +float autocvar_g_vehicle_yugo_speed_afterburn; +float autocvar_g_vehicle_yugo_afterburn_cost; + +var float autocvar_g_vehicle_yugo_cloak_cost = 20; +var float autocvar_g_vehicle_yugo_supercloak_cost = 1000; + +float autocvar_g_vehicle_yugo_anglestabilizer; +float autocvar_g_vehicle_yugo_downforce; + +float autocvar_g_vehicle_yugo_speed_forward; +float autocvar_g_vehicle_yugo_speed_strafe; +float autocvar_g_vehicle_yugo_springlength; +float autocvar_g_vehicle_yugo_upforcedamper; +float autocvar_g_vehicle_yugo_friction; + +float autocvar_g_vehicle_yugo_hovertype; +float autocvar_g_vehicle_yugo_hoverpower; +float autocvar_g_vehicle_yugo_hoverpower_idle; + +float autocvar_g_vehicle_yugo_turnroll; +float autocvar_g_vehicle_yugo_turnspeed; +float autocvar_g_vehicle_yugo_pitchspeed; + +float autocvar_g_vehicle_yugo_energy; +float autocvar_g_vehicle_yugo_energy_regen; +float autocvar_g_vehicle_yugo_energy_regen_pause; + +float autocvar_g_vehicle_yugo_health; +float autocvar_g_vehicle_yugo_health_regen; +float autocvar_g_vehicle_yugo_health_regen_pause; + +float autocvar_g_vehicle_yugo_shield; +float autocvar_g_vehicle_yugo_shield_regen; +float autocvar_g_vehicle_yugo_shield_regen_pause; + +float autocvar_g_vehicle_yugo_respawntime; + +float autocvar_g_vehicle_yugo_blowup_radius; +float autocvar_g_vehicle_yugo_blowup_coredamage; +float autocvar_g_vehicle_yugo_blowup_edgedamage; +float autocvar_g_vehicle_yugo_blowup_forceintensity; + +float autocvar_g_vehicle_yugo_bouncefactor; +float autocvar_g_vehicle_yugo_bouncestop; +vector autocvar_g_vehicle_yugo_bouncepain; + +var vector yugo_force_from_tag(string tag_name, float spring_length, float max_power); + +void yugo_align4point(float _delta) +{ + vector push_vector; + float fl_push, fr_push, bl_push, br_push; + float hoverpower = ((self.owner) ? autocvar_g_vehicle_yugo_hoverpower : autocvar_g_vehicle_yugo_hoverpower_idle); + + push_vector = yugo_force_from_tag("tag_engine_fr", autocvar_g_vehicle_yugo_springlength, hoverpower); + fr_push = force_fromtag_normpower; + //vehicles_sweap_collision(force_fromtag_origin, self.velocity, _delta, v_add, autocvar_g_vehicle_yugo_collision_multiplier); + + push_vector += yugo_force_from_tag("tag_engine_fl", autocvar_g_vehicle_yugo_springlength, hoverpower); + fl_push = force_fromtag_normpower; + //vehicles_sweap_collision(force_fromtag_origin, self.velocity, _delta, v_add, autocvar_g_vehicle_yugo_collision_multiplier); + + push_vector += yugo_force_from_tag("tag_engine_br", autocvar_g_vehicle_yugo_springlength, hoverpower); + br_push = force_fromtag_normpower; + //vehicles_sweap_collision(force_fromtag_origin, self.velocity, _delta, v_add, autocvar_g_vehicle_yugo_collision_multiplier); + + push_vector += yugo_force_from_tag("tag_engine_bl", autocvar_g_vehicle_yugo_springlength, hoverpower); + bl_push = force_fromtag_normpower; + //vehicles_sweap_collision(force_fromtag_origin, self.velocity, _delta, v_add, autocvar_g_vehicle_yugo_collision_multiplier); + + self.velocity += push_vector * _delta; + + if(pointcontents(self.origin - '0 0 64') == CONTENT_WATER) + self.velocity_z += 200; + + // Anti ocilation + if(self.velocity_z > 0) + self.velocity_z *= 1 - autocvar_g_vehicle_yugo_upforcedamper * _delta; + + push_vector_x = (fl_push - bl_push); + push_vector_x += (fr_push - br_push); + push_vector_x *= 360; + + push_vector_z = (fr_push - fl_push); + push_vector_z += (br_push - bl_push); + push_vector_z *= 360; + + // Apply angle diffrance + self.angles_z += push_vector_z * _delta; + self.angles_x += push_vector_x * _delta; + + // Apply stabilizer + self.angles_x *= 1 - (autocvar_g_vehicle_yugo_anglestabilizer * _delta); + self.angles_z *= 1 - (autocvar_g_vehicle_yugo_anglestabilizer * _delta); +} + +float yugo_frame() +{ + entity player, yugo; + vector df; + float ftmp; + + if(intermission_running) + { + self.vehicle.velocity = '0 0 0'; + self.vehicle.avelocity = '0 0 0'; + return 1; + } + + player = self; + yugo = self.vehicle; + self = yugo; + + player.BUTTON_ZOOM = player.BUTTON_CROUCH = 0; + + vehicles_painframe(); + + if(yugo.deadflag != DEAD_NO) + { + self = player; + player.BUTTON_ATCK = player.BUTTON_ATCK2 = 0; + return 1; + } + + yugo_align4point(frametime); + + crosshair_trace(player); + + yugo.angles_x *= -1; + + // Yaw + ftmp = autocvar_g_vehicle_yugo_turnspeed * frametime; + ftmp = bound(-ftmp, shortangle_f(player.v_angle_y - yugo.angles_y, yugo.angles_y), ftmp); + yugo.angles_y = anglemods(yugo.angles_y + ftmp); + + // Roll + yugo.angles_z += -ftmp * autocvar_g_vehicle_yugo_turnroll * frametime; + + // Pitch + ftmp = autocvar_g_vehicle_yugo_pitchspeed * frametime; + ftmp = bound(-ftmp, shortangle_f(player.v_angle_x - yugo.angles_x, yugo.angles_x), ftmp); + yugo.angles_x = bound(-30, anglemods(yugo.angles_x + ftmp), 30); + + makevectors(yugo.angles); + yugo.angles_x *= -1; + + //ftmp = yugo.velocity_z; + df = yugo.velocity * -autocvar_g_vehicle_yugo_friction; + //yugo.velocity_z = ftmp; + + if(vlen(player.movement) != 0) + { + if(player.movement_x) + df += v_forward * ((player.movement_x > 0) ? autocvar_g_vehicle_yugo_speed_forward : -autocvar_g_vehicle_yugo_speed_forward); + + if(player.movement_y) + df += v_right * ((player.movement_y > 0) ? autocvar_g_vehicle_yugo_speed_strafe : -autocvar_g_vehicle_yugo_speed_strafe); + + if(self.sound_nexttime < time || self.sounds != 1) + { + self.sounds = 1; + self.sound_nexttime = time + 10.922667; //soundlength("vehicles/yugo_move.wav"); + sound (self, CH_TRIGGER_SINGLE, "vehicles/racer_move.wav", VOL_VEHICLEENGINE, ATTEN_NORM); + } + } + else + { + if(self.sound_nexttime < time || self.sounds != 0) + { + self.sounds = 0; + self.sound_nexttime = time + 11.888604; //soundlength("vehicles/yugo_idle.wav"); + sound (self, CH_TRIGGER_SINGLE, "vehicles/racer_idle.wav", VOL_VEHICLEENGINE, ATTEN_NORM); + } + } + + // Afterburn + if (player.BUTTON_JUMP && yugo.vehicle_energy >= (autocvar_g_vehicle_yugo_afterburn_cost * frametime)) + { + if(time - yugo.wait > 0.2) + pointparticles(particleeffectnum("yugo_booster_smoke"), self.origin - v_forward * 32, v_forward * vlen(self.velocity), 1); + + yugo.wait = time; + yugo.vehicle_energy -= autocvar_g_vehicle_yugo_afterburn_cost * frametime; + df += (v_forward * autocvar_g_vehicle_yugo_speed_afterburn); + + if(yugo.invincible_finished < time) + { + traceline(yugo.origin, yugo.origin - '0 0 256', MOVE_NORMAL, self); + if(trace_fraction != 1.0) + pointparticles(particleeffectnum("smoke_small"), trace_endpos, '0 0 0', 1); + + yugo.invincible_finished = time + 0.1 + (random() * 0.1); + } + + if(yugo.strength_finished < time) + { + yugo.strength_finished = time + 10.922667; //soundlength("vehicles/yugo_boost.wav"); + sound (yugo.tur_head, CH_TRIGGER_SINGLE, "vehicles/racer_boost.wav", VOL_VEHICLEENGINE, ATTEN_NORM); + } + } + else + { + yugo.strength_finished = 0; + sound (yugo.tur_head, CH_TRIGGER_SINGLE, "misc/null.wav", VOL_VEHICLEENGINE, ATTEN_NORM); + } + + if(player.BUTTON_ATCK && time - yugo.pain_finished >= 0.5 && yugo.vehicle_energy >= (autocvar_g_vehicle_yugo_cloak_cost * frametime)) + { + yugo.wait = time; + yugo.vehicle_energy -= autocvar_g_vehicle_yugo_cloak_cost * frametime; + + if(yugo.vehicle_energy <= 50) + yugo.alpha = 0.5; + else if(yugo.vehicle_energy <= 100) + yugo.alpha = 0.3; + else + yugo.alpha = 0.1; + } + else if(player.BUTTON_ATCK2 && time - yugo.pain_finished >= 0.5 && yugo.vehicle_energy >= (autocvar_g_vehicle_yugo_supercloak_cost * frametime)) + { + yugo.wait = time; + yugo.vehicle_energy -= autocvar_g_vehicle_yugo_supercloak_cost * frametime; + + yugo.alpha = -1; + } + else + yugo.alpha = 1; + + df -= v_up * (vlen(yugo.velocity) * autocvar_g_vehicle_yugo_downforce); + player.movement = yugo.velocity += df * frametime; + + player.vehicle_reload1 = bound(0, 100 * ((time - yugo.lip) / (yugo.delay - yugo.lip)), 100); + + if(yugo.vehicle_flags & VHF_SHIELDREGEN) - vehicles_regen(yugo.dmg_time, vehicle_shield, autocvar_g_vehicle_yugo_shield, autocvar_g_vehicle_yugo_shield_regen_pause, autocvar_g_vehicle_yugo_shield_regen, frametime, TRUE); ++ vehicles_regen(yugo.dmg_time, vehicle_shield, autocvar_g_vehicle_yugo_shield, autocvar_g_vehicle_yugo_shield_regen_pause, autocvar_g_vehicle_yugo_shield_regen, frametime, true); + + if(yugo.vehicle_flags & VHF_HEALTHREGEN) - vehicles_regen(yugo.dmg_time, vehicle_health, autocvar_g_vehicle_yugo_health, autocvar_g_vehicle_yugo_health_regen_pause, autocvar_g_vehicle_yugo_health_regen, frametime, FALSE); ++ vehicles_regen(yugo.dmg_time, vehicle_health, autocvar_g_vehicle_yugo_health, autocvar_g_vehicle_yugo_health_regen_pause, autocvar_g_vehicle_yugo_health_regen, frametime, false); + + if(yugo.vehicle_flags & VHF_ENERGYREGEN) - vehicles_regen(yugo.wait, vehicle_energy, autocvar_g_vehicle_yugo_energy, autocvar_g_vehicle_yugo_energy_regen_pause, autocvar_g_vehicle_yugo_energy_regen, frametime, FALSE); ++ vehicles_regen(yugo.wait, vehicle_energy, autocvar_g_vehicle_yugo_energy, autocvar_g_vehicle_yugo_energy_regen_pause, autocvar_g_vehicle_yugo_energy_regen, frametime, false); + + + VEHICLE_UPDATE_PLAYER(player, health, yugo); + VEHICLE_UPDATE_PLAYER(player, energy, yugo); + + if(yugo.vehicle_flags & VHF_HASSHIELD) + VEHICLE_UPDATE_PLAYER(player, shield, yugo); + + player.BUTTON_ATCK = player.BUTTON_ATCK2 = 0; + setorigin(player,yugo.origin + '0 0 32'); + player.velocity = yugo.velocity; + + self = player; + return 1; +} + +void yugo_think() +{ + self.nextthink = time; + + float pushdeltatime = time - self.lastpushtime; + if (pushdeltatime > 0.15) pushdeltatime = 0; + self.lastpushtime = time; + if(!pushdeltatime) return; + + tracebox(self.origin, self.mins, self.maxs, self.origin - ('0 0 1' * autocvar_g_vehicle_yugo_springlength), MOVE_NOMONSTERS, self); + + vector df = self.velocity * -autocvar_g_vehicle_yugo_friction; + df_z += (1 - trace_fraction) * ((self.owner) ? autocvar_g_vehicle_yugo_hoverpower : autocvar_g_vehicle_yugo_hoverpower_idle) + sin(time * 2) * (autocvar_g_vehicle_yugo_springlength * 2); + + if(pointcontents(self.origin - '0 0 64') == CONTENT_WATER) + self.velocity_z += 200; + + self.velocity += df * pushdeltatime; + if(self.velocity_z > 0) + self.velocity_z *= 1 - autocvar_g_vehicle_yugo_upforcedamper * pushdeltatime; + + self.angles_x *= 1 - (autocvar_g_vehicle_yugo_anglestabilizer * pushdeltatime); + self.angles_z *= 1 - (autocvar_g_vehicle_yugo_anglestabilizer * pushdeltatime); + + CSQCMODEL_AUTOUPDATE(); +} + +void yugo_exit(float eject) +{ + vector spot; + + self.think = yugo_think; + self.nextthink = time; + self.movetype = MOVETYPE_BOUNCE; + self.alpha = 1; + sound (self.tur_head, CH_TRIGGER_SINGLE, "misc/null.wav", VOL_VEHICLEENGINE, ATTEN_NORM); + + if(!self.owner) + return; + + makevectors(self.angles); + if(eject) + { + spot = self.origin + v_forward * 100 + '0 0 64'; + spot = vehicles_findgoodexit(spot); + setorigin(self.owner , spot); + self.owner.velocity = (v_up + v_forward * 0.25) * 750; + self.owner.oldvelocity = self.owner.velocity; + } + else + { + if(vlen(self.velocity) > 2 * autocvar_sv_maxairspeed) + { + self.owner.velocity = normalize(self.velocity) * autocvar_sv_maxairspeed * 2; + self.owner.velocity_z += 200; + spot = self.origin + v_forward * 32 + '0 0 32'; + spot = vehicles_findgoodexit(spot); + } + else + { + self.owner.velocity = self.velocity * 0.5; + self.owner.velocity_z += 10; + spot = self.origin - v_forward * 200 + '0 0 32'; + spot = vehicles_findgoodexit(spot); + } + self.owner.oldvelocity = self.owner.velocity; + setorigin(self.owner , spot); + } + antilag_clear(self.owner); + self.owner = world; +} + +void yugo_blowup() +{ + self.deadflag = DEAD_DEAD; + self.vehicle_exit(VHEF_NORMAL); + + RadiusDamage (self, self.enemy, autocvar_g_vehicle_yugo_blowup_coredamage, + autocvar_g_vehicle_yugo_blowup_edgedamage, + autocvar_g_vehicle_yugo_blowup_radius, world, world, + autocvar_g_vehicle_yugo_blowup_forceintensity, + DEATH_VH_WAKI_DEATH, world); + + self.nextthink = time + autocvar_g_vehicle_yugo_respawntime; + self.think = vehicles_spawn; + self.movetype = MOVETYPE_NONE; + self.effects = EF_NODRAW; + + self.colormod = '0 0 0'; + self.avelocity = '0 0 0'; + self.velocity = '0 0 0'; + + setorigin(self, self.pos1); +} + +void yugo_blowup_think() +{ + self.nextthink = time; + + if(time >= self.delay) + yugo_blowup(); + + CSQCMODEL_AUTOUPDATE(); +} + +void yugo_deadtouch() +{ + self.avelocity_x *= 0.7; + self.cnt -= 1; + if(self.cnt <= 0) + yugo_blowup(); +} + +void spawnfunc_vehicle_yugo() +{ + if(!autocvar_g_vehicles_extra) { remove(self); return; } + if(!autocvar_g_vehicle_yugo) { remove(self); return; } - if(!vehicle_initialize(VEH_YUGO, FALSE)) { remove(self); return; } ++ if(!vehicle_initialize(VEH_YUGO, false)) { remove(self); return; } +} + +float v_yugo(float req) +{ + switch(req) + { + case VR_IMPACT: + { + if(autocvar_g_vehicle_yugo_bouncepain) + vehicles_impact(autocvar_g_vehicle_yugo_bouncepain_x, autocvar_g_vehicle_yugo_bouncepain_y, autocvar_g_vehicle_yugo_bouncepain_z); - return TRUE; ++ return true; + } + case VR_ENTER: + { + self.movetype = MOVETYPE_WALK; + self.owner.vehicle_health = (self.vehicle_health / autocvar_g_vehicle_yugo_health) * 100; + self.owner.vehicle_shield = (self.vehicle_shield / autocvar_g_vehicle_yugo_shield) * 100; + + if(self.owner.flagcarried) + setorigin(self.owner.flagcarried, '-190 0 96'); + - return TRUE; ++ return true; + } + case VR_THINK: + { + - return TRUE; ++ return true; + } + case VR_DEATH: + { + self.health = 0; + self.event_damage = func_null; + self.solid = SOLID_CORPSE; + self.takedamage = DAMAGE_NO; + self.deadflag = DEAD_DYING; + self.movetype = MOVETYPE_BOUNCE; + self.wait = time; + self.delay = 2 + time + random() * 3; + self.cnt = 1 + random() * 2; + self.touch = yugo_deadtouch; + + Send_Effect(EFFECT_EXPLOSION_MEDIUM, self.origin, '0 0 0', 1); + + if(random() < 0.5) + self.avelocity_z = 32; + else + self.avelocity_z = -32; + + self.avelocity_x = -vlen(self.velocity) * 0.2; + self.velocity += '0 0 700'; + self.colormod = '-0.5 -0.5 -0.5'; + + self.think = yugo_blowup_think; + self.nextthink = time; + - return TRUE; ++ return true; + } + case VR_SPAWN: + { + if(self.scale != 0.5) + { + if(autocvar_g_vehicle_yugo_hovertype) + yugo_force_from_tag = vehicles_force_fromtag_maglev; + else + yugo_force_from_tag = vehicles_force_fromtag_hover; + + // FIXME: this be hakkz, fix the models insted (scale body, add tag_viewport to the hudmodel). + self.scale = 0.5; + setattachment(self.vehicle_hudmodel, self, ""); + setattachment(self.vehicle_viewport, self, "tag_viewport"); + + self.mass = 900; + } + + self.think = yugo_think; + self.nextthink = time; + self.vehicle_health = autocvar_g_vehicle_yugo_health; + self.vehicle_shield = autocvar_g_vehicle_yugo_shield; + + self.movetype = MOVETYPE_TOSS; + self.solid = SOLID_SLIDEBOX; + self.delay = time; + self.scale = 0.5; + self.alpha = 1; + + self.PlayerPhysplug = yugo_frame; + + self.bouncefactor = autocvar_g_vehicle_yugo_bouncefactor; + self.bouncestop = autocvar_g_vehicle_yugo_bouncestop; + self.damageforcescale = 0.5; + self.vehicle_health = autocvar_g_vehicle_yugo_health; + self.vehicle_shield = autocvar_g_vehicle_yugo_shield; + - return TRUE; ++ return true; + } + case VR_SETUP: + { + if(autocvar_g_vehicle_yugo_energy) + if(autocvar_g_vehicle_yugo_energy_regen) + self.vehicle_flags |= VHF_ENERGYREGEN; + + if(autocvar_g_vehicle_yugo_shield) + self.vehicle_flags |= VHF_HASSHIELD; + + if(autocvar_g_vehicle_yugo_shield_regen) + self.vehicle_flags |= VHF_SHIELDREGEN; + + if(autocvar_g_vehicle_yugo_health_regen) + self.vehicle_flags |= VHF_HEALTHREGEN; + + self.vehicle_exit = yugo_exit; + self.respawntime = autocvar_g_vehicle_yugo_respawntime; + self.vehicle_health = autocvar_g_vehicle_yugo_health; + self.vehicle_shield = autocvar_g_vehicle_yugo_shield; + self.max_health = self.vehicle_health; + - return TRUE; ++ return true; + } + case VR_PRECACHE: + { + precache_sound (W_Sound("lasergun_fire")); + precache_sound (W_Sound("rocket_fire")); + + precache_sound ("vehicles/racer_idle.wav"); + precache_sound ("vehicles/racer_move.wav"); + precache_sound ("vehicles/racer_boost.wav"); + + precache_model ("models/vhshield.md3"); + precache_model ("models/vehicles/yugo.dpm"); - return TRUE; ++ return true; + } + } + - return TRUE; ++ return true; +} + +#endif // SVQC +#ifdef CSQC + +#define yugo_ico "gfx/vehicles/yugo.tga" + +float v_yugo(float req) +{ + switch(req) + { + case VR_HUD: + { + if(autocvar_r_letterbox) - return TRUE; ++ return true; + + vector picsize, hudloc = '0 0 0', pic2size, picloc; + + // Fetch health & ammo stats + HUD_GETVEHICLESTATS + + picsize = draw_getimagesize(hud_bg) * autocvar_cl_vehicles_hudscale; + hudloc_y = vid_conheight - picsize_y; + hudloc_x = vid_conwidth * 0.5 - picsize_x * 0.5; + + drawpic(hudloc, hud_bg, picsize, '1 1 1', autocvar_cl_vehicles_hudalpha, DRAWFLAG_NORMAL); + + shield *= 0.01; + vh_health *= 0.01; + energy *= 0.01; + reload1 *= 0.01; + + pic2size = draw_getimagesize(yugo_ico) * (autocvar_cl_vehicles_hudscale * 0.8); + picloc = picsize * 0.5 - pic2size * 0.5; + if(vh_health < 0.25) + drawpic(hudloc + picloc, yugo_ico, pic2size, '1 0 0' + '0 1 1' * sin(time * 8), 1, DRAWFLAG_NORMAL); + else + drawpic(hudloc + picloc, yugo_ico, pic2size, '1 1 1' * vh_health + '1 0 0' * (1 - vh_health), 1, DRAWFLAG_NORMAL); + //drawpic(hudloc + picloc, yugo_eng, pic2size, '1 1 1' * energy + '1 0 0' * (1 - energy), 1, DRAWFLAG_NORMAL); + //drawpic(hudloc + picloc, yugo_gun, pic2size, '1 1 1' * energy + '1 0 0' * (1 - energy), 1, DRAWFLAG_NORMAL); + drawpic(hudloc + picloc, hud_sh, pic2size, '1 1 1', shield, DRAWFLAG_NORMAL); + + // Health bar + picsize = draw_getimagesize(hud_hp_bar) * autocvar_cl_vehicles_hudscale; + picloc = '69 69 0' * autocvar_cl_vehicles_hudscale; + drawsetcliparea(hudloc_x + picloc_x + (picsize_x * (1 - vh_health)), 0, vid_conwidth, vid_conheight); + drawpic(hudloc + picloc, hud_hp_bar, picsize, '1 1 1', 1 , DRAWFLAG_NORMAL); + drawresetcliparea(); + // .. and icon + picsize = draw_getimagesize(hud_hp_ico) * autocvar_cl_vehicles_hudscale; + picloc = '37 65 0' * autocvar_cl_vehicles_hudscale; + if(vh_health < 0.25) + { + if(alarm1time < time) + { + alarm1time = time + 2; + vehicle_alarm(self, CH_PAIN_SINGLE, "vehicles/alarm.wav"); + } + + drawpic(hudloc + picloc, hud_hp_ico, picsize, '1 0 0' + '0 1 1' * sin(time * 8), 1, DRAWFLAG_NORMAL); + } + else + { + drawpic(hudloc + picloc, hud_hp_ico, picsize, '1 1 1', 1, DRAWFLAG_NORMAL); + if(alarm1time) + { + vehicle_alarm(self, CH_PAIN_SINGLE, "misc/null.wav"); + alarm1time = 0; + } + } + + + // Shield bar + picsize = draw_getimagesize(hud_sh_bar) * autocvar_cl_vehicles_hudscale; + picloc = '69 140 0' * autocvar_cl_vehicles_hudscale; + drawsetcliparea(hudloc_x + picloc_x + (picsize_x * (1 - shield)), 0, vid_conwidth, vid_conheight); + drawpic(hudloc + picloc, hud_sh_bar, picsize, '1 1 1', 1, DRAWFLAG_NORMAL); + drawresetcliparea(); + // .. and icon + picloc = '40 136 0' * autocvar_cl_vehicles_hudscale; + picsize = draw_getimagesize(hud_sh_ico) * autocvar_cl_vehicles_hudscale; + if(shield < 0.25) + { + if(alarm2time < time) + { + alarm2time = time + 1; + vehicle_alarm(self, CH_TRIGGER_SINGLE, "vehicles/alarm_shield.wav"); + } + drawpic(hudloc + picloc, hud_sh_ico, picsize, '1 0 0' + '0 1 1' * sin(time * 8), 1, DRAWFLAG_NORMAL); + } + else + { + drawpic(hudloc + picloc, hud_sh_ico, picsize, '1 1 1', 1, DRAWFLAG_NORMAL); + if(alarm2time) + { + vehicle_alarm(self, CH_TRIGGER_SINGLE, "misc/null.wav"); + alarm2time = 0; + } + } + + // Gun bar + picsize = draw_getimagesize(hud_ammo1_bar) * autocvar_cl_vehicles_hudscale; + picloc = '450 69 0' * autocvar_cl_vehicles_hudscale; + drawsetcliparea(hudloc_x + picloc_x, picloc_y, picsize_x * energy, vid_conheight); + drawpic(hudloc + picloc, hud_ammo1_bar, picsize, '1 1 1', 1, DRAWFLAG_NORMAL); + drawresetcliparea(); + // .. and icon + picsize = draw_getimagesize(hud_ammo1_ico) * autocvar_cl_vehicles_hudscale; + picloc = '664 60 0' * autocvar_cl_vehicles_hudscale; + if(energy < 0.2) + drawpic(hudloc + picloc, hud_ammo1_ico, picsize, '1 0 0' + '0 1 1' * sin(time * 8), 1, DRAWFLAG_NORMAL); + else + drawpic(hudloc + picloc, hud_ammo1_ico, picsize, '1 1 1', 1, DRAWFLAG_NORMAL); + + if (scoreboard_showscores) + HUD_DrawScoreboard(); - return TRUE; ++ return true; + } + case VR_SETUP: + { + AuxiliaryXhair[0].axh_image = "gfx/vehicles/axh-bracket.tga"; + AuxiliaryXhair[0].axh_scale = 0.25; - return TRUE; ++ return true; + } + case VR_PRECACHE: + { - return TRUE; ++ return true; + } + } + - return TRUE; ++ return true; +} + +#endif // CSQC +#endif // REGISTER_VEHICLE diff --cc qcsrc/common/vehicles/vehicles.qh index db5bb8ddc,000000000..bfc01b795 mode 100644,000000..100644 --- a/qcsrc/common/vehicles/vehicles.qh +++ b/qcsrc/common/vehicles/vehicles.qh @@@ -1,89 -1,0 +1,95 @@@ ++#ifndef VEHICLES_H ++#define VEHICLES_H ++ ++#include "sv_vehicles.qh" ++ +// vehicle requests - #define VR_SETUP 1 // (BOTH) setup vehicle data - #define VR_THINK 2 // (SERVER) logic to run every frame - #define VR_DEATH 3 // (SERVER) called when vehicle dies - #define VR_PRECACHE 4 // (BOTH) precaches models/sounds used by this vehicle - #define VR_ENTER 5 // (SERVER) called when a player enters this vehicle - #define VR_SPAWN 6 // (SERVER) called when the vehicle re-spawns - #define VR_IMPACT 7 // (SERVER) called when a vehicle hits something - #define VR_HUD 8 // (CLIENT) logic to run every frame ++const int VR_SETUP = 1; // (BOTH) setup vehicle data ++const int VR_THINK = 2; // (SERVER) logic to run every frame ++const int VR_DEATH = 3; // (SERVER) called when vehicle dies ++const int VR_PRECACHE = 4; // (BOTH) precaches models/sounds used by this vehicle ++const int VR_ENTER = 5; // (SERVER) called when a player enters this vehicle ++const int VR_SPAWN = 6; // (SERVER) called when the vehicle re-spawns ++const int VR_IMPACT = 7; // (SERVER) called when a vehicle hits something ++const int VR_HUD = 8; // (CLIENT) logic to run every frame ++ ++// vehicle spawn flags (need them here for common registrations) ++const int VHF_ISVEHICLE = 2; /// Indicates vehicle ++const int VHF_HASSHIELD = 4; /// Vehicle has shileding ++const int VHF_SHIELDREGEN = 8; /// Vehicles shield regenerates ++const int VHF_HEALTHREGEN = 16; /// Vehicles health regenerates ++const int VHF_ENERGYREGEN = 32; /// Vehicles energy regenerates ++const int VHF_DEATHEJECT = 64; /// Vehicle ejects pilot upon fatal damage ++const int VHF_MOVE_GROUND = 128; /// Vehicle moves on gound ++const int VHF_MOVE_HOVER = 256; /// Vehicle hover close to gound ++const int VHF_MOVE_FLY = 512; /// Vehicle is airborn ++const int VHF_DMGSHAKE = 1024; /// Add random velocity each frame if health < 50% ++const int VHF_DMGROLL = 2048; /// Add random angles each frame if health < 50% ++const int VHF_DMGHEADROLL = 4096; /// Add random head angles each frame if health < 50% ++const int VHF_MULTISLOT = 8192; /// Vehicle has multiple player slots ++const int VHF_PLAYERSLOT = 16384; /// This ent is a player slot on a multi-person vehicle + +// functions: +entity get_vehicleinfo(float id); + +// fields: +.entity tur_head; + - // flags: - .float vehicle_flags; - const float VHF_ISVEHICLE = 2; /// Indicates vehicle - const float VHF_HASSHIELD = 4; /// Vehicle has shileding - const float VHF_SHIELDREGEN = 8; /// Vehicles shield regenerates - const float VHF_HEALTHREGEN = 16; /// Vehicles health regenerates - const float VHF_ENERGYREGEN = 32; /// Vehicles energy regenerates - const float VHF_DEATHEJECT = 64; /// Vehicle ejects pilot upon fatal damage - const float VHF_MOVE_GROUND = 128; /// Vehicle moves on gound - const float VHF_MOVE_HOVER = 256; /// Vehicle hover close to gound - const float VHF_MOVE_FLY = 512; /// Vehicle is airborn - const float VHF_DMGSHAKE = 1024; /// Add random velocity each frame if health < 50% - const float VHF_DMGROLL = 2048; /// Add random angles each frame if health < 50% - const float VHF_DMGHEADROLL = 4096; /// Add random head angles each frame if health < 50% - const float VHF_MULTISLOT = 8192; /// Vehicle has multiple player slots - const float VHF_PLAYERSLOT = 16384; /// This ent is a player slot on a multi-person vehicle - + +// entity properties of vehicleinfo: +.float vehicleid; // VEH_... +.string netname; // short name +.string vehicle_name; // human readable name +.float(float) vehicle_func; // v_... +.string mdl; // currently a copy of the model +.string model; // full name of model +.string head_model; // full name of tur_head model +.string hud_model; // cockpit model +.string tag_head; // tur_head model tag +.string tag_hud; // hud model tag +.string tag_view; // cockpit model tag +.float() PlayerPhysplug; // player physics mod +.float spawnflags; +.vector mins, maxs; // vehicle hitbox size + +// other useful macros +#define VEH_ACTION(vehicletype,mrequest) (get_vehicleinfo(vehicletype)).vehicle_func(mrequest) +#define VEH_NAME(vehicletype) (get_vehicleinfo(vehicletype)).vehicle_name + +// ===================== +// Vehicle Registration +// ===================== + +float v_null(float dummy); +void register_vehicle(float id, float(float) func, float vehicleflags, vector min_s, vector max_s, string modelname, string headmodelname, string hudmodelname, string headtag, string hudtag, string viewtag, string shortname, string vname); +void register_vehicles_done(); + +const float VEH_MAXCOUNT = 24; +#define VEH_FIRST 1 +float VEH_COUNT; +float VEH_LAST; + +#define REGISTER_VEHICLE_2(id,func,vehicleflags,min_s,max_s,modelname,headmodelname,hudmodelname,headtag,hudtag,viewtag,shortname,vname) \ + float id; \ + float func(float); \ + void RegisterVehicles_##id() \ + { \ + VEH_LAST = (id = VEH_FIRST + VEH_COUNT); \ + ++VEH_COUNT; \ + register_vehicle(id,func,vehicleflags,min_s,max_s,modelname,headmodelname,hudmodelname,headtag,hudtag,viewtag,shortname,vname); \ + } \ + ACCUMULATE_FUNCTION(RegisterVehicles, RegisterVehicles_##id) +#ifdef MENUQC +#define REGISTER_VEHICLE(id,func,vehicleflags,min_s,max_s,modelname,headmodelname,hudmodelname,headtag,hudtag,viewtag,shortname,vname) \ + REGISTER_VEHICLE_2(VEH_##id,v_null,vehicleflags,min_s,max_s,modelname,headmodelname,hudmodelname,headtag,hudtag,viewtag,shortname,vname) +#else +#define REGISTER_VEHICLE(id,func,vehicleflags,min_s,max_s,modelname,headmodelname,hudmodelname,headtag,hudtag,viewtag,shortname,vname) \ + REGISTER_VEHICLE_2(VEH_##id,func,vehicleflags,min_s,max_s,modelname,headmodelname,hudmodelname,headtag,hudtag,viewtag,shortname,vname) +#endif + +#include "all.qh" + +#undef REGISTER_VEHICLE +ACCUMULATE_FUNCTION(RegisterVehicles, register_vehicles_done); ++ ++#endif diff --cc qcsrc/common/vehicles/vehicles_include.qc index 85b923afa,000000000..ac174ae0c mode 100644,000000..100644 --- a/qcsrc/common/vehicles/vehicles_include.qc +++ b/qcsrc/common/vehicles/vehicles_include.qc @@@ -1,8 -1,0 +1,10 @@@ ++#include "vehicles_include.qh" ++ +#ifdef CSQC +#include "cl_vehicles.qc" +#include "vehicles.qc" +#endif // CSQC +#ifdef SVQC +#include "sv_vehicles.qc" +#include "vehicles.qc" +#endif // SVQC diff --cc qcsrc/common/vehicles/vehicles_include.qh index d37d749a4,000000000..409e94cb2 mode 100644,000000..100644 --- a/qcsrc/common/vehicles/vehicles_include.qh +++ b/qcsrc/common/vehicles/vehicles_include.qh @@@ -1,8 -1,0 +1,13 @@@ ++#ifndef VEHICLES_INCLUDE_H ++#define VEHICLES_INCLUDE_H ++ +#ifdef CSQC +#include "vehicles.qh" +#include "cl_vehicles.qh" +#endif // CSQC +#ifdef SVQC +#include "vehicles.qh" +#include "sv_vehicles.qh" +#endif // SVQC ++ ++#endif diff --cc qcsrc/common/weapons/all.qh index 6576fcd60,b4ee1e25d..27f1666e7 --- a/qcsrc/common/weapons/all.qh +++ b/qcsrc/common/weapons/all.qh @@@ -28,6 -39,4 +39,8 @@@ #include "w_hmg.qc" #include "w_rpc.qc" +// more other weapons +#include "w_revolver.qc" +#include "w_lightsabre.qc" ++ + //#endif diff --cc qcsrc/common/weapons/w_arc.qc index 2607e47cd,d8faa7be9..5df3e6165 --- a/qcsrc/common/weapons/w_arc.qc +++ b/qcsrc/common/weapons/w_arc.qc @@@ -125,6 -125,6 +125,8 @@@ vector Draw_ArcBeam_callback_last_botto #endif #else #ifdef SVQC ++#include "../effects.qh" ++ void spawnfunc_weapon_arc(void) { weapon_defaultspawnfunc(WEP_ARC); } float W_Arc_Beam_Send(entity to, float sf) @@@ -257,15 -259,15 +259,15 @@@ void W_Arc_Beam_Think(void self.owner.arc_overheat = time + self.beam_heat / cooldown_speed; self.owner.arc_cooldown = cooldown_speed; } - + if ( WEP_CVAR(arc, overheat_max) > 0 && self.beam_heat >= WEP_CVAR(arc, overheat_max) ) { - pointparticles( particleeffectnum("arc_overheat"), + pointparticles( particleeffectnum("arc_overheat"), self.beam_start, self.beam_wantdir, 1 ); - sound(self, CH_WEAPON_A, "weapons/arc_stop.wav", VOL_BASE, ATTN_NORM); + sound(self, CH_WEAPON_A, W_Sound("arc_stop"), VOL_BASE, ATTN_NORM); } } - + if(self == self.owner.arc_beam) { self.owner.arc_beam = world; } entity oldself = self; self = self.owner; @@@ -687,13 -691,13 +689,13 @@@ float W_Arc(float req } } } - - return TRUE; + + return true; } - + if(self.arc_BUTTON_ATCK_prev != 0) { - sound(self, CH_WEAPON_A, "weapons/arc_stop.wav", VOL_BASE, ATTN_NORM); + sound(self, CH_WEAPON_A, W_Sound("arc_stop"), VOL_BASE, ATTN_NORM); weapon_thinkf(WFRAME_FIRE1, WEP_CVAR(arc, beam_animtime), w_ready); ATTACK_FINISHED(self) = time + WEP_CVAR(arc, beam_refire) * W_WeaponRateFactor(); } @@@ -714,22 -718,22 +716,22 @@@ } case WR_INIT: { - precache_model("models/weapons/g_arc.md3"); - precache_model("models/weapons/v_arc.md3"); - precache_model("models/weapons/h_arc.iqm"); - precache_sound("weapons/arc_fire.wav"); - precache_sound("weapons/arc_loop.wav"); - precache_sound("weapons/arc_stop.wav"); - precache_sound("weapons/arc_loop_overheat.wav"); + precache_model(W_Model("g_arc.md3")); + precache_model(W_Model("v_arc.md3")); + precache_model(W_Model("h_arc.iqm")); + precache_sound(W_Sound("arc_fire")); + precache_sound(W_Sound("arc_loop")); + precache_sound(W_Sound("arc_stop")); + precache_sound(W_Sound("arc_loop_overheat")); if(!arc_shotorigin[0]) { - arc_shotorigin[0] = shotorg_adjust_values(CL_Weapon_GetShotOrg(WEP_ARC), FALSE, FALSE, 1); - arc_shotorigin[1] = shotorg_adjust_values(CL_Weapon_GetShotOrg(WEP_ARC), FALSE, FALSE, 2); - arc_shotorigin[2] = shotorg_adjust_values(CL_Weapon_GetShotOrg(WEP_ARC), FALSE, FALSE, 3); - arc_shotorigin[3] = shotorg_adjust_values(CL_Weapon_GetShotOrg(WEP_ARC), FALSE, FALSE, 4); + arc_shotorigin[0] = shotorg_adjust_values(CL_Weapon_GetShotOrg(WEP_ARC), false, false, 1); + arc_shotorigin[1] = shotorg_adjust_values(CL_Weapon_GetShotOrg(WEP_ARC), false, false, 2); + arc_shotorigin[2] = shotorg_adjust_values(CL_Weapon_GetShotOrg(WEP_ARC), false, false, 3); + arc_shotorigin[3] = shotorg_adjust_values(CL_Weapon_GetShotOrg(WEP_ARC), false, false, 4); } - ARC_SETTINGS(WEP_SKIP_CVAR, WEP_SET_PROP) - return TRUE; + ARC_SETTINGS(WEP_SKIP_CVAR, WEP_SET_PROP); + return true; } case WR_CHECKAMMO1: { diff --cc qcsrc/common/weapons/w_blaster.qc index fdb0e9e52,8eaff86e3..06a925550 --- a/qcsrc/common/weapons/w_blaster.qc +++ b/qcsrc/common/weapons/w_blaster.qc @@@ -47,6 -47,6 +47,8 @@@ BLASTER_SETTINGS(WEP_ADD_CVAR, WEP_ADD_ #endif #else #ifdef SVQC ++#include "../effects.qh" ++ void spawnfunc_weapon_blaster(void) { weapon_defaultspawnfunc(WEP_BLASTER); } void spawnfunc_weapon_laser(void) { spawnfunc_weapon_blaster(); } @@@ -94,8 -94,8 +96,8 @@@ void W_Blaster_Attack { vector s_forward = v_forward * cos(atk_shotangle * DEG2RAD) + v_up * sin(atk_shotangle * DEG2RAD); - W_SetupShot_Dir(self, s_forward, FALSE, 3, W_Sound("lasergun_fire"), CH_WEAPON_B, atk_damage); - W_SetupShot_Dir(self, s_forward, false, 3, "weapons/lasergun_fire.wav", CH_WEAPON_B, atk_damage); - pointparticles(particleeffectnum("laser_muzzleflash"), w_shotorg, w_shotdir * 1000, 1); ++ W_SetupShot_Dir(self, s_forward, false, 3, W_Sound("lasergun_fire"), CH_WEAPON_B, atk_damage); + Send_Effect(EFFECT_LASER_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1); entity missile = spawn(); missile.owner = missile.realowner = self; @@@ -221,19 -221,19 +223,19 @@@ float W_Blaster(float request } } } - return TRUE; + return true; } - - case WR_INIT: + + case WR_INIT: { - precache_model("models/weapons/g_laser.md3"); - precache_model("models/weapons/v_laser.md3"); - precache_model("models/weapons/h_laser.iqm"); - precache_sound("weapons/lasergun_fire.wav"); - BLASTER_SETTINGS(WEP_SKIP_CVAR, WEP_SET_PROP); + precache_model(W_Model("g_laser.md3")); + precache_model(W_Model("v_laser.md3")); + precache_model(W_Model("h_laser.iqm")); + precache_sound(W_Sound("lasergun_fire")); + BLASTER_SETTINGS(WEP_SKIP_CVAR, WEP_SET_PROP) - return TRUE; + return true; } - + case WR_SETUP: { self.ammo_field = ammo_none; diff --cc qcsrc/common/weapons/w_crylink.qc index c0571cc20,21a86e7da..d4ebc059b --- a/qcsrc/common/weapons/w_crylink.qc +++ b/qcsrc/common/weapons/w_crylink.qc @@@ -63,6 -63,6 +63,8 @@@ CRYLINK_SETTINGS(WEP_ADD_CVAR, WEP_ADD_ #endif #else #ifdef SVQC ++#include "../effects.qh" ++ void spawnfunc_weapon_crylink(void) { weapon_defaultspawnfunc(WEP_CRYLINK); } void W_Crylink_CheckLinks(entity e) @@@ -353,7 -353,7 +355,7 @@@ void W_Crylink_Attack(void if(WEP_CVAR_PRI(crylink, joinexplode)) maxdmg += WEP_CVAR_PRI(crylink, joinexplode_damage); - W_SetupShot(self, FALSE, 2, W_Sound("crylink_fire"), CH_WEAPON_A, maxdmg); - W_SetupShot(self, false, 2, "weapons/crylink_fire.wav", CH_WEAPON_A, maxdmg); ++ W_SetupShot(self, false, 2, W_Sound("crylink_fire"), CH_WEAPON_A, maxdmg); forward = v_forward; right = v_right; up = v_up; @@@ -462,7 -462,7 +464,7 @@@ void W_Crylink_Attack2(void if(WEP_CVAR_SEC(crylink, joinexplode)) maxdmg += WEP_CVAR_SEC(crylink, joinexplode_damage); - W_SetupShot(self, FALSE, 2, W_Sound("crylink_fire2"), CH_WEAPON_A, maxdmg); - W_SetupShot(self, false, 2, "weapons/crylink_fire2.wav", CH_WEAPON_A, maxdmg); ++ W_SetupShot(self, false, 2, W_Sound("crylink_fire2"), CH_WEAPON_A, maxdmg); forward = v_forward; right = v_right; up = v_up; @@@ -637,14 -637,14 +639,14 @@@ float W_Crylink(float req } case WR_INIT: { - precache_model("models/weapons/g_crylink.md3"); - precache_model("models/weapons/v_crylink.md3"); - precache_model("models/weapons/h_crylink.iqm"); - precache_sound("weapons/crylink_fire.wav"); - precache_sound("weapons/crylink_fire2.wav"); - precache_sound("weapons/crylink_linkjoin.wav"); - CRYLINK_SETTINGS(WEP_SKIP_CVAR, WEP_SET_PROP); + precache_model(W_Model("g_crylink.md3")); + precache_model(W_Model("v_crylink.md3")); + precache_model(W_Model("h_crylink.iqm")); + precache_sound(W_Sound("crylink_fire")); + precache_sound(W_Sound("crylink_fire2")); + precache_sound(W_Sound("crylink_linkjoin")); + CRYLINK_SETTINGS(WEP_SKIP_CVAR, WEP_SET_PROP) - return TRUE; + return true; } case WR_CHECKAMMO1: { @@@ -673,8 -673,8 +675,8 @@@ } case WR_RELOAD: { - W_Reload(min(WEP_CVAR_PRI(crylink, ammo), WEP_CVAR_SEC(crylink, ammo)), "weapons/reload.wav"); + W_Reload(min(WEP_CVAR_PRI(crylink, ammo), WEP_CVAR_SEC(crylink, ammo)), W_Sound("reload")); - return TRUE; + return true; } case WR_SUICIDEMESSAGE: { diff --cc qcsrc/common/weapons/w_devastator.qc index 7460fe6d2,9e5f98a49..7b097298f --- a/qcsrc/common/weapons/w_devastator.qc +++ b/qcsrc/common/weapons/w_devastator.qc @@@ -61,6 -61,6 +61,8 @@@ DEVASTATOR_SETTINGS(WEP_ADD_CVAR, WEP_A #endif #else #ifdef SVQC ++#include "../effects.qh" ++ void spawnfunc_weapon_devastator(void) { weapon_defaultspawnfunc(WEP_DEVASTATOR); } void spawnfunc_weapon_rocketlauncher(void) { spawnfunc_weapon_devastator(); } @@@ -339,8 -339,8 +341,8 @@@ void W_Devastator_Attack(void W_DecreaseAmmo(WEP_CVAR(devastator, ammo)); - W_SetupShot_ProjectileSize(self, '-3 -3 -3', '3 3 3', FALSE, 5, W_Sound("rocket_fire"), CH_WEAPON_A, WEP_CVAR(devastator, damage)); - W_SetupShot_ProjectileSize(self, '-3 -3 -3', '3 3 3', false, 5, "weapons/rocket_fire.wav", CH_WEAPON_A, WEP_CVAR(devastator, damage)); - pointparticles(particleeffectnum("rocketlauncher_muzzleflash"), w_shotorg, w_shotdir * 1000, 1); ++ W_SetupShot_ProjectileSize(self, '-3 -3 -3', '3 3 3', false, 5, W_Sound("rocket_fire"), CH_WEAPON_A, WEP_CVAR(devastator, damage)); + Send_Effect(EFFECT_ROCKET_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1); missile = WarpZone_RefSys_SpawnSameRefSys(self); missile.owner = missile.realowner = self; @@@ -429,8 -429,8 +431,8 @@@ float W_Devastator(float req case WR_AIM: { // aim and decide to fire if appropriate - self.BUTTON_ATCK = bot_aim(WEP_CVAR(devastator, speed), 0, WEP_CVAR(devastator, lifetime), FALSE); + self.BUTTON_ATCK = bot_aim(WEP_CVAR(devastator, speed), 0, WEP_CVAR(devastator, lifetime), false); - if(skill >= 2) // skill 0 and 1 bots won't detonate rockets! + if(bot_skill >= 2) // skill 0 and 1 bots won't detonate rockets! { // decide whether to detonate rockets entity missile, targetlist, targ; @@@ -503,8 -503,8 +505,8 @@@ if(v_forward * normalize(missile.origin - self.enemy.origin)< 0.1) if(IS_PLAYER(self.enemy)) if(desirabledamage >= 0.1*coredamage) - if(random()/distance*300 > frametime*bound(0,(10-skill)*0.2,1)) + if(random()/distance*300 > frametime*bound(0,(10-bot_skill)*0.2,1)) - self.BUTTON_ATCK2 = TRUE; + self.BUTTON_ATCK2 = true; // dprint(ftos(random()/distance*300),">");dprint(ftos(frametime*bound(0,(10-skill)*0.2,1)),"\n"); } @@@ -513,15 -513,15 +515,15 @@@ // if we would be doing at X percent of the core damage, detonate it // but don't fire a new shot at the same time! if(desirabledamage >= 0.75 * coredamage) //this should do group damage in rare fortunate events - self.BUTTON_ATCK2 = TRUE; + self.BUTTON_ATCK2 = true; - if((skill > 6.5) && (selfdamage > self.health)) + if((bot_skill > 6.5) && (selfdamage > self.health)) - self.BUTTON_ATCK2 = FALSE; - //if(self.BUTTON_ATCK2 == TRUE) + self.BUTTON_ATCK2 = false; + //if(self.BUTTON_ATCK2 == true) // dprint(ftos(desirabledamage),"\n"); - if(self.BUTTON_ATCK2 == TRUE) self.BUTTON_ATCK = FALSE; + if(self.BUTTON_ATCK2 == true) self.BUTTON_ATCK = false; } - - return TRUE; + + return true; } #endif case WR_THINK: @@@ -556,26 -556,26 +558,26 @@@ } } if(rockfound) - sound(self, CH_WEAPON_B, "weapons/rocket_det.wav", VOL_BASE, ATTN_NORM); + sound(self, CH_WEAPON_B, W_Sound("rocket_det"), VOL_BASE, ATTN_NORM); } } - - return TRUE; + + return true; } case WR_INIT: { //if(autocvar_sv_precacheweapons) //{ precache_model("models/flash.md3"); - precache_model("models/weapons/g_rl.md3"); - precache_model("models/weapons/v_rl.md3"); - precache_model("models/weapons/h_rl.iqm"); - precache_sound("weapons/rocket_det.wav"); - precache_sound("weapons/rocket_fire.wav"); - precache_sound("weapons/rocket_mode.wav"); + precache_model(W_Model("g_rl.md3")); + precache_model(W_Model("v_rl.md3")); + precache_model(W_Model("h_rl.iqm")); + precache_sound(W_Sound("rocket_det")); + precache_sound(W_Sound("rocket_fire")); + precache_sound(W_Sound("rocket_mode")); //} - DEVASTATOR_SETTINGS(WEP_SKIP_CVAR, WEP_SET_PROP) - return TRUE; + DEVASTATOR_SETTINGS(WEP_SKIP_CVAR, WEP_SET_PROP); + return true; } case WR_SETUP: { @@@ -602,14 -602,14 +604,14 @@@ #if 0 if(self.rl_release == 0) { -- printf("W_Devastator(WR_CHECKAMMO1): %d, %.2f, %d: TRUE\n", self.rl_release, self.WEP_AMMO(DEVASTATOR), WEP_CVAR(devastator, ammo)); - return TRUE; ++ printf("W_Devastator(WR_CHECKAMMO1): %d, %.2f, %d: true\n", self.rl_release, self.WEP_AMMO(DEVASTATOR), WEP_CVAR(devastator, ammo)); + return true; } else { ammo_amount = self.WEP_AMMO(DEVASTATOR) >= WEP_CVAR(devastator, ammo); ammo_amount += self.(weapon_load[WEP_DEVASTATOR]) >= WEP_CVAR(devastator, ammo); -- printf("W_Devastator(WR_CHECKAMMO1): %d, %.2f, %d: %s\n", self.rl_release, self.WEP_AMMO(DEVASTATOR), WEP_CVAR(devastator, ammo), (ammo_amount ? "TRUE" : "FALSE")); ++ printf("W_Devastator(WR_CHECKAMMO1): %d, %.2f, %d: %s\n", self.rl_release, self.WEP_AMMO(DEVASTATOR), WEP_CVAR(devastator, ammo), (ammo_amount ? "true" : "false")); return ammo_amount; } #else @@@ -634,8 -634,8 +636,8 @@@ } case WR_RELOAD: { - W_Reload(WEP_CVAR(devastator, ammo), "weapons/reload.wav"); + W_Reload(WEP_CVAR(devastator, ammo), W_Sound("reload")); - return TRUE; + return true; } case WR_SUICIDEMESSAGE: { diff --cc qcsrc/common/weapons/w_electro.qc index c02c6be9f,3f5e75159..a69a4da0b --- a/qcsrc/common/weapons/w_electro.qc +++ b/qcsrc/common/weapons/w_electro.qc @@@ -67,53 -66,9 +67,55 @@@ void W_Electro_ExplodeCombo(void) #endif #else #ifdef SVQC ++#include "../effects.qh" ++ void spawnfunc_weapon_electro(void) { weapon_defaultspawnfunc(WEP_ELECTRO); } -void W_Electro_TriggerCombo(vector org, float rad, entity own) +void W_Electro_TriggerCombo_Simple(vector org, float rad, entity own) +{ + entity e; + for(e = world; (e = find(e, classname, "electro_orb")); ) + { + if(vlen(e.origin - org) <= rad) + { + // do we allow thruwall triggering? + if(WEP_CVAR(electro, combo_comboradius_thruwall)) + { + WarpZone_TraceLine(org, e.origin, MOVE_NOMONSTERS, e); + if(trace_fraction != 1) + { + if(vlen(e.origin - org) >= WEP_CVAR(electro, combo_comboradius_thruwall)) + { + // trigger is through a wall and outside of thruwall range, abort + continue; + } + } + } + + // change owner to whoever caused the combo explosion + e.realowner = own; + e.takedamage = DAMAGE_NO; + e.classname = "electro_orb_chain"; + + // now set the next one to trigger as well + e.think = W_Electro_ExplodeCombo; + + // delay combo chains, looks cooler + e.nextthink = + ( + time + + + (WEP_CVAR(electro, combo_speed) ? + (vlen(e.origin - org) / WEP_CVAR(electro, combo_speed)) + : + 0 + ) + ); + } + } +} + +void W_Electro_TriggerCombo_WarpZone(vector org, float rad, entity own) { entity e = WarpZone_FindRadius(org, rad, !WEP_CVAR(electro, combo_comboradius_thruwall)); while(e) @@@ -307,9 -252,9 +309,9 @@@ void W_Electro_Attack_Bolt(void self, '0 0 -3', '0 0 -3', - FALSE, + false, 2, - "weapons/electro_fire.wav", + W_Sound("electro_fire"), CH_WEAPON_A, WEP_CVAR_PRI(electro, damage) ); @@@ -405,9 -350,9 +407,9 @@@ void W_Electro_Attack_Orb(void self, '0 0 -4', '0 0 -4', - FALSE, + false, 2, - "weapons/electro_fire2.wav", + W_Sound("electro_fire2"), CH_WEAPON_A, WEP_CVAR_SEC(electro, damage) ); @@@ -552,16 -497,16 +554,16 @@@ float W_Electro(float req } case WR_INIT: { - precache_model("models/weapons/g_electro.md3"); - precache_model("models/weapons/v_electro.md3"); - precache_model("models/weapons/h_electro.iqm"); - precache_sound("weapons/electro_bounce.wav"); - precache_sound("weapons/electro_fire.wav"); - precache_sound("weapons/electro_fire2.wav"); - precache_sound("weapons/electro_impact.wav"); - precache_sound("weapons/electro_impact_combo.wav"); - ELECTRO_SETTINGS(WEP_SKIP_CVAR, WEP_SET_PROP); + precache_model(W_Model("g_electro.md3")); + precache_model(W_Model("v_electro.md3")); + precache_model(W_Model("h_electro.iqm")); + precache_sound(W_Sound("electro_bounce")); + precache_sound(W_Sound("electro_fire")); + precache_sound(W_Sound("electro_fire2")); + precache_sound(W_Sound("electro_impact")); + precache_sound(W_Sound("electro_impact_combo")); + ELECTRO_SETTINGS(WEP_SKIP_CVAR, WEP_SET_PROP) - return TRUE; + return true; } case WR_CHECKAMMO1: { @@@ -595,8 -540,8 +597,8 @@@ } case WR_RELOAD: { - W_Reload(min(WEP_CVAR_PRI(electro, ammo), WEP_CVAR_SEC(electro, ammo)), "weapons/reload.wav"); + W_Reload(min(WEP_CVAR_PRI(electro, ammo), WEP_CVAR_SEC(electro, ammo)), W_Sound("reload")); - return TRUE; + return true; } case WR_SUICIDEMESSAGE: { diff --cc qcsrc/common/weapons/w_fireball.qc index ead6567c8,d09800913..d919c0550 --- a/qcsrc/common/weapons/w_fireball.qc +++ b/qcsrc/common/weapons/w_fireball.qc @@@ -54,6 -54,6 +54,8 @@@ FIREBALL_SETTINGS(WEP_ADD_CVAR, WEP_ADD #endif #else #ifdef SVQC ++#include "../effects.qh" ++ void spawnfunc_weapon_fireball(void) { weapon_defaultspawnfunc(WEP_FIREBALL); } void W_Fireball_Explode(void) @@@ -180,9 -181,9 +182,9 @@@ void W_Fireball_Attack1(void { entity proj; - W_SetupShot_ProjectileSize(self, '-16 -16 -16', '16 16 16', FALSE, 2, W_Sound("fireball_fire2"), CH_WEAPON_A, WEP_CVAR_PRI(fireball, damage) + WEP_CVAR_PRI(fireball, bfgdamage)); - W_SetupShot_ProjectileSize(self, '-16 -16 -16', '16 16 16', false, 2, "weapons/fireball_fire2.wav", CH_WEAPON_A, WEP_CVAR_PRI(fireball, damage) + WEP_CVAR_PRI(fireball, bfgdamage)); ++ W_SetupShot_ProjectileSize(self, '-16 -16 -16', '16 16 16', false, 2, W_Sound("fireball_fire2"), CH_WEAPON_A, WEP_CVAR_PRI(fireball, damage) + WEP_CVAR_PRI(fireball, bfgdamage)); - pointparticles(particleeffectnum("fireball_muzzleflash"), w_shotorg, w_shotdir * 1000, 1); + Send_Effect(EFFECT_FIREBALL_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1); proj = spawn(); proj.classname = "plasma_prim"; @@@ -217,9 -218,9 +219,9 @@@ void W_Fireball_AttackEffect(float i, vector f_diff) { - W_SetupShot_ProjectileSize(self, '-16 -16 -16', '16 16 16', FALSE, 0, "", 0, 0); + W_SetupShot_ProjectileSize(self, '-16 -16 -16', '16 16 16', false, 0, "", 0, 0); - w_shotorg += f_diff.x * v_up + f_diff.y * v_right; - pointparticles(particleeffectnum("fireball_preattack_muzzleflash"), w_shotorg, w_shotdir * 1000, 1); + w_shotorg += f_diff_x * v_up + f_diff_y * v_right; + Send_Effect(EFFECT_FIREBALL_PRE_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1); } void W_Fireball_Attack1_Frame4(void) @@@ -314,11 -315,11 +316,11 @@@ void W_Fireball_Attack2(void f_diff = '+1.25 +3.75 0'; break; } - W_SetupShot_ProjectileSize(self, '-4 -4 -4', '4 4 4', FALSE, 2, W_Sound("fireball_fire"), CH_WEAPON_A, WEP_CVAR_SEC(fireball, damage)); - W_SetupShot_ProjectileSize(self, '-4 -4 -4', '4 4 4', false, 2, "weapons/fireball_fire.wav", CH_WEAPON_A, WEP_CVAR_SEC(fireball, damage)); - traceline(w_shotorg, w_shotorg + f_diff.x * v_up + f_diff.y * v_right, MOVE_NORMAL, self); ++ W_SetupShot_ProjectileSize(self, '-4 -4 -4', '4 4 4', false, 2, W_Sound("fireball_fire"), CH_WEAPON_A, WEP_CVAR_SEC(fireball, damage)); + traceline(w_shotorg, w_shotorg + f_diff_x * v_up + f_diff_y * v_right, MOVE_NORMAL, self); w_shotorg = trace_endpos; - pointparticles(particleeffectnum("fireball_muzzleflash"), w_shotorg, w_shotdir * 1000, 1); + Send_Effect(EFFECT_FIREBALL_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1); proj = spawn(); proj.owner = proj.realowner = self; @@@ -397,15 -398,15 +399,15 @@@ float W_Fireball(float req } case WR_INIT: { - precache_model("models/weapons/g_fireball.md3"); - precache_model("models/weapons/v_fireball.md3"); - precache_model("models/weapons/h_fireball.iqm"); + precache_model(W_Model("g_fireball.md3")); + precache_model(W_Model("v_fireball.md3")); + precache_model(W_Model("h_fireball.iqm")); precache_model("models/sphere/sphere.md3"); - precache_sound("weapons/fireball_fire.wav"); - precache_sound("weapons/fireball_fire2.wav"); - precache_sound("weapons/fireball_prefire2.wav"); - FIREBALL_SETTINGS(WEP_SKIP_CVAR, WEP_SET_PROP); + precache_sound(W_Sound("fireball_fire")); + precache_sound(W_Sound("fireball_fire2")); + precache_sound(W_Sound("fireball_prefire2")); + FIREBALL_SETTINGS(WEP_SKIP_CVAR, WEP_SET_PROP) - return TRUE; + return true; } case WR_SETUP: { diff --cc qcsrc/common/weapons/w_hagar.qc index 9ad4997a9,1085c315a..6950ca96d --- a/qcsrc/common/weapons/w_hagar.qc +++ b/qcsrc/common/weapons/w_hagar.qc @@@ -55,6 -55,6 +55,8 @@@ HAGAR_SETTINGS(WEP_ADD_CVAR, WEP_ADD_PR #endif #else #ifdef SVQC ++#include "../effects.qh" ++ void spawnfunc_weapon_hagar(void) { weapon_defaultspawnfunc(WEP_HAGAR); } // NO bounce protection, as bounces are limited! @@@ -126,9 -126,9 +128,9 @@@ void W_Hagar_Attack(void W_DecreaseAmmo(WEP_CVAR_PRI(hagar, ammo)); - W_SetupShot(self, FALSE, 2, W_Sound("hagar_fire"), CH_WEAPON_A, WEP_CVAR_PRI(hagar, damage)); - W_SetupShot(self, false, 2, "weapons/hagar_fire.wav", CH_WEAPON_A, WEP_CVAR_PRI(hagar, damage)); ++ W_SetupShot(self, false, 2, W_Sound("hagar_fire"), CH_WEAPON_A, WEP_CVAR_PRI(hagar, damage)); - pointparticles(particleeffectnum("hagar_muzzleflash"), w_shotorg, w_shotdir * 1000, 1); + Send_Effect(EFFECT_HAGAR_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1); missile = spawn(); missile.owner = missile.realowner = self; @@@ -169,9 -169,9 +171,9 @@@ void W_Hagar_Attack2(void W_DecreaseAmmo(WEP_CVAR_SEC(hagar, ammo)); - W_SetupShot(self, FALSE, 2, W_Sound("hagar_fire"), CH_WEAPON_A, WEP_CVAR_SEC(hagar, damage)); - W_SetupShot(self, false, 2, "weapons/hagar_fire.wav", CH_WEAPON_A, WEP_CVAR_SEC(hagar, damage)); ++ W_SetupShot(self, false, 2, W_Sound("hagar_fire"), CH_WEAPON_A, WEP_CVAR_SEC(hagar, damage)); - pointparticles(particleeffectnum("hagar_muzzleflash"), w_shotorg, w_shotdir * 1000, 1); + Send_Effect(EFFECT_HAGAR_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1); missile = spawn(); missile.owner = missile.realowner = self; @@@ -222,8 -222,8 +224,8 @@@ void W_Hagar_Attack2_Load_Release(void weapon_prepareattack_do(1, WEP_CVAR_SEC(hagar, refire)); - W_SetupShot(self, FALSE, 2, W_Sound("hagar_fire"), CH_WEAPON_A, WEP_CVAR_SEC(hagar, damage)); - W_SetupShot(self, false, 2, "weapons/hagar_fire.wav", CH_WEAPON_A, WEP_CVAR_SEC(hagar, damage)); - pointparticles(particleeffectnum("hagar_muzzleflash"), w_shotorg, w_shotdir * 1000, 1); ++ W_SetupShot(self, false, 2, W_Sound("hagar_fire"), CH_WEAPON_A, WEP_CVAR_SEC(hagar, damage)); + Send_Effect(EFFECT_HAGAR_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1); forward = v_forward; right = v_right; @@@ -344,8 -344,8 +346,8 @@@ void W_Hagar_Attack2_Load(void else if(!self.hagar_loadbeep && self.hagar_load) // prevents the beep from playing each frame { // if this is the last rocket we can load, play a beep sound to notify the player - sound(self, CH_WEAPON_A, "weapons/hagar_beep.wav", VOL_BASE, ATTN_NORM); + sound(self, CH_WEAPON_A, W_Sound("hagar_beep"), VOL_BASE, ATTN_NORM); - self.hagar_loadbeep = TRUE; + self.hagar_loadbeep = true; } } } @@@ -363,11 -363,11 +365,11 @@@ if(!self.hagar_warning && self.hagar_load) // prevents the beep from playing each frame { // we're about to automatically release after holding time, play a beep sound to notify the player - sound(self, CH_WEAPON_A, "weapons/hagar_beep.wav", VOL_BASE, ATTN_NORM); + sound(self, CH_WEAPON_A, W_Sound("hagar_beep"), VOL_BASE, ATTN_NORM); - self.hagar_warning = TRUE; + self.hagar_warning = true; } } - + // release if player let go of button or if they've held it in too long if(!self.BUTTON_ATCK2 || ((loaded || !enough_ammo) && self.hagar_loadstep < time && WEP_CVAR_SEC(hagar, load_hold) >= 0)) { @@@ -445,14 -445,14 +447,14 @@@ float W_Hagar(float req } case WR_INIT: { - precache_model("models/weapons/g_hagar.md3"); - precache_model("models/weapons/v_hagar.md3"); - precache_model("models/weapons/h_hagar.iqm"); - precache_sound("weapons/hagar_fire.wav"); - precache_sound("weapons/hagar_load.wav"); - precache_sound("weapons/hagar_beep.wav"); - HAGAR_SETTINGS(WEP_SKIP_CVAR, WEP_SET_PROP); + precache_model(W_Model("g_hagar.md3")); + precache_model(W_Model("v_hagar.md3")); + precache_model(W_Model("h_hagar.iqm")); + precache_sound(W_Sound("hagar_fire")); + precache_sound(W_Sound("hagar_load")); + precache_sound(W_Sound("hagar_beep")); + HAGAR_SETTINGS(WEP_SKIP_CVAR, WEP_SET_PROP) - return TRUE; + return true; } case WR_SETUP: { @@@ -499,9 -499,9 +501,9 @@@ case WR_RELOAD: { if(!self.hagar_load) // require releasing loaded rockets first - W_Reload(min(WEP_CVAR_PRI(hagar, ammo), WEP_CVAR_SEC(hagar, ammo)), "weapons/reload.wav"); - + W_Reload(min(WEP_CVAR_PRI(hagar, ammo), WEP_CVAR_SEC(hagar, ammo)), W_Sound("reload")); + - return TRUE; + return true; } case WR_SUICIDEMESSAGE: { diff --cc qcsrc/common/weapons/w_hlac.qc index b34ba4acd,38f04841a..3301d48ba --- a/qcsrc/common/weapons/w_hlac.qc +++ b/qcsrc/common/weapons/w_hlac.qc @@@ -47,6 -47,6 +47,8 @@@ HLAC_SETTINGS(WEP_ADD_CVAR, WEP_ADD_PRO #endif #else #ifdef SVQC ++#include "../effects.qh" ++ void spawnfunc_weapon_hlac(void) { weapon_defaultspawnfunc(WEP_HLAC); } void W_HLAC_Touch(void) @@@ -76,7 -76,7 +78,7 @@@ void W_HLAC_Attack(void if(self.crouch) spread = spread * WEP_CVAR_PRI(hlac, spread_crouchmod); - W_SetupShot(self, FALSE, 3, W_Sound("lasergun_fire"), CH_WEAPON_A, WEP_CVAR_PRI(hlac, damage)); - W_SetupShot(self, false, 3, "weapons/lasergun_fire.wav", CH_WEAPON_A, WEP_CVAR_PRI(hlac, damage)); ++ W_SetupShot(self, false, 3, W_Sound("lasergun_fire"), CH_WEAPON_A, WEP_CVAR_PRI(hlac, damage)); pointparticles(particleeffectnum("laser_muzzleflash"), w_shotorg, w_shotdir * 1000, 1); if(!autocvar_g_norecoil) { @@@ -124,8 -124,8 +126,8 @@@ void W_HLAC_Attack2(void if(self.crouch) spread = spread * WEP_CVAR_SEC(hlac, spread_crouchmod); - W_SetupShot(self, FALSE, 3, W_Sound("lasergun_fire"), CH_WEAPON_A, WEP_CVAR_SEC(hlac, damage)); - W_SetupShot(self, false, 3, "weapons/lasergun_fire.wav", CH_WEAPON_A, WEP_CVAR_SEC(hlac, damage)); - pointparticles(particleeffectnum("laser_muzzleflash"), w_shotorg, w_shotdir * 1000, 1); ++ W_SetupShot(self, false, 3, W_Sound("lasergun_fire"), CH_WEAPON_A, WEP_CVAR_SEC(hlac, damage)); + Send_Effect(EFFECT_LASER_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1); missile = spawn(); missile.owner = missile.realowner = self; @@@ -240,12 -240,12 +242,12 @@@ float W_HLAC(float req } case WR_INIT: { - precache_model("models/weapons/g_hlac.md3"); - precache_model("models/weapons/v_hlac.md3"); - precache_model("models/weapons/h_hlac.iqm"); - precache_sound("weapons/lasergun_fire.wav"); - HLAC_SETTINGS(WEP_SKIP_CVAR, WEP_SET_PROP); + precache_model(W_Model("g_hlac.md3")); + precache_model(W_Model("v_hlac.md3")); + precache_model(W_Model("h_hlac.iqm")); + precache_sound(W_Sound("lasergun_fire")); + HLAC_SETTINGS(WEP_SKIP_CVAR, WEP_SET_PROP) - return TRUE; + return true; } case WR_CHECKAMMO1: { @@@ -266,8 -266,8 +268,8 @@@ } case WR_RELOAD: { - W_Reload(min(WEP_CVAR_PRI(hlac, ammo), WEP_CVAR_SEC(hlac, ammo)), "weapons/reload.wav"); + W_Reload(min(WEP_CVAR_PRI(hlac, ammo), WEP_CVAR_SEC(hlac, ammo)), W_Sound("reload")); - return TRUE; + return true; } case WR_SUICIDEMESSAGE: { diff --cc qcsrc/common/weapons/w_hmg.qc index 0aef9d82c,8190411cd..77a12c87d --- a/qcsrc/common/weapons/w_hmg.qc +++ b/qcsrc/common/weapons/w_hmg.qc @@@ -60,7 -60,7 +60,7 @@@ void W_HeavyMachineGun_Attack_Auto( W_DecreaseAmmo(WEP_CVAR(hmg, ammo)); - W_SetupShot (self, TRUE, 0, W_Sound("uzi_fire"), CH_WEAPON_A, WEP_CVAR(hmg, damage)); - W_SetupShot (self, true, 0, "weapons/uzi_fire.wav", CH_WEAPON_A, WEP_CVAR(hmg, damage)); ++ W_SetupShot (self, true, 0, W_Sound("uzi_fire"), CH_WEAPON_A, WEP_CVAR(hmg, damage)); if(!autocvar_g_norecoil) { @@@ -92,12 -92,12 +92,12 @@@ float W_HeavyMachineGun(float req { case WR_AIM: { - if(vlen(self.origin-self.enemy.origin) < 3000 - bound(0, skill, 10) * 200) + if(vlen(self.origin-self.enemy.origin) < 3000 - bound(0, bot_skill, 10) * 200) - self.BUTTON_ATCK = bot_aim(1000000, 0, 0.001, FALSE); + self.BUTTON_ATCK = bot_aim(1000000, 0, 0.001, false); else - self.BUTTON_ATCK2 = bot_aim(1000000, 0, 0.001, FALSE); - - return TRUE; + self.BUTTON_ATCK2 = bot_aim(1000000, 0, 0.001, false); + + return true; } case WR_THINK: { @@@ -118,12 -118,12 +118,12 @@@ case WR_INIT: { precache_model ("models/uziflash.md3"); - precache_model ("models/weapons/g_ok_hmg.md3"); - precache_model ("models/weapons/v_ok_hmg.md3"); - precache_model ("models/weapons/h_ok_hmg.iqm"); - precache_sound ("weapons/uzi_fire.wav"); - HMG_SETTINGS(WEP_SKIP_CVAR, WEP_SET_PROP); + precache_model(W_Model("g_ok_hmg.md3")); + precache_model(W_Model("v_ok_hmg.md3")); + precache_model(W_Model("h_ok_hmg.iqm")); + precache_sound (W_Sound("uzi_fire")); + HMG_SETTINGS(WEP_SKIP_CVAR, WEP_SET_PROP) - return TRUE; + return true; } case WR_CHECKAMMO1: { @@@ -150,8 -150,8 +150,8 @@@ } case WR_RELOAD: { - W_Reload(WEP_CVAR(hmg, ammo), "weapons/reload.wav"); + W_Reload(WEP_CVAR(hmg, ammo), W_Sound("reload")); - return TRUE; + return true; } case WR_SUICIDEMESSAGE: { diff --cc qcsrc/common/weapons/w_hook.qc index f30b74901,5d52dabae..8aa51f3ed --- a/qcsrc/common/weapons/w_hook.qc +++ b/qcsrc/common/weapons/w_hook.qc @@@ -134,7 -133,7 +134,7 @@@ void W_Hook_Attack2(void entity gren; //W_DecreaseAmmo(WEP_CVAR_SEC(hook, ammo)); // WEAPONTODO: Figure out how to handle ammo with hook secondary (gravitybomb) - W_SetupShot(self, FALSE, 4, W_Sound("hookbomb_fire"), CH_WEAPON_A, WEP_CVAR_SEC(hook, damage)); - W_SetupShot(self, false, 4, "weapons/hookbomb_fire.wav", CH_WEAPON_A, WEP_CVAR_SEC(hook, damage)); ++ W_SetupShot(self, false, 4, W_Sound("hookbomb_fire"), CH_WEAPON_A, WEP_CVAR_SEC(hook, damage)); gren = spawn(); gren.owner = gren.realowner = self; @@@ -288,14 -287,14 +288,14 @@@ float W_Hook(float req } case WR_INIT: { - precache_model("models/weapons/g_hookgun.md3"); - precache_model("models/weapons/v_hookgun.md3"); - precache_model("models/weapons/h_hookgun.iqm"); - precache_sound("weapons/hook_impact.wav"); // done by g_hook.qc - precache_sound("weapons/hook_fire.wav"); - precache_sound("weapons/hookbomb_fire.wav"); - HOOK_SETTINGS(WEP_SKIP_CVAR, WEP_SET_PROP); + precache_model(W_Model("g_hookgun.md3")); + precache_model(W_Model("v_hookgun.md3")); + precache_model(W_Model("h_hookgun.iqm")); + precache_sound(W_Sound("hook_impact")); // done by g_hook.qc + precache_sound(W_Sound("hook_fire")); + precache_sound(W_Sound("hookbomb_fire")); + HOOK_SETTINGS(WEP_SKIP_CVAR, WEP_SET_PROP) - return TRUE; + return true; } case WR_SETUP: { diff --cc qcsrc/common/weapons/w_lightsabre.qc index f10ea5efb,000000000..c917899ec mode 100644,000000..100644 --- a/qcsrc/common/weapons/w_lightsabre.qc +++ b/qcsrc/common/weapons/w_lightsabre.qc @@@ -1,337 -1,0 +1,340 @@@ +#ifndef CHAOS - const float WEP_LIGHTSABRE = 1337; ++//const int WEP_LIGHTSABRE = 1337; +#else +#ifdef REGISTER_WEAPON +REGISTER_WEAPON( +/* WEP_##id */ LIGHTSABRE, +/* function */ W_Lightsabre, +/* ammotype */ ammo_none, +/* impulse */ 1, +/* flags */ WEP_FLAG_NORMAL | WEP_FLAG_RELOADABLE, +/* rating */ BOT_PICKUP_RATING_LOW, +/* color */ '1 0.25 0.25', +/* modelname */ "lightsabre", +/* simplemdl */ "foobar", +/* crosshair */ "gfx/crosshairlightsabre 0.35", +/* wepimg */ "weaponlightsabre", +/* refname */ "lightsabre", +/* wepname */ _("Lightsabre") +); + +#define LIGHTSABRE_SETTINGS(w_cvar,w_prop) LIGHTSABRE_SETTINGS_LIST(w_cvar, w_prop, LIGHTSABRE, lightsabre) +#define LIGHTSABRE_SETTINGS_LIST(w_cvar,w_prop,id,sn) \ + w_cvar(id, sn, BOTH, animtime) \ + w_cvar(id, sn, BOTH, refire) \ + w_cvar(id, sn, BOTH, damage) \ + w_cvar(id, sn, BOTH, force) \ + w_cvar(id, sn, BOTH, melee_time) \ + w_cvar(id, sn, BOTH, melee_no_doubleslap) \ + w_cvar(id, sn, BOTH, melee_traces) \ + w_cvar(id, sn, BOTH, melee_swing_up) \ + w_cvar(id, sn, BOTH, melee_swing_side) \ + w_cvar(id, sn, BOTH, melee_nonplayerdamage) \ + w_cvar(id, sn, BOTH, melee_multihit) \ + w_cvar(id, sn, BOTH, melee_delay) \ + w_cvar(id, sn, BOTH, melee_range) \ + w_prop(id, sn, float, reloading_ammo, reload_ammo) \ + w_prop(id, sn, float, reloading_time, reload_time) \ + w_prop(id, sn, float, switchdelay_raise, switchdelay_raise) \ + w_prop(id, sn, float, switchdelay_drop, switchdelay_drop) \ + w_prop(id, sn, string, weaponreplace, weaponreplace) \ + w_prop(id, sn, float, weaponstart, weaponstart) \ + w_prop(id, sn, float, weaponstartoverride, weaponstartoverride) \ + w_prop(id, sn, float, weaponthrowable, weaponthrowable) + +#ifdef SVQC +LIGHTSABRE_SETTINGS(WEP_ADD_CVAR, WEP_ADD_PROP) +#endif +#else +#ifdef SVQC ++#include "../../effects.qh" ++#include "../animdecide.qh" ++ +void spawnfunc_weapon_lightsabre(void) { weapon_defaultspawnfunc(WEP_LIGHTSABRE); } + +.float swing_prev; +.entity swing_alreadyhit; + +float W_Lightsabre_Melee_Block(entity player) +{ - if(!IS_PLAYER(player)) { return FALSE; } ++ if(!IS_PLAYER(player)) { return false; } + + entity player_melee = world, e; + entity myowner = self.realowner; + + for(e = world; (e = find(e, classname, "melee_temp")); ) + if(e.realowner == player) + { + player_melee = e; + break; + } + - if(!player_melee) { return FALSE; } ++ if(!player_melee) { return false; } + + makevectors (player.v_angle); + float dot = normalize (myowner.origin - player.origin) * v_forward; + - if(dot <= 0.3) { return FALSE; } ++ if(dot <= 0.3) { return false; } + + //if((myowner.v_angle_x - player.v_angle_x < 70) && (myowner.v_angle_x - player.v_angle_x > -70)) //Look up and down + //if((myowner.v_angle_y - player.v_angle_y > 160) || (myowner.v_angle_y - player.v_angle_y < -160)) //Side to side Facing eachother + // fun stuff + + animdecide_setaction(myowner, ANIMACTION_SHOOT, 1); + string thesound = strcat("lightsabre_hit", ftos(max(1, floor(random() * 4)))); + sound(myowner, CH_WEAPON_A, W_Sound(thesound), VOL_BASE, ATTEN_NORM); + - return TRUE; ++ return true; +} + +void W_Lightsabre_Melee_Think(void) +{ + // declarations + float i, f, swing, swing_factor, swing_damage, meleetime, is_player; + entity target_victim; + vector targpos; + float isprimary = !(self.realowner.BUTTON_ATCK2); + float deathtype = WEP_LIGHTSABRE; + if(!isprimary) + deathtype |= HITTYPE_SECONDARY; + + if(!self.cnt) // set start time of melee + { + self.cnt = time; + } + + makevectors(self.realowner.v_angle); // update values for v_* vectors + + // calculate swing percentage based on time + meleetime = WEP_CVAR_BOTH(lightsabre, isprimary, melee_time) * W_WeaponRateFactor(); + swing = bound(0, (self.cnt + meleetime - time) / meleetime, 10); + f = ((1 - swing) * WEP_CVAR_BOTH(lightsabre, isprimary, melee_traces)); + + // check to see if we can still continue, otherwise give up now + if((self.realowner.deadflag != DEAD_NO) && WEP_CVAR_BOTH(lightsabre, isprimary, melee_no_doubleslap)) + { + remove(self); + return; + } + + // if okay, perform the traces needed for this frame + for(i=self.swing_prev; i < f; ++i) + { + swing_factor = ((1 - (i / WEP_CVAR_BOTH(lightsabre, isprimary, melee_traces))) * 2 - 1); + + targpos = (self.realowner.origin + self.realowner.view_ofs + + (v_forward * WEP_CVAR_BOTH(lightsabre, isprimary, melee_range)) + + (v_up * swing_factor * WEP_CVAR_BOTH(lightsabre, isprimary, melee_swing_up)) + + (v_right * swing_factor * WEP_CVAR_BOTH(lightsabre, isprimary, melee_swing_side))); + - WarpZone_traceline_antilag(self, self.realowner.origin + self.realowner.view_ofs, targpos, FALSE, self.realowner, ANTILAG_LATENCY(self.realowner)); ++ WarpZone_traceline_antilag(self, self.realowner.origin + self.realowner.view_ofs, targpos, false, self.realowner, ANTILAG_LATENCY(self.realowner)); + + // draw lightning beams for debugging + //te_lightning2(world, targpos, self.realowner.origin + self.realowner.view_ofs + v_forward * 5 - v_up * 5); + //te_customflash(targpos, 40, 2, '1 1 1'); + + is_player = (IS_PLAYER(trace_ent) || trace_ent.classname == "body" || (trace_ent.flags & FL_MONSTER)); + + if((trace_fraction < 1) // if trace is good, apply the damage and remove self + && (trace_ent.takedamage == DAMAGE_AIM) + && (trace_ent != self.swing_alreadyhit) + && (is_player || WEP_CVAR_BOTH(lightsabre, isprimary, melee_nonplayerdamage))) + { + target_victim = trace_ent; // so it persists through other calls + + if(!W_Lightsabre_Melee_Block(trace_ent)) + { + if(is_player) // this allows us to be able to nerf the non-player damage done in e.g. assault or onslaught. + swing_damage = (WEP_CVAR_BOTH(lightsabre, isprimary, damage) * min(1, swing_factor + 1)); + else + swing_damage = (WEP_CVAR_BOTH(lightsabre, isprimary, melee_nonplayerdamage) * min(1, swing_factor + 1)); + + //print(strcat(self.realowner.netname, " hitting ", target_victim.netname, " with ", strcat(ftos(swing_damage), " damage (factor: ", ftos(swing_factor), ") at "), ftos(time), " seconds.\n")); + + Damage(target_victim, self.realowner, self.realowner, + swing_damage, deathtype, + self.realowner.origin + self.realowner.view_ofs, + v_forward * WEP_CVAR_BOTH(lightsabre, isprimary, force)); + + string thesound = strcat("lightsabre_hit", ftos(max(1, floor(random() * 4)))); + + sound(self.realowner, CH_WEAPON_A, W_Sound(thesound), VOL_BASE, ATTEN_NORM); + + if(accuracy_isgooddamage(self.realowner, target_victim)) { accuracy_add(self.realowner, WEP_LIGHTSABRE, 0, swing_damage); } + } + + // draw large red flash for debugging + //te_customflash(targpos, 200, 2, '15 0 0'); + + if(WEP_CVAR_BOTH(lightsabre, isprimary, melee_multihit)) // allow multiple hits with one swing, but not against the same player twice. + { + self.swing_alreadyhit = target_victim; + continue; // move along to next trace + } + else + { + remove(self); + return; + } + } + } + + if(time >= self.cnt + meleetime) + { + // melee is finished + remove(self); + return; + } + else + { + // set up next frame + self.swing_prev = i; + self.nextthink = time; + } +} + +void W_Lightsabre_Attack(void) +{ + float isprimary = !(self.BUTTON_ATCK2); + + sound(self, CH_WEAPON_A, ((isprimary) ? W_Sound("lightsabre_melee2") : W_Sound("lightsabre_melee1")), VOL_BASE, ATTEN_NORM); + weapon_thinkf(((isprimary) ? WFRAME_FIRE2 : WFRAME_FIRE1), WEP_CVAR_BOTH(lightsabre, isprimary, animtime), w_ready); + + entity meleetemp; + meleetemp = spawn(); + meleetemp.classname = "melee_temp"; + meleetemp.realowner = self; + meleetemp.think = W_Lightsabre_Melee_Think; + meleetemp.nextthink = time + WEP_CVAR_BOTH(lightsabre, isprimary, melee_delay) * W_WeaponRateFactor(); - W_SetupShot_Range(self, TRUE, 0, "", 0, WEP_CVAR_BOTH(lightsabre, isprimary, damage), WEP_CVAR_BOTH(lightsabre, isprimary, melee_range)); ++ W_SetupShot_Range(self, true, 0, "", 0, WEP_CVAR_BOTH(lightsabre, isprimary, damage), WEP_CVAR_BOTH(lightsabre, isprimary, melee_range)); +} + +.float lightsabre_active; + +void W_LightSabre_SetActive(float newactive, float dosound) +{ + if(newactive) + { - self.lightsabre_active = TRUE; ++ self.lightsabre_active = true; + self.weaponname = "lightsabre_active"; + if(dosound) + sound(self, CH_WEAPON_A, W_Sound("lightsabre_activate"), VOL_BASE, ATTEN_NORM); + } + else + { - self.lightsabre_active = FALSE; ++ self.lightsabre_active = false; + self.weaponname = "lightsabre"; + if(dosound) + sound(self, CH_WEAPON_A, W_Sound("lightsabre_deactivate"), VOL_BASE, ATTEN_NORM); + } +} + +float W_Lightsabre(float req) +{ + switch(req) + { + case WR_AIM: + { + if(random() >= 0.5) - self.BUTTON_ATCK2 = bot_aim(1000000, 0, 0.001, FALSE); ++ self.BUTTON_ATCK2 = bot_aim(1000000, 0, 0.001, false); + else - self.BUTTON_ATCK = bot_aim(1000000, 0, 0.001, FALSE); ++ self.BUTTON_ATCK = bot_aim(1000000, 0, 0.001, false); + - return TRUE; ++ return true; + } + case WR_THINK: + { + if(self.BUTTON_ATCK || self.BUTTON_ATCK2) + if(weapon_prepareattack(self.BUTTON_ATCK2, WEP_CVAR_BOTH(lightsabre, self.BUTTON_ATCK2, refire))) + { + if(!self.lightsabre_active) + { - W_LightSabre_SetActive(1, TRUE); ++ W_LightSabre_SetActive(1, true); + weapon_thinkf(WFRAME_RELOAD, WEP_CVAR_BOTH(lightsabre, self.BUTTON_ATCK2, animtime), w_ready); + } + else + weapon_thinkf(WFRAME_FIRE1, 0, W_Lightsabre_Attack); + } + - return TRUE; ++ return true; + } + case WR_INIT: + { + precache_model("models/uziflash.md3"); + precache_model(W_Model("g_lightsabre.md3")); + precache_model(W_Model("v_lightsabre.md3")); + precache_model(W_Model("h_lightsabre.iqm")); + precache_model(W_Model("v_lightsabre_active.md3")); + precache_model(W_Model("h_lightsabre_active.iqm")); + precache_sound("misc/itempickup.wav"); + precache_sound(W_Sound("lightsabre_melee1")); + precache_sound(W_Sound("lightsabre_melee2")); + precache_sound(W_Sound("lightsabre_activate")); + precache_sound(W_Sound("lightsabre_deactivate")); + precache_sound(W_Sound("lightsabre_hit1")); + precache_sound(W_Sound("lightsabre_hit2")); + precache_sound(W_Sound("lightsabre_hit3")); + LIGHTSABRE_SETTINGS(WEP_SKIP_CVAR, WEP_SET_PROP) - return TRUE; ++ return true; + } + case WR_SETUP: + { + self.ammo_field = ammo_none; - W_LightSabre_SetActive(0, FALSE); - return TRUE; ++ W_LightSabre_SetActive(0, false); ++ return true; + } + case WR_CHECKAMMO1: + { - return TRUE; ++ return true; + } + case WR_CHECKAMMO2: + { - return TRUE; ++ return true; + } + case WR_CONFIG: + { + LIGHTSABRE_SETTINGS(WEP_CONFIG_WRITE_CVARS, WEP_CONFIG_WRITE_PROPS) - return TRUE; ++ return true; + } + case WR_RELOAD: + { - W_LightSabre_SetActive(((self.lightsabre_active) ? 0 : 1), TRUE); - return TRUE; ++ W_LightSabre_SetActive(((self.lightsabre_active) ? 0 : 1), true); ++ return true; + } + case WR_SUICIDEMESSAGE: + { + return WEAPON_THINKING_WITH_PORTALS; + } + case WR_KILLMESSAGE: + { + return WEAPON_LIGHTSABRE_MURDER; + } + } - return FALSE; ++ return false; +} +#endif +#ifdef CSQC +.float prevric; +float W_Lightsabre(float req) +{ + switch(req) + { + case WR_IMPACTEFFECT: + { - return TRUE; ++ return true; + } + case WR_INIT: + { - return TRUE; ++ return true; + } + case WR_ZOOMRETICLE: + { + // no weapon specific image for this weapon - return FALSE; ++ return false; + } + } - return FALSE; ++ return false; +} +#endif +#endif +#endif diff --cc qcsrc/common/weapons/w_machinegun.qc index 8714aa717,14a59e398..ccdbce212 --- a/qcsrc/common/weapons/w_machinegun.qc +++ b/qcsrc/common/weapons/w_machinegun.qc @@@ -103,7 -103,7 +103,7 @@@ void W_MachineGun_MuzzleFlash(void void W_MachineGun_Attack(float deathtype) { - W_SetupShot(self, TRUE, 0, W_Sound("uzi_fire"), CH_WEAPON_A, ((self.misc_bulletcounter == 1) ? WEP_CVAR(machinegun, first_damage) : WEP_CVAR(machinegun, sustained_damage))); - W_SetupShot(self, true, 0, "weapons/uzi_fire.wav", CH_WEAPON_A, ((self.misc_bulletcounter == 1) ? WEP_CVAR(machinegun, first_damage) : WEP_CVAR(machinegun, sustained_damage))); ++ W_SetupShot(self, true, 0, W_Sound("uzi_fire"), CH_WEAPON_A, ((self.misc_bulletcounter == 1) ? WEP_CVAR(machinegun, first_damage) : WEP_CVAR(machinegun, sustained_damage))); if(!autocvar_g_norecoil) { self.punchangle_x = random() - 0.5; @@@ -179,7 -179,7 +179,7 @@@ void W_MachineGun_Attack_Auto(void W_DecreaseAmmo(WEP_CVAR(machinegun, sustained_ammo)); - W_SetupShot(self, TRUE, 0, W_Sound("uzi_fire"), CH_WEAPON_A, WEP_CVAR(machinegun, sustained_damage)); - W_SetupShot(self, true, 0, "weapons/uzi_fire.wav", CH_WEAPON_A, WEP_CVAR(machinegun, sustained_damage)); ++ W_SetupShot(self, true, 0, W_Sound("uzi_fire"), CH_WEAPON_A, WEP_CVAR(machinegun, sustained_damage)); if(!autocvar_g_norecoil) { self.punchangle_x = random() - 0.5; @@@ -205,7 -205,7 +205,7 @@@ void W_MachineGun_Attack_Burst(void) { - W_SetupShot(self, TRUE, 0, W_Sound("uzi_fire"), CH_WEAPON_A, WEP_CVAR(machinegun, sustained_damage)); - W_SetupShot(self, true, 0, "weapons/uzi_fire.wav", CH_WEAPON_A, WEP_CVAR(machinegun, sustained_damage)); ++ W_SetupShot(self, true, 0, W_Sound("uzi_fire"), CH_WEAPON_A, WEP_CVAR(machinegun, sustained_damage)); if(!autocvar_g_norecoil) { self.punchangle_x = random() - 0.5; @@@ -242,12 -242,12 +242,12 @@@ float W_MachineGun(float req { case WR_AIM: { - if(vlen(self.origin-self.enemy.origin) < 3000 - bound(0, skill, 10) * 200) + if(vlen(self.origin-self.enemy.origin) < 3000 - bound(0, bot_skill, 10) * 200) - self.BUTTON_ATCK = bot_aim(1000000, 0, 0.001, FALSE); + self.BUTTON_ATCK = bot_aim(1000000, 0, 0.001, false); else - self.BUTTON_ATCK2 = bot_aim(1000000, 0, 0.001, FALSE); - - return TRUE; + self.BUTTON_ATCK2 = bot_aim(1000000, 0, 0.001, false); + + return true; } case WR_THINK: { @@@ -304,12 -304,12 +304,12 @@@ case WR_INIT: { precache_model("models/uziflash.md3"); - precache_model("models/weapons/g_uzi.md3"); - precache_model("models/weapons/v_uzi.md3"); - precache_model("models/weapons/h_uzi.iqm"); - precache_sound("weapons/uzi_fire.wav"); - MACHINEGUN_SETTINGS(WEP_SKIP_CVAR, WEP_SET_PROP); + precache_model(W_Model("g_uzi.md3")); + precache_model(W_Model("v_uzi.md3")); + precache_model(W_Model("h_uzi.iqm")); + precache_sound(W_Sound("uzi_fire")); + MACHINEGUN_SETTINGS(WEP_SKIP_CVAR, WEP_SET_PROP) - return TRUE; + return true; } case WR_CHECKAMMO1: { @@@ -350,8 -350,8 +350,8 @@@ } case WR_RELOAD: { - W_Reload(min(max(WEP_CVAR(machinegun, sustained_ammo), WEP_CVAR(machinegun, first_ammo)), WEP_CVAR(machinegun, burst_ammo)), "weapons/reload.wav"); + W_Reload(min(max(WEP_CVAR(machinegun, sustained_ammo), WEP_CVAR(machinegun, first_ammo)), WEP_CVAR(machinegun, burst_ammo)), W_Sound("reload")); - return TRUE; + return true; } case WR_SUICIDEMESSAGE: { diff --cc qcsrc/common/weapons/w_minelayer.qc index b6df9e4a0,2fe0172a9..f3972380e --- a/qcsrc/common/weapons/w_minelayer.qc +++ b/qcsrc/common/weapons/w_minelayer.qc @@@ -56,6 -56,6 +56,8 @@@ void W_MineLayer_Think(void) #endif #else #ifdef SVQC ++#include "../effects.qh" ++ void spawnfunc_weapon_minelayer(void) { weapon_defaultspawnfunc(WEP_MINE_LAYER); } void W_MineLayer_Stick(entity to) @@@ -326,8 -326,8 +328,8 @@@ void W_MineLayer_Attack(void W_DecreaseAmmo(WEP_CVAR(minelayer, ammo)); - W_SetupShot_ProjectileSize(self, '-4 -4 -4', '4 4 4', FALSE, 5, W_Sound("mine_fire"), CH_WEAPON_A, WEP_CVAR(minelayer, damage)); - W_SetupShot_ProjectileSize(self, '-4 -4 -4', '4 4 4', false, 5, "weapons/mine_fire.wav", CH_WEAPON_A, WEP_CVAR(minelayer, damage)); - pointparticles(particleeffectnum("rocketlauncher_muzzleflash"), w_shotorg, w_shotdir * 1000, 1); ++ W_SetupShot_ProjectileSize(self, '-4 -4 -4', '4 4 4', false, 5, W_Sound("mine_fire"), CH_WEAPON_A, WEP_CVAR(minelayer, damage)); + Send_Effect(EFFECT_ROCKET_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1); mine = WarpZone_RefSys_SpawnSameRefSys(self); mine.owner = mine.realowner = self; @@@ -410,10 -410,10 +412,10 @@@ float W_MineLayer(float req { // aim and decide to fire if appropriate if(self.minelayer_mines >= WEP_CVAR(minelayer, limit)) - self.BUTTON_ATCK = FALSE; + self.BUTTON_ATCK = false; else - self.BUTTON_ATCK = bot_aim(WEP_CVAR(minelayer, speed), 0, WEP_CVAR(minelayer, lifetime), FALSE); + self.BUTTON_ATCK = bot_aim(WEP_CVAR(minelayer, speed), 0, WEP_CVAR(minelayer, lifetime), false); - if(skill >= 2) // skill 0 and 1 bots won't detonate mines! + if(bot_skill >= 2) // skill 0 and 1 bots won't detonate mines! { // decide whether to detonate mines entity targetlist, targ; @@@ -486,8 -486,8 +488,8 @@@ if(v_forward * normalize(mine.origin - self.enemy.origin)< 0.1) if(IS_PLAYER(self.enemy)) if(desirabledamage >= 0.1*coredamage) - if(random()/distance*300 > frametime*bound(0,(10-skill)*0.2,1)) + if(random()/distance*300 > frametime*bound(0,(10-bot_skill)*0.2,1)) - self.BUTTON_ATCK2 = TRUE; + self.BUTTON_ATCK2 = true; // dprint(ftos(random()/distance*300),">");dprint(ftos(frametime*bound(0,(10-skill)*0.2,1)),"\n"); } @@@ -496,15 -496,15 +498,15 @@@ // if we would be doing at X percent of the core damage, detonate it // but don't fire a new shot at the same time! if(desirabledamage >= 0.75 * coredamage) //this should do group damage in rare fortunate events - self.BUTTON_ATCK2 = TRUE; + self.BUTTON_ATCK2 = true; - if((skill > 6.5) && (selfdamage > self.health)) + if((bot_skill > 6.5) && (selfdamage > self.health)) - self.BUTTON_ATCK2 = FALSE; - //if(self.BUTTON_ATCK2 == TRUE) + self.BUTTON_ATCK2 = false; + //if(self.BUTTON_ATCK2 == true) // dprint(ftos(desirabledamage),"\n"); - if(self.BUTTON_ATCK2 == TRUE) self.BUTTON_ATCK = FALSE; + if(self.BUTTON_ATCK2 == true) self.BUTTON_ATCK = false; } - - return TRUE; + + return true; } case WR_THINK: { @@@ -525,25 -525,25 +527,25 @@@ if(self.BUTTON_ATCK2) { - if(W_MineLayer_PlacedMines(TRUE)) + if(W_MineLayer_PlacedMines(true)) - sound(self, CH_WEAPON_B, "weapons/mine_det.wav", VOL_BASE, ATTN_NORM); + sound(self, CH_WEAPON_B, W_Sound("mine_det"), VOL_BASE, ATTN_NORM); } - - return TRUE; + + return true; } case WR_INIT: { precache_model("models/flash.md3"); precache_model("models/mine.md3"); - precache_model("models/weapons/g_minelayer.md3"); - precache_model("models/weapons/v_minelayer.md3"); - precache_model("models/weapons/h_minelayer.iqm"); - precache_sound("weapons/mine_det.wav"); - precache_sound("weapons/mine_fire.wav"); - precache_sound("weapons/mine_stick.wav"); - precache_sound("weapons/mine_trigger.wav"); - MINELAYER_SETTINGS(WEP_SKIP_CVAR, WEP_SET_PROP); + precache_model(W_Model("g_minelayer.md3")); + precache_model(W_Model("v_minelayer.md3")); + precache_model(W_Model("h_minelayer.iqm")); + precache_sound(W_Sound("mine_det")); + precache_sound(W_Sound("mine_fire")); + precache_sound(W_Sound("mine_stick")); + precache_sound(W_Sound("mine_trigger")); + MINELAYER_SETTINGS(WEP_SKIP_CVAR, WEP_SET_PROP) - return TRUE; + return true; } case WR_CHECKAMMO1: { @@@ -575,8 -575,8 +577,8 @@@ } case WR_RELOAD: { - W_Reload(WEP_CVAR(minelayer, ammo), "weapons/reload.wav"); + W_Reload(WEP_CVAR(minelayer, ammo), W_Sound("reload")); - return TRUE; + return true; } case WR_SUICIDEMESSAGE: { diff --cc qcsrc/common/weapons/w_mortar.qc index e531756ef,34b5e5747..95d93d345 --- a/qcsrc/common/weapons/w_mortar.qc +++ b/qcsrc/common/weapons/w_mortar.qc @@@ -184,24 -184,24 +184,24 @@@ void W_Mortar_Grenade_Touch2(void float r; r = random() * 6; if(r < 1) - spamsound(self, CH_SHOTS, "weapons/grenade_bounce1.wav", VOL_BASE, ATTN_NORM); + spamsound(self, CH_SHOTS, W_Sound("grenade_bounce1"), VOL_BASE, ATTN_NORM); else if(r < 2) - spamsound(self, CH_SHOTS, "weapons/grenade_bounce2.wav", VOL_BASE, ATTN_NORM); + spamsound(self, CH_SHOTS, W_Sound("grenade_bounce2"), VOL_BASE, ATTN_NORM); else if(r < 3) - spamsound(self, CH_SHOTS, "weapons/grenade_bounce3.wav", VOL_BASE, ATTN_NORM); + spamsound(self, CH_SHOTS, W_Sound("grenade_bounce3"), VOL_BASE, ATTN_NORM); else if(r < 4) - spamsound(self, CH_SHOTS, "weapons/grenade_bounce4.wav", VOL_BASE, ATTN_NORM); + spamsound(self, CH_SHOTS, W_Sound("grenade_bounce4"), VOL_BASE, ATTN_NORM); else if(r < 5) - spamsound(self, CH_SHOTS, "weapons/grenade_bounce5.wav", VOL_BASE, ATTN_NORM); + spamsound(self, CH_SHOTS, W_Sound("grenade_bounce5"), VOL_BASE, ATTN_NORM); else - spamsound(self, CH_SHOTS, "weapons/grenade_bounce6.wav", VOL_BASE, ATTN_NORM); - pointparticles(particleeffectnum("hagar_bounce"), self.origin, self.velocity, 1); + spamsound(self, CH_SHOTS, W_Sound("grenade_bounce6"), VOL_BASE, ATTN_NORM); + Send_Effect(EFFECT_HAGAR_BOUNCE, self.origin, self.velocity, 1); self.projectiledeathtype |= HITTYPE_BOUNCE; self.gl_bouncecnt += 1; - + if(WEP_CVAR_SEC(mortar, lifetime_bounce) && self.gl_bouncecnt == 1) self.nextthink = time + WEP_CVAR_SEC(mortar, lifetime_bounce); - + } else if(WEP_CVAR_SEC(mortar, type) == 2 && (!other || (other.takedamage != DAMAGE_AIM && other.movetype == MOVETYPE_NONE))) // stick { @@@ -227,10 -227,10 +227,10 @@@ void W_Mortar_Attack(void W_DecreaseAmmo(WEP_CVAR_PRI(mortar, ammo)); - W_SetupShot_ProjectileSize(self, '-3 -3 -3', '3 3 3', FALSE, 4, W_Sound("grenade_fire"), CH_WEAPON_A, WEP_CVAR_PRI(mortar, damage)); - W_SetupShot_ProjectileSize(self, '-3 -3 -3', '3 3 3', false, 4, "weapons/grenade_fire.wav", CH_WEAPON_A, WEP_CVAR_PRI(mortar, damage)); ++ W_SetupShot_ProjectileSize(self, '-3 -3 -3', '3 3 3', false, 4, W_Sound("grenade_fire"), CH_WEAPON_A, WEP_CVAR_PRI(mortar, damage)); w_shotdir = v_forward; // no TrueAim for grenades please - pointparticles(particleeffectnum("grenadelauncher_muzzleflash"), w_shotorg, w_shotdir * 1000, 1); + Send_Effect(EFFECT_GRENADE_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1); gren = spawn(); gren.owner = gren.realowner = self; @@@ -276,10 -276,10 +276,10 @@@ void W_Mortar_Attack2(void W_DecreaseAmmo(WEP_CVAR_SEC(mortar, ammo)); - W_SetupShot_ProjectileSize(self, '-3 -3 -3', '3 3 3', FALSE, 4, W_Sound("grenade_fire"), CH_WEAPON_A, WEP_CVAR_SEC(mortar, damage)); - W_SetupShot_ProjectileSize(self, '-3 -3 -3', '3 3 3', false, 4, "weapons/grenade_fire.wav", CH_WEAPON_A, WEP_CVAR_SEC(mortar, damage)); ++ W_SetupShot_ProjectileSize(self, '-3 -3 -3', '3 3 3', false, 4, W_Sound("grenade_fire"), CH_WEAPON_A, WEP_CVAR_SEC(mortar, damage)); w_shotdir = v_forward; // no TrueAim for grenades please - pointparticles(particleeffectnum("grenadelauncher_muzzleflash"), w_shotorg, w_shotdir * 1000, 1); + Send_Effect(EFFECT_GRENADE_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1); gren = spawn(); gren.owner = gren.realowner = self; @@@ -404,19 -404,19 +404,19 @@@ float W_Mortar(float req } case WR_INIT: { - precache_model("models/weapons/g_gl.md3"); - precache_model("models/weapons/v_gl.md3"); - precache_model("models/weapons/h_gl.iqm"); - precache_sound("weapons/grenade_bounce1.wav"); - precache_sound("weapons/grenade_bounce2.wav"); - precache_sound("weapons/grenade_bounce3.wav"); - precache_sound("weapons/grenade_bounce4.wav"); - precache_sound("weapons/grenade_bounce5.wav"); - precache_sound("weapons/grenade_bounce6.wav"); - precache_sound("weapons/grenade_stick.wav"); - precache_sound("weapons/grenade_fire.wav"); - MORTAR_SETTINGS(WEP_SKIP_CVAR, WEP_SET_PROP); + precache_model(W_Model("g_gl.md3")); + precache_model(W_Model("v_gl.md3")); + precache_model(W_Model("h_gl.iqm")); + precache_sound(W_Sound("grenade_bounce1")); + precache_sound(W_Sound("grenade_bounce2")); + precache_sound(W_Sound("grenade_bounce3")); + precache_sound(W_Sound("grenade_bounce4")); + precache_sound(W_Sound("grenade_bounce5")); + precache_sound(W_Sound("grenade_bounce6")); + precache_sound(W_Sound("grenade_stick")); + precache_sound(W_Sound("grenade_fire")); + MORTAR_SETTINGS(WEP_SKIP_CVAR, WEP_SET_PROP) - return TRUE; + return true; } case WR_CHECKAMMO1: { @@@ -437,8 -437,8 +437,8 @@@ } case WR_RELOAD: { - W_Reload(min(WEP_CVAR_PRI(mortar, ammo), WEP_CVAR_SEC(mortar, ammo)), "weapons/reload.wav"); // WEAPONTODO + W_Reload(min(WEP_CVAR_PRI(mortar, ammo), WEP_CVAR_SEC(mortar, ammo)), W_Sound("reload")); // WEAPONTODO - return TRUE; + return true; } case WR_SUICIDEMESSAGE: { diff --cc qcsrc/common/weapons/w_porto.qc index fb14e9861,0010449a0..6f49dbffd --- a/qcsrc/common/weapons/w_porto.qc +++ b/qcsrc/common/weapons/w_porto.qc @@@ -38,6 -38,6 +38,8 @@@ PORTO_SETTINGS(WEP_ADD_CVAR, WEP_ADD_PR #endif #else #ifdef SVQC ++#include "../effects.qh" ++ void spawnfunc_weapon_porto(void) { weapon_defaultspawnfunc(WEP_PORTO); } void W_Porto_Success(void) diff --cc qcsrc/common/weapons/w_revolver.qc index 7f309cb16,000000000..0f117a401 mode 100644,000000..100644 --- a/qcsrc/common/weapons/w_revolver.qc +++ b/qcsrc/common/weapons/w_revolver.qc @@@ -1,242 -1,0 +1,244 @@@ +#ifndef CHAOS - const float WEP_REVOLVER = 1337; // abuse ++//const int WEP_REVOLVER = 1337; // abuse +#else +#ifdef REGISTER_WEAPON +REGISTER_WEAPON( +/* WEP_##id */ REVOLVER, +/* function */ W_Revolver, +/* ammotype */ ammo_shells, +/* impulse */ 2, +/* flags */ WEP_FLAG_NORMAL | WEP_FLAG_RELOADABLE | WEP_TYPE_HITSCAN, +/* rating */ BOT_PICKUP_RATING_LOW, +/* color */ '1 1 0.3', +/* modelname */ "revolver", +/* simplemdl */ "foobar", +/* crosshair */ "gfx/crosshairrevolver 0.4", +/* wepimg */ "weaponrevolver", +/* refname */ "revolver", +/* wepname */ _("Revolver") +); + +#define REVOLVER_SETTINGS(w_cvar,w_prop) REVOLVER_SETTINGS_LIST(w_cvar, w_prop, REVOLVER, revolver) +#define REVOLVER_SETTINGS_LIST(w_cvar,w_prop,id,sn) \ + w_cvar(id, sn, NONE, ammo) \ + w_cvar(id, sn, NONE, animtime) \ + w_cvar(id, sn, NONE, refire) \ + w_cvar(id, sn, NONE, damage) \ + w_cvar(id, sn, NONE, force) \ + w_cvar(id, sn, NONE, solidpenetration) \ + w_cvar(id, sn, NONE, spread) \ + w_cvar(id, sn, NONE, load_time) \ + w_cvar(id, sn, NONE, load_refire) \ + w_prop(id, sn, float, reloading_ammo, reload_ammo) \ + w_prop(id, sn, float, reloading_time, reload_time) \ + w_prop(id, sn, float, switchdelay_raise, switchdelay_raise) \ + w_prop(id, sn, float, switchdelay_drop, switchdelay_drop) \ + w_prop(id, sn, string, weaponreplace, weaponreplace) \ + w_prop(id, sn, float, weaponstart, weaponstart) \ + w_prop(id, sn, float, weaponstartoverride, weaponstartoverride) \ + w_prop(id, sn, float, weaponthrowable, weaponthrowable) + +#ifdef SVQC +REVOLVER_SETTINGS(WEP_ADD_CVAR, WEP_ADD_PROP) +#endif +#else +#ifdef SVQC ++#include "../effects.qh" ++ +void spawnfunc_weapon_revolver(void) { weapon_defaultspawnfunc(WEP_REVOLVER); } + +void W_Revolver_Attack() +{ + entity flash; + + W_DecreaseAmmo(WEP_CVAR(revolver, ammo)); + - W_SetupShot(self, TRUE, 5, W_Sound("revolver_fire"), CH_WEAPON_A, WEP_CVAR(revolver, damage)); ++ W_SetupShot(self, true, 5, W_Sound("revolver_fire"), CH_WEAPON_A, WEP_CVAR(revolver, damage)); + fireBullet(w_shotorg, w_shotdir, WEP_CVAR(revolver, spread), WEP_CVAR(revolver, solidpenetration), WEP_CVAR(revolver, damage), WEP_CVAR(revolver, force), WEP_REVOLVER, 0); + + Send_Effect(EFFECT_SHOTGUN_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, WEP_CVAR(revolver, ammo)); + + // muzzle flash for 1st person view + flash = spawn(); + setmodel(flash, "models/uziflash.md3"); // precision set below + flash.think = SUB_Remove; + flash.nextthink = time + 0.06; + flash.effects = EF_ADDITIVE | EF_FULLBRIGHT | EF_LOWPRECISION; + W_AttachToShotorg(flash, '5 0 0'); +} + +.float revolver_primarytime; +.float revolver_load; +.float revolver_loadspamtime; + +void W_Revolver_SetLoad(float newload) +{ + if(newload == self.revolver_load) { return; } + + self.revolver_load = newload; + + self.weaponname = ((newload) ? "revolver-cocked" : "revolver"); + + // casing code + if(autocvar_g_casings >= 1) + if(!newload) + SpawnCasing(((random() * 50 + 50) * v_right) - (v_forward * (random() * 25 + 25)) - ((random() * 5 - 30) * v_up), 2, vectoangles(v_forward),'0 250 0', 100, 1, self); +} + +float W_Revolver_CheckLoad() +{ - if(!self.revolver_load) { return TRUE; } ++ if(!self.revolver_load) { return true; } + else if(time >= self.revolver_loadspamtime && IS_REAL_CLIENT(self) && self.BUTTON_ATCK && !self.BUTTON_ATCK2) // TODO + { + self.revolver_loadspamtime = time + WEP_CVAR(revolver, refire) * W_WeaponRateFactor(); + play2(self, W_Sound("dryfire")); + sprint(self, "Please use secondary fire to load the revolver!\n"); - return FALSE; ++ return false; + } + - return FALSE; ++ return false; +} + +float W_Revolver(float req) +{ + float ammo_amount; + switch(req) + { + case WR_AIM: + { + if(!W_Revolver_CheckLoad()) - self.BUTTON_ATCK2 = TRUE; ++ self.BUTTON_ATCK2 = true; + else - self.BUTTON_ATCK = bot_aim(1000000, 0, 0.001, FALSE); ++ self.BUTTON_ATCK = bot_aim(1000000, 0, 0.001, false); + - return TRUE; ++ return true; + } + case WR_THINK: + { + if(WEP_CVAR(revolver, reload_ammo) && self.clip_load < WEP_CVAR(revolver, ammo)) // forced reload + { + WEP_ACTION(self.weapon, WR_RELOAD); + } + else + { + if(self.BUTTON_ATCK) + if(W_Revolver_CheckLoad()) + if(time >= self.revolver_primarytime) // handle refire separately so the secondary can be fired straight after a primary + { + if(weapon_prepareattack(0, WEP_CVAR(revolver, animtime))) + { + W_Revolver_Attack(); + self.revolver_primarytime = time + WEP_CVAR(revolver, refire) * W_WeaponRateFactor(); + self.revolver_loadspamtime = time + self.revolver_primarytime; // just enough to not spam it this frame + W_Revolver_SetLoad(1); + weapon_thinkf(WFRAME_FIRE1, WEP_CVAR(revolver, animtime), w_ready); + } + } + } + if(self.BUTTON_ATCK2) + if(!W_Revolver_CheckLoad()) + if(weapon_prepareattack(1, WEP_CVAR(revolver, load_refire))) + { + W_Revolver_SetLoad(0); + weapon_thinkf(WFRAME_FIRE1, WEP_CVAR(revolver, load_time), w_ready); + } + - return TRUE; ++ return true; + } + case WR_INIT: + { + precache_model("models/uziflash.md3"); + precache_model(W_Model("g_revolver.md3")); + precache_model(W_Model("v_revolver.md3")); + precache_model(W_Model("h_revolver.iqm")); + precache_model(W_Model("v_revolver-cocked.md3")); + precache_model(W_Model("h_revolver-cocked.iqm")); + precache_sound("misc/itempickup.wav"); + precache_sound(W_Sound("revolver_fire")); + REVOLVER_SETTINGS(WEP_SKIP_CVAR, WEP_SET_PROP) - return TRUE; ++ return true; + } + case WR_SETUP: + { + W_Revolver_SetLoad(0); - return TRUE; ++ return true; + } + case WR_CHECKAMMO1: + { + ammo_amount = self.WEP_AMMO(REVOLVER) >= WEP_CVAR(revolver, ammo); + ammo_amount += self.(weapon_load[WEP_REVOLVER]) >= WEP_CVAR(revolver, ammo); + return ammo_amount; + } + case WR_CHECKAMMO2: + { + ammo_amount = self.WEP_AMMO(REVOLVER) >= WEP_CVAR(revolver, ammo); + ammo_amount += self.(weapon_load[WEP_REVOLVER]) >= WEP_CVAR(revolver, ammo); + if(ammo_amount >= 1) + return !W_Revolver_CheckLoad(); + else - return FALSE; ++ return false; + } + case WR_CONFIG: + { + REVOLVER_SETTINGS(WEP_CONFIG_WRITE_CVARS, WEP_CONFIG_WRITE_PROPS) - return TRUE; ++ return true; + } + case WR_RELOAD: + { + W_Reload(WEP_CVAR(revolver, ammo), W_Sound("reload")); // WEAPONTODO - return TRUE; ++ return true; + } + case WR_SUICIDEMESSAGE: + { + return WEAPON_THINKING_WITH_PORTALS; + } + case WR_KILLMESSAGE: + { + return WEAPON_REVOLVER_MURDER; + } + } - return FALSE; ++ return false; +} +#endif +#ifdef CSQC +.float prevric; +float W_Revolver(float req) +{ + switch(req) + { + case WR_IMPACTEFFECT: + { + vector org2; + org2 = w_org + w_backoff * 2; + pointparticles(particleeffectnum("shotgun_impact"), org2, w_backoff * 1000, 1); + if(!w_issilent && time - self.prevric > 0.25) + { + if(w_random < 0.0165) + sound(self, CH_SHOTS, "weapons/ric1.wav", VOL_BASE, ATTEN_NORM); + else if(w_random < 0.033) + sound(self, CH_SHOTS, "weapons/ric2.wav", VOL_BASE, ATTEN_NORM); + else if(w_random < 0.05) + sound(self, CH_SHOTS, "weapons/ric3.wav", VOL_BASE, ATTEN_NORM); + self.prevric = time; + } + - return TRUE; ++ return true; + } + case WR_INIT: + { + precache_sound("weapons/ric1.wav"); + precache_sound("weapons/ric2.wav"); + precache_sound("weapons/ric3.wav"); - return TRUE; ++ return true; + } + case WR_ZOOMRETICLE: + { + // no weapon specific image for this weapon - return FALSE; ++ return false; + } + } - return FALSE; ++ return false; +} +#endif +#endif +#endif diff --cc qcsrc/common/weapons/w_rifle.qc index 7257c6694,f20872f70..cb2e6280c --- a/qcsrc/common/weapons/w_rifle.qc +++ b/qcsrc/common/weapons/w_rifle.qc @@@ -7,9 -7,9 +7,9 @@@ REGISTER_WEAPON /* flags */ WEP_FLAG_MUTATORBLOCKED | WEP_FLAG_RELOADABLE | WEP_TYPE_HITSCAN, /* rating */ BOT_PICKUP_RATING_MID, /* color */ '0.5 1 0', -/* modelname */ "campingrifle", +/* modelname */ "sniperrifle", /* simplemdl */ "foobar", - /* crosshair */ "gfx/crosshairrifle 0.5", + /* crosshair */ "gfx/crosshairrifle 0.6", /* wepimg */ "weaponrifle", /* refname */ "rifle", /* wepname */ _("Rifle") @@@ -46,6 -46,6 +46,8 @@@ RIFLE_SETTINGS(WEP_ADD_CVAR, WEP_ADD_PR #endif #else #ifdef SVQC ++#include "../effects.qh" ++ void spawnfunc_weapon_rifle(void) { weapon_defaultspawnfunc(WEP_RIFLE); } void spawnfunc_weapon_campingrifle(void) { spawnfunc_weapon_rifle(); } void spawnfunc_weapon_sniperrifle(void) { spawnfunc_weapon_rifle(); } @@@ -56,9 -56,9 +58,9 @@@ void W_Rifle_FireBullet(float pSpread, W_DecreaseAmmo(pAmmo); - W_SetupShot(self, TRUE, 2, pSound, CH_WEAPON_A, pDamage * pShots); + W_SetupShot(self, true, 2, pSound, CH_WEAPON_A, pDamage * pShots); - pointparticles(particleeffectnum("rifle_muzzleflash"), w_shotorg, w_shotdir * 2000, 1); + Send_Effect(EFFECT_RIFLE_MUZZLEFLASH, w_shotorg, w_shotdir * 2000, 1); if(self.BUTTON_ZOOM | self.BUTTON_ZOOMSCRIPT) // if zoomed, shoot from the eye { @@@ -203,13 -203,13 +205,13 @@@ float W_Rifle(float req } case WR_INIT: { - precache_model("models/weapons/g_campingrifle.md3"); - precache_model("models/weapons/v_campingrifle.md3"); - precache_model("models/weapons/h_campingrifle.iqm"); - precache_sound("weapons/campingrifle_fire.wav"); - precache_sound("weapons/campingrifle_fire2.wav"); - RIFLE_SETTINGS(WEP_SKIP_CVAR, WEP_SET_PROP); + precache_model(W_Model("g_sniperrifle.md3")); + precache_model(W_Model("v_sniperrifle.md3")); + precache_model(W_Model("h_sniperrifle.iqm")); + precache_sound(W_Sound("campingrifle_fire")); + precache_sound(W_Sound("campingrifle_fire2")); + RIFLE_SETTINGS(WEP_SKIP_CVAR, WEP_SET_PROP) - return TRUE; + return true; } case WR_CHECKAMMO1: { @@@ -235,8 -235,8 +237,8 @@@ } case WR_RELOAD: { - W_Reload(min(WEP_CVAR_PRI(rifle, ammo), WEP_CVAR_SEC(rifle, ammo)), "weapons/reload.wav"); + W_Reload(min(WEP_CVAR_PRI(rifle, ammo), WEP_CVAR_SEC(rifle, ammo)), W_Sound("reload")); - return TRUE; + return true; } case WR_SUICIDEMESSAGE: { diff --cc qcsrc/common/weapons/w_rpc.qc index d624c197f,387dc32d8..9b796a732 --- a/qcsrc/common/weapons/w_rpc.qc +++ b/qcsrc/common/weapons/w_rpc.qc @@@ -44,6 -44,6 +44,8 @@@ RPC_SETTINGS(WEP_ADD_CVAR, WEP_ADD_PROP #endif #else #ifdef SVQC ++#include "../effects.qh" ++ void spawnfunc_weapon_rpc() { weapon_defaultspawnfunc(WEP_RPC); } void W_RocketPropelledChainsaw_Explode() @@@ -107,8 -107,8 +109,8 @@@ void W_RocketPropelledChainsaw_Attack ( entity flash = spawn (); W_DecreaseAmmo(WEP_CVAR(rpc, ammo)); - W_SetupShot_ProjectileSize (self, '-3 -3 -3', '3 3 3', FALSE, 5, W_Sound("rocket_fire"), CH_WEAPON_A, WEP_CVAR(rpc, damage)); - W_SetupShot_ProjectileSize (self, '-3 -3 -3', '3 3 3', false, 5, "weapons/rocket_fire.wav", CH_WEAPON_A, WEP_CVAR(rpc, damage)); - pointparticles(particleeffectnum("rocketlauncher_muzzleflash"), w_shotorg, w_shotdir * 1000, 1); ++ W_SetupShot_ProjectileSize (self, '-3 -3 -3', '3 3 3', false, 5, W_Sound("rocket_fire"), CH_WEAPON_A, WEP_CVAR(rpc, damage)); + Send_Effect(EFFECT_ROCKET_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1); PROJECTILE_MAKETRIGGER(missile); missile.owner = missile.realowner = self; @@@ -182,12 -182,12 +184,12 @@@ float W_RocketPropelledChainsaw(float r case WR_INIT: { precache_model ("models/flash.md3"); - precache_model("models/weapons/h_ok_rl.iqm"); - precache_model("models/weapons/v_ok_rl.md3"); - precache_model("models/weapons/g_ok_rl.md3"); - precache_sound ("weapons/rocket_fire.wav"); - RPC_SETTINGS(WEP_SKIP_CVAR, WEP_SET_PROP); + precache_model(W_Model("g_ok_rl.md3")); + precache_model(W_Model("v_ok_rl.md3")); + precache_model(W_Model("h_ok_rl.iqm")); + precache_sound (W_Sound("rocket_fire")); + RPC_SETTINGS(WEP_SKIP_CVAR, WEP_SET_PROP) - return TRUE; + return true; } case WR_CHECKAMMO1: { @@@ -206,8 -206,8 +208,8 @@@ } case WR_RELOAD: { - W_Reload(WEP_CVAR(rpc, ammo), "weapons/reload.wav"); + W_Reload(WEP_CVAR(rpc, ammo), W_Sound("reload")); - return TRUE; + return true; } case WR_SUICIDEMESSAGE: { diff --cc qcsrc/common/weapons/w_seeker.qc index dac77797b,45a121ab4..3912740b5 --- a/qcsrc/common/weapons/w_seeker.qc +++ b/qcsrc/common/weapons/w_seeker.qc @@@ -83,6 -83,6 +83,8 @@@ SEEKER_SETTINGS(WEP_ADD_CVAR, WEP_ADD_P #endif #else #ifdef SVQC ++#include "../effects.qh" ++ void spawnfunc_weapon_seeker(void) { weapon_defaultspawnfunc(WEP_SEEKER); } // ============================ @@@ -249,11 -249,11 +251,11 @@@ void W_Seeker_Fire_Missile(vector f_dif W_DecreaseAmmo(WEP_CVAR(seeker, missile_ammo)); makevectors(self.v_angle); - W_SetupShot_ProjectileSize(self, '-2 -2 -2', '2 2 2', FALSE, 2, W_Sound("seeker_fire.wav"), CH_WEAPON_A, 0); - W_SetupShot_ProjectileSize(self, '-2 -2 -2', '2 2 2', false, 2, "weapons/seeker_fire.wav", CH_WEAPON_A, 0); ++ W_SetupShot_ProjectileSize(self, '-2 -2 -2', '2 2 2', false, 2, W_Sound("seeker_fire.wav"), CH_WEAPON_A, 0); w_shotorg += f_diff; - pointparticles(particleeffectnum("seeker_muzzleflash"), w_shotorg, w_shotdir * 1000, 1); + Send_Effect(EFFECT_SEEKER_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1); - //self.detornator = FALSE; + //self.detornator = false; missile = spawn(); missile.owner = missile.realowner = self; @@@ -340,10 -340,10 +342,10 @@@ void W_Seeker_Fire_Flac(void f_diff = '+1.25 +3.75 0'; break; } - W_SetupShot_ProjectileSize(self, '-2 -2 -2', '2 2 2', FALSE, 2, W_Sound("flac_fire"), CH_WEAPON_A, WEP_CVAR(seeker, flac_damage)); - W_SetupShot_ProjectileSize(self, '-2 -2 -2', '2 2 2', false, 2, "weapons/flac_fire.wav", CH_WEAPON_A, WEP_CVAR(seeker, flac_damage)); ++ W_SetupShot_ProjectileSize(self, '-2 -2 -2', '2 2 2', false, 2, W_Sound("flac_fire"), CH_WEAPON_A, WEP_CVAR(seeker, flac_damage)); w_shotorg += f_diff; - pointparticles(particleeffectnum("hagar_muzzleflash"), w_shotorg, w_shotdir * 1000, 1); + Send_Effect(EFFECT_HAGAR_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1); missile = spawn(); missile.owner = missile.realowner = self; @@@ -559,7 -559,7 +561,7 @@@ void W_Seeker_Fire_Tag(void entity missile; W_DecreaseAmmo(WEP_CVAR(seeker, tag_ammo)); - W_SetupShot_ProjectileSize(self, '-2 -2 -2', '2 2 2', FALSE, 2, W_Sound("tag_fire"), CH_WEAPON_A, WEP_CVAR(seeker, missile_damage) * WEP_CVAR(seeker, missile_count)); - W_SetupShot_ProjectileSize(self, '-2 -2 -2', '2 2 2', false, 2, "weapons/tag_fire.wav", CH_WEAPON_A, WEP_CVAR(seeker, missile_damage) * WEP_CVAR(seeker, missile_count)); ++ W_SetupShot_ProjectileSize(self, '-2 -2 -2', '2 2 2', false, 2, W_Sound("tag_fire"), CH_WEAPON_A, WEP_CVAR(seeker, missile_damage) * WEP_CVAR(seeker, missile_count)); missile = spawn(); missile.owner = missile.realowner = self; @@@ -662,14 -662,14 +664,14 @@@ float W_Seeker(float req } case WR_INIT: { - precache_model("models/weapons/g_seeker.md3"); - precache_model("models/weapons/v_seeker.md3"); - precache_model("models/weapons/h_seeker.iqm"); - precache_sound("weapons/tag_fire.wav"); - precache_sound("weapons/flac_fire.wav"); - precache_sound("weapons/seeker_fire.wav"); - SEEKER_SETTINGS(WEP_SKIP_CVAR, WEP_SET_PROP); + precache_model(W_Model("g_seeker.md3")); + precache_model(W_Model("v_seeker.md3")); + precache_model(W_Model("h_seeker.iqm")); + precache_sound(W_Sound("tag_fire")); + precache_sound(W_Sound("flac_fire")); + precache_sound(W_Sound("seeker_fire")); + SEEKER_SETTINGS(WEP_SKIP_CVAR, WEP_SET_PROP) - return TRUE; + return true; } case WR_CHECKAMMO1: { @@@ -706,8 -706,8 +708,8 @@@ } case WR_RELOAD: { - W_Reload(min(WEP_CVAR(seeker, missile_ammo), WEP_CVAR(seeker, tag_ammo)), "weapons/reload.wav"); + W_Reload(min(WEP_CVAR(seeker, missile_ammo), WEP_CVAR(seeker, tag_ammo)), W_Sound("reload")); - return TRUE; + return true; } case WR_SUICIDEMESSAGE: { diff --cc qcsrc/common/weapons/w_shockwave.qc index abf7afa85,24b8634b9..c69e2f976 --- a/qcsrc/common/weapons/w_shockwave.qc +++ b/qcsrc/common/weapons/w_shockwave.qc @@@ -81,6 -81,6 +81,8 @@@ void Net_ReadShockwaveParticle(void) #endif #else #ifdef SVQC ++#include "../effects.qh" ++ void spawnfunc_weapon_shockwave(void) { //if(autocvar_sv_q3acompat_machineshockwaveswap) // WEAPONTODO @@@ -146,17 -147,17 +148,17 @@@ void W_Shockwave_Melee_Think(void self.realowner, (self.realowner.origin + self.realowner.view_ofs), targpos, - FALSE, + false, - self.realowner, + world, //self.realowner, ANTILAG_LATENCY(self.realowner) ); - + // draw lightning beams for debugging #ifdef DEBUG_SHOCKWAVE - te_lightning2(world, targpos, self.realowner.origin + self.realowner.view_ofs + v_forward * 5 - v_up * 5); + te_lightning2(world, targpos, self.realowner.origin + self.realowner.view_ofs + v_forward * 5 - v_up * 5); te_customflash(targpos, 40, 2, '1 1 1'); #endif - + is_player = (IS_PLAYER(trace_ent) || trace_ent.classname == "body" || (trace_ent.flags & FL_MONSTER)); if((trace_fraction < 1) // if trace is good, apply the damage and remove self if necessary @@@ -356,9 -357,9 +358,9 @@@ void W_Shockwave_Attack(void entity head; float i, queue = 0; - + // set up the shot direction - W_SetupShot(self, FALSE, 3, W_Sound("lasergun_fire"), CH_WEAPON_B, WEP_CVAR(shockwave, blast_damage)); - W_SetupShot(self, false, 3, "weapons/lasergun_fire.wav", CH_WEAPON_B, WEP_CVAR(shockwave, blast_damage)); ++ W_SetupShot(self, false, 3, W_Sound("lasergun_fire"), CH_WEAPON_B, WEP_CVAR(shockwave, blast_damage)); vector attack_endpos = (w_shotorg + (w_shotdir * WEP_CVAR(shockwave, blast_distance))); WarpZone_TraceLine(w_shotorg, attack_endpos, MOVE_NOMONSTERS, self); vector attack_hitpos = trace_endpos; @@@ -709,14 -710,14 +711,14 @@@ float W_Shockwave(float req case WR_INIT: { precache_model("models/uziflash.md3"); - precache_model("models/weapons/g_shotgun.md3"); - precache_model("models/weapons/v_shotgun.md3"); - precache_model("models/weapons/h_shotgun.iqm"); + precache_model(W_Model("g_shotgun.md3")); + precache_model(W_Model("v_shotgun.md3")); + precache_model(W_Model("h_shotgun.iqm")); precache_sound("misc/itempickup.wav"); - precache_sound("weapons/lasergun_fire.wav"); - precache_sound("weapons/shotgun_melee.wav"); - SHOCKWAVE_SETTINGS(WEP_SKIP_CVAR, WEP_SET_PROP); + precache_sound(W_Sound("lasergun_fire")); + precache_sound(W_Sound("shotgun_melee")); + SHOCKWAVE_SETTINGS(WEP_SKIP_CVAR, WEP_SET_PROP) - return TRUE; + return true; } case WR_CHECKAMMO1: case WR_CHECKAMMO2: diff --cc qcsrc/common/weapons/w_shotgun.qc index d6cd41434,5415b4a6f..4fc96eb2c --- a/qcsrc/common/weapons/w_shotgun.qc +++ b/qcsrc/common/weapons/w_shotgun.qc @@@ -51,6 -51,6 +51,8 @@@ SHOTGUN_SETTINGS(WEP_ADD_CVAR, WEP_ADD_ #endif #else #ifdef SVQC ++#include "../effects.qh" ++ void spawnfunc_weapon_shotgun(void) { weapon_defaultspawnfunc(WEP_SHOTGUN); } void W_Shotgun_Attack(float isprimary) @@@ -60,7 -60,7 +62,7 @@@ W_DecreaseAmmo(WEP_CVAR_PRI(shotgun, ammo)); - W_SetupShot(self, TRUE, 5, W_Sound("shotgun_fire"), ((isprimary) ? CH_WEAPON_A : CH_WEAPON_SINGLE), WEP_CVAR_PRI(shotgun, damage) * WEP_CVAR_PRI(shotgun, bullets)); - W_SetupShot(self, true, 5, "weapons/shotgun_fire.wav", ((isprimary) ? CH_WEAPON_A : CH_WEAPON_SINGLE), WEP_CVAR_PRI(shotgun, damage) * WEP_CVAR_PRI(shotgun, bullets)); ++ W_SetupShot(self, true, 5, W_Sound("shotgun_fire"), ((isprimary) ? CH_WEAPON_A : CH_WEAPON_SINGLE), WEP_CVAR_PRI(shotgun, damage) * WEP_CVAR_PRI(shotgun, bullets)); for(sc = 0;sc < WEP_CVAR_PRI(shotgun, bullets);sc = sc + 1) fireBullet(w_shotorg, w_shotdir, WEP_CVAR_PRI(shotgun, spread), WEP_CVAR_PRI(shotgun, solidpenetration), WEP_CVAR_PRI(shotgun, damage), WEP_CVAR_PRI(shotgun, force), WEP_SHOTGUN, 0); @@@ -118,7 -119,7 +120,7 @@@ void W_Shotgun_Melee_Think(void + (v_up * swing_factor * WEP_CVAR_SEC(shotgun, melee_swing_up)) + (v_right * swing_factor * WEP_CVAR_SEC(shotgun, melee_swing_side))); - WarpZone_traceline_antilag(self, self.realowner.origin + self.realowner.view_ofs, targpos, FALSE, self.realowner, ANTILAG_LATENCY(self.realowner)); - WarpZone_traceline_antilag(self, self.realowner.origin + self.realowner.view_ofs, targpos, false, self, ANTILAG_LATENCY(self.realowner)); ++ WarpZone_traceline_antilag(self, self.realowner.origin + self.realowner.view_ofs, targpos, false, self.realowner, ANTILAG_LATENCY(self.realowner)); // draw lightning beams for debugging //te_lightning2(world, targpos, self.realowner.origin + self.realowner.view_ofs + v_forward * 5 - v_up * 5); @@@ -285,14 -286,14 +287,14 @@@ float W_Shotgun(float req case WR_INIT: { precache_model("models/uziflash.md3"); - precache_model("models/weapons/g_shotgun.md3"); - precache_model("models/weapons/v_shotgun.md3"); - precache_model("models/weapons/h_shotgun.iqm"); + precache_model(W_Model("g_shotgun.md3")); + precache_model(W_Model("v_shotgun.md3")); + precache_model(W_Model("h_shotgun.iqm")); precache_sound("misc/itempickup.wav"); - precache_sound("weapons/shotgun_fire.wav"); - precache_sound("weapons/shotgun_melee.wav"); - SHOTGUN_SETTINGS(WEP_SKIP_CVAR, WEP_SET_PROP); + precache_sound(W_Sound("shotgun_fire")); + precache_sound(W_Sound("shotgun_melee")); + SHOTGUN_SETTINGS(WEP_SKIP_CVAR, WEP_SET_PROP) - return TRUE; + return true; } case WR_SETUP: { @@@ -329,8 -330,8 +331,8 @@@ } case WR_RELOAD: { - W_Reload(WEP_CVAR_PRI(shotgun, ammo), "weapons/reload.wav"); // WEAPONTODO + W_Reload(WEP_CVAR_PRI(shotgun, ammo), W_Sound("reload")); // WEAPONTODO - return TRUE; + return true; } case WR_SUICIDEMESSAGE: { diff --cc qcsrc/common/weapons/w_tuba.qc index 9a327a679,0b70b8572..641222828 --- a/qcsrc/common/weapons/w_tuba.qc +++ b/qcsrc/common/weapons/w_tuba.qc @@@ -53,6 -53,6 +53,8 @@@ float W_Tuba_MarkClientOnlyFieldsAsUsed #endif #else #ifdef SVQC ++#include "../effects.qh" ++ void spawnfunc_weapon_tuba(void) { weapon_defaultspawnfunc(WEP_TUBA); } float W_Tuba_HasPlayed(entity pl, string melody, float instrument, float ignorepitch, float mintempo, float maxtempo) @@@ -419,15 -416,15 +418,15 @@@ float W_Tuba(float req } case WR_INIT: { - precache_model("models/weapons/g_tuba.md3"); - precache_model("models/weapons/v_tuba.md3"); - precache_model("models/weapons/h_tuba.iqm"); - precache_model("models/weapons/v_akordeon.md3"); - precache_model("models/weapons/h_akordeon.iqm"); - precache_model("models/weapons/v_kleinbottle.md3"); - precache_model("models/weapons/h_kleinbottle.iqm"); - TUBA_SETTINGS(WEP_SKIP_CVAR, WEP_SET_PROP); + precache_model(W_Model("g_tuba.md3")); + precache_model(W_Model("v_tuba.md3")); + precache_model(W_Model("h_tuba.iqm")); + precache_model(W_Model("v_akordeon.md3")); + precache_model(W_Model("h_akordeon.iqm")); + precache_model(W_Model("v_kleinbottle.md3")); + precache_model(W_Model("h_kleinbottle.iqm")); + TUBA_SETTINGS(WEP_SKIP_CVAR, WEP_SET_PROP) - return TRUE; + return true; } case WR_SETUP: { @@@ -455,8 -452,8 +454,8 @@@ self.weaponname = "tuba"; break; } - W_SetupShot(self, FALSE, 0, "", 0, 0); + W_SetupShot(self, false, 0, "", 0, 0); - pointparticles(particleeffectnum("teleport"), w_shotorg, '0 0 0', 1); + Send_Effect(EFFECT_TELEPORT, w_shotorg, '0 0 0', 1); self.weaponentity.state = WS_INUSE; weapon_thinkf(WFRAME_RELOAD, 0.5, w_ready); } diff --cc qcsrc/common/weapons/w_vaporizer.qc index 37aae84a3,14377edb8..e0b43ac64 --- a/qcsrc/common/weapons/w_vaporizer.qc +++ b/qcsrc/common/weapons/w_vaporizer.qc @@@ -88,179 -48,18 +88,181 @@@ void Net_ReadSuperBlastParticle() #endif #else #ifdef SVQC ++#include "../effects.qh" ++ void spawnfunc_weapon_vaporizer(void) { weapon_defaultspawnfunc(WEP_VAPORIZER); } void spawnfunc_weapon_minstanex(void) { spawnfunc_weapon_vaporizer(); } +void SendCSQCSuperBlastParticle(vector endpos) +{ + //endpos = WarpZone_UnTransformOrigin(transform, endpos); + + WriteByte(MSG_BROADCAST, SVC_TEMPENTITY); + WriteByte(MSG_BROADCAST, TE_CSQC_SUPERBLASTPARTICLE); + WriteCoord(MSG_BROADCAST, w_shotorg_x); + WriteCoord(MSG_BROADCAST, w_shotorg_y); + WriteCoord(MSG_BROADCAST, w_shotorg_z); + WriteCoord(MSG_BROADCAST, endpos_x); + WriteCoord(MSG_BROADCAST, endpos_y); + WriteCoord(MSG_BROADCAST, endpos_z); + WriteByte(MSG_BROADCAST, bound(0, WEP_CVAR_PRI(vaporizer, charge_spread_max), 255)); + WriteByte(MSG_BROADCAST, bound(0, WEP_CVAR_PRI(vaporizer, charge_spread_min), 255)); + WriteByte(MSG_BROADCAST, num_for_edict(self)); +} + +float W_Vaporizer_SuperBlast_CheckSpread(vector targetorg, vector nearest_on_line, vector sw_shotorg, vector attack_endpos) +{ + float spreadlimit; + float distance_of_attack = vlen(sw_shotorg - attack_endpos); + float distance_from_line = vlen(targetorg - nearest_on_line); + + spreadlimit = (distance_of_attack ? min(1, (vlen(sw_shotorg - nearest_on_line) / distance_of_attack)) : 1); + spreadlimit = (WEP_CVAR_PRI(vaporizer, charge_spread_min) * (1 - spreadlimit) + WEP_CVAR_PRI(vaporizer, charge_spread_max) * spreadlimit); + + if(spreadlimit && (distance_from_line <= spreadlimit) && ((vlen(normalize(targetorg - sw_shotorg) - normalize(attack_endpos - sw_shotorg)) * RAD2DEG) <= 90)) + return bound(0, (distance_from_line / spreadlimit), 1); + else - return FALSE; ++ return false; +} + +float W_Vaporizer_SuperBlast_IsVisible(entity head, vector nearest_on_line, vector sw_shotorg, vector attack_endpos) +{ + vector nearest_to_attacker = head.WarpZone_findradius_nearest; + vector center = (head.origin + (head.mins + head.maxs) * 0.5); + vector corner; + float i; + + // STEP ONE: Check if the nearest point is clear + if(W_Vaporizer_SuperBlast_CheckSpread(nearest_to_attacker, nearest_on_line, sw_shotorg, attack_endpos)) + { + WarpZone_TraceLine(sw_shotorg, nearest_to_attacker, MOVE_NOMONSTERS, self); - if(trace_fraction == 1) { return TRUE; } // yes, the nearest point is clear and we can allow the damage ++ if(trace_fraction == 1) { return true; } // yes, the nearest point is clear and we can allow the damage + } + + // STEP TWO: Check if shotorg to center point is clear + if(W_Vaporizer_SuperBlast_CheckSpread(center, nearest_on_line, sw_shotorg, attack_endpos)) + { + WarpZone_TraceLine(sw_shotorg, center, MOVE_NOMONSTERS, self); - if(trace_fraction == 1) { return TRUE; } // yes, the center point is clear and we can allow the damage ++ if(trace_fraction == 1) { return true; } // yes, the center point is clear and we can allow the damage + } + + // STEP THREE: Check each corner to see if they are clear + for(i=1; i<=8; ++i) + { + corner = get_corner_position(head, i); + if(W_Vaporizer_SuperBlast_CheckSpread(corner, nearest_on_line, sw_shotorg, attack_endpos)) + { + WarpZone_TraceLine(sw_shotorg, corner, MOVE_NOMONSTERS, self); - if(trace_fraction == 1) { return TRUE; } // yes, this corner is clear and we can allow the damage ++ if(trace_fraction == 1) { return true; } // yes, this corner is clear and we can allow the damage + } + } + - return FALSE; ++ return false; +} + - entity shockwave_hit[32]; - float shockwave_hit_damage[32]; - vector shockwave_hit_force[32]; ++entity superblast_hit[32]; ++float superblast_hit_damage[32]; ++vector superblast_hit_force[32]; + +float W_Vaporizer_SuperBlast_CheckHit(float queue, entity head, vector final_force, float final_damage) +{ - if(!head) { return FALSE; } ++ if(!head) { return false; } + float i; + + ++queue; + + for(i = 1; i <= queue; ++i) + { - if(shockwave_hit[i] == head) ++ if(superblast_hit[i] == head) + { - if(vlen(final_force) > vlen(shockwave_hit_force[i])) { shockwave_hit_force[i] = final_force; } - if(final_damage > shockwave_hit_damage[i]) { shockwave_hit_damage[i] = final_damage; } - return FALSE; ++ if(vlen(final_force) > vlen(superblast_hit_force[i])) { superblast_hit_force[i] = final_force; } ++ if(final_damage > superblast_hit_damage[i]) { superblast_hit_damage[i] = final_damage; } ++ return false; + } + } + - shockwave_hit[queue] = head; - shockwave_hit_force[queue] = final_force; - shockwave_hit_damage[queue] = final_damage; - return TRUE; ++ superblast_hit[queue] = head; ++ superblast_hit_force[queue] = final_force; ++ superblast_hit_damage[queue] = final_damage; ++ return true; +} + +void W_RocketMinsta_Explosion(vector loc) +{ + Send_Effect(EFFECT_ROCKET_EXPLODE, loc, '0 0 0', 1); + + if(accuracy_canbegooddamage(self)) + accuracy_add(self, WEP_DEVASTATOR, autocvar_g_rm_damage, 0); + + // declarations + float multiplier, multiplier_from_accuracy; + float final_damage = 0; //, final_spread; + vector final_force, center; + entity head, next; + + float i, queue = 0; + + // splash damage/jumping trace - head = WarpZone_FindRadius(loc, autocvar_g_rm_radius, FALSE); ++ head = WarpZone_FindRadius(loc, autocvar_g_rm_radius, false); + while(head) + { + next = head.chain; + + if(head.takedamage) + { + // if it's a player, use the view origin as reference (stolen from RadiusDamage functions in g_damage.qc) + //center = CENTER_OR_VIEWOFS(head); + center = WarpZone_UnTransformOrigin(head, CENTER_OR_VIEWOFS(head)); + + float distance_to_head = vlen(loc - head.WarpZone_findradius_nearest); + + if (distance_to_head <= autocvar_g_rm_radius || head == self) + { + multiplier_from_accuracy = (1 - (distance_to_head ? min(1, (distance_to_head / autocvar_g_rm_radius)) : 0)); + multiplier = max(autocvar_g_rm_damage_multiplier_min, multiplier_from_accuracy * autocvar_g_rm_damage_multiplier_accuracy); + + final_force = normalize(center - loc); + if(head == self) + final_force = (final_force * autocvar_g_rm_force); + else + final_force = ((final_force * autocvar_g_rm_force) * multiplier); + + final_damage = (autocvar_g_rm_damage * multiplier + autocvar_g_rm_edgedamage * (1 - multiplier)); + + if(W_Vaporizer_SuperBlast_CheckHit(queue, head, final_force, final_damage)) { ++queue; } + } + } + head = next; + } + + for(i = 1; i <= queue; ++i) + { - head = shockwave_hit[i]; - final_force = shockwave_hit_force[i]; - final_damage = shockwave_hit_damage[i]; ++ head = superblast_hit[i]; ++ final_force = superblast_hit_force[i]; ++ final_damage = superblast_hit_damage[i]; + + if(accuracy_isgooddamage(self, head)) + accuracy_add(self, WEP_DEVASTATOR, 0, final_damage); + + Damage(head, self, self, final_damage, (WEP_DEVASTATOR | HITTYPE_SPLASH), head.origin, final_force); + //print("SHOCKWAVE by ", self.netname, ": damage = ", ftos(final_damage), ", force = ", ftos(vlen(final_force)), ".\n"); + - shockwave_hit[i] = world; - shockwave_hit_force[i] = '0 0 0'; - shockwave_hit_damage[i] = 0; ++ superblast_hit[i] = world; ++ superblast_hit_force[i] = '0 0 0'; ++ superblast_hit_damage[i] = 0; + } +} + void W_Vaporizer_Attack(void) { - float flying; + float flying, vaporizer_damage; flying = IsFlying(self); // do this BEFORE to make the trace values from FireRailgunBullet last + vaporizer_damage = ((WEP_CVAR_PRI(vaporizer, damage) > 0) ? WEP_CVAR_PRI(vaporizer, damage) : 10000); - W_SetupShot(self, TRUE, 0, "", CH_WEAPON_A, vaporizer_damage); - W_SetupShot(self, true, 0, "", CH_WEAPON_A, 10000); ++ W_SetupShot(self, true, 0, "", CH_WEAPON_A, vaporizer_damage); // handle sound separately so we can change the volume // added bonus: no longer plays the strength sound (strength gives no bonus to instakill anyway) - sound (self, CH_WEAPON_A, "weapons/minstanexfire.wav", VOL_BASE * 0.8, ATTEN_NORM); + sound (self, CH_WEAPON_A, W_Sound("minstanexfire"), VOL_BASE * 0.8, ATTEN_NORM); yoda = 0; damage_goodhits = 0; @@@ -300,375 -117,6 +302,375 @@@ W_DecreaseAmmo(((g_instagib) ? 1 : WEP_CVAR_PRI(vaporizer, ammo))); } +void W_Vaporizer_SuperBlast() +{ + // declarations + float multiplier, multiplier_from_accuracy, multiplier_from_distance; + float final_damage; //, final_spread; + vector final_force, center, vel; + entity head, next; + + float i, queue = 0; + + // set up the shot direction - W_SetupShot(self, FALSE, 3, W_Sound("minstanex_charge2"), CH_WEAPON_B, WEP_CVAR_PRI(vaporizer, damage)); ++ W_SetupShot(self, false, 3, W_Sound("minstanex_charge2"), CH_WEAPON_B, WEP_CVAR_PRI(vaporizer, damage)); + vector attack_endpos = (w_shotorg + (w_shotdir * WEP_CVAR_PRI(vaporizer, charge_distance))); + WarpZone_TraceLine(w_shotorg, attack_endpos, MOVE_NOMONSTERS, self); + vector attack_hitpos = trace_endpos; + float distance_to_end = vlen(w_shotorg - attack_endpos); + float distance_to_hit = vlen(w_shotorg - attack_hitpos); + //entity transform = WarpZone_trace_transform; + + // do the firing effect now + SendCSQCSuperBlastParticle(attack_endpos); + Damage_DamageInfo(attack_hitpos, WEP_CVAR_PRI(vaporizer, charge_splash_damage), WEP_CVAR_PRI(vaporizer, charge_splash_edgedamage), WEP_CVAR_PRI(vaporizer, charge_splash_radius), w_shotdir * WEP_CVAR_PRI(vaporizer, charge_splash_force), (WEP_VAPORIZER | HITTYPE_SPLASH), 0, self); + + // splash damage/jumping trace - head = WarpZone_FindRadius(attack_hitpos, max(WEP_CVAR_PRI(vaporizer, charge_splash_radius), WEP_CVAR_PRI(vaporizer, charge_jump_radius)), FALSE); ++ head = WarpZone_FindRadius(attack_hitpos, max(WEP_CVAR_PRI(vaporizer, charge_splash_radius), WEP_CVAR_PRI(vaporizer, charge_jump_radius)), false); + while(head) + { + next = head.chain; + + if(head.takedamage) + { + // if it's a player, use the view origin as reference (stolen from RadiusDamage functions in g_damage.qc) + center = CENTER_OR_VIEWOFS(head); + + float distance_to_head = vlen(attack_hitpos - head.WarpZone_findradius_nearest); + + if((head == self) && (distance_to_head <= WEP_CVAR_PRI(vaporizer, charge_jump_radius))) + { + multiplier_from_accuracy = (1 - (distance_to_head ? min(1, (distance_to_head / WEP_CVAR_PRI(vaporizer, charge_jump_radius))) : 0)); + multiplier_from_distance = (1 - (distance_to_hit ? min(1, (distance_to_hit / distance_to_end)) : 0)); + multiplier = max(WEP_CVAR_PRI(vaporizer, charge_jump_multiplier_min), ((multiplier_from_accuracy * WEP_CVAR_PRI(vaporizer, charge_jump_multiplier_accuracy)) + (multiplier_from_distance * WEP_CVAR_PRI(vaporizer, charge_jump_multiplier_distance)))); + + final_force = ((normalize(center - attack_hitpos) * WEP_CVAR_PRI(vaporizer, charge_jump_force)) * multiplier); + vel = head.velocity; vel_z = 0; + vel = normalize(vel) * bound(0, vlen(vel) / autocvar_sv_maxspeed, 1) * WEP_CVAR_PRI(vaporizer, charge_jump_force_velocitybias); + final_force = (vlen(final_force) * normalize(normalize(final_force) + vel)); + final_force_z *= WEP_CVAR_PRI(vaporizer, charge_jump_force_zscale); + final_damage = (WEP_CVAR_PRI(vaporizer, charge_jump_damage) * multiplier + WEP_CVAR_PRI(vaporizer, charge_jump_edgedamage) * (1 - multiplier)); + + Damage(head, self, self, final_damage, (WEP_VAPORIZER | HITTYPE_SPLASH), head.origin, final_force); + //print("SELF HIT: multiplier = ", ftos(multiplier), strcat(", damage = ", ftos(final_damage), ", force = ", ftos(vlen(final_force))),"... multiplier_from_accuracy = ", ftos(multiplier_from_accuracy), ", multiplier_from_distance = ", ftos(multiplier_from_distance), ".\n"); + } + else if (distance_to_head <= WEP_CVAR_PRI(vaporizer, charge_splash_radius)) + { + multiplier_from_accuracy = (1 - (distance_to_head ? min(1, (distance_to_head / WEP_CVAR_PRI(vaporizer, charge_splash_radius))) : 0)); + multiplier_from_distance = (1 - (distance_to_hit ? min(1, (distance_to_hit / distance_to_end)) : 0)); + multiplier = max(WEP_CVAR_PRI(vaporizer, charge_splash_multiplier_min), ((multiplier_from_accuracy * WEP_CVAR_PRI(vaporizer, charge_splash_multiplier_accuracy)) + (multiplier_from_distance * WEP_CVAR_PRI(vaporizer, charge_splash_multiplier_distance)))); + + final_force = normalize(center - (attack_hitpos - (w_shotdir * WEP_CVAR_PRI(vaporizer, charge_splash_force_forwardbias)))); + //te_lightning2(world, attack_hitpos, (attack_hitpos + (final_force * 200))); + final_force = ((final_force * WEP_CVAR_PRI(vaporizer, charge_splash_force)) * multiplier); + final_force_z *= WEP_CVAR_PRI(vaporizer, charge_splash_force_zscale); + final_damage = (WEP_CVAR_PRI(vaporizer, charge_splash_damage) * multiplier + WEP_CVAR_PRI(vaporizer, charge_splash_edgedamage) * (1 - multiplier)); + + if(W_Vaporizer_SuperBlast_CheckHit(queue, head, final_force, final_damage)) { ++queue; } + //print("SPLASH HIT: multiplier = ", ftos(multiplier), strcat(", damage = ", ftos(final_damage), ", force = ", ftos(vlen(final_force))),"... multiplier_from_accuracy = ", ftos(multiplier_from_accuracy), ", multiplier_from_distance = ", ftos(multiplier_from_distance), ".\n"); + } + } + head = next; + } + + // cone damage trace - head = WarpZone_FindRadius(w_shotorg, WEP_CVAR_PRI(vaporizer, charge_distance), FALSE); ++ head = WarpZone_FindRadius(w_shotorg, WEP_CVAR_PRI(vaporizer, charge_distance), false); + while(head) + { + next = head.chain; + + if((head != self) && head.takedamage) + { + // if it's a player, use the view origin as reference (stolen from RadiusDamage functions in g_damage.qc) + center = CENTER_OR_VIEWOFS(head); + + // find the closest point on the enemy to the center of the attack + float ang; // angle between shotdir and h + float h; // hypotenuse, which is the distance between attacker to head + float a; // adjacent side, which is the distance between attacker and the point on w_shotdir that is closest to head.origin + + h = vlen(center - self.origin); + ang = acos(dotproduct(normalize(center - self.origin), w_shotdir)); + a = h * cos(ang); + + vector nearest_on_line = (w_shotorg + a * w_shotdir); + vector nearest_to_attacker = WarpZoneLib_NearestPointOnBox(center + head.mins, center + head.maxs, nearest_on_line); + float distance_to_target = vlen(w_shotorg - nearest_to_attacker); // todo: use the findradius function for this + + if((distance_to_target <= WEP_CVAR_PRI(vaporizer, charge_distance)) + && (W_Vaporizer_SuperBlast_IsVisible(head, nearest_on_line, w_shotorg, attack_endpos))) + { + multiplier_from_accuracy = (1 - W_Vaporizer_SuperBlast_CheckSpread(nearest_to_attacker, nearest_on_line, w_shotorg, attack_endpos)); + multiplier_from_distance = (1 - (distance_to_hit ? min(1, (distance_to_target / distance_to_end)) : 0)); + multiplier = max(WEP_CVAR_PRI(vaporizer, charge_multiplier_min), ((multiplier_from_accuracy * WEP_CVAR_PRI(vaporizer, charge_multiplier_accuracy)) + (multiplier_from_distance * WEP_CVAR_PRI(vaporizer, charge_multiplier_distance)))); + + final_force = normalize(center - (nearest_on_line - (w_shotdir * WEP_CVAR_PRI(vaporizer, charge_force_forwardbias)))); + //te_lightning2(world, nearest_on_line, (attack_hitpos + (final_force * 200))); + final_force = ((final_force * WEP_CVAR_PRI(vaporizer, charge_force)) * multiplier); + final_force_z *= WEP_CVAR_PRI(vaporizer, charge_force_zscale); + final_damage = (WEP_CVAR_PRI(vaporizer, damage) * multiplier + WEP_CVAR_PRI(vaporizer, damage) * (1 - multiplier)); + + if(W_Vaporizer_SuperBlast_CheckHit(queue, head, final_force, final_damage)) { ++queue; } + //print("CONE HIT: multiplier = ", ftos(multiplier), strcat(", damage = ", ftos(final_damage), ", force = ", ftos(vlen(final_force))),"... multiplier_from_accuracy = ", ftos(multiplier_from_accuracy), ", multiplier_from_distance = ", ftos(multiplier_from_distance), ".\n"); + } + } + head = next; + } + + for(i = 1; i <= queue; ++i) + { - head = shockwave_hit[i]; - final_force = shockwave_hit_force[i]; - final_damage = shockwave_hit_damage[i]; ++ head = superblast_hit[i]; ++ final_force = superblast_hit_force[i]; ++ final_damage = superblast_hit_damage[i]; + + Damage(head, self, self, final_damage, (WEP_VAPORIZER | HITTYPE_SPLASH), head.origin, final_force); + + if(accuracy_isgooddamage(self, head)) + accuracy_add(self, WEP_VAPORIZER, 1, final_damage); + //print("SHOCKWAVE by ", self.netname, ": damage = ", ftos(final_damage), ", force = ", ftos(vlen(final_force)), ".\n"); + - shockwave_hit[i] = world; - shockwave_hit_force[i] = '0 0 0'; - shockwave_hit_damage[i] = 0; ++ superblast_hit[i] = world; ++ superblast_hit_force[i] = '0 0 0'; ++ superblast_hit_damage[i] = 0; + } + //print("queue was ", ftos(queue), ".\n\n"); + + self.ammo_supercells -= 1; +} + +float W_Vaporizer_SuperBlast_CheckAmmo() +{ + if(WEP_CVAR_PRI(vaporizer, charge) == 3) - return TRUE; // forced ++ return true; // forced + float ammo_amount; + ammo_amount = self.ammo_supercells >= 1; + ammo_amount += self.(weapon_load[WEP_VAPORIZER]) >= 1; + return ammo_amount; +} + +void W_RocketMinsta_SuperRocket_Explode() +{ + self.event_damage = func_null; + self.takedamage = DAMAGE_NO; + + RadiusDamage (self, self.realowner, 250, 250, 250, world, world, 250, self.projectiledeathtype, other); + + remove (self); +} + +void W_RocketMinsta_SuperRocket_Think (void) +{ + self.nextthink = time; + makevectors(self.angles); + self.velocity = v_forward * autocvar_g_rm_superrocket_speed; + if (time > self.cnt) + { + other = world; + self.projectiledeathtype |= HITTYPE_BOUNCE; + W_RocketMinsta_SuperRocket_Explode (); + return; + } +} + +void W_RocketMinsta_SuperRocket_Touch (void) +{ + if(other.takedamage == DAMAGE_AIM && other.classname != "object" && time > other.superrocket_lasthit) + { + float vaporizer_damage = ((WEP_CVAR_PRI(vaporizer, damage)) ? WEP_CVAR_PRI(vaporizer, damage) : 10000); + Damage(other, self, self.realowner, vaporizer_damage, WEP_DEVASTATOR, other.origin, '0 0 0'); + other.superrocket_lasthit = time + 1; + return; + } + W_RocketMinsta_SuperRocket_Explode(); +} + +void W_RocketMinsta_SuperRocket_Damage (entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force) +{ + if (self.health <= 0) + return; + + if (!W_CheckProjectileDamage(inflictor.realowner, self.realowner, deathtype, -1)) // no exceptions + return; // g_projectiles_damage says to halt + + self.health = self.health - damage; + self.angles = vectoangles(self.velocity); + + if (self.health <= 0) + W_PrepareExplosionByDamage(attacker, W_RocketMinsta_SuperRocket_Explode); +} + +void W_RocketMinsta_SuperRocket() +{ + entity missile; + float vaporizer_damage = ((WEP_CVAR_PRI(vaporizer, damage)) ? WEP_CVAR_PRI(vaporizer, damage) : 10000); + + makevectors(self.angles); + - W_SetupShot_ProjectileSize (self, '-32 -32 -32', '32 32 32', FALSE, 5, W_Sound("minstanex_charge2"), CH_WEAPON_A, vaporizer_damage); ++ W_SetupShot_ProjectileSize (self, '-32 -32 -32', '32 32 32', false, 5, W_Sound("minstanex_charge2"), CH_WEAPON_A, vaporizer_damage); + Send_Effect(EFFECT_ROCKET_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1); + + missile = WarpZone_RefSys_SpawnSameRefSys(self); + missile.owner = missile.realowner = self; + missile.classname = "rocket"; - missile.bot_dodge = TRUE; ++ missile.bot_dodge = true; + missile.bot_dodgerating = vaporizer_damage; + + missile.takedamage = DAMAGE_YES; + missile.damageforcescale = 0; + missile.health = 350; + missile.event_damage = W_RocketMinsta_SuperRocket_Damage; - missile.damagedbycontents = TRUE; ++ missile.damagedbycontents = true; + + missile.movetype = MOVETYPE_FLYMISSILE; + PROJECTILE_MAKETRIGGER(missile); + missile.projectiledeathtype = WEP_DEVASTATOR; + setsize (missile, '-32 -32 -32', '32 32 32'); // give it some size so it can be shot + + setorigin (missile, w_shotorg - v_forward * 3); // move it back so it hits the wall at the right point + W_SetupProjVelocity_Basic(missile, autocvar_g_rm_superrocket_speed, 0); + missile.angles = self.v_angle; + + missile.touch = W_RocketMinsta_SuperRocket_Touch; + missile.think = W_RocketMinsta_SuperRocket_Think; + missile.nextthink = time; + missile.cnt = time + 10; + missile.flags = FL_PROJECTILE; + //missile.missile_flags = MIF_SPLASH; + - CSQCProjectile(missile, TRUE, PROJECTILE_SUPERROCKET, FALSE); // because of fly sound ++ CSQCProjectile(missile, true, PROJECTILE_SUPERROCKET, false); // because of fly sound + + self.ammo_supercells -= 1; +} + +void W_RocketMinsta_Laser_Explode (void) +{ + if(other.takedamage == DAMAGE_AIM) + if(IS_PLAYER(other)) + if(DIFF_TEAM(self.realowner, other)) + if(other.deadflag == DEAD_NO) + if(IsFlying(other)) + Send_Notification(NOTIF_ONE, self.realowner, MSG_ANNCE, ANNCE_ACHIEVEMENT_ELECTROBITCH); + + self.event_damage = func_null; + self.takedamage = DAMAGE_NO; + RadiusDamage (self, self.realowner, self.rm_damage, self.rm_edmg, autocvar_g_rm_laser_radius, world, world, self.rm_force, self.projectiledeathtype, other); + remove(self); +} + +void W_RocketMinsta_Laser_Touch (void) +{ + PROJECTILE_TOUCH; + //W_RocketMinsta_Laser_Explode (); + RadiusDamage (self, self.realowner, self.rm_damage, self.rm_edmg, autocvar_g_rm_laser_radius, world, world, self.rm_force, self.projectiledeathtype, other); + remove(self); +} + +void W_RocketMinsta_Attack2(void) +{ + makevectors(self.v_angle); + + entity proj; + float counter = 0; + float total = autocvar_g_rm_laser_count; + float spread = autocvar_g_rm_laser_spread; + float rndspread = autocvar_g_rm_laser_spread_random; + + float w = self.weapon; + self.weapon = WEP_ELECTRO; - W_SetupShot_ProjectileSize (self, '0 0 -3', '0 0 -3', FALSE, 2, W_Sound("crylink_fire"), CH_WEAPON_A, autocvar_g_rm_laser_damage); ++ W_SetupShot_ProjectileSize (self, '0 0 -3', '0 0 -3', false, 2, W_Sound("crylink_fire"), CH_WEAPON_A, autocvar_g_rm_laser_damage); + self.weapon = w; + + Send_Effect(EFFECT_ELECTRO_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1); + + while(counter < total) + { + proj = spawn (); + proj.classname = "plasma_prim"; + proj.owner = proj.realowner = self; - proj.bot_dodge = TRUE; ++ proj.bot_dodge = true; + proj.bot_dodgerating = autocvar_g_rm_laser_damage; + proj.use = W_RocketMinsta_Laser_Explode; + proj.think = adaptor_think2use_hittype_splash; + proj.nextthink = time + autocvar_g_rm_laser_lifetime; + PROJECTILE_MAKETRIGGER(proj); + proj.projectiledeathtype = WEP_ELECTRO; + setorigin(proj, w_shotorg); + + proj.rm_force = autocvar_g_rm_laser_force / total; + proj.rm_damage = autocvar_g_rm_laser_damage / total; + proj.rm_edmg = proj.rm_damage; + + //W_SetupProjectileVelocity(proj, autocvar_g_rm_laser_speed, spread * (rndspread ? random() : 1) * autocvar_g_rm_laser_speed); + + proj.movetype = MOVETYPE_BOUNCEMISSILE; + //W_SETUPPROJECTILEVELOCITY(proj, g_balance_minstanex_laser); + proj.velocity = (w_shotdir + (((counter + 0.5) / total) * 2 - 1) * v_right * (spread * (rndspread ? random() : 1))) * cvar("g_rm_laser_speed"); + proj.velocity_z = proj.velocity_z + cvar("g_rm_laser_zspread") * (random() - 0.5); - proj.velocity = W_CalculateProjectileVelocity(proj.realowner.velocity, proj.velocity, TRUE); ++ proj.velocity = W_CalculateProjectileVelocity(proj.realowner.velocity, proj.velocity, true); + proj.angles = vectoangles(proj.velocity); + proj.touch = W_RocketMinsta_Laser_Touch; + setsize(proj, '0 0 -3', '0 0 -3'); + proj.flags = FL_PROJECTILE; + proj.missile_flags = MIF_SPLASH; + - CSQCProjectile(proj, TRUE, PROJECTILE_ROCKETMINSTA_LASER, TRUE); ++ CSQCProjectile(proj, true, PROJECTILE_ROCKETMINSTA_LASER, true); + + other = proj; MUTATOR_CALLHOOK(EditProjectile); + counter++; + } +} + +void W_RocketMinsta_Attack3 (void) +{ + makevectors(self.v_angle); + + entity proj; + float counter = 0; + float total = 1; + + float w = self.weapon; + self.weapon = WEP_ELECTRO; - W_SetupShot_ProjectileSize (self, '0 0 -3', '0 0 -3', FALSE, 2, W_Sound("electro_fire2"), CH_WEAPON_A, autocvar_g_rm_laser_damage); ++ W_SetupShot_ProjectileSize (self, '0 0 -3', '0 0 -3', false, 2, W_Sound("electro_fire2"), CH_WEAPON_A, autocvar_g_rm_laser_damage); + self.weapon = w; + + Send_Effect(EFFECT_ELECTRO_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1); + + while(counter < total) + { + proj = spawn (); + proj.classname = "plasma_prim"; + proj.owner = proj.realowner = self; - proj.bot_dodge = TRUE; ++ proj.bot_dodge = true; + proj.bot_dodgerating = autocvar_g_rm_laser_damage; + proj.use = W_RocketMinsta_Laser_Explode; + proj.think = adaptor_think2use_hittype_splash; + proj.nextthink = time + autocvar_g_rm_laser_lifetime; + PROJECTILE_MAKETRIGGER(proj); + proj.projectiledeathtype = WEP_ELECTRO; + setorigin(proj, w_shotorg); + + proj.rm_force = autocvar_g_rm_laser_force / total; + proj.rm_damage = autocvar_g_rm_laser_damage / total; + proj.rm_edmg = proj.rm_damage; + + //W_SetupProjectileVelocity(proj, autocvar_g_rm_laser_speed, spread * (rndspread ? random() : 1) * autocvar_g_rm_laser_speed); + + proj.movetype = MOVETYPE_BOUNCEMISSILE; + proj.velocity = w_shotdir * autocvar_g_rm_laser_speed; - proj.velocity = W_CalculateProjectileVelocity(proj.realowner.velocity, proj.velocity, TRUE); ++ proj.velocity = W_CalculateProjectileVelocity(proj.realowner.velocity, proj.velocity, true); + proj.angles = vectoangles(proj.velocity); + proj.touch = W_RocketMinsta_Laser_Touch; + setsize(proj, '0 0 -3', '0 0 -3'); + proj.flags = FL_PROJECTILE; + proj.missile_flags = MIF_SPLASH; + - CSQCProjectile(proj, TRUE, PROJECTILE_ROCKETMINSTA_LASER, TRUE); ++ CSQCProjectile(proj, true, PROJECTILE_ROCKETMINSTA_LASER, true); + + other = proj; MUTATOR_CALLHOOK(EditProjectile); + counter++; + } +} + float W_Vaporizer(float req) { float ammo_amount; @@@ -696,74 -143,18 +698,74 @@@ WEP_ACTION(self.weapon, WR_RELOAD); else if(WEP_CVAR(vaporizer, reload_ammo) && self.clip_load < vaporizer_ammo) // forced reload WEP_ACTION(self.weapon, WR_RELOAD); - else if(self.BUTTON_ATCK) + if (self.BUTTON_ATCK && ((self.ammo_cells && autocvar_g_rm) || !autocvar_g_rm) && !forbidWeaponUse(self)) { - if(weapon_prepareattack(0, WEP_CVAR_PRI(vaporizer, refire))) + if (!self.vaporizer_held_down) { - W_Vaporizer_Attack(); - weapon_thinkf(WFRAME_FIRE1, WEP_CVAR_PRI(vaporizer, animtime), w_ready); + if(weapon_prepareattack(0, WEP_CVAR_PRI(vaporizer, refire))) + { + W_Vaporizer_Attack(); + if(time >= self.vaporizer_jumpinterval) + if(WEP_CVAR_PRI(vaporizer, charge) == 1 || (WEP_CVAR_PRI(vaporizer, charge) == 2 && self.cvar_cl_charge) || WEP_CVAR_PRI(vaporizer, charge) >= 3) + if(W_Vaporizer_SuperBlast_CheckAmmo()) + self.vaporizer_held_down = 1; + weapon_thinkf(WFRAME_FIRE1, WEP_CVAR_PRI(vaporizer, animtime), w_ready); + } + } + else if(self.vaporizer_held_down == 1) + { + if(weapon_prepareattack(0, WEP_CVAR_PRI(vaporizer, refire))) + { + sound(self, CH_SHOTS_SINGLE, W_Sound("minstanex_charge_new"), VOL_BASE, ATTN_NORM); + self.vaporizer_held_down = 2; + self.vaporizer_jumpinterval = time + 1.822; + weapon_thinkf(WFRAME_DONTCHANGE, WEP_CVAR_PRI(vaporizer, refire), w_ready); // update frame so we dont get stuck + } + } + else if(time >= self.vaporizer_jumpinterval && self.vaporizer_held_down == 2) + { + if(weapon_prepareattack(0, WEP_CVAR_PRI(vaporizer, refire))) + { + stopsound(self, CH_SHOTS_SINGLE); + if((autocvar_g_rm && autocvar_g_rm_superrocket) || autocvar_g_rm_superrocket == 2) + W_RocketMinsta_SuperRocket(); + else + W_Vaporizer_SuperBlast(); + + self.vaporizer_held_down = 0; + weapon_thinkf(WFRAME_FIRE1, WEP_CVAR_PRI(vaporizer, animtime), w_ready); + } } } - else if(self.BUTTON_ATCK2) + else { - if(self.jump_interval <= time) - if(weapon_prepareattack(1, -1)) + if(self.vaporizer_held_down >= 2) + stopsound(self, CH_SHOTS_SINGLE); - self.vaporizer_held_down = FALSE; ++ self.vaporizer_held_down = false; + } + + if (self.BUTTON_ATCK2 || (self.BUTTON_ATCK && !self.ammo_cells && autocvar_g_rm)) + { + if((autocvar_g_rm && autocvar_g_rm_laser) || autocvar_g_rm_laser == 2) + { + if(self.jump_interval <= time && !self.held_down) + { + if(rapid) - self.held_down = TRUE; ++ self.held_down = true; + self.jump_interval = time + autocvar_g_rm_laser_refire; + self.jump_interval2 = time + autocvar_g_rm_laser_rapid_delay; + damage_goodhits = 0; + W_RocketMinsta_Attack2(); + } + else if(rapid && self.jump_interval2 <= time && self.held_down) + { + self.jump_interval2 = time + autocvar_g_rm_laser_rapid_refire; + damage_goodhits = 0; + W_RocketMinsta_Attack3(); + //weapon_thinkf(WFRAME_FIRE2, autocvar_g_rm_laser_rapid_animtime, w_ready); + } + } + else if (self.jump_interval <= time) { // handle refire manually, so that primary and secondary can be fired without conflictions (important for instagib) self.jump_interval = time + WEP_CVAR_SEC(vaporizer, refire) * W_WeaponRateFactor(); @@@ -790,26 -181,22 +792,26 @@@ weapon_thinkf(WFRAME_FIRE2, WEP_CVAR_SEC(vaporizer, animtime), w_ready); } } - + else - self.held_down = FALSE; ++ self.held_down = false; + - return TRUE; + return true; } case WR_INIT: { precache_model("models/nexflash.md3"); - precache_model("models/weapons/g_minstanex.md3"); - precache_model("models/weapons/v_minstanex.md3"); - precache_model("models/weapons/h_minstanex.iqm"); - precache_sound("weapons/minstanexfire.wav"); - precache_sound("weapons/nexwhoosh1.wav"); - precache_sound("weapons/nexwhoosh2.wav"); - precache_sound("weapons/nexwhoosh3.wav"); + precache_model(W_Model("g_minstanex.md3")); + precache_model(W_Model("v_minstanex.md3")); + precache_model(W_Model("h_minstanex.iqm")); + precache_sound (W_Sound("minstanex_charge_new")); + precache_sound (W_Sound("minstanex_charge2")); + precache_sound(W_Sound("minstanexfire")); + precache_sound(W_Sound("nexwhoosh1")); + precache_sound(W_Sound("nexwhoosh2")); + precache_sound(W_Sound("nexwhoosh3")); //W_Blaster(WR_INIT); // Samual: Is this really the proper thing to do? Didn't we already run this previously? - VAPORIZER_SETTINGS(WEP_SKIP_CVAR, WEP_SET_PROP) - return TRUE; + VAPORIZER_SETTINGS(WEP_SKIP_CVAR, WEP_SET_PROP); + return true; } case WR_SETUP: { @@@ -849,8 -236,8 +851,8 @@@ else used_ammo = vaporizer_ammo; - W_Reload(used_ammo, "weapons/reload.wav"); + W_Reload(used_ammo, W_Sound("reload")); - return TRUE; + return true; } case WR_SUICIDEMESSAGE: { @@@ -861,134 -245,13 +863,134 @@@ } case WR_KILLMESSAGE: { - return WEAPON_VAPORIZER_MURDER; + if(w_deathtype & HITTYPE_SPLASH) + return WEAPON_VAPORIZER_MURDER_CHARGE; + else + return WEAPON_VAPORIZER_MURDER; } } - return FALSE; + return false; } #endif #ifdef CSQC + +.vector sb_shotorg; +.vector sb_endpos; +.float sb_spread_max; +.float sb_spread_min; +.float sb_time; + +void Draw_SuperBlast() +{ + float a = bound(0, (0.5 - ((time - self.sb_time) / 0.4)), 0.5); + + if(!a) { remove(self); } + + vector deviation, angle; + + vector sb_color = getcsqcplayercolor(self.sv_entnum); // GetTeamRGB(GetPlayerColor(self.sv_entnum)); + + vector first_min_end = '0 0 0', prev_min_end = '0 0 0', new_min_end = '0 0 0'; + vector first_max_end = '0 0 0', prev_max_end = '0 0 0', new_max_end = '0 0 0'; + + float new_max_dist, new_min_dist; + + vector shotdir = normalize(self.sb_endpos - self.sb_shotorg); + vectorvectors(shotdir); + vector right = v_right; + vector up = v_up; + + float counter, dist_before_normal = 200, shots = 20; + + vector min_end = ((self.sb_shotorg + (shotdir * dist_before_normal)) + (up * self.sb_spread_min)); + vector max_end = (self.sb_endpos + (up * self.sb_spread_max)); + + float spread_to_min = vlen(normalize(min_end - self.sb_shotorg) - shotdir); + float spread_to_max = vlen(normalize(max_end - min_end) - shotdir); + + for(counter = 0; counter < shots; ++counter) + { + // perfect circle effect lines + angle = '0 0 0'; + makevectors('0 360 0' * (0.75 + (counter - 0.5) / shots)); + angle_y = v_forward_x; + angle_z = v_forward_y; + + // first do the spread_to_min effect + deviation = angle * spread_to_min; + deviation = ((shotdir + (right * deviation_y) + (up * deviation_z))); + new_min_dist = dist_before_normal; + new_min_end = (self.sb_shotorg + (deviation * new_min_dist)); + //te_lightning2(world, new_min_end, self.sb_shotorg); + + // then calculate spread_to_max effect + deviation = angle * spread_to_max; + deviation = ((shotdir + (right * deviation_y) + (up * deviation_z))); + new_max_dist = vlen(new_min_end - self.sb_endpos); + new_max_end = (new_min_end + (deviation * new_max_dist)); + //te_lightning2(world, new_end, prev_min_end); + + + if(counter == 0) + { + first_min_end = new_min_end; + first_max_end = new_max_end; + } + + if(counter >= 1) + { + R_BeginPolygon("", DRAWFLAG_NORMAL); + R_PolygonVertex(prev_min_end, '0 0 0', sb_color, a); + R_PolygonVertex(new_min_end, '0 0 0', sb_color, a); + R_PolygonVertex(self.sb_shotorg, '0 0 0', sb_color, a); + R_EndPolygon(); + + R_BeginPolygon("", DRAWFLAG_NORMAL); + R_PolygonVertex(new_min_end, '0 0 0', sb_color, a); + R_PolygonVertex(prev_min_end, '0 0 0', sb_color, a); + R_PolygonVertex(prev_max_end, '0 0 0', sb_color, a); + R_PolygonVertex(new_max_end, '0 0 0', sb_color, a); + R_EndPolygon(); + } + + prev_min_end = new_min_end; + prev_max_end = new_max_end; + + if((counter + 1) == shots) + { + R_BeginPolygon("", DRAWFLAG_NORMAL); + R_PolygonVertex(prev_min_end, '0 0 0', sb_color, a); + R_PolygonVertex(first_min_end, '0 0 0', sb_color, a); + R_PolygonVertex(self.sb_shotorg, '0 0 0', sb_color, a); + R_EndPolygon(); + + R_BeginPolygon("", DRAWFLAG_NORMAL); + R_PolygonVertex(first_min_end, '0 0 0', sb_color, a); + R_PolygonVertex(prev_min_end, '0 0 0', sb_color, a); + R_PolygonVertex(prev_max_end, '0 0 0', sb_color, a); + R_PolygonVertex(first_max_end, '0 0 0', sb_color, a); + R_EndPolygon(); + } + } +} + +void Net_ReadSuperBlastParticle() +{ - entity shockwave; - shockwave = spawn(); - shockwave.draw = Draw_SuperBlast; ++ entity superblast; ++ superblast = spawn(); ++ superblast.draw = Draw_SuperBlast; + - shockwave.sb_shotorg_x = ReadCoord(); shockwave.sb_shotorg_y = ReadCoord(); shockwave.sb_shotorg_z = ReadCoord(); - shockwave.sb_endpos_x = ReadCoord(); shockwave.sb_endpos_y = ReadCoord(); shockwave.sb_endpos_z = ReadCoord(); ++ superblast.sb_shotorg_x = ReadCoord(); superblast.sb_shotorg_y = ReadCoord(); superblast.sb_shotorg_z = ReadCoord(); ++ superblast.sb_endpos_x = ReadCoord(); superblast.sb_endpos_y = ReadCoord(); superblast.sb_endpos_z = ReadCoord(); + - shockwave.sb_spread_max = ReadByte(); - shockwave.sb_spread_min = ReadByte(); ++ superblast.sb_spread_max = ReadByte(); ++ superblast.sb_spread_min = ReadByte(); + - shockwave.sv_entnum = ReadByte(); ++ superblast.sv_entnum = ReadByte(); + - shockwave.sb_time = time; ++ superblast.sb_time = time; +} + float W_Vaporizer(float req) { switch(req) diff --cc qcsrc/common/weapons/w_vortex.qc index c61cd75bd,374256403..65e431eff --- a/qcsrc/common/weapons/w_vortex.qc +++ b/qcsrc/common/weapons/w_vortex.qc @@@ -56,6 -56,6 +56,8 @@@ VORTEX_SETTINGS(WEP_ADD_CVAR, WEP_ADD_P #endif #else #ifdef SVQC ++#include "../effects.qh" ++ void spawnfunc_weapon_vortex(void) { weapon_defaultspawnfunc(WEP_VORTEX); } void spawnfunc_weapon_nex(void) { spawnfunc_weapon_vortex(); } @@@ -100,10 -100,10 +102,10 @@@ void W_Vortex_Attack(float issecondary mydmg *= charge; myforce *= charge; - W_SetupShot(self, TRUE, 5, W_Sound("nexfire"), CH_WEAPON_A, mydmg); - W_SetupShot(self, true, 5, "weapons/nexfire.wav", CH_WEAPON_A, mydmg); - if(charge > WEP_CVAR(vortex, charge_animlimit) && WEP_CVAR(vortex, charge_animlimit)) // if the Vortex is overcharged, we play an extra sound ++ W_SetupShot(self, true, 5, W_Sound("nexfire"), CH_WEAPON_A, mydmg); + if(WEP_CVAR(vortex, charge) && charge > WEP_CVAR(vortex, charge_animlimit) && WEP_CVAR(vortex, charge_animlimit)) // if the Vortex is overcharged, we play an extra sound { - sound(self, CH_WEAPON_B, "weapons/nexcharge.wav", VOL_BASE * (charge - 0.5 * WEP_CVAR(vortex, charge_animlimit)) / (1 - 0.5 * WEP_CVAR(vortex, charge_animlimit)), ATTN_NORM); + sound(self, CH_WEAPON_B, W_Sound("nexcharge"), VOL_BASE * (charge - 0.5 * WEP_CVAR(vortex, charge_animlimit)) / (1 - 0.5 * WEP_CVAR(vortex, charge_animlimit)), ATTN_NORM); } yoda = 0; @@@ -243,16 -243,16 +245,16 @@@ float W_Vortex(float req case WR_INIT: { precache_model("models/nexflash.md3"); - precache_model("models/weapons/g_nex.md3"); - precache_model("models/weapons/v_nex.md3"); - precache_model("models/weapons/h_nex.iqm"); - precache_sound("weapons/nexfire.wav"); - precache_sound("weapons/nexcharge.wav"); - precache_sound("weapons/nexwhoosh1.wav"); - precache_sound("weapons/nexwhoosh2.wav"); - precache_sound("weapons/nexwhoosh3.wav"); - VORTEX_SETTINGS(WEP_SKIP_CVAR, WEP_SET_PROP); + precache_model(W_Model("g_nex.md3")); + precache_model(W_Model("v_nex.md3")); + precache_model(W_Model("h_nex.iqm")); + precache_sound(W_Sound("nexfire")); + precache_sound(W_Sound("nexcharge")); + precache_sound(W_Sound("nexwhoosh1")); + precache_sound(W_Sound("nexwhoosh2")); + precache_sound(W_Sound("nexwhoosh3")); + VORTEX_SETTINGS(WEP_SKIP_CVAR, WEP_SET_PROP) - return TRUE; + return true; } case WR_CHECKAMMO1: { @@@ -281,8 -281,8 +283,8 @@@ } case WR_RELOAD: { - W_Reload(min(WEP_CVAR_PRI(vortex, ammo), WEP_CVAR_SEC(vortex, ammo)), "weapons/reload.wav"); + W_Reload(min(WEP_CVAR_PRI(vortex, ammo), WEP_CVAR_SEC(vortex, ammo)), W_Sound("reload")); - return TRUE; + return true; } case WR_SUICIDEMESSAGE: { @@@ -306,11 -306,11 +308,11 @@@ float W_Vortex(float req { vector org2; org2 = w_org + w_backoff * 6; - pointparticles(particleeffectnum("nex_impact"), org2, '0 0 0', 1); + pointparticles(particleeffectnum(((autocvar_cl_particles_newvortexbeam && (getstati(STAT_ALLOW_OLDVORTEXBEAM) || isdemo())) ? "nex_impact_new" : "nex_impact")), org2, '0 0 0', 1); if(!w_issilent) sound(self, CH_SHOTS, "weapons/neximpact.wav", VOL_BASE, ATTN_NORM); - - return TRUE; + + return true; } case WR_INIT: { diff --cc qcsrc/common/weapons/weapons.qc index 90287b789,55fcc0e14..c119d79cd --- a/qcsrc/common/weapons/weapons.qc +++ b/qcsrc/common/weapons/weapons.qc @@@ -348,31 -372,4 +401,34 @@@ int GetAmmoStat(.float ammotype } } #endif + +#ifdef SVQC +string W_Sound(string w_snd) +{ + if(autocvar_sv_weapons_sounddir != "" && autocvar_sv_weapons_sounddir != "default") + { + string thesnd = sprintf("weapons_%s/%s", autocvar_sv_weapons_sounddir, w_snd); - float globhandle = search_begin(strcat("sound/", thesnd, ".*"), TRUE, FALSE); ++ float globhandle = search_begin(strcat("sound/", thesnd, ".*"), true, false); + if(globhandle >= 0) + { + search_end(globhandle); + return strcat(thesnd, ".wav"); + } + } + return strcat("weapons/", w_snd, ".wav"); +} + +string W_Model(string w_mdl) +{ + if(autocvar_sv_weapons_modeloverride != "" && autocvar_sv_weapons_modeloverride != "default") + { + string themdl = sprintf("models/weapons_%s/%s", autocvar_sv_weapons_modeloverride, w_mdl); + if(fexists(themdl)) + return themdl; + } + return strcat("models/weapons/", w_mdl); +} ++ ++#endif ++ #endif diff --cc qcsrc/common/weapons/weapons.qh index 99ad7eb51,7ac8a9e8d..895ef257c --- a/qcsrc/common/weapons/weapons.qh +++ b/qcsrc/common/weapons/weapons.qh @@@ -78,15 -80,10 +82,15 @@@ void W_RandomWeapons(entity e, float n) string GetAmmoPicture(.float ammotype); #ifdef CSQC - .float GetAmmoFieldFromNum(float i); - float GetAmmoStat(.float ammotype); + .float GetAmmoFieldFromNum(int i); + int GetAmmoStat(.float ammotype); #endif +#ifdef SVQC +string W_Sound(string w_snd); +string W_Model(string w_mdl); +#endif + // ammo types .float ammo_shells; .float ammo_nails; diff --cc qcsrc/csqcmodellib/cl_player.qc index c8a821be1,1f3017afd..0c740a253 --- a/qcsrc/csqcmodellib/cl_player.qc +++ b/qcsrc/csqcmodellib/cl_player.qc @@@ -239,11 -250,10 +250,11 @@@ void CSQCPlayer_SetCamera( CSQCPlayer_SavePrediction(); } - CSQCPlayer_PredictTo(clientcommandframe + 1, TRUE); + CSQCPlayer_PredictTo(clientcommandframe + 1, true); -#ifdef CSQCMODEL_SERVERSIDE_CROUCH +//#ifdef CSQCMODEL_SERVERSIDE_CROUCH // get crouch state from the server (LAG) + if(!autocvar_cl_crouch) if(getstati(STAT_VIEWHEIGHT) == PL_VIEW_OFS_z) self.pmove_flags &= ~PMF_DUCKED; else if(getstati(STAT_VIEWHEIGHT) == PL_CROUCH_VIEW_OFS_z) diff --cc qcsrc/dpdefs/csprogsdefs.qh index 000000000,6f820c729..79b3e9b35 mode 000000,100644..100644 --- a/qcsrc/dpdefs/csprogsdefs.qh +++ b/qcsrc/dpdefs/csprogsdefs.qh @@@ -1,0 -1,1453 +1,1454 @@@ + #ifndef CSPROGSDEFS_H + #define CSPROGSDEFS_H + + #pragma noref 1 + + /* + ============================================================================== + + SOURCE FOR GLOBALVARS_T C STRUCTURE + MUST NOT BE MODIFIED, OR CRC ERRORS WILL APPEAR + + ============================================================================== + */ + + // + // system globals + // + entity self; + entity other; + entity world; + float time; + float frametime; + + int player_localentnum; //the entnum + int player_localnum; //the playernum + float maxclients; //a constant filled in by the engine. gah, portability eh? + + float clientcommandframe; //player movement + float servercommandframe; //clientframe echoed off the server + + string mapname; + + // + // global variables set by built in functions + // + vector v_forward, v_up, v_right; // set by makevectors() + + // set by traceline / tracebox + float trace_allsolid; + float trace_startsolid; + float trace_fraction; + vector trace_endpos; + vector trace_plane_normal; + float trace_plane_dist; + entity trace_ent; + float trace_inopen; + float trace_inwater; + + // + // required prog functions + // + void() CSQC_Init; + void() CSQC_Shutdown; + float(float f, float t, float n) CSQC_InputEvent; + void(float w, float h) CSQC_UpdateView; + bool(string s) CSQC_ConsoleCommand; + + //these fields are read and set by the default player physics + vector pmove_org; + vector pmove_vel; + vector pmove_mins; + vector pmove_maxs; + //retrieved from the current movement commands (read by player physics) + float input_timelength; + vector input_angles; + vector input_movevalues; //forwards, right, up. + int input_buttons; //attack, use, jump (default physics only uses jump) + + float movevar_gravity; + float movevar_stopspeed; + float movevar_maxspeed; + float movevar_spectatormaxspeed; //used by NOCLIP movetypes. + float movevar_accelerate; + float movevar_airaccelerate; + float movevar_wateraccelerate; + float movevar_friction; + float movevar_waterfriction; + float movevar_entgravity; //the local player's gravity field. Is a multiple (1 is the normal value) + + //================================================ + void end_sys_globals; // flag for structure dumping + //================================================ + + /* + ============================================================================== + + SOURCE FOR ENTVARS_T C STRUCTURE + MUST NOT BE MODIFIED, OR CRC ERRORS WILL APPEAR + + ============================================================================== + */ + + // + // system fields (*** = do not set in prog code, maintained by C code) + // + .int modelindex; // *** model index in the precached list + .vector absmin, absmax; // *** origin + mins / maxs + + .int entnum; // *** the ent number as on the server + .float drawmask; + .void() predraw; + + .float movetype; + .float solid; + + .vector origin; // *** + .vector oldorigin; // *** + .vector velocity; + .vector angles; + .vector avelocity; + + .string classname; // spawn function + .string model; + .int frame; + .int skin; + .int effects; + + .vector mins, maxs; // bounding box extents reletive to origin + .vector size; // maxs - mins + + .void() touch; + .void() use; + .void() think; + .void() blocked; // for doors or plats, called when can't push other + + .float nextthink; + + .entity chain; + + .string netname; + + .entity enemy; + + .int flags; + + .int colormap; + + .entity owner; // who launched a missile + + //================================================ + void end_sys_fields; // flag for structure dumping + //================================================ + + /* + ============================================================================== + + OPTIONAL FIELDS AND GLOBALS + + ============================================================================== + */ + + // Additional OPTIONAL Fields and Globals + float intermission; // indicates intermission state (0 = normal, 1 = scores, 2 = finale text) + + vector view_angles; // same as input_angles + vector view_punchangle; // from server + vector view_punchvector; // from server + + /* + ============================================================================== + + CONSTANT DEFINITIONS + + ============================================================================== + */ + + const int MASK_ENGINE = 1; + const int MASK_ENGINEVIEWMODELS = 2; + const int MASK_NORMAL = 4; + + const int RF_VIEWMODEL = 1; + const int RF_EXTERNALMODEL = 2; + const int RF_DEPTHHACK = 4; + const int RF_ADDITIVE = 8; + const int RF_USEAXIS = 16; + + const int VF_MIN = 1; //(vector) + const int VF_MIN_X = 2; //(float) + const int VF_MIN_Y = 3; //(float) + const int VF_SIZE = 4; //(vector) (viewport size) + const int VF_SIZE_Y = 5; //(float) + const int VF_SIZE_X = 6; //(float) + const int VF_VIEWPORT = 7; //(vector, vector) + const int VF_FOV = 8; //(vector) + const int VF_FOVX = 9; //(float) + const int VF_FOVY = 10; //(float) + const int VF_ORIGIN = 11; //(vector) + const int VF_ORIGIN_X = 12; //(float) + const int VF_ORIGIN_Y = 13; //(float) + const int VF_ORIGIN_Z = 14; //(float) + const int VF_ANGLES = 15; //(vector) + const int VF_ANGLES_X = 16; //(float) + const int VF_ANGLES_Y = 17; //(float) + const int VF_ANGLES_Z = 18; //(float) + const int VF_DRAWWORLD = 19; //(float) + const int VF_DRAWENGINESBAR = 20; //(float) + const int VF_DRAWCROSSHAIR = 21; //(float) + + const int VF_CL_VIEWANGLES = 33; //(vector) + const int VF_CL_VIEWANGLES_X = 34; //(float) + const int VF_CL_VIEWANGLES_Y = 35; //(float) + const int VF_CL_VIEWANGLES_Z = 36; //(float) + + const int VF_PERSPECTIVE = 200; + + //const int STAT_HEALTH = 0; + //const int STAT_WEAPONMODEL = 2; + //const int STAT_AMMO = 3; + //const int STAT_ARMOR = 4; + //const int STAT_WEAPONFRAME = 5; + //const int STAT_SHELLS = 6; + //const int STAT_NAILS = 7; + //const int STAT_ROCKETS = 8; + //const int STAT_CELLS = 9; + //const int STAT_ACTIVEWEAPON = 10; + //const int STAT_TOTALSECRETS = 11; + //const int STAT_TOTALMONSTERS = 12; + //const int STAT_SECRETS = 13; + //const int STAT_MONSTERS = 14; + //const int STAT_ITEMS = 15; + //const int STAT_VIEWHEIGHT = 16; + + // Quake Sound Constants + const int CHAN_AUTO = 0; + const int CHAN_WEAPON = 1; + const int CHAN_VOICE = 2; + const int CHAN_ITEM = 3; + const int CHAN_BODY = 4; + + const int ATTN_NONE = 0; + const int ATTN_NORM = 1; + const int ATTN_IDLE = 2; + const int ATTN_STATIC = 3; + + // Frik File Constants + const int FILE_READ = 0; + const int FILE_APPEND = 1; + const int FILE_WRITE = 2; + + // Quake Point Contents + const int CONTENT_EMPTY = -1; + const int CONTENT_SOLID = -2; + const int CONTENT_WATER = -3; + const int CONTENT_SLIME = -4; + const int CONTENT_LAVA = -5; + const int CONTENT_SKY = -6; + + // Quake Solid Constants + const int SOLID_NOT = 0; + const int SOLID_TRIGGER = 1; + const int SOLID_BBOX = 2; + const int SOLID_SLIDEBOX = 3; + const int SOLID_BSP = 4; + const int SOLID_CORPSE = 5; + + // Quake Move Constants + const int MOVE_NORMAL = 0; + const int MOVE_NOMONSTERS = 1; + const int MOVE_MISSILE = 2; + + const float EXTRA_LOW = -99999999; + const float EXTRA_HIGH = 99999999; + + const vector VEC_1 = '1 1 1'; + const vector VEC_0 = '0 0 0'; + const vector VEC_M1 = '-1 -1 -1'; + + //const float M_PI = 3.14159265358979323846; + + vector VEC_HULL_MIN = '-16 -16 -24'; + vector VEC_HULL_MAX = '16 16 32'; + + // Quake Temporary Entity Constants + const int TE_SPIKE = 0; + const int TE_SUPERSPIKE = 1; + const int TE_GUNSHOT = 2; + const int TE_EXPLOSION = 3; + const int TE_TAREXPLOSION = 4; + const int TE_LIGHTNING1 = 5; + const int TE_LIGHTNING2 = 6; + const int TE_WIZSPIKE = 7; + const int TE_KNIGHTSPIKE = 8; + const int TE_LIGHTNING3 = 9; + const int TE_LAVASPLASH = 10; + const int TE_TELEPORT = 11; + const int TE_EXPLOSION2 = 12; + + // Darkplaces Additions + const int TE_EXPLOSIONRGB = 53; + const int TE_GUNSHOTQUAD = 57; + const int TE_EXPLOSIONQUAD = 70; + const int TE_SPIKEQUAD = 58; + const int TE_SUPERSPIKEQUAD = 59; + + // PFlags for Dynamic Lights + const int PFLAGS_NOSHADOW = 1; + const int PFLAGS_CORONA = 2; + const int PFLAGS_FULLDYNAMIC = 128; + + const int EF_ADDITIVE = 32; + const int EF_BLUE = 64; + const int EF_FLAME = 1024; + const int EF_FULLBRIGHT = 512; + const int EF_NODEPTHTEST = 8192; + const int EF_NODRAW = 16; + const int EF_NOSHADOW = 4096; + const int EF_RED = 128; + const int EF_STARDUST = 2048; + const int EF_SELECTABLE = 16384; + + const int PFL_ONGROUND = 1; + const int PFL_CROUCH = 2; + const int PFL_DEAD = 4; + const int PFL_GIBBED = 8; + + // draw flags + const int DRAWFLAG_NORMAL = 0; + const int DRAWFLAG_ADDITIVE = 1; + const int DRAWFLAG_MODULATE = 2; + const int DRAWFLAG_2XMODULATE = 3; + const int DRAWFLAG_SCREEN = 4; + const int DRAWFLAG_MIPMAP = 0x100; // only for R_BeginPolygon + + /* + ============================================================================== + + BUILTIN DEFINITIONS + EXTENSIONS ARE NOT ADDED HERE, BUT BELOW! + + ============================================================================== + */ + + void(vector ang) makevectors = #1; + void(entity e, vector o) setorigin = #2; + void(entity e, string m) setmodel = #3; + void(entity e, vector min, vector max) setsize = #4; + + void() break_to_debugger = #6; + float() random = #7; + void(entity e, float chan, string samp) sound = #8; + vector(vector v) normalize = #9; + void(string e) error = #10; + void(string e) objerror = #11; + float(vector v) vlen = #12; + float(vector v) vectoyaw = #13; + entity() spawn = #14; + void(entity e) remove = #15; + float(vector v1, vector v2, float tryents, entity ignoreentity) traceline = #16; + + entity(entity start, .string fld, string match) find = #18; + void(string s) precache_sound = #19; + void(string s) precache_model = #20; + + entity(vector org, float rad) findradius = #22; + + void(string s, ...) dprint = #25; + string(float f) ftos = #26; + string(vector v) vtos = #27; + void() coredump = #28; + void() traceon = #29; + void() traceoff = #30; + void(entity e) eprint = #31; + // settrace optional + float(float yaw, float dist, float settrace) walkmove = #32; + + float() droptofloor = #34; + void(float style, string value) lightstyle = #35; + int(float v) rint = #36; + int(float v) floor = #37; + int(float v) ceil = #38; + + float(entity e) checkbottom = #40; + float(vector v) pointcontents = #41; + + float(float f) fabs = #43; + + float(string s) cvar = #45; + void(string s, ...) localcmd = #46; + entity(entity e) nextent = #47; + void(vector o, vector d, float color, float count) particle = #48; + void() ChangeYaw = #49; + + vector(vector v) vectoangles = #51; + vector(vector v, vector w) vectoangles2 = #51; + + float(float f) sin = #60; + float(float f) cos = #61; + float(float f) sqrt = #62; + void(entity ent) changepitch = #63; + void(entity e, entity ignore) tracetoss = #64; + string(entity ent) etos = #65; + + string(string s) precache_file = #68; + void(entity e) makestatic = #69; + + void(string name, string value) cvar_set = #72; + + void(vector pos, string samp, float vol, float atten) ambientsound = #74; + string(string s) precache_model2 = #75; + string(string s) precache_sound2 = #76; + string(string s) precache_file2 = #77; + + float(string s) stof = #81; + + + void(vector v1, vector min, vector max, vector v2, float nomonsters, entity forent) tracebox = #90; + vector() randomvec = #91; + vector(vector org) getlight = #92; + vector(vector org, float lpflags) getlight2 = #92; + vector getlight_dir; + vector getlight_ambient; + vector getlight_diffuse; + const float LP_LIGHTMAP = 1; + const float LP_RTWORLD = 2; + const float LP_DYNLIGHT = 4; + const float LP_COMPLETE = 7; + + float(string name, string value) registercvar = #93; + float( float a, ... ) min = #94; + float( float b, ... ) max = #95; + float(float minimum, float val, float maximum) bound = #96; + float(float f, float f) pow = #97; + entity(entity start, .float fld, float match) findfloat = #98; ++entity(entity start, .entity fld, entity match) findentity = #98; + float(string s) checkextension = #99; + // FrikaC and Telejano range #100-#199 + + int(string filename, int mode) fopen = #110; + void(float fhandle) fclose = #111; + string(float fhandle) fgets = #112; + void(float fhandle, string s) fputs = #113; + float(string s) strlen = #114; + string(...) strcat = #115; + string(string s, float start, float length) substring = #116; + vector(string) stov = #117; + string(string s) strzone = #118; + void(string s) strunzone = #119; + + // FTEQW range #200-#299 + + float(float number, float quantity) bitshift = #218; + + //float(string str, string sub[, float startpos]) strstrofs = #221; + int(string str, string sub, float startpos) strstrofs = #221; + int(string str, float ofs) str2chr = #222; + string(int c, ...) chr2str = #223; + string(float ccase, float calpha, float cnum, string s, ...) strconv = #224; + string(float chars, string s, ...) strpad = #225; + string(string info, string key, string value, ...) infoadd = #226; + string(string info, string key) infoget = #227; + int(string s1, string s2) strcmp = #228; + int(string s1, string s2, float len) strncmp = #228; + int(string s1, string s2) strcasecmp = #229; + int(string s1, string s2, float len) strncasecmp = #230; + + // CSQC range #300-#399 + void() clearscene = #300; + void(float mask) addentities = #301; + void(entity ent) addentity = #302; + float(float property, ...) setproperty = #303; + float(float property) getproperty = #309; + vector(float property) getpropertyvec = #309; + void() renderscene = #304; + void(vector org, float radius, vector lightcolours) adddynamiclight = #305; + void(vector org, float radius, vector lightcolours, float style, string cubemapname, float pflags) adddynamiclight2 = #305; + //void(string texturename, float flag[, float is2d, float lines]) R_BeginPolygon = #306; + void(string texturename, float flag, ...) R_BeginPolygon = #306; + void(vector org, vector texcoords, vector rgb, float alpha) R_PolygonVertex = #307; + void() R_EndPolygon = #308; + vector (vector v) cs_unproject = #310; + vector (vector v) cs_project = #311; + + void(float width, vector pos1, vector pos2, float flag) drawline = #315; + float(string name) iscachedpic = #316; + string(string name, ...) precache_pic = #317; + string(string name) precache_cubemap = #317; + vector(string picname) draw_getimagesize = #318; + void(string name) freepic = #319; + float(vector position, float character, vector scale, vector rgb, float alpha, float flag) drawcharacter = #320; + float(vector position, string text, vector scale, vector rgb, float alpha, float flag) drawstring = #321; + float(vector position, string pic, vector size, vector rgb, float alpha, float flag) drawpic = #322; + float(vector position, vector size, vector rgb, float alpha, float flag) drawfill = #323; + void(float x, float y, float width, float height) drawsetcliparea = #324; + void(void) drawresetcliparea = #325; + float(vector position, string text, vector scale, float alpha, float flag) drawcolorcodedstring = #326; + vector(vector position, string text, vector scale, vector rgb, float alpha, float flag) drawcolorcodedstring2 = #326; + + float(int stnum) getstatf = #330; + int(int stnum, ...) getstati = #331; // can optionally take first bit and count + string(float firststnum) getstats = #332; + void(entity e, float mdlindex) setmodelindex = #333; + string(float mdlindex) modelnameforindex = #334; + int(string effectname) particleeffectnum = #335; + void(entity ent, float effectnum, vector start, vector end) trailparticles = #336; + //void(float effectnum, vector origin [, vector dir, float count]) pointparticles = #337; + void(float effectnum, vector origin , vector dir, float count) pointparticles = #337; + void(string s, ...) centerprint = #338; + void(string s, ...) print = #339; + string(float keynum) keynumtostring = #340; + float(string keyname) stringtokeynum = #341; + string(float keynum) getkeybind = #342; + void(float usecursor) setcursormode = #343; + vector() getmousepos = #344; + float(float framenum) getinputstate = #345; + void(float sens) setsensitivityscale = #346; + void(...) runstandardplayerphysics = #347; // this may or may not take a player ent + string(float playernum, string keyname) getplayerkeyvalue = #348; + float() isdemo = #349; + float() isserver = #350; + void(vector origin, vector forward, vector right, vector up) SetListener = #351; + void(string cmdname) registercommand = #352; + float(entity ent) wasfreed = #353; + string(string key) serverkey = #354; + + // Use proper case; refer to the id1 Write* functions! + int() ReadByte = #360; + int() ReadChar = #361; + int() ReadShort = #362; + int() ReadLong = #363; + float() ReadCoord = #364; + float() ReadAngle = #365; + string() ReadString = #366; + float() ReadFloat = #367; + + // LordHavoc's range #400-#499 + void(entity from, entity to) copyentity = #400; + + entity(.string fld, string match) findchain = #402; + entity(.float fld, float match) findchainfloat = #403; + void(vector org, string modelname, float startframe, float endframe, float framerate) effect = #404; + void(vector org, vector velocity, float howmany) te_blood = #405; + void(vector mincorner, vector maxcorner, float explosionspeed, float howmany) te_bloodshower = #406; + void(vector org, vector color) te_explosionrgb = #407; + void(vector mincorner, vector maxcorner, vector vel, float howmany, float color, float gravityflag, float randomveljitter) te_particlecube = #408; + void(vector mincorner, vector maxcorner, vector vel, float howmany, float color) te_particlerain = #409; + void(vector mincorner, vector maxcorner, vector vel, float howmany, float color) te_particlesnow = #410; + void(vector org, vector vel, float howmany) te_spark = #411; + void(vector org) te_gunshotquad = #412; + void(vector org) te_spikequad = #413; + void(vector org) te_superspikequad = #414; + void(vector org) te_explosionquad = #415; + void(vector org) te_smallflash = #416; + void(vector org, float radius, float lifetime, vector color) te_customflash = #417; + void(vector org) te_gunshot = #418; + void(vector org) te_spike = #419; + void(vector org) te_superspike = #420; + void(vector org) te_explosion = #421; + void(vector org) te_tarexplosion = #422; + void(vector org) te_wizspike = #423; + void(vector org) te_knightspike = #424; + void(vector org) te_lavasplash = #425; + void(vector org) te_teleport = #426; + void(vector org, float colorstart, float colorlength) te_explosion2 = #427; + void(entity own, vector start, vector end) te_lightning1 = #428; + void(entity own, vector start, vector end) te_lightning2 = #429; + void(entity own, vector start, vector end) te_lightning3 = #430; + void(entity own, vector start, vector end) te_beam = #431; + void(vector dir) vectorvectors = #432; + void(vector org) te_plasmaburn = #433; + //float(entity e, float s) getsurfacenumpoints = #434; + //vector(entity e, float s, float n) getsurfacepoint = #435; + //vector(entity e, float s) getsurfacenormal = #436; + //string(entity e, float s) getsurfacetexture = #437; + //float(entity e, vector p) getsurfacenearpoint = #438; + //vector(entity e, float s, vector p) getsurfaceclippedpoint = #439; + + int(string s) tokenize = #441; + string(float n) argv = #442; + void(entity e, entity tagentity, string tagname) setattachment = #443; + float(string pattern, float caseinsensitive, float quiet) search_begin = #444; + void(float handle) search_end = #445; + float(float handle) search_getsize = #446; + string(float handle, float num) search_getfilename = #447; + string(string s) cvar_string = #448; + entity(entity start, .float fld, float match) findflags = #449; + entity(.float fld, float match) findchainflags = #450; + int(entity ent, string tagname) gettagindex = #451; + vector(entity ent, float tagindex) gettaginfo = #452; + + void(vector org, vector vel, float howmany) te_flamejet = #457; + + entity(float num) entitybyindex = #459; + int() buf_create = #460; + void(float bufhandle) buf_del = #461; + float(float bufhandle) buf_getsize = #462; + void(float bufhandle_from, float bufhandle_to) buf_copy = #463; + void(float bufhandle, float sortpower, float backward) buf_sort = #464; + string(float bufhandle, string glue) buf_implode = #465; + string(float bufhandle, float string_index) bufstr_get = #466; + void(float bufhandle, float string_index, string str) bufstr_set = #467; + float(float bufhandle, string str, float order) bufstr_add = #468; + void(float bufhandle, float string_index) bufstr_free = #469; + + //float(float s) asin = #471; + //float(float c) acos = #472; + //float(float t) atan = #473; + //float(float c, float s) atan2 = #474; + //float(float a) tan = #475; + float(string s) strippedstringlen = #476; + float(string s) strlennocol = #476; // This is the correct name for the function, but not removing the decolorizedstring mapping. + string(string s) decolorizedstring = #477; + string(string s) strdecolorize = #477; // This is the correct name for the function, but not removing the decolorizedstring mapping. + string(float uselocaltime, string format, ...) strftime = #478; + string(string s) strtolower = #480; + string(string s) strtoupper = #481; + string(string s) cvar_defstring = #482; + void(vector origin, string sample, float volume, float attenuation) pointsound = #483; + string(string search, string replace, string subject) strreplace = #484; + string(string search, string replace, string subject) strireplace = #485; + //vector(entity e, float s, float n, float a) getsurfacepointattribute = #486; + #ifdef SUPPORT_GECKO + float gecko_create( string name ) = #487; + void gecko_destroy( string name ) = #488; + void gecko_navigate( string name, string URI ) = #489; + float gecko_keyevent( string name, float key, float eventtype ) = #490; + void gecko_mousemove( string name, float x, float y ) = #491; + void gecko_resize( string name, float w, float h ) = #492; + vector gecko_get_texture_extent( string name ) = #493; + #else + + #endif + + /* + ============================================================================== + + EXTENSION DEFINITIONS + + ============================================================================== + */ + + // DP_CSQC_SPAWNPARTICLE + // idea: VorteX + // darkplaces implementation: VorteX + // constant definitions: + // particle base behavior: + float PT_ALPHASTATIC = 1; + float PT_STATIC = 2; + float PT_SPARK = 3; + float PT_BEAM = 4; + float PT_RAIN = 5; + float PT_RAINDECAL = 6; + float PT_SNOW = 7; + float PT_BUBBLE = 8; + float PT_BLOOD = 9; + float PT_SMOKE = 10; + float PT_DECAL = 11; + float PT_ENTITYPARTICLE = 12; + // particle blendtypes: + float PBLEND_ALPHA = 0; + float PBLEND_ADD = 1; + float PBLEND_INVMOD = 2; + // particle orientation: + float PARTICLE_BILLBOARD = 0; + float PARTICLE_SPARK = 1; + float PARTICLE_ORIENTED_DOUBLESIDED = 2; + float PARTICLE_BEAM = 3; + // global definitions: + float particle_type; // one of PT_ + float particle_blendmode; // one of PBLEND_ values + float particle_orientation; // one of PARTICLE_ values + vector particle_color1; + vector particle_color2; + float particle_tex; // number of chunk in particlefont + float particle_size; + float particle_sizeincrease; + float particle_alpha; + float particle_alphafade; + float particle_time; + float particle_gravity; + float particle_bounce; + float particle_airfriction; + float particle_liquidfriction; + float particle_originjitter; + float particle_velocityjitter; + float particle_qualityreduction; // enable culling of this particle when FPS is low + float particle_stretch; + vector particle_staincolor1; + vector particle_staincolor2; + float particle_staintex; + float particle_stainalpha; + float particle_stainsize; + float particle_delayspawn; + float particle_delaycollision; + float particle_angle; + float particle_spin; + // builtin definitions: + float(float max_themes) initparticlespawner = #522; // check fields/globals for integration and enable particle spawner, return 1 is succeded, otherwise returns 0 + void() resetparticle = #523; // reset p_ globals to default theme #0 + void(float theme) particletheme = #524; // restore p_ globals from saved theme + float() particlethemesave = #525; // save p_ globals to new particletheme and return it's index + void(float theme) particlethemeupdate = #525; // save p_ globals to new particletheme and return it's index + void(float theme) particlethemefree = #526; // delete a particle theme + float(vector org, vector vel) spawnparticle = #527; // returns 0 when failed, 1 when spawned + float(vector org, vector vel, float theme) quickparticle = #527; // not reading globals, just theme, returns 0 when failed, 1 when spawned + float(vector org, vector vel, float delay, float collisiondelay) delayedparticle = #528; + float(vector org, vector vel, float delay, float collisiondelay, float theme) quickdelayedparticle = #528; + // description: this builtin provides an easy and flexible way to spawn particles, + // it is not created as replace for DP_SV_POINTPARTICLES but as an addition to it. + // With this extension you can create a specific particles like rain particles, or entity particles + // notes: + // 1) 0 is default particle template, it could be changed + // 2) color vectors could have value 0-255 of each component + // restrictions: max themes could be between 4 and 2048 + // warning: you should call initparticlespawner() at very beginning BEFORE all other particle spawner functions + // function to query particle info + // don't remove this function as it protects all particle_ globals from FTEQCC/FRIKQCC non-referenced removal optimisation + void() printparticle = + { + // vortex: this also protects from 'non-referenced' optimisation on some compilers + print("PARTICLE:\n"); + print(strcat(" type: ", ftos(particle_type), "\n")); + print(strcat(" blendmode: ", ftos(particle_blendmode), "\n")); + print(strcat(" orientation: ", ftos(particle_orientation), "\n")); + print(strcat(" color1: ", vtos(particle_color1), "\n")); + print(strcat(" color2: ", vtos(particle_color2), "\n")); + print(strcat(" tex: ", ftos(particle_tex), "\n")); + print(strcat(" size: ", ftos(particle_size), "\n")); + print(strcat(" sizeincrease: ", ftos(particle_sizeincrease), "\n")); + print(strcat(" alpha: ", ftos(particle_alpha), "\n")); + print(strcat(" alphafade: ", ftos(particle_alphafade), "\n")); + print(strcat(" time: ", ftos(particle_time), "\n")); + print(strcat(" gravity: ", ftos(particle_gravity), "\n")); + print(strcat(" bounce: ", ftos(particle_bounce), "\n")); + print(strcat(" airfriction: ", ftos(particle_airfriction), "\n")); + print(strcat(" liquidfriction: ", ftos(particle_liquidfriction), "\n")); + print(strcat(" originjitter: ", ftos(particle_originjitter), "\n")); + print(strcat(" velocityjitter: ", ftos(particle_velocityjitter), "\n")); + print(strcat(" qualityreduction: ", ftos(particle_qualityreduction), "\n")); + print(strcat(" stretch: ", ftos(particle_stretch), "\n")); + print(strcat(" staincolor1: ", vtos(particle_staincolor1), "\n")); + print(strcat(" staincolor2: ", vtos(particle_staincolor2), "\n")); + print(strcat(" staintex: ", ftos(particle_staintex), "\n")); + print(strcat(" stainalpha: ", ftos(particle_stainalpha), "\n")); + print(strcat(" stainsize: ", ftos(particle_stainsize), "\n")); + print(strcat(" delayspawn: ", ftos(particle_delayspawn), "\n")); + print(strcat(" delaycollision: ", ftos(particle_delaycollision), "\n")); + print(strcat(" angle: ", ftos(particle_angle), "\n")); + print(strcat(" spin: ", ftos(particle_spin), "\n")); + } + + // DP_CSQC_ENTITYTRANSPARENTSORTING_OFFSET + // idea: VorteX + // darkplaces implementation: VorteX + float RF_USETRANSPARENTOFFSET = 64; // enables transparent origin offsetting + // global definitions + float transparent_offset; // should be set before entity is added + // description: offset a model's meshes origin used for transparent sorting. Could be used to tweak sorting bugs on very large transparent entities or hacking transparent sorting order for certain objects + // example: transparent_offset = 1000000; // entity always appear on background of other transparents + // note: offset is done in view forward axis + + // DP_CSQC_ENTITYWORLDOBJECT + // idea: VorteX + // darkplaces implementation: VorteX + const float RF_WORLDOBJECT = 128; + // description: when renderflag is set, engine will not use culling methods for this entity, e.g. it will always be drawn + // useful for large outdoor objects (like asteroids on sky horizon or sky models) + + // DP_CSQC_ENTITYMODELLIGHT + // idea: VorteX + // darkplaces implementation: VorteX + const float RF_MODELLIGHT = 4096; + .vector modellight_ambient; + .vector modellight_diffuse; + .vector modellight_dir; + // description: allows CSQC to override directional model lightning on entity + + // DP_CSQC_SETPAUSE + // idea: VorteX + // darkplaces implementation: VorteX + // builtin definitions: + void(float ispaused) setpause = #531; + // description: provides ability to set pause in local games (similar to one set once console is activated) + // not stopping sound/cd track, useful for inventory screens, ingame menus with input etc. + + // DP_CSQC_QUERYRENDERENTITY + // idea: VorteX + // darkplaces implementation: VorteX + // constant definitions: + // render entity fields: + float E_ACTIVE = 0; // float 0/1 + float E_ORIGIN = 1; // vector + float E_FORWARD = 2; // vector + float E_RIGHT = 3; // vector + float E_UP = 4; // vector + float E_SCALE = 5; // float + float E_ORIGINANDVECTORS = 6; // returns origin, + sets v_* vectors to orientation + float E_ALPHA = 7; // float + float E_COLORMOD = 8; // vector + float E_PANTSCOLOR = 9; // vector + float E_SHIRTCOLOR = 10; // vector + float E_SKIN = 11; // float + float E_MINS = 12; // vector + float E_MAXS = 13; // vector + float E_ABSMIN = 14; // vector + float E_ABSMAX = 15; // vector + float E_LIGHT = 16; // vector - modellight + // builtin definitions: + float(float entitynum, float fldnum) getentity = #504; + vector(float entitynum, float fldnum) getentityvec = #504; + // description: allows to query parms from render entities, especially useful with attaching CSQC ents to + // server entities networked and interpolated by engine (monsters, players), number of entity is it's SVQC number + // you can send it via tempentity/CSQC entity message. Note that this builtin doesnt know about entity removing/reallocating + // so it's meaning to work for short period of time, dont use it on missiles/grenades whatever will be removed next five seconds + + //DP_GFX_FONTS + //idea: Blub\0, divVerent + //darkplaces implementation: Blub\0 + //console commands: + // loadfont fontname fontmaps size1 size2 ... + // A font can simply be gfx/tgafile (freetype fonts doent need extension), + // or alternatively you can specify multiple fonts and faces + // Like this: gfx/vera-sans:2,gfx/fallback:1 + // to load face 2 of the font gfx/vera-sans and use face 1 + // of gfx/fallback as fallback font + // You can also specify a list of font sizes to load, like this: + // loadfont console gfx/conchars,gfx/fallback 8 12 16 24 32 + // In many cases, 8 12 16 24 32 should be a good choice. + // for slots see: + //constant definitions: + float drawfont; // set it before drawstring()/drawchar() calls + float FONT_DEFAULT = 0; // 'default' + float FONT_CONSOLE = 1; // 'console', REALLY should be fixed width (ls!) + float FONT_SBAR = 2; // 'sbar', used on hud, must be fixed width + float FONT_NOTIFY = 3; // 'notify', used on sprint/bprint + float FONT_CHAT = 4; // 'chat' + float FONT_CENTERPRINT = 5;// 'centerprint' + float FONT_INFOBAR = 6; // 'infobar' + float FONT_MENU = 7; // 'menu', should be fixed width + float FONT_USER0 = 8; // 'user0', userdefined fonts + float FONT_USER1 = 9; // 'user1', userdefined fonts + float FONT_USER2 = 10; // 'user2', userdefined fonts + float FONT_USER3 = 11; // 'user3', userdefined fonts + float FONT_USER4 = 12; // 'user4', userdefined fonts + float FONT_USER5 = 13; // 'user5', userdefined fonts + float FONT_USER6 = 14; // 'user6', userdefined fonts + float FONT_USER7 = 15; // 'user7' slot, userdefined fonts + //builtin definitions: + float findfont(string s) = #356; // find font by fontname and return it's index + float loadfont(string fontname, string fontmaps, string sizes, float slot, float fix_scale, float fix_voffset) = #357; + // loads font immediately so stringwidth() function can be used just after builtin call + // returns a font slotnum (which is used to set drawfont to) + // first 3 parms are identical to "loadfont" console command ones + // slot could be one of FONT_ constants or result of findfont() or -1 to not use it + // if slot is given, font will be loaded to this slotnum and fontname become new title for it + // this way you can rename user* fonts to something more usable + // fix_* parms let you fix badly made fonts by applying some transformations to them + // fix_scale : per-character center-oriented scale (doesn't change line height at all) + // fix_voffset : vertical offset for each character, it's a multiplier to character height + float stringwidth(string text, float allowColorCodes, vector size) = #327; // get a width of string with given font and char size + float stringwidth_menu(string text, float allowColorCodes, vector size) = #468; // in menu.dat it has different builtin # + //description: engine support for custom fonts in console, hud, qc etc. + // limits: + // max 128 chars for font name + // max 3 font fallbacks + // max 8 sizes per font + + //DP_GFX_FONTS_FREETYPE + //idea: Blub\0, divVerent + //darkplaces implementation: Blub\0 + //cvar definitions: + // r_font_disable_freetype 0/1 : disable freetype fonts loading (uttetly disables freetype library initialization) + // r_font_antialias 0/1 : antialiasing when loading font + // r_font_hint 0/1/2/3 : hinting when loading font, 0 is no hinting, 1 light autohinting , 2 full autohinting, 3 full hinting + // r_font_postprocess_blur X : font outline blur amount + // r_font_postprocess_outline X : font outline width + // r_font_postprocess_shadow_x X : font outline shadow x shift amount, applied during outlining + // r_font_postprocess_shadow_y X : font outline shadow y shift amount, applied during outlining + // r_font_postprocess_shadow_z X : font outline shadow z shift amount, applied during blurring + //description: engine support for truetype/freetype fonts + //so .AFM+.PFB/.OTF/.TTF files could be stuffed as fontmaps in loadfont() + //(console command version will support them as well) + + //DP_CSQC_BINDMAPS + //idea: daemon, motorsep + //darkplaces implementation: divVerent + //builtin definitions: + string(float key, float bindmap) getkeybind_bindmap = #342; + float(float key, string bind, float bindmap) setkeybind_bindmap = #630; + vector(void) getbindmaps = #631; + float(vector bm) setbindmaps = #632; + string(string command, float bindmap) findkeysforcommand = #610; + // float(string key) stringtokeynum = #341; + // string(float keynum) keynumtostring = #340; + //description: key bind setting/getting including support for switchable + //bindmaps. + + //DP_CRYPTO + //idea: divVerent + //darkplaces implementation: divVerent + //builtin definitions: (CSQC) + float(string url, float id, string content_type, string delim, float buf, float keyid) crypto_uri_postbuf = #513; + //description: + //use -1 as buffer handle to justs end delim as postdata + + //DP_CSQC_MAINVIEW + //idea: divVerent + //darkplaces implementation: divVerent + //constant definitions: + const int VF_MAINVIEW = 400; + //use setproperty(VF_MAINVIEW, 1); before calling R_RenderView for the render + //that shall become the "main" view, which is e.g. used by PRYDON_CLIENTCURSOR + //this flag is set for the first scene, and not cleared by R_ClearScene + //this flag is automatically cleared by R_RenderView + //so when not using this extension, the first view rendered is the main view + + //DP_CSQC_MINFPS_QUALITY + //idea: divVerent + //darkplaces implementation: divVerent + //constant definitions: + const int VF_MINFPS_QUALITY = 401; + //use getproperty(VF_MINFPS_QUALITY); to do CSQC based LOD based on cl_minfps + //1 should lead to an unmodified view + + //DP_CSQC_V_CALCREFDEF_WIP1 + //DP_CSQC_V_CALCREFDEF_WIP2 + //idea: divVerent + //darkplaces implementation: divVerent + //builtin definitions: + void(entity e, float refdefflags) V_CalcRefdef = #640; + //constant definitions: + const int PMF_DUCKED = 4; + const int PMF_ONGROUND = 8; + const int REFDEFFLAG_TELEPORTED = 1; + const int REFDEFFLAG_JUMPING = 2; + const int REFDEFFLAG_DEAD = 4; + const int REFDEFFLAG_INTERMISSION = 8; + //- use this on the player entity after performing prediction + //- pass REFDEFFLAG_TELEPORTED if the player teleported since last frame + //- pass REFDEFFLAG_JUMPING if jump button is pressed + //- pass REFDEFFLAG_DEAD if dead (DP_CSQC_V_CALCREFDEF_WIP2) + //- pass REFDEFFLAG_INTERMISSION if in intermission (DP_CSQC_V_CALCREFDEF_WIP2) + //- the player entity needs to have origin, velocity, pmove_flags set according + // to prediction (the above two PMF_ flags are used in the player's pmove_flags) + //- NOTE: to check for this, ALSO OR a check with DP_CSQC_V_CALCREFDEF to also support + // the finished extension once done + + // assorted builtins + float drawsubpic(vector position, vector size, string pic, vector srcPosition, vector srcSize, vector rgb, float alpha, float flag) = #328; + vector drawgetimagesize(string pic) = #318; + const float SPA_POSITION = 0; + const float SPA_S_AXIS = 1; + const float SPA_T_AXIS = 2; + const float SPA_R_AXIS = 3; + const float SPA_TEXCOORDS0 = 4; + const float SPA_LIGHTMAP0_TEXCOORDS = 5; + const float SPA_LIGHTMAP_COLOR = 6; + float (entity e, float s) getsurfacenumpoints = #434; + vector (entity e, float s, float n) getsurfacepoint = #435; + vector (entity e, float s) getsurfacenormal = #436; + string (entity e, float s) getsurfacetexture = #437; + float (entity e, vector p) getsurfacenearpoint = #438; + vector (entity e, float s, vector p) getsurfaceclippedpoint = #439; + vector(entity e, float s, float n, float a) getsurfacepointattribute = #486; + float(entity e, float s) getsurfacenumtriangles = #628; + vector(entity e, float s, float n) getsurfacetriangle = #629; + + //DP_QC_ASINACOSATANATAN2TAN + //idea: Urre + //darkplaces implementation: LordHavoc + //constant definitions: + float DEG2RAD = 0.0174532925199432957692369076848861271344287188854172545609719144; + float RAD2DEG = 57.2957795130823208767981548141051703324054724665643215491602438612; + float PI = 3.1415926535897932384626433832795028841971693993751058209749445923; + //builtin definitions: + float(float s) asin = #471; // returns angle in radians for a given sin() value, the result is in the range -PI*0.5 to PI*0.5 + float(float c) acos = #472; // returns angle in radians for a given cos() value, the result is in the range 0 to PI + float(float t) atan = #473; // returns angle in radians for a given tan() value, the result is in the range -PI*0.5 to PI*0.5 + float(float c, float s) atan2 = #474; // returns angle in radians for a given cos() and sin() value pair, the result is in the range -PI to PI (this is identical to vectoyaw except it returns radians rather than degrees) + float(float a) tan = #475; // returns tangent value (which is simply sin(a)/cos(a)) for the given angle in radians, the result is in the range -infinity to +infinity + //description: + //useful math functions for analyzing vectors, note that these all use angles in radians (just like the cos/sin functions) not degrees unlike makevectors/vectoyaw/vectoangles, so be sure to do the appropriate conversions (multiply by DEG2RAD or RAD2DEG as needed). + //note: atan2 can take unnormalized vectors (just like vectoyaw), and the function was included only for completeness (more often you want vectoyaw or vectoangles), atan2(v_x,v_y) * RAD2DEG gives the same result as vectoyaw(v) + + //DP_QC_SPRINTF + //idea: divVerent + //darkplaces implementation: divVerent + //builtin definitions: + string(string format, ...) sprintf = #627; + //description: + //you know sprintf :P + //supported stuff: + // % + // optional: $ for the argument to format + // flags: #0- + + // optional: , *, or *$ for the field width + // optional: ., .*, or .*$ for the precision + // length modifiers: h for forcing a float, l for forcing an int/entity (by default, %d etc. cast a float to int) + // conversions: + // d takes a float if no length is specified or h is, and an int/entity if l is specified as length, and cast it to an int + // i takes an int/entity if no length is specified or i is, and a float if h is specified as length, and cast it to an int + // ouxXc take a float if no length is specified or h is, and an int/entity if l is specified as length, and cast it to an unsigned int + // eEfFgG take a float if no length is specified or h is, and an int/entity if l is specified as length, and cast it to a double + // s takes a string + // vV takes a vector, and processes the three components as if it were a gG for all three components, separated by space + // For conversions s and c, the flag # makes precision and width interpreted + // as byte count, by default it is interpreted as character count in UTF-8 + // enabled engines. No other conversions can create wide characters, and # + // has another meaning in these. + + //DP_QC_GETTIME + //idea: tZork + //darkplaces implementation: tZork, divVerent + //constant definitions: + float GETTIME_FRAMESTART = 0; // time of start of frame + float GETTIME_REALTIME = 1; // current time (may be OS specific) + float GETTIME_HIRES = 2; // like REALTIME, but may reset between QC invocations and thus can be higher precision + float GETTIME_UPTIME = 3; // time since start of the engine + //builtin definitions: + float(float tmr) gettime = #519; + //description: + //some timers to query... + + //DP_QC_GETTIME_CDTRACK + //idea: divVerent + //darkplaces implementation: divVerent + //constant definitions: + float GETTIME_CDTRACK = 4; + //description: + //returns the playing time of the current cdtrack when passed to gettime() + //see DP_END_GETSOUNDTIME for similar functionality but for entity sound channels + + //DP_QC_TOKENIZEBYSEPARATOR + //idea: Electro, SavageX, LordHavoc + //darkplaces implementation: LordHavoc + //builtin definitions: + int(string s, string separator1, ...) tokenizebyseparator = #479; + //description: + //this function returns tokens separated by any of the supplied separator strings, example: + //numnumbers = tokenizebyseparator("10.2.3.4", "."); + //returns 4 and the tokens are "10" "2" "3" "4" + //possibly useful for parsing IPv4 addresses (such as "1.2.3.4") and IPv6 addresses (such as "[1234:5678:9abc:def0:1234:5678:9abc:def0]:26000") + + //DP_QC_TOKENIZE_CONSOLE + //idea: divVerent + //darkplaces implementation: divVerent + //builtin definitions: + int(string s) tokenize_console = #514; + int(float i) argv_start_index = #515; + int(float i) argv_end_index = #516; + //description: + //this function returns tokens separated just like the console does + //also, functions are provided to get the index of the first and last character of each token in the original string + //Passing negative values to them, or to argv, will be treated as indexes from the LAST token (like lists work in Perl). So argv(-1) will return the LAST token. + + //DP_SND_SOUND7_WIP1 + //DP_SND_SOUND7_WIP2 + //idea: divVerent + //darkplaces implementation: divVerent + //builtin definitions: + void(entity e, float chan, string samp, float vol, float atten, float speed, float flags) sound7 = #8; + float SOUNDFLAG_RELIABLE = 1; + //description: + //plays a sound, with some more flags + //extensions to sound(): + //- channel may be in the range from -128 to 127; channels -128 to 0 are "auto", + // i.e. support multiple sounds at once, but cannot be stopped/restarted + //- a value 0 in the speed parameter means no change; otherwise, it is a + // percentage of playback speed ("pitch shifting"). 100 is normal pitch, 50 is + // half speed, 200 is double speed, etc. (DP_SND_SOUND7_WIP2) + //- the flag SOUNDFLAG_RELIABLE can be specified, which makes the sound send + // to MSG_ALL (reliable) instead of MSG_BROADCAST (unreliable, default); + // similarily, SOUNDFLAG_RELIABLE_TO_ONE sends to MSG_ONE + //- channel 0 is controlled by snd_channel0volume; channel 1 and -1 by + // snd_channel1volume, etc. (so, a channel shares the cvar with its respective + // auto-channel); however, the mod MUST define snd_channel8volume and upwards + // in default.cfg if they are to be used, as the engine does not create them + // to not litter the cvar list + //- this extension applies to CSQC as well; CSQC_Event_Sound will get speed and + // flags as extra 7th and 8th argument + //- WIP2 ideas: SOUNDFLAG_RELIABLE_TO_ONE, SOUNDFLAG_NOPHS, SOUNDFLAG_FORCELOOP + //- NOTE: to check for this, ALSO OR a check with DP_SND_SOUND7 to also support + // the finished extension once done + + //DP_PRECACHE_PIC_FLAGS + //idea: divVerent + //darkplaces implementation: divVerent + //constant definitions: + float PRECACHE_PIC_FROMWAD = 1; // this one actually is part of EXT_CSQC + float PRECACHE_PIC_NOTPERSISTENT = 2; // picture may get deallocated when unused + float PRECACHE_PIC_MIPMAP = 8; // mipmap the texture for possibly better downscaling at memory expense + //notes: these constants are given as optional second argument to precache_pic() + + //DP_QC_TRACE_MOVETYPE_WORLDONLY + //idea: LordHavoc + //darkplaces implementation: LordHavoc + //constant definitions: + float MOVE_WORLDONLY = 3; + //description: + //allows traces to hit only world (ignoring all entities, unlike MOVE_NOMONSTERS which hits all bmodels), use as the nomonsters parameter to trace functions + + //DP_SND_GETSOUNDTIME + //idea: VorteX + //darkplaces implementation: VorteX + //constant definitions: + float(entity e, float channel) getsoundtime = #533; // get currently sound playing position on entity channel, -1 if not playing or error + float(string sample) soundlength = #534; // returns length of sound sample in seconds, -1 on error (sound not precached, sound system not initialized etc.) + //description: provides opportunity to query length of sound samples and realtime tracking of sound playing on entities (similar to DP_GETTIME_CDTRACK) + //note: beware dedicated server not running sound engine at all, so in dedicated mode this builtins will not work in server progs + //note also: menu progs not supporting getsoundtime() (will give a warning) since it has no sound playing on entities + //examples of use: + // - QC-driven looped sounds + // - QC events when sound playing is finished + // - toggleable ambientsounds + // - subtitles + + //DP_QC_NUM_FOR_EDICT + //idea: Blub\0 + //darkplaces implementation: Blub\0 + //Function to get the number of an entity - a clean way. + float(entity num) num_for_edict = #512; + + //DP_TRACE_HITCONTENTSMASK_SURFACEINFO + //idea: LordHavoc + //darkplaces implementation: LordHavoc + //globals: + .int dphitcontentsmask; // if non-zero on the entity passed to traceline/tracebox/tracetoss this will override the normal collidable contents rules and instead hit these contents values (for example AI can use tracelines that hit DONOTENTER if it wants to, by simply changing this field on the entity passed to traceline), this affects normal movement as well as trace calls + float trace_dpstartcontents; // DPCONTENTS_ value at start position of trace + int trace_dphitcontents; // DPCONTENTS_ value of impacted surface (not contents at impact point, just contents of the surface that was hit) + int trace_dphitq3surfaceflags; // Q3SURFACEFLAG_ value of impacted surface + string trace_dphittexturename; // texture name of impacted surface + //constants: + const int DPCONTENTS_SOLID = 1; // hit a bmodel, not a bounding box + const int DPCONTENTS_WATER = 2; + const int DPCONTENTS_SLIME = 4; + const int DPCONTENTS_LAVA = 8; + const int DPCONTENTS_SKY = 16; + const int DPCONTENTS_BODY = 32; // hit a bounding box, not a bmodel + const int DPCONTENTS_CORPSE = 64; // hit a SOLID_CORPSE entity + const int DPCONTENTS_NODROP = 128; // an area where backpacks should not spawn + const int DPCONTENTS_PLAYERCLIP = 256; // blocks player movement + const int DPCONTENTS_MONSTERCLIP = 512; // blocks monster movement + const int DPCONTENTS_DONOTENTER = 1024; // AI hint brush + const int DPCONTENTS_LIQUIDSMASK = 14; // WATER | SLIME | LAVA + const int DPCONTENTS_BOTCLIP = 2048; // AI hint brush + const int DPCONTENTS_OPAQUE = 4096; // only fully opaque brushes get this (may be useful for line of sight checks) + const int Q3SURFACEFLAG_NODAMAGE = 1; + const int Q3SURFACEFLAG_SLICK = 2; // low friction surface + const int Q3SURFACEFLAG_SKY = 4; // sky surface (also has NOIMPACT and NOMARKS set) + const int Q3SURFACEFLAG_LADDER = 8; // climbable surface + const int Q3SURFACEFLAG_NOIMPACT = 16; // projectiles should remove themselves on impact (this is set on sky) + const int Q3SURFACEFLAG_NOMARKS = 32; // projectiles should not leave marks, such as decals (this is set on sky) + const int Q3SURFACEFLAG_FLESH = 64; // projectiles should do a fleshy effect (blood?) on impact + const int Q3SURFACEFLAG_NODRAW = 128; // compiler hint (not important to qc) + //float Q3SURFACEFLAG_HINT = 256; // compiler hint (not important to qc) + //float Q3SURFACEFLAG_SKIP = 512; // compiler hint (not important to qc) + //float Q3SURFACEFLAG_NOLIGHTMAP = 1024; // compiler hint (not important to qc) + //float Q3SURFACEFLAG_POINTLIGHT = 2048; // compiler hint (not important to qc) + const int Q3SURFACEFLAG_METALSTEPS = 4096; // walking on this surface should make metal step sounds + const int Q3SURFACEFLAG_NOSTEPS = 8192; // walking on this surface should not make footstep sounds + const int Q3SURFACEFLAG_NONSOLID = 16384; // compiler hint (not important to qc) + //float Q3SURFACEFLAG_LIGHTFILTER = 32768; // compiler hint (not important to qc) + //float Q3SURFACEFLAG_ALPHASHADOW = 65536; // compiler hint (not important to qc) + //float Q3SURFACEFLAG_NODLIGHT = 131072; // compiler hint (not important to qc) + //float Q3SURFACEFLAG_DUST = 262144; // translucent 'light beam' effect (not important to qc) + //description: + //adds additional information after a traceline/tracebox/tracetoss call. + //also (very important) sets trace_* globals before calling .touch functions, + //this allows them to inspect the nature of the collision (for example + //determining if a projectile hit sky), clears trace_* variables for the other + //object in a touch event (that is to say, a projectile moving will see the + //trace results in its .touch function, but the player it hit will see very + //little information in the trace_ variables as it was not moving at the time) + + //DP_QC_CVAR_TYPE + //idea: divVerent + //DarkPlaces implementation: divVerent + //builtin definitions: + float(string name) cvar_type = #495; + const int CVAR_TYPEFLAG_EXISTS = 1; + const int CVAR_TYPEFLAG_SAVED = 2; + const int CVAR_TYPEFLAG_PRIVATE = 4; + const int CVAR_TYPEFLAG_ENGINE = 8; + const int CVAR_TYPEFLAG_HASDESCRIPTION = 16; + const int CVAR_TYPEFLAG_READONLY = 32; + + //DP_QC_CRC16 + //idea: divVerent + //darkplaces implementation: divVerent + //Some hash function to build hash tables with. This has to be be the CRC-16-CCITT that is also required for the QuakeWorld download protocol. + //When caseinsensitive is set, the CRC is calculated of the lower cased string. + int(bool caseinsensitive, string s, ...) crc16 = #494; + + //DP_QC_URI_ESCAPE + //idea: divVerent + //darkplaces implementation: divVerent + //URI::Escape's functionality + string(string in) uri_escape = #510; + string(string in) uri_unescape = #511; + + //DP_QC_DIGEST + //idea: motorsep, Spike + //DarkPlaces implementation: divVerent + //builtin definitions: + string(string digest, string data, ...) digest_hex = #639; + //description: + //returns a given hex digest of given data + //the returned digest is always encoded in hexadecimal + //only the "MD4" digest is always supported! + //if the given digest is not supported, string_null is returned + //the digest string is matched case sensitively, use "MD4", not "md4"! + + //DP_QC_DIGEST_SHA256 + //idea: motorsep, Spike + //DarkPlaces implementation: divVerent + //description: + //"SHA256" is also an allowed digest type + + //DP_QC_LOG + //darkplaces implementation: divVerent + //builtin definitions: + float log(float f) = #532; + //description: + //logarithm + + //FTE_CSQC_SKELETONOBJECTS + //idea: Spike, LordHavoc + //darkplaces implementation: LordHavoc + //builtin definitions: + // all skeleton numbers are 1-based (0 being no skeleton) + // all bone numbers are 1-based (0 being invalid) + float(float modlindex) skel_create = #263; // create a skeleton (be sure to assign this value into .skeletonindex for use), returns skeleton index (1 or higher) on success, returns 0 on failure (for example if the modelindex is not skeletal), it is recommended that you create a new skeleton if you change modelindex, as the skeleton uses the hierarchy from the model. + float(float skel, entity ent, float modlindex, float retainfrac, float firstbone, float lastbone) skel_build = #264; // blend in a percentage of standard animation, 0 replaces entirely, 1 does nothing, 0.5 blends half, etc, and this only alters the bones in the specified range for which out of bounds values like 0,100000 are safe (uses .frame, .frame2, .frame3, .frame4, .lerpfrac, .lerpfrac3, .lerpfrac4, .frame1time, .frame2time, .frame3time, .frame4time), returns skel on success, 0 on failure + float(float skel) skel_get_numbones = #265; // returns how many bones exist in the created skeleton, 0 if skeleton does not exist + string(float skel, float bonenum) skel_get_bonename = #266; // returns name of bone (as a tempstring), "" if invalid bonenum (< 1 for example) or skeleton does not exist + int(float skel, float bonenum) skel_get_boneparent = #267; // returns parent num for supplied bonenum, 0 if bonenum has no parent or bone does not exist (returned value is always less than bonenum, you can loop on this) + int(float skel, string tagname) skel_find_bone = #268; // get number of bone with specified name, 0 on failure, bonenum (1-based) on success, same as using gettagindex but takes modelindex instead of entity + vector(float skel, float bonenum) skel_get_bonerel = #269; // get matrix of bone in skeleton relative to its parent - sets v_forward, v_right, v_up, returns origin (relative to parent bone) + vector(float skel, float bonenum) skel_get_boneabs = #270; // get matrix of bone in skeleton in model space - sets v_forward, v_right, v_up, returns origin (relative to entity) + void(float skel, float bonenum, vector org) skel_set_bone = #271; // set matrix of bone relative to its parent, reads v_forward, v_right, v_up, takes origin as parameter (relative to parent bone) + void(float skel, float bonenum, vector org) skel_mul_bone = #272; // transform bone matrix (relative to its parent) by the supplied matrix in v_forward, v_right, v_up, takes origin as parameter (relative to parent bone) + void(float skel, float startbone, float endbone, vector org) skel_mul_bones = #273; // transform bone matrices (relative to their parents) by the supplied matrix in v_forward, v_right, v_up, takes origin as parameter (relative to parent bones) + void(float skeldst, float skelsrc, float startbone, float endbone) skel_copybones = #274; // copy bone matrices (relative to their parents) from one skeleton to another, useful for copying a skeleton to a corpse + void(float skel) skel_delete = #275; // deletes skeleton at the beginning of the next frame (you can add the entity, delete the skeleton, renderscene, and it will still work) + float(float modlindex, string framename) frameforname = #276; // finds number of a specified frame in the animation, returns -1 if no match found + float(float modlindex, float framenum) frameduration = #277; // returns the intended play time (in seconds) of the specified framegroup, if it does not exist the result is 0, if it is a single frame it may be a small value around 0.1 or 0. + //fields: + .float skeletonindex; // active skeleton overriding standard animation on model + .int frame; // primary framegroup animation (strength = 1 - lerpfrac - lerpfrac3 - lerpfrac4) + .int frame2; // secondary framegroup animation (strength = lerpfrac) + .int frame3; // tertiary framegroup animation (strength = lerpfrac3) + .int frame4; // quaternary framegroup animation (strength = lerpfrac4) + .float lerpfrac; // strength of framegroup blend + .float lerpfrac3; // strength of framegroup blend + .float lerpfrac4; // strength of framegroup blend + .float frame1time; // start time of framegroup animation + .float frame2time; // start time of framegroup animation + .float frame3time; // start time of framegroup animation + .float frame4time; // start time of framegroup animation + //description: + //this extension provides a way to do complex skeletal animation on an entity. + // + //see also DP_SKELETONOBJECTS (this extension implemented on server as well as client) + // + //notes: + //each model contains its own skeleton, reusing a skeleton with incompatible models will yield garbage (or not render). + //each model contains its own animation data, you can use animations from other model files (for example saving out all character animations as separate model files). + //if an engine supports loading an animation-only file format such as .md5anim in FTEQW, it can be used to animate any model with a compatible skeleton. + //proper use of this extension may require understanding matrix transforms (v_forward, v_right, v_up, origin), and you must keep in mind that v_right is negative for this purpose. + // + //features include: + //multiple animations blended together. + //animating a model with animations from another model with a compatible skeleton. + //restricting animation blends to certain bones of a model - for example independent animation of legs, torso, head. + //custom bone controllers - for example making eyes track a target location. + // + // + // + //example code follows... + // + //this helper function lets you identify (by parentage) what group a bone + //belongs to - for example "torso", "leftarm", would return 1 ("torso") for + //all children of the bone named "torso", unless they are children of + //"leftarm" (which is a child of "torso") which would return 2 instead... + float(float skel, float bonenum, string g1, string g2, string g3, string g4, string g5, string g6) example_skel_findbonegroup = + { + string bonename; + while (bonenum >= 0) + { + bonename = skel_get_bonename(skel, bonenum); + if (bonename == g1) return 1; + if (bonename == g2) return 2; + if (bonename == g3) return 3; + if (bonename == g4) return 4; + if (bonename == g5) return 5; + if (bonename == g6) return 6; + bonenum = skel_get_boneparent(skel, bonenum); + } + return 0; + }; + // create a skeletonindex for our player using current modelindex + void() example_skel_player_setup = + { + self.skeletonindex = skel_create(self.modelindex); + }; + // setup bones of skeleton based on an animation + // note: animmodelindex can be a different model than self.modelindex + void(float animmodelindex, float framegroup, float framegroupstarttime) example_skel_player_update_begin = + { + // start with our standard animation + self.frame = framegroup; + self.frame2 = 0; + self.frame3 = 0; + self.frame4 = 0; + self.frame1time = framegroupstarttime; + self.frame2time = 0; + self.frame3time = 0; + self.frame4time = 0; + self.lerpfrac = 0; + self.lerpfrac3 = 0; + self.lerpfrac4 = 0; + skel_build(self.skeletonindex, self, animmodelindex, 0, 0, 100000); + }; + // apply a different framegroup animation to bones with a specified parent + void(float animmodelindex, float framegroup, float framegroupstarttime, float blendalpha, string groupbonename, string excludegroupname1, string excludegroupname2) example_skel_player_update_applyoverride = + { + self.frame = framegroup; + self.frame2 = 0; + self.frame3 = 0; + self.frame4 = 0; + self.frame1time = framegroupstarttime; + self.frame2time = 0; + self.frame3time = 0; + self.frame4time = 0; + self.lerpfrac = 0; + self.lerpfrac3 = 0; + self.lerpfrac4 = 0; + float bonenum = 0; + float numbones = skel_get_numbones(self.skeletonindex); + while (bonenum < numbones) + { + if (example_skel_findbonegroup(self.skeletonindex, bonenum, groupbonename, excludegroupname1, excludegroupname2, "", "", "") == 1) + skel_build(self.skeletonindex, self, animmodelindex, 1 - blendalpha, bonenum, bonenum + 1); + bonenum = bonenum + 1; + } + }; + // make eyes point at a target location, be sure v_forward, v_right, v_up are set correctly before calling + void(vector eyetarget, string bonename) example_skel_player_update_eyetarget = + { + float bonenum = skel_find_bone(self.skeletonindex, bonename) - 1; + if (bonenum < 0) + return; + vector oldforward = v_forward; + vector oldright = v_right; + vector oldup = v_up; + vector v = eyetarget - self.origin; + vector modeleyetarget; + modeleyetarget.x = v * v_forward; + modeleyetarget.y = 0-v * v_right; + modeleyetarget.z = v * v_up; + // this is an eyeball, make it point at the target location + // first get all the data we can... + vector relorg = skel_get_bonerel(self.skeletonindex, bonenum); + vector relforward = v_forward; + vector relright = v_right; + vector relup = v_up; + vector boneorg = skel_get_boneabs(self.skeletonindex, bonenum); + vector boneforward = v_forward; + vector boneright = v_right; + vector boneup = v_up; + vector parentorg = skel_get_boneabs(self.skeletonindex, skel_get_boneparent(self.skeletonindex, bonenum)); + vector parentforward = v_forward; + vector parentright = v_right; + vector parentup = v_up; + // get the vector from the eyeball to the target + vector u = modeleyetarget - boneorg; + // now transform it inversely by the parent matrix to produce new rel vectors + v.x = u * parentforward; + v.y = u * parentright; + v.z = u * parentup; + vector ang = vectoangles2(v, relup); + ang.x = 0 - ang.x; + makevectors(ang); + // set the relative bone matrix + skel_set_bone(self.skeletonindex, bonenum, relorg); + // restore caller's v_ vectors + v_forward = oldforward; + v_right = oldright; + v_up = oldup; + }; + // delete skeleton when we're done with it + // note: skeleton remains valid until next frame when it is really deleted + void() example_skel_player_delete = + { + skel_delete(self.skeletonindex); + self.skeletonindex = 0; + }; + // + // END OF EXAMPLES FOR FTE_CSQC_SKELETONOBJECTS + // + + //DP_QC_ENTITYDATA + //idea: KrimZon + //darkplaces implementation: KrimZon + //builtin definitions: + float() numentityfields = #496; + string(float fieldnum) entityfieldname = #497; + float(float fieldnum) entityfieldtype = #498; + string(float fieldnum, entity ent) getentityfieldstring = #499; + float(float fieldnum, entity ent, string s) putentityfieldstring = #500; + //constants: + //Returned by entityfieldtype + float FIELD_STRING = 1; + float FIELD_FLOAT = 2; + float FIELD_VECTOR = 3; + float FIELD_ENTITY = 4; + float FIELD_FUNCTION = 6; + //description: + //Versatile functions intended for storing data from specific entities between level changes, but can be customized for some kind of partial savegame. + //WARNING: .entity fields cannot be saved and restored between map loads as they will leave dangling pointers. + //numentityfields returns the number of entity fields. NOT offsets. Vectors comprise 4 fields: v, v_x, v_y and v_z. + //entityfieldname returns the name as a string, eg. "origin" or "classname" or whatever. + //entityfieldtype returns a value that the constants represent, but the field may be of another type in more exotic progs.dat formats or compilers. + //getentityfieldstring returns data as would be written to a savegame, eg... "0.05" (float), "0 0 1" (vector), or "Hello World!" (string). Function names can also be returned. + //putentityfieldstring puts the data returned by getentityfieldstring back into the entity. + + //DP_QC_ENTITYSTRING + void(string s) loadfromdata = #529; + void(string s) loadfromfile = #530; + void(string s) callfunction = #605; + void(float fh, entity e) writetofile = #606; + float(string s) isfunction = #607; + void(entity e, string s) parseentitydata = #608; + + // assorted builtins + //const int STAT_MOVEVARS_TICRATE = 240; + //const int STAT_MOVEVARS_TIMESCALE = 241; + //const int STAT_FRAGLIMIT = 235; + //const int STAT_TIMELIMIT = 236; + //const int STAT_MOVEVARS_GRAVITY = 242; + string(void) ReadPicture = #501; + const int PARTICLES_USEALPHA = 1; + float particles_alphamin, particles_alphamax; + const int PARTICLES_USECOLOR = 2; + vector particles_colormin, particles_colormax; + const int PARTICLES_USEFADE = 4; // fades the COUNT (fade alpha using alphamin/alphamax) + float particles_fade; + const int PARTICLES_DRAWASTRAIL = 128; + void(float effectindex, entity own, vector org_from, vector org_to, vector dir_from, vector dir_to, float countmultiplier, float flags) boxparticles = #502; + float trace_networkentity; + const int RF_FULLBRIGHT = 256; + const int RF_NOSHADOW = 512; + const int RF_DYNAMICMODELLIGHT = 8192; + + float gettaginfo_parent; + string gettaginfo_name; + vector gettaginfo_offset; + vector gettaginfo_forward; + vector gettaginfo_right; + vector gettaginfo_up; + float checkpvs(vector viewpos, entity viewee) = #240; + + #pragma noref 0 + + #endif diff --cc qcsrc/dpdefs/menudefs.qh index 000000000,83cb073a3..098fbddd3 mode 000000,100644..100644 --- a/qcsrc/dpdefs/menudefs.qh +++ b/qcsrc/dpdefs/menudefs.qh @@@ -1,0 -1,574 +1,607 @@@ + #ifndef MENUDEFS_H + #define MENUDEFS_H + + #pragma noref 1 + + ////////////////////////////////////////////////////////// + // sys globals + + entity self; + + ///////////////////////////////////////////////////////// + void end_sys_globals; + ///////////////////////////////////////////////////////// + // sys fields + + ///////////////////////////////////////////////////////// + void end_sys_fields; + ///////////////////////////////////////////////////////// + // sys functions + + void() m_init; + void(float keynr, float ascii) m_keydown; + void(float width, float height) m_draw; + void(float mode) m_toggle; + void() m_shutdown; + // optional: float(float) m_gethostcachecategory; + + ///////////////////////////////////////////////////////// + // sys constants + /////////////////////////// + // key dest constants + + float KEY_UNKNOWN = -1; + float KEY_GAME = 0; + float KEY_MENU = 2; + float KEY_MENU_GRABBED = 3; + + /////////////////////////// + // file constants + + float FILE_READ = 0; + float FILE_APPEND = 1; + float FILE_WRITE = 2; + + /////////////////////////// + // msg constants + + float MSG_BROADCAST = 0; // unreliable to all + float MSG_ONE = 1; // reliable to one (msg_entity) + float MSG_ALL = 2; // reliable to all + float MSG_INIT = 3; // write to the init string + + ///////////////////////////// + // mouse target constants + + float MT_MENU = 1; + float MT_CLIENT = 2; + + ///////////////////////// + // client state constants + + float CS_DEDICATED = 0; + float CS_DISCONNECTED = 1; + float CS_CONNECTED = 2; + + /////////////////////////// + // blend flags + + float DRAWFLAG_NORMAL = 0; + float DRAWFLAG_ADDITIVE = 1; + float DRAWFLAG_MODULATE = 2; + float DRAWFLAG_2XMODULATE = 3; + + /////////////////////////// + // null entity (actually it is the same like the world entity) + + entity null_entity; + + /////////////////////////// + // error constants + + // file handling + float ERR_CANNOTOPEN = -1; // fopen + float ERR_NOTENOUGHFILEHANDLES = -2; // fopen + float ERR_INVALIDMODE = -3; // fopen + float ERR_BADFILENAME = -4; // fopen + + // drawing functions + + float ERR_NULLSTRING = -1; + float ERR_BADDRAWFLAG = -2; + float ERR_BADSCALE = -3; + float ERR_BADSIZE = -3; // same as ERR_BADSCALE + float ERR_NOTCACHED = -4; + + // server list stuff + float SLIST_HOSTCACHEVIEWCOUNT = 0; + float SLIST_HOSTCACHETOTALCOUNT = 1; + float SLIST_MASTERQUERYCOUNT = 2; + float SLIST_MASTERREPLYCOUNT = 3; + float SLIST_SERVERQUERYCOUNT = 4; + float SLIST_SERVERREPLYCOUNT = 5; + float SLIST_SORTFIELD = 6; + float SLIST_SORTDESCENDING = 7; + float SLIST_LEGACY_LINE1 = 1024; + float SLIST_LEGACY_LINE2 = 1025; + float SLIST_TEST_CONTAINS = 0; + float SLIST_TEST_NOTCONTAIN = 1; + float SLIST_TEST_LESSEQUAL = 2; + float SLIST_TEST_LESS = 3; + float SLIST_TEST_EQUAL = 4; + float SLIST_TEST_GREATER = 5; + float SLIST_TEST_GREATEREQUAL = 6; + float SLIST_TEST_NOTEQUAL = 7; + float SLIST_TEST_STARTSWITH = 8; + float SLIST_TEST_NOTSTARTSWITH = 9; + float SLIST_MASK_AND = 0; + float SLIST_MASK_OR = 512; + + // font stuff + float FONT_DEFAULT = 0; + float FONT_CONSOLE = 1; + float FONT_SBAR = 2; + float FONT_NOTIFY = 3; + float FONT_CHAT = 4; + float FONT_CENTERPRINT = 5; + float FONT_INFOBAR = 6; + float FONT_MENU = 7; + float FONT_USER = 8; // add to this the index, like FONT_USER+3 = user3. At least 8 of them are supported. + float drawfont; + + /* not supported at the moment + /////////////////////////// + // os constants + + float OS_WINDOWS = 0; + float OS_LINUX = 1; + float OS_MAC = 2; + */ + + + + + + + + + + + ////////////////////////////////////////////////// + // common cmd + ////////////////////////////////////////////////// + // AK FIXME: Create perhaps a special builtin file for the common cmds + + void checkextension(string ext) = #1; + + // error cmds + void error(string err,...) = #2; + void objerror(string err,...) = #3; + + // print + + void print(string text,...) = #4; + void bprint(string text,...) = #5; + void sprint(float clientnum, string text,...) = #6; + void centerprint(string text,...) = #7; + + // vector stuff + + vector normalize(vector v) = #8; + float vlen(vector v) = #9; + float vectoyaw(vector v) = #10; + vector vectoangles(vector v) = #11; + + float random(void) = #12; + + void cmd(string command, ...) = #13; + + // cvar cmds + + float cvar(string name) = #14; + const string str_cvar(string name) = #71; + void cvar_set(string name, string value) = #15; + + void dprint(string text,...) = #16; + + // conversion functions + + string ftos(float f) = #17; + float fabs(float f) = #18; + string vtos(vector v) = #19; + string etos(entity e) = #20; + + float stof(string val,...) = #21; + + entity spawn(void) = #22; + void remove(entity e) = #23; + + entity find(entity start, .string field, string match) = #24; + entity findfloat(entity start, .float field, float match) = #25; + entity findentity(entity start, .entity field, entity match) = #25; + + entity findchainstring(.string field, string match) = #26; + entity findchainfloat(.float field, float match) = #27; + entity findchainentity(.entity field, entity match) = #27; + + string precache_file(string file) = #28; + string precache_sound(string sample) = #29; + + void crash(void) = #72; + void coredump(void) = #30; + void stackdump(void) = #73; + void traceon(void) = #31; + void traceoff(void) = #32; + + void eprint(entity e) = #33; + float rint(float f) = #34; + float floor(float f) = #35; + float ceil(float f) = #36; + entity nextent(entity e) = #37; + float sin(float f) = #38; + float cos(float f) = #39; + float sqrt(float f) = #40; + vector randomvec(void) = #41; + + float registercvar(string name, string value, float flags) = #42; // returns 1 if success + + float min(float f,...) = #43; + float max(float f,...) = #44; + + float bound(float min,float value, float max) = #45; + float pow(float a, float b) = #46; + + void copyentity(entity src, entity dst) = #47; + + float fopen(string filename, float mode) = #48; + void fclose(float fhandle) = #49; + string fgets(float fhandle) = #50; + void fputs(float fhandle, string s) = #51; + + float strlen(string s) = #52; + string strcat(string s1,string s2,...) = #53; + string substring(string s, float start, float length) = #54; + + vector stov(string s) = #55; + + string strzone(string s) = #56; + void strunzone(string s) = #57; + + float tokenize(string s) = #58; + string argv(float n) = #59; + + float isserver(void) = #60; + float clientcount(void) = #61; + float clientstate(void) = #62; + void clientcommand(float client, string s) = #63; + void changelevel(string map) = #64; + void localsound(string sample) = #65; + vector getmousepos(void) = #66; + float gettime(void) = #67; + void loadfromdata(string data) = #68; + void loadfromfile(string file) = #69; + + float mod(float val, float m) = #70; + + float search_begin(string pattern, float caseinsensitive, float quiet) = #74; + void search_end(float handle) = #75; + float search_getsize(float handle) = #76; + string search_getfilename(float handle, float num) = #77; + + string chr(float ascii) = #78; + + ///////////////////////////////////////////////// + // Write* Functions + ///////////////////////////////////////////////// + void WriteByte(float data, float dest, float desto) = #401; + void WriteChar(float data, float dest, float desto) = #402; + void WriteShort(float data, float dest, float desto) = #403; + void WriteLong(float data, float dest, float desto) = #404; + void WriteAngle(float data, float dest, float desto) = #405; + void WriteCoord(float data, float dest, float desto) = #406; + void WriteString(string data, float dest, float desto)= #407; + void WriteEntity(entity data, float dest, float desto) = #408; + + ////////////////////////////////////////////////// + // Draw funtions + ////////////////////////////////////////////////// + + float iscachedpic(string name) = #451; + string precache_pic(string name, ...) = #452; + void freepic(string name) = #453; + + float drawcharacter(vector position, float character, vector scale, vector rgb, float alpha, float flag) = #454; + + float drawstring(vector position, string text, vector scale, vector rgb, float alpha, float flag) = #455; + + float drawcolorcodedstring(vector position, string text, vector scale, float alpha, float flag) = #467; + + vector drawcolorcodedstring2(vector position, string text, vector scale, vector rgb, float alpha, float flag) = #467; + + float drawpic(vector position, string pic, vector size, vector rgb, float alpha, float flag) = #456; + + float drawfill(vector position, vector size, vector rgb, float alpha, float flag) = #457; + + void drawsetcliparea(float x, float y, float width, float height) = #458; + + void drawresetcliparea(void) = #459; + + vector drawgetimagesize(string pic) = #460; + + //////////////////////////////////////////////// + // Menu functions + //////////////////////////////////////////////// + + void setkeydest(float dest) = #601; + float getkeydest(void) = #602; + + void setmousetarget(float trg) = #603; + float getmousetarget(void) = #604; + + float isfunction(string function_name) = #607; + void callfunction(...) = #605; + void writetofile(float fhandle, entity ent) = #606; + vector getresolution(float number) = #608; + string keynumtostring(float keynum) = #609; + + float gethostcachevalue(float type) = #611; + string gethostcachestring(float type, float hostnr) = #612; + + //DP_CSQC_BINDMAPS + //idea: daemon, motorsep + //darkplaces implementation: divVerent + //builtin definitions: + string(float key, float bindmap) getkeybind_bindmap = #342; + float(float key, string bind, float bindmap) setkeybind_bindmap = #630; + vector(void) getbindmaps = #631; + float(vector bm) setbindmaps = #632; + string(string command, float bindmap) findkeysforcommand = #610; + float(string key) stringtokeynum = #341; + // string(float keynum) keynumtostring = #340; + //description: key bind setting/getting including support for switchable + //bindmaps. + + //DP_CRYPTO + //idea: divVerent + //darkplaces implementation: divVerent + //field definitions: (MENUQC) + string(string serveraddress) crypto_getkeyfp = #633; // retrieves the cached host key's CA fingerprint of a server given by IP address + string(string serveraddress) crypto_getidfp = #634; // retrieves the cached host key fingerprint of a server given by IP address + string(string serveraddress) crypto_getencryptlevel = #635; // 0 if never encrypting, 1 supported, 2 requested, 3 required, appended by list of allowed methods in order of preference ("AES128"), preceded by a space each + string(float i) crypto_getmykeyfp = #636; // retrieves the CA key fingerprint of a given CA slot, or "" if slot is unused but more to come, or string_null if end of list + string(float i) crypto_getmyidfp = #637; // retrieves the ID fingerprint of a given CA slot, or "" if slot is unused but more to come, or string_null if end of list + float CRYPTO_IDSTATUS_OUTOFRANGE = -1; + float CRYPTO_IDSTATUS_EMPTY = 0; + float CRYPTO_IDSTATUS_UNSIGNED = 1; + float CRYPTO_IDSTATUS_SIGNED = 2; + float(float i) crypto_getmyidstatus = #641; // retrieves the ID's status of a given CA slot, or 0 if slot is unused but more to come, or -1 if end of list + float(string url, float id, string content_type, string delim, float buf, float keyid) crypto_uri_postbuf = #513; + //description: + //use -1 as buffer handle to justs end delim as postdata + + //DP_GECKO_SUPPORT + //idea: Res2k, BlackHC + //darkplaces implementation: Res2k, BlackHC + //constant definitions: + float GECKO_BUTTON_DOWN = 0; + float GECKO_BUTTON_UP = 1; + // either use down and up or just press but not all of them! + float GECKO_BUTTON_PRESS = 2; + // use this for mouse events if needed? + float GECKO_BUTTON_DOUBLECLICK = 3; + //builtin definitions: + float gecko_create( string name ) = #487; + void gecko_destroy( string name ) = #488; + void gecko_navigate( string name, string URI ) = #489; + float gecko_keyevent( string name, float key, float eventtype ) = #490; + void gecko_mousemove( string name, float x, float y ) = #491; + void gecko_resize( string name, float w, float h ) = #492; + vector gecko_get_texture_extent( string name ) = #493; + //engine-called QC prototypes: + //string(string name, string query) Qecko_Query; + //description: + //provides an interface to the offscreengecko library and allows for internet browsing in games + + //FTE_STRINGS + //idea: many + //darkplaces implementation: KrimZon + //description: + //various string manipulation functions + int(string str, string sub, float startpos) strstrofs = #221; + int(string str, float ofs) str2chr = #222; + string(int c, ...) chr2str = #223; + string(float ccase, float calpha, float cnum, string s, ...) strconv = #224; + string(float chars, string s, ...) strpad = #225; + string(string info, string key, string value, ...) infoadd = #226; + string(string info, string key) infoget = #227; + int(string s1, string s2) strcmp = #228; + int(string s1, string s2, float len) strncmp = #228; + int(string s1, string s2) strcasecmp = #229; + int(string s1, string s2, float len) strncasecmp = #230; + + //DP_PRECACHE_PIC_FLAGS + //idea: divVerent + //darkplaces implementation: divVerent + //constant definitions: + float PRECACHE_PIC_FROMWAD = 1; // this one actually is part of EXT_CSQC + float PRECACHE_PIC_NOTPERSISTENT = 2; // picture may get deallocated when unused + float PRECACHE_PIC_MIPMAP = 8; // mipmap the texture for possibly better downscaling at memory expense + //notes: these constants are given as optional second argument to precache_pic() + + //DP_QC_CRC16 + //idea: div0 + //darkplaces implementation: div0 + //Some hash function to build hash tables with. This has to be be the CRC-16-CCITT that is also required for the QuakeWorld download protocol. + //When caseinsensitive is set, the CRC is calculated of the lower cased string. + float(float caseinsensitive, string s, ...) crc16 = #494; + + //DP_QC_CVAR_TYPE + float(string name) cvar_type = #495; + float CVAR_TYPEFLAG_EXISTS = 1; + float CVAR_TYPEFLAG_SAVED = 2; + float CVAR_TYPEFLAG_PRIVATE = 4; + float CVAR_TYPEFLAG_ENGINE = 8; + float CVAR_TYPEFLAG_HASDESCRIPTION = 16; + float CVAR_TYPEFLAG_READONLY = 32; + + //DP_QC_STRINGBUFFERS + //idea: ?? + //darkplaces implementation: LordHavoc + //functions to manage string buffer objects - that is, arbitrary length string arrays that are handled by the engine + int() buf_create = #440; + void(float bufhandle) buf_del = #441; + float(float bufhandle) buf_getsize = #442; + void(float bufhandle_from, float bufhandle_to) buf_copy = #443; + void(float bufhandle, float sortpower, float backward) buf_sort = #444; + string(float bufhandle, string glue) buf_implode = #445; + string(float bufhandle, float string_index) bufstr_get = #446; + void(float bufhandle, float string_index, string str) bufstr_set = #447; + float(float bufhandle, string str, float order) bufstr_add = #448; + void(float bufhandle, float string_index) bufstr_free = #449; + void(float bufhandle, string pattern, string antipattern) buf_cvarlist = #517; + ++//DP_QC_ASINACOSATANATAN2TAN ++//idea: Urre ++//darkplaces implementation: LordHavoc ++//constant definitions: ++float DEG2RAD = 0.0174532925199432957692369076848861271344287188854172545609719144; ++float RAD2DEG = 57.2957795130823208767981548141051703324054724665643215491602438612; ++float PI = 3.1415926535897932384626433832795028841971693993751058209749445923; ++//builtin definitions: ++float(float s) asin = #471; // returns angle in radians for a given sin() value, the result is in the range -PI*0.5 to PI*0.5 ++float(float c) acos = #472; // returns angle in radians for a given cos() value, the result is in the range 0 to PI ++float(float t) atan = #473; // returns angle in radians for a given tan() value, the result is in the range -PI*0.5 to PI*0.5 ++float(float c, float s) atan2 = #474; // returns angle in radians for a given cos() and sin() value pair, the result is in the range -PI to PI (this is identical to vectoyaw except it returns radians rather than degrees) ++float(float a) tan = #475; // returns tangent value (which is simply sin(a)/cos(a)) for the given angle in radians, the result is in the range -infinity to +infinity ++//description: ++//useful math functions for analyzing vectors, note that these all use angles in radians (just like the cos/sin functions) not degrees unlike makevectors/vectoyaw/vectoangles, so be sure to do the appropriate conversions (multiply by DEG2RAD or RAD2DEG as needed). ++//note: atan2 can take unnormalized vectors (just like vectoyaw), and the function was included only for completeness (more often you want vectoyaw or vectoangles), atan2(v_x,v_y) * RAD2DEG gives the same result as vectoyaw(v) ++ ++//DP_QC_NUM_FOR_EDICT ++//idea: Blub\0 ++//darkplaces implementation: Blub\0 ++//Function to get the number of an entity - a clean way. ++float(entity num) num_for_edict = #512; ++ ++//DP_QC_EDICT_NUM ++//idea: 515 ++//DarkPlaces implementation: LordHavoc ++//builtin definitions: ++entity(float entnum) edict_num = #459; ++float(entity ent) wasfreed = #353; // same as in EXT_CSQC extension ++//description: ++//edict_num returns the entity corresponding to a given number, this works even for freed entities, but you should call wasfreed(ent) to see if is currently active. ++//wasfreed returns whether an entity slot is currently free (entities that have never spawned are free, entities that have had remove called on them are also free). ++ + //DP_QC_STRING_CASE_FUNCTIONS + //idea: Dresk + //darkplaces implementation: LordHavoc / Dresk + //builtin definitions: + string(string s) strtolower = #480; // returns the passed in string in pure lowercase form + string(string s) strtoupper = #481; // returns the passed in string in pure uppercase form + //description: + //provides simple string uppercase and lowercase functions + + //DP_QC_CVAR_DESCRIPTION + //idea: divVerent + //DarkPlaces implementation: divVerent + //builtin definitions: + string(string name) cvar_description = #518; + //description: + //returns the description of a cvar + + //DP_QC_DIGEST + //idea: motorsep, Spike + //DarkPlaces implementation: divVerent + //builtin definitions: + string(string digest, string data, ...) digest_hex = #639; + //description: + //returns a given hex digest of given data + //the returned digest is always encoded in hexadecimal + //only the "MD4" digest is always supported! + //if the given digest is not supported, string_null is returned + //the digest string is matched case sensitively, use "MD4", not "md4"! + + //DP_QC_URI_ESCAPE + //idea: div0 + //darkplaces implementation: div0 + //URI::Escape's functionality + string(string in) uri_escape = #510; + string(string in) uri_unescape = #511; + + //DP_QC_URI_GET + //idea: divVerent + //darkplaces implementation: divVerent + //loads text from an URL into a string + //returns 1 on success of initiation, 0 if there are too many concurrent + //connections already or if the URL is invalid + //the following callback will receive the data and MUST exist! + // void(float id, float status, string data) URI_Get_Callback; + //status is either + // negative for an internal error, + // 0 for success, or + // the HTTP response code on server error (e.g. 404) + //if 1 is returned by uri_get, the callback will be called in the future + float(string url, float id) uri_get = #513; + //DP_QC_URI_POST + //idea: divVerent + //darkplaces implementation: divVerent + //loads text from an URL into a string after POSTing via HTTP + //works like uri_get, but uri_post sends data with Content-Type: content_type to the server + //and uri_post sends the string buffer buf, joined using the delimiter delim + float(string url, float id, string content_type, string data) uri_post = #513; + float(string url, float id, string content_type, string delim, float buf) uri_postbuf = #513; + + //DP_QC_ENTITYDATA + //idea: KrimZon + //darkplaces implementation: KrimZon + //builtin definitions: + float() numentityfields = #496; + string(float fieldnum) entityfieldname = #497; + float(float fieldnum) entityfieldtype = #498; + string(float fieldnum, entity ent) getentityfieldstring = #499; + float(float fieldnum, entity ent, string s) putentityfieldstring = #500; + //constants: + //Returned by entityfieldtype + float FIELD_STRING = 1; + float FIELD_FLOAT = 2; + float FIELD_VECTOR = 3; + float FIELD_ENTITY = 4; + float FIELD_FUNCTION = 6; + //description: + //Versatile functions intended for storing data from specific entities between level changes, but can be customized for some kind of partial savegame. + //WARNING: .entity fields cannot be saved and restored between map loads as they will leave dangling pointers. + //numentityfields returns the number of entity fields. NOT offsets. Vectors comprise 4 fields: v, v_x, v_y and v_z. + //entityfieldname returns the name as a string, eg. "origin" or "classname" or whatever. + //entityfieldtype returns a value that the constants represent, but the field may be of another type in more exotic progs.dat formats or compilers. + //getentityfieldstring returns data as would be written to a savegame, eg... "0.05" (float), "0 0 1" (vector), or "Hello World!" (string). Function names can also be returned. + //putentityfieldstring puts the data returned by getentityfieldstring back into the entity. + + // assorted undocumented extensions + string(string, float) netaddress_resolve = #625; + string(string search, string replace, string subject) strreplace = #484; + string(float uselocaltime, string format, ...) strftime = #478; + float(string s) tokenize_console = #514; + float(float i) argv_start_index = #515; + float(float i) argv_end_index = #516; + string(float, float) getgamedirinfo = #626; + const float GETGAMEDIRINFO_NAME = 0; + const float GETGAMEDIRINFO_DESCRIPTION = 1; + float log(float f) = #532; + string(string format, ...) sprintf = #627; + string(string s) strdecolorize = #477; + entity findflags(entity start, .float field, float match) = #87; + entity findchainflags(.float field, float match) = #88; + float(string s, string separator1, ...) tokenizebyseparator = #479; + float etof(entity ent) = #79; + entity ftoe(float num) = #80; + float validstring(string str) = #81; + float altstr_count(string str) = #82; + string altstr_prepare(string str) = #83; + string altstr_get(string str, float num) = #84; + string altstr_set(string str, float num, string set) = #85; + string altstr_ins(string str, float num, string set) = #86; + float isdemo() = #349; + float drawsubpic(vector position, vector size, string pic, vector srcPosition, vector srcSize, vector rgb, float alpha, float flag) = #469; + //vector getresolution(float number, ...) = #608; // optional argument "isfullscreen" + void parseentitydata(entity ent, string data) = #613; + void resethostcachemasks(void) = #615; + void sethostcachemaskstring(float mask, float fld, string str, float op) = #616; + void sethostcachemasknumber(float mask, float fld, float num, float op) = #617; + void resorthostcache(void) = #618; + float SLSF_DESCENDING = 1; + float SLSF_FAVORITES = 2; + float SLSF_CATEGORIES = 4; + void sethostcachesort(float fld, float slsf) = #619; + void refreshhostcache(...) = #620; // optional boolean argument "clear_list" + float gethostcachenumber(float fld, float hostnr) = #621; + float gethostcacheindexforkey(string key) = #622; + void addwantedhostcachekey(string key) = #623; + string getextresponse(void) = #624; + const string cvar_string(string name) = #71; + const string cvar_defstring(string name) = #89; + float stringwidth(string text, float handleColors, vector size) = #468; + + #pragma noref 0 + + #endif diff --cc qcsrc/menu/progs.src index 0e0378790,a1fb38b1a..8efffdc06 --- a/qcsrc/menu/progs.src +++ b/qcsrc/menu/progs.src @@@ -55,8 -19,17 +19,18 @@@ xonotic/util.q ../common/campaign_file.qc ../common/campaign_setup.qc ../common/mapinfo.qc - ../common/weapons/weapons.qc // TODO + ../common/playerstats.qc + ../common/test.qc ../common/urllib.qc + ../common/util.qc + + ../common/command/generic.qc + ../common/command/markup.qc + ../common/command/rpn.qc ++../common/command/script.qc + ../common/monsters/monsters.qc + ../common/weapons/weapons.qc // TODO + ../warpzonelib/mathlib.qc diff --cc qcsrc/server/antilag.qc index c53452ab7,6f8f0f487..21b1fcbc6 --- a/qcsrc/server/antilag.qc +++ b/qcsrc/server/antilag.qc @@@ -1,7 -1,16 +1,16 @@@ - #define ANTILAG_MAX_ORIGINS 64 + #if defined(CSQC) + #elif defined(MENUQC) + #elif defined(SVQC) + #include "../dpdefs/progsdefs.qh" + #include "../dpdefs/dpextensions.qh" - #include "vehicles/vehicles_def.qh" ++ #include "../common/vehicles/sv_vehicles.qh" + #include "antilag.qh" + #endif + + const int ANTILAG_MAX_ORIGINS = 64; .vector antilag_origins[ANTILAG_MAX_ORIGINS]; .float antilag_times[ANTILAG_MAX_ORIGINS]; - .float antilag_index; + .int antilag_index; .vector antilag_saved_origin; .float antilag_takenback; diff --cc qcsrc/server/autocvars.qh index 808d5a929,bcb9d28d7..b3a26e3ca --- a/qcsrc/server/autocvars.qh +++ b/qcsrc/server/autocvars.qh @@@ -67,19 -70,18 +70,19 @@@ int autocvar_captureleadlimit_override float autocvar_ekg; #define autocvar_fraglimit cvar("fraglimit") #define autocvar_fraglimit_override cvar("fraglimit_override") - float autocvar_g_allow_oldvortexbeam; - float autocvar_g_antilag; + bool autocvar_g_allow_oldvortexbeam; + int autocvar_g_antilag; float autocvar_g_antilag_nudge; +float autocvar_g_balance_armor_block_bycount; float autocvar_g_balance_armor_blockpercent; - float autocvar_g_balance_armor_limit; + int autocvar_g_balance_armor_limit; float autocvar_g_balance_armor_regen; - float autocvar_g_balance_armor_regenlinear; - float autocvar_g_balance_armor_regenstable; + float autocvar_g_balance_armor_regenlinear; // TODO: int/bool? + int autocvar_g_balance_armor_regenstable; float autocvar_g_balance_armor_rot; float autocvar_g_balance_armor_rotlinear; - float autocvar_g_balance_armor_rotstable; - float autocvar_g_balance_armor_start; + int autocvar_g_balance_armor_rotstable; + int autocvar_g_balance_armor_start; float autocvar_g_balance_cloaked_alpha; float autocvar_g_balance_contents_damagerate; float autocvar_g_balance_contents_drowndelay; @@@ -97,11 -99,10 +100,11 @@@ int autocvar_g_balance_firetransfer_tim float autocvar_g_balance_fuel_limit; float autocvar_g_balance_fuel_regen; float autocvar_g_balance_fuel_regenlinear; - float autocvar_g_balance_fuel_regenstable; + int autocvar_g_balance_fuel_regenstable; float autocvar_g_balance_fuel_rot; float autocvar_g_balance_fuel_rotlinear; - float autocvar_g_balance_fuel_rotstable; - var float autocvar_g_balance_grapplehook_piggybackfriction = 1; + int autocvar_g_balance_fuel_rotstable; ++float autocvar_g_balance_grapplehook_piggybackfriction = 1; float autocvar_g_balance_grapplehook_airfriction; float autocvar_g_balance_grapplehook_force_rubber; float autocvar_g_balance_grapplehook_force_rubber_overstretch; @@@ -296,18 -308,7 +299,17 @@@ float autocvar_g_domination_warmup #define autocvar_g_domination_point_limit cvar("g_domination_point_limit") float autocvar_g_domination_point_rate; float autocvar_g_domination_teams_override; +float autocvar_g_domination_controlpoint_unlock_speed; +float autocvar_g_domination_controlpoint_unlock_damage_pushback; +float autocvar_g_domination_controlpoint_idletime_neutral_initial; +float autocvar_g_domination_controlpoint_idletime_initial; +float autocvar_g_domination_controlpoint_idletime_neutral; +float autocvar_g_domination_controlpoint_idletime_neutral_power; +float autocvar_g_domination_controlpoint_idletime_neutral_factor; +float autocvar_g_domination_controlpoint_idletime; +float autocvar_g_domination_controlpoint_idletime_power; +float autocvar_g_domination_controlpoint_idletime_factor; float autocvar_g_forced_respawn; - float autocvar_g_respawn_delay_max; string autocvar_g_forced_team_blue; string autocvar_g_forced_team_otherwise; string autocvar_g_forced_team_pink; @@@ -600,8 -585,20 +602,8 @@@ float autocvar_skill_auto float autocvar_snd_soundradius; float autocvar_spawn_debug; float autocvar_speedmeter; - var float autocvar_sv_accuracy_data_share = 1; -float autocvar_sv_accelerate; + float autocvar_sv_accuracy_data_share = 1; string autocvar_sv_adminnick; -float autocvar_sv_airaccel_qw; -float autocvar_sv_airaccel_qw_stretchfactor; -float autocvar_sv_airaccel_sideways_friction; -float autocvar_sv_airaccelerate; -float autocvar_sv_aircontrol; -float autocvar_sv_aircontrol_penalty; -float autocvar_sv_aircontrol_power; -float autocvar_sv_airspeedlimit_nonqw; -float autocvar_sv_airstopaccelerate; -float autocvar_sv_airstrafeaccel_qw; -float autocvar_sv_airstrafeaccelerate; float autocvar_sv_autoscreenshot; float autocvar_sv_cheats; float autocvar_sv_clientcommand_antispam_time; @@@ -1034,19 -882,4 +1036,21 @@@ float autocvar_g_buffs_vampire_damage_s float autocvar_g_buffs_invisible_alpha; float autocvar_g_buffs_flight_gravity; float autocvar_g_buffs_jump_height; +// TODO float autocvar_g_buffs_replace_flags; - float autocvar_g_physics_clientselect; ++bool autocvar_g_physics_clientselect; +string autocvar_g_physics_clientselect_options; +string autocvar_sv_announcer; - float autocvar_sv_minigames; - float autocvar_sv_minigames_observer; ++bool autocvar_sv_minigames; ++bool autocvar_sv_minigames_observer; +float autocvar_g_itemeditor_spawn_distance; - float autocvar_g_itemeditor_storage_autosave; - float autocvar_g_itemeditor_storage_autoload; - float autocvar_g_itemeditor_readonly; - float autocvar_g_itemeditor_max; - float autocvar_g_itemeditor_debug; - var string autocvar_g_itemeditor_storage_name = "default"; ++bool autocvar_g_itemeditor_storage_autosave; ++bool autocvar_g_itemeditor_storage_autoload; ++bool autocvar_g_itemeditor_readonly; ++int autocvar_g_itemeditor_max; ++int autocvar_g_itemeditor_debug; ++string autocvar_g_itemeditor_storage_name = "default"; +string autocvar_sv_weapons_modeloverride; +string autocvar_sv_weapons_sounddir; +string autocvar_sv_items_modeloverride; ++ + #endif diff --cc qcsrc/server/bot/aim.qc index 96165fcca,b529ed1c3..2352e1940 --- a/qcsrc/server/bot/aim.qc +++ b/qcsrc/server/bot/aim.qc @@@ -112,8 -111,14 +111,8 @@@ float bot_shouldattack(entity e } if(e.frozen) - return FALSE; + return false; - // If neither player has ball then don't attack unless the ball is on the - // ground. - if (g_keepaway) - if (!e.ballcarried && !self.ballcarried && ka_ball.owner) - return false; - if(teamplay) { if(e.team==0) @@@ -121,24 -126,22 +120,24 @@@ } else if(bot_ignore_bots) if(IS_BOT_CLIENT(e)) - return FALSE; + return false; if (!e.takedamage) - return FALSE; + return false; if (e.deadflag) - return FALSE; + return false; if (e.BUTTON_CHAT) - return FALSE; + return false; if(e.flags & FL_NOTARGET) - return FALSE; + return false; + if(e.alpha <= 0.5 && e.alpha != 0) - return FALSE; // invisible ++ return false; // invisible - checkentity = e; + other = e; if(MUTATOR_CALLHOOK(BotShouldAttack)) - return FALSE; + return false; - return TRUE; + return true; } void bot_lagfunc(float t, float f1, float f2, entity e1, vector v1, vector v2, vector v3, vector v4) @@@ -273,12 -276,12 +272,12 @@@ float bot_aimdir(vector v, float maxfir blendrate = autocvar_bot_ai_aimskill_blendrate; r = max(fixedrate, blendrate); //self.v_angle = self.v_angle + diffang * bound(frametime, r * frametime * (2+skill*skill*0.05-random()*0.05*(10-skill)), 1); - self.v_angle = self.v_angle + diffang * bound(delta_t, r * delta_t * (2+pow(skill+self.bot_mouseskill,3)*0.005-random()), 1); + self.v_angle = self.v_angle + diffang * bound(delta_t, r * delta_t * (2+pow(bot_skill+self.bot_mouseskill,3)*0.005-random()), 1); self.v_angle = self.v_angle * bound(0,autocvar_bot_ai_aimskill_mouse,1) + desiredang * bound(0,(1-autocvar_bot_ai_aimskill_mouse),1); - //self.v_angle = self.v_angle + diffang * bound(0, r * frametime * (skill * 0.5 + 2), 1); + //self.v_angle = self.v_angle + diffang * bound(0, r * frametime * (bot_skill * 0.5 + 2), 1); //self.v_angle = self.v_angle + diffang * (1/ blendrate); self.v_angle_z = 0; - self.v_angle_y = self.v_angle_y - floor(self.v_angle_y / 360) * 360; + self.v_angle_y = self.v_angle.y - floor(self.v_angle.y / 360) * 360; //dprint(" turn:", vtos(self.v_angle)); makevectors(self.v_angle); @@@ -300,9 -303,9 +299,9 @@@ // note the maxfiredeviation is in degrees so this has to convert to radians first //if ((normalize(v) * shotdir) >= cos(maxfiredeviation * (3.14159265358979323846 / 180))) if ((normalize(v) * shotdir) >= cos(maxfiredeviation * (3.14159265358979323846 / 180))) - if (vlen(trace_endpos-shotorg) < 500+500*bound(0, skill+self.bot_aggresskill, 10) || random()*random()>bound(0,(skill+self.bot_aggresskill)*0.05,1)) - self.bot_firetimer = time + bound(0.1, 0.5-(skill+self.bot_aggresskill)*0.05, 0.5); + if (vlen(trace_endpos-shotorg) < 500+500*bound(0, bot_skill+self.bot_aggresskill, 10) || random()*random()>bound(0,(bot_skill+self.bot_aggresskill)*0.05,1)) + self.bot_firetimer = time + bound(0.1, 0.5-(bot_skill+self.bot_aggresskill)*0.05, 0.5); - //traceline(shotorg,shotorg+shotdir*1000,FALSE,world); + //traceline(shotorg,shotorg+shotdir*1000,false,world); //dprint(ftos(maxfiredeviation),"\n"); //dprint(" diff:", vtos(diffang), "\n"); diff --cc qcsrc/server/bot/bot.qc index 7114d004c,0acd32993..f3eb532b8 --- a/qcsrc/server/bot/bot.qc +++ b/qcsrc/server/bot/bot.qc @@@ -391,8 -414,7 +414,8 @@@ void bot_clientconnect( self.bot_preferredcolors = self.clientcolors; self.bot_nextthink = time - random(); self.lag_func = bot_lagfunc; - self.isbot = TRUE; + self.isbot = true; + self.clientfov = 90; self.createdtime = self.bot_nextthink; if(!self.bot_config_loaded) // This is needed so team overrider doesn't break between matches diff --cc qcsrc/server/bot/bot.qh index e677e0cf1,bcae58dc1..845c75fef --- a/qcsrc/server/bot/bot.qh +++ b/qcsrc/server/bot/bot.qh @@@ -2,24 -4,24 +4,24 @@@ * Globals and Fields */ - const float AI_STATUS_ROAMING = 1; // Bot is just crawling the map. No enemies at sight - const float AI_STATUS_ATTACKING = 2; // There are enemies at sight - const float AI_STATUS_RUNNING = 4; // Bot is bunny hopping - const float AI_STATUS_DANGER_AHEAD = 8; // There is lava/slime/trigger_hurt ahead - const float AI_STATUS_OUT_JUMPPAD = 16; // Trying to get out of a "vertical" jump pad - const float AI_STATUS_OUT_WATER = 32; // Trying to get out of water - const float AI_STATUS_WAYPOINT_PERSONAL_LINKING = 64; // Waiting for the personal waypoint to be linked - const float AI_STATUS_WAYPOINT_PERSONAL_GOING = 128; // Going to a personal waypoint - const float AI_STATUS_WAYPOINT_PERSONAL_REACHED = 256; // Personal waypoint reached - const float AI_STATUS_JETPACK_FLYING = 512; - const float AI_STATUS_JETPACK_LANDING = 1024; - const float AI_STATUS_STUCK = 2048; // Cannot reach any goal + const int AI_STATUS_ROAMING = 1; // Bot is just crawling the map. No enemies at sight + const int AI_STATUS_ATTACKING = 2; // There are enemies at sight + const int AI_STATUS_RUNNING = 4; // Bot is bunny hopping + const int AI_STATUS_DANGER_AHEAD = 8; // There is lava/slime/trigger_hurt ahead + const int AI_STATUS_OUT_JUMPPAD = 16; // Trying to get out of a "vertical" jump pad + const int AI_STATUS_OUT_WATER = 32; // Trying to get out of water + const int AI_STATUS_WAYPOINT_PERSONAL_LINKING = 64; // Waiting for the personal waypoint to be linked + const int AI_STATUS_WAYPOINT_PERSONAL_GOING = 128; // Going to a personal waypoint + const int AI_STATUS_WAYPOINT_PERSONAL_REACHED = 256; // Personal waypoint reached + const int AI_STATUS_JETPACK_FLYING = 512; + const int AI_STATUS_JETPACK_LANDING = 1024; + const int AI_STATUS_STUCK = 2048; // Cannot reach any goal .float isbot; // true if this client is actually a bot - .float aistatus; + .int aistatus; // Skill system -float skill; +float bot_skill; float autoskill_nextthink; // havocbot_keyboardskill // keyboard movement @@@ -111,4 -113,9 +113,5 @@@ void bot_serverframe() void() havocbot_setupbot; -//float c1, c2, c3, c4; -void CheckAllowedTeams(entity for_whom); void GetTeamCounts(entity other); -float JoinBestTeam(entity pl, float only_return_best, float forcebestteam); - void bot_calculate_stepheightvec(void); + #endif diff --cc qcsrc/server/bot/havocbot/havocbot.qc index 9a0a70405,0ecafaf1e..421f46713 --- a/qcsrc/server/bot/havocbot/havocbot.qc +++ b/qcsrc/server/bot/havocbot/havocbot.qc @@@ -797,11 -799,11 +797,11 @@@ void havocbot_movetogoal( } dodge = havocbot_dodge(); - dodge = dodge * bound(0,0.5+(skill+self.bot_dodgeskill)*0.1,1); - evadelava = evadelava * bound(1,3-(skill+self.bot_dodgeskill),3); //Noobs fear lava a lot and take more distance from it + dodge = dodge * bound(0,0.5+(bot_skill+self.bot_dodgeskill)*0.1,1); + evadelava = evadelava * bound(1,3-(bot_skill+self.bot_dodgeskill),3); //Noobs fear lava a lot and take more distance from it - traceline(self.origin, ( ( self.enemy.absmin + self.enemy.absmax ) * 0.5 ), TRUE, world); + traceline(self.origin, ( ( self.enemy.absmin + self.enemy.absmax ) * 0.5 ), true, world); if(IS_PLAYER(trace_ent)) - dir = dir * bound(0,(skill+self.bot_dodgeskill)/7,1); + dir = dir * bound(0,(bot_skill+self.bot_dodgeskill)/7,1); dir = normalize(dir + dodge + evadeobstacle + evadelava); // self.bot_dodgevector = dir; @@@ -840,8 -842,8 +840,8 @@@ havocbot_bunnyhop(dir); if ((dir * v_up) >= autocvar_sv_jumpvelocity*0.5 && (self.flags & FL_ONGROUND)) self.BUTTON_JUMP=1; - if (((dodge * v_up) > 0) && random()*frametime >= 0.2*bound(0,(10-bot_skill-self.bot_dodgeskill)*0.1,1)) self.BUTTON_JUMP=TRUE; - if (((dodge * v_up) > 0) && random()*frametime >= 0.2*bound(0,(10-skill-self.bot_dodgeskill)*0.1,1)) self.BUTTON_JUMP=true; - if (((dodge * v_up) < 0) && random()*frametime >= 0.5*bound(0,(10-skill-self.bot_dodgeskill)*0.1,1)) self.havocbot_ducktime=time+0.3/bound(0.1,skill+self.bot_dodgeskill,10); ++ if (((dodge * v_up) > 0) && random()*frametime >= 0.2*bound(0,(10-bot_skill-self.bot_dodgeskill)*0.1,1)) self.BUTTON_JUMP=true; + if (((dodge * v_up) < 0) && random()*frametime >= 0.5*bound(0,(10-bot_skill-self.bot_dodgeskill)*0.1,1)) self.havocbot_ducktime=time+0.3/bound(0.1,bot_skill+self.bot_dodgeskill,10); } void havocbot_chooseenemy() @@@ -944,8 -946,8 +944,8 @@@ float havocbot_chooseweapon_checkreload // bots under this skill cannot find unloaded weapons to reload idly when not in combat, // so skip this for them, or they'll never get to reload their weapons at all. // this also allows bots under this skill to be more stupid, and reload more often during combat :) - if(skill < 5) + if(bot_skill < 5) - return FALSE; + return false; // if this weapon is scheduled for reloading, don't switch to it during combat if (self.weapon_load[new_weapon] < 0) @@@ -1007,9 -1009,9 +1007,9 @@@ void havocbot_chooseweapon( // Bots with no skill will be 4 times more slower than "godlike" bots when doing weapon combos // Ideally this 4 should be calculated as longest_weapon_refire / bot_ai_weapon_combo_threshold - combo_time = time + ct + (ct * ((-0.3*(skill+self.bot_weaponskill))+3)); + combo_time = time + ct + (ct * ((-0.3*(bot_skill+self.bot_weaponskill))+3)); - combo = FALSE; + combo = false; if(autocvar_bot_ai_weapon_combo) if(self.weapon == self.lastfiredweapon) diff --cc qcsrc/server/cheats.qc index 003e3bc92,657ec4d7f..9bc06ea88 --- a/qcsrc/server/cheats.qc +++ b/qcsrc/server/cheats.qc @@@ -1,3 -1,27 +1,28 @@@ + #include "cheats.qh" + #include "g_damage.qh" + #include "race.qh" + #include "t_teleporters.qh" + + #if defined(CSQC) + #elif defined(MENUQC) + #elif defined(SVQC) + #include "../dpdefs/progsdefs.qh" + #include "../dpdefs/dpextensions.qh" + #include "../warpzonelib/anglestransform.qh" + #include "../warpzonelib/util_server.qh" + #include "../common/constants.qh" + #include "../common/util.qh" + #include "../common/monsters/monsters.qh" + #include "../common/weapons/weapons.qh" + #include "weapons/tracing.qh" + #include "autocvars.qh" + #include "defs.qh" ++ #include "../common/effects.qh" + #include "../common/deathtypes.qh" + #include "mutators/mutators_include.qh" + #include "../csqcmodellib/sv_model.qh" + #endif + void CopyBody(float keepvelocity); #ifdef NOCHEATS diff --cc qcsrc/server/cl_client.qc index 88515da42,89647d5be..072aca612 --- a/qcsrc/server/cl_client.qc +++ b/qcsrc/server/cl_client.qc @@@ -1,40 -1,36 +1,77 @@@ + #include "waypointsprites.qh" + + #include "cl_impulse.qh" ++#include "cl_physics.qh" + #include "cl_player.qh" + #include "ent_cs.qh" + #include "g_subs.qh" + #include "ipban.qh" + #include "miscfunctions.qh" + #include "portals.qh" + #include "teamplay.qh" + #include "playerdemo.qh" ++#include "round_handler.qh" + #include "secret.qh" + + #include "bot/bot.qh" + #include "bot/navigation.qh" + + #include "weapons/hitplot.qh" + #include "weapons/weaponsystem.qh" + ++#include "../common/animdecide.qh" ++ ++#include "../common/effects.qh" ++ + #include "../common/net_notice.qh" + ++#include "../common/minigames/sv_minigames.qh" ++ + #include "../common/monsters/sv_monsters.qh" + ++#include "../common/vehicles/sv_vehicles.qh" ++ + #include "../warpzonelib/server.qh" + + float c1, c2, c3, c4; + void send_CSQC_teamnagger() { WriteByte(MSG_BROADCAST, SVC_TEMPENTITY); WriteByte(MSG_BROADCAST, TE_CSQC_TEAMNAGGER); } - float CountSpectators(entity player, entity to) -float ClientData_Send(entity to, int sf) ++int CountSpectators(entity player, entity to) +{ - if(!player) { return FALSE; } // not sure how, but best to be safe ++ if(!player) { return 0; } // not sure how, but best to be safe + + entity head; + float spec_count = 0; + FOR_EACH_REALCLIENT(head) + { + if(IS_SPEC(head)) + if(head != to) + if(head.enemy == player) + spec_count += 1; + } + + return spec_count; +} + +void WriteSpectators(entity player, entity to) +{ + if(!player) { return; } // not sure how, but best to be safe + + entity head; + FOR_EACH_REALCLIENT(head) + { + if(IS_SPEC(head)) + if(head != to) + if(head.enemy == player) + WriteByte(MSG_ENTITY, num_for_edict(head)); + } +} + - float ClientData_Send(entity to, float sf) ++bool ClientData_Send(entity to, int sf) { if(to != self.owner) { @@@ -69,19 -63,11 +106,19 @@@ if(sf & 8) { - WriteAngle(MSG_ENTITY, e.v_angle_x); - WriteAngle(MSG_ENTITY, e.v_angle_y); + WriteAngle(MSG_ENTITY, e.v_angle.x); + WriteAngle(MSG_ENTITY, e.v_angle.y); } + if(sf & 16) + { + float specs = CountSpectators(e, to); + WriteByte(MSG_ENTITY, e.clientfov); + WriteByte(MSG_ENTITY, specs); + WriteSpectators(e, to); + } + - return TRUE; + return true; } void ClientData_Attach() @@@ -178,18 -164,12 +215,18 @@@ putting a client as observer in the ser void FixPlayermodel(); void PutObserverInServer (void) { - entity spot; + entity spot; + + SetSpectator(self, world); + self.hud = HUD_NORMAL; - if(IS_PLAYER(self)) { pointparticles(particleeffectnum("spawn_event_neutral"), self.origin, '0 0 0', 1); } + if(IS_PLAYER(self)) { Send_Effect(EFFECT_SPAWN_NEUTRAL, self.origin, '0 0 0', 1); } + + if(autocvar_g_observer_glowtrails) + CSQCModel_UnlinkEntity(); - spot = SelectSpawnPoint (TRUE); + spot = SelectSpawnPoint (true); if(!spot) error("No spawnpoints for observers?!?\n"); RemoveGrapplingHook(self); // Wazat's Grappling Hook @@@ -428,61 -378,6 +465,61 @@@ void FixPlayermodel( setcolor(self, stof(autocvar_sv_defaultplayercolors)); } +void PlayerTouch (void) +{ + if(other == world) + return; + + if(!IS_PLAYER(self) || !IS_PLAYER(other)) + return; + + if(self.deadflag != DEAD_NO || other.deadflag != DEAD_NO) + return; + + if(!self.iscreature || !other.iscreature) + return; + + if(forbidWeaponUse(self)) + return; + + if(autocvar_g_player_crush_simple) + { + vector vdir = normalize(other.origin - self.origin); + + if(vdir_z > autocvar_g_player_crush_headheight) // adjust this to set how sharp from above players need to hit the player to crush them. + Damage (self, other, other, autocvar_g_player_crush_damage, DEATH_CRUSH, self.origin, '0 0 0'); + } + else + { + + tracebox(self.origin, self.mins, self.maxs, self.origin - ('0 0 1' * (self.maxs_z + 5)), MOVE_NORMAL, self); + + if(trace_ent == other) + { + float mjumpheight = autocvar_g_player_crush_bounce; + + setorigin(self, self.origin + '0 0 2'); + + if(self.BUTTON_JUMP) + { + mjumpheight = autocvar_g_player_crush_bounce_jump; + self.flags &= ~FL_JUMPRELEASED; + } + + self.flags &= ~FL_ONGROUND; + + self.velocity_z = mjumpheight; + self.oldvelocity_z = self.velocity_z; + - animdecide_setaction(self, ANIMACTION_JUMP, TRUE); ++ animdecide_setaction(self, ANIMACTION_JUMP, true); + + self.restart_jump = -1; // restart jump anim next time + + Damage (other, self, self, autocvar_g_player_crush_damage, DEATH_CRUSH, other.origin, '0 0 0'); + } + } +} + /* ============= PutClientInServer @@@ -534,16 -426,13 +571,16 @@@ void PutClientInServer (void RemoveGrapplingHook(self); // Wazat's Grappling Hook if(self.vehicle) - vehicles_exit(VHEF_RELESE); + vehicles_exit(VHEF_RELEASE); + + if(autocvar_g_observer_glowtrails) + CSQCMODEL_AUTOINIT(); self.classname = "player"; - self.wasplayer = TRUE; - self.iscreature = TRUE; + self.wasplayer = true; + self.iscreature = true; self.teleportable = TELEPORT_NORMAL; - self.damagedbycontents = TRUE; + self.damagedbycontents = true; self.movetype = MOVETYPE_WALK; self.solid = SOLID_SLIDEBOX; self.dphitcontentsmask = DPCONTENTS_BODY | DPCONTENTS_SOLID; @@@ -805,10 -680,7 +841,10 @@@ float ClientInit_SendEntity(entity to, WriteByte(MSG_ENTITY, WEP_CVAR_SEC(hagar, load_max)); // hagar max loadable rockets // WEAPONTODO WriteCoord(MSG_ENTITY, autocvar_g_trueaim_minrange); WriteByte(MSG_ENTITY, WEP_CVAR(porto, secondary)); // WEAPONTODO + WriteByte(MSG_ENTITY, WEP_CVAR_PRI(vaporizer, refire)); + WriteByte(MSG_ENTITY, sv_showfps); + WriteString(MSG_ENTITY, autocvar_sv_announcer); - return TRUE; + return true; } void ClientInit_CheckUpdate() @@@ -1477,22 -1340,6 +1513,22 @@@ void ClientDisconnect (void } .float BUTTON_CHAT; +.float crouch; +.float bubble_oldmax; +float ChatBubbleCustomize() +{ + entity e = WaypointSprite_getviewentity(other), own = self.owner; + + if(!own.deadflag && IS_PLAYER(own)) + { - if(own.BUTTON_CHAT) { self.skin = 0; return TRUE; } - if(own.active_minigame) { self.skin = 1; return TRUE; } - if(SAME_TEAM(own, e) && e != own) { self.skin = 2; return TRUE; } ++ if(own.BUTTON_CHAT) { self.skin = 0; return true; } ++ if(own.active_minigame) { self.skin = 1; return true; } ++ if(SAME_TEAM(own, e) && e != own) { self.skin = 2; return true; } + } + - return FALSE; ++ return false; +} + void ChatBubbleThink() { self.nextthink = time; @@@ -1521,16 -1370,14 +1557,16 @@@ void UpdateChatBubble( self.chatbubbleentity = spawn(); self.chatbubbleentity.owner = self; self.chatbubbleentity.exteriormodeltoclient = self; + self.chatbubbleentity.alpha = 1; + self.chatbubbleentity.customizeentityforclient = ChatBubbleCustomize; self.chatbubbleentity.think = ChatBubbleThink; self.chatbubbleentity.nextthink = time; - setmodel(self.chatbubbleentity, "models/misc/chatbubble.spr"); // precision set below + setmodel(self.chatbubbleentity, "models/misc/chatbubble.md3"); // precision set below //setorigin(self.chatbubbleentity, self.origin + '0 0 15' + self.maxs_z * '0 0 1'); - setorigin(self.chatbubbleentity, '0 0 15' + self.maxs_z * '0 0 1'); + setorigin(self.chatbubbleentity, '0 0 15' + self.maxs.z * '0 0 1'); setattachment(self.chatbubbleentity, self, ""); // sticks to moving player better, also conserves bandwidth self.chatbubbleentity.mdl = self.chatbubbleentity.model; - self.chatbubbleentity.model = ""; + //self.chatbubbleentity.model = ""; self.chatbubbleentity.effects = EF_LOWPRECISION; } } @@@ -1921,22 -1805,32 +1957,22 @@@ float SpectateUpdate( float SpectateSet() { - if(self.enemy.classname != "player") + if(!IS_PLAYER(self.enemy)) - return FALSE; + return false; - /*if(self.enemy.vehicle) - { - msg_entity = self; - WriteByte(MSG_ONE, SVC_SETVIEW); - WriteEntity(MSG_ONE, self.enemy); - //stuffcmd(self, "set viewsize $tmpviewsize \n"); + ClientData_Touch(self.enemy); - self.movetype = MOVETYPE_NONE; - accuracy_resend(self); - } - else - {*/ - msg_entity = self; - WriteByte(MSG_ONE, SVC_SETVIEW); - WriteEntity(MSG_ONE, self.enemy); - //stuffcmd(self, "set viewsize $tmpviewsize \n"); - self.movetype = MOVETYPE_NONE; - accuracy_resend(self); + msg_entity = self; + WriteByte(MSG_ONE, SVC_SETVIEW); + WriteEntity(MSG_ONE, self.enemy); + //stuffcmd(self, "set viewsize $tmpviewsize \n"); + self.movetype = MOVETYPE_NONE; + accuracy_resend(self); + + if(!SpectateUpdate()) + PutObserverInServer(); - return TRUE; - if(!SpectateUpdate()) - PutObserverInServer(); - //} + return true; } void SetSpectator(entity player, entity spectatee) @@@ -2089,10 -1971,8 +2125,10 @@@ void LeaveSpectatorMode( self.classname = "player"; nades_RemoveBonus(self); + SetSpectator(self, world); + if(autocvar_g_campaign || autocvar_g_balance_teams) - { JoinBestTeam(self, FALSE, TRUE); } + { JoinBestTeam(self, false, true); } if(autocvar_g_campaign) { campaign_bots_may_start = 1; } @@@ -2314,41 -2183,8 +2350,41 @@@ void PlayerUseKey( if(self.vehicle) { - vehicles_exit(VHEF_NORMAL); - return; + if(!gameover) + { + vehicles_exit(VHEF_NORMAL); + return; + } + } + else if(autocvar_g_vehicles_enter) + { + if(!self.frozen) + if(self.deadflag == DEAD_NO) + if(!gameover) + { + entity head, closest_target = world; - head = WarpZone_FindRadius(self.origin, autocvar_g_vehicles_enter_radius, TRUE); ++ head = WarpZone_FindRadius(self.origin, autocvar_g_vehicles_enter_radius, true); + + while(head) // find the closest acceptable target to enter + { + if(IS_VEHICLE(head)) + if(head.deadflag == DEAD_NO) + if(!head.owner || ((head.vehicle_flags & VHF_MULTISLOT) && SAME_TEAM(head.owner, self))) + if(head.takedamage != DAMAGE_NO) + { + if(closest_target) + { + if(vlen(self.origin - head.origin) < vlen(self.origin - closest_target.origin)) + { closest_target = head; } + } + else { closest_target = head; } + } + + head = head.chain; + } + + if(closest_target) { vehicles_enter(self, closest_target); return; } + } } // a use key was pressed; call handlers @@@ -2389,10 -2225,9 +2425,10 @@@ PlayerPreThin Called every frame for each client before the physics are run ============= */ --.float usekeypressed; ++.bool usekeypressed; void() nexball_setstatus; - .float items_added; + .int items_added; +.float last_vehiclecheck; void PlayerPreThink (void) { WarpZone_PlayerPhysics_FixVAngle(); @@@ -2670,10 -2484,10 +2706,10 @@@ { if (self.crouch) { - tracebox(self.origin, PL_MIN, PL_MAX, self.origin, FALSE, self); + tracebox(self.origin, PL_MIN, PL_MAX, self.origin, false, self); - if (!trace_startsolid) + if (!trace_startsolid || self.pbhost) { - self.crouch = FALSE; + self.crouch = false; self.view_ofs = PL_VIEW_OFS; setsize (self, PL_MIN, PL_MAX); } diff --cc qcsrc/server/cl_impulse.qc index f8323142d,2cc8e0215..7c851599e --- a/qcsrc/server/cl_impulse.qc +++ b/qcsrc/server/cl_impulse.qc @@@ -1,3 -1,11 +1,13 @@@ + #include "round_handler.qh" + + #include "bot/waypoints.qh" + + #include "weapons/throwing.qh" + ++#include "../common/minigames/sv_minigames.qh" ++ + #include "../common/weapons/weapons.qh" + /* * Impulse map: * diff --cc qcsrc/server/cl_physics.qc index 12d696000,337342333..c0b7d3e33 --- a/qcsrc/server/cl_physics.qc +++ b/qcsrc/server/cl_physics.qc @@@ -1,5 -1,31 +1,32 @@@ + #if defined(CSQC) + #elif defined(MENUQC) + #elif defined(SVQC) + #include "../dpdefs/progsdefs.qh" + #include "../dpdefs/dpextensions.qh" + #include "../warpzonelib/mathlib.qh" + #include "../warpzonelib/server.qh" + #include "../common/constants.qh" + #include "../common/util.qh" + #include "../common/animdecide.qh" + #include "../common/monsters/sv_monsters.qh" + #include "../common/weapons/weapons.qh" + #include "t_items.qh" + #include "autocvars.qh" ++ #include "cl_physics.qh" + #include "defs.qh" ++ #include "miscfunctions.qh" + #include "../common/notifications.qh" + #include "mutators/mutators_include.qh" + #include "../common/mapinfo.qh" + #include "../csqcmodellib/sv_model.qh" + #include "anticheat.qh" + #include "cheats.qh" + #include "g_hook.qh" + #include "race.qh" + #include "playerdemo.qh" + #endif + .float race_penalty; --.float restart_jump; .float ladder_time; .entity ladder_entity; @@@ -38,13 -47,13 +65,13 @@@ returns true if handle float PlayerJump (void) { if(self.frozen) - return TRUE; // no jumping in freezetag when frozen + return true; // no jumping in freezetag when frozen if(self.player_blocked) - return TRUE; // no jumping while blocked + return true; // no jumping while blocked - float doublejump = FALSE; + float doublejump = false; - float mjumpheight = autocvar_sv_jumpvelocity; + float mjumpheight = self.stat_sv_jumpvelocity; player_multijump = doublejump; player_jumpheight = mjumpheight; @@@ -484,9 -493,9 +511,9 @@@ void CPM_PM_Aircontrol(vector wishdir, return; #endif - k *= bound(0, wishspeed / autocvar_sv_maxairspeed, 1); + k *= bound(0, wishspeed / self.stat_sv_maxairspeed, 1); - zspeed = self.velocity_z; + zspeed = self.velocity.z; self.velocity_z = 0; xyspeed = vlen(self.velocity); self.velocity = normalize(self.velocity); @@@ -817,16 -837,6 +844,16 @@@ void SV_PlayerPhysics( } } + if ( self.discomode ) + { + if(IS_PLAYER(self)) + self.BUTTON_JUMP = 1; + + self.angles_y = time*180; + self.velocity = randomvec() * 80; - self.fixangle = TRUE; ++ self.fixangle = true; + } + if (self.movetype == MOVETYPE_NONE) return; @@@ -972,10 -988,10 +999,10 @@@ // noclipping or flying self.flags &= ~FL_ONGROUND; - self.velocity = self.velocity * (1 - frametime * autocvar_sv_friction); + self.velocity = self.velocity * (1 - frametime * self.stat_sv_friction); makevectors(self.v_angle); //wishvel = v_forward * self.movement_x + v_right * self.movement_y + v_up * self.movement_z; - wishvel = v_forward * self.movement_x + v_right * self.movement_y + '0 0 1' * self.movement_z; + wishvel = v_forward * self.movement.x + v_right * self.movement.y + '0 0 1' * self.movement.z; // acceleration wishdir = normalize(wishvel); wishspeed = vlen(wishvel); @@@ -1022,10 -1038,10 +1049,10 @@@ self.velocity_z += g; } - self.velocity = self.velocity * (1 - frametime * autocvar_sv_friction); + self.velocity = self.velocity * (1 - frametime * self.stat_sv_friction); makevectors(self.v_angle); //wishvel = v_forward * self.movement_x + v_right * self.movement_y + v_up * self.movement_z; - wishvel = v_forward * self.movement_x + v_right * self.movement_y + '0 0 1' * self.movement_z; + wishvel = v_forward * self.movement.x + v_right * self.movement.y + '0 0 1' * self.movement.z; self.velocity_z += g; if (self.ladder_entity.classname == "func_water") { @@@ -1062,9 -1078,9 +1089,9 @@@ { //makevectors(self.v_angle_y * '0 1 0'); makevectors(self.v_angle); - wishvel = v_forward * self.movement_x + v_right * self.movement_y; + wishvel = v_forward * self.movement.x + v_right * self.movement.y; // add remaining speed as Z component - maxairspd = autocvar_sv_maxairspeed*max(1, maxspd_mod); + maxairspd = self.stat_sv_maxairspeed*max(1, maxspd_mod); // fix speedhacks :P wishvel = normalize(wishvel) * min(vlen(wishvel) / maxairspd, 1); // add the unused velocity as up component @@@ -1078,15 -1094,12 +1105,15 @@@ a_side = autocvar_g_jetpack_acceleration_side; a_up = autocvar_g_jetpack_acceleration_up; a_add = autocvar_g_jetpack_antigravity * autocvar_sv_gravity; + if(autocvar_g_jetpack_reverse_thrust && self.crouch) { a_up = autocvar_g_jetpack_reverse_thrust; } - wishvel_x *= a_side; - wishvel_y *= a_side; - wishvel_z *= a_up; - wishvel_z += a_add; + wishvel.x *= a_side; + wishvel.y *= a_side; + wishvel.z *= a_up; + wishvel.z += a_add; + if(autocvar_g_jetpack_reverse_thrust && self.crouch) { wishvel_z *= -1; } + float best; best = 0; ////////////////////////////////////////////////////////////////////////////////////// @@@ -1241,12 -1254,12 +1268,12 @@@ } else { - maxairspd = autocvar_sv_maxairspeed; - airaccel = autocvar_sv_airaccelerate; + maxairspd = self.stat_sv_maxairspeed; + airaccel = self.stat_sv_airaccelerate; } // airborn - makevectors(self.v_angle_y * '0 1 0'); - wishvel = v_forward * self.movement_x + v_right * self.movement_y; + makevectors(self.v_angle.y * '0 1 0'); + wishvel = v_forward * self.movement.x + v_right * self.movement.y; // acceleration wishdir = normalize(wishvel); wishspeed = wishspeed0 = vlen(wishvel); @@@ -1272,9 -1285,9 +1299,9 @@@ { vector curdir; curdir = self.velocity; - curdir_z = 0; + curdir.z = 0; curdir = normalize(curdir); - airaccel = airaccel + (autocvar_sv_airstopaccelerate*maxspd_mod - airaccel) * max(0, -(curdir * wishdir)); + airaccel = airaccel + (self.stat_sv_airstopaccelerate*maxspd_mod - airaccel) * max(0, -(curdir * wishdir)); } // note that for straight forward jumping: // step = accel * frametime * wishspeed0; diff --cc qcsrc/server/cl_physics.qh index 000000000,000000000..8d3db725c new file mode 100644 --- /dev/null +++ b/qcsrc/server/cl_physics.qh @@@ -1,0 -1,0 +1,6 @@@ ++#ifndef CL_PHYSICS_H ++#define CL_PHYSICS_H ++ ++.float restart_jump; ++ ++#endif diff --cc qcsrc/server/cl_player.qc index 30af660f0,49d1c1b46..f558fe59a --- a/qcsrc/server/cl_player.qc +++ b/qcsrc/server/cl_player.qc @@@ -1,9 -1,10 +1,17 @@@ - .entity pusher; - .float pushltime; - .float istypefrag; + #include "cl_player.qh" ++#include "jeff.qh" + #include "g_triggers.qh" + #include "g_violence.qh" + #include "miscfunctions.qh" + ++#include "mutators/base.qh" ++ ++#include "../common/effects.qh" ++ ++#include "../common/minigames/sv_minigames.qh" ++ + #include "weapons/weaponstats.qh" - .float CopyBody_nextthink; - .void(void) CopyBody_think; void CopyBody_Think(void) { if(self.CopyBody_nextthink && time > self.CopyBody_nextthink) @@@ -130,14 -131,12 +138,14 @@@ void player_anim (void // Clear a previous death animation. deadbits = 0; } - float animbits = deadbits; + int animbits = deadbits; if(self.frozen) animbits |= ANIMSTATE_FROZEN; + if(self.movetype == MOVETYPE_FOLLOW) + animbits |= ANIMSTATE_FOLLOW; if(self.crouch) animbits |= ANIMSTATE_DUCK; - animdecide_setstate(self, animbits, FALSE); + animdecide_setstate(self, animbits, false); animdecide_setimplicitstate(self, (self.flags & FL_ONGROUND)); if (self.weaponentity) @@@ -157,9 -156,9 +165,9 @@@ void PlayerCorpseDamage (entity inflict // damage resistance (ignore most of the damage from a bullet or similar) damage = max(damage - 5, 1); - v = healtharmor_applydamage(self.armorvalue, autocvar_g_balance_armor_blockpercent, deathtype, damage, FALSE); - v = healtharmor_applydamage(self.armorvalue, autocvar_g_balance_armor_blockpercent, deathtype, damage); - take = v.x; - save = v.y; ++ v = healtharmor_applydamage(self.armorvalue, autocvar_g_balance_armor_blockpercent, deathtype, damage, false); + take = v_x; + save = v_y; if(sound_allowed(MSG_BROADCAST, attacker)) { @@@ -352,10 -338,10 +347,9 @@@ void PlayerDamage (entity inflictor, en else Violence_GibSplash_At(hitloc, force, 2, bound(0, damage, 200) / 16, self, attacker); -- - v = healtharmor_applydamage(self.armorvalue, autocvar_g_balance_armor_blockpercent, deathtype, damage); + v = healtharmor_applydamage(self.armorvalue, autocvar_g_balance_armor_blockpercent, deathtype, damage, autocvar_g_balance_armor_block_bycount); - take = v_x; - save = v_y; + take = v.x; + save = v.y; if(attacker == self) { diff --cc qcsrc/server/cl_player.qh index 000000000,cf5ec0a7d..2ad1fa44d mode 000000,100644..100644 --- a/qcsrc/server/cl_player.qh +++ b/qcsrc/server/cl_player.qh @@@ -1,0 -1,75 +1,75 @@@ + #ifndef CL_PLAYER_H + #define CL_PLAYER_H + + .entity pusher; + .float pushltime; + .float istypefrag; + + .float CopyBody_nextthink; + .void(void) CopyBody_think; + void CopyBody_Think(void); + void CopyBody(float keepvelocity); + + float player_getspecies(); + + void player_setupanimsformodel(); + + void player_anim (void); + + void PlayerCorpseDamage (entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force); + + // g__str: + // If 0, default is used. + // If <0, 0 is used. + // Otherwise, g_str (default value) is used. + // For consistency, negative values there are mapped to zero too. + #define GAMETYPE_DEFAULTED_SETTING(str) \ + ((gametype_setting_tmp = cvar(strcat("g_", GetGametype(), "_" #str))), \ + (gametype_setting_tmp < 0) ? 0 : \ - (gametype_setting_tmp == 0) ? max(0, autocvar_g_##str) : \ ++ (gametype_setting_tmp == 0 || autocvar_g_respawn_delay_forced) ? max(0, autocvar_g_##str) : \ + gametype_setting_tmp) + + + void calculate_player_respawn_time(); + + void ClientKill_Now_TeamChange(); + + void PlayerDamage (entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force); + + .float muted; // to be used by prvm_edictset server playernumber muted 1 + float Say(entity source, float teamsay, entity privatesay, string msgin, float floodcontrol); + // message "": do not say, just test flood control + // return value: + // 1 = accept + // 0 = reject + // -1 = fake accept + + float GetVoiceMessageVoiceType(string type); + + string allvoicesamples; + .string GetVoiceMessageSampleField(string type); + + .string GetPlayerSoundSampleField(string type); + + void PrecacheGlobalSound(string samplestring); + + void PrecachePlayerSounds(string f); + + void ClearPlayerSounds(); + + float LoadPlayerSounds(string f, float first); + + .int modelindex_for_playersound; + .int skin_for_playersound; + void UpdatePlayerSounds(); + + void FakeGlobalSound(string sample, float chan, float voicetype); + + void GlobalSound(string sample, float chan, float voicetype); + + void PlayerSound(.string samplefield, float chan, float voicetype); + + void VoiceMessage(string type, string msg); + + void MoveToTeam(entity client, float team_colour, float type); + #endif diff --cc qcsrc/server/cl_weapons.qc index f27ec9f79,000000000..7120ff537 mode 100644,000000..100644 --- a/qcsrc/server/cl_weapons.qc +++ b/qcsrc/server/cl_weapons.qc @@@ -1,549 -1,0 +1,551 @@@ ++#include "../round_handler.qh" ++ +void W_TriggerReload() +{ + weapon_action(self.weapon, WR_RELOAD); +} + +// switch between weapons +void W_SwitchWeapon(float imp) +{ + if (self.switchweapon != imp) + { - if (client_hasweapon(self, imp, TRUE, TRUE)) ++ if (client_hasweapon(self, imp, true, true)) + W_SwitchWeapon_Force(self, imp); + else + self.selectweapon = imp; // update selectweapon ANYWAY + } + else + { + W_TriggerReload(); + } +} + +.float weaponcomplainindex; +float W_GetCycleWeapon(entity pl, string weaponorder, float dir, float imp, float complain, float skipmissing) +{ + // We cannot tokenize in this function, as GiveItems calls this + // function. Thus we must use car/cdr. + float weaponwant, first_valid, prev_valid, switchtonext, switchtolast, c; + string rest; + switchtonext = switchtolast = 0; + first_valid = prev_valid = 0; + float weaponcur; + + if(skipmissing || pl.selectweapon == 0) + weaponcur = pl.switchweapon; + else + weaponcur = pl.selectweapon; + + if(dir == 0) + switchtonext = 1; + + c = 0; + + rest = weaponorder; + while(rest != "") + { + weaponwant = stof(car(rest)); rest = cdr(rest); + if(imp >= 0) + if((get_weaponinfo(weaponwant)).impulse != imp) + continue; + + ++c; + - if(!skipmissing || client_hasweapon(pl, weaponwant, TRUE, FALSE)) ++ if(!skipmissing || client_hasweapon(pl, weaponwant, true, false)) + { + if(switchtonext) + return weaponwant; + if(!first_valid) + first_valid = weaponwant; + if(weaponwant == weaponcur) + { + if(dir >= 0) + switchtonext = 1; + else if(prev_valid) + return prev_valid; + else + switchtolast = 1; + } + prev_valid = weaponwant; + } + } + if(first_valid) + { + if(switchtolast) + return prev_valid; + else + return first_valid; + } + // complain (but only for one weapon on the button that has been pressed) + if(complain) + { + self.weaponcomplainindex += 1; + c = mod(self.weaponcomplainindex, c) + 1; + rest = weaponorder; + while(rest != "") + { + weaponwant = stof(car(rest)); rest = cdr(rest); + if(imp >= 0) + if((get_weaponinfo(weaponwant)).impulse != imp) + continue; + + --c; + if(c == 0) + { - client_hasweapon(pl, weaponwant, TRUE, TRUE); ++ client_hasweapon(pl, weaponwant, true, true); + break; + } + } + } + return 0; +} + +void W_CycleWeapon(string weaponorder, float dir) +{ + float w; - w = W_GetCycleWeapon(self, weaponorder, dir, -1, 1, TRUE); ++ w = W_GetCycleWeapon(self, weaponorder, dir, -1, 1, true); + if(w > 0) + W_SwitchWeapon(w); +} + +void W_NextWeaponOnImpulse(float imp) +{ + float w; + w = W_GetCycleWeapon(self, self.cvar_cl_weaponpriority, +1, imp, 1, (self.cvar_cl_weaponimpulsemode == 0)); + if(w > 0) + W_SwitchWeapon(w); +} + +// next weapon +void W_NextWeapon(float list) +{ + if(list == 0) + W_CycleWeapon(weaponorder_byid, -1); + else if(list == 1) + W_CycleWeapon(self.weaponorder_byimpulse, -1); + else if(list == 2) + W_CycleWeapon(self.cvar_cl_weaponpriority, -1); +} + +// prev weapon +void W_PreviousWeapon(float list) +{ + if(list == 0) + W_CycleWeapon(weaponorder_byid, +1); + else if(list == 1) + W_CycleWeapon(self.weaponorder_byimpulse, +1); + else if(list == 2) + W_CycleWeapon(self.cvar_cl_weaponpriority, +1); +} + +// previously used if exists and has ammo, (second) best otherwise +void W_LastWeapon() +{ - if(client_hasweapon(self, self.cnt, TRUE, FALSE)) ++ if(client_hasweapon(self, self.cnt, true, false)) + W_SwitchWeapon(self.cnt); + else + W_SwitchToOtherWeapon(self); +} + +float w_getbestweapon(entity e) +{ - return W_GetCycleWeapon(e, e.cvar_cl_weaponpriority, 0, -1, FALSE, TRUE); ++ return W_GetCycleWeapon(e, e.cvar_cl_weaponpriority, 0, -1, false, true); +} + +// generic weapons table +// TODO should they be macros instead? +float weapon_action(float wpn, float wrequest) +{ + return (get_weaponinfo(wpn)).weapon_func(wrequest); +} + +.float savenextthink; +void thrown_wep_think() +{ + self.nextthink = time; + if(self.oldorigin != self.origin) + { + self.SendFlags |= ISF_LOCATION; + self.oldorigin = self.origin; + } + self.owner = world; + float timeleft = self.savenextthink - time; + if(timeleft > 1) + SUB_SetFade(self, self.savenextthink - 1, 1); + else if(timeleft > 0) + SUB_SetFade(self, time, timeleft); + else + SUB_VanishOrRemove(self); +} + +// returns amount of ammo used as string, or -1 for failure, or 0 for no ammo count +string W_ThrowNewWeapon(entity own, float wpn, float doreduce, vector org, vector velo) +{ + entity oldself, wep; + float wa, thisammo, i, j; + string s; + var .float ammofield; + + wep = spawn(); + + setorigin(wep, org); + wep.classname = "droppedweapon"; + wep.velocity = velo; + wep.owner = wep.enemy = own; + wep.flags |= FL_TOSSED; + wep.colormap = own.colormap; + + if(WepSet_FromWeapon(wpn) & WEPSET_SUPERWEAPONS) + { + if(own.items & IT_UNLIMITED_SUPERWEAPONS) + { + wep.superweapons_finished = time + autocvar_g_balance_superweapons_time; + } + else + { + float superweapons = 1; + for(i = WEP_FIRST; i <= WEP_LAST; ++i) + if(WepSet_FromWeapon(i) & WEPSET_SUPERWEAPONS) + if(own.weapons & WepSet_FromWeapon(i)) + ++superweapons; + if(superweapons <= 1) + { + wep.superweapons_finished = own.superweapons_finished; + own.superweapons_finished = 0; + } + else + { + float timeleft = own.superweapons_finished - time; + float weptimeleft = timeleft / superweapons; + wep.superweapons_finished = time + weptimeleft; + own.superweapons_finished -= weptimeleft; + } + } + } + + wa = W_AmmoItemCode(wpn); + if(wa == 0) + { + oldself = self; + self = wep; + weapon_defaultspawnfunc(wpn); + self = oldself; + if(startitem_failed) + return string_null; + wep.glowmod = own.weaponentity_glowmod; + wep.think = thrown_wep_think; + wep.savenextthink = wep.nextthink; + wep.nextthink = min(wep.nextthink, time + 0.5); - wep.pickup_anyway = TRUE; // these are ALWAYS pickable ++ wep.pickup_anyway = true; // these are ALWAYS pickable + return ""; + } + else + { + s = ""; + oldself = self; + self = wep; + weapon_defaultspawnfunc(wpn); + self = oldself; + if(startitem_failed) + return string_null; + if(doreduce && g_weapon_stay == 2) + { + for(i = 0, j = 1; i < 24; ++i, j *= 2) + { + if(wa & j) + { + ammofield = Item_CounterField(j); + + // if our weapon is loaded, give its load back to the player + if(self.(weapon_load[self.weapon]) > 0) + { + own.ammofield += self.(weapon_load[self.weapon]); + self.(weapon_load[self.weapon]) = -1; // schedule the weapon for reloading + } + + wep.ammofield = 0; + } + } + } + else if(doreduce) + { + for(i = 0, j = 1; i < 24; ++i, j *= 2) + { + if(wa & j) + { + ammofield = Item_CounterField(j); + + // if our weapon is loaded, give its load back to the player + if(self.(weapon_load[self.weapon]) > 0) + { + own.ammofield += self.(weapon_load[self.weapon]); + self.(weapon_load[self.weapon]) = -1; // schedule the weapon for reloading + } + + thisammo = min(own.ammofield, wep.ammofield); + wep.ammofield = thisammo; + own.ammofield -= thisammo; + s = strcat(s, " and ", ftos(thisammo), " ", Item_CounterFieldName(j)); + } + } + s = substring(s, 5, -1); + } + wep.glowmod = own.weaponentity_glowmod; + wep.think = thrown_wep_think; + wep.savenextthink = wep.nextthink; + wep.nextthink = min(wep.nextthink, time + 0.5); - wep.pickup_anyway = TRUE; // these are ALWAYS pickable ++ wep.pickup_anyway = true; // these are ALWAYS pickable + + return s; + } +} + +float W_IsWeaponThrowable(float w) +{ + float wa; + + if (!autocvar_g_pickup_items) + return 0; + if (g_weaponarena) + return 0; + if(w == 0) + return 0; + + wa = W_AmmoItemCode(w); + if(start_weapons & WepSet_FromWeapon(w)) + { + // start weapons that take no ammo can't be dropped (this prevents dropping the laser, as long as it continues to use no ammo) + if(start_items & IT_UNLIMITED_WEAPON_AMMO) + return 0; + if(wa == 0) + return 0; + } + + return 1; +} + +// toss current weapon +void W_ThrowWeapon(vector velo, vector delta, float doreduce) +{ + float w; + string a; + + w = self.weapon; + if (w == 0) + return; // just in case + if(self.frozen) + return; + if(self.vehicle) + return; + if(MUTATOR_CALLHOOK(ForbidThrowCurrentWeapon)) + return; + if(!autocvar_g_weapon_throwable) + return; + if(self.weaponentity.state != WS_READY) + return; + if(!W_IsWeaponThrowable(w)) + return; + + if(!(self.weapons & WepSet_FromWeapon(w))) + return; + self.weapons &= ~WepSet_FromWeapon(w); + + W_SwitchWeapon_Force(self, w_getbestweapon(self)); + a = W_ThrowNewWeapon(self, w, doreduce, self.origin + delta, velo); + + if (!a) return; + Send_Notification(NOTIF_ONE, self, MSG_MULTI, ITEM_WEAPON_DROP, a, w); +} + +float forbidWeaponUse(entity player) +{ + if(time < game_starttime && !autocvar_sv_ready_restart_after_countdown) + return 1; + if(player.player_blocked) + return 3; + if(player.weapon_blocked) + return 4; + if(player.frozen) + return 5; + if(round_handler_IsActive() && !round_handler_IsRoundStarted()) + return 2; + if ( player.discomode ) + return 6; + return 0; +} + +void W_WeaponFrame() +{ + vector fo, ri, up; + + if (frametime) + self.weapon_frametime = frametime; + + if (!self.weaponentity || self.health < 1) + return; // Dead player can't use weapons and injure impulse commands + + float forbid_weaponuse = forbidWeaponUse(self); + + if(forbid_weaponuse && (!g_instagib || forbid_weaponuse != 2)) + if(self.weaponentity.state != WS_CLEAR) + { + w_ready(); + return; + } + + if(!self.switchweapon) + { + self.weapon = 0; + self.switchingweapon = 0; + self.weaponentity.state = WS_CLEAR; + self.weaponname = ""; + self.items &= ~IT_AMMO; + return; + } + + makevectors(self.v_angle); + fo = v_forward; // save them in case the weapon think functions change it + ri = v_right; + up = v_up; + + // Change weapon + if (self.weapon != self.switchweapon) + { + if (self.weaponentity.state == WS_CLEAR) + { + // end switching! + self.switchingweapon = self.switchweapon; + + entity newwep = get_weaponinfo(self.switchweapon); + - //setanim(self, self.anim_draw, FALSE, TRUE, TRUE); ++ //setanim(self, self.anim_draw, false, true, true); + self.weaponentity.state = WS_RAISE; + weapon_action(self.switchweapon, WR_SETUP); + + // set our clip load to the load of the weapon we switched to, if it's reloadable + if((newwep.spawnflags & WEP_FLAG_RELOADABLE) && cvar(strcat("g_balance_", newwep.netname, "_reload_ammo"))) // prevent accessing undefined cvars + { + self.clip_load = self.(weapon_load[self.switchweapon]); + self.clip_size = cvar(strcat("g_balance_", newwep.netname, "_reload_ammo")); + } + else + self.clip_load = self.clip_size = 0; + + // VorteX: add player model weapon select frame here + // setcustomframe(PlayerWeaponRaise); + weapon_thinkf(WFRAME_IDLE, cvar(sprintf("g_balance_%s_switchdelay_raise", newwep.netname)), w_ready); + //printf("W_WeaponFrame(): cvar: %s, value: %f\n", sprintf("g_balance_%s_switchdelay_raise", newwep.netname), cvar(sprintf("g_balance_%s_switchdelay_raise", newwep.netname))); + weapon_boblayer1(PLAYER_WEAPONSELECTION_SPEED, '0 0 0'); + } + else if (self.weaponentity.state == WS_DROP) + { + // in dropping phase we can switch at any time + self.switchingweapon = self.switchweapon; + } + else if (self.weaponentity.state == WS_READY) + { + // start switching! + self.switchingweapon = self.switchweapon; + + entity oldwep = get_weaponinfo(self.weapon); + +#ifndef INDEPENDENT_ATTACK_FINISHED + if(ATTACK_FINISHED(self) <= time + self.weapon_frametime * 0.5) + { +#endif + sound (self, CH_WEAPON_SINGLE, W_Sound("weapon_switch"), VOL_BASE, ATTEN_NORM); + self.weaponentity.state = WS_DROP; + // set up weapon switch think in the future, and start drop anim + weapon_thinkf(WFRAME_DONTCHANGE, cvar(sprintf("g_balance_%s_switchdelay_drop", oldwep.netname)), w_clear); + //printf("W_WeaponFrame(): cvar: %s, value: %f\n", sprintf("g_balance_%s_switchdelay_drop", oldwep.netname), cvar(sprintf("g_balance_%s_switchdelay_drop", oldwep.netname))); + weapon_boblayer1(PLAYER_WEAPONSELECTION_SPEED, PLAYER_WEAPONSELECTION_RANGE); +#ifndef INDEPENDENT_ATTACK_FINISHED + } +#endif + } + } + + // LordHavoc: network timing test code + //if (self.button0) + // print(ftos(frametime), " ", ftos(time), " >= ", ftos(ATTACK_FINISHED(self)), " >= ", ftos(self.weapon_nextthink), "\n"); + + float w; + w = self.weapon; + + // call the think code which may fire the weapon + // and do so multiple times to resolve framerate dependency issues if the + // server framerate is very low and the weapon fire rate very high + float c; + c = 0; + while (c < W_TICSPERFRAME) + { + c = c + 1; + if(w && !(self.weapons & WepSet_FromWeapon(w))) + { + if(self.weapon == self.switchweapon) + W_SwitchWeapon_Force(self, w_getbestweapon(self)); + w = 0; + } + + v_forward = fo; + v_right = ri; + v_up = up; + + if(w && self.weapon == self.switchweapon) + weapon_action(self.weapon, WR_THINK); + else + weapon_action(self.weapon, WR_GONETHINK); + + if (time + self.weapon_frametime * 0.5 >= self.weapon_nextthink) + { + if(self.weapon_think) + { + v_forward = fo; + v_right = ri; + v_up = up; + self.weapon_think(); + } + else + bprint("\{1}^1ERROR: undefined weapon think function for ", self.netname, "\n"); + } + } + + // don't let attack_finished fall behind when not firing (must be after weapon_setup calls!) + //if (ATTACK_FINISHED(self) < time) + // ATTACK_FINISHED(self) = time; + + //if (self.weapon_nextthink < time) + // self.weapon_nextthink = time; + + // update currentammo incase it has changed +#if 0 + if (self.items & IT_CELLS) + self.currentammo = self.ammo_cells; + else if (self.items & IT_ROCKETS) + self.currentammo = self.ammo_rockets; + else if (self.items & IT_NAILS) + self.currentammo = self.ammo_nails; + else if (self.items & IT_SHELLS) + self.currentammo = self.ammo_shells; + else + self.currentammo = 1; +#endif +} + +string W_Apply_Weaponreplace(string in) +{ + float n = tokenize_console(in); + string out = ""; + float i; + for(i = 0; i < n; ++i) + { + string s = argv(i); + string r = cvar_string(strcat("g_weaponreplace_", s)); + if(r == "") + out = strcat(out, " ", s); + else if(r != "0") + out = strcat(out, " ", r); + } + return substring(out, 1, -1); +} diff --cc qcsrc/server/command/cmd.qc index 1a4768d27,4a8b59eba..15929c601 --- a/qcsrc/server/command/cmd.qc +++ b/qcsrc/server/command/cmd.qc @@@ -1,3 -1,32 +1,32 @@@ + #if defined(CSQC) + #elif defined(MENUQC) + #elif defined(SVQC) + #include "../../dpdefs/progsdefs.qh" + #include "../../dpdefs/dpextensions.qh" + #include "../../warpzonelib/common.qh" + #include "../../common/constants.qh" + #include "../../common/teams.qh" + #include "../../common/util.qh" + #include "../../common/command/shared_defs.qh" + #include "../../common/monsters/monsters.qh" + #include "../../common/monsters/sv_monsters.qh" + #include "../../common/monsters/spawn.qh" + #include "../autocvars.qh" + #include "../defs.qh" + #include "../../common/notifications.qh" + #include "../../common/deathtypes.qh" + #include "../mutators/mutators_include.qh" - #include "../vehicles/vehicles_def.qh" ++ #include "../../common/vehicles/sv_vehicles.qh" + #include "../campaign.qh" + #include "../../common/mapinfo.qh" + #include "common.qh" + #include "vote.qh" + #include "cmd.qh" + #include "../cheats.qh" + #include "../scores.qh" + #include "../ipban.qh" + #endif + // ========================================================= // Server side networked commands code, reworked by Samual // Last updated: December 28th, 2011 @@@ -185,49 -213,158 +214,47 @@@ void ClientCommand_join(float request } } - void ClientCommand_physics(float request, float argc) -void ClientCommand_mobedit(float request, float argc) ++void ClientCommand_physics(int request, int argc) { switch(request) { case CMD_REQUEST_COMMAND: { - if(argv(1) && argv(2)) - { - makevectors(self.v_angle); - WarpZone_TraceLine(self.origin + self.view_ofs, self.origin + self.view_ofs + v_forward * 100, MOVE_NORMAL, self); - - if(!autocvar_g_monsters_edit) { sprint(self, "Monster property editing is not enabled.\n"); return; } - if(trace_ent.flags & FL_MONSTER) - { - if(trace_ent.realowner != self) { sprint(self, "That monster does not belong to you.\n"); return; } - switch(argv(1)) - { - case "skin": - { - if(trace_ent.monsterid != MON_MAGE) - trace_ent.skin = stof(argv(2)); - return; - } - case "movetarget": - { - trace_ent.monster_moveflags = stof(argv(2)); - return; - } - } - } - } - } - default: - sprint(self, "Incorrect parameters for ^2mobedit^7\n"); - case CMD_REQUEST_USAGE: - { - sprint(self, "\nUsage:^3 cmd mobedit [argument]\n"); - sprint(self, " Where 'argument' can be skin or movetarget.\n"); - sprint(self, " Aim at your monster to edit its properties.\n"); - return; - } - } -} + string command = strtolower(argv(1)); -void ClientCommand_mobkill(float request) -{ - switch(request) - { - case CMD_REQUEST_COMMAND: - { - makevectors(self.v_angle); - WarpZone_TraceLine(self.origin + self.view_ofs, self.origin + self.view_ofs + v_forward * 100, MOVE_NORMAL, self); - - if(trace_ent.flags & FL_MONSTER) + if(command == "list" || command == "help") { - if(trace_ent.realowner != self) - { - sprint(self, "That monster does not belong to you.\n"); - return; - } - sprint(self, strcat("Your pet '", trace_ent.monster_name, "' has been brutally mutilated.\n")); - Damage (trace_ent, world, world, trace_ent.health + trace_ent.max_health + 200, DEATH_KILL, trace_ent.origin, '0 0 0'); + sprint(self, strcat("Available physics sets: \n\n", autocvar_g_physics_clientselect_options, " default\n")); + if(!autocvar_g_physics_clientselect) + sprint(self, "These are unusable currently, as client physics selection is disabled\n"); return; } - } - default: - sprint(self, "Incorrect parameters for ^2mobkill^7\n"); - case CMD_REQUEST_USAGE: - { - sprint(self, "\nUsage:^3 cmd mobkill\n"); - sprint(self, " Aim at your monster to kill it.\n"); - return; - } - } -} - -void ClientCommand_mobspawn(float request, float argc) -{ - switch(request) - { - case CMD_REQUEST_COMMAND: - { - entity e; - string tospawn; - float moveflag, monstercount = 0; - - moveflag = (argv(2) ? stof(argv(2)) : 1); // follow owner if not defined - tospawn = strtolower(argv(1)); - - if(tospawn == "list") + if(Physics_Valid(command) || command == "default") { - sprint(self, monsterlist_reply); - return; - } + stuffcmd(self, strcat("\nseta cl_physics ", command, "\nsendcvar cl_physics\n")); - FOR_EACH_MONSTER(e) - { - if(e.realowner == self) - ++monstercount; + if(!autocvar_g_physics_clientselect) + sprint(self, strcat("Physics set to ^3", command, "^7, but will not be used since client physics selection is disabled\n")); + else + sprint(self, strcat("^2Physics set successfully changed to ^3", command, "\n")); + return; } - if(autocvar_g_monsters_max <= 0 || autocvar_g_monsters_max_perplayer <= 0) { sprint(self, "Monster spawning is disabled.\n"); return; } - else if(!IS_PLAYER(self)) { sprint(self, "You can't spawn monsters while spectating.\n"); return; } - else if(MUTATOR_CALLHOOK(AllowMobSpawning)) { sprint(self, "Monster spawning is currently disabled by a mutator.\n"); return; } - else if(!autocvar_g_monsters) { Send_Notification(NOTIF_ONE, self, MSG_INFO, INFO_MONSTERS_DISABLED); return; } - else if(self.vehicle) { sprint(self, "You can't spawn monsters while driving a vehicle.\n"); return; } - else if(self.frozen) { sprint(self, "You can't spawn monsters while frozen.\n"); return; } - else if(autocvar_g_campaign) { sprint(self, "You can't spawn monsters in campaign mode.\n"); return; } - else if(self.deadflag != DEAD_NO) { sprint(self, "You can't spawn monsters while dead.\n"); return; } - else if(monstercount >= autocvar_g_monsters_max_perplayer) { sprint(self, "You have spawned too many monsters, kill some before trying to spawn any more.\n"); return; } - else if(totalspawned >= autocvar_g_monsters_max) { sprint(self, "The global maximum monster count has been reached, kill some before trying to spawn any more.\n"); return; } - else if(tospawn != "") + if(!autocvar_g_physics_clientselect) { - float found = 0, i; - entity mon; - - for(i = MON_FIRST; i <= MON_LAST; ++i) - { - mon = get_monsterinfo(i); - if(mon.netname == tospawn) - { - found = true; - break; - } - } - - if(found || tospawn == "random") - { - totalspawned += 1; - - makevectors(self.v_angle); - WarpZone_TraceBox (CENTER_OR_VIEWOFS(self), PL_MIN, PL_MAX, CENTER_OR_VIEWOFS(self) + v_forward * 150, true, self); - //WarpZone_TraceLine(self.origin + self.view_ofs, self.origin + self.view_ofs + v_forward * 150, MOVE_NORMAL, self); - - e = spawnmonster(tospawn, 0, self, self, trace_endpos, false, false, moveflag); - - sprint(self, strcat("Spawned ", e.monster_name, "\n")); - - return; - } + sprint(self, "Client physics selection is currently disabled.\n"); + return; } - - } - + default: - sprint(self, "Incorrect parameters for ^2mobspawn^7\n"); + sprint(self, strcat("Current physics set: ^3", self.cvar_cl_physics, "\n")); case CMD_REQUEST_USAGE: { - sprint(self, "\nUsage:^3 cmd mobspawn [movetype]\n"); - sprint(self, " See 'cmd mobspawn list' for available monsters.\n"); - sprint(self, " Argument 'random' spawns a random monster.\n"); - sprint(self, " Monster will follow the owner if second argument is not defined.\n"); + sprint(self, "\nUsage:^3 cmd physics \n"); + sprint(self, " See 'cmd physics list' for available physics sets.\n"); + sprint(self, " Argument 'default' resets to standard physics.\n"); return; } } diff --cc qcsrc/server/command/common.qc index 3156bc114,5b7e761b7..16ae289c6 --- a/qcsrc/server/command/common.qc +++ b/qcsrc/server/command/common.qc @@@ -302,147 -307,6 +307,147 @@@ void CommonCommand_cvar_purechanges(flo } } +void CommonCommand_editmob(float request, entity caller, float argc) +{ + switch(request) + { + case CMD_REQUEST_COMMAND: + { + if(autocvar_g_campaign) { print_to(caller, "Monster editing is disabled in singleplayer"); return; } + // no checks for g_monsters here, as it may be toggled mid match which existing monsters + + if(caller) + { + makevectors(self.v_angle); + WarpZone_TraceLine(self.origin + self.view_ofs, self.origin + self.view_ofs + v_forward * 100, MOVE_NORMAL, self); + } + + entity mon = trace_ent; + float is_visible = IS_MONSTER(mon); + string argument = argv(2); + + switch(argv(1)) + { + case "name": + { + if(!caller) { print_to(caller, "Only players can edit monsters"); return; } + if(!argument) { break; } // escape to usage + if(!autocvar_g_monsters_edit) { print_to(caller, "Monster editing is disabled"); return; } + if(mon.realowner != caller && autocvar_g_monsters_edit < 2) { print_to(caller, "This monster does not belong to you"); return; } + if(!is_visible) { print_to(caller, "You must look at your monster to edit it"); return; } + + string mon_oldname = mon.monster_name; + + mon.monster_name = argument; + if(mon.sprite) { WaypointSprite_UpdateSprites(mon.sprite, strzone(mon.monster_name), string_null, string_null); } + print_to(caller, sprintf("Your pet '%s' is now known as '%s'", mon_oldname, mon.monster_name)); + return; + } + case "spawn": + { + if(!caller) { print_to(caller, "Only players can spawn monsters"); return; } + if(!argv(2)) { break; } // escape to usage + + float moveflag, tmp_moncount = 0; + string arg_lower = strtolower(argument); + moveflag = (argv(3)) ? stof(argv(3)) : 1; // follow owner if not defined + ret_string = "Monster spawning is currently disabled by a mutator"; + + if(arg_lower == "list") { print_to(caller, monsterlist_reply); return; } + + FOR_EACH_MONSTER(mon) { if(mon.realowner == caller) ++tmp_moncount; } + + if(!autocvar_g_monsters) { print_to(caller, "Monsters are disabled"); return; } + if(autocvar_g_monsters_max <= 0 || autocvar_g_monsters_max_perplayer <= 0) { print_to(caller, "Monster spawning is disabled"); return; } + if(!IS_PLAYER(caller)) { print_to(caller, "You must be playing to spawn a monster"); return; } + if(MUTATOR_CALLHOOK(AllowMobSpawning)) { print_to(caller, ret_string); return; } + if(caller.vehicle) { print_to(caller, "You can't spawn monsters while driving a vehicle"); return; } + if(caller.frozen) { print_to(caller, "You can't spawn monsters while frozen"); return; } + if(caller.deadflag != DEAD_NO) { print_to(caller, "You can't spawn monsters while dead"); return; } + if(tmp_moncount >= autocvar_g_monsters_max) { print_to(caller, "The maximum monster count has been reached"); return; } + if(tmp_moncount >= autocvar_g_monsters_max_perplayer) { print_to(caller, "You can't spawn any more monsters"); return; } + - float i = 0, found = FALSE; ++ float i = 0, found = false; + for(i = MON_FIRST; i <= MON_LAST; ++i) + { + mon = get_monsterinfo(i); - if(mon.netname == arg_lower) { found = TRUE; break; } ++ if(mon.netname == arg_lower) { found = true; break; } + } + + if(!found && arg_lower != "random") { print_to(caller, "Invalid monster"); return; } + + totalspawned += 1; - WarpZone_TraceBox (CENTER_OR_VIEWOFS(caller), caller.mins, caller.maxs, CENTER_OR_VIEWOFS(caller) + v_forward * 150, TRUE, caller); - mon = spawnmonster(arg_lower, 0, caller, caller, trace_endpos, FALSE, FALSE, moveflag); ++ WarpZone_TraceBox (CENTER_OR_VIEWOFS(caller), caller.mins, caller.maxs, CENTER_OR_VIEWOFS(caller) + v_forward * 150, true, caller); ++ mon = spawnmonster(arg_lower, 0, caller, caller, trace_endpos, false, false, moveflag); + print_to(caller, strcat("Spawned ", mon.monster_name)); + return; + } + case "kill": + { + if(!caller) { print_to(caller, "Only players can kill monsters"); return; } + if(mon.realowner != caller && autocvar_g_monsters_edit < 2) { print_to(caller, "This monster does not belong to you"); return; } + if(!is_visible) { print_to(caller, "You must look at your monster to edit it"); return; } + + Damage (mon, world, world, mon.health + mon.max_health + 200, DEATH_KILL, mon.origin, '0 0 0'); + print_to(caller, strcat("Your pet '", mon.monster_name, "' has been brutally mutilated")); + return; + } + case "skin": + { + if(!caller) { print_to(caller, "Only players can edit monsters"); return; } + if(!argument) { break; } // escape to usage + if(!autocvar_g_monsters_edit) { print_to(caller, "Monster editing is disabled"); return; } + if(!is_visible) { print_to(caller, "You must look at your monster to edit it"); return; } + if(mon.realowner != caller && autocvar_g_monsters_edit < 2) { print_to(caller, "This monster does not belong to you"); return; } + if(mon.monsterid == MON_MAGE) { print_to(caller, "Mage skins can't be changed"); return; } // TODO + + mon.skin = stof(argument); + print_to(caller, strcat("Monster skin successfully changed to ", ftos(mon.skin))); + return; + } + case "movetarget": + { + if(!caller) { print_to(caller, "Only players can edit monsters"); return; } + if(!argument) { break; } // escape to usage + if(!autocvar_g_monsters_edit) { print_to(caller, "Monster editing is disabled"); return; } + if(!is_visible) { print_to(caller, "You must look at your monster to edit it"); return; } + if(mon.realowner != caller && autocvar_g_monsters_edit < 2) { print_to(caller, "This monster does not belong to you"); return; } + + mon.monster_moveflags = stof(argument); + print_to(caller, strcat("Monster move target successfully changed to ", ftos(mon.monster_moveflags))); + return; + } + case "butcher": + { + if(caller) { print_to(caller, "This command is not available to players"); return; } + if(g_invasion) { print_to(caller, "This command does not work during an invasion!"); return; } + + float tmp_remcount = 0; + entity tmp_entity; + + FOR_EACH_MONSTER(tmp_entity) { Monster_Remove(tmp_entity); ++tmp_remcount; } + + monsters_total = monsters_killed = totalspawned = 0; + + print_to(caller, (tmp_remcount) ? sprintf("Killed %d monster%s", tmp_remcount, (tmp_remcount == 1) ? "" : "s") : "No monsters to kill"); + return; + } + } + } + + default: + case CMD_REQUEST_USAGE: + { + print_to(caller, strcat("\nUsage:^3 ", GetCommandPrefix(caller), " editmob command [arguments]")); + print_to(caller, " Where 'command' can be butcher spawn skin movetarget kill name"); + print_to(caller, " spawn, skin, movetarget and name require 'arguments'"); + print_to(caller, " spawn also takes arguments list and random"); + print_to(caller, " Monster will follow owner if third argument of spawn command is not defined"); + return; + } + } +} + void CommonCommand_info(float request, entity caller, float argc) { switch(request) diff --cc qcsrc/server/command/common.qh index 589388bba,ac99e32dc..73102cfdf --- a/qcsrc/server/command/common.qh +++ b/qcsrc/server/command/common.qh @@@ -35,4 -43,143 +43,146 @@@ void timeout_handler_think() void CommonCommand_macro_write_aliases(float fh); // keep track of the next token to use for argc - float next_token; + float next_token; + + // select the proper prefix for usage and other messages + string GetCommandPrefix(entity caller); + + // if client return player nickname, or if server return admin nickname + string GetCallerName(entity caller); + + // verify that the client provided is acceptable for kicking + float VerifyKickableEntity(entity client); + + // verify that the client provided is acceptable for use + float VerifyClientEntity(entity client, float must_be_real, float must_be_bots); + + // if the client is not acceptable, return a string to be used for error messages + string GetClientErrorString_color(float clienterror, string original_input, string col); + #define GetClientErrorString(clienterror,original_input) GetClientErrorString_color(clienterror,original_input,"^7") + + // is this entity number even in the possible range of entities? + float VerifyClientNumber(float tmp_number); + + entity GetIndexedEntity(float argc, float start_index); + + // find a player which matches the input string, and return their entity + entity GetFilteredEntity(string input); + + // same thing, but instead return their edict number + float GetFilteredNumber(string input); + + // switch between sprint and print depending on whether the receiver is the server or a player + void print_to(entity to, string input); + + // ========================================== + // Supporting functions for common commands + // ========================================== + + // used by CommonCommand_timeout() and CommonCommand_timein() to handle game pausing and messaging and such. + void timeout_handler_reset(); + + void timeout_handler_think(); + + // =================================================== + // Common commands used in both sv_cmd.qc and cmd.qc + // =================================================== + + void CommonCommand_cvar_changes(float request, entity caller); + + void CommonCommand_cvar_purechanges(float request, entity caller); + ++void CommonCommand_editmob(float request, entity caller, float argc); ++ + void CommonCommand_info(float request, entity caller, float argc); + + void CommonCommand_ladder(float request, entity caller); + + void CommonCommand_lsmaps(float request, entity caller); + + void CommonCommand_printmaplist(float request, entity caller); + + void CommonCommand_rankings(float request, entity caller); + + void CommonCommand_records(float request, entity caller); + + void CommonCommand_teamstatus(float request, entity caller); + + void CommonCommand_time(float request, entity caller); + + void CommonCommand_timein(float request, entity caller); + + void CommonCommand_timeout(float request, entity caller); + + void CommonCommand_who(float request, entity caller, float argc); + + + // ================================== + // Macro system for common commands + // ================================== + + // Do not hard code aliases for these, instead create them in commands.cfg... also: keep in alphabetical order, please ;) + #define COMMON_COMMANDS(request,caller,arguments,command) \ + COMMON_COMMAND("cvar_changes", CommonCommand_cvar_changes(request, caller), "Prints a list of all changed server cvars") \ + COMMON_COMMAND("cvar_purechanges", CommonCommand_cvar_purechanges(request, caller), "Prints a list of all changed gameplay cvars") \ ++ COMMON_COMMAND("editmob", CommonCommand_editmob(request, caller, arguments), "Modifies a monster or all monsters") \ + COMMON_COMMAND("info", CommonCommand_info(request, caller, arguments), "Request for unique server information set up by admin") \ + COMMON_COMMAND("ladder", CommonCommand_ladder(request, caller), "Get information about top players if supported") \ + COMMON_COMMAND("lsmaps", CommonCommand_lsmaps(request, caller), "List maps which can be used with the current game mode") \ + COMMON_COMMAND("printmaplist", CommonCommand_printmaplist(request, caller), "Display full server maplist reply") \ + COMMON_COMMAND("rankings", CommonCommand_rankings(request, caller), "Print information about rankings") \ + COMMON_COMMAND("records", CommonCommand_records(request, caller), "List top 10 records for the current map") \ + COMMON_COMMAND("teamstatus", CommonCommand_teamstatus(request, caller), "Show information about player and team scores") \ + COMMON_COMMAND("time", CommonCommand_time(request, caller), "Print different formats/readouts of time") \ + COMMON_COMMAND("timein", CommonCommand_timein(request, caller), "Resume the game from being paused with a timeout") \ + COMMON_COMMAND("timeout", CommonCommand_timeout(request, caller), "Call a timeout which pauses the game for certain amount of time unless unpaused") \ + COMMON_COMMAND("vote", VoteCommand(request, caller, arguments, command), "Request an action to be voted upon by players") \ + COMMON_COMMAND("who", CommonCommand_who(request, caller, arguments), "Display detailed client information about all players") \ + /* nothing */ + + void CommonCommand_macro_help(entity caller) + { + #define COMMON_COMMAND(name,function,description) \ + { print_to(caller, strcat(" ^2", name, "^7: ", description)); } + + COMMON_COMMANDS(0, caller, 0, ""); + #undef COMMON_COMMAND + + return; + } + + float CommonCommand_macro_command(float argc, entity caller, string command) + { + #define COMMON_COMMAND(name,function,description) \ + { if(name == strtolower(argv(0))) { function; return true; } } + + COMMON_COMMANDS(CMD_REQUEST_COMMAND, caller, argc, command); + #undef COMMON_COMMAND + + return false; + } + + float CommonCommand_macro_usage(float argc, entity caller) + { + #define COMMON_COMMAND(name,function,description) \ + { if(name == strtolower(argv(1))) { function; return true; } } + + COMMON_COMMANDS(CMD_REQUEST_USAGE, caller, argc, ""); + #undef COMMON_COMMAND + + return false; + } + + void CommonCommand_macro_write_aliases(float fh) + { + #define COMMON_COMMAND(name,function,description) \ + { CMD_Write_Alias("qc_cmd_svcmd", name, description); } + + COMMON_COMMANDS(0, world, 0, ""); + #undef COMMON_COMMAND + + return; + } + + + #endif diff --cc qcsrc/server/command/sv_cmd.qc index f3d619d2f,7166ab7cf..fdc0b0659 --- a/qcsrc/server/command/sv_cmd.qc +++ b/qcsrc/server/command/sv_cmd.qc @@@ -19,16 -42,6 +42,16 @@@ void make_mapinfo_Think( } } +float GameCommand_checkinlist(string game_command, string list) +{ + string l = strcat(" ", list, " "); + + if(strstrofs(l, strcat(" ", game_command, " "), 0) >= 0) - return TRUE; ++ return true; + - return FALSE; ++ return false; +} + // used by GameCommand_extendmatchtime() and GameCommand_reducematchtime() void changematchtime(float delta, float mi, float ma) { @@@ -71,21 -84,6 +94,21 @@@ cvar_set("timelimit", ftos(new / 60)); } +void DiscoMode(entity e, float enable) +{ + if(e == world) + return; + + float accepted; + - accepted = VerifyClientEntity(e, TRUE, FALSE); ++ accepted = VerifyClientEntity(e, true, false); + + if(accepted > 0) + { + e.discomode = enable; + } +} + // ======================= // Command Sub-Functions @@@ -1516,31 -1491,8 +1540,31 @@@ void GameCommand_stuffto(float request { if(argv(2)) { + float accepted; + if(argv(1) == "all") + { + entity head; + + FOR_EACH_REALCLIENT(head) + { - accepted = VerifyClientEntity(head, TRUE, FALSE); ++ accepted = VerifyClientEntity(head, true, false); + if(accepted > 0) + { + stuffcmd(head, strcat("\n", argv(2), "\n")); + print(strcat("Command: \"", argv(2), "\" sent to ", GetCallerName(head), ".\n")); + continue; + } + else + { + print("stuffto failed on ", GetCallerName(head), ".\n"); + continue; + } + } + return; + } + entity client = GetIndexedEntity(argc, 1); - accepted = VerifyClientEntity(client, TRUE, FALSE); - float accepted = VerifyClientEntity(client, true, false); ++ accepted = VerifyClientEntity(client, true, false); if(accepted > 0) { @@@ -1573,73 -1525,6 +1597,73 @@@ #endif } +void GameCommand_discomode(float request, float argc) +{ + switch(request) + { + case CMD_REQUEST_COMMAND: + { + float accepted; + float enable; + string msg = ""; + + if ( argv(2) ) + { + enable = stof( argv(2) ); + if ( !enable ) + msg = " disabled"; + } + else + enable = 1; + + if(argv(1) == "all") + { + entity head; + + FOR_EACH_REALCLIENT(head) + { - accepted = VerifyClientEntity(head, TRUE, FALSE); ++ accepted = VerifyClientEntity(head, true, false); + if(accepted > 0) + { + DiscoMode(head,enable); + print(strcat("Disco Mode",msg," for ", GetCallerName(head), ".\n")); + continue; + } + else + { + print("Disco Mode failed on ", GetCallerName(head), ".\n"); + continue; + } + } + return; + } + + entity client = GetIndexedEntity(argc, 1); - accepted = VerifyClientEntity(client, TRUE, FALSE); ++ accepted = VerifyClientEntity(client, true, false); + + if(accepted > 0) + { + DiscoMode(client,enable); + print(strcat("Disco Mode",msg," for ", GetCallerName(client), ".\n")); + } + else + print("Disco Mode: ", GetClientErrorString(accepted, argv(1)), ".\n"); + + return; + } + + default: + print("Incorrect parameters for ^2discomode^7\n"); + case CMD_REQUEST_USAGE: + { + print("\nUsage:^3 sv_cmd discomode client [enable]\n"); + print(" 'client' is the entity number or name of the player,\n"); + print(" 'enable' 1 by default. 0 disable, 1 enable \n"); + return; + } + } +} + void GameCommand_trace(float request, float argc) { switch(request) diff --cc qcsrc/server/command/sv_cmd.qh index be19cbcf3,0cc520c2e..b2f444829 --- a/qcsrc/server/command/sv_cmd.qh +++ b/qcsrc/server/command/sv_cmd.qh @@@ -14,5 -17,4 +17,6 @@@ float shuffleteams_teams[SHUFFLETEAMS_M // used by common/command/generic.qc:GenericCommand_dumpcommands to list all commands into a .txt file void GameCommand_macro_write_aliases(float fh); + +void DiscoMode(entity e, float enable); + #endif diff --cc qcsrc/server/command/vote.qc index 8659fecf3,6edccafb1..8055d58ef --- a/qcsrc/server/command/vote.qc +++ b/qcsrc/server/command/vote.qc @@@ -816,18 -841,7 +838,18 @@@ void VoteCommand_call(float request, en bprint("\{1}^2* ^3", GetCallerName(vote_caller), "^2 calls a vote for ", vote_called_display, "\n"); if(autocvar_sv_eventlog) { GameLogEcho(strcat(":vote:vcall:", ftos(vote_caller.playerid), ":", vote_called_display)); } Nagger_VoteChanged(); + + if(autocvar_sv_vote_auto) + FOR_EACH_REALCLIENT(tmp_player) if(tmp_player != vote_caller) + switch(tmp_player.cvar_cl_autovote) + { + case "yes": tmp_player.vote_selection = VOTE_SELECT_ACCEPT; print_to(tmp_player, "You automatically accepted the vote\n"); break; + case "no": tmp_player.vote_selection = VOTE_SELECT_REJECT; print_to(tmp_player, "You automatically rejected the vote\n"); break; + case "abstain": tmp_player.vote_selection = VOTE_SELECT_ABSTAIN; print_to(tmp_player, "You automatically abstained from the vote\n"); break; + default: break; + } + - VoteCount(TRUE); // needed if you are the only one + VoteCount(true); // needed if you are the only one } return; @@@ -1097,16 -1111,12 +1119,16 @@@ void VoteCommand_macro_help(entity call #define VOTE_COMMAND(name,function,description,assignment) \ { if(Votecommand_check_assignment(caller, assignment)) { print_to(caller, strcat(" ^2", name, "^7: ", description)); } } - VOTE_COMMANDS(0, caller, 0, "") + VOTE_COMMANDS(0, caller, 0, ""); #undef VOTE_COMMAND + string vote_list = autocvar_sv_vote_commands; + if(argv(2) == "1") vote_list = strreplace(" ", "\n^3", vote_list); + print_to(caller, strcat("\nUsage:^3 ", command_origin, " vote COMMAND...^7, where possible commands are listed above.\n")); print_to(caller, strcat("For help about a specific command, type ", command_origin, " vote help COMMAND")); - print_to(caller, strcat("\n^7You can call a vote for or execute these commands: ^3", autocvar_sv_vote_commands, "^7 and maybe further ^3arguments^7")); + print_to(caller, "\n^7You can call a vote for or execute these commands (and maybe further ^3arguments^7):"); + print_to(caller, strcat("\n^3", vote_list, "^7\n")); } else // usage for individual command { diff --cc qcsrc/server/controlpoint.qc index d088e2e82,000000000..00de4e119 mode 100644,000000..100644 --- a/qcsrc/server/controlpoint.qc +++ b/qcsrc/server/controlpoint.qc @@@ -1,36 -1,0 +1,38 @@@ ++#include "controlpoint.qh" ++ +float cpicon_send(entity to, float sf) +{ + WriteByte(MSG_ENTITY, ENT_CLIENT_CONTROLPOINT_ICON); + WriteByte(MSG_ENTITY, sf); + if(sf & CPSF_SETUP) + { + WriteCoord(MSG_ENTITY, self.origin_x); + WriteCoord(MSG_ENTITY, self.origin_y); + WriteCoord(MSG_ENTITY, self.origin_z); + + WriteByte(MSG_ENTITY, self.health); + WriteByte(MSG_ENTITY, self.max_health); + WriteByte(MSG_ENTITY, self.count); + WriteByte(MSG_ENTITY, self.team); + WriteByte(MSG_ENTITY, self.owner.iscaptured); + } + + if(sf & CPSF_STATUS) + { + WriteByte(MSG_ENTITY, self.team); + + if(self.health <= 0) + WriteByte(MSG_ENTITY, 0); + else + WriteByte(MSG_ENTITY, ceil((self.health / self.max_health) * 255)); + } + - return TRUE; ++ return true; +} + +void onslaught_controlpoint_icon_link(entity e, void() spawnproc) +{ - Net_LinkEntity(e, TRUE, 0, cpicon_send); ++ Net_LinkEntity(e, true, 0, cpicon_send); + e.think = spawnproc; + e.nextthink = time * sys_frametime; +} diff --cc qcsrc/server/controlpoint.qh index e489f90ac,000000000..d76f0ea06 mode 100644,000000..100644 --- a/qcsrc/server/controlpoint.qh +++ b/qcsrc/server/controlpoint.qh @@@ -1,5 -1,0 +1,10 @@@ ++#ifndef CONTROLPOINT_H ++#define CONTROLPOINT_H ++ +const vector CPICON_MIN = '-32 -32 -9'; +const vector CPICON_MAX = '32 32 25'; + - float CPSF_STATUS = 4; - float CPSF_SETUP = 8; ++const int CPSF_STATUS = 4; ++const int CPSF_SETUP = 8; ++ ++#endif diff --cc qcsrc/server/defs.qh index 533b4ccb9,175179b50..c915e3573 --- a/qcsrc/server/defs.qh +++ b/qcsrc/server/defs.qh @@@ -448,30 -452,9 +453,30 @@@ float round_starttime; //point in time .float stat_sv_airspeedlimit_nonqw; .float stat_sv_maxspeed; +// new properties +.float stat_sv_jumpvelocity; +.float stat_sv_airaccel_qw_stretchfactor; +.float stat_sv_maxairstrafespeed; +.float stat_sv_maxairspeed; +.float stat_sv_airstrafeaccelerate; +.float stat_sv_warsowbunny_turnaccel; +.float stat_sv_airaccel_sideways_friction; +.float stat_sv_aircontrol; +.float stat_sv_aircontrol_power; +.float stat_sv_aircontrol_penalty; +.float stat_sv_warsowbunny_airforwardaccel; +.float stat_sv_warsowbunny_topspeed; +.float stat_sv_warsowbunny_accel; +.float stat_sv_warsowbunny_backtosideratio; +.float stat_sv_friction; +.float stat_sv_accelerate; +.float stat_sv_stopspeed; +.float stat_sv_airaccelerate; +.float stat_sv_airstopaccelerate; + void W_Porto_Remove (entity p); - .float projectiledeathtype; + .int projectiledeathtype; .string message2; @@@ -640,28 -623,4 +645,28 @@@ const int MIF_GUIDED_TAG = 128 .string playernick; .float elos; .float ranks; + - .float cvar_cl_sparkle; - .float cvar_cl_pony; - .float cvar_cl_pony_skin; - .float cvar_cl_damnfurries; - .float cvar_cl_thestars; - .float cvar_cl_robot; - .float cvar_cl_charge; ++.int cvar_cl_sparkle; ++.bool cvar_cl_pony; ++.int cvar_cl_pony_skin; ++.int cvar_cl_damnfurries; ++.bool cvar_cl_thestars; ++.bool cvar_cl_robot; ++.int cvar_cl_charge; + +.string cvar_cl_autovote; + +.entity lastkiller; +.entity lastkilled; + +.float vaporizer_refire; + - float sv_showfps; ++bool sv_showfps; + +.float clientfov; + - .float sub_target_used; ++.bool sub_target_used; + +.string cvar_cl_physics; + - .float skill; + #endif diff --cc qcsrc/server/g_damage.qc index 55bf4cb48,d170cf4c0..00c12703e --- a/qcsrc/server/g_damage.qc +++ b/qcsrc/server/g_damage.qc @@@ -1,7 -1,31 +1,33 @@@ - .float dmg; - .float dmg_edge; - .float dmg_force; - .float dmg_radius; + #include "g_damage.qh" + + #if defined(CSQC) + #elif defined(MENUQC) + #elif defined(SVQC) + #include "../warpzonelib/common.qh" + #include "../common/constants.qh" ++ #include "../common/buffs.qh" + #include "../common/teams.qh" + #include "../common/util.qh" + #include "../common/weapons/weapons.qh" + #include "weapons/accuracy.qh" + #include "weapons/csqcprojectile.qh" + #include "weapons/selection.qh" + #include "t_items.qh" + #include "autocvars.qh" + #include "constants.qh" + #include "defs.qh" ++ #include "../common/effects.qh" + #include "../common/notifications.qh" + #include "../common/deathtypes.qh" + #include "mutators/mutators_include.qh" - #include "tturrets/include/turrets_early.qh" - #include "vehicles/vehicles_def.qh" ++ #include "../common/turrets/sv_turrets.qh" ++ #include "../common/vehicles/sv_vehicles.qh" + #include "../csqcmodellib/sv_model.qh" + #include "../common/playerstats.qh" + #include "g_hook.qh" + #include "scores.qh" + #include "spawnpoints.qh" + #endif float Damage_DamageInfo_SendEntity(entity to, float sf) { @@@ -492,12 -499,8 +500,12 @@@ void Obituary(entity attacker, entity i ); } + float f3 = 0; + if(deathtype == DEATH_BUFF) + f3 = attacker.buffs; + - if (!Obituary_WeaponDeath(targ, TRUE, deathtype, targ.netname, attacker.netname, deathlocation, targ.killcount, kill_count_to_attacker)) - Obituary_SpecialDeath(targ, TRUE, deathtype, targ.netname, attacker.netname, deathlocation, targ.killcount, kill_count_to_attacker, f3); + if (!Obituary_WeaponDeath(targ, true, deathtype, targ.netname, attacker.netname, deathlocation, targ.killcount, kill_count_to_attacker)) - Obituary_SpecialDeath(targ, true, deathtype, targ.netname, attacker.netname, deathlocation, targ.killcount, kill_count_to_attacker, 0); ++ Obituary_SpecialDeath(targ, true, deathtype, targ.netname, attacker.netname, deathlocation, targ.killcount, kill_count_to_attacker, f3); } } @@@ -511,17 -514,13 +519,17 @@@ // For now, we're just forcing HURTTRIGGER to behave as "DEATH_VOID" and giving it no special options... // Later on you will only be able to make custom messages using DEATH_CUSTOM, // and there will be a REAL DEATH_VOID implementation which mappers will use. - /*case DEATH_HURTTRIGGER: + case DEATH_HURTTRIGGER: { - Obituary_SpecialDeath(targ, FALSE, deathtype, - s1 = targ.netname; - s2 = inflictor.message; - if(strstrofs(s2, "%", 0) < 0) { s2 = strcat("%s ", s2); } ++ Obituary_SpecialDeath(targ, false, deathtype, + targ.netname, + inflictor.message, + deathlocation, + targ.killcount, + 0, + 0); break; - }*/ + } case DEATH_CUSTOM: { @@@ -730,19 -724,19 +733,19 @@@ void Damage (entity targ, entity inflic if(autocvar_g_mirrordamage_virtual) { - vector v = healtharmor_applydamage(attacker.armorvalue, autocvar_g_balance_armor_blockpercent, deathtype, mirrordamage); + vector v = healtharmor_applydamage(attacker.armorvalue, autocvar_g_balance_armor_blockpercent, deathtype, mirrordamage, autocvar_g_balance_armor_block_bycount); - attacker.dmg_take += v_x; - attacker.dmg_save += v_y; + attacker.dmg_take += v.x; + attacker.dmg_save += v.y; attacker.dmg_inflictor = inflictor; - mirrordamage = v_z; + mirrordamage = v.z; mirrorforce = 0; } if(autocvar_g_friendlyfire_virtual) { - vector v = healtharmor_applydamage(targ.armorvalue, autocvar_g_balance_armor_blockpercent, deathtype, damage); + vector v = healtharmor_applydamage(targ.armorvalue, autocvar_g_balance_armor_blockpercent, deathtype, damage, autocvar_g_balance_armor_block_bycount); - targ.dmg_take += v_x; - targ.dmg_save += v_y; + targ.dmg_take += v.x; + targ.dmg_save += v.y; targ.dmg_inflictor = inflictor; damage = 0; if(!autocvar_g_friendlyfire_virtual_force) @@@ -793,17 -786,17 +796,17 @@@ } damage = 0; - force *= autocvar_g_freezetag_frozen_force; + force *= autocvar_g_freeze_frozen_force; } - - if(targ.frozen && deathtype == DEATH_HURTTRIGGER && !autocvar_g_freezetag_frozen_damage_trigger) + + if(targ.frozen && deathtype == DEATH_HURTTRIGGER && !autocvar_g_freeze_frozen_damage_trigger) { - pointparticles(particleeffectnum("teleport"), targ.origin, '0 0 0', 1); + Send_Effect(EFFECT_TELEPORT, targ.origin, '0 0 0', 1); - + entity oldself = self; self = targ; - entity spot = SelectSpawnPoint (FALSE); - + entity spot = SelectSpawnPoint (false); + if(spot) { damage = 0; @@@ -828,9 -821,9 +831,9 @@@ self.oldorigin = self.origin; self.prevorigin = self.origin; - pointparticles(particleeffectnum("teleport"), self.origin, '0 0 0', 1); + Send_Effect(EFFECT_TELEPORT, self.origin, '0 0 0', 1); } - + self = oldself; } @@@ -1295,9 -1300,9 +1288,9 @@@ void Fire_ApplyDamage(entity e e.fire_owner.damage_dealt = hi; e.fire_owner.typehitsound = ty; } - e.fire_hitsound = TRUE; + e.fire_hitsound = true; - if (!IS_INDEPENDENT_PLAYER(e)) + if(!IS_INDEPENDENT_PLAYER(e)) if(!e.frozen) FOR_EACH_PLAYER(other) if(e != other) { diff --cc qcsrc/server/g_damage.qh index 000000000,4cc3f6f63..a4cc95f89 mode 000000,100644..100644 --- a/qcsrc/server/g_damage.qh +++ b/qcsrc/server/g_damage.qh @@@ -1,0 -1,123 +1,123 @@@ + #ifndef G_DAMAGE_H + #define G_DAMAGE_H + + #if defined(CSQC) + #elif defined(MENUQC) + #elif defined(SVQC) + #include "../dpdefs/progsdefs.qh" + #include "../dpdefs/dpextensions.qh" + #include "../warpzonelib/common.qh" + #include "../common/constants.qh" + #include "../common/teams.qh" + #include "../common/util.qh" + #include "../common/weapons/weapons.qh" + #include "weapons/accuracy.qh" + #include "weapons/csqcprojectile.qh" + #include "weapons/selection.qh" + #include "t_items.qh" + #include "autocvars.qh" + #include "constants.qh" + #include "defs.qh" + #include "../common/notifications.qh" + #include "../common/deathtypes.qh" + #include "mutators/mutators_include.qh" - #include "tturrets/include/turrets_early.qh" - #include "vehicles/vehicles_def.qh" ++ #include "../common/turrets/sv_turrets.qh" ++ #include "../common/vehicles/sv_vehicles.qh" + #include "../csqcmodellib/sv_model.qh" + #include "../common/playerstats.qh" + #include "g_hook.qh" + #include "scores.qh" + #include "spawnpoints.qh" + #endif + + .float dmg; + .float dmg_edge; + .float dmg_force; + .float dmg_radius; + + float Damage_DamageInfo_SendEntity(entity to, float sf); + + void Damage_DamageInfo(vector org, float coredamage, float edgedamage, float rad, vector force, float deathtype, float bloodtype, entity dmgowner); + + float checkrules_firstblood; + + float yoda; + float damage_goodhits; + float damage_gooddamage; + + .float dmg_team; + .float teamkill_complain; + .float teamkill_soundtime; + .entity teamkill_soundsource; + .entity pusher; + .float istypefrag; + .float taunt_soundtime; + + float IsFlying(entity a); + + void UpdateFrags(entity player, float f); + + // NOTE: f=0 means still count as a (positive) kill, but count no frags for it + void W_SwitchWeapon_Force(entity e, float w); + entity GiveFrags_randomweapons; + void GiveFrags (entity attacker, entity targ, float f, float deathtype); + + string AppendItemcodes(string s, entity player); + + void LogDeath(string mode, float deathtype, entity killer, entity killed); + + void Obituary_SpecialDeath( + entity notif_target, + float murder, + float deathtype, + string s1, string s2, string s3, + float f1, float f2, float f3); + + float w_deathtype; + float Obituary_WeaponDeath( + entity notif_target, + float murder, + float deathtype, + string s1, string s2, string s3, + float f1, float f2); + + void Obituary(entity attacker, entity inflictor, entity targ, float deathtype); + + void Ice_Think(); + + void Freeze (entity targ, float freeze_time, float frozen_type, float show_waypoint); + + void Unfreeze (entity targ); + + // these are updated by each Damage call for use in button triggering and such + entity damage_targ; + entity damage_inflictor; + entity damage_attacker; + + void Damage (entity targ, entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force); + + float RadiusDamage_running; + float RadiusDamageForSource (entity inflictor, vector inflictororigin, vector inflictorvelocity, entity attacker, float coredamage, float edgedamage, float rad, entity cantbe, entity mustbe, float inflictorselfdamage, float forceintensity, float deathtype, entity directhitentity); + // Returns total damage applies to creatures + + float RadiusDamage (entity inflictor, entity attacker, float coredamage, float edgedamage, float rad, entity cantbe, entity mustbe, float forceintensity, float deathtype, entity directhitentity); + + .float fire_damagepersec; + .float fire_endtime; + .float fire_deathtype; + .entity fire_owner; + .float fire_hitsound; + .entity fire_burner; + + void fireburner_think(); + + float Fire_IsBurning(entity e); + + float Fire_AddDamage(entity e, entity o, float d, float t, float dt); + + void Fire_ApplyDamage(entity e); + + void Fire_ApplyEffect(entity e); + + void fireburner_think(); + #endif diff --cc qcsrc/server/g_hook.qc index 99294703c,310833e43..92a797679 --- a/qcsrc/server/g_hook.qc +++ b/qcsrc/server/g_hook.qc @@@ -1,3 -1,22 +1,24 @@@ + #if defined(CSQC) + #elif defined(MENUQC) + #elif defined(SVQC) + #include "../dpdefs/progsdefs.qh" + #include "../dpdefs/dpextensions.qh" + #include "../warpzonelib/common.qh" + #include "../warpzonelib/server.qh" + #include "../common/constants.qh" ++ #include "../common/effects.qh" + #include "../common/util.qh" + #include "../common/weapons/weapons.qh" + #include "autocvars.qh" + #include "constants.qh" + #include "defs.qh" - #include "vehicles/vehicles_def.qh" ++ #include "../common/vehicles/vehicles.qh" ++ #include "../common/vehicles/sv_vehicles.qh" + #include "command/common.qh" + #include "g_hook.qh" + #include "round_handler.qh" + #endif + /*============================================ Wazat's Xonotic Grappling Hook diff --cc qcsrc/server/g_triggers.qc index d3500da9a,9bb1e06e7..964bbfe86 --- a/qcsrc/server/g_triggers.qc +++ b/qcsrc/server/g_triggers.qc @@@ -172,9 -134,6 +173,9 @@@ void SUB_UseTargets_Ex(float preventReu other = otemp; } - void SUB_UseTargets() { SUB_UseTargets_Ex(FALSE); } - void SUB_UseTargets_PreventReuse() { SUB_UseTargets_Ex(TRUE); } ++void SUB_UseTargets() { SUB_UseTargets_Ex(false); } ++void SUB_UseTargets_PreventReuse() { SUB_UseTargets_Ex(true); } + //============================================================================= diff --cc qcsrc/server/g_world.qc index d05a458ea,0a2708340..d923ce0ec --- a/qcsrc/server/g_world.qc +++ b/qcsrc/server/g_world.qc @@@ -1,4 -1,41 +1,45 @@@ - #define LATENCY_THINKRATE 10 + #include "g_world.qh" + + #include "../common/buffs.qh" + + #if defined(CSQC) + #elif defined(MENUQC) + #elif defined(SVQC) + #include "../common/constants.qh" + #include "../common/stats.qh" + #include "../common/teams.qh" + #include "../common/util.qh" ++ #include "../common/effects.qh" + #include "../common/monsters/sv_monsters.qh" ++ #include "../common/monsters/monsters.qh" ++ #include "../common/turrets/turrets.qh" ++ #include "../common/vehicles/vehicles.qh" + #include "../common/weapons/weapons.qh" + #include "weapons/weaponstats.qh" + #include "autocvars.qh" + #include "constants.qh" + #include "defs.qh" + #include "../common/notifications.qh" + #include "mutators/mutators_include.qh" + #include "campaign.qh" + #include "../common/mapinfo.qh" + #include "command/common.qh" + #include "command/vote.qh" + #include "command/getreplies.qh" + #include "command/sv_cmd.qh" + #include "anticheat.qh" + #include "cheats.qh" + #include "../common/playerstats.qh" + #include "g_hook.qh" + #include "scores.qh" + #include "mapvoting.qh" + #include "ipban.qh" + #include "race.qh" + #include "antilag.qh" + #include "secret.qh" + #endif + + const float LATENCY_THINKRATE = 10; .float latency_sum; .float latency_cnt; .float latency_time; diff --cc qcsrc/server/generator.qc index 13aa35ab5,000000000..40cff619e mode 100644,000000..100644 --- a/qcsrc/server/generator.qc +++ b/qcsrc/server/generator.qc @@@ -1,35 -1,0 +1,37 @@@ ++#include "generator.qh" ++ +float generator_send(entity to, float sf) +{ + WriteByte(MSG_ENTITY, ENT_CLIENT_GENERATOR); + WriteByte(MSG_ENTITY, sf); + if(sf & GSF_SETUP) + { + WriteCoord(MSG_ENTITY, self.origin_x); + WriteCoord(MSG_ENTITY, self.origin_y); + WriteCoord(MSG_ENTITY, self.origin_z); + + WriteByte(MSG_ENTITY, self.health); + WriteByte(MSG_ENTITY, self.max_health); + WriteByte(MSG_ENTITY, self.count); + WriteByte(MSG_ENTITY, self.team); + } + + if(sf & GSF_STATUS) + { + WriteByte(MSG_ENTITY, self.team); + + if(self.health <= 0) + WriteByte(MSG_ENTITY, 0); + else + WriteByte(MSG_ENTITY, ceil((self.health / self.max_health) * 255)); + } + - return TRUE; ++ return true; +} + +void generator_link(void() spawnproc) +{ - Net_LinkEntity(self, TRUE, 0, generator_send); ++ Net_LinkEntity(self, true, 0, generator_send); + self.think = spawnproc; + self.nextthink = time; +} diff --cc qcsrc/server/generator.qh index a991874c4,000000000..1483255cd mode 100644,000000..100644 --- a/qcsrc/server/generator.qh +++ b/qcsrc/server/generator.qh @@@ -1,5 -1,0 +1,10 @@@ ++#ifndef GENERATOR_H ++#define GENERATOR_H ++ +const vector GENERATOR_MIN = '-52 -52 -14'; +const vector GENERATOR_MAX = '52 52 75'; + - float GSF_STATUS = 4; - float GSF_SETUP = 8; ++const int GSF_STATUS = 4; ++const int GSF_SETUP = 8; ++ ++#endif diff --cc qcsrc/server/jeff.qc index b3cca029c,000000000..6b967bca6 mode 100644,000000..100644 --- a/qcsrc/server/jeff.qc +++ b/qcsrc/server/jeff.qc @@@ -1,331 -1,0 +1,332 @@@ +// Modded announcer and random functions for the Jeff Resurrection servers +#ifdef JEFF ++#include "../common/buffs.qh" + +var float autocvar_jeff_announce_eagleeye_distance = 32768; +var float autocvar_jeff_announce_rocketscientist_count = 5; +var float autocvar_jeff_announce_shaftmaster_count = 5; +var float autocvar_jeff_announce_flakmaster_count = 5; +var float autocvar_jeff_announce_jackhammer_count = 5; +var float autocvar_jeff_announce_nodebuster_count = 5; +var float autocvar_jeff_announce_mutdestruct_time = 1.5; +var float autocvar_jeff_announce_multikill_time = 0.6; +var float autocvar_jeff_announce_roadrage_count = 6; +var float autocvar_jeff_announce_roadrampage_count = 12; +var float autocvar_jeff_announce_manslaughter_count = 20; +var float autocvar_jeff_announce_hattrick_count = 50; +var float autocvar_jeff_announce_denied_radius = 60; +var float autocvar_jeff_announce_bottomfeeder_count = 20; +var float autocvar_jeff_announce_wickedsick_count = 3; +var float autocvar_jeff_announce_bluestreak_distance = 400; +var float autocvar_jeff_announce_holy_distance = 800; +var float autocvar_jeff_announce_hitandrun_speed = 1000; +var float autocvar_jeff_announce_juggernaut_armor = 180; +var float autocvar_jeff_announce_juggernaut_health = 170; +var float autocvar_jeff_announce_slacker_time = 30; +var float autocvar_jeff_announce_comboking_count = 30; + +.float rocketkill_count; // counter for rocket scientist +.float arckill_count; // counter for shaft master +.float hagarkill_count; // counter for flak master +.float meleekill_count; // counter for flak master +.float electrokill_count; // counter for flak master +.float vkill_count; // counter for vehicle kills +.float minipickup_count; // counter for bottom feeder + +.float lastkiller_weapon; +.float lastkiller_time; +.float lastkilled_time; +.float lastkilled_flying; // actually a counter + +.float jeff_weaponswitch_count; + +.float annce_count; +.float last_announcer; + +float Fire_IsBurning(entity e); // defined later + +void jeff_Announcer_Send(entity player, float toall, float announce) +{ + if(player == world || !IS_PLAYER(player)) { return; } + if(player.last_announcer > time) { return; } + + player.last_announcer = time + 0.3; // avoid spam + player.annce_count += 1; + + //dprint("Sending an announcement to player ", player.netname, "\n"); + + if(player.annce_count == autocvar_jeff_announce_hattrick_count) + { + Send_Notification(NOTIF_ONE, player, MSG_ANNCE, ANNCE_JEFF_HATTRICK); + return; + } + + Send_Notification(((toall) ? NOTIF_ALL : NOTIF_ONE), ((toall) ? world : player), MSG_ANNCE, announce); +} + +void jeff_Announcer_PlayerDies(entity attacker, float deathtype, entity targ, entity inflictor) +{ + if(attacker == targ) { return; } // don't play announcements for self kills (yet) + if(!IS_PLAYER(targ) || !targ) { return; } // don't play announcements for non-player kills + + // set required values + float death_weapon = DEATH_WEAPONOF(deathtype); + float targ_flying = !(targ.flags & FL_ONGROUND); + float attacker_flying = !(attacker.flags & FL_ONGROUND); - float denied = FALSE; entity head; - float bluestreak = FALSE; ++ float denied = false; entity head; ++ float bluestreak = false; + float killed = 0; + float player_count = 0, best_killcount = 0; + float wepcount = 0, mywepcount = 0; + float i; + + for(i = WEP_FIRST; i <= WEP_LAST; ++i) + { + if(weaponsInMap & WepSet_FromWeapon(i)) + { + if(attacker.weapons & WepSet_FromWeapon(i)) + ++mywepcount; + ++wepcount; + } + } + + FOR_EACH_PLAYER(head) + { + if(head != attacker) + if(head.lastkiller == attacker) + if(head.lastkiller_time - time <= 0.1) // killed within 0.1 seconds by this attacker + ++killed; + + ++player_count; + } + + if(player_count > 5) + FOR_EACH_PLAYER(head) + { + if(head != attacker) + if(head.killcount >= 1) + if(!best_killcount || head.killcount >= best_killcount) + best_killcount = head.killcount; + } + + if(!IS_PLAYER(attacker)) + FOR_EACH_PLAYER(head) + if(vlen(head.origin - targ.origin) <= autocvar_jeff_announce_bluestreak_distance) + { - bluestreak = TRUE; ++ bluestreak = true; + break; + } + + if(DEATH_ISWEAPON(deathtype, WEP_DEVASTATOR)) { attacker.rocketkill_count += 1; } + else { attacker.rocketkill_count = 0; } + + if(DEATH_ISWEAPON(deathtype, WEP_HAGAR)) { attacker.hagarkill_count += 1; } + else { attacker.hagarkill_count = 0; } + + if(DEATH_ISWEAPON(deathtype, WEP_ARC)) { attacker.arckill_count += 1; } + else { attacker.arckill_count = 0; } + + if((DEATH_ISWEAPON(deathtype, WEP_SHOTGUN) || DEATH_ISWEAPON(deathtype, WEP_SHOCKWAVE)) && (deathtype & HITTYPE_SECONDARY)) { attacker.meleekill_count += 1; } + else { attacker.meleekill_count = 0; } + + if(DEATH_ISWEAPON(deathtype, WEP_ELECTRO)) { attacker.electrokill_count += 1; } + else { attacker.electrokill_count = 0; } + + if(attacker.vehicle) { attacker.vkill_count += 1; } + else { attacker.vkill_count = 0; } + + if(attacker_flying) { attacker.lastkilled_flying += 1; } + else { attacker.lastkilled_flying = 0; } + + targ.lastkiller_weapon = death_weapon; + targ.lastkiller_time = time; + + for(head = findradius(targ.origin, autocvar_jeff_announce_denied_radius); head; head = head.chain) + { + float avail = (head.ItemStatus & ITS_AVAILABLE); + if( (head.classname == "item_health_mega" && avail) + || (head.classname == "item_armor_large" && avail) + || (head.classname == "item_flag_team" && (CTF_DIFFTEAM(head, targ) || targ.flagcarried)) + || (avail && (head.weapon == WEP_VORTEX || head.weapon == WEP_DEVASTATOR || (head.weapon == WEP_VAPORIZER && !g_instagib))) + ) + { - denied = TRUE; ++ denied = true; + break; + } + } + + // play the announcements + if(attacker.meleekill_count == autocvar_jeff_announce_jackhammer_count) + if((DEATH_ISWEAPON(deathtype, WEP_SHOTGUN) || DEATH_ISWEAPON(deathtype, WEP_SHOCKWAVE)) && (deathtype & HITTYPE_SECONDARY)) - jeff_Announcer_Send(attacker, TRUE, ANNCE_JEFF_JACKHAMMER); ++ jeff_Announcer_Send(attacker, true, ANNCE_JEFF_JACKHAMMER); + + if(DEATH_ISWEAPON(deathtype, WEP_BLASTER) || ((DEATH_ISWEAPON(deathtype, WEP_SHOTGUN) || DEATH_ISWEAPON(deathtype, WEP_SHOCKWAVE)) && (deathtype & HITTYPE_SECONDARY))) - jeff_Announcer_Send(attacker, FALSE, ANNCE_JEFF_HUMILIATION); ++ jeff_Announcer_Send(attacker, false, ANNCE_JEFF_HUMILIATION); + + if(deathtype == DEATH_FALL && IS_PLAYER(attacker)) - jeff_Announcer_Send(attacker, FALSE, ANNCE_JEFF_PANCAKE); ++ jeff_Announcer_Send(attacker, false, ANNCE_JEFF_PANCAKE); + + if(SAME_TEAM(attacker, targ)) + if(attacker.lastkiller == targ) - jeff_Announcer_Send(attacker, FALSE, ANNCE_JEFF_RETRIBUTION); ++ jeff_Announcer_Send(attacker, false, ANNCE_JEFF_RETRIBUTION); + else - jeff_Announcer_Send(attacker, TRUE, ANNCE_JEFF_TEAMKILLER); ++ jeff_Announcer_Send(attacker, true, ANNCE_JEFF_TEAMKILLER); + + if(vlen(attacker.origin - targ.origin) > autocvar_jeff_announce_eagleeye_distance) - jeff_Announcer_Send(attacker, FALSE, ANNCE_JEFF_EAGLEEYE); ++ jeff_Announcer_Send(attacker, false, ANNCE_JEFF_EAGLEEYE); + + if(attacker.lastkiller == targ) + if(attacker.lastkiller_weapon == death_weapon) - jeff_Announcer_Send(attacker, FALSE, ANNCE_JEFF_PAYBACK); ++ jeff_Announcer_Send(attacker, false, ANNCE_JEFF_PAYBACK); + else if(player_count > 2) - jeff_Announcer_Send(attacker, FALSE, ANNCE_JEFF_VENGEANCE); ++ jeff_Announcer_Send(attacker, false, ANNCE_JEFF_VENGEANCE); + + if(attacker.rocketkill_count == autocvar_jeff_announce_rocketscientist_count && DEATH_ISWEAPON(deathtype, WEP_DEVASTATOR)) - jeff_Announcer_Send(attacker, FALSE, ANNCE_JEFF_ROCKETSCIENTIST); ++ jeff_Announcer_Send(attacker, false, ANNCE_JEFF_ROCKETSCIENTIST); + + if(attacker.arckill_count == autocvar_jeff_announce_shaftmaster_count && DEATH_ISWEAPON(deathtype, WEP_ARC)) - jeff_Announcer_Send(attacker, FALSE, ANNCE_JEFF_SHAFTMASTER); ++ jeff_Announcer_Send(attacker, false, ANNCE_JEFF_SHAFTMASTER); + + if(time - attacker.lastkiller_time <= autocvar_jeff_announce_mutdestruct_time) + if(time - targ.lastkiller_time <= autocvar_jeff_announce_mutdestruct_time) + if(attacker.lastkiller == targ && targ.lastkiller == attacker) + { - jeff_Announcer_Send(attacker, TRUE, ANNCE_JEFF_MUTDESTRUCT); - //jeff_Announcer_Send(targ, FALSE, ANNCE_JEFF_MUTDESTRUCT); ++ jeff_Announcer_Send(attacker, true, ANNCE_JEFF_MUTDESTRUCT); ++ //jeff_Announcer_Send(targ, false, ANNCE_JEFF_MUTDESTRUCT); + } + + if(time - attacker.lastkilled_time <= autocvar_jeff_announce_multikill_time && time - attacker.lastkilled_time > 0.2) // don't do this for real multikills + if(attacker.lastkilled_flying == autocvar_jeff_announce_wickedsick_count) - jeff_Announcer_Send(attacker, TRUE, ANNCE_JEFF_NUKEMHOLY); ++ jeff_Announcer_Send(attacker, true, ANNCE_JEFF_NUKEMHOLY); + else - jeff_Announcer_Send(attacker, FALSE, ANNCE_JEFF_MULTIKILL); ++ jeff_Announcer_Send(attacker, false, ANNCE_JEFF_MULTIKILL); + + if(attacker.vehicle && deathtype == DEATH_VH_CRUSH && attacker.vkill_count && !!(attacker.vkill_count % 2)) - jeff_Announcer_Send(attacker, FALSE, ANNCE_JEFF_ROADKILL); ++ jeff_Announcer_Send(attacker, false, ANNCE_JEFF_ROADKILL); + + if(attacker.vehicle && attacker.vkill_count == autocvar_jeff_announce_roadrage_count) - jeff_Announcer_Send(attacker, FALSE, ANNCE_JEFF_ROADRAGE); ++ jeff_Announcer_Send(attacker, false, ANNCE_JEFF_ROADRAGE); + + if(attacker.vehicle && attacker.vkill_count == autocvar_jeff_announce_roadrampage_count) - jeff_Announcer_Send(attacker, FALSE, ANNCE_JEFF_ROADRAMPAGE); ++ jeff_Announcer_Send(attacker, false, ANNCE_JEFF_ROADRAMPAGE); + + if(attacker.vehicle && attacker.vkill_count == autocvar_jeff_announce_manslaughter_count) - jeff_Announcer_Send(attacker, TRUE, ANNCE_JEFF_MANSLAUGHTER); ++ jeff_Announcer_Send(attacker, true, ANNCE_JEFF_MANSLAUGHTER); + + if(attacker_flying && targ_flying) + if(DEATH_ISWEAPON(deathtype, WEP_DEVASTATOR) && !(deathtype & HITTYPE_SPLASH)) + if(vlen(targ.origin - attacker.origin) >= autocvar_jeff_announce_holy_distance) - jeff_Announcer_Send(attacker, TRUE, ANNCE_JEFF_HOLY); ++ jeff_Announcer_Send(attacker, true, ANNCE_JEFF_HOLY); + + if(denied) - jeff_Announcer_Send(attacker, FALSE, ANNCE_JEFF_DENIED); ++ jeff_Announcer_Send(attacker, false, ANNCE_JEFF_DENIED); + + if(inflictor.realowner == attacker && inflictor.jeff_projowner == targ) // killed by their own projectile - jeff_Announcer_Send(targ, FALSE, ANNCE_JEFF_REJECTED); ++ jeff_Announcer_Send(targ, false, ANNCE_JEFF_REJECTED); + + if(attacker.minipickup_count >= autocvar_jeff_announce_bottomfeeder_count) - jeff_Announcer_Send(attacker, FALSE, ANNCE_JEFF_BOTTOMFEEDER); ++ jeff_Announcer_Send(attacker, false, ANNCE_JEFF_BOTTOMFEEDER); + + if(attacker.lastkilled_flying == autocvar_jeff_announce_wickedsick_count) - jeff_Announcer_Send(attacker, FALSE, ANNCE_JEFF_WICKEDSICK); ++ jeff_Announcer_Send(attacker, false, ANNCE_JEFF_WICKEDSICK); + + if(deathtype == DEATH_SLIME && IS_PLAYER(attacker)) - jeff_Announcer_Send(attacker, TRUE, ANNCE_JEFF_BIOHAZARD); ++ jeff_Announcer_Send(attacker, true, ANNCE_JEFF_BIOHAZARD); + + if(Fire_IsBurning(attacker) && deathtype == DEATH_FIRE) - jeff_Announcer_Send(attacker, FALSE, ANNCE_JEFF_BLAZEOFGLORY); ++ jeff_Announcer_Send(attacker, false, ANNCE_JEFF_BLAZEOFGLORY); + + if(bluestreak) - jeff_Announcer_Send(attacker, FALSE, ANNCE_JEFF_BLUESTREAK); ++ jeff_Announcer_Send(attacker, false, ANNCE_JEFF_BLUESTREAK); + + if(vlen(attacker.velocity) >= autocvar_jeff_announce_hitandrun_speed) - jeff_Announcer_Send(attacker, FALSE, ANNCE_JEFF_HITANDRUN); ++ jeff_Announcer_Send(attacker, false, ANNCE_JEFF_HITANDRUN); + + if(attacker.armorvalue >= autocvar_jeff_announce_juggernaut_armor && attacker.health >= autocvar_jeff_announce_juggernaut_health) - jeff_Announcer_Send(attacker, TRUE, ANNCE_JEFF_JUGGERNAUT); ++ jeff_Announcer_Send(attacker, true, ANNCE_JEFF_JUGGERNAUT); + + switch(killed) + { - case 2: jeff_Announcer_Send(attacker, FALSE, ANNCE_JEFF_DOUBLEKILL); break; - case 3: jeff_Announcer_Send(attacker, FALSE, ANNCE_JEFF_MULTIKILL); break; - case 4: jeff_Announcer_Send(attacker, TRUE, ANNCE_JEFF_MEGAKILL); break; - case 5: jeff_Announcer_Send(attacker, TRUE, ANNCE_JEFF_ULTRAKILL); break; - case 6: jeff_Announcer_Send(attacker, TRUE, ANNCE_JEFF_MONSTERKILL); break; - case 7: jeff_Announcer_Send(attacker, TRUE, ANNCE_JEFF_LUDICROUSKILL); break; - case 10: jeff_Announcer_Send(attacker, TRUE, ANNCE_JEFF_GODLIKE); break; ++ case 2: jeff_Announcer_Send(attacker, false, ANNCE_JEFF_DOUBLEKILL); break; ++ case 3: jeff_Announcer_Send(attacker, false, ANNCE_JEFF_MULTIKILL); break; ++ case 4: jeff_Announcer_Send(attacker, true, ANNCE_JEFF_MEGAKILL); break; ++ case 5: jeff_Announcer_Send(attacker, true, ANNCE_JEFF_ULTRAKILL); break; ++ case 6: jeff_Announcer_Send(attacker, true, ANNCE_JEFF_MONSTERKILL); break; ++ case 7: jeff_Announcer_Send(attacker, true, ANNCE_JEFF_LUDICROUSKILL); break; ++ case 10: jeff_Announcer_Send(attacker, true, ANNCE_JEFF_GODLIKE); break; + } + + if(attacker.hagarkill_count == autocvar_jeff_announce_flakmaster_count && DEATH_ISWEAPON(deathtype, WEP_HAGAR)) - jeff_Announcer_Send(attacker, TRUE, ANNCE_JEFF_FLAKMASTER); ++ jeff_Announcer_Send(attacker, true, ANNCE_JEFF_FLAKMASTER); + + if(attacker.electrokill_count == autocvar_jeff_announce_nodebuster_count && DEATH_ISWEAPON(deathtype, WEP_ELECTRO)) - jeff_Announcer_Send(attacker, TRUE, ANNCE_JEFF_NODEBUSTER); ++ jeff_Announcer_Send(attacker, true, ANNCE_JEFF_NODEBUSTER); + + if(attacker.lastkilled_time - time >= autocvar_jeff_announce_slacker_time) - jeff_Announcer_Send(attacker, TRUE, ANNCE_JEFF_SLACKER); ++ jeff_Announcer_Send(attacker, true, ANNCE_JEFF_SLACKER); + + if(attacker.killcount >= 1) + if(best_killcount == attacker.killcount - 1) // attacker just took the lead (TODO: make sure this isn't spammed) - jeff_Announcer_Send(attacker, TRUE, ANNCE_JEFF_TOPGUN); ++ jeff_Announcer_Send(attacker, true, ANNCE_JEFF_TOPGUN); + + if(attacker.jeff_weaponswitch_count >= autocvar_jeff_announce_comboking_count) - jeff_Announcer_Send(attacker, FALSE, ANNCE_JEFF_COMBOKING); ++ jeff_Announcer_Send(attacker, false, ANNCE_JEFF_COMBOKING); + + if(mywepcount >= wepcount) - jeff_Announcer_Send(attacker, FALSE, ANNCE_JEFF_GUNSLINGER); ++ jeff_Announcer_Send(attacker, false, ANNCE_JEFF_GUNSLINGER); + + if(targ_flying && attacker_flying && (DEATH_ISWEAPON(deathtype, WEP_HAGAR) || DEATH_ISWEAPON(deathtype, WEP_SEEKER) || DEATH_ISWEAPON(deathtype, WEP_CRYLINK))) - jeff_Announcer_Send(attacker, FALSE, ANNCE_JEFF_OUTSTANDING); ++ jeff_Announcer_Send(attacker, false, ANNCE_JEFF_OUTSTANDING); + + if(targ.buffs & BUFF_SPEED) - jeff_Announcer_Send(attacker, TRUE, ANNCE_JEFF_SPEED); ++ jeff_Announcer_Send(attacker, true, ANNCE_JEFF_SPEED); + + + attacker.jeff_weaponswitch_count = 0; + attacker.lastkilled_time = time; +} + +void jeff_Announcer_MatchEnd() +{ + Send_Notification(NOTIF_ALL, world, MSG_ANNCE, ANNCE_JEFF_GAMEOVER); +} + +void jeff_Announcer_VehicleEnter(entity player, entity veh) +{ + if(DIFF_TEAM(player, veh)) - jeff_Announcer_Send(player, TRUE, ANNCE_JEFF_HIJACKED); ++ jeff_Announcer_Send(player, true, ANNCE_JEFF_HIJACKED); +} + +void jeff_Announcer_ItemTouch(entity player, entity item) +{ + if(item.classname == "item_health_small" || item.classname == "item_armor_small") + player.minipickup_count += 1; + else + player.minipickup_count -= 1; +} + +void jeff_Announcer_FireBullet(entity targ, entity ent, vector hitloc, vector start, vector end) +{ + if(ent.weapon != WEP_RIFLE && ent.weapon != WEP_VAPORIZER && ent.weapon != WEP_VORTEX) + return; + if(!IS_PLAYER(targ)) + return; + if(!Player_Trapped(targ) || !targ.takedamage) + return; + vector headmins, headmaxs, org; + org = antilag_takebackorigin(targ, time - ANTILAG_LATENCY(ent)); + headmins = org - '5 5 10'; + headmaxs = org + '5 5 10'; + if(trace_hits_box(start, end, headmins, headmaxs)) - jeff_Announcer_Send(ent, FALSE, ANNCE_JEFF_BULLSEYE); ++ jeff_Announcer_Send(ent, false, ANNCE_JEFF_BULLSEYE); +} + +#else + +void jeff_Announcer_PlayerDies(entity attacker, float deathtype, entity targ, entity inflictor) { } +void jeff_Announcer_MatchEnd() { } +void jeff_Announcer_VehicleEnter(entity player, entity veh) { } +void jeff_Announcer_ItemTouch(entity player, entity item) { } +void jeff_Announcer_FireBullet(entity targ, entity ent, vector hitloc, vector start, vector end) { } + +#endif diff --cc qcsrc/server/jeff.qh index 000000000,000000000..a0ad2f749 new file mode 100644 --- /dev/null +++ b/qcsrc/server/jeff.qh @@@ -1,0 -1,0 +1,14 @@@ ++#ifndef SV_JEFF_H ++#define SV_JEFF_H ++ ++void jeff_Announcer_MatchEnd(); ++ ++void jeff_Announcer_PlayerDies(entity attacker, float deathtype, entity targ, entity inflictor); ++ ++void jeff_Announcer_VehicleEnter(entity player, entity veh); ++ ++void jeff_Announcer_ItemTouch(entity player, entity item); ++ ++void jeff_Announcer_FireBullet(entity targ, entity ent, vector hitloc, vector start, vector end); ++ ++#endif diff --cc qcsrc/server/miscfunctions.qc index a8323e928,d998ea799..9057014d6 --- a/qcsrc/server/miscfunctions.qc +++ b/qcsrc/server/miscfunctions.qc @@@ -1,31 -1,35 +1,57 @@@ - var void remove(entity e); - void objerror(string s); - void droptofloor(); - .vector dropped_origin; + #if defined(CSQC) + #elif defined(MENUQC) + #elif defined(SVQC) + #include "miscfunctions.qh" + #include "../dpdefs/progsdefs.qh" + #include "../dpdefs/dpextensions.qh" + #include "../common/playerstats.qh" + #include "../warpzonelib/anglestransform.qh" + #include "../warpzonelib/server.qh" + #include "../common/constants.qh" + #include "../common/teams.qh" + #include "../common/util.qh" + #include "../common/urllib.qh" + #include "../common/command/generic.qh" + #include "../common/weapons/weapons.qh" + #include "weapons/accuracy.qh" + #include "weapons/csqcprojectile.qh" + #include "weapons/selection.qh" + #include "t_items.qh" + #include "autocvars.qh" + #include "constants.qh" + #include "defs.qh" + #include "../common/notifications.qh" + #include "../common/deathtypes.qh" + #include "mutators/mutators_include.qh" - #include "tturrets/include/turrets_early.qh" ++ #include "../common/turrets/sv_turrets.qh" + #include "../common/mapinfo.qh" + #include "command/common.qh" + #include "../csqcmodellib/sv_model.qh" + #include "ipban.qh" + #endif - float Player_Trapped(entity player) ++int Player_Trapped(entity player) +{ + // 0: free, 1: confined, but free movement, 2: confined and unable to move + if(player.frozen) + return 2; + if(player.deadflag != DEAD_NO) + return 2; // technically speaking, player is kinda trapped in limbo (TODO: make sure checking this doesn't break anything) + if(player.jb_isprisoned) + return 1; + return 0; +} + - float checkinlist(string command, string list) ++bool checkinlist(string command, string list) +{ + string l = strcat(" ", list, " "); + + if(strstrofs(l, strcat(" ", command, " "), 0) >= 0) - return TRUE; ++ return true; + - return FALSE; ++ return false; +} + - void traceline_antilag (entity source, vector v1, vector v2, float nomonst, entity forent, float lag); void crosshair_trace(entity pl) { traceline_antilag(pl, pl.cursor_trace_start, pl.cursor_trace_start + normalize(pl.cursor_trace_endpos - pl.cursor_trace_start) * MAX_SHOT_DISTANCE, MOVE_NORMAL, pl, ANTILAG_LATENCY(pl)); @@@ -51,12 -54,15 +76,8 @@@ void WarpZone_crosshair_trace(entity pl WarpZone_traceline_antilag(pl, pl.cursor_trace_start, pl.cursor_trace_start + normalize(pl.cursor_trace_endpos - pl.cursor_trace_start) * MAX_SHOT_DISTANCE, MOVE_NORMAL, pl, ANTILAG_LATENCY(pl)); } - string GetMapname(); // why... -- -string admin_name(void) -{ - if(autocvar_sv_adminnick != "") - return autocvar_sv_adminnick; - else - return "SERVER ADMIN"; -} +string admin_name() { return (autocvar_sv_adminnick != "") ? autocvar_sv_adminnick : "SERVER ADMIN"; } - float DistributeEvenly_amount; - float DistributeEvenly_totalweight; void DistributeEvenly_Init(float amount, float totalweight) { if (DistributeEvenly_amount) @@@ -91,41 -97,7 +112,6 @@@ float DistributeEvenly_GetRandomized(fl return f; } - #define move_out_of_solid(e) WarpZoneLib_MoveOutOfSolid(e) - - const string STR_PLAYER = "player"; - const string STR_SPECTATOR = "spectator"; - const string STR_OBSERVER = "observer"; - - #define IS_PLAYER(v) (v.classname == STR_PLAYER) - #define IS_SPEC(v) (v.classname == STR_SPECTATOR) - #define IS_OBSERVER(v) (v.classname == STR_OBSERVER) - #define IS_CLIENT(v) (v.flags & FL_CLIENT) - #define IS_BOT_CLIENT(v) (clienttype(v) == CLIENTTYPE_BOT) - #define IS_REAL_CLIENT(v) (clienttype(v) == CLIENTTYPE_REAL) - #define IS_NOT_A_CLIENT(v) (clienttype(v) == CLIENTTYPE_NOTACLIENT) - #define IS_TURRET(v) (v.turret_flags & TUR_FLAG_ISTURRET) - #define IS_VEHICLE(v) (v.vehicle_flags & VHF_ISVEHICLE) - #define IS_MONSTER(v) (v.flags & FL_MONSTER) - - #define FOR_EACH_CLIENTSLOT(v) for(v = world; (v = nextent(v)) && (num_for_edict(v) <= maxclients); ) - #define FOR_EACH_CLIENT(v) FOR_EACH_CLIENTSLOT(v) if(IS_CLIENT(v)) - #define FOR_EACH_REALCLIENT(v) FOR_EACH_CLIENT(v) if(IS_REAL_CLIENT(v)) - - #define FOR_EACH_PLAYER(v) FOR_EACH_CLIENT(v) if(IS_PLAYER(v)) - #define FOR_EACH_SPEC(v) FOR_EACH_CLIENT(v) if (!IS_PLAYER(v)) // Samual: shouldn't this be IS_SPEC(v)? and rather create a separate macro to include observers too - #define FOR_EACH_REALPLAYER(v) FOR_EACH_REALCLIENT(v) if(IS_PLAYER(v)) - - #define FOR_EACH_MONSTER(v) for(v = world; (v = findflags(v, flags, FL_MONSTER)) != world; ) - - #define CENTER_OR_VIEWOFS(ent) (ent.origin + (IS_PLAYER(ent) ? ent.view_ofs : ((ent.mins + ent.maxs) * 0.5))) - - // copies a string to a tempstring (so one can strunzone it) - string strcat1(string s) = #115; // FRIK_FILE - - float logfile_open; - float logfile; -- void GameLogEcho(string s) { string fn; @@@ -566,114 -507,44 +526,36 @@@ string playername(entity p return p.netname; } - //#NO AUTOCVARS START - - float g_pickup_shells; - float g_pickup_shells_max; - float g_pickup_nails; - float g_pickup_nails_max; - float g_pickup_rockets; - float g_pickup_rockets_max; - float g_pickup_cells; - float g_pickup_cells_max; - float g_pickup_plasma; - float g_pickup_plasma_max; - float g_pickup_fuel; - float g_pickup_fuel_jetpack; - float g_pickup_fuel_max; - float g_pickup_armorsmall; - float g_pickup_armorsmall_max; - float g_pickup_armorsmall_anyway; - float g_pickup_armormedium; - float g_pickup_armormedium_max; - float g_pickup_armormedium_anyway; - float g_pickup_armorbig; - float g_pickup_armorbig_max; - float g_pickup_armorbig_anyway; - float g_pickup_armorlarge; - float g_pickup_armorlarge_max; - float g_pickup_armorlarge_anyway; - float g_pickup_healthsmall; - float g_pickup_healthsmall_max; - float g_pickup_healthsmall_anyway; - float g_pickup_healthmedium; - float g_pickup_healthmedium_max; - float g_pickup_healthmedium_anyway; - float g_pickup_healthlarge; - float g_pickup_healthlarge_max; - float g_pickup_healthlarge_anyway; - float g_pickup_healthmega; - float g_pickup_healthmega_max; - float g_pickup_healthmega_anyway; - float g_pickup_ammo_anyway; - float g_pickup_weapons_anyway; - float g_weaponarena; - WepSet g_weaponarena_weapons; - float g_weaponarena_random; - float g_weaponarena_random_with_blaster; - string g_weaponarena_list; - float g_weaponspeedfactor; - float g_weaponratefactor; - float g_weapondamagefactor; - float g_weaponforcefactor; - float g_weaponspreadfactor; - - WepSet start_weapons; - WepSet start_weapons_default; - WepSet start_weapons_defaultmask; - float start_items; - float start_ammo_shells; - float start_ammo_nails; - float start_ammo_rockets; - float start_ammo_cells; - float start_ammo_plasma; - float start_ammo_fuel; - float start_health; - float start_armorvalue; - WepSet warmup_start_weapons; - WepSet warmup_start_weapons_default; - WepSet warmup_start_weapons_defaultmask; - #define WARMUP_START_WEAPONS ((g_warmup_allguns == 1) ? (warmup_start_weapons & (weaponsInMap | start_weapons)) : warmup_start_weapons) - float warmup_start_ammo_shells; - float warmup_start_ammo_nails; - float warmup_start_ammo_rockets; - float warmup_start_ammo_cells; - float warmup_start_ammo_plasma; - float warmup_start_ammo_fuel; - float warmup_start_health; - float warmup_start_armorvalue; - float g_weapon_stay; - - float want_weapon(entity weaponinfo, float allguns) // WEAPONTODO: what still needs done? -vector randompos(vector m1, vector m2) --{ - var float i = weaponinfo.weapon; - var float d = 0; - vector v; - m2 = m2 - m1; - v.x = m2_x * random() + m1_x; - v.y = m2_y * random() + m1_y; - v.z = m2_z * random() + m1_z; - return v; -} - -float want_weapon(entity weaponinfo, float allguns) // WEAPONTODO: what still needs done? ++int want_weapon(entity weaponinfo, bool allguns) // WEAPONTODO: what still needs done? + { + int i = weaponinfo.weapon; + int d = 0; if (!i) return 0; - if (g_lms || g_ca || allguns) + if (allguns) { if(weaponinfo.spawnflags & WEP_FLAG_NORMAL) - d = TRUE; + d = true; else - d = FALSE; + d = false; } - else if (g_cts) - d = (i == WEP_SHOTGUN); - else if (g_nexball) - d = 0; // weapon is set a few lines later else d = !(!weaponinfo.weaponstart); + ret_float = d; + other = weaponinfo; + MUTATOR_CALLHOOK(WantWeapon); + d = ret_float; + weaponinfo = other; + if(g_grappling_hook) // if possible, redirect off-hand hook to on-hand hook d |= (i == WEP_HOOK); - if(!g_cts && (weaponinfo.spawnflags & WEP_FLAG_MUTATORBLOCKED)) // never default mutator blocked guns + if(weaponinfo.spawnflags & WEP_FLAG_MUTATORBLOCKED) // never default mutator blocked guns d = 0; - var float t = weaponinfo.weaponstartoverride; - float t = weaponinfo.weaponstartoverride; ++ int t = weaponinfo.weaponstartoverride; //print(strcat("want_weapon: ", weaponinfo.netname, " - d: ", ftos(d), ", t: ", ftos(t), ". \n")); @@@ -2389,12 -2045,10 +2073,12 @@@ float LostMovetypeFollow(entity ent float isPushable(entity e) { - if(e.iscreature) - return true; if(e.pushable) - return TRUE; + return true; + if(IS_VEHICLE(e)) - return FALSE; ++ return false; + if(e.iscreature) - return TRUE; ++ return true; switch(e.classname) { case "body": diff --cc qcsrc/server/miscfunctions.qh index 000000000,f19f53fd0..e096e0c4c mode 000000,100644..100644 --- a/qcsrc/server/miscfunctions.qh +++ b/qcsrc/server/miscfunctions.qh @@@ -1,0 -1,431 +1,439 @@@ + #ifndef MISCFUNCTIONS_H + #define MISCFUNCTIONS_H + + #include "t_items.qh" + + #include "mutators/base.qh" + #include "mutators/gamemode_race.qh" + + #include "../common/constants.qh" + #include "../common/mapinfo.qh" + ++#include "../common/turrets/turrets.qh" ++#include "../common/vehicles/vehicles.qh" ++ + #ifdef RELEASE + #define cvar_string_normal builtin_cvar_string + #define cvar_normal builtin_cvar + #else + string cvar_string_normal(string n) + { + if (!(cvar_type(n) & 1)) + backtrace(strcat("Attempt to access undefined cvar: ", n)); + return builtin_cvar_string(n); + } + + float cvar_normal(string n) + { + return stof(cvar_string_normal(n)); + } + #endif + #define cvar_set_normal builtin_cvar_set + + .vector dropped_origin; + .void(void) uncustomizeentityforclient; + .float uncustomizeentityforclient_set; + .float nottargeted; + + + float DistributeEvenly_amount; + float DistributeEvenly_totalweight; + var void remove(entity e); + void objerror(string s); + void droptofloor(); + void() spawnfunc_info_player_deathmatch; // needed for the other spawnpoints + void() spawnpoint_use; + void() SUB_Remove; + + void attach_sameorigin(entity e, entity to, string tag); + ++bool checkinlist(string command, string list); ++ + void crosshair_trace(entity pl); + + void crosshair_trace_plusvisibletriggers(entity pl); + + void detach_sameorigin(entity e); + + void follow_sameorigin(entity e, entity to); + + string formatmessage(string msg); + + void GameLogEcho(string s); + + void GameLogInit(); + + void GameLogClose(); + + void GetCvars(float f); + + string GetMapname(); + + float isPushable(entity e); + + float LostMovetypeFollow(entity ent); + + float MoveToRandomMapLocation(entity e, float goodcontents, float badcontents, float badsurfaceflags, float attempts, float maxaboveground, float minviewdistance); + + string NearestLocation(vector p); + + void play2(entity e, string filename); + + string playername(entity p); + ++int Player_Trapped(entity player); ++ + void precache(); + + void remove_safely(entity e); + + void remove_unsafely(entity e); + + void SetMovetypeFollow(entity ent, entity e); + + vector shotorg_adjust_values(vector vecs, float y_is_right, float visual, float algn); + + void soundto(float dest, entity e, float chan, string samp, float vol, float atten); + + void stopsound(entity e, float chan); + + float tracebox_hits_box(vector start, vector mi, vector ma, vector end, vector thmi, vector thma); + + void traceline_antilag (entity source, vector v1, vector v2, float nomonst, entity forent, float lag); + + void WarpZone_crosshair_trace(entity pl); + + void WarpZone_traceline_antilag (entity source, vector v1, vector v2, float nomonst, entity forent, float lag); + + + #define IFTARGETED if(!self.nottargeted && self.targetname != "") + + #define ITEM_TOUCH_NEEDKILL() (((trace_dpstartcontents | trace_dphitcontents) & DPCONTENTS_NODROP) || (trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY)) + #define ITEM_DAMAGE_NEEDKILL(dt) (((dt) == DEATH_HURTTRIGGER) || ((dt) == DEATH_SLIME) || ((dt) == DEATH_LAVA) || ((dt) == DEATH_SWAMP)) + + #define PROJECTILE_TOUCH if(WarpZone_Projectile_Touch()) return + + #define move_out_of_solid(e) WarpZoneLib_MoveOutOfSolid(e) + + const string STR_PLAYER = "player"; + const string STR_SPECTATOR = "spectator"; + const string STR_OBSERVER = "observer"; + + #define IS_PLAYER(v) (v.classname == STR_PLAYER) + #define IS_SPEC(v) (v.classname == STR_SPECTATOR) + #define IS_OBSERVER(v) (v.classname == STR_OBSERVER) + #define IS_CLIENT(v) (v.flags & FL_CLIENT) + #define IS_BOT_CLIENT(v) (clienttype(v) == CLIENTTYPE_BOT) + #define IS_REAL_CLIENT(v) (clienttype(v) == CLIENTTYPE_REAL) + #define IS_NOT_A_CLIENT(v) (clienttype(v) == CLIENTTYPE_NOTACLIENT) ++#define IS_TURRET(v) (v.turret_flags & TUR_FLAG_ISTURRET) ++#define IS_VEHICLE(v) (v.vehicle_flags & VHF_ISVEHICLE) ++#define IS_MONSTER(v) (v.flags & FL_MONSTER) + + #define FOR_EACH_CLIENTSLOT(v) for(v = world; (v = nextent(v)) && (num_for_edict(v) <= maxclients); ) + #define FOR_EACH_CLIENT(v) FOR_EACH_CLIENTSLOT(v) if(IS_CLIENT(v)) + #define FOR_EACH_REALCLIENT(v) FOR_EACH_CLIENT(v) if(IS_REAL_CLIENT(v)) + + #define FOR_EACH_PLAYER(v) FOR_EACH_CLIENT(v) if(IS_PLAYER(v)) + #define FOR_EACH_SPEC(v) FOR_EACH_CLIENT(v) if (!IS_PLAYER(v)) // Samual: shouldn't this be IS_SPEC(v)? and rather create a separate macro to include observers too + #define FOR_EACH_REALPLAYER(v) FOR_EACH_REALCLIENT(v) if(IS_PLAYER(v)) + + #define FOR_EACH_MONSTER(v) for(v = world; (v = findflags(v, flags, FL_MONSTER)) != world; ) + + #define CENTER_OR_VIEWOFS(ent) (ent.origin + (IS_PLAYER(ent) ? ent.view_ofs : ((ent.mins + ent.maxs) * 0.5))) + + // copies a string to a tempstring (so one can strunzone it) + string strcat1(string s) = #115; // FRIK_FILE + + float logfile_open; + float logfile; + + #define strstr strstrofs + /* + // NOTE: DO NOT USE THIS FUNCTION TOO OFTEN. + // IT WILL MOST PROBABLY DESTROY _ALL_ OTHER TEMP + // STRINGS AND TAKE QUITE LONG. haystack and needle MUST + // BE CONSTANT OR strzoneD! + float strstr(string haystack, string needle, float offset) + { + float len, endpos; + string found; + len = strlen(needle); + endpos = strlen(haystack) - len; + while(offset <= endpos) + { + found = substring(haystack, offset, len); + if(found == needle) + return offset; + offset = offset + 1; + } + return -1; + } + */ + + const float NUM_NEAREST_ENTITIES = 4; + entity nearest_entity[NUM_NEAREST_ENTITIES]; + float nearest_length[NUM_NEAREST_ENTITIES]; + + + //#NO AUTOCVARS START + + float g_pickup_shells; + float g_pickup_shells_max; + float g_pickup_nails; + float g_pickup_nails_max; + float g_pickup_rockets; + float g_pickup_rockets_max; + float g_pickup_cells; + float g_pickup_cells_max; + float g_pickup_plasma; + float g_pickup_plasma_max; + float g_pickup_fuel; + float g_pickup_fuel_jetpack; + float g_pickup_fuel_max; + float g_pickup_armorsmall; + float g_pickup_armorsmall_max; + float g_pickup_armorsmall_anyway; + float g_pickup_armormedium; + float g_pickup_armormedium_max; + float g_pickup_armormedium_anyway; + float g_pickup_armorbig; + float g_pickup_armorbig_max; + float g_pickup_armorbig_anyway; + float g_pickup_armorlarge; + float g_pickup_armorlarge_max; + float g_pickup_armorlarge_anyway; + float g_pickup_healthsmall; + float g_pickup_healthsmall_max; + float g_pickup_healthsmall_anyway; + float g_pickup_healthmedium; + float g_pickup_healthmedium_max; + float g_pickup_healthmedium_anyway; + float g_pickup_healthlarge; + float g_pickup_healthlarge_max; + float g_pickup_healthlarge_anyway; + float g_pickup_healthmega; + float g_pickup_healthmega_max; + float g_pickup_healthmega_anyway; + float g_pickup_ammo_anyway; + float g_pickup_weapons_anyway; + float g_weaponarena; + WepSet g_weaponarena_weapons; + float g_weaponarena_random; + float g_weaponarena_random_with_blaster; + string g_weaponarena_list; + float g_weaponspeedfactor; + float g_weaponratefactor; + float g_weapondamagefactor; + float g_weaponforcefactor; + float g_weaponspreadfactor; + + WepSet start_weapons; + WepSet start_weapons_default; + WepSet start_weapons_defaultmask; + int start_items; + float start_ammo_shells; + float start_ammo_nails; + float start_ammo_rockets; + float start_ammo_cells; + float start_ammo_plasma; + float start_ammo_fuel; + float start_health; + float start_armorvalue; + WepSet warmup_start_weapons; + WepSet warmup_start_weapons_default; + WepSet warmup_start_weapons_defaultmask; + #define WARMUP_START_WEAPONS ((g_warmup_allguns == 1) ? (warmup_start_weapons & (weaponsInMap | start_weapons)) : warmup_start_weapons) + float warmup_start_ammo_shells; + float warmup_start_ammo_nails; + float warmup_start_ammo_rockets; + float warmup_start_ammo_cells; + float warmup_start_ammo_plasma; + float warmup_start_ammo_fuel; + float warmup_start_health; + float warmup_start_armorvalue; + float g_weapon_stay; + + float want_weapon(entity weaponinfo, float allguns); // WEAPONTODO: what still needs done? + void readplayerstartcvars(); + + float g_bugrigs; + float g_bugrigs_planar_movement; + float g_bugrigs_planar_movement_car_jumping; + float g_bugrigs_reverse_spinning; + float g_bugrigs_reverse_speeding; + float g_bugrigs_reverse_stopping; + float g_bugrigs_air_steering; + float g_bugrigs_angle_smoothing; + float g_bugrigs_friction_floor; + float g_bugrigs_friction_brake; + float g_bugrigs_friction_air; + float g_bugrigs_accel; + float g_bugrigs_speed_ref; + float g_bugrigs_speed_pow; + float g_bugrigs_steer; + + float sv_autotaunt; + float sv_taunt; + + string GetGametype(); // g_world.qc + void mutators_add(); // mutators.qc + void readlevelcvars(void) + { + // load mutators + mutators_add(); + + if(cvar("sv_allow_fullbright")) + serverflags |= SERVERFLAG_ALLOW_FULLBRIGHT; + + g_bugrigs = cvar("g_bugrigs"); + g_bugrigs_planar_movement = cvar("g_bugrigs_planar_movement"); + g_bugrigs_planar_movement_car_jumping = cvar("g_bugrigs_planar_movement_car_jumping"); + g_bugrigs_reverse_spinning = cvar("g_bugrigs_reverse_spinning"); + g_bugrigs_reverse_speeding = cvar("g_bugrigs_reverse_speeding"); + g_bugrigs_reverse_stopping = cvar("g_bugrigs_reverse_stopping"); + g_bugrigs_air_steering = cvar("g_bugrigs_air_steering"); + g_bugrigs_angle_smoothing = cvar("g_bugrigs_angle_smoothing"); + g_bugrigs_friction_floor = cvar("g_bugrigs_friction_floor"); + g_bugrigs_friction_brake = cvar("g_bugrigs_friction_brake"); + g_bugrigs_friction_air = cvar("g_bugrigs_friction_air"); + g_bugrigs_accel = cvar("g_bugrigs_accel"); + g_bugrigs_speed_ref = cvar("g_bugrigs_speed_ref"); + g_bugrigs_speed_pow = cvar("g_bugrigs_speed_pow"); + g_bugrigs_steer = cvar("g_bugrigs_steer"); + - g_instagib = cvar("g_instagib"); - + sv_clones = cvar("sv_clones"); + sv_foginterval = cvar("sv_foginterval"); + g_cloaked = cvar("g_cloaked"); + g_footsteps = cvar("g_footsteps"); + g_grappling_hook = cvar("g_grappling_hook"); + g_jetpack = cvar("g_jetpack"); + sv_maxidle = cvar("sv_maxidle"); + sv_maxidle_spectatorsareidle = cvar("sv_maxidle_spectatorsareidle"); + sv_autotaunt = cvar("sv_autotaunt"); + sv_taunt = cvar("sv_taunt"); + + warmup_stage = cvar("g_warmup"); + g_warmup_limit = cvar("g_warmup_limit"); + g_warmup_allguns = cvar("g_warmup_allguns"); + g_warmup_allow_timeout = cvar("g_warmup_allow_timeout"); + + if ((g_race && g_race_qualifying == 2) || g_assault || cvar("g_campaign")) + warmup_stage = 0; // these modes cannot work together, sorry + + g_pickup_respawntime_weapon = cvar("g_pickup_respawntime_weapon"); + g_pickup_respawntime_superweapon = cvar("g_pickup_respawntime_superweapon"); + g_pickup_respawntime_ammo = cvar("g_pickup_respawntime_ammo"); + g_pickup_respawntime_short = cvar("g_pickup_respawntime_short"); + g_pickup_respawntime_medium = cvar("g_pickup_respawntime_medium"); + g_pickup_respawntime_long = cvar("g_pickup_respawntime_long"); + g_pickup_respawntime_powerup = cvar("g_pickup_respawntime_powerup"); + g_pickup_respawntimejitter_weapon = cvar("g_pickup_respawntimejitter_weapon"); + g_pickup_respawntimejitter_superweapon = cvar("g_pickup_respawntimejitter_superweapon"); + g_pickup_respawntimejitter_ammo = cvar("g_pickup_respawntimejitter_ammo"); + g_pickup_respawntimejitter_short = cvar("g_pickup_respawntimejitter_short"); + g_pickup_respawntimejitter_medium = cvar("g_pickup_respawntimejitter_medium"); + g_pickup_respawntimejitter_long = cvar("g_pickup_respawntimejitter_long"); + g_pickup_respawntimejitter_powerup = cvar("g_pickup_respawntimejitter_powerup"); + + g_weaponspeedfactor = cvar("g_weaponspeedfactor"); + g_weaponratefactor = cvar("g_weaponratefactor"); + g_weapondamagefactor = cvar("g_weapondamagefactor"); + g_weaponforcefactor = cvar("g_weaponforcefactor"); + g_weaponspreadfactor = cvar("g_weaponspreadfactor"); + + g_pickup_shells = cvar("g_pickup_shells"); + g_pickup_shells_max = cvar("g_pickup_shells_max"); + g_pickup_nails = cvar("g_pickup_nails"); + g_pickup_nails_max = cvar("g_pickup_nails_max"); + g_pickup_rockets = cvar("g_pickup_rockets"); + g_pickup_rockets_max = cvar("g_pickup_rockets_max"); + g_pickup_cells = cvar("g_pickup_cells"); + g_pickup_cells_max = cvar("g_pickup_cells_max"); + g_pickup_plasma = cvar("g_pickup_plasma"); + g_pickup_plasma_max = cvar("g_pickup_plasma_max"); + g_pickup_fuel = cvar("g_pickup_fuel"); + g_pickup_fuel_jetpack = cvar("g_pickup_fuel_jetpack"); + g_pickup_fuel_max = cvar("g_pickup_fuel_max"); + g_pickup_armorsmall = cvar("g_pickup_armorsmall"); + g_pickup_armorsmall_max = cvar("g_pickup_armorsmall_max"); + g_pickup_armorsmall_anyway = cvar("g_pickup_armorsmall_anyway"); + g_pickup_armormedium = cvar("g_pickup_armormedium"); + g_pickup_armormedium_max = cvar("g_pickup_armormedium_max"); + g_pickup_armormedium_anyway = cvar("g_pickup_armormedium_anyway"); + g_pickup_armorbig = cvar("g_pickup_armorbig"); + g_pickup_armorbig_max = cvar("g_pickup_armorbig_max"); + g_pickup_armorbig_anyway = cvar("g_pickup_armorbig_anyway"); + g_pickup_armorlarge = cvar("g_pickup_armorlarge"); + g_pickup_armorlarge_max = cvar("g_pickup_armorlarge_max"); + g_pickup_armorlarge_anyway = cvar("g_pickup_armorlarge_anyway"); + g_pickup_healthsmall = cvar("g_pickup_healthsmall"); + g_pickup_healthsmall_max = cvar("g_pickup_healthsmall_max"); + g_pickup_healthsmall_anyway = cvar("g_pickup_healthsmall_anyway"); + g_pickup_healthmedium = cvar("g_pickup_healthmedium"); + g_pickup_healthmedium_max = cvar("g_pickup_healthmedium_max"); + g_pickup_healthmedium_anyway = cvar("g_pickup_healthmedium_anyway"); + g_pickup_healthlarge = cvar("g_pickup_healthlarge"); + g_pickup_healthlarge_max = cvar("g_pickup_healthlarge_max"); + g_pickup_healthlarge_anyway = cvar("g_pickup_healthlarge_anyway"); + g_pickup_healthmega = cvar("g_pickup_healthmega"); + g_pickup_healthmega_max = cvar("g_pickup_healthmega_max"); + g_pickup_healthmega_anyway = cvar("g_pickup_healthmega_anyway"); + + g_pickup_ammo_anyway = cvar("g_pickup_ammo_anyway"); + g_pickup_weapons_anyway = cvar("g_pickup_weapons_anyway"); + + g_weapon_stay = cvar(strcat("g_", GetGametype(), "_weapon_stay")); + if(!g_weapon_stay) + g_weapon_stay = cvar("g_weapon_stay"); + + if (!warmup_stage) + game_starttime = time + cvar("g_start_delay"); + + for(int i = WEP_FIRST; i <= WEP_LAST; ++i) + WEP_ACTION(i, WR_INIT); + + readplayerstartcvars(); + } + + //#NO AUTOCVARS END + + + // Sound functions + //string precache_sound (string s) = #19; + // hack + float precache_sound_index (string s) = #19; + + const float SND_VOLUME = 1; + const float SND_ATTENUATION = 2; + const float SND_LARGEENTITY = 8; + const float SND_LARGESOUND = 16; + + // WARNING: this kills the trace globals + #define EXACTTRIGGER_TOUCH if(WarpZoneLib_ExactTrigger_Touch()) return + #define EXACTTRIGGER_INIT WarpZoneLib_ExactTrigger_Init() + + const float INITPRIO_FIRST = 0; + const float INITPRIO_GAMETYPE = 0; + const float INITPRIO_GAMETYPE_FALLBACK = 1; + const float INITPRIO_FINDTARGET = 10; + const float INITPRIO_DROPTOFLOOR = 20; + const float INITPRIO_SETLOCATION = 90; + const float INITPRIO_LINKDOORS = 91; + const float INITPRIO_LAST = 99; + + .void(void) initialize_entity; + .float initialize_entity_order; + .entity initialize_entity_next; + entity initialize_entity_first; + + + + + + float sound_allowed(float dest, entity e); + void InitializeEntity(entity e, void(void) func, float order); + void SetCustomizer(entity e, float(void) customizer, void(void) uncustomizer); + void Net_LinkEntity(entity e, float docull, float dt, float(entity, float) sendfunc); + + #endif diff --cc qcsrc/server/mutators/base.qc index 248f923d0,e13919a23..95252b321 --- a/qcsrc/server/mutators/base.qc +++ b/qcsrc/server/mutators/base.qc @@@ -91,7 -91,7 +91,7 @@@ float CallbackChain_Call(entity cb return r; // callbacks return an error status, so 0 is default return value } - #define MAX_MUTATORS 20 -const float MAX_MUTATORS = 15; ++const float MAX_MUTATORS = 20; string loaded_mutators[MAX_MUTATORS]; float Mutator_Add(mutatorfunc_t func, string name) { diff --cc qcsrc/server/mutators/base.qh index 9bab139fe,7d36af08e..271516733 --- a/qcsrc/server/mutators/base.qh +++ b/qcsrc/server/mutators/base.qh @@@ -109,19 -111,13 +112,19 @@@ MUTATOR_HOOKABLE(WeaponRateFactor) // INPUT, OUTPUT: float weapon_rate; +MUTATOR_HOOKABLE(WantWeapon); + // returns which weapon the player/bot should choose + // INPUT, OUTPUT: - float ret_float; - entity other; ++ //float ret_float; ++ //entity other; + MUTATOR_HOOKABLE(SetStartItems); // adjusts {warmup_}start_{items,weapons,ammo_{cells,plasma,rockets,nails,shells,fuel}} MUTATOR_HOOKABLE(BuildMutatorsString); // appends ":mutatorname" to ret_string for logging // INPUT, OUTPUT: -- string ret_string; ++ //string ret_string; MUTATOR_HOOKABLE(BuildMutatorsPrettyString); // appends ", Mutator name" to ret_string for display @@@ -172,11 -168,12 +175,11 @@@ MUTATOR_HOOKABLE(MonsterSpawn) MUTATOR_HOOKABLE(MonsterDies); // called when a monster dies // INPUT: - entity frag_attacker; + // entity frag_attacker; -MUTATOR_HOOKABLE(MonsterRespawn); - // called when a monster wants to respawn - // INPUT: -// entity other; +MUTATOR_HOOKABLE(MonsterRemove); + // called when a monster is being removed - // returning TRUE hides removal effect ++ // returning true hides removal effect MUTATOR_HOOKABLE(MonsterDropItem); // called when a monster is dropping loot @@@ -211,11 -202,10 +214,11 @@@ MUTATOR_HOOKABLE(FormatMessage) MUTATOR_HOOKABLE(PlayerDamage_SplitHealthArmor); // called when a player gets damaged to e.g. remove stuff he was carrying. // INPUT: - entity frag_inflictor; - entity frag_attacker; - entity frag_target; // same as self + // entity frag_inflictor; + // entity frag_attacker; + // entity frag_target; // same as self vector damage_force; // NOTE: this force already HAS been applied + float frag_mirrordamage; // INPUT, OUTPUT: float damage_take; float damage_save; @@@ -224,12 -214,12 +227,12 @@@ MUTATOR_HOOKABLE(PlayerDamage_Calculate // called to adjust damage and force values which are applied to the player, used for e.g. strength damage/force multiplier // i'm not sure if I should change this around slightly (Naming of the entities, and also how they're done in g_damage). // INPUT: - entity frag_attacker; - entity frag_target; - float frag_deathtype; + // entity frag_attacker; + // entity frag_target; + // float frag_deathtype; // INPUT, OUTPUT: float frag_damage; -- float frag_mirrordamage; ++ //float frag_mirrordamage; vector frag_force; MUTATOR_HOOKABLE(PlayerPowerups); @@@ -261,16 -251,6 +264,16 @@@ MUTATOR_HOOKABLE(SV_ParseServerCommand) string cmd_name; // command name float cmd_argc; // also, argv() can be used string cmd_string; // whole command, use only if you really have to + +MUTATOR_HOOKABLE(SV_ParseClientCommand); + // called when a client command is parsed + // NOTE: hooks MUST start with if(MUTATOR_RETURNVALUE) return 0; + // NOTE: return 1 if you handled the command, return 0 to continue handling + // NOTE: THESE HOOKS MUST NEVER EVER CALL tokenize() + // INPUT - string cmd_name; // command name - float cmd_argc; // also, argv() can be used - string cmd_string; // whole command, use only if you really have to ++ //string cmd_name; // command name ++ //float cmd_argc; // also, argv() can be used ++ //string cmd_string; // whole command, use only if you really have to /* // example: MUTATOR_HOOKFUNCTION(foo_SV_ParseClientCommand) @@@ -332,7 -309,7 +335,7 @@@ MUTATOR_HOOKABLE(Item_RespawnCountdown) MUTATOR_HOOKABLE(BotShouldAttack); // called when a bot checks a target to attack // INPUT - entity other; - entity checkentity; ++ //entity other; MUTATOR_HOOKABLE(PortalTeleport); // called whenever a player goes through a portal gun teleport diff --cc qcsrc/server/mutators/gamemode_assault.qc index ec353346c,49deac0e8..e2628b508 --- a/qcsrc/server/mutators/gamemode_assault.qc +++ b/qcsrc/server/mutators/gamemode_assault.qc @@@ -1,3 -1,3 +1,6 @@@ ++#include "../../common/vehicles/sv_vehicles.qh" ++#include "../../common/turrets/sv_turrets.qh" ++ // random functions void assault_objective_use() { @@@ -554,10 -525,10 +557,10 @@@ MUTATOR_HOOKFUNCTION(assault_PlayerSpaw MUTATOR_HOOKFUNCTION(assault_TurretSpawn) { - if (!self.team) - self.team = 14; + if(!self.team || self.team == MAX_SHOT_DISTANCE) + self.team = 5; // this gets reversed when match starts? - return FALSE; + return false; } MUTATOR_HOOKFUNCTION(assault_VehicleSpawn) @@@ -570,63 -541,15 +573,63 @@@ MUTATOR_HOOKFUNCTION(assault_BotRoles) { havocbot_ast_reset_role(self); - return TRUE; + return true; } +MUTATOR_HOOKFUNCTION(assault_GetTeamCount) +{ + c1 = c2 = 0; // assault always has 2 teams - return TRUE; ++ return true; +} + +.float assault_repair_delay; +MUTATOR_HOOKFUNCTION(assault_PlayerThink) +{ - if(self.team == assault_attacker_team) { return FALSE; } - if(autocvar_g_assault_repair_amount <= 0) { return FALSE; } - if(time < self.assault_repair_delay) { return FALSE; } ++ if(self.team == assault_attacker_team) { return false; } ++ if(autocvar_g_assault_repair_amount <= 0) { return false; } ++ if(time < self.assault_repair_delay) { return false; } + if(gameover || self.frozen || self.deadflag != DEAD_NO || self.vehicle) - return FALSE; ++ return false; + + float n_players = 0; + entity cp = findchain(classname, "func_assault_destructible"), head; + self.assault_repair_delay = time + 0.5; + + FOR_EACH_PLAYER(head) if(SAME_TEAM(head, self)) { ++n_players; } + + for(; cp; cp = cp.chain) + if(vlen((cp.origin + 0.5 * (cp.absmin + cp.absmax)) - self.origin) < 300) + if(cp.health > 0) + if(cp.health < cp.max_health) + if(cp.target != "") + if(time - cp.pain_finished > 3) + { + cp.health += autocvar_g_assault_repair_amount / n_players; + if(cp.sprite) { WaypointSprite_UpdateHealth(cp.sprite, cp.health); } + } - return FALSE; ++ return false; +} + +MUTATOR_HOOKFUNCTION(assault_OnEntityPreSpawn) +{ + switch(self.classname) + { - case "info_player_team1": return TRUE; - case "info_player_team2": return TRUE; - case "info_player_team3": return TRUE; - case "info_player_team4": return TRUE; ++ case "info_player_team1": return true; ++ case "info_player_team2": return true; ++ case "info_player_team3": return true; ++ case "info_player_team4": return true; + } + - return FALSE; ++ return false; +} + // scoreboard setup void assault_ScoreRules() { - ScoreRules_basics(2, SFL_SORT_PRIO_PRIMARY, 0, TRUE); - ScoreRules_basics(2, SFL_SORT_PRIO_SECONDARY, SFL_SORT_PRIO_SECONDARY, true); - ScoreInfo_SetLabel_TeamScore( ST_ASSAULT_OBJECTIVES, "objectives", SFL_SORT_PRIO_PRIMARY); - ScoreInfo_SetLabel_PlayerScore(SP_ASSAULT_OBJECTIVES, "objectives", SFL_SORT_PRIO_PRIMARY); ++ ScoreRules_basics(2, SFL_SORT_PRIO_PRIMARY, 0, true); + ScoreInfo_SetLabel_TeamScore( ST_ASSAULT_DESTROYED, "destroyed", SFL_SORT_PRIO_PRIMARY); + ScoreInfo_SetLabel_TeamScore( ST_ASSAULT_OBJECTIVES, "objectives", 0); + ScoreInfo_SetLabel_PlayerScore(SP_ASSAULT_OBJECTIVES, "objectives", 0); ScoreRules_basics_end(); } diff --cc qcsrc/server/mutators/gamemode_assault.qh index 1d0441315,330707e67..b94f53132 --- a/qcsrc/server/mutators/gamemode_assault.qh +++ b/qcsrc/server/mutators/gamemode_assault.qh @@@ -23,10 -25,10 +25,12 @@@ void(float ratingscale, vector org, flo void(float ratingscale, vector org, float sradius) havocbot_goalrating_enemyplayers; // scoreboard stuff - #define ST_ASSAULT_DESTROYED 1 - #define ST_ASSAULT_OBJECTIVES -1 - #define SP_ASSAULT_OBJECTIVES 4 -const float ST_ASSAULT_OBJECTIVES = 1; -const float SP_ASSAULT_OBJECTIVES = 4; ++const int ST_ASSAULT_DESTROYED = 1; ++const int ST_ASSAULT_OBJECTIVES = -1; ++const int SP_ASSAULT_OBJECTIVES = 4; // predefined spawnfuncs void spawnfunc_func_breakable(); void target_objective_decrease_activate(); ++ + #endif diff --cc qcsrc/server/mutators/gamemode_ca.qc index b44e8ab31,a0f238358..62bb31d0a --- a/qcsrc/server/mutators/gamemode_ca.qc +++ b/qcsrc/server/mutators/gamemode_ca.qc @@@ -1,3 -1,3 +1,5 @@@ ++#include "../round_handler.qh" ++ float total_players; float redalive, bluealive, yellowalive, pinkalive; .float redalive_stat, bluealive_stat, yellowalive_stat, pinkalive_stat; @@@ -68,9 -80,10 +70,9 @@@ float CA_CheckWinner( { Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_ROUND_OVER); Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_ROUND_OVER); - allowed_to_spawn = FALSE; + allowed_to_spawn = false; round_handler_Init(5, autocvar_g_ca_warmup, autocvar_g_ca_round_timelimit); - nades_Clear(world, TRUE); - FOR_EACH_PLAYER(e) - nades_Clear(e); ++ nades_Clear(world, true); return 1; } @@@ -91,10 -104,11 +93,10 @@@ Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_ROUND_TIED); } - allowed_to_spawn = FALSE; + allowed_to_spawn = false; round_handler_Init(5, autocvar_g_ca_warmup, autocvar_g_ca_round_timelimit); - nades_Clear(world, TRUE); - FOR_EACH_PLAYER(e) - nades_Clear(e); ++ nades_Clear(world, true); return 1; } @@@ -311,10 -325,14 +313,10 @@@ MUTATOR_HOOKFUNCTION(ca_PlayerDamage MUTATOR_HOOKFUNCTION(ca_FilterItem) { - if(autocvar_g_powerups <= 0) - if(self.flags & FL_POWERUP) - return true; - if(autocvar_g_pickup_items <= 0) - return TRUE; + return true; - return FALSE; + return false; } MUTATOR_HOOKFUNCTION(ca_PlayerDamage_SplitHealthArmor) @@@ -330,28 -348,12 +332,28 @@@ MUTATOR_HOOKFUNCTION(ca_PlayerRegen) { // no regeneration in CA - return TRUE; + return true; } +MUTATOR_HOOKFUNCTION(ca_WantWeapon) +{ + if(other.spawnflags & WEP_FLAG_NORMAL) - ret_float = TRUE; ++ ret_float = true; + else - ret_float = FALSE; ++ ret_float = false; + - return FALSE; ++ return false; +} + +MUTATOR_HOOKFUNCTION(ca_CountFrags) +{ + // announce remaining frags - return TRUE; ++ return true; +} + void ca_Initialize() { - allowed_to_spawn = TRUE; + allowed_to_spawn = true; ca_teams = autocvar_g_ca_teams_override; if(ca_teams < 2) diff --cc qcsrc/server/mutators/gamemode_conquest.qc index 07990f25c,000000000..1a8f34858 mode 100644,000000..100644 --- a/qcsrc/server/mutators/gamemode_conquest.qc +++ b/qcsrc/server/mutators/gamemode_conquest.qc @@@ -1,928 -1,0 +1,931 @@@ ++#include "../../common/effects.qh" ++#include "../round_handler.qh" ++ +float cq_ControlPoint_Send(entity to, float sf) +{ + WriteByte(MSG_ENTITY, ENT_CLIENT_CONQUEST_CONTROLPOINT); + WriteByte(MSG_ENTITY, sf); + + if(sf & CQSF_SETUP) + { + WriteCoord(MSG_ENTITY, self.origin_x); + WriteCoord(MSG_ENTITY, self.origin_y); + WriteCoord(MSG_ENTITY, self.origin_z); + + WriteCoord(MSG_ENTITY, self.angles_y); + + WriteLong(MSG_ENTITY, self.cq_capdistance); + } + + if(sf & CQSF_STATE) + { + WriteByte(MSG_ENTITY, self.cq_status); + } + + if(sf & CQSF_TEAM) + { + WriteByte(MSG_ENTITY, self.team); + } + + if(sf & CQSF_HEALTH) + { + WriteByte(MSG_ENTITY, ceil((self.health / self.max_health) * 255)); + WriteLong(MSG_ENTITY, autocvar_g_conquest_capture_sps); + } + + if(sf & CQSF_NAME) + { + WriteString(MSG_ENTITY, self.netname); + } + + - return TRUE; ++ return true; +} + +void cq_ControlPoint_SwitchTeam(float newteam, float newstatus, float showmessage) +{ + // TODO: add sounds + // TODO: clean this up! + + if(showmessage) + if(newstatus == CP_NEUTRAL && self.cq_status != CP_NEUTRAL) + { + Send_Notification(NOTIF_ALL, world, MSG_INFO, APP_TEAM_ENT_4(self, INFO_CONQUEST_LIBERATE_), self.netname); + Send_Notification(NOTIF_TEAM, self, MSG_CENTER, APP_TEAM_NUM_4(newteam, CENTER_CONQUEST_LOST_), self.netname); + + entity player; + FOR_EACH_REALPLAYER(player) + { + if(player.team == newteam) + Send_Notification(NOTIF_ONE, player, MSG_CENTER, APP_TEAM_ENT_4(self, CENTER_CONQUEST_LIBERATE_TEAM_), self.netname); + else if(DIFF_TEAM(player, self)) + Send_Notification(NOTIF_ONE, player, MSG_CENTER, APP_TEAM_NUM_4(newteam, CENTER_CONQUEST_LIBERATE_), self.netname); + } + } + + self.team = newteam; + self.cq_status = newstatus; + + switch(self.cq_status) + { + case CP_NEUTRAL: + { + activator = world; + self.skin = 0; + break; + } + + case CP_CAPTURED: + { + activator = self; + self.skin = Team_TeamToNumber(self.team) + 1; + + if(showmessage) + { + Send_Notification(NOTIF_ALL, world, MSG_INFO, APP_TEAM_ENT_4(self, INFO_CONQUEST_CAPTURE_), self.netname); + Send_Notification(NOTIF_TEAM, self, MSG_CENTER, CENTER_CONQUEST_CAPTURE_TEAM, self.netname); + + entity player; + FOR_EACH_PLAYER(player) + { + if(DIFF_TEAM(player, self)) + Send_Notification(NOTIF_ONE, player, MSG_CENTER, APP_TEAM_ENT_4(self, CENTER_CONQUEST_CAPTURE_), self.netname); + } + } + + break; + } + + default: { dprint("Control point without status?\n"); break; } + } + + SUB_UseTargets(); + + self.SendFlags |= CQSF_STATE | CQSF_TEAM; +} + +void cq_ControlPoint_Think() +{ + self.nextthink = time + CQ_CP_THINKRATE; + + if(!round_handler_IsRoundStarted()) { return; } + + float dist, old_health = self.health; + entity player; + + FOR_EACH_PLAYER(player) + { + if(!player.deadflag) + if(!player.vehicle) + if(!player.frozen) + { + dist = vlen(player.origin - self.origin); + if(dist <= self.cq_capdistance) + { + traceline(CENTER_OR_VIEWOFS(player), CENTER_OR_VIEWOFS(self), MOVE_WORLDONLY, player); + if(trace_fraction == 1.0) + { + if(SAME_TEAM(player, self)) + self.health = min(self.max_health, self.health + autocvar_g_conquest_capture_sps * CQ_CP_THINKRATE); + else + self.health = max(0, self.health - autocvar_g_conquest_capture_sps * CQ_CP_THINKRATE); + + switch(self.cq_status) + { + case CP_CAPTURED: + { + if(self.health == 0) + { + PlayerScore_Add(player, SP_CONQUEST_LIBERATED, 1); + Send_Effect(EFFECT_EXPLOSION_BIG, findbetterlocation(self.origin, 16), '0 0 0', 1); - cq_ControlPoint_SwitchTeam(player.team, CP_NEUTRAL, TRUE); ++ cq_ControlPoint_SwitchTeam(player.team, CP_NEUTRAL, true); + } + + break; + } + + case CP_NEUTRAL: + { + if(self.health == 0) - cq_ControlPoint_SwitchTeam(player.team, CP_NEUTRAL, TRUE); ++ cq_ControlPoint_SwitchTeam(player.team, CP_NEUTRAL, true); + + if(self.health == self.max_health) + { + PlayerScore_Add(player, SP_CONQUEST_CAPTURED, 1); - cq_ControlPoint_SwitchTeam(player.team, CP_CAPTURED, TRUE); ++ cq_ControlPoint_SwitchTeam(player.team, CP_CAPTURED, true); + } + + break; + } + } + } + } + } + } + + if(self.health != old_health) + self.SendFlags |= CQSF_HEALTH; +} + +void cq_ControlPoint_Reset() +{ + if(self.cq_originalteam) + self.cq_status = CP_CAPTURED; + else + { + self.cq_status = CP_NEUTRAL; + self.health = 0; + } + self.SendFlags |= CQSF_HEALTH | CQSF_TEAM; - cq_ControlPoint_SwitchTeam(self.cq_originalteam, self.cq_status, FALSE); ++ cq_ControlPoint_SwitchTeam(self.cq_originalteam, self.cq_status, false); +} + +void spawnfunc_conquest_controlpoint() +{ + if(!g_conquest) { remove(self); return; } + + self.cq_worldcpnext = cq_worldcplist; // link control point into cq_worldcplist + cq_worldcplist = self; + + self.model = ""; // should we support custom models? + + setsize(self, CQ_CP_MIN, CQ_CP_MAX); + + if(self.netname == "") { self.netname = "a spawnpoint"; } + if(!self.health) { self.health = autocvar_g_conquest_controlpoint_health_default; } + if(!self.cq_capdistance) { self.cq_capdistance = autocvar_g_conquest_capture_distance_default; } + + self.classname = "conquest_controlpoint"; + self.max_health = self.health; + self.solid = SOLID_TRIGGER; + self.nextthink = time + CQ_CP_THINKRATE; + self.think = cq_ControlPoint_Think; + self.reset = cq_ControlPoint_Reset; + + if(self.team == NUM_TEAM_4 && cq_teams < 4) + self.team = NUM_TEAM_2; + if(self.team == NUM_TEAM_3 && cq_teams < 3) + self.team = NUM_TEAM_1; + + if(self.team) + self.cq_status = CP_CAPTURED; + else + { + self.cq_status = CP_NEUTRAL; + self.health = 0; + } + + self.cq_originalteam = self.team; + + waypoint_spawnforitem(self); - Net_LinkEntity(self, FALSE, 0, cq_ControlPoint_Send); ++ Net_LinkEntity(self, false, 0, cq_ControlPoint_Send); + self.SendFlags = CQSF_SETUP | CQSF_STATE | CQSF_TEAM | CQSF_HEALTH | CQSF_NAME; - cq_ControlPoint_SwitchTeam(self.team, self.cq_status, FALSE); ++ cq_ControlPoint_SwitchTeam(self.team, self.cq_status, false); +} + +// round handler +void cq_CountAlivePlayers() +{ + entity e; + total_players = redalive = bluealive = yellowalive = pinkalive = 0; + + FOR_EACH_PLAYER(e) + { + ++total_players; + redalive += (e.team == NUM_TEAM_1 && e.deadflag == DEAD_NO); + bluealive += (e.team == NUM_TEAM_2 && e.deadflag == DEAD_NO); + yellowalive += (e.team == NUM_TEAM_3 && e.deadflag == DEAD_NO); + pinkalive += (e.team == NUM_TEAM_4 && e.deadflag == DEAD_NO); + } +} + +#define CQ_ALIVE_TEAMS() ((redalive > 0) + (bluealive > 0) + (yellowalive > 0) + (pinkalive > 0)) +#define CQ_ALIVE_TEAMS_OK() (CQ_ALIVE_TEAMS() == cq_teams) + +float cq_getWinnerTeam() +{ + entity tmp_entity; + float redcp = 0, bluecp = 0, yellowcp = 0, pinkcp = 0; + for(tmp_entity = cq_worldcplist; tmp_entity; tmp_entity = tmp_entity.cq_worldcpnext) + { + if(tmp_entity.cq_status == CP_CAPTURED) + switch(tmp_entity.team) + { + case NUM_TEAM_1: ++redcp; break; + case NUM_TEAM_2: ++bluecp; break; + case NUM_TEAM_3: ++yellowcp; break; + case NUM_TEAM_4: ++pinkcp; break; + } + } + + float winner_team = 0; + if(redcp >= 1) { winner_team = NUM_TEAM_1; } + if(bluecp >= 1) + { + if(winner_team) return 0; + winner_team = NUM_TEAM_2; + } + if(yellowcp >= 1) + { + if(winner_team) return 0; + winner_team = NUM_TEAM_3; + } + if(pinkcp >= 1) + { + if(winner_team) return 0; + winner_team = NUM_TEAM_4; + } + if(winner_team) + return winner_team; + return -1; // nobody owns anything +} + +float cq_CheckWinner() +{ + if(round_handler_GetEndTime() > 0 && round_handler_GetEndTime() - time <= 0) + { + Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_ROUND_OVER); + Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_ROUND_OVER); + round_handler_Init(5, autocvar_g_conquest_warmup, autocvar_g_conquest_round_timelimit); + return 1; + } + + cq_CountAlivePlayers(); + + entity tmp_entity; + float redcp = 0, bluecp = 0, yellowcp = 0, pinkcp = 0, neutralcp = 0; + for(tmp_entity = cq_worldcplist; tmp_entity; tmp_entity = tmp_entity.cq_worldcpnext) + { + if(tmp_entity.cq_status == CP_CAPTURED) + switch(tmp_entity.team) + { + case NUM_TEAM_1: ++redcp; break; + case NUM_TEAM_2: ++bluecp; break; + case NUM_TEAM_3: ++yellowcp; break; + case NUM_TEAM_4: ++pinkcp; break; + } + else { ++neutralcp; } + } + if(((redcp > 0) + (bluecp > 0) + (yellowcp > 0) + (pinkcp > 0) + (neutralcp > 0)) > 1) // more than 1 team owns control points + return 0; + + float winner_team; + winner_team = cq_getWinnerTeam(); + if(winner_team > 0) + { + Send_Notification(NOTIF_ALL, world, MSG_CENTER, APP_TEAM_NUM_4(winner_team, CENTER_ROUND_TEAM_WIN_)); + Send_Notification(NOTIF_ALL, world, MSG_INFO, APP_TEAM_NUM_4(winner_team, INFO_ROUND_TEAM_WIN_)); + TeamScore_AddToTeam(winner_team, ST_CONQUEST_ROUNDS, +1); + } + else if(winner_team == -1) + { + Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_ROUND_TIED); + Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_ROUND_TIED); + } + + round_handler_Init(5, autocvar_g_conquest_warmup, autocvar_g_conquest_round_timelimit); + return 1; +} + - float prev_missing_teams_mask; +float cq_CheckTeams() +{ - allowed_to_spawn = TRUE; ++ static float prev_missing_teams_mask; ++ allowed_to_spawn = true; + cq_CountAlivePlayers(); + if(CQ_ALIVE_TEAMS_OK()) + { + if(prev_missing_teams_mask > 0) + Kill_Notification(NOTIF_ALL, world, MSG_CENTER_CPID, CPID_MISSING_TEAMS); + prev_missing_teams_mask = -1; + return 1; + } + if(total_players == 0) + { + if(prev_missing_teams_mask > 0) + Kill_Notification(NOTIF_ALL, world, MSG_CENTER_CPID, CPID_MISSING_TEAMS); + prev_missing_teams_mask = -1; + return 0; + } + float missing_teams_mask = (!redalive) + (!bluealive) * 2; + if(cq_teams >= 3) missing_teams_mask += (!yellowalive) * 4; + if(cq_teams >= 4) missing_teams_mask += (!pinkalive) * 8; + if(prev_missing_teams_mask != missing_teams_mask) + { + Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_MISSING_TEAMS, missing_teams_mask); + prev_missing_teams_mask = missing_teams_mask; + } + return 0; +} + + +// ====================== +// Teleportation Handling +// ====================== + +/* + * Find control point or generator owned by the same team self which is nearest to pos + * if max_dist is positive, only control points within this range will be considered + */ +entity cq_Nearest_ControlPoint(vector pos, float max_dist) +{ + entity tmp_entity, closest_target = world; + for(tmp_entity = cq_worldcplist; tmp_entity; tmp_entity = tmp_entity.cq_worldcpnext) + { + if(SAME_TEAM(tmp_entity, self)) + if(tmp_entity.cq_status == CP_CAPTURED) + if(max_dist <= 0 || vlen(tmp_entity.origin - pos) <= max_dist) + if(vlen(tmp_entity.origin - pos) <= vlen(closest_target.origin - pos) || closest_target == world) + closest_target = tmp_entity; + } + + return closest_target; +} + +/* + * Find control point or generator owned by the same team self which is nearest to pos + * if max_dist is positive, only control points within this range will be considered + * This function only check distances on the XY plane, disregarding Z + */ +entity cq_Nearest_ControlPoint_2D(vector pos, float max_dist) +{ + entity tmp_entity, closest_target = world; + vector delta; + float smallest_distance = 0, distance; + + for(tmp_entity = cq_worldcplist; tmp_entity; tmp_entity = tmp_entity.cq_worldcpnext) + { + delta = tmp_entity.origin - pos; + delta_z = 0; + distance = vlen(delta); + + if(SAME_TEAM(tmp_entity, self)) + if(tmp_entity.cq_status == CP_CAPTURED) + if(max_dist <= 0 || distance <= max_dist) + if(closest_target == world || distance <= smallest_distance ) + { + closest_target = tmp_entity; + smallest_distance = distance; + } + } + + return closest_target; +} +/** + * find the number of control points and generators in the same team as self + */ +float cq_Count_SelfControlPoints() +{ + float n = 0; + entity tmp_entity; + for(tmp_entity = cq_worldcplist; tmp_entity; tmp_entity = tmp_entity.cq_worldcpnext) + { + if(SAME_TEAM(tmp_entity, self)) + if(tmp_entity.cq_status == CP_CAPTURED) + n++; + } + return n; +} + +/** + * Teleport player to a random position near tele_target + * if tele_effects is true, teleport sound+particles are created - * return FALSE on failure ++ * return false on failure + */ +float cq_Teleport(entity player, entity tele_target, float range, float tele_effects) +{ + if ( !tele_target ) - return FALSE; ++ return false; + + float i; + vector loc; + float theta; + for(i = 0; i < 16; ++i) + { + theta = random() * 2 * M_PI; + loc_y = sin(theta); + loc_x = cos(theta); + loc_z = 0; + loc *= random() * range; + + loc += tele_target.origin + '0 0 128'; + + tracebox(loc, PL_MIN, PL_MAX, loc, MOVE_NORMAL, player); + if(trace_fraction == 1.0 && !trace_startsolid) + { + traceline(tele_target.origin, loc, MOVE_NOMONSTERS, tele_target); // double check to make sure we're not spawning outside the world + if(trace_fraction == 1.0) + { + if ( tele_effects ) + { + Send_Effect(EFFECT_TELEPORT, player.origin, '0 0 0', 1); + sound (player, CH_TRIGGER, "misc/teleport.wav", VOL_BASE, ATTEN_NORM); + } + setorigin(player, loc); + player.teleport_antispam = time + autocvar_g_conquest_teleport_wait; + + if ( tele_effects ) + Send_Effect(EFFECT_TELEPORT, player.origin + v_forward * 32, '0 0 0', 1); - return TRUE; ++ return true; + } + } + } + - return FALSE; ++ return false; +} + + +// ================== +// Legacy Bot Support +// ================== + +void havocbot_role_cq_liberating(); + +void havocbot_goalrating_defendpoints(float ratingscale, vector org, float sradius) +{ + entity head; + float distance; + + for(head = cq_worldcplist; head; head = head.cq_worldcpnext) + { + if (SAME_TEAM(head, self)) + { + if (head.health < head.max_health) + { + distance = vlen(head.origin - org); + if (distance > sradius) + continue; + navigation_routerating(head, ratingscale, 2000); + } + else + { + // If control point is not under attack, seek it out anyway + navigation_routerating(head, ratingscale/3, 2000); + } + } + } +} + +void havocbot_goalrating_enemypoints(float ratingscale, vector org, float sradius) +{ + entity head; + float distance; + + for(head = cq_worldcplist; head; head = head.cq_worldcpnext) + { + if (DIFF_TEAM(head, self)) + { + distance = vlen(head.origin - org); + if (distance > sradius) + continue; + navigation_routerating(head, ratingscale, 2000); + } + } +} + +void havocbot_role_cq_offense() +{ + entity head; + float owned; + + if(self.deadflag != DEAD_NO) + return; + + if (!self.havocbot_role_timeout) + self.havocbot_role_timeout = time + random() * 10 + 20; + + // Count how many control points on team are owned. + owned = 0; + for(head = cq_worldcplist; head; head = head.cq_worldcpnext) + { + if ((SAME_TEAM(head, self)) && (head.cq_status == CP_CAPTURED)) + owned++; + } + + // If only one left on team or if role has timed out then start trying to liberate control points. + if ((owned == 0) || (time > self.havocbot_role_timeout)) + { + dprint("changing role to liberating\n"); + self.havocbot_role = havocbot_role_cq_liberating; + self.havocbot_role_timeout = 0; + return; + } + + if (time > self.bot_strategytime) + { + self.bot_strategytime = time + autocvar_bot_ai_strategyinterval; + + navigation_goalrating_start(); + havocbot_goalrating_items(10000, self.origin, 10000); + havocbot_goalrating_enemypoints(20000, self.origin, 10000); + havocbot_goalrating_defendpoints(9000, self.origin, 10000); + //havocbot_goalrating_waypoints(1, self.origin, 1000); + navigation_goalrating_end(); + } +} + +void havocbot_role_cq_liberating() +{ + if(self.deadflag != DEAD_NO) + return; + + if (!self.havocbot_role_timeout) + self.havocbot_role_timeout = time + random() * 10 + 20; + + if (time > self.havocbot_role_timeout) + { + dprint("changing role to offense\n"); + self.havocbot_role = havocbot_role_cq_offense; + self.havocbot_role_timeout = 0; + return; + } + + if (time > self.bot_strategytime) + { + self.bot_strategytime = time + autocvar_bot_ai_strategyinterval; + + navigation_goalrating_start(); + havocbot_goalrating_items(8000, self.origin, 10000); + havocbot_goalrating_enemyplayers(10000, self.origin, 10000); + havocbot_goalrating_defendpoints(20000, self.origin, 10000); + //havocbot_goalrating_waypoints(1, self.origin, 1000); + navigation_goalrating_end(); + } +} + + +// ============= +// Compatibility +// ============= + +void cq_Setup_Compat_dom() +{ + // if map already has control points, don't spawn more + if(cq_worldcplist && (!cq_worldcplist.cq_compat || cq_worldcplist.cq_compat != COMPAT_DOM)) + { + self.think = SUB_Remove; + self.nextthink = time; + return; + } + + float redcp = 0, bluecp = 0, yellowcp = 0, pinkcp = 0; + + entity head; + for(head = cq_worldcplist; head; head = head.cq_worldcpnext) + { + switch(head.team) + { + case NUM_TEAM_1: ++redcp; break; + case NUM_TEAM_2: ++bluecp; break; + case NUM_TEAM_3: ++yellowcp; break; + case NUM_TEAM_4: ++pinkcp; break; + } + } + + if(!redcp) { self.team = NUM_TEAM_1; } + if(!bluecp) { self.team = NUM_TEAM_2; } + if(!yellowcp && cq_teams >= 3) { self.team = NUM_TEAM_3; } + if(!pinkcp && cq_teams >= 4) { self.team = NUM_TEAM_4; } + + self.cq_compat = COMPAT_DOM; // compatibility flag + + spawnfunc_conquest_controlpoint(); +} + +void cq_Setup_Compat_ons() +{ + // if map already has control points, don't spawn more + if(cq_worldcplist && (!cq_worldcplist.cq_compat || cq_worldcplist.cq_compat != COMPAT_ONS)) + { + self.think = SUB_Remove; + self.nextthink = time; + return; + } + + // teams are already setup for onslaught + + self.cq_compat = COMPAT_ONS; // compatibility flag + + spawnfunc_conquest_controlpoint(); +} + + +// ============== +// Hook Functions +// ============== + +MUTATOR_HOOKFUNCTION(conquest_RemovePlayer) +{ + self.cq_deathloc = '0 0 0'; - return FALSE; ++ return false; +} + +MUTATOR_HOOKFUNCTION(conquest_PlayerDies) +{ + frag_target.cq_deathloc = frag_target.origin; + + if ( autocvar_g_conquest_spawn_choose ) + if ( cq_Count_SelfControlPoints() > 1 ) + stuffcmd(self, "qc_cmd_cl hud clickradar\n"); - return FALSE; ++ return false; +} + +MUTATOR_HOOKFUNCTION(conquest_ResetMap) +{ + for(self = cq_worldcplist; self; self = self.cq_worldcpnext) + self.reset(); // do this now as teams aren't setup in time for PlayerSpawn + + FOR_EACH_PLAYER(self) + PutClientInServer(); - return FALSE; ++ return false; +} + +MUTATOR_HOOKFUNCTION(conquest_PlayerSpawn) +{ + if ( autocvar_g_conquest_spawn_choose ) + if ( self.cq_spawn_by ) - if ( cq_Teleport(self,self.cq_spawn_by,autocvar_g_conquest_teleport_radius,FALSE) ) ++ if ( cq_Teleport(self,self.cq_spawn_by,autocvar_g_conquest_teleport_radius,false) ) + { + self.cq_spawn_by = world; - return FALSE; ++ return false; + } + + float random_target = !(autocvar_g_conquest_spawn_close_to_death), owned_count = cq_Count_SelfControlPoints(); + entity tmp_entity, closest_target = world; + vector spawn_loc = self.cq_deathloc; + + // new joining player or round reset, don't bother checking - //if(spawn_loc == '0 0 0') { return FALSE; } ++ //if(spawn_loc == '0 0 0') { return false; } + + if(random_target) { RandomSelection_Init(); } + + for(tmp_entity = cq_worldcplist; tmp_entity; tmp_entity = tmp_entity.cq_worldcpnext) + { + if(SAME_TEAM(tmp_entity, self) || (!tmp_entity.team && !owned_count)) + if(tmp_entity.cq_status == CP_CAPTURED || !owned_count) + if(random_target) + RandomSelection_Add(tmp_entity, 0, string_null, 1, 1); + else if(vlen(tmp_entity.origin - spawn_loc) <= vlen(closest_target.origin - spawn_loc) || closest_target == world) + closest_target = tmp_entity; + } + + if(random_target) { closest_target = RandomSelection_chosen_ent; } + + if(!closest_target) + { + for(tmp_entity = cq_worldcplist; tmp_entity; tmp_entity = tmp_entity.cq_worldcpnext) + { + if(SAME_TEAM(tmp_entity, self) || (!tmp_entity.team && !owned_count)) + if(tmp_entity.cq_status == CP_CAPTURED || !owned_count) + { + closest_target = tmp_entity; + break; + } + } + } + + if(closest_target) + { + float i; + vector loc; + for(i = 0; i < 10; ++i) + { + loc = closest_target.origin + '0 0 96'; + if(random() >= 0.5) + loc += ('1 1 0' * random()) * 1024; + else + loc -= ('1 1 0' * random()) * 1024; + tracebox(loc, PL_MIN, PL_MAX, loc, MOVE_NORMAL, self); + if(trace_fraction == 1.0 && !trace_startsolid) + { + traceline(closest_target.origin, loc, MOVE_NOMONSTERS, closest_target); // double check to make sure we're not spawning outside the world + if(trace_fraction == 1.0) + { + setorigin(self, loc); + self.angles = normalize(loc - closest_target.origin); - return FALSE; ++ return false; + } + } + } + } + + print("Warning: No spawns found for team ", ftos(self.team), "\n"); - return FALSE; ++ return false; +} + +MUTATOR_HOOKFUNCTION(conquest_SV_ParseClientCommand) +{ + if(MUTATOR_RETURNVALUE) // command was already handled? - return FALSE; ++ return false; + + if ( cmd_name == "cq_spawn" ) + { + vector pos = self.origin; + if(cmd_argc > 1) + pos_x = stof(argv(1)); + if(cmd_argc > 2) + pos_y = stof(argv(2)); + if(cmd_argc > 3) + pos_z = stof(argv(3)); + + if ( IS_PLAYER(self) ) + { + if(!self.vehicle) + if(!self.frozen) + { + entity source_point = cq_Nearest_ControlPoint(self.origin, autocvar_g_conquest_teleport_radius); + + if ( !source_point && self.health > 0 ) + { + sprint(self, "\nYou need to be next to a control point\n"); + return 1; + } + + + entity closest_target = cq_Nearest_ControlPoint_2D(pos, autocvar_g_conquest_click_radius); + + if ( closest_target == world ) + { + sprint(self, "\nNo control point found\n"); + return 1; + } + + if ( self.health <= 0 ) + { + self.cq_spawn_by = closest_target; + self.respawn_flags = self.respawn_flags | RESPAWN_FORCE; + } + else + { + if ( source_point == closest_target ) + { + sprint(self, "\nTeleporting to the same point\n"); + return 1; + } + - if ( !cq_Teleport(self,closest_target,autocvar_g_conquest_teleport_radius,TRUE) ) ++ if ( !cq_Teleport(self,closest_target,autocvar_g_conquest_teleport_radius,true) ) + sprint(self, "\nUnable to teleport there\n"); + } + + return 1; + } + + sprint(self, "\nNo teleportation for you\n"); + } + + return 1; + } + return 0; +} + +MUTATOR_HOOKFUNCTION(conquest_PlayerUseKey) +{ - if(MUTATOR_RETURNVALUE || gameover) { return FALSE; } ++ if(MUTATOR_RETURNVALUE || gameover) { return false; } + + if((time > self.teleport_antispam) && (self.deadflag == DEAD_NO) && !self.vehicle && !self.frozen) + { + entity source_point = cq_Nearest_ControlPoint(self.origin, autocvar_g_conquest_teleport_radius); + if ( source_point ) + { + stuffcmd(self, "qc_cmd_cl hud clickradar\n"); - return TRUE; ++ return true; + } + } + - return FALSE; ++ return false; +} + +MUTATOR_HOOKFUNCTION(conquest_OnEntityPreSpawn) +{ + // onslaught support comes first, as it's most likely to have the best layout + if(self.classname == "onslaught_generator" || self.classname == "onslaught_controlpoint") + { + entity cp = spawn(), oldself = self; + cp.team = self.team; + setorigin(cp, self.origin + '0 0 20'); + self = cp; + droptofloor(); + InitializeEntity(cp, cq_Setup_Compat_ons, INITPRIO_FINDTARGET); + self = oldself; - return FALSE; ++ return false; + } + if(self.classname == "dom_controlpoint") + { + entity cp = spawn(), oldself = self; + // domination doesn't use teams + setorigin(cp, self.origin + '0 0 20'); + self = cp; + droptofloor(); + InitializeEntity(cp, cq_Setup_Compat_dom, INITPRIO_FINDTARGET); + self = oldself; - return FALSE; ++ return false; + } - return FALSE; ++ return false; +} + +MUTATOR_HOOKFUNCTION(conquest_BotRoles) +{ + self.havocbot_role = havocbot_role_cq_offense; - return TRUE; ++ return true; +} + +MUTATOR_HOOKFUNCTION(conquest_GetTeamCount) +{ + ret_float = cq_teams; - return FALSE; ++ return false; +} + +void cq_ScoreRules(float teams) +{ - ScoreRules_basics(teams, SFL_SORT_PRIO_PRIMARY, 0, TRUE); ++ ScoreRules_basics(teams, SFL_SORT_PRIO_PRIMARY, 0, true); + + ScoreInfo_SetLabel_TeamScore(ST_CONQUEST_ROUNDS, "rounds", SFL_SORT_PRIO_PRIMARY); + ScoreInfo_SetLabel_PlayerScore(SP_CONQUEST_LIBERATED, "liberated", 0); + ScoreInfo_SetLabel_PlayerScore(SP_CONQUEST_CAPTURED, "captured", 0); + + ScoreRules_basics_end(); +} + +void cq_DelayedInit() +{ + cq_teams = autocvar_g_conquest_teams; + if(autocvar_g_conquest_teams_override >= 2) { cq_teams = autocvar_g_conquest_teams_override; } + cq_teams = bound(2, cq_teams, 4); + cq_ScoreRules(cq_teams); + + round_handler_Spawn(cq_CheckTeams, cq_CheckWinner, func_null); + round_handler_Init(5, autocvar_g_conquest_warmup, autocvar_g_conquest_round_timelimit); +} + +MUTATOR_DEFINITION(gamemode_conquest) +{ + //precache_model("models/conquest/spawn.md3"); + //precache_model("models/conquest/flag.md3"); + //precache_model("models/conquest/stand.md3"); + + MUTATOR_HOOK(MakePlayerObserver, conquest_RemovePlayer, CBC_ORDER_ANY); + MUTATOR_HOOK(ClientDisconnect, conquest_RemovePlayer, CBC_ORDER_ANY); + MUTATOR_HOOK(PlayerSpawn, conquest_PlayerSpawn, CBC_ORDER_ANY); + MUTATOR_HOOK(PlayerDies, conquest_PlayerDies, CBC_ORDER_ANY); + MUTATOR_HOOK(reset_map_global, conquest_ResetMap, CBC_ORDER_ANY); + MUTATOR_HOOK(SV_ParseClientCommand, conquest_SV_ParseClientCommand, CBC_ORDER_ANY); + MUTATOR_HOOK(PlayerUseKey, conquest_PlayerUseKey, CBC_ORDER_ANY); + MUTATOR_HOOK(OnEntityPreSpawn, conquest_OnEntityPreSpawn, CBC_ORDER_ANY); + MUTATOR_HOOK(HavocBot_ChooseRole, conquest_BotRoles, CBC_ORDER_ANY); + MUTATOR_HOOK(GetTeamCount, conquest_GetTeamCount, CBC_ORDER_ANY); + + + MUTATOR_ONADD + { + if(time > 1) // game loads at time 1 + error("This is a game type and it cannot be added at runtime."); + + InitializeEntity(world, cq_DelayedInit, INITPRIO_GAMETYPE); + } + + MUTATOR_ONREMOVE + { + error("This is a game type and it cannot be removed at runtime."); + } + + return 0; +} diff --cc qcsrc/server/mutators/gamemode_conquest.qh index ee6a1fd9a,000000000..24e37cbc9 mode 100644,000000..100644 --- a/qcsrc/server/mutators/gamemode_conquest.qh +++ b/qcsrc/server/mutators/gamemode_conquest.qh @@@ -1,44 -1,0 +1,47 @@@ - // these are needed since mutators are compiled last ++#ifndef GAMEMODE_CONQUEST_H ++#define GAMEMODE_CONQUEST_H + +//csqc networking flags - #define CQSF_SETUP 1 //! Initial setup, responsible for communicating location, y-angle and model - #define CQSF_TEAM 2 //! What team point belong to - #define CQSF_HEALTH 4 //! Capture progress. Networked as 0--255 - #define CQSF_STATE 8 //! Captured or not - #define CQSF_NAME 16 //! Display name (can be defined by mapper) ++const int CQSF_SETUP = 1; //! Initial setup, responsible for communicating location, y-angle and model ++const int CQSF_TEAM = 2; //! What team point belong to ++const int CQSF_HEALTH = 4; //! Capture progress. Networked as 0--255 ++const int CQSF_STATE = 8; //! Captured or not ++const int CQSF_NAME = 16; //! Display name (can be defined by mapper) + +// score rule declarations - #define ST_CONQUEST_ROUNDS 1 - #define SP_CONQUEST_LIBERATED 4 - #define SP_CONQUEST_CAPTURED 5 ++const int ST_CONQUEST_ROUNDS = 1; ++const int SP_CONQUEST_LIBERATED = 4; ++const int SP_CONQUEST_CAPTURED = 5; + +// list of control points on the map +entity cq_worldcplist; +.entity cq_worldcpnext; + +// control point constants - float cq_teams; ++int cq_teams; +#define CQ_CP_THINKRATE 0.15 + +#define CQ_CP_MIN ('-35 -35 -3') +#define CQ_CP_MAX ('35 35 195') + +// teleportation +.float teleport_antispam; +.vector cq_deathloc; +.entity cq_spawn_by; + +// statuses - #define CP_NEUTRAL 1 - #define CP_CAPTURED 2 ++const int CP_NEUTRAL = 1; ++const int CP_CAPTURED = 2; + +// control point properties - .float cq_status; // status of the control point (CP_NEUTRAL, CP_CAPTURED declared globally) ++.int cq_status; // status of the control point (CP_NEUTRAL, CP_CAPTURED declared globally) + +// compatibility with old maps - #define COMPAT_DOM 1 - #define COMPAT_ONS 2 - .float cq_compat; // for checking if a map already has conquest support ++const int COMPAT_DOM = 1; ++const int COMPAT_ONS = 2; ++.int cq_compat; // for checking if a map already has conquest support + +.float cq_capdistance; - .float cq_originalteam; // stored spawn team for resetting ++.int cq_originalteam; // stored spawn team for resetting ++ ++#endif diff --cc qcsrc/server/mutators/gamemode_ctf.qc index eff1c082e,2e6eb4ff7..4afec770e --- a/qcsrc/server/mutators/gamemode_ctf.qc +++ b/qcsrc/server/mutators/gamemode_ctf.qc @@@ -1,7 -1,7 +1,4 @@@ --// ================================================================ --// Official capture the flag game mode coding, reworked by Samual --// Last updated: September, 2012 --// ================================================================ ++#include "../../common/effects.qh" void ctf_FakeTimeLimit(entity e, float t) { @@@ -126,17 -123,11 +123,17 @@@ float ctf_CaptureShield_CheckStatus(ent float players_worseeq, players_total; if(ctf_captureshield_max_ratio <= 0) - return FALSE; + return false; - s = PlayerScore_Add(p, SP_SCORE, 0); - if(s >= -ctf_captureshield_min_negscore) + s = PlayerScore_Add(p, SP_CTF_CAPS, 0); + s2 = PlayerScore_Add(p, SP_CTF_PICKUPS, 0); + s3 = PlayerScore_Add(p, SP_CTF_RETURNS, 0); + s4 = PlayerScore_Add(p, SP_CTF_FCKILLS, 0); + + sr = ((s - s2) + (s3 + s4)); + + if(sr >= -ctf_captureshield_min_negscore) - return FALSE; + return false; players_total = players_worseeq = 0; FOR_EACH_PLAYER(e) @@@ -176,11 -161,10 +173,11 @@@ void ctf_CaptureShield_Update(entity pl float ctf_CaptureShield_Customize() { - if(self.enemy.active != ACTIVE_ACTIVE) { return TRUE; } - if(!other.ctf_captureshielded) { return FALSE; } - if(CTF_SAMETEAM(self, other)) { return FALSE; } ++ if(self.enemy.active != ACTIVE_ACTIVE) { return true; } + if(!other.ctf_captureshielded) { return false; } - if(SAME_TEAM(self, other)) { return false; } ++ if(CTF_SAMETEAM(self, other)) { return false; } - return TRUE; + return true; } void ctf_CaptureShield_Touch() @@@ -378,10 -350,10 +375,10 @@@ void ctf_Handle_Throw(entity player, en case DROP_THROW: { - makevectors((player.v_angle_y * '0 1 0') + (bound(autocvar_g_ctf_throw_angle_min, player.v_angle_x, autocvar_g_ctf_throw_angle_max) * '1 0 0')); + makevectors((player.v_angle.y * '0 1 0') + (bound(autocvar_g_ctf_throw_angle_min, player.v_angle.x, autocvar_g_ctf_throw_angle_max) * '1 0 0')); - flag_velocity = (('0 0 1' * autocvar_g_ctf_throw_velocity_up) + ((v_forward * autocvar_g_ctf_throw_velocity_forward) * ((player.items & IT_STRENGTH) ? autocvar_g_ctf_throw_strengthmultiplier : 1))); + flag_velocity = (('0 0 1' * autocvar_g_ctf_throw_velocity_up) + ((v_forward * autocvar_g_ctf_throw_velocity_forward))); - flag.velocity = W_CalculateProjectileVelocity(player.velocity, flag_velocity, FALSE); + flag.velocity = W_CalculateProjectileVelocity(player.velocity, flag_velocity, false); ctf_Handle_Drop(flag, player, droptype); break; } @@@ -657,21 -593,6 +654,21 @@@ void ctf_CheckFlagReturn(entity flag, f } } +float ctf_Stalemate_Customize() +{ + // make spectators see what the player would see + entity e, wp_owner; + e = WaypointSprite_getviewentity(other); + wp_owner = self.owner; + + // team waypoints - if(CTF_SAMETEAM(wp_owner.flagcarried, wp_owner)) { return FALSE; } - if(SAME_TEAM(wp_owner, e)) { return FALSE; } - if(!IS_PLAYER(e)) { return FALSE; } ++ if(CTF_SAMETEAM(wp_owner.flagcarried, wp_owner)) { return false; } ++ if(SAME_TEAM(wp_owner, e)) { return false; } ++ if(!IS_PLAYER(e)) { return false; } + - return TRUE; ++ return true; +} + void ctf_CheckStalemate(void) { // declarations @@@ -701,19 -619,12 +698,19 @@@ } } - if(stale_red_flags && stale_blue_flags) + if(ctf_oneflag) + stale_flags = (stale_neutral_flags >= 1); + else + stale_flags = (stale_red_flags >= 1) + (stale_blue_flags >= 1) + (stale_yellow_flags >= 1) + (stale_pink_flags >= 1); + + if(ctf_oneflag && stale_flags == 1) - ctf_stalemate = TRUE; ++ ctf_stalemate = true; + else if(stale_flags == ctf_teams) - ctf_stalemate = TRUE; + ctf_stalemate = true; - else if((!stale_red_flags && !stale_blue_flags) && autocvar_g_ctf_stalemate_endcondition == 2) + else if(stale_flags == 0 && autocvar_g_ctf_stalemate_endcondition == 2) - { ctf_stalemate = FALSE; wpforenemy_announced = FALSE; } + { ctf_stalemate = false; wpforenemy_announced = false; } - else if((!stale_red_flags || !stale_blue_flags) && autocvar_g_ctf_stalemate_endcondition == 1) + else if(stale_flags < ctf_teams && autocvar_g_ctf_stalemate_endcondition == 1) - { ctf_stalemate = FALSE; wpforenemy_announced = FALSE; } + { ctf_stalemate = false; wpforenemy_announced = false; } // if sufficient stalemate, then set up the waypointsprite and announce the stalemate if necessary if(ctf_stalemate) @@@ -721,10 -632,7 +718,10 @@@ for(tmp_entity = ctf_staleflaglist; tmp_entity; tmp_entity = tmp_entity.ctf_staleflagnext) { if((tmp_entity.owner) && (!tmp_entity.owner.wps_enemyflagcarrier)) - WaypointSprite_Spawn("enemyflagcarrier", 0, 0, tmp_entity.owner, FLAG_WAYPOINT_OFFSET, world, tmp_entity.team, tmp_entity.owner, wps_enemyflagcarrier, true, RADARICON_FLAG, WPCOLOR_ENEMYFC(tmp_entity.owner.team)); + { - WaypointSprite_Spawn(((ctf_oneflag) ? "flagcarrier" : "enemyflagcarrier"), 0, 0, tmp_entity.owner, FLAG_WAYPOINT_OFFSET, world, 0, tmp_entity.owner, wps_enemyflagcarrier, TRUE, RADARICON_FLAG, WPCOLOR_ENEMYFC(tmp_entity.owner.team)); ++ WaypointSprite_Spawn(((ctf_oneflag) ? "flagcarrier" : "enemyflagcarrier"), 0, 0, tmp_entity.owner, FLAG_WAYPOINT_OFFSET, world, 0, tmp_entity.owner, wps_enemyflagcarrier, true, RADARICON_FLAG, WPCOLOR_ENEMYFC(tmp_entity.owner.team)); + tmp_entity.owner.wps_enemyflagcarrier.customizeentityforclient = ctf_Stalemate_Customize; + } } if (!wpforenemy_announced) @@@ -741,15 -649,9 +738,15 @@@ void ctf_FlagDamage(entity inflictor, e { if(ITEM_DAMAGE_NEEDKILL(deathtype)) { - // automatically kill the flag and return it - self.health = 0; - ctf_CheckFlagReturn(self, RETURN_NEEDKILL); + if(autocvar_g_ctf_flag_return_damage_delay) + { - self.ctf_flagdamaged = TRUE; ++ self.ctf_flagdamaged = true; + } + else + { + self.health = 0; + ctf_CheckFlagReturn(self, RETURN_NEEDKILL); + } return; } if(autocvar_g_ctf_flag_return_damage) @@@ -1067,22 -943,6 +1064,22 @@@ void ctf_Reset( ctf_RespawnFlag(self); } +void ctf_Use() +{ + if(self.ctf_status != FLAG_BASE) { return; } + + self.active = ((self.active) ? ACTIVE_NOT : ACTIVE_ACTIVE); + + if(self.active == ACTIVE_ACTIVE) + WaypointSprite_Ping(self.wps_flagbase); +} + +float ctf_FlagWaypoint_Customize() +{ - if(self.owner.active != ACTIVE_ACTIVE) { return FALSE; } - return TRUE; ++ if(self.owner.active != ACTIVE_ACTIVE) { return false; } ++ return true; +} + void ctf_DelayedFlagSetup(void) // called after a flag is placed on a map by ctf_FlagSetup() { // bot waypoints @@@ -1091,20 -951,8 +1088,20 @@@ self.bot_basewaypoint = self.nearestwaypoint; // waypointsprites - WaypointSprite_SpawnFixed(((self.team == NUM_TEAM_1) ? "redbase" : "bluebase"), self.origin + FLAG_WAYPOINT_OFFSET, self, wps_flagbase, RADARICON_FLAG, colormapPaletteColor(self.team - 1, false)); - WaypointSprite_UpdateTeamRadar(self.wps_flagbase, RADARICON_FLAG, colormapPaletteColor(self.team - 1, false)); + string basename = "base"; + + switch(self.team) + { + case NUM_TEAM_1: basename = "redbase"; break; + case NUM_TEAM_2: basename = "bluebase"; break; + case NUM_TEAM_3: basename = "yellowbase"; break; + case NUM_TEAM_4: basename = "pinkbase"; break; + default: basename = "neutralbase"; break; + } + + WaypointSprite_SpawnFixed(basename, self.origin + FLAG_WAYPOINT_OFFSET, self, wps_flagbase, RADARICON_FLAG, ((self.team) ? Team_ColorRGB(self.team) : '1 1 1')); - WaypointSprite_UpdateTeamRadar(self.wps_flagbase, RADARICON_FLAG, ((self.team) ? colormapPaletteColor(self.team - 1, FALSE) : '1 1 1')); ++ WaypointSprite_UpdateTeamRadar(self.wps_flagbase, RADARICON_FLAG, ((self.team) ? colormapPaletteColor(self.team - 1, false) : '1 1 1')); + self.wps_flagbase.customizeentityforclient = ctf_FlagWaypoint_Customize; // captureshield setup ctf_CaptureShield_Spawn(self); @@@ -1132,9 -982,8 +1129,9 @@@ void ctf_FlagSetup(float teamnumber, en flag.max_flag_health = ((autocvar_g_ctf_flag_return_damage && autocvar_g_ctf_flag_health) ? autocvar_g_ctf_flag_health : 100); flag.health = flag.max_flag_health; flag.event_damage = ctf_FlagDamage; - flag.pushable = TRUE; + flag.pushable = true; flag.teleportable = TELEPORT_NORMAL; + flag.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_PLAYERCLIP | DPCONTENTS_BOTCLIP; flag.damagedbytriggers = autocvar_g_ctf_flag_return_when_unreachable; flag.damagedbycontents = autocvar_g_ctf_flag_return_when_unreachable; flag.velocity = '0 0 0'; @@@ -2001,13 -1797,9 +1998,13 @@@ MUTATOR_HOOKFUNCTION(ctf_PlayerDies } if(frag_target.flagcarried) - { ctf_Handle_Throw(frag_target, world, DROP_NORMAL); } + { + entity tmp_entity = frag_target.flagcarried; + ctf_Handle_Throw(frag_target, world, DROP_NORMAL); + tmp_entity.ctf_dropper = world; + } - return FALSE; + return false; } MUTATOR_HOOKFUNCTION(ctf_GiveFragsForKill) @@@ -2190,12 -1982,12 +2187,12 @@@ MUTATOR_HOOKFUNCTION(ctf_AbortSpeedrun { if(self.flagcarried) { - Send_Notification(NOTIF_ALL, world, MSG_INFO, APP_TEAM_ENT_2(self.flagcarried, INFO_CTF_FLAGRETURN_ABORTRUN_)); + Send_Notification(NOTIF_ALL, world, MSG_INFO, ((self.flagcarried.team) ? APP_TEAM_ENT_4(self.flagcarried, INFO_CTF_FLAGRETURN_ABORTRUN_) : INFO_CTF_FLAGRETURN_ABORTRUN_NEUTRAL)); ctf_RespawnFlag(self.flagcarried); - return TRUE; + return true; } - return FALSE; + return false; } MUTATOR_HOOKFUNCTION(ctf_MatchEnd) @@@ -2235,61 -2027,60 +2232,61 @@@ MUTATOR_HOOKFUNCTION(ctf_BotRoles) { havocbot_ctf_reset_role(self); - return TRUE; + return true; } - -// ========== -// Spawnfuncs -// ========== - -/*QUAKED spawnfunc_info_player_team1 (1 0 0) (-16 -16 -24) (16 16 24) -CTF Starting point for a player in team one (Red). -Keys: "angle" viewing angle when spawning. */ -void spawnfunc_info_player_team1() +MUTATOR_HOOKFUNCTION(ctf_GetTeamCount) { - if(g_assault) { remove(self); return; } - - self.team = NUM_TEAM_1; // red - spawnfunc_info_player_deathmatch(); + //ret_float = ctf_teams; + ret_string = "ctf_team"; - return TRUE; ++ return true; } - -/*QUAKED spawnfunc_info_player_team2 (1 0 0) (-16 -16 -24) (16 16 24) -CTF Starting point for a player in team two (Blue). -Keys: "angle" viewing angle when spawning. */ -void spawnfunc_info_player_team2() +MUTATOR_HOOKFUNCTION(ctf_SpectateCopy) { - if(g_assault) { remove(self); return; } - - self.team = NUM_TEAM_2; // blue - spawnfunc_info_player_deathmatch(); + self.ctf_flagstatus = other.ctf_flagstatus; - return FALSE; ++ return false; } -/*QUAKED spawnfunc_info_player_team3 (1 0 0) (-16 -16 -24) (16 16 24) -CTF Starting point for a player in team three (Yellow). -Keys: "angle" viewing angle when spawning. */ -void spawnfunc_info_player_team3() +MUTATOR_HOOKFUNCTION(ctf_FormatMessage) { - if(g_assault) { remove(self); return; } + entity bluefc = world, redfc = world, yellowfc = world, pinkfc = world, tmp_entity; // NOTE: blue = red player + entity tfc = world, sfc = world; - self.team = NUM_TEAM_3; // yellow - spawnfunc_info_player_deathmatch(); -} + for(tmp_entity = ctf_worldflaglist; tmp_entity; tmp_entity = tmp_entity.ctf_worldflagnext) + { + if(tmp_entity.owner) + { + switch(tmp_entity.team) + { + case NUM_TEAM_1: redfc = tmp_entity.owner; break; + case NUM_TEAM_2: bluefc = tmp_entity.owner; break; + case NUM_TEAM_3: yellowfc = tmp_entity.owner; break; + case NUM_TEAM_4: pinkfc = tmp_entity.owner; break; + } + if(SAME_TEAM(tmp_entity.owner, self)) { tfc = tmp_entity.owner; } + if(SAME_TEAM(tmp_entity, self)) { sfc = tmp_entity.owner; } + } + } -/*QUAKED spawnfunc_info_player_team4 (1 0 0) (-16 -16 -24) (16 16 24) -CTF Starting point for a player in team four (Purple). -Keys: "angle" viewing angle when spawning. */ -void spawnfunc_info_player_team4() -{ - if(g_assault) { remove(self); return; } + switch(format_escape) + { + case "r": format_replacement = ((tfc) ? tfc.netname : "(nobody)"); break; + case "R": format_replacement = ((sfc) ? sfc.netname : "(nobody)"); break; + case "t": format_replacement = ((redfc) ? redfc.netname : "(nobody)"); break; + case "T": format_replacement = ((bluefc) ? bluefc.netname : "(nobody)"); break; + case "p": format_replacement = ((yellowfc) ? yellowfc.netname : "(nobody)"); break; + case "P": format_replacement = ((pinkfc) ? pinkfc.netname : "(nobody)"); break; + } - return FALSE; - self.team = NUM_TEAM_4; // purple - spawnfunc_info_player_deathmatch(); ++ return false; } +// ========== +// Spawnfuncs +// ========== + /*QUAKED spawnfunc_item_flag_team1 (0 0.5 0.8) (-48 -48 -37) (48 48 37) CTF flag for team one (Red). Keys: @@@ -2409,10 -2145,9 +2406,10 @@@ void spawnfunc_team_CTF_bluespawn() { // ============== // scoreboard setup -void ctf_ScoreRules() +void ctf_ScoreRules(float teams) { - ScoreRules_basics(2, SFL_SORT_PRIO_PRIMARY, 0, true); + CheckAllowedTeams(world); - ScoreRules_basics(teams, SFL_SORT_PRIO_PRIMARY, 0, TRUE); ++ ScoreRules_basics(teams, SFL_SORT_PRIO_PRIMARY, 0, true); ScoreInfo_SetLabel_TeamScore (ST_CTF_CAPS, "caps", SFL_SORT_PRIO_PRIMARY); ScoreInfo_SetLabel_PlayerScore(SP_CTF_CAPS, "caps", SFL_SORT_PRIO_SECONDARY); ScoreInfo_SetLabel_PlayerScore(SP_CTF_CAPTIME, "captime", SFL_LOWER_IS_BETTER | SFL_TIME); @@@ -2440,18 -2175,6 +2437,18 @@@ void ctf_SpawnTeam (string teamname, fl void ctf_DelayedInit() // Do this check with a delay so we can wait for teams to be set up. { + ctf_teams = 2; + + entity tmp_entity; + for(tmp_entity = ctf_worldflaglist; tmp_entity; tmp_entity = tmp_entity.ctf_worldflagnext) + { + if(tmp_entity.team == NUM_TEAM_3) { ctf_teams = max(3, ctf_teams); } + if(tmp_entity.team == NUM_TEAM_4) { ctf_teams = max(4, ctf_teams); } - if(tmp_entity.team == 0) { ctf_oneflag = TRUE; } ++ if(tmp_entity.team == 0) { ctf_oneflag = true; } + } + + ctf_teams = bound(2, ctf_teams, 4); + // if no teams are found, spawn defaults if(find(world, classname, "ctf_team") == world) { diff --cc qcsrc/server/mutators/gamemode_ctf.qh index 2e80aa45f,0e5930db2..a334fbec7 --- a/qcsrc/server/mutators/gamemode_ctf.qh +++ b/qcsrc/server/mutators/gamemode_ctf.qh @@@ -31,12 -33,12 +33,12 @@@ const float WPFE_THINKRATE = 0.5 #define FLAG_PASS_ARC_OFFSET ('0 0 -10') #define VEHICLE_FLAG_OFFSET ('0 0 96') - #define VEHICLE_FLAG_SCALE 1.0 + const float VEHICLE_FLAG_SCALE = 1.0; // waypoint colors - #define WPCOLOR_ENEMYFC(t) ((t) ? colormapPaletteColor(t - 1, FALSE) * 0.75 : '1 1 1') -#define WPCOLOR_ENEMYFC(t) (colormapPaletteColor(t - 1, false) * 0.75) ++#define WPCOLOR_ENEMYFC(t) ((t) ? colormapPaletteColor(t - 1, false) * 0.75 : '1 1 1') #define WPCOLOR_FLAGCARRIER(t) ('0.8 0.8 0') - #define WPCOLOR_DROPPEDFLAG(t) ((t) ? ('0.25 0.25 0.25' + colormapPaletteColor(t - 1, FALSE)) * 0.5 : '1 1 1') -#define WPCOLOR_DROPPEDFLAG(t) (('0.25 0.25 0.25' + colormapPaletteColor(t - 1, false)) * 0.5) ++#define WPCOLOR_DROPPEDFLAG(t) ((t) ? ('0.25 0.25 0.25' + colormapPaletteColor(t - 1, false)) * 0.5 : '1 1 1') // sounds #define snd_flag_taken noise @@@ -122,17 -118,14 +124,17 @@@ float ctf_captureshield_min_negscore; / float ctf_captureshield_max_ratio; // punish at most 30% of each team float ctf_captureshield_force; // push force of the shield +// 1 flag ctf +float ctf_oneflag; // indicates whether or not a neutral flag has been found + // bot player logic - #define HAVOCBOT_CTF_ROLE_NONE 0 - #define HAVOCBOT_CTF_ROLE_DEFENSE 2 - #define HAVOCBOT_CTF_ROLE_MIDDLE 4 - #define HAVOCBOT_CTF_ROLE_OFFENSE 8 - #define HAVOCBOT_CTF_ROLE_CARRIER 16 - #define HAVOCBOT_CTF_ROLE_RETRIEVER 32 - #define HAVOCBOT_CTF_ROLE_ESCORT 64 + const float HAVOCBOT_CTF_ROLE_NONE = 0; + const float HAVOCBOT_CTF_ROLE_DEFENSE = 2; + const float HAVOCBOT_CTF_ROLE_MIDDLE = 4; + const float HAVOCBOT_CTF_ROLE_OFFENSE = 8; + const float HAVOCBOT_CTF_ROLE_CARRIER = 16; + const float HAVOCBOT_CTF_ROLE_RETRIEVER = 32; + const float HAVOCBOT_CTF_ROLE_ESCORT = 64; .float havocbot_cantfindflag; @@@ -140,30 -133,5 +142,32 @@@ vector havocbot_ctf_middlepoint float havocbot_ctf_middlepoint_radius; void havocbot_role_ctf_setrole(entity bot, float role); + +// team checking +#define CTF_SAMETEAM(a,b) ((autocvar_g_ctf_reverse || (ctf_oneflag && autocvar_g_ctf_oneflag_reverse)) ? DIFF_TEAM(a,b) : SAME_TEAM(a,b)) +#define CTF_DIFFTEAM(a,b) ((autocvar_g_ctf_reverse || (ctf_oneflag && autocvar_g_ctf_oneflag_reverse)) ? SAME_TEAM(a,b) : DIFF_TEAM(a,b)) + #endif + +// networked flag statuses - .float ctf_flagstatus; - - const float CTF_RED_FLAG_TAKEN = 1; - const float CTF_RED_FLAG_LOST = 2; - const float CTF_RED_FLAG_CARRYING = 3; - const float CTF_BLUE_FLAG_TAKEN = 4; - const float CTF_BLUE_FLAG_LOST = 8; - const float CTF_BLUE_FLAG_CARRYING = 12; - const float CTF_YELLOW_FLAG_TAKEN = 16; - const float CTF_YELLOW_FLAG_LOST = 32; - const float CTF_YELLOW_FLAG_CARRYING = 48; - const float CTF_PINK_FLAG_TAKEN = 64; - const float CTF_PINK_FLAG_LOST = 128; - const float CTF_PINK_FLAG_CARRYING = 192; - const float CTF_NEUTRAL_FLAG_TAKEN = 256; - const float CTF_NEUTRAL_FLAG_LOST = 512; - const float CTF_NEUTRAL_FLAG_CARRYING = 768; - const float CTF_FLAG_NEUTRAL = 2048; - const float CTF_SHIELDED = 4096; ++.int ctf_flagstatus; ++ ++const int CTF_RED_FLAG_TAKEN = 1; ++const int CTF_RED_FLAG_LOST = 2; ++const int CTF_RED_FLAG_CARRYING = 3; ++const int CTF_BLUE_FLAG_TAKEN = 4; ++const int CTF_BLUE_FLAG_LOST = 8; ++const int CTF_BLUE_FLAG_CARRYING = 12; ++const int CTF_YELLOW_FLAG_TAKEN = 16; ++const int CTF_YELLOW_FLAG_LOST = 32; ++const int CTF_YELLOW_FLAG_CARRYING = 48; ++const int CTF_PINK_FLAG_TAKEN = 64; ++const int CTF_PINK_FLAG_LOST = 128; ++const int CTF_PINK_FLAG_CARRYING = 192; ++const int CTF_NEUTRAL_FLAG_TAKEN = 256; ++const int CTF_NEUTRAL_FLAG_LOST = 512; ++const int CTF_NEUTRAL_FLAG_CARRYING = 768; ++const int CTF_FLAG_NEUTRAL = 2048; ++const int CTF_SHIELDED = 4096; ++ + #endif diff --cc qcsrc/server/mutators/gamemode_cts.qc index 2d054c204,0886a9ce5..9b0d8c669 --- a/qcsrc/server/mutators/gamemode_cts.qc +++ b/qcsrc/server/mutators/gamemode_cts.qc @@@ -276,21 -276,9 +276,21 @@@ MUTATOR_HOOKFUNCTION(cts_SetMods { g_cloaked = 1; // always enable cloak in CTS - return FALSE; + return false; } +MUTATOR_HOOKFUNCTION(cts_WantWeapon) +{ + ret_float = (other.weapon == WEP_SHOTGUN); - return FALSE; ++ return false; +} + +MUTATOR_HOOKFUNCTION(cts_AllowMobSpawning) +{ + ret_string = "You cannot spawn monsters in CTS"; - return TRUE; ++ return true; +} + void cts_Initialize() { cts_ScoreRules(); diff --cc qcsrc/server/mutators/gamemode_deathmatch.qc index 81caee50d,000000000..4779a855a mode 100644,000000..100644 --- a/qcsrc/server/mutators/gamemode_deathmatch.qc +++ b/qcsrc/server/mutators/gamemode_deathmatch.qc @@@ -1,36 -1,0 +1,36 @@@ +void dm_DelayedInit() +{ +} + +MUTATOR_HOOKFUNCTION(dm_CountFrags) +{ + // announce remaining frags - return TRUE; ++ return true; +} + +MUTATOR_DEFINITION(gamemode_deathmatch) +{ + MUTATOR_HOOK(Scores_CountFragsRemaining, dm_CountFrags, CBC_ORDER_ANY); + + MUTATOR_ONADD + { + if(time > 1) // game loads at time 1 + error("This is a game type and it cannot be added at runtime."); + InitializeEntity(world, dm_DelayedInit, INITPRIO_GAMETYPE); + } + + MUTATOR_ONROLLBACK_OR_REMOVE + { + // we actually cannot roll back dm_Initialize here + // BUT: we don't need to! If this gets called, adding always + // succeeds. + } + + MUTATOR_ONREMOVE + { + print("This is a game type and it cannot be removed at runtime."); + return -1; + } + + return 0; +} diff --cc qcsrc/server/mutators/gamemode_domination.qc index 182ec1d0f,dd6e8b210..d9dd8f864 --- a/qcsrc/server/mutators/gamemode_domination.qc +++ b/qcsrc/server/mutators/gamemode_domination.qc @@@ -1,7 -1,18 +1,10 @@@ -void dom_EventLog(string mode, float team_before, entity actor) // use an alias for easy changing and quick editing later -{ - if(autocvar_sv_eventlog) - GameLogEcho(strcat(":dom:", mode, ":", ftos(team_before), ((actor != world) ? (strcat(":", ftos(actor.playerid))) : ""))); -} ++#include "../../common/vehicles/sv_vehicles.qh" ++#include "../round_handler.qh" + -void set_dom_state(entity e) +void dom_EventLog(string mode, float cpteam, entity actor) // use an alias for easy changing and quick editing later { - e.dom_total_pps = total_pps; - e.dom_pps_red = pps_red; - e.dom_pps_blue = pps_blue; - if(domination_teams >= 3) - e.dom_pps_yellow = pps_yellow; - if(domination_teams >= 4) - e.dom_pps_pink = pps_pink; + if(autocvar_sv_eventlog) + GameLogEcho(sprintf(":dom:%s:%d%s", mode, cpteam, ((actor != world) ? strcat(":", ftos(actor.playerid)) : ""))); } void dompoint_captured () @@@ -137,65 -136,7 +140,65 @@@ void AnimateDomPoint( self.frame = 0; } -void dompointthink() +float dom_Cooldown(entity e) +{ + float base, pw, f, c; + + c = 1; + + if(e.cnt == -1 && !e.team) + { + base = autocvar_g_domination_controlpoint_idletime_neutral; + pw = autocvar_g_domination_controlpoint_idletime_neutral_power; + f = autocvar_g_domination_controlpoint_idletime_neutral_factor; + } + else + { + base = autocvar_g_domination_controlpoint_idletime; + pw = autocvar_g_domination_controlpoint_idletime_power; + f = autocvar_g_domination_controlpoint_idletime_factor; + } + + return base + pow(c, pw) * f * base; +} + +float dom_InitialCooldown(entity e) { return ((e.cnt == -1 && !e.team) ? autocvar_g_domination_controlpoint_idletime_neutral_initial : autocvar_g_domination_controlpoint_idletime_initial); } + +void dom_Activate(entity e) +{ + e.model = dom_model[e.team]; + e.modelindex = dom_modelindex[e.team]; + e.skin = dom_skin[e.team]; + - e.dom_active = TRUE; ++ e.dom_active = true; + e.dom_cooldown = 0; + //e.dom_cooldown_max = 0; + setsize(e, DOM_CP_MIN, DOM_CP_MAX); + WaypointSprite_UpdateMaxHealth(e.sprite, 0); + WaypointSprite_UpdateHealth(e.sprite, 0); +} + +void dom_Deactivate(entity e, float cooldown) +{ + e.dom_cooldown_max = max(e.dom_cooldown_max, cooldown); + e.dom_cooldown = max(e.dom_cooldown, cooldown); + + if(e.dom_active && e.dom_cooldown > 0) + { + e.model = dom_model[e.team]; + e.modelindex = dom_modelindex[e.team]; + setsize(e, DOM_CP_MIN, DOM_CP_MAX); - e.dom_active = FALSE; ++ e.dom_active = false; + } +} + +void Dom_ControlPoint_UpdateCooldownProgress(entity e) +{ + WaypointSprite_UpdateMaxHealth(e.sprite, e.dom_cooldown_max); + WaypointSprite_UpdateHealth(e.sprite, e.dom_cooldown_max - e.dom_cooldown); +} + +void dom_ControlPointThink() { float fragamt; @@@ -240,191 -178,116 +243,191 @@@ else self.enemy = world; } + + if(self.dom_cooldown) { Dom_ControlPoint_UpdateCooldownProgress(self); } + else if(!self.dom_active) { dom_Activate(self); } + + if(time - self.pointupdatetime >= 0.1) + { + self.dom_unlock_progress = 0; + self.dom_capturingplayer = world; + } + + self.dom_cooldown = max(0, self.dom_cooldown - frametime); } -void dompointtouch() +void dom_ControlPointTouch() { - entity head; - if (!IS_PLAYER(other)) - return; - if (other.health < 1) - return; + if(gameover) { return; } + if(round_handler_IsActive() && !round_handler_IsRoundStarted()) { return; } + + entity toucher = other; - if(round_handler_IsActive() && !round_handler_IsRoundStarted()) - return; + if(gameover) { return; } + if(!IS_PLAYER(toucher)) { return; } + if(toucher.health < 1 || toucher.frozen) { return; } + if(round_handler_IsActive() && !round_handler_IsRoundStarted()) { return; } - if(time < self.captime + 0.3) - return; + toucher.pointupdatetime = time; - // only valid teams can claim it - head = find(world, classname, "dom_team"); - while (head && head.team != other.team) - head = find(head, classname, "dom_team"); - if (!head || head.netname == "" || head == self.goalentity) + if(SAME_TEAM(toucher, self)) + { + if(time >= self.dom_lastmessage) + { + Send_Notification(NOTIF_ONE, toucher, MSG_CENTER, CENTER_JAILBREAK_WRONGTEAM); + self.dom_lastmessage = time + 1.5; + } + return; + } + + if(!self.dom_active) + { + if(time >= self.dom_lastmessage) + { + Send_Notification(NOTIF_ONE, toucher, MSG_CENTER, CENTER_JAILBREAK_NOTREADY); + self.dom_lastmessage = time + 1.5; + } return; + } + + if(self.dom_capturingplayer && self.dom_capturingplayer != toucher) + { + if(time >= self.dom_lastmessage) + { + Send_Notification(NOTIF_ONE, toucher, MSG_CENTER, CENTER_JAILBREAK_TOOLATE); + self.dom_lastmessage = time + 1.5; + } + return; + } + + //if(self.cnt == -1 && !self.team) + self.dom_unlock_progress = bound(0, self.dom_unlock_progress + frametime * autocvar_g_domination_controlpoint_unlock_speed, 1); - // delay capture + self.pointupdatetime = time; + self.dom_capturingplayer = toucher; + toucher.dom_unlock_progress = self.dom_unlock_progress; - self.team = self.goalentity.team; // this stores the PREVIOUS team! + if(self.dom_unlock_progress >= 1) + { + nades_GiveBonus(toucher, autocvar_g_nades_bonus_score_medium); - self.cnt = other.team; - self.owner = head; // team to switch to after the delay - self.dmg_inflictor = other; + self.team = 0; // neutral for now + self.cnt = toucher.team; - // self.state = 1; - // self.delay = time + cvar("g_domination_point_capturetime"); - //self.nextthink = time + cvar("g_domination_point_capturetime"); - //self.think = dompoint_captured; + self.dmg_inflictor = toucher; - // go to neutral team in the mean time - head = find(world, classname, "dom_team"); - while (head && head.netname != "") - head = find(head, classname, "dom_team"); - if(head == world) - return; + WaypointSprite_UpdateSprites(self.sprite, "dom-neut", "", ""); + WaypointSprite_UpdateTeamRadar(self.sprite, RADARICON_DOMPOINT, '0 1 1'); + WaypointSprite_Ping(self.sprite); - WaypointSprite_UpdateSprites(self.sprite, "dom-neut", "", ""); - WaypointSprite_UpdateTeamRadar(self.sprite, RADARICON_DOMPOINT, '0 1 1'); - WaypointSprite_Ping(self.sprite); + self.skin = dom_skin[self.team]; + self.model = dom_model[self.team]; + self.modelindex = dom_modelindex[self.team]; - self.goalentity = head; - self.model = head.mdl; - self.modelindex = head.dmg; - self.skin = head.skin; + self.dom_lastmessage = time + 2; // no spam - self.enemy = other; // individual player scoring - self.enemy_playerid = other.playerid; - dompoint_captured(); + self.enemy = toucher; // individual player scoring + self.enemy_playerid = toucher.playerid; + dompoint_captured(); + + dom_Deactivate(self, dom_Cooldown(self)); + entity e; + for(e = world; (e = find(e, classname, "dom_controlpoint")) != world; ) + if(e != self) + if(e.team) + dom_Deactivate(e, autocvar_g_domination_controlpoint_idletime); + else + dom_Deactivate(e, autocvar_g_domination_controlpoint_idletime_neutral); + } } -void dom_controlpoint_setup() +void dom_DelayedControlPointSetup(void) // called after a control point is placed on the map by dom_ControlPointSetup() { - entity head; - // find the spawnfunc_dom_team representing unclaimed points - head = find(world, classname, "dom_team"); - while(head && head.netname != "") - head = find(head, classname, "dom_team"); - if (!head) - objerror("no spawnfunc_dom_team with netname \"\" found\n"); - - // copy important properties from spawnfunc_dom_team entity - self.goalentity = head; - setmodel(self, head.mdl); // precision already set - self.skin = head.skin; + // model setup + self.model = dom_model[self.team]; + self.modelindex = dom_modelindex[self.team]; + self.noise = dom_noise[self.team]; // capture sound + self.noise1 = dom_noise1[self.team]; // global capture sound + self.skin = dom_skin[self.team]; + + // appearence + //setmodel(self, self.model); + self.effects = self.effects | EF_LOWPRECISION; + if(autocvar_g_domination_point_fullbright) + self.effects |= EF_FULLBRIGHT; - self.cnt = -1; + dom_Deactivate(self, dom_InitialCooldown(self)); - if(self.message == "") - self.message = " has captured a control point"; + waypoint_spawnforitem(self); + WaypointSprite_SpawnFixed("dom-neut", self.origin + DOM_CP_WPOFFSET, self, sprite, RADARICON_DOMPOINT, '0 1 1'); +} - if(self.frags <= 0) - self.frags = 1; - if(self.wait <= 0) - self.wait = 5; +void dom_Reset() +{ + // reset team + self.team = 0; + self.cnt = -1; - float points, waittime; - if (autocvar_g_domination_point_amt) - points = autocvar_g_domination_point_amt; - else - points = self.frags; - if (autocvar_g_domination_point_rate) - waittime = autocvar_g_domination_point_rate; - else - waittime = self.wait; + // model setup + self.model = dom_model[self.team]; + self.modelindex = dom_modelindex[self.team]; + self.noise = dom_noise[self.team]; // capture sound + self.noise1 = dom_noise1[self.team]; // global capture sound + self.skin = dom_skin[self.team]; + + // main setup + self.think = dom_ControlPointThink; + self.nextthink = time; - total_pps += points/waittime; + // waypoint setup + WaypointSprite_UpdateSprites(self.sprite, "dom-neut", "", ""); + WaypointSprite_UpdateTeamRadar(self.sprite, RADARICON_DOMPOINT, '0 1 1'); - if(!self.t_width) - self.t_width = 0.02; // frame animation rate - if(!self.t_length) - self.t_length = 239; // maximum frame + dom_Deactivate(self, dom_InitialCooldown(self)); +} - self.think = dompointthink; - self.nextthink = time; - self.touch = dompointtouch; - self.solid = SOLID_TRIGGER; - self.flags = FL_ITEM; - setsize(self, '-32 -32 -32', '32 32 32'); - setorigin(self, self.origin + '0 0 20'); - droptofloor(); +void dom_ControlPointSetup(entity cp) +{ + // declarations + self = cp; // for later usage with droptofloor() + + if(!cp.t_width) { cp.t_width = CP_TICRATE; } + if(!cp.t_length) { cp.t_length = CP_MAXFRAMES; } + if(!cp.scale) { cp.scale = CP_SCALE; } + if(!cp.frags) { cp.frags = 1; } + if(!cp.wait) { cp.wait = 5; } + + total_pps += ((autocvar_g_domination_point_amt) ? autocvar_g_domination_point_amt : cp.frags) / ((autocvar_g_domination_point_rate) ? autocvar_g_domination_point_rate : cp.wait); + + // main setup + cp.think = dom_ControlPointThink; + cp.nextthink = time + 0.5; + cp.touch = dom_ControlPointTouch; + cp.solid = SOLID_TRIGGER; + cp.dom_capturingplayer = world; + cp.reset = dom_Reset; + cp.flags = FL_ITEM; - cp.dom_active = TRUE; ++ cp.dom_active = true; + cp.cnt = -1; + cp.team = 0; + setsize(cp, DOM_CP_MIN, DOM_CP_MAX); + setorigin(cp, cp.origin + '0 0 20'); // move up so droptofloor doesn't put it in solid + + // control point placement + if((cp.spawnflags & 1) || cp.noalign) // don't drop to floor, just stay at fixed location + { - cp.noalign = TRUE; ++ cp.noalign = true; + cp.movetype = MOVETYPE_NONE; + } + else // drop to floor, automatically find a platform and set that as spawn origin + { - cp.noalign = FALSE; ++ cp.noalign = false; + self = cp; + droptofloor(); + cp.movetype = MOVETYPE_NONE; + } - waypoint_spawnforitem(self); - WaypointSprite_SpawnFixed("dom-neut", self.origin + '0 0 32', self, sprite, RADARICON_DOMPOINT, '0 1 1'); + InitializeEntity(cp, dom_DelayedControlPointSetup, INITPRIO_SETLOCATION); } float total_controlpoints, redowned, blueowned, yellowowned, pinkowned; @@@ -476,7 -339,6 +479,7 @@@ float Domination_CheckWinner( Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_ROUND_OVER); Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_ROUND_OVER); round_handler_Init(5, autocvar_g_domination_warmup, autocvar_g_domination_round_timelimit); - nades_Clear(world, TRUE); ++ nades_Clear(world, true); return 1; } @@@ -498,11 -360,9 +501,11 @@@ Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_ROUND_TIED); Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_ROUND_TIED); } - + round_handler_Init(5, autocvar_g_domination_warmup, autocvar_g_domination_round_timelimit); - nades_Clear(world, TRUE); ++ nades_Clear(world, true); + return 1; } @@@ -545,59 -398,37 +548,59 @@@ void havocbot_role_dom( MUTATOR_HOOKFUNCTION(dom_GetTeamCount) { - ret_float = domination_teams; - return 0; + //ret_float = domination_teams; + ret_string = "dom_team"; - return TRUE; ++ return true; } MUTATOR_HOOKFUNCTION(dom_ResetMap) { total_pps = 0, pps_red = 0, pps_blue = 0, pps_yellow = 0, pps_pink = 0; - return TRUE; - FOR_EACH_PLAYER(self) ++ return true; +} + +MUTATOR_HOOKFUNCTION(dom_PlayerThink) +{ + if(round_handler_IsActive() && !round_handler_IsRoundStarted()) { - PutClientInServer(); - self.player_blocked = 1; - if(IS_REAL_CLIENT(self)) - set_dom_state(self); + self.dom_unlock_progress = 0; - return FALSE; ++ return false; } - return 1; + + if(time - self.pointupdatetime >= 0.01) + self.dom_unlock_progress = 0; + + self.dom_total_pps = total_pps; + self.dom_pps_red = pps_red; + self.dom_pps_blue = pps_blue; + self.dom_pps_yellow = pps_yellow; + self.dom_pps_pink = pps_pink; + - return FALSE; ++ return false; } MUTATOR_HOOKFUNCTION(dom_PlayerSpawn) { - return FALSE; - if(domination_roundbased) - if(!round_handler_IsRoundStarted()) - self.player_blocked = 1; - else - self.player_blocked = 0; + return false; } -MUTATOR_HOOKFUNCTION(dom_ClientConnect) +MUTATOR_HOOKFUNCTION(dom_SpectateCopy) +{ + self.dom_unlock_progress = other.dom_unlock_progress; - return FALSE; ++ return false; +} + +MUTATOR_HOOKFUNCTION(dom_PlayerDamage) { - set_dom_state(self); + entity e; + if(self.dom_unlock_progress) + { + for(e = world; (e = find(e, classname, "dom_controlpoint")) != world; ) + if(e.dom_capturingplayer == self) + e.dom_unlock_progress = bound(0, e.dom_unlock_progress - autocvar_g_domination_controlpoint_unlock_damage_pushback, 1); + } + - return FALSE; + return false; } MUTATOR_HOOKFUNCTION(dom_BotRoles) @@@ -821,5 -651,5 +824,5 @@@ MUTATOR_DEFINITION(gamemode_domination return -1; } - return FALSE; - return 0; ++ return false; } diff --cc qcsrc/server/mutators/gamemode_freezetag.qc index b1306a8d0,d6285cdaf..bd2a531ef --- a/qcsrc/server/mutators/gamemode_freezetag.qc +++ b/qcsrc/server/mutators/gamemode_freezetag.qc @@@ -1,13 -1,13 +1,15 @@@ ++#include "../round_handler.qh" ++ .float freezetag_frozen_time; .float freezetag_frozen_timeout; - #define ICE_MAX_ALPHA 1 - #define ICE_MIN_ALPHA 0.1 + const float ICE_MAX_ALPHA = 1; + const float ICE_MIN_ALPHA = 0.1; float freezetag_teams; - #define SP_FREEZETAG_REVIVALS 4 + const float SP_FREEZETAG_REVIVALS = 4; void freezetag_ScoreRules(float teams) { - ScoreRules_basics(teams, SFL_SORT_PRIO_PRIMARY, SFL_SORT_PRIO_PRIMARY, TRUE); // SFL_SORT_PRIO_PRIMARY + ScoreRules_basics(teams, SFL_SORT_PRIO_PRIMARY, SFL_SORT_PRIO_PRIMARY, true); // SFL_SORT_PRIO_PRIMARY ScoreInfo_SetLabel_PlayerScore(SP_FREEZETAG_REVIVALS, "revivals", 0); ScoreRules_basics_end(); } @@@ -39,11 -39,9 +41,11 @@@ void freezetag_count_alive_players( #define FREEZETAG_ALIVE_TEAMS() ((redalive > 0) + (bluealive > 0) + (yellowalive > 0) + (pinkalive > 0)) #define FREEZETAG_ALIVE_TEAMS_OK() (FREEZETAG_ALIVE_TEAMS() == freezetag_teams) - float prev_missing_teams_mask; float freezetag_CheckTeams() { + freezetag_count_alive_players(); + + static float prev_missing_teams_mask; if(FREEZETAG_ALIVE_TEAMS_OK()) { if(prev_missing_teams_mask > 0) @@@ -94,44 -92,22 +96,44 @@@ float freezetag_getWinnerTeam( return -1; // no player left } +float ft_EnemyWaypoint_Customize() +{ - if(self.owner.frozen || self.owner.deadflag) { return FALSE; } ++ if(self.owner.frozen || self.owner.deadflag) { return false; } + + entity e = WaypointSprite_getviewentity(other); + - if(SAME_TEAM(self.owner, e)) { return FALSE; } ++ if(SAME_TEAM(self.owner, e)) { return false; } + - return TRUE; ++ return true; +} + +void ft_EnemyWaypoints(entity player) +{ - WaypointSprite_Spawn("enemy", 0, 0, player, ENEMY_WAYPOINT_OFFSET, world, 0, player, wps_enemy, TRUE, RADARICON_FLAG, WPCOLOR_ENEMY(player.team)); ++ WaypointSprite_Spawn("enemy", 0, 0, player, ENEMY_WAYPOINT_OFFSET, world, 0, player, wps_enemy, true, RADARICON_FLAG, WPCOLOR_ENEMY(player.team)); + WaypointSprite_UpdateMaxHealth(player.wps_enemy, '1 0 0' * healtharmor_maxdamage(start_health, start_armorvalue, autocvar_g_balance_armor_blockpercent, DEATH_WEAPON) * 2); + WaypointSprite_UpdateHealth(player.wps_enemy, '1 0 0' * healtharmor_maxdamage(player.health, player.armorvalue, autocvar_g_balance_armor_blockpercent, DEATH_WEAPON)); + WaypointSprite_UpdateTeamRadar(player.wps_enemy, RADARICON_FLAGCARRIER, WPCOLOR_ENEMY(player.team)); + + player.wps_enemy.customizeentityforclient = ft_EnemyWaypoint_Customize; +} + float freezetag_CheckWinner() { entity e; + + if(!ft_stalemate) if(round_handler_GetEndTime() > 0 && round_handler_GetEndTime() - time <= 0) { - ft_stalemate = TRUE; - Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_ROUND_OVER); - Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_ROUND_OVER); ++ ft_stalemate = true; + Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_FREEZETAG_STALEMATE); FOR_EACH_PLAYER(e) { - e.freezetag_frozen_timeout = 0; - nades_Clear(e); + ft_EnemyWaypoints(e); } - round_handler_Init(5, autocvar_g_freezetag_warmup, autocvar_g_freezetag_round_timelimit); - return 1; } + freezetag_count_alive_players(); + if(FREEZETAG_ALIVE_TEAMS() > 1) return 0; @@@ -149,16 -125,11 +151,16 @@@ Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_ROUND_TIED); } - ft_stalemate = FALSE; ++ ft_stalemate = false; FOR_EACH_PLAYER(e) { e.freezetag_frozen_timeout = 0; - nades_Clear(e); + e.revive_progress = 0; + WaypointSprite_Kill(e.wps_enemy); } + - nades_Clear(world, TRUE); ++ nades_Clear(world, true); + round_handler_Init(5, autocvar_g_freezetag_warmup, autocvar_g_freezetag_round_timelimit); return 1; } @@@ -452,12 -418,7 +454,12 @@@ MUTATOR_HOOKFUNCTION(freezetag_reset_ma return 1; } +MUTATOR_HOOKFUNCTION(freezetag_ResetMap) +{ - ft_stalemate = FALSE; - return FALSE; ++ ft_stalemate = false; ++ return false; +} + MUTATOR_HOOKFUNCTION(freezetag_GiveFragsForKill) { frag_score = 0; // no frags counted in Freeze Tag @@@ -583,7 -542,7 +585,7 @@@ MUTATOR_HOOKFUNCTION(freezetag_BotRoles MUTATOR_HOOKFUNCTION(freezetag_GetTeamCount) { ret_float = freezetag_teams; - return FALSE; - return 0; ++ return false; } void freezetag_Initialize() diff --cc qcsrc/server/mutators/gamemode_freezetag.qh index 48ddda235,000000000..3fbb40062 mode 100644,000000..100644 --- a/qcsrc/server/mutators/gamemode_freezetag.qh +++ b/qcsrc/server/mutators/gamemode_freezetag.qh @@@ -1,8 -1,0 +1,8 @@@ +// stalemate +float ft_stalemate; + +// waypoint setup +.entity wps_enemy; - #define WPCOLOR_ENEMY(t) ((t) ? colormapPaletteColor(t - 1, FALSE) * 0.75 : '1 1 1') ++#define WPCOLOR_ENEMY(t) ((t) ? colormapPaletteColor(t - 1, false) * 0.75 : '1 1 1') + +#define ENEMY_WAYPOINT_OFFSET ('0 0 64') diff --cc qcsrc/server/mutators/gamemode_infection.qc index 97cfc205c,000000000..266069835 mode 100644,000000..100644 --- a/qcsrc/server/mutators/gamemode_infection.qc +++ b/qcsrc/server/mutators/gamemode_infection.qc @@@ -1,294 -1,0 +1,294 @@@ +void INF_count_alive_players() +{ + entity e; + float i; + for(i = 1; i <= infection_teams; ++i) { aliveplayers[i] = 0; } + FOR_EACH_PLAYER(e) + { + ++total_players; + + if(e.health > 0) + aliveplayers[e.inf_team] += 1; + } +} + +void INF_count_survivors() +{ + entity e; + float i; + for(i = 1; i <= infection_teams; ++i) { aliveplayers[i] = 0; } + FOR_EACH_PLAYER(e) + { + ++total_players; + if(e.inf_realteam == e.inf_team) + //if(e.health > 0) // TODO: check if this is needed? + aliveplayers[e.inf_realteam] += 1; + } +} + +float inf_alive_teams() +{ + float total_alive = 0; + float i; + for(i = 1; i <= infection_teams; ++i) + if(aliveplayers[i] >= 1) + total_alive += 1; + + return total_alive; +} +#define INF_ALIVE_TEAMS_OK() (inf_alive_teams() == infection_teams) + +float Infection_CheckWinner() +{ + entity e; + + if(round_handler_GetEndTime() > 0 && round_handler_GetEndTime() - time <= 0) + { + Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_ROUND_OVER); + Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_ROUND_OVER); - allowed_to_spawn = FALSE; ++ allowed_to_spawn = false; + round_handler_Init(5, autocvar_g_infection_warmup, autocvar_g_infection_round_timelimit); - nades_Clear(world, TRUE); ++ nades_Clear(world, true); + return 1; + } + + INF_count_survivors(); + if(inf_alive_teams() > 1) + return 0; + + float winner_team = 0; + + FOR_EACH_CLIENT(e) + { + if(e.inf_realteam == e.inf_team) + if(IS_PLAYER(e)) + if(e.inf_nextteam == 0) + winner_team = e.inf_realteam; + } + + if(winner_team) + { + Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_ROUND_INFECTION_WIN); + Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_ROUND_INFECTION_WIN); + FOR_EACH_CLIENT(e) + { + if(e.inf_team == e.inf_realteam) + if(e.inf_realteam == winner_team) + { + PlayerScore_Add(e, SP_INF_ROUNDS, +1); + UpdateFrags(e, +1); + } + } + } + else + { + Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_ROUND_TIED); + Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_ROUND_TIED); + } + + round_handler_Init(5, autocvar_g_infection_warmup, autocvar_g_infection_round_timelimit); + - nades_Clear(world, TRUE); ++ nades_Clear(world, true); + + return 1; +} + +void INF_RoundStart() +{ + allowed_to_spawn = warmup_stage; + + entity head; + FOR_EACH_PLAYER(head) + { + head.inf_realteam = head.inf_team; + head.inf_nextteam = 0; + } +} + +float INF_CheckTeams() +{ - allowed_to_spawn = TRUE; ++ allowed_to_spawn = true; + INF_count_alive_players(); + if(INF_ALIVE_TEAMS_OK()) + { + if(prev_total_players > 0) + Kill_Notification(NOTIF_ALL, world, MSG_CENTER_CPID, CPID_MISSING_PLAYERS); + prev_total_players = -1; + return 1; + } + if(prev_total_players != total_players) + { + float plcnt = 0, i; + for(i = 1; i <= infection_teams; ++i) + { + if(aliveplayers[i] < 1) + ++plcnt; + } + Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_MISSING_PLAYERS, plcnt); + prev_total_players = total_players; + } + return 0; +} + +float inf_PickSmallestTeam() +{ + INF_count_alive_players(); + float i, smallest_team = 0; + + for(i = 1; i <= infection_teams; ++i) + { + if(aliveplayers[i] <= aliveplayers[smallest_team]) + smallest_team = i; + } + + return smallest_team; +} + +MUTATOR_HOOKFUNCTION(inf_RemovePlayer) +{ + self.inf_team = 0; + self.inf_realteam = 0; + self.inf_nextteam = 0; - return FALSE; ++ return false; +} + +MUTATOR_HOOKFUNCTION(inf_BotAttack) +{ + if(self.inf_team == other.inf_team) - return TRUE; ++ return true; + - return FALSE; ++ return false; +} + +MUTATOR_HOOKFUNCTION(inf_PlayerThink) +{ + // temporary hack to fix colors + setcolor(self, 16 * self.inf_team + self.inf_team); - return FALSE; ++ return false; +} + +MUTATOR_HOOKFUNCTION(inf_PlayerSpawn) +{ + if(IS_REAL_CLIENT(self)) + if(!self.inf_team) + if(bots_would_leave) + { - entity head = findchainfloat(isbot, TRUE); ++ entity head = findchainfloat(isbot, true); + if(head) + { + entity best = head; + float besttime = head.createdtime; + while (head) + { + if (besttime < head.createdtime) + { + besttime = head.createdtime; + best = head; + } + head = head.chain; + } + + self.inf_team = self.inf_realteam = best.inf_realteam; + } + } + + if(self.inf_nextteam) // respawning player + { + self.inf_team = self.inf_nextteam; + self.inf_nextteam = 0; + } + else if(!self.inf_team) // new joining player + self.inf_realteam = self.inf_team = inf_PickSmallestTeam(); + + setcolor(self, 16 * self.inf_team + self.inf_team); + - return FALSE; ++ return false; +} + +MUTATOR_HOOKFUNCTION(inf_PlayerDies) +{ + if(IS_PLAYER(frag_attacker) && frag_attacker.inf_team != frag_target.inf_team) + { + Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_INFECTION_INFECTED, frag_target.netname, frag_attacker.netname); + Send_Notification(NOTIF_ONE, frag_target, MSG_CENTER, CENTER_INFECTION_INFECTED, frag_attacker.netname); + frag_target.inf_nextteam = frag_attacker.inf_team; + frag_target.respawn_flags |= RESPAWN_FORCE; + + PlayerScore_Add(frag_target, SP_SCORE, -1); + } + - return FALSE; ++ return false; +} + +MUTATOR_HOOKFUNCTION(inf_ResetMap) +{ + FOR_EACH_PLAYER(self) + { + if(self.inf_realteam && self.inf_realteam != self.inf_team) + self.inf_team = self.inf_realteam; + self.inf_nextteam = 0; + PutClientInServer(); // player re-spawns on new team? + } + + FOR_EACH_SPEC(self) if(IS_BOT_CLIENT(self)) + { + self.inf_realteam = self.inf_team = inf_PickSmallestTeam(); + self.inf_nextteam = 0; + + PutClientInServer(); + } + - return TRUE; ++ return true; +} + +MUTATOR_HOOKFUNCTION(inf_ForbidPlayerScore_Clear) +{ - return TRUE; ++ return true; +} + +// scoreboard stuff +void inf_ScoreRules() +{ - ScoreRules_basics(0, SFL_SORT_PRIO_PRIMARY, 0, TRUE); ++ ScoreRules_basics(0, SFL_SORT_PRIO_PRIMARY, 0, true); + ScoreInfo_SetLabel_PlayerScore(SP_INF_ROUNDS, "survivals", SFL_SORT_PRIO_PRIMARY); + ScoreRules_basics_end(); +} + +void inf_Initialize() +{ - allowed_to_spawn = TRUE; ++ allowed_to_spawn = true; + + infection_teams = autocvar_g_infection_teams; + + inf_ScoreRules(); + + round_handler_Spawn(INF_CheckTeams, Infection_CheckWinner, INF_RoundStart); + round_handler_Init(5, autocvar_g_infection_warmup, autocvar_g_infection_round_timelimit); +} + +MUTATOR_DEFINITION(gamemode_infection) +{ + MUTATOR_HOOK(MakePlayerObserver, inf_RemovePlayer, CBC_ORDER_ANY); + MUTATOR_HOOK(PlayerPreThink, inf_PlayerThink, CBC_ORDER_ANY); + MUTATOR_HOOK(PlayerSpawn, inf_PlayerSpawn, CBC_ORDER_ANY); + MUTATOR_HOOK(ClientDisconnect, inf_RemovePlayer, CBC_ORDER_ANY); + MUTATOR_HOOK(BotShouldAttack, inf_BotAttack, CBC_ORDER_ANY); + MUTATOR_HOOK(PlayerDies, inf_PlayerDies, CBC_ORDER_ANY); + MUTATOR_HOOK(reset_map_players, inf_ResetMap, CBC_ORDER_ANY); + MUTATOR_HOOK(ForbidPlayerScore_Clear, inf_ForbidPlayerScore_Clear, CBC_ORDER_ANY); + + MUTATOR_ONADD + { + if(time > 1) // game loads at time 1 + error("This is a game type and it cannot be added at runtime."); + inf_Initialize(); + } + + MUTATOR_ONREMOVE + { + print("This is a game type and it cannot be removed at runtime."); + return -1; + } + - return FALSE; ++ return false; +} diff --cc qcsrc/server/mutators/gamemode_infection.qh index 9f75f89b5,000000000..b7e56f6e4 mode 100644,000000..100644 --- a/qcsrc/server/mutators/gamemode_infection.qh +++ b/qcsrc/server/mutators/gamemode_infection.qh @@@ -1,13 -1,0 +1,11 @@@ - float total_players; +float infection_teams; +float aliveplayers[17]; - float allowed_to_spawn; + +.float inf_team; +.float inf_nextteam; +.float inf_realteam; + +float prev_total_players; + +// scores +#define SP_INF_ROUNDS 4 diff --cc qcsrc/server/mutators/gamemode_invasion.qc index ec4da5fb7,fe0d0a7ec..833911be6 --- a/qcsrc/server/mutators/gamemode_invasion.qc +++ b/qcsrc/server/mutators/gamemode_invasion.qc @@@ -1,3 -1,3 +1,5 @@@ ++#include "../../common/monsters/sv_monsters.qh" ++ void spawnfunc_invasion_spawnpoint() { if(!g_invasion) { remove(self); return; } @@@ -209,8 -213,6 +211,8 @@@ float Invasion_CheckWinner( round_handler_Init(5, autocvar_g_invasion_warmup, autocvar_g_invasion_round_timelimit); - nades_Clear(world, TRUE); ++ nades_Clear(world, true); + return 1; } @@@ -356,10 -358,10 +358,10 @@@ MUTATOR_HOOKFUNCTION(invasion_PlayerCom MUTATOR_HOOKFUNCTION(invasion_BotShouldAttack) { - if(!(checkentity.flags & FL_MONSTER)) + if(!IS_MONSTER(other)) - return TRUE; + return true; - + - return FALSE; + return false; } MUTATOR_HOOKFUNCTION(invasion_SetStartItems) @@@ -379,8 -381,8 +381,8 @@@ MUTATOR_HOOKFUNCTION(invasion_AccuracyT MUTATOR_HOOKFUNCTION(invasion_AllowMobSpawning) { - // monster spawning disabled during an invasion + ret_string = "You can't spawn monsters during an invasion!"; - return TRUE; + return true; } MUTATOR_HOOKFUNCTION(invasion_GetTeamCount) @@@ -414,8 -416,6 +416,8 @@@ void invasion_DelayedInit() // Do this round_handler_Spawn(Invasion_CheckPlayers, Invasion_CheckWinner, Invasion_RoundStart); round_handler_Init(5, autocvar_g_invasion_warmup, autocvar_g_invasion_round_timelimit); - allowed_to_spawn = TRUE; ++ allowed_to_spawn = true; + inv_roundcnt = 0; inv_maxrounds = 15; // 15? } diff --cc qcsrc/server/mutators/gamemode_jailbreak.qc index b9f9a9f15,000000000..ec92eb67c mode 100644,000000..100644 --- a/qcsrc/server/mutators/gamemode_jailbreak.qc +++ b/qcsrc/server/mutators/gamemode_jailbreak.qc @@@ -1,1344 -1,0 +1,1342 @@@ - // round handling - float total_players; - float redalive, bluealive, yellowalive, pinkalive; - .float redalive_stat, bluealive_stat, yellowalive_stat, pinkalive_stat; - float allowed_to_spawn; ++#include "../../common/effects.qh" ++#include "../round_handler.qh" + ++// round handling +void JB_count_alive_players() +{ + entity e; + total_players = redalive = bluealive = yellowalive = pinkalive = 0; + FOR_EACH_PLAYER(e) + { + switch(e.team) + { + case NUM_TEAM_1: ++total_players; if(!e.jb_isprisoned) ++redalive; break; + case NUM_TEAM_2: ++total_players; if(!e.jb_isprisoned) ++bluealive; break; + case NUM_TEAM_3: ++total_players; if(!e.jb_isprisoned) ++yellowalive; break; + case NUM_TEAM_4: ++total_players; if(!e.jb_isprisoned) ++pinkalive; break; + } + } + FOR_EACH_REALCLIENT(e) + { + e.redalive_stat = redalive; + e.bluealive_stat = bluealive; + e.yellowalive_stat = yellowalive; + e.pinkalive_stat = pinkalive; + } +} + +float JB_GetWinnerTeam() +{ + float winner_team = 0; + if(redalive >= 1) + winner_team = NUM_TEAM_1; + if(bluealive >= 1) + { + if(winner_team) return 0; + winner_team = NUM_TEAM_2; + } + if(yellowalive >= 1) + { + if(winner_team) return 0; + winner_team = NUM_TEAM_3; + } + if(pinkalive >= 1) + { + if(winner_team) return 0; + winner_team = NUM_TEAM_4; + } + if(winner_team) + return winner_team; + return -1; // no player left +} + +#define JB_ALIVE_TEAMS() ((redalive > 0) + (bluealive > 0) + (yellowalive > 0) + (pinkalive > 0)) +#define JB_ALIVE_TEAMS_OK() (JB_ALIVE_TEAMS() == jb_teams) +float JB_CheckWinner() +{ + entity e, oldself; + if(round_handler_GetEndTime() > 0 && round_handler_GetEndTime() - time <= 0) + { + Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_ROUND_OVER); + Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_ROUND_OVER); - allowed_to_spawn = FALSE; ++ allowed_to_spawn = false; + round_handler_Init(5, autocvar_g_jailbreak_warmup, autocvar_g_jailbreak_round_timelimit); + FOR_EACH_PLAYER(e) + { + if(!e.jb_isprisoned) + { + oldself = self; + self = e; + if(!e.jb_isprisoned) + e.player_blocked = 1; + PutClientInServer(); + self = oldself; + } + } - nades_Clear(world, TRUE); - jb_roundover = TRUE; ++ nades_Clear(world, true); ++ jb_roundover = true; + return 1; + } + + JB_count_alive_players(); + if(JB_ALIVE_TEAMS() > 1) + return 0; + + float winner_team = JB_GetWinnerTeam(); + + if(JB_JailIsOpen(winner_team)) + return 0; // ??? + + if(winner_team > 0) + { + Send_Notification(NOTIF_ALL, world, MSG_CENTER, APP_TEAM_NUM_4(winner_team, CENTER_ROUND_TEAM_WIN_)); + Send_Notification(NOTIF_ALL, world, MSG_INFO, APP_TEAM_NUM_4(winner_team, INFO_ROUND_TEAM_WIN_)); + TeamScore_AddToTeam(winner_team, ST_JB_ROUNDS, +1); + + JB_ActivateCamera(winner_team); + JB_TorturePrisonersLater(winner_team, 3); + } + else if(winner_team == -1) + { + Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_ROUND_TIED); + Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_ROUND_TIED); + } + - jb_roundover = TRUE; ++ jb_roundover = true; + - allowed_to_spawn = FALSE; ++ allowed_to_spawn = false; + round_handler_Init(JB_TORTURE_DURATION, autocvar_g_jailbreak_warmup, autocvar_g_jailbreak_round_timelimit); + + FOR_EACH_PLAYER(e) + { + if(!e.jb_isprisoned) + { + oldself = self; + self = e; + e.player_blocked = 1; + PutClientInServer(); + self = oldself; + } + } + - nades_Clear(world, TRUE); ++ nades_Clear(world, true); + + return 1; +} + +void JB_RoundStart() +{ + if(warmup_stage) - allowed_to_spawn = TRUE; ++ allowed_to_spawn = true; + else - allowed_to_spawn = FALSE; ++ allowed_to_spawn = false; +} + - float prev_missing_teams_mask; +float JB_CheckTeams() +{ - allowed_to_spawn = TRUE; ++ static float prev_missing_teams_mask; ++ allowed_to_spawn = true; + JB_count_alive_players(); + if(JB_ALIVE_TEAMS_OK()) + { + if(prev_missing_teams_mask > 0) + Kill_Notification(NOTIF_ALL, world, MSG_CENTER_CPID, CPID_MISSING_TEAMS); + prev_missing_teams_mask = -1; + return 1; + } + if(total_players == 0) + { + if(prev_missing_teams_mask > 0) + Kill_Notification(NOTIF_ALL, world, MSG_CENTER_CPID, CPID_MISSING_TEAMS); + prev_missing_teams_mask = -1; + return 0; + } + float missing_teams_mask = (!redalive) + (!bluealive) * 2; + if(jb_teams >= 3) missing_teams_mask += (!yellowalive) * 4; + if(jb_teams >= 4) missing_teams_mask += (!pinkalive) * 8; + if(prev_missing_teams_mask != missing_teams_mask) + { + Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_MISSING_TEAMS, missing_teams_mask); + prev_missing_teams_mask = missing_teams_mask; + } + return 0; +} + +// logging +void jb_debug(string input) +{ + switch(autocvar_g_jailbreak_debug) + { + case 1: dprint(input); break; + case 2: print(input); break; + case 3: bprint(input); break; + } +} + +void JB_AddDoor(entity door, float theteam, vector vdata, string sdata, entity cpoint) +{ + if(door.classname != "door" && door.classname != "door_rotating") + { + jb_debug(sprintf("Warning: %s^7 at %v is linked with an entity of unhandled class (%s^7)\n", JB_ControlPoint_Name(cpoint), cpoint.origin, door.classname)); + return; + } + + door.jb_worlddoornext = jb_worlddoorlist; // link door into jb_worlddoorlist + jb_worlddoorlist = door; +} + +float jb_doors_opened[2]; +void JB_MaybeOpenDoor(entity door, float openjails, vector vdata, string sdata, entity cpoint) +{ + if(openjails == OPENJAILS_LOCKED && door.jaildoormode != JAILDOORMODE_OPEN) + return; + + if(openjails == OPENJAILS_OPEN && door.jaildoormode == JAILDOORMODE_CLOSED) + return; + + // OPENJAILS_LOCKED_FORCE is handled in JB_NonJBInit + // For OPENJAILS_OPEN_FORCE, the below is always executed + + entity oldself = self; + self = door; - float opened = TRUE; ++ float opened = true; + + switch(door.classname) + { + case "door": + door_init_startopen(); + break; + + case "door_rotating": + door_rotating_init_startopen(); + InitMovingBrushTrigger(); + break; + + default: + jb_debug(sprintf("Warning: %s^7 at %v is linked with an entity of unhandled class (%s^7)\n", JB_ControlPoint_Name(cpoint), cpoint.origin, door.classname)); - opened = FALSE; ++ opened = false; + break; + } + + self = oldself; + + if(opened) + { + float idx = Team_TeamToNumber(cpoint.team); + jb_doors_opened[idx] = jb_doors_opened[idx] + 1; + } +} + +// This is called for non-jailbreak modes only, to alter jb-specific entities on the map +void JB_NonJBInit() +{ + entity tmp_entity; + float openjails = autocvar_g_jailbreak_nonjb_openjails; + + SUB_ForEachTarget_Init(); + for(tmp_entity = jb_worldcplist; tmp_entity; tmp_entity = tmp_entity.jb_worldcpnext) + { + if(tmp_entity.team) + { + if(openjails != OPENJAILS_LOCKED_FORCE) - SUB_ForEachTarget(tmp_entity, JB_MaybeOpenDoor, TRUE, openjails, '0 0 0', string_null, tmp_entity); ++ SUB_ForEachTarget(tmp_entity, JB_MaybeOpenDoor, true, openjails, '0 0 0', string_null, tmp_entity); + tmp_entity.think = SUB_Remove; + tmp_entity.nextthink = time; + } + } + + // If all jail doors are locked, it means that the jail is not intended to be accessible. + // We have to keep the jail sectors then to ensure it's not possible to get in with translocator (or something more evil to be added in the future). + // Otherwise, they have to be removed. TODO: do something about maps with multiple jails (if we ever get any). + entity e; // TODO + for(e = findchain(classname, "jailbreak_jail"); e; e = e.chain) + { + float idx = Team_TeamToNumber(e.team); + if(!autocvar_g_nades || jb_doors_opened[idx]) + { + e.think = SUB_Remove; + e.nextthink = time; + } + } +} + +// +// Gametype logic +// + +float JB_JailIsOpen(float theteam) +{ + entity tmp_entity; + for(tmp_entity = jb_worlddoorlist; tmp_entity; tmp_entity = tmp_entity.jb_worlddoornext) + { + if(tmp_entity.team == theteam) + if(tmp_entity.state != STATE_BOTTOM) - return TRUE; ++ return true; + } - return FALSE; ++ return false; +} + +void JB_TeleportToJail(entity p, entity attacker) +{ + vector a; + entity spot = jb_ChooseJailSpawn(p, attacker); + + float tries; + tries = 3; + + while(!spot && tries > 0) + { + spot = jb_ChooseJailSpawn(p, attacker); + tries--; + } + + if(!spot) + { + jb_debug(strcat("Failed to pick a jail spawnpoint for ", self.netname, "^7, cannot imprison!\n")); + return; + } + + a = spot.angles; + a_z = 0; + TeleportPlayer(spot, self, spot.origin, self.mangle, a, '0 0 0', '0 0 0', TELEPORT_FLAGS_TELEPORTER); +} + +void JB_Imprison(entity attacker) +{ + if(self.jb_isprisoned) + { + jb_debug(strcat("Tried to imprison a prisoned player (", self.netname, "^7)\n")); + return; + } + + self.health = autocvar_g_jailbreak_prisoner_health; + self.armorvalue = autocvar_g_jailbreak_prisoner_armor; + + self.jb_had_unlimited_ammo = (self.items & IT_UNLIMITED_WEAPON_AMMO); + + if(!self.jb_had_unlimited_ammo) + self.items |= IT_UNLIMITED_WEAPON_AMMO; + - self.weapon_blocked = TRUE; ++ self.weapon_blocked = true; + - nades_Clear(self, FALSE); ++ nades_Clear(self, false); + + jb_debug(sprintf("Imprisoning %s^7, attacker: %e with netname: %s\n", self.netname, attacker, attacker.netname)); + JB_TeleportToJail(self, attacker); + - self.jb_isprisoned = TRUE; ++ self.jb_isprisoned = true; + self.jb_prisontime = time; + + Send_Notification(NOTIF_ONE, self, MSG_CENTER, CENTER_JAILBREAK_IMPRISON); +} + +void JB_TorturePrisonersLater_Think() +{ + JB_TorturePrisoners(self.team); + remove(self); +} + +void JB_TorturePrisonersLater(float theteam, float thedelay) +{ + entity e = spawn(); + e.team = theteam; + e.think = JB_TorturePrisonersLater_Think; + e.nextthink = time + thedelay; +} + +void JB_Release(entity saviour) +{ + if(!self.jb_isprisoned) + { + jb_debug(strcat("Tried to release a free player (", self.netname, "^7)\n")); + return; + } + - self.jb_isprisoned = FALSE; ++ self.jb_isprisoned = false; + self.jb_imprisoner = world; - self.weapon_blocked = FALSE; - self.player_blocked = FALSE; // just incase ++ self.weapon_blocked = false; ++ self.player_blocked = false; // just incase + + if(!self.jb_had_unlimited_ammo) + self.items &= ~IT_UNLIMITED_WEAPON_AMMO; + + if(g_jailbreak_jail_deathmatch) + { + self.health = max(self.health, autocvar_g_jailbreak_prisoner_health); + self.armorvalue = max(self.armorvalue, autocvar_g_jailbreak_prisoner_armor); + } +} + +// +// Torture logic +// + +#define JITTER(v,j) (v) + (j) * 2 * (random() - 0.5) + +void JB_TorturePrisoners(float theteam) +{ + entity spot = world; + + for(;(spot = find(spot, classname, "info_jailbreak_torturespawn"));) + if(spot.team == theteam) + JB_Torture_Start(spot); +} + +void JB_Torture_Think() +{ + if(gameover) + { + remove(self); + return; + } + + makevectors(self.angles); + //self.nextthink = time + JITTER(self.jb_torture_delay, self.jb_torture_delay_jitter); + + float j = self.jb_torture_delay - JITTER(self.jb_torture_delay, self.jb_torture_delay_jitter); + + if(j > 0) + j = 0.5 * j; + + self.nextthink = time + max(0.1, self.jb_torture_delay + j); + self.jb_torture_suggestedforce = JITTER(self.jb_torture_force, self.jb_torture_force_jitter); + + Send_Effect(EFFECT_FIREFIELD, self.origin, '0 0 0', 2); + + entity head; + FOR_EACH_PLAYER(head) + if(DIFF_TEAM(head, self)) + if(head.jb_isprisoned) + if(!Fire_IsBurning(head)) + Fire_AddDamage(head, world, 100, 6, DEATH_FIRE); +} + +void JB_Torture_Start(entity spot) +{ + entity e = spawn(); + e.classname = "jailbreak_torture"; + e.reset = SUB_Remove; + e.reset2 = e.reset; + e.think = JB_Torture_Think; + e.angles = spot.angles; + e.jb_torture_delay = spot.jb_torture_delay; + e.jb_torture_delay_jitter = spot.jb_torture_delay_jitter; + e.jb_torture_force = spot.jb_torture_force; + e.jb_torture_force_jitter = spot.jb_torture_force_jitter; + e.owner = spot; + e.team = e.owner.team; + setorigin(e, spot.origin); + e.nextthink = time + JITTER(0, e.jb_torture_delay_jitter); +} + +#undef JITTER + +.float pointupdatetime; + +// +// Utility functions +// + +entity jb_ChooseJailSpawn(entity player, entity attacker) +{ + entity spot; + + RandomSelection_Init(); + + for(spot = world; (spot = find(spot, classname, "info_jailbreak_jailspawn")); ) + { + if(attacker && DIFF_TEAM(player, attacker)) // don't throw teammates in own jail? + { + if(SAME_TEAM(spot, attacker)) + RandomSelection_Add(spot, 0, string_null, 1, 1); + } + else + { + if(DIFF_TEAM(spot, player)) + RandomSelection_Add(spot, 0, string_null, 1, 1); + } + } + + if(!RandomSelection_chosen_ent) + jb_debug(strcat("Unable to find an enemy jail spawnpoint, player team: ", ftos(player.team), "\n")); + + return RandomSelection_chosen_ent; +} + +float JB_TotalPlayersOnTeam(float theteam) +{ + entity e; + float plcount = 0; + FOR_EACH_PLAYER(e) if(e.team == theteam) ++plcount; + + return plcount; +} + +float JB_AlivePlayersOnTeam(float theteam) +{ + entity e; + float plcount = 0; + FOR_EACH_PLAYER(e) if(e.team == theteam) if(!e.jb_isprisoned) ++plcount; + + return plcount; +} + +entity JB_FindCamera(float theteam) +{ + RandomSelection_Init(); + + entity e = world; + for(;(e = find(e, classname, "info_jailbreak_jailcamera"));) if(e.team == theteam) + RandomSelection_Add(e, 0, string_null, 1, 1); + + return RandomSelection_chosen_ent; +} + +float jb_ce_pvs, jb_ce_trace; + +void JB_ActivateCamera(float theteam) +{ + entity cam = JB_FindCamera(theteam); + + if(!cam) + { + jb_debug(strcat("Team ", ftos(theteam), " has no camera entities, fail!\n")); + return; + } + + jb_ce_pvs = cvar("sv_cullentities_pvs"); + jb_ce_trace = cvar("sv_cullentities_trace"); + + // without this we won't be able to watch them burn! + cvar_settemp("sv_cullentities_pvs", "0"); + cvar_settemp("sv_cullentities_trace", "0"); + + entity p; + FOR_EACH_REALCLIENT(p) - p.jb_roundlost = TRUE; ++ p.jb_roundlost = true; + - cam.active = TRUE; ++ cam.active = true; + cam.SendFlags |= 2; + + entity e; + for(e = world; (e = find(e, classname, "info_jailbreak_jailcamera")); ) + if(e != cam) + { - e.active = FALSE; ++ e.active = false; + e.SendFlags |= 2; + } +} + +// +// Setup functions +// + +void JB_SetupJailSpawnpoint() +{ + if(!g_jailbreak) { remove(self); return; } + + self.classname = "info_jailbreak_jailspawn"; +} + +void JB_Jail_Touch() +{ + if(autocvar_g_nades) + if(other.classname == "nade") + { + entity own = other.realowner; + remove(other); - nades_Clear(own, FALSE); ++ nades_Clear(own, false); + return; + } + + if(other.classname == "grapplinghook") + { + RemoveGrapplingHook(other.realowner); + return; + } + + if(!g_jailbreak) + return; + + if(!IS_PLAYER(other)) + return; + + if(!other.jb_isprisoned) + { + vector mymid = (self.absmin + self.absmax) * 0.5; + vector othermid = (other.absmin + other.absmax) * 0.5; + + Damage(other, self, self, 0, DEATH_HURTTRIGGER, mymid, normalize(othermid - mymid) * min(500, vlen(other.velocity))); + Send_Notification(NOTIF_ONE, other, MSG_CENTER, CENTER_JAILBREAK_NOENTRY); + return; + } + + other.jb_isprisoned = 2; + + if(SAME_TEAM(other, self)) + return; + + other.jb_jail_resettime = time + frametime * 5; +} + +void JB_SetupJail() +{ + self.classname = "jailbreak_jail"; + self.touch = JB_Jail_Touch; + EXACTTRIGGER_INIT; +} + +float jailcamera_send(entity to, float sf) +{ + WriteByte(MSG_ENTITY, ENT_CLIENT_JAILCAMERA); + WriteByte(MSG_ENTITY, sf); + + if(sf & 1) + { + WriteCoord(MSG_ENTITY, self.origin_x); + WriteCoord(MSG_ENTITY, self.origin_y); + WriteCoord(MSG_ENTITY, self.origin_z); + + WriteAngle(MSG_ENTITY, self.angles_x); + WriteAngle(MSG_ENTITY, self.angles_y); + } + + if(sf & 2) + { + WriteByte(MSG_ENTITY, self.active); + } + - return TRUE; ++ return true; +} + + +void JB_SetupJailCamera() +{ + if(!g_jailbreak) { remove(self); return; } + + self.classname = "info_jailbreak_jailcamera"; + - Net_LinkEntity(self, FALSE, 0, jailcamera_send); ++ Net_LinkEntity(self, false, 0, jailcamera_send); +} + +void JB_SetupTortureSpawnpoint() +{ + if(!g_jailbreak) { remove(self); return; } + + self.classname = "info_jailbreak_torturespawn"; + + if(!self.jb_torture_force) + self.jb_torture_force = 400; + + if(!self.jb_torture_force_jitter) + self.jb_torture_force_jitter = self.jb_torture_force * 0.1; + + if(!self.jb_torture_delay) + self.jb_torture_delay = 2; + + if(!self.jb_torture_delay_jitter) + self.jb_torture_delay_jitter = self.jb_torture_delay * 0.5; +} + +.entity sprite; +void AnimateDomPoint(); + +string JB_ControlPoint_ModelForTeam(float t) +{ + switch(t) + { + case NUM_TEAM_1: return "models/domination/dom_red.md3"; + case NUM_TEAM_2: return "models/domination/dom_blue.md3"; + case NUM_TEAM_3: return "models/domination/dom_yellow.md3"; + case NUM_TEAM_4: return "models/domination/dom_pink.md3"; + default: return "models/domination/dom_unclaimed.md3"; + } +} + +string JB_ControlPoint_WaypointForTeam(float t) +{ + switch(t) + { + case NUM_TEAM_1: return "dom-red"; + case NUM_TEAM_2: return "dom-blue"; + case NUM_TEAM_3: return "dom-yellow"; + case NUM_TEAM_4: return "dom-pink"; + default: return "dom-neut"; + } +} + +float JB_ControlPoint_Cooldown(entity e) +{ + float base, pw, f, c; + + c = e.jb_capturecount; + + if(e.team == 0) + { + base = autocvar_g_jailbreak_controlpoint_idletime_neutral; + pw = autocvar_g_jailbreak_controlpoint_idletime_neutral_power; + f = autocvar_g_jailbreak_controlpoint_idletime_neutral_factor; + } + else + { + base = autocvar_g_jailbreak_controlpoint_idletime; + pw = autocvar_g_jailbreak_controlpoint_idletime_power; + f = autocvar_g_jailbreak_controlpoint_idletime_factor; + } + + return base + pow(c, pw) * f * base; +} + +float JB_ControlPoint_InitialCooldown(entity e) { return ((e.team == 0) ? autocvar_g_jailbreak_controlpoint_idletime_neutral_initial : autocvar_g_jailbreak_controlpoint_idletime_initial); } + +void JB_ControlPoint_Activate(entity e) +{ - e.jb_active = TRUE; ++ e.jb_active = true; + e.jb_cooldown = 0; + //e.jb_cooldown_max = 0; + setmodel(e, JB_ControlPoint_ModelForTeam(e.team)); + setsize(e, JB_CP_MIN, JB_CP_MAX); + WaypointSprite_UpdateMaxHealth(e.jb_waypoint, 0); + WaypointSprite_UpdateHealth(e.jb_waypoint, 0); +} + +void JB_ControlPoint_Deactivate(entity e, float cooldown) +{ + e.jb_cooldown_max = max(e.jb_cooldown_max, cooldown); + e.jb_cooldown = max(e.jb_cooldown, cooldown); + + jb_debug(sprintf("%e: %ds cooldown, team: %d, caps: %d\n", e, e.jb_cooldown, e.team, e.jb_capturecount)); + + if(e.jb_active && e.jb_cooldown > 0) + { + setmodel(e, "models/domination/dom_unclaimed.md3"); + setsize(e, JB_CP_MIN, JB_CP_MAX); - e.jb_active = FALSE; ++ e.jb_active = false; + } +} + +void JB_ControlPoint_UpdateCooldownProgress(entity e) +{ + WaypointSprite_UpdateMaxHealth(e.jb_waypoint, e.jb_cooldown_max); + WaypointSprite_UpdateHealth(e.jb_waypoint, e.jb_cooldown_max - e.jb_cooldown); +} + +void JB_ControlPoint_Think() +{ + self.nextthink = time; + AnimateDomPoint(); + + if(time < game_starttime || jb_roundover) + return; + + if(self.jb_cooldown) { JB_ControlPoint_UpdateCooldownProgress(self); } + else if(!self.jb_active) { JB_ControlPoint_Activate(self); } + + if(time - self.pointupdatetime >= 0.1) + { + self.jb_unlock_progress = 0; + self.jb_capturingplayer = world; + } + + self.jb_cooldown = max(0, self.jb_cooldown - frametime); +} + +void JB_ControlPoint_SwitchTeam(entity e, float t) +{ + e.team = t; + //WaypointSprite_UpdateSprites(e.jb_waypoint, e.jb_waypoint.model1, "", e.jb_waypoint.model3); - WaypointSprite_UpdateTeamRadar(e.jb_waypoint, RADARICON_FLAG, (e.team) ? colormapPaletteColor(e.team - 1, FALSE) : '0 1 1'); ++ WaypointSprite_UpdateTeamRadar(e.jb_waypoint, RADARICON_FLAG, (e.team) ? colormapPaletteColor(e.team - 1, false) : '0 1 1'); + //WaypointSprite_UpdateTextColors(e.jb_waypoint, TeamColor(e.team), e.jb_waypoint.clr2, e.jb_waypoint.clr3); +} + +void JB_TriggerTeamControlPoints(entity cp, entity player) +{ + entity oldself = self; + self = world; + + for(self = jb_worldcplist; self; self = self.jb_worldcpnext) + { + if(cp.team) + { + if(SAME_TEAM(self, cp)) + SUB_UseTargets_PreventReuse(); + } + else + { + if(self.jb_team_initial != player.team) + SUB_UseTargets_PreventReuse(); + } + } + + self = oldself; +} + +string JB_ControlPoint_Name(entity p) +{ + string clr, tm, end; + + clr = Team_ColorCode(p.team); + tm = Team_ColorName(p.team); + + end = strcat(" (Point ", chr2str(str2chr("A", 0) + p.cnt), ")"); + + if(!p.netname) + return strcat(clr, tm, " Control Point", end); + return strcat(clr, strdecolorize(p.netname), end); +} + +void JB_ControlPoint_Capture(entity player) +{ + entity e; - float pc = FALSE; ++ float pc = false; + + activator = self; + + if(!self.team || g_jailbreak_claim) + JB_TriggerTeamControlPoints(self, player); + else SUB_UseTargets(); + + FOR_EACH_PLAYER(e) + { + if(DIFF_TEAM(e, player)) + Send_Notification(NOTIF_ONE, e, MSG_CENTER, CENTER_JAILBREAK_ESCAPE, player.team); + else if(e == player) + Send_Notification(NOTIF_ONE, e, MSG_CENTER, CENTER_JAILBREAK_FREED); + + if(e.jb_isprisoned && SAME_TEAM(e, player)) + { + Send_Notification(NOTIF_ONE, e, MSG_CENTER, CENTER_JAILBREAK_FREE); + pc++; + } + + } + + Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_JAILBREAK_CAPTURE, player.netname, JB_ControlPoint_Name(self)); + + for(e = jb_worldcplist; e; e = e.jb_worldcpnext) + { + e.jb_lastmessage = time + 3; + } + + PlayerScore_Add(player, SP_SCORE, ((self.team == 0)? autocvar_g_jailbreak_score_jbreak_neutralmultiplier : 1) + * (autocvar_g_jailbreak_score_jbreak + autocvar_g_jailbreak_score_jbreak_perplayer * pc)); + PlayerScore_Add(player, SP_JB_JBREAKS, 1); + PlayerScore_Add(player, SP_JB_FREED, pc); + nades_GiveBonus(player, autocvar_g_nades_bonus_score_medium); + play2all("kh/alarm.wav"); + + if(autocvar_g_jailbreak_controlpoint_claim_noneutral) + if(self.team == 0) + return; + + JB_ControlPoint_SwitchTeam(self, player.team); +} + +void JB_ControlPoint_Touch() +{ + if(jb_roundover) + return; + + if(other.health < 1 || other.frozen) + return; + + if(gameover) + return; + + if(!IS_PLAYER(other)) + return; + + other.pointupdatetime = time; + + if(SAME_TEAM(other, self)) + { + if(time >= self.jb_lastmessage) + { + Send_Notification(NOTIF_ONE, other, MSG_CENTER, CENTER_JAILBREAK_WRONGTEAM); + self.jb_lastmessage = time + 1.5; + } + return; + } + + if(!self.jb_active) + { + if(time >= self.jb_lastmessage) + { + Send_Notification(NOTIF_ONE, other, MSG_CENTER, CENTER_JAILBREAK_NOTREADY); + self.jb_lastmessage = time + 1.5; + } + return; + } + + if(self.jb_capturingplayer && self.jb_capturingplayer != other) + { + if(time >= self.jb_lastmessage) + { + Send_Notification(NOTIF_ONE, other, MSG_CENTER, CENTER_JAILBREAK_TOOLATE); + self.jb_lastmessage = time + 1.5; + } + return; + } + + if(JB_TotalPlayersOnTeam(other.team) == JB_AlivePlayersOnTeam(other.team)) + { + if(time >= self.jb_lastmessage) + { + Send_Notification(NOTIF_ONE, other, MSG_CENTER, CENTER_JAILBREAK_TEAMALIVE); + self.jb_lastmessage = time + 1.5; + } + return; + } + + entity tmp_entity; - float capping_neutral = FALSE; ++ float capping_neutral = false; + if(self.team) + for(tmp_entity = jb_worldcplist; tmp_entity; tmp_entity = tmp_entity.jb_worldcpnext) + { + if(!tmp_entity.team) + if(tmp_entity.jb_unlock_progress) + if(SAME_TEAM(tmp_entity.jb_capturingplayer, other)) + { - capping_neutral = TRUE; ++ capping_neutral = true; + break; + } + } + + if(!capping_neutral || !self.team) + self.jb_unlock_progress = bound(0, self.jb_unlock_progress + frametime * autocvar_g_jailbreak_controlpoint_unlock_speed, 1); + + self.pointupdatetime = time; + self.jb_capturingplayer = other; + other.jb_unlock_progress = self.jb_unlock_progress; + + if(self.jb_unlock_progress >= 1) + { + JB_ControlPoint_Capture(other); + + JB_ControlPoint_Deactivate(self, JB_ControlPoint_Cooldown(self)); + + for(tmp_entity = jb_worldcplist; tmp_entity; tmp_entity = tmp_entity.jb_worldcpnext) + { + if(tmp_entity != self) + if(SAME_TEAM(tmp_entity, other)) + JB_ControlPoint_Deactivate(tmp_entity, autocvar_g_jailbreak_controlpoint_idletime_global_own); + else if(!tmp_entity.team || DIFF_TEAM(tmp_entity, other)) + JB_ControlPoint_Deactivate(tmp_entity, autocvar_g_jailbreak_controlpoint_idletime_global); + } + + self.jb_capturecount += 1; + } +} + +void JB_ControlPoint_Reset() +{ + self.jb_capturecount = 0; - self.jb_active = TRUE; ++ self.jb_active = true; + self.jb_cooldown = 0; + self.jb_cooldown_max = 0; + JB_ControlPoint_Deactivate(self, JB_ControlPoint_InitialCooldown(self)); + WaypointSprite_UpdateMaxHealth(self.jb_waypoint, 0); + WaypointSprite_UpdateHealth(self.jb_waypoint, 0); + JB_ControlPoint_SwitchTeam(self, autocvar_g_jailbreak_controlpoint_claim_allneutral ? 0 : self.jb_team_initial); +} + +float jb_ControlPoint_Waypoint_Customize() +{ - if(!self.owner.team) { return TRUE; } ++ if(!self.owner.team) { return true; } + + entity e = WaypointSprite_getviewentity(other); + + // hide from owner's team - if(SAME_TEAM(self.owner, e)) { return FALSE; } ++ if(SAME_TEAM(self.owner, e)) { return false; } + - return TRUE; ++ return true; +} + +void JB_SetupControlPoint() +{ + self.jb_worldcpnext = jb_worldcplist; // link control point into jb_worldcplist + jb_worldcplist = self; + + if(!g_jailbreak) { return; } // removal is done in JB_NonJBInit + + self.classname = "jailbreak_controlpoint"; + self.jb_team_initial = self.team; + + if(autocvar_g_jailbreak_controlpoint_claim_allneutral) + self.team = 0; + + setmodel(self, JB_ControlPoint_ModelForTeam(self.team)); + self.skin = 0; + + if(!self.t_width) + self.t_width = 0.02; // frame animation rate + if(!self.t_length) + self.t_length = 239; // maximum frame + + self.think = JB_ControlPoint_Think; + self.nextthink = time; + self.touch = JB_ControlPoint_Touch; + self.solid = SOLID_TRIGGER; + self.flags = FL_ITEM; + self.reset = JB_ControlPoint_Reset; + self.jb_capturecount = 0; - self.jb_active = TRUE; ++ self.jb_active = true; + self.cnt = jb_cp_num; + self.scale = JB_CP_SCALE; + JB_ControlPoint_Deactivate(self, JB_ControlPoint_InitialCooldown(self)); + setsize(self, JB_CP_MIN, JB_CP_MAX); + setorigin(self, self.origin + '0 0 20'); + droptofloor(); + + waypoint_spawnforitem_force(self, self.origin); + self.nearestwaypointtimeout = 0; // activate waypointing again + WaypointSprite_SpawnFixed(strzone(strcat("Point ", chr2str(str2chr("A", 0) + jb_cp_num))), self.origin + JB_CP_WPOFFSET, self, jb_waypoint, RADARICON_DOMPOINT, '0 1 1'); + self.jb_waypoint.customizeentityforclient = jb_ControlPoint_Waypoint_Customize; - WaypointSprite_UpdateTeamRadar(self.jb_waypoint, RADARICON_FLAG, (self.team) ? colormapPaletteColor(self.team - 1, FALSE) : '0 1 1'); ++ WaypointSprite_UpdateTeamRadar(self.jb_waypoint, RADARICON_FLAG, (self.team) ? colormapPaletteColor(self.team - 1, false) : '0 1 1'); + //WaypointSprite_UpdateTextColors(self.jb_waypoint, TeamColor(self.team), '1 0.5 0', '0 0 0'); + //WaypointSprite_UpdateSprites(self.jb_waypoint, self.jb_waypoint.model1, self.jb_waypoint.model2, ""); + + ++jb_cp_num; +} + +// mutator hooks +MUTATOR_HOOKFUNCTION(jb_OnEntityPreSpawn) +{ + switch(self.classname) + { + case "item_flag_team1": + case "item_flag_team2": + case "item_flag_team3": + case "item_flag_team4": - return TRUE; ++ return true; + } + - return FALSE; ++ return false; +} + +MUTATOR_HOOKFUNCTION(jb_ResetMap) +{ - jb_roundover = FALSE; ++ jb_roundover = false; + + cvar_set("sv_cullentities_pvs", ftos(jb_ce_pvs)); + cvar_set("sv_cullentities_trace", ftos(jb_ce_trace)); + + for(self = world; (self = find(self, classname, "info_jailbreak_jailcamera")); ) - self.active = FALSE; ++ self.active = false; + + FOR_EACH_CLIENT(self) + { + if(IS_PLAYER(self)) + { + JB_Release(world); + PutClientInServer(); + } + self.player_blocked = 0; - self.weapon_blocked = FALSE; - self.jb_roundlost = FALSE; ++ self.weapon_blocked = false; ++ self.jb_roundlost = false; + } + - return TRUE; ++ return true; +} + +MUTATOR_HOOKFUNCTION(jb_PlayerDies) +{ + if(jb_roundover && frag_deathtype == DEATH_FIRE) + PlayerScore_Add(frag_target, SP_KILLS, +1); // dying negates 1, so we bring it back up + + if(!round_handler_IsRoundStarted() || jb_roundover) - return FALSE; ++ return false; + + if(!frag_target.jb_isprisoned) + { + if(frag_attacker == frag_target || !frag_attacker) + PlayerScore_Add(frag_target, SP_SCORE, -autocvar_g_jailbreak_penalty_death); + else if(IS_PLAYER(frag_attacker)) + { + if(DIFF_TEAM(frag_attacker, frag_target)) + { + PlayerScore_Add(frag_target, SP_SCORE, -autocvar_g_jailbreak_penalty_death); + PlayerScore_Add(frag_attacker, SP_SCORE, autocvar_g_jailbreak_score_imprison); + + float rng = autocvar_g_jailbreak_defense_range; + entity cp; + if(rng) + for(cp = jb_worldcplist; cp; cp = cp.jb_worldcpnext) + { + if(SAME_TEAM(cp, frag_attacker) || (cp.team == 0 && cp.jb_active)) + { + // Rewards control point defense if fragging nearby your team's or neutral cp. + // In case of neutral cp, it has to be active (no defense farming in the beginning of the round) + if(vlen(cp.origin - frag_target.origin) < rng) + { + Send_Notification(NOTIF_ONE, frag_attacker, MSG_CENTER, CENTER_JAILBREAK_DEFENSE); + PlayerScore_Add(frag_attacker, SP_SCORE, autocvar_g_jailbreak_score_defense); + PlayerScore_Add(frag_attacker, SP_JB_DEFENSE, 1); + nades_GiveBonus(frag_attacker, autocvar_g_nades_bonus_score_minor); + break; + } + } + } + } + else PlayerScore_Add(frag_attacker, SP_SCORE, -autocvar_g_jailbreak_penalty_teamkill); + } + + frag_target.jb_imprisoner = frag_attacker; + frag_target.respawn_flags |= RESPAWN_FORCE; + } + else + { + jb_debug(strcat("Prisoned player ", frag_target.netname, "^7 just died, should this really happen?\n")); + PutClientInServer(); + } + - return FALSE; ++ return false; +} + +MUTATOR_HOOKFUNCTION(jb_PlayerSpawn) +{ - //self.jb_isprisoned = FALSE; ++ //self.jb_isprisoned = false; + - if(!round_handler_IsRoundStarted()) { return FALSE; } ++ if(!round_handler_IsRoundStarted()) { return false; } + + if(self.jb_imprisoner != world) + { + JB_Imprison(self.jb_imprisoner); + self.jb_imprisoner = world; + } + + if(JB_TotalPlayersOnTeam(self.team) - 1 > 0) // allow to spawn non-prisoned if there are no players on that team + JB_Imprison(world); + - return FALSE; ++ return false; +} + +MUTATOR_HOOKFUNCTION(jb_SpectateCopy) +{ + self.stat_jb_isprisoned = other.jb_isprisoned; + self.jb_unlock_progress = other.jb_unlock_progress; - return FALSE; ++ return false; +} + +MUTATOR_HOOKFUNCTION(jb_RemovePlayer) +{ + if(self.jb_isprisoned) + JB_Release(world); + - self.jb_roundlost = FALSE; ++ self.jb_roundlost = false; + - return FALSE; ++ return false; +} + +MUTATOR_HOOKFUNCTION(jb_PlayerPreThink) +{ + if(gameover) + { - self.stat_jb_isprisoned = FALSE; - return FALSE; ++ self.stat_jb_isprisoned = false; ++ return false; + } + + self.stat_jb_isprisoned = self.jb_isprisoned; // these are different to allow spectating + + if(!round_handler_IsRoundStarted()) + { + self.jb_isprisoned_prev = 0; + self.jb_unlock_progress = 0; - return FALSE; ++ return false; + } + + float ps = min(1, self.jb_isprisoned); + if(ps != self.jb_isprisoned_prev) + { + if(!ps) + Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_JAILBREAK_FREE, self.netname); + self.jb_isprisoned_prev = ps; + } + + if(time - self.pointupdatetime >= 0.01) + self.jb_unlock_progress = 0; + + if(time - self.jb_prisontime < 0.5) - return FALSE; ++ return false; + + if(self.jb_isprisoned == 1) + { + jb_debug(strcat("Warning: ", self.netname, "^7managed to leave the jail without touching the jail sector! Attempting to put them back in\n")); + JB_TeleportToJail(self, world); + } + else if(self.jb_isprisoned) + if(time > self.jb_jail_resettime) + JB_Release(world); + - return FALSE; ++ return false; +} + +MUTATOR_HOOKFUNCTION(jb_PlayerDamage) +{ + if(self.jb_isprisoned && frag_deathtype != DEATH_FIRE) + frag_damage = 0; + + entity tmp_entity; + if(self.jb_unlock_progress) + for(tmp_entity = jb_worldcplist; tmp_entity; tmp_entity = tmp_entity.jb_worldcpnext) + { + if(tmp_entity.jb_capturingplayer == self) + tmp_entity.jb_unlock_progress = bound(0, tmp_entity.jb_unlock_progress - autocvar_g_jailbreak_controlpoint_unlock_damage_pushback, 1); + } + - return FALSE; ++ return false; +} + +MUTATOR_HOOKFUNCTION(jb_ForbidThrowing) +{ + if(self.jb_isprisoned) - return TRUE; ++ return true; + - return FALSE; ++ return false; +} + +MUTATOR_HOOKFUNCTION(jb_GiveFrags) +{ + if(jb_roundover) + { + frag_score = 0; - return TRUE; ++ return true; + } - return FALSE; ++ return false; +} + +MUTATOR_HOOKFUNCTION(jb_GetTeamCount) +{ + ret_float = jb_teams; - return FALSE; ++ return false; +} + +MUTATOR_HOOKFUNCTION(jb_AllowMobSpawning) +{ + if(self.jb_isprisoned) + { + ret_string = "You can't spawn monsters in prison!"; - return TRUE; ++ return true; + } + - return FALSE; ++ return false; +} + +// spawn functions +#define JB_SPAWNFUNC(e,s,t) void spawnfunc_##e() { self.team = t; s(); } + +JB_SPAWNFUNC(info_jailbreak_jailspawn_red, JB_SetupJailSpawnpoint, NUM_TEAM_1) +JB_SPAWNFUNC(info_jailbreak_jailspawn_blue, JB_SetupJailSpawnpoint, NUM_TEAM_2) +JB_SPAWNFUNC(info_jailbreak_jailspawn_yellow, JB_SetupJailSpawnpoint, NUM_TEAM_3) +JB_SPAWNFUNC(info_jailbreak_jailspawn_pink, JB_SetupJailSpawnpoint, NUM_TEAM_4) + +JB_SPAWNFUNC(func_jailbreak_jail_red, JB_SetupJail, NUM_TEAM_1) +JB_SPAWNFUNC(func_jailbreak_jail_blue, JB_SetupJail, NUM_TEAM_2) +JB_SPAWNFUNC(func_jailbreak_jail_yellow, JB_SetupJail, NUM_TEAM_3) +JB_SPAWNFUNC(func_jailbreak_jail_pink, JB_SetupJail, NUM_TEAM_4) + +JB_SPAWNFUNC(info_jailbreak_jailcamera_red, JB_SetupJailCamera, NUM_TEAM_1) +JB_SPAWNFUNC(info_jailbreak_jailcamera_blue, JB_SetupJailCamera, NUM_TEAM_2) +JB_SPAWNFUNC(info_jailbreak_jailcamera_yellow, JB_SetupJailCamera, NUM_TEAM_3) +JB_SPAWNFUNC(info_jailbreak_jailcamera_pink, JB_SetupJailCamera, NUM_TEAM_4) + +JB_SPAWNFUNC(info_jailbreak_torturespawn_red, JB_SetupTortureSpawnpoint, NUM_TEAM_1) +JB_SPAWNFUNC(info_jailbreak_torturespawn_blue, JB_SetupTortureSpawnpoint, NUM_TEAM_2) +JB_SPAWNFUNC(info_jailbreak_torturespawn_yellow, JB_SetupTortureSpawnpoint, NUM_TEAM_3) +JB_SPAWNFUNC(info_jailbreak_torturespawn_pink, JB_SetupTortureSpawnpoint, NUM_TEAM_4) + +JB_SPAWNFUNC(jailbreak_controlpoint_red, JB_SetupControlPoint, NUM_TEAM_1) +JB_SPAWNFUNC(jailbreak_controlpoint_blue, JB_SetupControlPoint, NUM_TEAM_2) +JB_SPAWNFUNC(jailbreak_controlpoint_yellow, JB_SetupControlPoint, NUM_TEAM_3) +JB_SPAWNFUNC(jailbreak_controlpoint_pink, JB_SetupControlPoint, NUM_TEAM_4) +JB_SPAWNFUNC(jailbreak_controlpoint_neutral, JB_SetupControlPoint, 0) + +// scores +void jb_ScoreRules(float teams) +{ - ScoreRules_basics(teams, SFL_SORT_PRIO_PRIMARY, 0, TRUE); // SFL_SORT_PRIO_PRIMARY ++ ScoreRules_basics(teams, SFL_SORT_PRIO_PRIMARY, 0, true); // SFL_SORT_PRIO_PRIMARY + ScoreInfo_SetLabel_TeamScore(ST_JB_ROUNDS, "rounds", SFL_SORT_PRIO_PRIMARY); + ScoreInfo_SetLabel_PlayerScore(SP_JB_JBREAKS, "jbs", 0); + ScoreInfo_SetLabel_PlayerScore(SP_JB_FREED, "freed", SFL_SORT_PRIO_SECONDARY); + ScoreInfo_SetLabel_PlayerScore(SP_JB_DEFENSE, "def", 0); + ScoreRules_basics_end(); +} + +// initialization +void jb_DelayedInit() +{ + entity tmp_entity; + + SUB_ForEachTarget_Init(); + for(tmp_entity = jb_worldcplist; tmp_entity; tmp_entity = tmp_entity.jb_worldcpnext) - SUB_ForEachTarget(tmp_entity, JB_AddDoor, TRUE, tmp_entity.jb_team_initial, '0 0 0', string_null, tmp_entity); ++ SUB_ForEachTarget(tmp_entity, JB_AddDoor, true, tmp_entity.jb_team_initial, '0 0 0', string_null, tmp_entity); +} + +void jb_Initialize() +{ + precache_sound("kh/alarm.wav"); + + if(autocvar_g_jailbreak_teams_override >= 2) + jb_teams = autocvar_g_jailbreak_teams_override; + else + jb_teams = autocvar_g_jailbreak_teams; + + jb_teams = bound(2, jb_teams, 4); + + jb_ScoreRules(jb_teams); + + round_handler_Spawn(JB_CheckTeams, JB_CheckWinner, JB_RoundStart); + round_handler_Init(5, autocvar_g_jailbreak_warmup, autocvar_g_jailbreak_round_timelimit); + + g_jailbreak_claim = autocvar_g_jailbreak_controlpoint_claim; + + addstat(STAT_REDALIVE, AS_INT, redalive_stat); + addstat(STAT_BLUEALIVE, AS_INT, bluealive_stat); + addstat(STAT_YELLOWALIVE, AS_INT, yellowalive_stat); + addstat(STAT_PINKALIVE, AS_INT, pinkalive_stat); + addstat(STAT_CAPTURE_PROGRESS, AS_FLOAT, jb_unlock_progress); + addstat(STAT_PRISONED, AS_INT, stat_jb_isprisoned); + addstat(STAT_ROUNDLOST, AS_INT, jb_roundlost); + + g_jailbreak_jail_deathmatch = autocvar_g_jailbreak_jail_deathmatch; + InitializeEntity(world, jb_DelayedInit, INITPRIO_GAMETYPE); +} + +MUTATOR_DEFINITION(gamemode_jailbreak) +{ + MUTATOR_HOOK(OnEntityPreSpawn, jb_OnEntityPreSpawn, CBC_ORDER_FIRST); + MUTATOR_HOOK(reset_map_players, jb_ResetMap, CBC_ORDER_ANY); + MUTATOR_HOOK(PlayerDies, jb_PlayerDies, CBC_ORDER_ANY); + MUTATOR_HOOK(PlayerSpawn, jb_PlayerSpawn, CBC_ORDER_ANY); + MUTATOR_HOOK(SpectateCopy, jb_SpectateCopy, CBC_ORDER_ANY); + MUTATOR_HOOK(ClientDisconnect, jb_RemovePlayer, CBC_ORDER_ANY); + MUTATOR_HOOK(MakePlayerObserver, jb_RemovePlayer, CBC_ORDER_ANY); + MUTATOR_HOOK(PlayerPreThink, jb_PlayerPreThink, CBC_ORDER_ANY); + MUTATOR_HOOK(PlayerDamage_Calculate, jb_PlayerDamage, CBC_ORDER_ANY); + MUTATOR_HOOK(GiveFragsForKill, jb_GiveFrags, CBC_ORDER_ANY); + MUTATOR_HOOK(ForbidThrowCurrentWeapon, jb_ForbidThrowing, CBC_ORDER_ANY); + MUTATOR_HOOK(GetTeamCount, jb_GetTeamCount, CBC_ORDER_ANY); + MUTATOR_HOOK(AllowMobSpawning, jb_AllowMobSpawning, CBC_ORDER_LAST); + + MUTATOR_ONADD + { + if(time > 1) // game loads at time 1 + error("This is a game type and it cannot be added at runtime."); + jb_Initialize(); + } + + MUTATOR_ONROLLBACK_OR_REMOVE + { + // we actually cannot roll back jb_Initialize here + // BUT: we don't need to! If this gets called, adding always + // succeeds. + } + + MUTATOR_ONREMOVE + { + print("This is a game type and it cannot be removed at runtime."); + return -1; + } + + return 0; +} diff --cc qcsrc/server/mutators/gamemode_jailbreak.qh index 4d3301c4d,000000000..ee884bef0 mode 100644,000000..100644 --- a/qcsrc/server/mutators/gamemode_jailbreak.qh +++ b/qcsrc/server/mutators/gamemode_jailbreak.qh @@@ -1,86 -1,0 +1,91 @@@ ++#ifndef GAMEMODE_JAILBREAK_H ++#define GAMEMODE_JAILBREAK_H ++ +float g_jailbreak_claim; +float g_jailbreak_jail_deathmatch; + +void JB_NonJBInit(); +entity jb_ChooseJailSpawn(entity player, entity attacker); +float JB_TotalPlayersOnTeam(float); +float JB_AlivePlayersOnTeam(float); +void JB_ActivateCamera(float); +void JB_TorturePrisoners(float); +void JB_TorturePrisonersLater(float, float); +void JB_Torture_Start(entity); +// void JB_SetCamera(entity, entity, float); +// void JB_ClearCamera(entity); +// void JB_ClearCameraForAll(); +void JB_ControlPoint_Activate(entity); +void JB_ControlPoint_Deactivate(entity, float); +void JB_ControlPoint_UpdateCooldownProgress(entity); +string JB_ControlPoint_Name(entity); +float JB_JailIsOpen(float); + +.float stat_jb_isprisoned; + +.float jb_unlock_progress; +.float jb_isprisoned; +.float jb_isprisoned_prev; +.float jb_jail_resettime; +.float jb_prisontime; +.float jb_cooldown; +.float jb_cooldown_max; +.float jb_active; +.float jb_capturecount; +.float jb_roundlost; +.float jb_team_initial; +.float jb_defendthink_next; +.float jb_had_unlimited_ammo; +.float jb_lastmessage; + +.entity jb_waypoint; +.entity jb_capturingplayer; +.entity jb_imprisoner; + +float jb_roundover; +float jb_teams; + +float jb_cp_num; + +#define JB_CP_MIN ('-32 -32 -32') +#define JB_CP_MAX ('32 32 32') +#define JB_CP_SCALE 0.6 +#define JB_CP_WPOFFSET ('0 0 32') + +#define ST_JB_ROUNDS 1 +#define SP_JB_JBREAKS 4 +#define SP_JB_FREED 5 +#define SP_JB_DEFENSE 6 + +.float jaildoormode; +#define JAILDOORMODE_DEFAULT 0 +#define JAILDOORMODE_OPEN 1 +#define JAILDOORMODE_CLOSED 2 + +#define OPENJAILS_LOCKED 0 +#define OPENJAILS_OPEN 1 +#define OPENJAILS_LOCKED_FORCE 2 +#define OPENJAILS_OPEN_FORCE 3 + +// list of doors on the map +entity jb_worlddoorlist; +.entity jb_worlddoornext; + +// list of control points on the map +entity jb_worldcplist; +.entity jb_worldcpnext; + +.float jb_torture_force; +.float jb_torture_force_jitter; +.float jb_torture_delay; +.float jb_torture_delay_jitter; +.float jb_torture_suggestedforce; + +#define JB_PROJ_OWNERSTATE_UNDEFINED 0 +#define JB_PROJ_OWNERSTATE_IMPRISONED 1 +#define JB_PROJ_OWNERSTATE_FREE 2 + +// TODO: cvar this +#define JB_TORTURE_DURATION 15 ++ ++#endif diff --cc qcsrc/server/mutators/gamemode_keepaway.qc index f46d6883b,095857e14..fc4a487b2 --- a/qcsrc/server/mutators/gamemode_keepaway.qc +++ b/qcsrc/server/mutators/gamemode_keepaway.qc @@@ -1,7 -1,7 +1,4 @@@ --// =========================================================== --// Keepaway game mode coding, written by Samual and Diabolik --// Last updated: September, 2012 --// =========================================================== ++#include "../../common/effects.qh" float ka_ballcarrier_waypointsprite_visible_for_player(entity e) // runs on waypoints which are attached to ballcarriers, updates once per frame { @@@ -40,10 -40,10 +37,10 @@@ void ka_RespawnBall() // runs whenever self.think = ka_RespawnBall; self.nextthink = time + autocvar_g_keepawayball_respawntime; - pointparticles(particleeffectnum("electro_combo"), oldballorigin, '0 0 0', 1); - pointparticles(particleeffectnum("electro_combo"), self.origin, '0 0 0', 1); + Send_Effect(EFFECT_ELECTRO_COMBO, oldballorigin, '0 0 0', 1); + Send_Effect(EFFECT_ELECTRO_COMBO, self.origin, '0 0 0', 1); - WaypointSprite_Spawn("ka-ball", 0, 0, self, '0 0 64', world, self.team, self, waypointsprite_attachedforcarrier, FALSE, RADARICON_FLAGCARRIER, '0 1 1'); + WaypointSprite_Spawn("ka-ball", 0, 0, self, '0 0 64', world, self.team, self, waypointsprite_attachedforcarrier, false, RADARICON_FLAGCARRIER, '0 1 1'); WaypointSprite_Ping(self.waypointsprite_attachedforcarrier); sound(self, CH_TRIGGER, "keepaway/respawn.wav", VOL_BASE, ATTEN_NONE); // ATTEN_NONE (it's a sound intended to be heard anywhere) @@@ -343,33 -343,15 +340,33 @@@ MUTATOR_HOOKFUNCTION(ka_PlayerPowerups return 0; } +MUTATOR_HOOKFUNCTION(ka_PlayerPhysics) +{ + if(self.ballcarried) + { + self.stat_sv_airspeedlimit_nonqw *= autocvar_g_keepaway_ballcarrier_highspeed; + self.stat_sv_maxspeed *= autocvar_g_keepaway_ballcarrier_highspeed; + } - return FALSE; ++ return false; +} + MUTATOR_HOOKFUNCTION(ka_BotRoles) { if (self.ballcarried) self.havocbot_role = havocbot_role_ka_carrier; else self.havocbot_role = havocbot_role_ka_collector; - return TRUE; + return true; } +MUTATOR_HOOKFUNCTION(ka_BotShouldAttack) +{ + // if neither player has ball then don't attack unless the ball is on the ground + if(!other.ballcarried && !self.ballcarried && ka_ball.owner) - return TRUE; - return FALSE; ++ return true; ++ return false; +} + // ============== // Initialization diff --cc qcsrc/server/mutators/gamemode_keyhunt.qc index ba7b9322f,2a6731610..5935f8c1a --- a/qcsrc/server/mutators/gamemode_keyhunt.qc +++ b/qcsrc/server/mutators/gamemode_keyhunt.qc @@@ -1,84 -1,201 +1,82 @@@ - // ========================================================= - // Unofficial key hunt game mode coding, reworked by Mario - // ========================================================= -#define FOR_EACH_KH_KEY(v) for(v = kh_worldkeylist; v; v = v.kh_worldkeynext ) - -// #define KH_PLAYER_USE_ATTACHMENT -// #define KH_PLAYER_USE_CARRIEDMODEL - -#ifdef KH_PLAYER_USE_ATTACHMENT -const vector KH_PLAYER_ATTACHMENT_DIST_ROTATED = '0 -4 0'; -const vector KH_PLAYER_ATTACHMENT_DIST = '4 0 0'; -const vector KH_PLAYER_ATTACHMENT = '0 0 0'; -const vector KH_PLAYER_ATTACHMENT_ANGLES = '0 0 0'; -const string KH_PLAYER_ATTACHMENT_BONE = ""; -#else -const float KH_KEY_ZSHIFT = 22; -const float KH_KEY_XYDIST = 24; -const float KH_KEY_XYSPEED = 45; -#endif -const float KH_KEY_WP_ZSHIFT = 20; - -const vector KH_KEY_MIN = '-10 -10 -46'; -const vector KH_KEY_MAX = '10 10 3'; -const float KH_KEY_BRIGHTNESS = 2; - -float kh_no_radar_circles; - -// kh_state -// bits 0- 4: team of key 1, or 0 for no such key, or 30 for dropped, or 31 for self -// bits 5- 9: team of key 2, or 0 for no such key, or 30 for dropped, or 31 for self -// bits 10-14: team of key 3, or 0 for no such key, or 30 for dropped, or 31 for self -// bits 15-19: team of key 4, or 0 for no such key, or 30 for dropped, or 31 for self -.float kh_state; -.float siren_time; // time delay the siren -//.float stuff_time; // time delay to stuffcmd a cvar - -float kh_keystatus[17]; -//kh_keystatus[0] = status of dropped keys, kh_keystatus[1 - 16] = player # -//replace 17 with cvar("maxplayers") or similar !!!!!!!!! -//for(i = 0; i < maxplayers; ++i) -// kh_keystatus[i] = "0"; - -float kh_Team_ByID(float t) -{ - if(t == 0) return NUM_TEAM_1; - if(t == 1) return NUM_TEAM_2; - if(t == 2) return NUM_TEAM_3; - if(t == 3) return NUM_TEAM_4; - return 0; -} - -//entity kh_worldkeylist; -.entity kh_worldkeynext; -entity kh_controller; -//float kh_tracking_enabled; -float kh_teams; -float kh_interferemsg_time, kh_interferemsg_team; -.entity kh_next, kh_prev; // linked list -.float kh_droptime; -.float kh_dropperteam; -.entity kh_previous_owner; -.float kh_previous_owner_playerid; -.float kh_cp_duration; - -string kh_sound_capture = "kh/capture.wav"; -string kh_sound_destroy = "kh/destroy.wav"; -string kh_sound_drop = "kh/drop.wav"; -string kh_sound_collect = "kh/collect.wav"; -string kh_sound_alarm = "kh/alarm.wav"; // the new siren/alarm - -float kh_key_dropped, kh_key_carried; - -const float ST_KH_CAPS = 1; -const float SP_KH_CAPS = 4; -const float SP_KH_PUSHES = 5; -const float SP_KH_DESTROYS = 6; -const float SP_KH_PICKUPS = 7; -const float SP_KH_KCKILLS = 8; -const float SP_KH_LOSSES = 9; -void kh_ScoreRules(float teams) -{ - ScoreRules_basics(teams, SFL_SORT_PRIO_PRIMARY, SFL_SORT_PRIO_PRIMARY, true); - ScoreInfo_SetLabel_TeamScore( ST_KH_CAPS, "caps", SFL_SORT_PRIO_SECONDARY); - ScoreInfo_SetLabel_PlayerScore(SP_KH_CAPS, "caps", SFL_SORT_PRIO_SECONDARY); - ScoreInfo_SetLabel_PlayerScore(SP_KH_PUSHES, "pushes", 0); - ScoreInfo_SetLabel_PlayerScore(SP_KH_DESTROYS, "destroyed", SFL_LOWER_IS_BETTER); - ScoreInfo_SetLabel_PlayerScore(SP_KH_PICKUPS, "pickups", 0); - ScoreInfo_SetLabel_PlayerScore(SP_KH_KCKILLS, "kckills", 0); - ScoreInfo_SetLabel_PlayerScore(SP_KH_LOSSES, "losses", SFL_LOWER_IS_BETTER); - ScoreRules_basics_end(); -} ++#include "../../common/effects.qh" -float kh_KeyCarrier_waypointsprite_visible_for_player(entity e) // runs all the time +void kh_EventLog(string mode, float keyteam, entity actor) // use an alias for easy changing and quick editing later { - if(!IS_PLAYER(e) || self.team != e.team) - if(!kh_tracking_enabled) - return false; - - return true; + if(autocvar_sv_eventlog) + GameLogEcho(sprintf(":keyhunt:%s:%d:%d:%s", mode, keyteam, actor.team, ((actor != world) ? ftos(actor.playerid) : ""))); } -float kh_Key_waypointsprite_visible_for_player(entity e) // ?? +void kh_KeycarrierWaypoints(entity player) { - WaypointSprite_Spawn("keycarrier", 0, 0, player, KEY_WAYPOINT_OFFSET, world, 0, player, wps_keycarrier, TRUE, RADARICON_FLAG, WPCOLOR_ENEMYKC(player.team)); - if(!kh_tracking_enabled) - return false; - if(!self.owner) - return true; - if(!self.owner.owner) - return true; - return false; // draw only when key is not owned ++ WaypointSprite_Spawn("keycarrier", 0, 0, player, KEY_WAYPOINT_OFFSET, world, 0, player, wps_keycarrier, true, RADARICON_FLAG, WPCOLOR_ENEMYKC(player.team)); + WaypointSprite_UpdateMaxHealth(player.wps_keycarrier, '1 0 0' * healtharmor_maxdamage(start_health, start_armorvalue, autocvar_g_balance_armor_blockpercent, DEATH_WEAPON) * 2); + WaypointSprite_UpdateHealth(player.wps_keycarrier, '1 0 0' * healtharmor_maxdamage(player.health, player.armorvalue, autocvar_g_balance_armor_blockpercent, DEATH_WEAPON)); + WaypointSprite_UpdateTeamRadar(player.wps_keycarrier, RADARICON_FLAGCARRIER, WPCOLOR_ENEMYKC(player.team)); } -void kh_update_state() +void kh_CalculatePassVelocity(entity key, vector to, vector from, float turnrate) { - entity player; - entity key; - float s; - float f; + float current_distance = vlen((('1 0 0' * to_x) + ('0 1 0' * to_y)) - (('1 0 0' * from_x) + ('0 1 0' * from_y))); // for the sake of this check, exclude Z axis + float initial_height = min(autocvar_g_keyhunt_pass_arc_max, (key.pass_distance * tanh(autocvar_g_keyhunt_pass_arc))); + float current_height = (initial_height * min(1, (current_distance / key.pass_distance))); + //print("current_height = ", ftos(current_height), ", initial_height = ", ftos(initial_height), ".\n"); - s = 0; - FOR_EACH_KH_KEY(key) + vector targpos; + if(current_height) // make sure we can actually do this arcing path { - if(key.owner) - f = key.team; - else - f = 30; - s |= pow(32, key.count) * f; + targpos = (to + ('0 0 1' * current_height)); + WarpZone_TraceLine(key.origin, targpos, MOVE_NOMONSTERS, key); + if(trace_fraction < 1) + { + //print("normal arc line failed, trying to find new pos..."); + WarpZone_TraceLine(to, targpos, MOVE_NOMONSTERS, key); + targpos = (trace_endpos + KEY_PASS_ARC_OFFSET); + WarpZone_TraceLine(key.origin, targpos, MOVE_NOMONSTERS, key); + if(trace_fraction < 1) { targpos = to; /* print(" ^1FAILURE^7, reverting to original direction.\n"); */ } + /*else { print(" ^3SUCCESS^7, using new arc line.\n"); } */ + } } + else { targpos = to; } - FOR_EACH_CLIENT(player) - { - player.kh_state = s; - } + //key.angles = normalize(('0 1 0' * to_y) - ('0 1 0' * from_y)); - FOR_EACH_KH_KEY(key) - { - if(key.owner) - key.owner.kh_state |= pow(32, key.count) * 31; - } - //print(ftos((nextent(world)).kh_state), "\n"); + vector desired_direction = normalize(targpos - from); + if(turnrate) { key.velocity = (normalize(normalize(key.velocity) + (desired_direction * autocvar_g_keyhunt_pass_turnrate)) * autocvar_g_keyhunt_pass_velocity); } + else { key.velocity = (desired_direction * autocvar_g_keyhunt_pass_velocity); } } - - - -var kh_Think_t kh_Controller_Thinkfunc; -void kh_Controller_SetThink(float t, kh_Think_t func) // runs occasionaly +float kh_CheckPassDirection(vector head_center, vector passer_center, vector passer_angle, vector nearest_to_passer) { - kh_Controller_Thinkfunc = func; - kh_controller.cnt = ceil(t); - if(t == 0) - kh_controller.nextthink = time; // force -} -void kh_WaitForPlayers(); -void kh_Controller_Think() // called a lot -{ - if(intermission_running) - return; - if(self.cnt > 0) - { if(self.think != kh_WaitForPlayers) { self.cnt -= 1; } } - else if(self.cnt == 0) + if(autocvar_g_keyhunt_pass_directional_max || autocvar_g_keyhunt_pass_directional_min) { - self.cnt -= 1; - kh_Controller_Thinkfunc(); - } - self.nextthink = time + 1; -} - -// frags f: take from cvar * f -// frags 0: no frags -void kh_Scores_Event(entity player, entity key, string what, float frags_player, float frags_owner) // update the score when a key is captured -{ - string s; - if(intermission_running) - return; - - if(frags_player) - UpdateFrags(player, frags_player); - - if(key && key.owner && frags_owner) - UpdateFrags(key.owner, frags_owner); - - if(!autocvar_sv_eventlog) //output extra info to the console or text file - return; + // directional tracing only + float spreadlimit; + makevectors(passer_angle); - s = strcat(":keyhunt:", what, ":", ftos(player.playerid), ":", ftos(frags_player)); + // find the closest point on the enemy to the center of the attack + float ang; // angle between shotdir and h + float h; // hypotenuse, which is the distance between attacker to head + float a; // adjacent side, which is the distance between attacker and the point on w_shotdir that is closest to head.origin - if(key && key.owner) - s = strcat(s, ":", ftos(key.owner.playerid)); - else - s = strcat(s, ":0"); + h = vlen(head_center - passer_center); + ang = acos(dotproduct(normalize(head_center - passer_center), v_forward)); + a = h * cos(ang); - s = strcat(s, ":", ftos(frags_owner), ":"); + vector nearest_on_line = (passer_center + a * v_forward); + float distance_from_line = vlen(nearest_to_passer - nearest_on_line); - if(key) - s = strcat(s, key.netname); + spreadlimit = (autocvar_g_keyhunt_pass_radius ? min(1, (vlen(passer_center - nearest_on_line) / autocvar_g_keyhunt_pass_radius)) : 1); + spreadlimit = (autocvar_g_keyhunt_pass_directional_min * (1 - spreadlimit) + autocvar_g_keyhunt_pass_directional_max * spreadlimit); - GameLogEcho(s); + if(spreadlimit && (distance_from_line <= spreadlimit) && ((vlen(normalize(head_center - passer_center) - v_forward) * RAD2DEG) <= 90)) - { return TRUE; } ++ { return true; } + else - { return FALSE; } ++ { return false; } + } - else { return TRUE; } ++ else { return true; } } -vector kh_AttachedOrigin(entity e) // runs when a team captures the flag, it can run 2 or 3 times. +vector kh_AttachedOrigin(entity e) { if(e.tag_entity) { @@@ -89,1501 -206,889 +87,1498 @@@ return e.origin; } -void kh_Key_Attach(entity key) // runs when a player picks up a key and several times when a key is assigned to a player at the start of a round -{ -#ifdef KH_PLAYER_USE_ATTACHMENT - entity first; - first = key.owner.kh_next; - if(key == first) - { - setattachment(key, key.owner, KH_PLAYER_ATTACHMENT_BONE); - if(key.kh_next) - { - setattachment(key.kh_next, key, ""); - setorigin(key, key.kh_next.origin - 0.5 * KH_PLAYER_ATTACHMENT_DIST); - setorigin(key.kh_next, KH_PLAYER_ATTACHMENT_DIST_ROTATED); - key.kh_next.angles = '0 0 0'; - } - else - setorigin(key, KH_PLAYER_ATTACHMENT); - key.angles = KH_PLAYER_ATTACHMENT_ANGLES; - } - else - { - setattachment(key, key.kh_prev, ""); - if(key.kh_next) - setattachment(key.kh_next, key, ""); - setorigin(key, KH_PLAYER_ATTACHMENT_DIST_ROTATED); - setorigin(first, first.origin - 0.5 * KH_PLAYER_ATTACHMENT_DIST); - key.angles = '0 0 0'; - } -#else - setattachment(key, key.owner, ""); - setorigin(key, '0 0 1' * KH_KEY_ZSHIFT); // fixing x, y in think - key.angles_y -= key.owner.angles.y; -#endif - key.flags = 0; - key.solid = SOLID_NOT; - key.movetype = MOVETYPE_NONE; - key.team = key.owner.team; - key.nextthink = time; - key.damageforcescale = 0; - key.takedamage = DAMAGE_NO; - key.modelindex = kh_key_carried; -} -void kh_Key_Detach(entity key) // runs every time a key is dropped or lost. Runs several times times when all the keys are captured +// ================ +// Round Handling +// ================ + - float KH_GetWinnerTeam() ++int KH_GetWinnerTeam() { - float winner_team = 0; -#ifdef KH_PLAYER_USE_ATTACHMENT - entity first; - first = key.owner.kh_next; - if(key == first) - { - if(key.kh_next) - { - setattachment(key.kh_next, key.owner, KH_PLAYER_ATTACHMENT_BONE); - setorigin(key.kh_next, key.origin + 0.5 * KH_PLAYER_ATTACHMENT_DIST); - key.kh_next.angles = KH_PLAYER_ATTACHMENT_ANGLES; - } - } - else ++ int winner_team = 0; + entity key; + + KH_FOR_EACH_KEY(key) { - if(key.kh_next) - setattachment(key.kh_next, key.kh_prev, ""); - setorigin(first, first.origin + 0.5 * KH_PLAYER_ATTACHMENT_DIST); + if(!key.owner) { return 0; } // no carriers, no winners + if(winner_team && winner_team != key.owner.team) { return 0; } + winner_team = key.owner.team; } - // in any case: - setattachment(key, world, ""); - setorigin(key, key.owner.origin + '0 0 1' * (PL_MIN_z - KH_KEY_MIN_z)); - key.angles = key.owner.angles; -#else - setorigin(key, key.owner.origin + key.origin.z * '0 0 1'); - setattachment(key, world, ""); - key.angles_y += key.owner.angles.y; -#endif - key.flags = FL_ITEM; - key.solid = SOLID_TRIGGER; - key.movetype = MOVETYPE_TOSS; - key.pain_finished = time + autocvar_g_balance_keyhunt_delay_return; - key.damageforcescale = autocvar_g_balance_keyhunt_damageforcescale; - key.takedamage = DAMAGE_YES; - // let key.team stay - key.modelindex = kh_key_dropped; - key.kh_previous_owner = key.owner; - key.kh_previous_owner_playerid = key.owner.playerid; + + if(winner_team) { return winner_team; } + + return -1; // no winner? } -void kh_Key_AssignTo(entity key, entity player) // runs every time a key is picked up or assigned. Runs prior to kh_key_attach +void kh_Handle_Capture(float capturetype); +void kh_RemoveKey(entity key); +float KH_CheckWinner() { - entity k; - float ownerteam0, ownerteam; - if(key.owner == player) - return; - - ownerteam0 = kh_Key_AllOwnedByWhichTeam(); - - if(key.owner) + entity e, key; + if(round_handler_GetEndTime() > 0 && round_handler_GetEndTime() - time <= 0) { - kh_Key_Detach(key); - - // remove from linked list - if(key.kh_next) - key.kh_next.kh_prev = key.kh_prev; - key.kh_prev.kh_next = key.kh_next; - key.kh_next = world; - key.kh_prev = world; - - if(key.owner.kh_next == world) + Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_ROUND_OVER); + Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_ROUND_OVER); + round_handler_Init(5, autocvar_g_keyhunt_warmup, autocvar_g_keyhunt_round_timelimit); + FOR_EACH_CLIENT(e) { - // No longer a key carrier - if(!kh_no_radar_circles) - WaypointSprite_Ping(key.owner.waypointsprite_attachedforcarrier); - WaypointSprite_DetachCarrier(key.owner); + e.kh_lastkiller = world; } - nades_Clear(world, TRUE); ++ nades_Clear(world, true); + KH_FOR_EACH_KEY(key) { if(!wasfreed(key)) kh_RemoveKey(key); } + kh_worldkeylist = world; // reset key list + return 1; } - - key.owner = player; - - if(player) - { - // insert into linked list - key.kh_next = player.kh_next; - key.kh_prev = player; - player.kh_next = key; - if(key.kh_next) - key.kh_next.kh_prev = key; - - float i; - i = kh_keystatus[key.owner.playerid]; - if(key.netname == "^1red key") - i += 1; - if(key.netname == "^4blue key") - i += 2; - if(key.netname == "^3yellow key") - i += 4; - if(key.netname == "^6pink key") - i += 8; - kh_keystatus[key.owner.playerid] = i; - - kh_Key_Attach(key); - - if(key.kh_next == world) + + float winner_team = KH_GetWinnerTeam(); + + float key_count = 0; + KH_FOR_EACH_KEY(key) { if(!wasfreed(key)) ++key_count; } + if(key_count >= kh_teams && winner_team == 0) { return 0; } + + if(winner_team > 0) + { + float largest_dist = 0; + KH_FOR_EACH_KEY(key) { - // player is now a key carrier - WaypointSprite_AttachCarrier("", player, RADARICON_FLAGCARRIER, colormapPaletteColor(player.team - 1, 0)); - player.waypointsprite_attachedforcarrier.waypointsprite_visible_for_player = kh_KeyCarrier_waypointsprite_visible_for_player; - WaypointSprite_UpdateRule(player.waypointsprite_attachedforcarrier, player.team, SPRITERULE_TEAMPLAY); - if(player.team == NUM_TEAM_1) - WaypointSprite_UpdateSprites(player.waypointsprite_attachedforcarrier, "keycarrier-red", "keycarrier-friend", "keycarrier-red"); - else if(player.team == NUM_TEAM_2) - WaypointSprite_UpdateSprites(player.waypointsprite_attachedforcarrier, "keycarrier-blue", "keycarrier-friend", "keycarrier-blue"); - else if(player.team == NUM_TEAM_3) - WaypointSprite_UpdateSprites(player.waypointsprite_attachedforcarrier, "keycarrier-yellow", "keycarrier-friend", "keycarrier-yellow"); - else if(player.team == NUM_TEAM_4) - WaypointSprite_UpdateSprites(player.waypointsprite_attachedforcarrier, "keycarrier-pink", "keycarrier-friend", "keycarrier-pink"); - if(!kh_no_radar_circles) - WaypointSprite_Ping(player.waypointsprite_attachedforcarrier); + if(!key.owner) { return 0; } + if(key.kh_worldkeynext) + if(vlen(kh_AttachedOrigin(key) - kh_AttachedOrigin(key.kh_worldkeynext)) >= largest_dist || largest_dist == 0) + largest_dist = vlen(kh_AttachedOrigin(key) - kh_AttachedOrigin(key.kh_worldkeynext)); } - } - - // moved that here, also update if there's no player - kh_update_state(); - - key.pusher = world; - - ownerteam = kh_Key_AllOwnedByWhichTeam(); - if(ownerteam != ownerteam0) - { - if(ownerteam != -1) + if(largest_dist >= autocvar_g_keyhunt_capture_radius) { - kh_interferemsg_time = time + 0.2; - kh_interferemsg_team = player.team; - - // audit all key carrier sprites, update them to RUN HERE - FOR_EACH_KH_KEY(k) + entity head; + if(time >= kh_alarm_time) + FOR_EACH_PLAYER(head) { - float head_iscarrier = FALSE; - if(k.owner) - WaypointSprite_UpdateSprites(k.owner.waypointsprite_attachedforcarrier, k.owner.waypointsprite_attachedforcarrier.model1, "keycarrier-finish", k.owner.waypointsprite_attachedforcarrier.model3); ++ float head_iscarrier = false; + KH_FOR_EACH_KEY(key) + if(key.owner == head) + { - head_iscarrier = TRUE; ++ head_iscarrier = true; + break; + } + + if(head_iscarrier) { Send_Notification(NOTIF_ONE, head, MSG_CENTER, CENTER_KEYHUNT_MEET); } + else if(head.team == winner_team) { Send_Notification(NOTIF_ONE, head, MSG_CENTER, CENTER_KEYHUNT_HELP); } + else { Send_Notification(NOTIF_ONE, head, MSG_CENTER, APP_TEAM_NUM_4(winner_team, CENTER_KEYHUNT_INTERFERE_)); } + + kh_alarm_time = time + 2; } - } - else - { - kh_interferemsg_time = 0; - - // audit all key carrier sprites, update them to RUN HERE - FOR_EACH_KH_KEY(k) + if(time >= kh_siren_time) { - if(k.owner) - WaypointSprite_UpdateSprites(k.owner.waypointsprite_attachedforcarrier, k.owner.waypointsprite_attachedforcarrier.model1, "keycarrier-friend", k.owner.waypointsprite_attachedforcarrier.model3); + KH_FOR_EACH_KEY(key) + sound(key.owner, CH_TRIGGER, "kh/alarm.wav", VOL_BASE, ATTEN_NORM); + kh_siren_time = time + 3; } + return 0; } - } -} -void kh_Key_Damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force) -{ - if(self.owner) - return; - if(ITEM_DAMAGE_NEEDKILL(deathtype)) - { - // touching lava, or hurt trigger - // what shall we do? - // immediately return is bad - // maybe start a shorter countdown? + kh_Handle_Capture(CAPTURE_NORMAL); + TeamScore_AddToTeam(winner_team, ST_KH_CAPS, 1); + + kh_EventLog("capture", winner_team, world); } - if(vlen(force) <= 0) - return; - if(time > self.pushltime) - if(IS_PLAYER(attacker)) - self.team = attacker.team; -} -void kh_Key_Collect(entity key, entity player) //a player picks up a dropped key -{ - sound(player, CH_TRIGGER, kh_sound_collect, VOL_BASE, ATTEN_NORM); + round_handler_Init(5, autocvar_g_keyhunt_warmup, autocvar_g_keyhunt_round_timelimit); - if(key.kh_dropperteam != player.team) + FOR_EACH_CLIENT(e) { - kh_Scores_Event(player, key, "collect", autocvar_g_balance_keyhunt_score_collect, 0); - PlayerScore_Add(player, SP_KH_PICKUPS, 1); + e.kh_lastkiller = world; } - key.kh_dropperteam = 0; - Send_Notification(NOTIF_ALL, world, MSG_INFO, APP_TEAM_ENT_4(key, INFO_KEYHUNT_PICKUP_), player.netname); + - nades_Clear(world, TRUE); ++ nades_Clear(world, true); - kh_Key_AssignTo(key, player); // this also updates .kh_state + KH_FOR_EACH_KEY(key) { if(!wasfreed(key)) kh_RemoveKey(key); } + kh_worldkeylist = world; // reset key list + + return 1; } -void kh_Key_Touch() // runs many, many times when a key has been dropped and can be picked up +entity KH_ChooseCarrier(float teamnumber) { - if(intermission_running) - return; - - if(self.owner) // already carried - return; - - if(ITEM_TOUCH_NEEDKILL()) - { - // touching sky, or nodrop - // what shall we do? - // immediately return is bad - // maybe start a shorter countdown? - } + entity head; + RandomSelection_Init(); + FOR_EACH_PLAYER(head) + if(head.health > 0) + if(!head.vehicle || autocvar_g_keyhunt_allow_vehicle_carry) + if(head.frozen == 0) + if(head.team == teamnumber) + RandomSelection_Add(head, 0, string_null, 1, ((IS_REAL_CLIENT(head)) ? 1 : 0.5)); + + return RandomSelection_chosen_ent; +} - if (!IS_PLAYER(other)) - return; - if(other.deadflag != DEAD_NO) - return; - if(other == self.enemy) - if(time < self.kh_droptime + autocvar_g_balance_keyhunt_delay_collect) - return; // you just dropped it! - kh_Key_Collect(self, other); +void kh_KeySetup(float teamnumber, entity key, entity player); +void KH_RoundStart() +{ + entity tmp_entity; + kh_KeySetup(NUM_TEAM_1, (tmp_entity = spawn()), KH_ChooseCarrier(NUM_TEAM_1)); + kh_KeySetup(NUM_TEAM_2, (tmp_entity = spawn()), KH_ChooseCarrier(NUM_TEAM_2)); + if(kh_teams >= 3) { kh_KeySetup(NUM_TEAM_3, (tmp_entity = spawn()), KH_ChooseCarrier(NUM_TEAM_3)); } + if(kh_teams >= 4) { kh_KeySetup(NUM_TEAM_4, (tmp_entity = spawn()), KH_ChooseCarrier(NUM_TEAM_4)); } + + FOR_EACH_PLAYER(tmp_entity) { tmp_entity.spawnshieldtime = time + autocvar_g_spawnshieldtime; } } - float total_players; - float redalive, bluealive, yellowalive, pinkalive; -void kh_Key_Remove(entity key) // runs after when all the keys have been collected or when a key has been dropped for more than X seconds +void KH_count_alive_players() { - entity o; - o = key.owner; - kh_Key_AssignTo(key, world); - if(o) // it was attached - WaypointSprite_Kill(key.waypointsprite_attachedforcarrier); - else // it was dropped - WaypointSprite_DetachCarrier(key); - - // remove key from key list - if (kh_worldkeylist == key) - kh_worldkeylist = kh_worldkeylist.kh_worldkeynext; - else + entity e; + total_players = redalive = bluealive = yellowalive = pinkalive = 0; + FOR_EACH_PLAYER(e) { - o = kh_worldkeylist; - while (o) + switch(e.team) { - if (o.kh_worldkeynext == key) - { - o.kh_worldkeynext = o.kh_worldkeynext.kh_worldkeynext; - break; - } - o = o.kh_worldkeynext; + case NUM_TEAM_1: ++total_players; if(e.health > 0 && e.frozen == 0 && (!e.vehicle || autocvar_g_keyhunt_allow_vehicle_carry)) ++redalive; break; + case NUM_TEAM_2: ++total_players; if(e.health > 0 && e.frozen == 0 && (!e.vehicle || autocvar_g_keyhunt_allow_vehicle_carry)) ++bluealive; break; + case NUM_TEAM_3: ++total_players; if(e.health > 0 && e.frozen == 0 && (!e.vehicle || autocvar_g_keyhunt_allow_vehicle_carry)) ++yellowalive; break; + case NUM_TEAM_4: ++total_players; if(e.health > 0 && e.frozen == 0 && (!e.vehicle || autocvar_g_keyhunt_allow_vehicle_carry)) ++pinkalive; break; } } +} - remove(key); +#define KH_ALIVE_TEAMS() ((redalive > 0) + (bluealive > 0) + (yellowalive > 0) + (pinkalive > 0)) +#define KH_ALIVE_TEAMS_OK() (KH_ALIVE_TEAMS() == kh_teams) - float prev_missing_teams_mask; - kh_update_state(); +float KH_CheckTeams() +{ - allowed_to_spawn = TRUE; ++ static float prev_missing_teams_mask; ++ allowed_to_spawn = true; + KH_count_alive_players(); + if(KH_ALIVE_TEAMS_OK()) + { + if(prev_missing_teams_mask > 0) + Kill_Notification(NOTIF_ALL, world, MSG_CENTER_CPID, CPID_MISSING_TEAMS); + prev_missing_teams_mask = -1; + return 1; + } + if(total_players == 0) + { + if(prev_missing_teams_mask > 0) + Kill_Notification(NOTIF_ALL, world, MSG_CENTER_CPID, CPID_MISSING_TEAMS); + prev_missing_teams_mask = -1; + return 0; + } + float missing_teams_mask = (!redalive) + (!bluealive) * 2; + if(kh_teams >= 3) missing_teams_mask += (!yellowalive) * 4; + if(kh_teams >= 4) missing_teams_mask += (!pinkalive) * 8; + if(prev_missing_teams_mask != missing_teams_mask) + { + Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_MISSING_TEAMS, missing_teams_mask); + prev_missing_teams_mask = missing_teams_mask; + } + return 0; } -void kh_FinishRound() // runs when a team captures the keys +entity kh_ChooseNewCarrier(entity key) { - // prepare next round - kh_interferemsg_time = 0; - entity key; - - kh_no_radar_circles = true; - FOR_EACH_KH_KEY(key) - kh_Key_Remove(key); - kh_no_radar_circles = false; - - Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_KEYHUNT_ROUNDSTART, autocvar_g_balance_keyhunt_delay_round); - kh_Controller_SetThink(autocvar_g_balance_keyhunt_delay_round, kh_StartRound); + entity tmp_entity, player = key.kh_dropper; + + RandomSelection_Init(); + FOR_EACH_PLAYER(tmp_entity) + if(tmp_entity != player) + if(!tmp_entity.frozen) + if(!tmp_entity.vehicle || autocvar_g_keyhunt_allow_vehicle_carry) + if(tmp_entity.health > 0) + if(DIFF_TEAM(tmp_entity, player)) + RandomSelection_Add(tmp_entity, 0, string_null, 1, ((IS_REAL_CLIENT(tmp_entity)) ? 1 : 0.5)); + + return RandomSelection_chosen_ent; } - -void kh_WinnerTeam(float teem) // runs when a team wins // Samual: Teem?.... TEEM?!?! what the fuck is wrong with you people +// ==================== +// Drop/Pass/Throw Code +// ==================== + +void kh_Handle_Drop(entity key, entity player, float droptype) { - // all key carriers get some points - vector firstorigin, lastorigin, midpoint; - float first; - entity key; - float score; - score = (kh_teams - 1) * autocvar_g_balance_keyhunt_score_capture; - DistributeEvenly_Init(score, kh_teams); - // twice the score for 3 team games, three times the score for 4 team games! - // note: for a win by destroying the key, this should NOT be applied - FOR_EACH_KH_KEY(key) - { - float f; - f = DistributeEvenly_Get(1); - kh_Scores_Event(key.owner, key, "capture", f, 0); - PlayerTeamScore_Add(key.owner, SP_KH_CAPS, ST_KH_CAPS, 1); - nades_GiveBonus(key.owner, autocvar_g_nades_bonus_score_high); - } - - first = true; - string keyowner = ""; - FOR_EACH_KH_KEY(key) - if(key.owner.kh_next == key) - { - if(!first) - keyowner = strcat(keyowner, ", "); - keyowner = key.owner.netname; - first = false; - } + //print("Drop called for ", key.netname, " by player ", player, ", droptype: ", ftos(droptype), "\n"); + // declarations + player = (player ? player : key.pass_sender); + + // main + key.movetype = MOVETYPE_TOSS; + key.takedamage = DAMAGE_YES; + key.angles = '0 0 0'; + key.health = key.max_key_health; + key.kh_droptime = time; + key.kh_dropper = player; + key.kh_status = KEY_DROPPED; - Send_Notification(NOTIF_ALL, world, MSG_INFO, APP_TEAM_NUM_4(teem, INFO_KEYHUNT_CAPTURE_), keyowner); + // messages and sounds + Send_Notification(NOTIF_ALL, world, MSG_INFO, APP_TEAM_ENT_4(key, INFO_KEYHUNT_LOST_), player.netname); + sound(key, CH_TRIGGER, key.snd_key_dropped, VOL_BASE, ATTEN_NONE); + kh_EventLog("dropped", player.team, player); - first = true; - midpoint = '0 0 0'; - firstorigin = '0 0 0'; - lastorigin = '0 0 0'; - FOR_EACH_KH_KEY(key) - { - vector thisorigin; + // scoring + PlayerTeamScore_AddScore(player, -autocvar_g_keyhunt_score_penalty_drop); + PlayerScore_Add(player, SP_KH_DROPS, 1); - thisorigin = kh_AttachedOrigin(key); - //dprint("Key origin: ", vtos(thisorigin), "\n"); - midpoint += thisorigin; + // waypoints + if(autocvar_g_keyhunt_key_dropped_waypoint) - WaypointSprite_Spawn("keydropped", 0, 0, key, KEY_DROP_OFFSET, world, ((autocvar_g_keyhunt_key_dropped_waypoint == 2) ? 0 : player.team), key, wps_keydropped, TRUE, RADARICON_FLAG, WPCOLOR_DROPPEDKEY(key.team)); ++ WaypointSprite_Spawn("keydropped", 0, 0, key, KEY_DROP_OFFSET, world, ((autocvar_g_keyhunt_key_dropped_waypoint == 2) ? 0 : player.team), key, wps_keydropped, true, RADARICON_FLAG, WPCOLOR_DROPPEDKEY(key.team)); - if(!first) - te_lightning2(world, lastorigin, thisorigin); - lastorigin = thisorigin; - if(first) - firstorigin = thisorigin; - first = false; - } - if(kh_teams > 2) + if(autocvar_g_keyhunt_key_return_time || (autocvar_g_keyhunt_key_return_damage && autocvar_g_keyhunt_key_health)) { - te_lightning2(world, lastorigin, firstorigin); + WaypointSprite_UpdateMaxHealth(key.wps_keydropped, key.max_key_health); + WaypointSprite_UpdateHealth(key.wps_keydropped, key.health); } - midpoint = midpoint * (1 / kh_teams); - te_customflash(midpoint, 1000, 1, Team_ColorRGB(teem) * 0.5 + '0.5 0.5 0.5'); // make the color >=0.5 in each component - play2all(kh_sound_capture); - kh_FinishRound(); + player.throw_antispam = time + autocvar_g_keyhunt_pass_wait; + + if(droptype == DROP_PASS) + { + key.pass_distance = 0; + key.pass_sender = world; + key.pass_target = world; + } } -void kh_LoserTeam(float teem, entity lostkey) // runs when a player pushes a flag carrier off the map +void kh_Handle_Retrieve(entity key, entity player) { - entity player, key, attacker; - float players; - float keys; - float f; + entity tmp_player; // temporary entity which the FOR_EACH_PLAYER loop uses to scan players + entity sender = key.pass_sender; - attacker = world; - if(lostkey.pusher) - if(lostkey.pusher.team != teem) - if(IS_PLAYER(lostkey.pusher)) - attacker = lostkey.pusher; - - players = keys = 0; + // transfer key to player + key.owner = player; - if(attacker) + // reset key + if(player.vehicle) { - if(lostkey.kh_previous_owner) - kh_Scores_Event(lostkey.kh_previous_owner, world, "pushed", 0, -autocvar_g_balance_keyhunt_score_push); - // don't actually GIVE him the -nn points, just log - kh_Scores_Event(attacker, world, "push", autocvar_g_balance_keyhunt_score_push, 0); - PlayerScore_Add(attacker, SP_KH_PUSHES, 1); - //centerprint(attacker, "Your push is the best!"); // does this really need to exist? + setattachment(key, player.vehicle, ""); + setorigin(key, VEHICLE_KEY_OFFSET); + key.scale = VEHICLE_KEY_SCALE; } else { - float of, fragsleft, i, j, thisteam; - of = autocvar_g_balance_keyhunt_score_destroyed_ownfactor; - - FOR_EACH_PLAYER(player) - if(player.team != teem) - ++players; - - FOR_EACH_KH_KEY(key) - if(key.owner && key.team != teem) - ++keys; - - if(lostkey.kh_previous_owner) - kh_Scores_Event(lostkey.kh_previous_owner, world, "destroyed", 0, -autocvar_g_balance_keyhunt_score_destroyed); - // don't actually GIVE him the -nn points, just log - - if(lostkey.kh_previous_owner.playerid == lostkey.kh_previous_owner_playerid) - PlayerScore_Add(lostkey.kh_previous_owner, SP_KH_DESTROYS, 1); - - DistributeEvenly_Init(autocvar_g_balance_keyhunt_score_destroyed, keys * of + players); - - FOR_EACH_KH_KEY(key) - if(key.owner && key.team != teem) - { - f = DistributeEvenly_Get(of); - kh_Scores_Event(key.owner, world, "destroyed_holdingkey", f, 0); - } - - fragsleft = DistributeEvenly_Get(players); - - // Now distribute these among all other teams... - j = kh_teams - 1; - for(i = 0; i < kh_teams; ++i) - { - thisteam = kh_Team_ByID(i); - if(thisteam == teem) // bad boy, no cookie - this WILL happen - continue; - - players = 0; - FOR_EACH_PLAYER(player) - if(player.team == thisteam) - ++players; - - DistributeEvenly_Init(fragsleft, j); - fragsleft = DistributeEvenly_Get(j - 1); - DistributeEvenly_Init(DistributeEvenly_Get(1), players); + setattachment(key, player, ""); + setorigin(key, KEY_CARRY_OFFSET); + } + key.movetype = MOVETYPE_NONE; + key.takedamage = DAMAGE_NO; + key.solid = SOLID_NOT; + key.angles = '0 0 0'; + key.kh_status = KEY_CARRY; - FOR_EACH_PLAYER(player) - if(player.team == thisteam) - { - f = DistributeEvenly_Get(1); - kh_Scores_Event(player, world, "destroyed", f, 0); - } + // messages and sounds + sound(player, CH_TRIGGER, key.snd_key_pass, VOL_BASE, ATTEN_NORM); + kh_EventLog("receive", key.team, player); - --j; - } + FOR_EACH_REALPLAYER(tmp_player) + { + if(tmp_player == sender) + Send_Notification(NOTIF_ONE, tmp_player, MSG_CENTER, APP_TEAM_ENT_4(key, CENTER_KEYHUNT_PASS_SENT_), player.netname); + else if(tmp_player == player) + Send_Notification(NOTIF_ONE, tmp_player, MSG_CENTER, APP_TEAM_ENT_4(key, CENTER_KEYHUNT_PASS_RECEIVED_), sender.netname); + else if(SAME_TEAM(tmp_player, sender)) + Send_Notification(NOTIF_ONE, tmp_player, MSG_CENTER, APP_TEAM_ENT_4(key, CENTER_KEYHUNT_PASS_OTHER_), sender.netname, player.netname); } - Send_Notification(NOTIF_ALL, world, MSG_INFO, APP_TEAM_ENT_4(lostkey, INFO_KEYHUNT_LOST_), lostkey.kh_previous_owner.netname); + // create new waypoint + kh_KeycarrierWaypoints(player); - play2all(kh_sound_destroy); - te_tarexplosion(lostkey.origin); + sender.throw_antispam = time + autocvar_g_keyhunt_pass_wait; + player.throw_antispam = sender.throw_antispam; - kh_FinishRound(); + key.pass_distance = 0; + key.pass_sender = world; + key.pass_target = world; } -void kh_Key_Think() // runs all the time +void kh_Handle_Throw(entity player, entity receiver, entity key, float droptype) { - entity head; - //entity player; // needed by FOR_EACH_PLAYER + vector targ_origin, key_velocity; - if(intermission_running) - return; + if(!key) { return; } + if((droptype == DROP_PASS) && !receiver) { return; } - if(self.owner) - { -#ifndef KH_PLAYER_USE_ATTACHMENT - makevectors('0 1 0' * (self.cnt + (time % 360) * KH_KEY_XYSPEED)); - setorigin(self, v_forward * KH_KEY_XYDIST + '0 0 1' * self.origin.z); -#endif - } + // reset the key + setattachment(key, world, ""); + setorigin(key, player.origin + KEY_DROP_OFFSET); + key.angles_y += key.owner.angles_y; + key.owner = world; + key.solid = SOLID_TRIGGER; + key.kh_droptime = time; + key.kh_dropper = player; - // if in nodrop or time over, end the round - if(!self.owner) - if(time > self.pain_finished) - kh_LoserTeam(self.team, self); + key.flags = FL_ITEM | FL_NOTARGET; // clear FL_ONGROUND for MOVETYPE_TOSS - if(self.owner) - if(kh_Key_AllOwnedByWhichTeam() != -1) + switch(droptype) { - if(self.siren_time < time) + case DROP_PASS: { - sound(self.owner, CH_TRIGGER, kh_sound_alarm, VOL_BASE, ATTEN_NORM); // play a simple alarm - self.siren_time = time + 2.5; // repeat every 2.5 seconds + // warpzone support: + // for the examples, we assume player -> wz1 -> ... -> wzn -> receiver + // findradius has already put wzn ... wz1 into receiver's warpzone parameters! + WarpZone_RefSys_Copy(key, receiver); + WarpZone_RefSys_AddInverse(key, receiver); // wz1^-1 ... wzn^-1 receiver + targ_origin = WarpZone_RefSys_TransformOrigin(receiver, key, (0.5 * (receiver.absmin + receiver.absmax))); // this is target origin as seen by the key + + key.pass_distance = vlen((('1 0 0' * targ_origin_x) + ('0 1 0' * targ_origin_y)) - (('1 0 0' * player.origin_x) + ('0 1 0' * player.origin_y))); // for the sake of this check, exclude Z axis - kh_CalculatePassVelocity(key, targ_origin, player.origin, FALSE); ++ kh_CalculatePassVelocity(key, targ_origin, player.origin, false); + + // main + key.movetype = MOVETYPE_FLY; + key.takedamage = DAMAGE_NO; + key.pass_sender = player; + key.pass_target = receiver; + key.kh_status = KEY_PASSING; + + // other + sound(player, CH_TRIGGER, key.snd_key_touch, VOL_BASE, ATTEN_NORM); + Send_Effect(key.passeffectnum, player.origin, targ_origin, 0); + kh_EventLog("pass", key.team, player); + break; } - entity key; - vector p; - p = self.owner.origin; - FOR_EACH_KH_KEY(key) - if(vlen(key.owner.origin - p) > autocvar_g_balance_keyhunt_maxdist) - goto not_winning; - kh_WinnerTeam(self.team); -:not_winning - } - - if(kh_interferemsg_time && time > kh_interferemsg_time) - { - kh_interferemsg_time = 0; - FOR_EACH_PLAYER(head) + case DROP_THROW: { - if(head.team == kh_interferemsg_team) - if(head.kh_next) - Send_Notification(NOTIF_ONE, head, MSG_CENTER, CENTER_KEYHUNT_MEET); - else - Send_Notification(NOTIF_ONE, head, MSG_CENTER, CENTER_KEYHUNT_HELP); - else - Send_Notification(NOTIF_ONE, head, MSG_CENTER, APP_TEAM_NUM_4(kh_interferemsg_team, CENTER_KEYHUNT_INTERFERE_)); - } - } + makevectors((player.v_angle_y * '0 1 0') + (bound(autocvar_g_keyhunt_throw_angle_min, player.v_angle_x, autocvar_g_keyhunt_throw_angle_max) * '1 0 0')); - self.nextthink = time + 0.05; -} - -void key_reset() -{ - kh_Key_AssignTo(self, world); - kh_Key_Remove(self); -} - -const string STR_ITEM_KH_KEY = "item_kh_key"; -void kh_Key_Spawn(entity initial_owner, float angle, float i) // runs every time a new flag is created, ie after all the keys have been collected -{ - entity key; - key = spawn(); - key.count = i; - key.classname = STR_ITEM_KH_KEY; - key.touch = kh_Key_Touch; - key.think = kh_Key_Think; - key.nextthink = time; - key.items = IT_KEY1 | IT_KEY2; - key.cnt = angle; - key.angles = '0 360 0' * random(); - key.event_damage = kh_Key_Damage; - key.takedamage = DAMAGE_YES; - key.modelindex = kh_key_dropped; - key.model = "key"; - key.kh_dropperteam = 0; - key.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_PLAYERCLIP | DPCONTENTS_BOTCLIP; - setsize(key, KH_KEY_MIN, KH_KEY_MAX); - key.colormod = Team_ColorRGB(initial_owner.team) * KH_KEY_BRIGHTNESS; - key.reset = key_reset; - - switch(initial_owner.team) - { - case NUM_TEAM_1: - key.netname = "^1red key"; + key_velocity = (('0 0 1' * autocvar_g_keyhunt_throw_velocity_up) + ((v_forward * autocvar_g_keyhunt_throw_velocity_forward))); - key.velocity = W_CalculateProjectileVelocity(player.velocity, key_velocity, FALSE); ++ key.velocity = W_CalculateProjectileVelocity(player.velocity, key_velocity, false); + kh_Handle_Drop(key, player, droptype); break; - case NUM_TEAM_2: - key.netname = "^4blue key"; - break; - case NUM_TEAM_3: - key.netname = "^3yellow key"; - break; - case NUM_TEAM_4: - key.netname = "^6pink key"; + } + + case DROP_RESET: + { + key.velocity = '0 0 0'; // do nothing break; + } + default: - key.netname = "NETGIER key"; + case DROP_NORMAL: + { - key.velocity = W_CalculateProjectileVelocity(player.velocity, (('0 0 1' * autocvar_g_keyhunt_drop_velocity_up) + ((('0 1 0' * crandom()) + ('1 0 0' * crandom())) * autocvar_g_keyhunt_drop_velocity_side)), FALSE); ++ key.velocity = W_CalculateProjectileVelocity(player.velocity, (('0 0 1' * autocvar_g_keyhunt_drop_velocity_up) + ((('0 1 0' * crandom()) + ('1 0 0' * crandom())) * autocvar_g_keyhunt_drop_velocity_side)), false); + kh_Handle_Drop(key, player, droptype); break; + } } - // link into key list - key.kh_worldkeynext = kh_worldkeylist; - kh_worldkeylist = key; - - Send_Notification(NOTIF_ONE, initial_owner, MSG_CENTER, APP_TEAM_NUM_4(initial_owner.team, CENTER_KEYHUNT_START_)); - - WaypointSprite_Spawn("key-dropped", 0, 0, key, '0 0 1' * KH_KEY_WP_ZSHIFT, world, key.team, key, waypointsprite_attachedforcarrier, false, RADARICON_FLAG, '0 1 1'); - key.waypointsprite_attachedforcarrier.waypointsprite_visible_for_player = kh_Key_waypointsprite_visible_for_player; - - kh_Key_AssignTo(key, initial_owner); -} + // kill old waypointsprite + if(player.wps_keycarrier) + { + WaypointSprite_Ping(player.wps_keycarrier); + WaypointSprite_Kill(player.wps_keycarrier); + } -// -1 when no team completely owns all keys yet -float kh_Key_AllOwnedByWhichTeam() // constantly called. check to see if all the keys are owned by the same team -{ - entity key; - float teem; - float keys; - - teem = -1; - keys = kh_teams; - FOR_EACH_KH_KEY(key) - { - if(!key.owner) - return -1; - if(teem == -1) - teem = key.team; - else if(teem != key.team) - return -1; - --keys; - } - if(keys != 0) - return -1; - return teem; + if(player.wps_enemykeycarrier) + WaypointSprite_Kill(player.wps_enemykeycarrier); } -void kh_Key_DropOne(entity key) -{ - // prevent collecting this one for some time - entity player; - player = key.owner; - - key.kh_droptime = time; - key.enemy = player; - - kh_Scores_Event(player, key, "dropkey", 0, 0); - PlayerScore_Add(player, SP_KH_LOSSES, 1); - Send_Notification(NOTIF_ALL, world, MSG_INFO, APP_TEAM_ENT_4(key, INFO_KEYHUNT_DROP_), player.netname); - - kh_Key_AssignTo(key, world); - makevectors(player.v_angle); - key.velocity = W_CalculateProjectileVelocity(player.velocity, autocvar_g_balance_keyhunt_throwvelocity * v_forward, false); - key.pusher = world; - key.pushltime = time + autocvar_g_balance_keyhunt_protecttime; - key.kh_dropperteam = key.team; - sound(player, CH_TRIGGER, kh_sound_drop, VOL_BASE, ATTEN_NORM); -} +// ============== +// Event Handlers +// ============== -void kh_Key_DropAll(entity player, float suicide) // runs whenever a player dies +void kh_RemoveKey(entity key); +void kh_Handle_Capture(float capturetype) { entity key; - entity mypusher; - if(player.kh_next) - { - mypusher = world; - if(player.pusher) - if(time < player.pushltime) - mypusher = player.pusher; - while((key = player.kh_next)) + KH_FOR_EACH_KEY(key) + { + entity player = key.owner; + + if(!player) { continue; } // without someone to give the reward to, we can't possibly cap + + nades_GiveBonus(player, autocvar_g_nades_bonus_score_high); + + player.throw_prevtime = time; + player.throw_count = 0; + + Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_KEYHUNT_CAPTURE); + Send_Notification(NOTIF_ALL, world, MSG_INFO, APP_TEAM_ENT_4(key, INFO_KEYHUNT_CAPTURE_), player.netname); + + PlayerTeamScore_AddScore(player, autocvar_g_keyhunt_score_capture); + PlayerScore_Add(player, SP_KH_CAPS, 1); + + Send_Effect(key.capeffectnum, kh_AttachedOrigin(key), '0 0 0', 1); + + if(key.kh_worldkeynext) + te_lightning2(world, kh_AttachedOrigin(key), kh_AttachedOrigin(key.kh_worldkeynext)); + + // kill old waypointsprite + WaypointSprite_Kill(player.wps_keycarrier); + WaypointSprite_Kill(player.wps_enemykeycarrier); + + if(capturetype == CAPTURE_NORMAL) { - kh_Scores_Event(player, key, "losekey", 0, 0); - PlayerScore_Add(player, SP_KH_LOSSES, 1); - Send_Notification(NOTIF_ALL, world, MSG_INFO, APP_TEAM_ENT_4(key, INFO_KEYHUNT_LOST_), player.netname); - kh_Key_AssignTo(key, world); - makevectors('-1 0 0' * (45 + 45 * random()) + '0 360 0' * random()); - key.velocity = W_CalculateProjectileVelocity(player.velocity, autocvar_g_balance_keyhunt_dropvelocity * v_forward, false); - key.pusher = mypusher; - key.pushltime = time + autocvar_g_balance_keyhunt_protecttime; - if(suicide) - key.kh_dropperteam = player.team; + if((key.kh_dropper) && (player != key.kh_dropper)) + { PlayerTeamScore_AddScore(key.kh_dropper, autocvar_g_keyhunt_score_capture_assist); } } - sound(player, CH_TRIGGER, kh_sound_drop, VOL_BASE, ATTEN_NORM); - } -} - -float kh_CheckPlayers(float num) -{ - if(num < kh_teams) - { - float t_team = kh_Team_ByID(num); - float players = 0; - entity tmp_player; - FOR_EACH_PLAYER(tmp_player) - if(tmp_player.deadflag == DEAD_NO) - if(!tmp_player.BUTTON_CHAT) - if(tmp_player.team == t_team) - ++players; - - if (!players) { return t_team; } + + player.next_take_time = time + autocvar_g_keyhunt_key_collect_delay; + kh_RemoveKey(key); // when all is done, it should be safe to kill the key here } - return 0; + + play2all("kh/capture.wav"); } -void kh_WaitForPlayers() // delay start of the round until enough players are present +void kh_Handle_Pickup(entity key, entity player, float pickuptype) { - if(time < game_starttime) - { - kh_Controller_SetThink(game_starttime - time + 0.1, kh_WaitForPlayers); - return; - } + // declarations + float pickup_dropped_score; // used to calculate dropped pickup score - float p1 = kh_CheckPlayers(0), p2 = kh_CheckPlayers(1), p3 = kh_CheckPlayers(2), p4 = kh_CheckPlayers(3); - if (!(p1 || p2 || p3 || p4)) + // attach the key to the player + key.owner = player; + if(player.vehicle) { - Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_KEYHUNT_ROUNDSTART, autocvar_g_balance_keyhunt_delay_round); - kh_Controller_SetThink(autocvar_g_balance_keyhunt_delay_round, kh_StartRound); + setattachment(key, player.vehicle, ""); + //setorigin(key, VEHICLE_KEY_OFFSET); + key.scale = VEHICLE_KEY_SCALE; + setorigin(key, '0 0 1' * KH_VEHICLE_KEY_ZSHIFT); // fixing x, y in think + key.angles_y -= player.angles_y; } else { - Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_KEYHUNT_WAIT, p1, p2, p3, p4); - kh_Controller_SetThink(1, kh_WaitForPlayers); + setattachment(key, player, ""); + //setorigin(key, KEY_CARRY_OFFSET); + setorigin(key, '0 0 1' * KH_KEY_ZSHIFT); // fixing x, y in think + key.angles_y -= player.angles_y; } -} - -void kh_EnableTrackingDevice() // runs after each round -{ - Kill_Notification(NOTIF_ALL, world, MSG_CENTER_CPID, CPID_KEYHUNT); - Kill_Notification(NOTIF_ALL, world, MSG_CENTER_CPID, CPID_KEYHUNT_OTHER); - kh_tracking_enabled = true; -} - -void kh_StartRound() // runs at the start of each round -{ - float i, players, teem; - entity player; - - if(time < game_starttime) + // key setup + key.movetype = MOVETYPE_NONE; + key.takedamage = DAMAGE_NO; + key.solid = SOLID_NOT; + key.angles = '0 0 0'; + key.kh_status = KEY_CARRY; + + //if(pickuptype != PICKUP_KILLED) + if(key.kh_dropper) { - kh_Controller_SetThink(game_starttime - time + 0.1, kh_WaitForPlayers); - return; + entity tmp_entity; + float key_count = 0; + KH_FOR_EACH_KEY(tmp_entity) if(tmp_entity.kh_dropper == key.kh_dropper && tmp_entity != key) { ++key_count; } + if(key_count < 1) // player dropped no other keys + key.kh_dropper.kh_lastkiller = world; // reset when picked up } - float p1 = kh_CheckPlayers(0), p2 = kh_CheckPlayers(1), p3 = kh_CheckPlayers(2), p4 = kh_CheckPlayers(3); - if(p1 || p2 || p3 || p4) + switch(pickuptype) { - kh_Controller_SetThink(1, kh_WaitForPlayers); - Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_KEYHUNT_WAIT, p1, p2, p3, p4); - return; + case PICKUP_KILLED: + case PICKUP_DROPPED: key.health = key.max_key_health; break; // reset health/return timelimit + default: break; } - Kill_Notification(NOTIF_ALL, world, MSG_CENTER_CPID, CPID_KEYHUNT); - Kill_Notification(NOTIF_ALL, world, MSG_CENTER_CPID, CPID_KEYHUNT_OTHER); - - for(i = 0; i < kh_teams; ++i) + // messages and sounds + if(pickuptype == PICKUP_START) { - teem = kh_Team_ByID(i); - players = 0; - entity my_player = world; - FOR_EACH_PLAYER(player) - if(player.deadflag == DEAD_NO) - if(!player.BUTTON_CHAT) - if(player.team == teem) - { - ++players; - if(random() * players <= 1) - my_player = player; - } - kh_Key_Spawn(my_player, 360 * i / kh_teams, i); + Send_Notification(NOTIF_ALL, world, MSG_INFO, APP_TEAM_ENT_4(key, INFO_KEYHUNT_START_), player.netname); + Send_Notification(NOTIF_ONE, player, MSG_CENTER, APP_TEAM_ENT_4(key, CENTER_KEYHUNT_START_)); + Send_Notification(NOTIF_TEAM_EXCEPT, player, MSG_CHOICE, APP_TEAM_ENT_4(key, CHOICE_KEYHUNT_START_TEAM_), Team_ColorCode(player.team), player.netname); } + else + { + Send_Notification(NOTIF_ALL, world, MSG_INFO, APP_TEAM_ENT_4(key, INFO_KEYHUNT_PICKUP_), player.netname); + Send_Notification(NOTIF_ONE, player, MSG_CENTER, APP_TEAM_ENT_4(key, CENTER_KEYHUNT_PICKUP_)); + Send_Notification(NOTIF_TEAM_EXCEPT, player, MSG_CHOICE, APP_TEAM_ENT_4(key, CHOICE_KEYHUNT_PICKUP_TEAM_), Team_ColorCode(player.team), player.netname); - kh_tracking_enabled = false; - Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_KEYHUNT_SCAN, autocvar_g_balance_keyhunt_delay_tracking); - kh_Controller_SetThink(autocvar_g_balance_keyhunt_delay_tracking, kh_EnableTrackingDevice); -} - -float kh_HandleFrags(entity attacker, entity targ, float f) // adds to the player score -{ - if(attacker == targ) - return f; + sound(player, CH_TRIGGER, key.snd_key_taken, VOL_BASE, ATTEN_NONE); + } - if(targ.kh_next) + // scoring + PlayerScore_Add(player, SP_KH_PICKUPS, 1); + nades_GiveBonus(player, autocvar_g_nades_bonus_score_minor); + switch(pickuptype) { - if(attacker.team == targ.team) - { - entity k; - float nk; - nk = 0; - for(k = targ.kh_next; k != world; k = k.kh_next) - ++nk; - kh_Scores_Event(attacker, targ.kh_next, "carrierfrag", -nk * autocvar_g_balance_keyhunt_score_collect, 0); - } - else + case PICKUP_KILLED: + case PICKUP_DROPPED: { - kh_Scores_Event(attacker, targ.kh_next, "carrierfrag", autocvar_g_balance_keyhunt_score_carrierfrag-1, 0); - PlayerScore_Add(attacker, SP_KH_KCKILLS, 1); - // the frag gets added later + pickup_dropped_score = (autocvar_g_keyhunt_key_return_time ? bound(0, ((key.kh_droptime + autocvar_g_keyhunt_key_return_time) - time) / autocvar_g_keyhunt_key_return_time, 1) : 1); + pickup_dropped_score = floor((autocvar_g_keyhunt_score_pickup_dropped_late * (1 - pickup_dropped_score) + autocvar_g_keyhunt_score_pickup_dropped_early * pickup_dropped_score) + 0.5); + dprint("pickup_dropped_score is ", ftos(pickup_dropped_score), "\n"); + PlayerTeamScore_AddScore(player, pickup_dropped_score); + kh_EventLog("pickup", key.team, player); + break; } + + default: break; } - return f; + // effects + Send_Effect(key.toucheffectnum, player.origin, '0 0 0', 1); + + // waypoints + if(pickuptype == PICKUP_DROPPED || pickuptype == PICKUP_KILLED) { WaypointSprite_Kill(key.wps_keydropped); } + kh_KeycarrierWaypoints(player); + WaypointSprite_Ping(player.wps_keycarrier); } -void kh_Initialize() // sets up th KH environment + +// =================== +// Main Key Functions +// =================== + +void kh_CheckKeyReturn(entity key, float returntype) { - precache_sound(kh_sound_capture); - precache_sound(kh_sound_destroy); - precache_sound(kh_sound_drop); - precache_sound(kh_sound_collect); - precache_sound(kh_sound_alarm); // the new siren - -#ifdef KH_PLAYER_USE_CARRIEDMODEL - precache_model("models/keyhunt/key-carried.md3"); -#endif - precache_model("models/keyhunt/key.md3"); + if((key.kh_status == KEY_DROPPED) || (key.kh_status == KEY_PASSING)) + { + if(key.wps_keydropped) { WaypointSprite_UpdateHealth(key.wps_keydropped, key.health); } - // setup variables - kh_teams = autocvar_g_keyhunt_teams_override; - if(kh_teams < 2) - kh_teams = autocvar_g_keyhunt_teams; - kh_teams = bound(2, kh_teams, 4); + if((key.health <= 0) || (time >= key.kh_droptime + autocvar_g_keyhunt_key_return_time)) + { + switch(returntype) + { + case RETURN_DAMAGE: Send_Notification(NOTIF_ALL, world, MSG_INFO, APP_TEAM_ENT_4(key, INFO_KEYHUNT_KEYRETURN_DAMAGED_)); break; + case RETURN_NEEDKILL: + { + entity killer = key.kh_dropper.kh_lastkiller; + if(autocvar_g_keyhunt_key_return_tokiller && IS_PLAYER(killer) && killer.health > 0 && !killer.frozen) + { + kh_Handle_Pickup(key, killer, PICKUP_KILLED); + return; + } + else + { + entity newcarrier = kh_ChooseNewCarrier(key); + if(autocvar_g_keyhunt_key_return_toenemy && newcarrier) + { + kh_Handle_Pickup(key, newcarrier, PICKUP_KILLED); + return; + } + Send_Notification(NOTIF_ALL, world, MSG_INFO, APP_TEAM_ENT_4(key, INFO_KEYHUNT_KEYRETURN_NEEDKILL_)); + break; + } + } - // make a KH entity for controlling the game - kh_controller = spawn(); - kh_controller.think = kh_Controller_Think; - kh_Controller_SetThink(0, kh_WaitForPlayers); - - setmodel(kh_controller, "models/keyhunt/key.md3"); - kh_key_dropped = kh_controller.modelindex; - /* - dprint(vtos(kh_controller.mins)); - dprint(vtos(kh_controller.maxs)); - dprint("\n"); - */ -#ifdef KH_PLAYER_USE_CARRIEDMODEL - setmodel(kh_controller, "models/keyhunt/key-carried.md3"); - kh_key_carried = kh_controller.modelindex; -#else - kh_key_carried = kh_key_dropped; -#endif - - kh_controller.model = ""; - kh_controller.modelindex = 0; - - addstat(STAT_KH_KEYS, AS_INT, kh_state); + default: + case RETURN_TIMEOUT: + { Send_Notification(NOTIF_ALL, world, MSG_INFO, APP_TEAM_ENT_4(key, INFO_KEYHUNT_KEYRETURN_TIMEOUT_)); break; } + } + sound(key, CH_TRIGGER, key.snd_key_respawn, VOL_BASE, ATTEN_NONE); + kh_EventLog("returned", key.team, world); + kh_RemoveKey(key); + } + } +} - kh_ScoreRules(kh_teams); +void kh_KeyDamage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force) +{ + if(ITEM_DAMAGE_NEEDKILL(deathtype)) + { + self.health = 0; + kh_CheckKeyReturn(self, RETURN_NEEDKILL); + return; + } + if(autocvar_g_keyhunt_key_return_damage) + { + // reduce health and check if it should be returned + self.health = self.health - damage; + kh_CheckKeyReturn(self, RETURN_DAMAGE); + return; + } } -void kh_finalize() +void kh_KeyThink() { - // to be called before intermission - kh_FinishRound(); - remove(kh_controller); - kh_controller = world; + self.nextthink = time + KEY_THINKRATE; // only 5 fps, more is unnecessary. + + // sanity checks + if(self.mins != KEY_MIN || self.maxs != KEY_MAX) { // reset the key boundaries in case it got squished + dprint("wtf the key got squashed?\n"); + tracebox(self.origin, KEY_MIN, KEY_MAX, self.origin, MOVE_NOMONSTERS, self); + if(!trace_startsolid) // can we resize it without getting stuck? + setsize(self, KEY_MIN, KEY_MAX); } + + switch(self.kh_status) // reset key angles in case warpzones adjust it + { + case KEY_DROPPED: + { + self.angles = '0 0 0'; + break; + } + + default: break; + } + + // main think method + switch(self.kh_status) + { + case KEY_DROPPED: + { + if(autocvar_g_keyhunt_key_dropped_floatinwater) + { + vector midpoint = ((self.absmin + self.absmax) * 0.5); + if(pointcontents(midpoint) == CONTENT_WATER) + { + self.velocity = self.velocity * 0.5; + + if(pointcontents(midpoint + KEY_FLOAT_OFFSET) == CONTENT_WATER) + { self.velocity_z = autocvar_g_keyhunt_key_dropped_floatinwater; } + else + { self.movetype = MOVETYPE_FLY; } + } + else if(self.movetype == MOVETYPE_FLY) { self.movetype = MOVETYPE_TOSS; } + } + if(autocvar_g_keyhunt_key_return_time) + { + self.health -= ((self.max_key_health / autocvar_g_keyhunt_key_return_time) * KEY_THINKRATE); + kh_CheckKeyReturn(self, RETURN_TIMEOUT); + return; + } + return; + } + + case KEY_CARRY: + { + makevectors('0 1 0' * (self.cnt + (time % 360) * KH_KEY_XYSPEED)); + setorigin(self, v_forward * ((self.owner.vehicle) ? KH_VEHICLE_KEY_XYDIST : KH_KEY_XYDIST) + '0 0 1' * self.origin_z); + return; + } + + case KEY_PASSING: + { + vector targ_origin = ((self.pass_target.absmin + self.pass_target.absmax) * 0.5); + targ_origin = WarpZone_RefSys_TransformOrigin(self.pass_target, self, targ_origin); // origin of target as seen by the key (us) + WarpZone_TraceLine(self.origin, targ_origin, MOVE_NOMONSTERS, self); + + if((self.pass_target == world) + || (self.pass_target.deadflag != DEAD_NO) + || (vlen(self.origin - targ_origin) > autocvar_g_keyhunt_pass_radius) + || ((trace_fraction < 1) && (trace_ent != self.pass_target)) + || (time > self.kh_droptime + autocvar_g_keyhunt_pass_timelimit)) + { + // give up, pass failed + kh_Handle_Drop(self, world, DROP_PASS); + } + else + { + // still a viable target, go for it - kh_CalculatePassVelocity(self, targ_origin, self.origin, TRUE); ++ kh_CalculatePassVelocity(self, targ_origin, self.origin, true); + } + return; + } + + default: // this should never happen + { + dprint("kh_KeyThink(): Key exists with no status?\n"); + return; + } + } } -// register this as a mutator +float kh_Customize() +{ + entity e = WaypointSprite_getviewentity(other); -MUTATOR_HOOKFUNCTION(kh_Key_DropAll) + if(self.owner == e) + self.glow_trail = 0; + else if(autocvar_g_keyhunt_key_glowtrails) + self.glow_trail = 1; + - return TRUE; ++ return true; +} + +void kh_KeyTouch() { - kh_Key_DropAll(self, true); - return 0; + if(gameover) { return; } + + entity toucher = other; + + // automatically kill the key and return it if it touched lava/slime/nodrop surfaces + if(ITEM_TOUCH_NEEDKILL()) + { + self.health = 0; + kh_CheckKeyReturn(self, RETURN_NEEDKILL); + return; + } + + // special touch behaviors + if(toucher.frozen) { return; } + else if(IS_VEHICLE(toucher)) + { + if(autocvar_g_keyhunt_allow_vehicle_touch && toucher.owner) + toucher = toucher.owner; // the player is actually the vehicle owner, not other + else + return; // do nothing + } + else if (!IS_PLAYER(toucher)) // The key just touched an object, most likely the world + { + if(time > self.wait) // if we haven't in a while, play a sound/effect + { + Send_Effect(self.toucheffectnum, self.origin, '0 0 0', 1); + sound(self, CH_TRIGGER, self.snd_key_touch, VOL_BASE, ATTEN_NORM); + self.wait = time + KEY_TOUCHRATE; + } + return; + } + else if(toucher.deadflag != DEAD_NO) { return; } + + switch(self.kh_status) + { + case KEY_DROPPED: + { + if(((toucher != self.kh_dropper) || (time > self.kh_droptime + autocvar_g_keyhunt_key_collect_delay))) + kh_Handle_Pickup(self, toucher, PICKUP_DROPPED); // toucher just picked up a dropped enemy key + break; + } + + case KEY_CARRY: + { + dprint("Someone touched a key even though it was being carried?\n"); + break; + } + + case KEY_PASSING: + { + if((IS_PLAYER(toucher)) && (toucher.deadflag == DEAD_NO) && (toucher != self.pass_sender)) + kh_Handle_Retrieve(self, toucher); + break; + } + } +} + +void kh_RemoveKey(entity key) +{ + // kill old waypointsprite + WaypointSprite_Kill(key.owner.wps_keycarrier); + WaypointSprite_Kill(key.owner.wps_enemykeycarrier); + + if(key.kh_status == KEY_DROPPED) + { WaypointSprite_Kill(key.wps_keydropped); } + + // reset the key + setattachment(key, world, ""); + + remove(key); +} + +void kh_Reset() +{ + if(self.owner) + if(IS_PLAYER(self.owner)) + kh_Handle_Throw(self.owner, world, self, DROP_RESET); + + kh_RemoveKey(self); +} + +void kh_KeySetup(float teamnumber, entity key, entity player) // called when spawning a key entity +{ + // declarations + self = key; // for later usage with droptofloor() + + // main setup + key.kh_worldkeynext = kh_worldkeylist; // link key into kh_worldkeylist + kh_worldkeylist = key; + + setattachment(key, world, ""); + + key.netname = sprintf("%s%s^7 key", Team_ColorCode(teamnumber), Team_ColorName_Upper(teamnumber)); + key.team = teamnumber; + key.classname = "item_key_team"; + key.target = "###item###"; // wut? + key.flags = FL_ITEM | FL_NOTARGET; + key.solid = SOLID_TRIGGER; + key.takedamage = DAMAGE_NO; + key.customizeentityforclient = kh_Customize; + key.damageforcescale = autocvar_g_keyhunt_key_damageforcescale; + key.max_key_health = ((autocvar_g_keyhunt_key_return_damage && autocvar_g_keyhunt_key_health) ? autocvar_g_keyhunt_key_health : 100); + key.health = key.max_key_health; + key.event_damage = kh_KeyDamage; - key.pushable = TRUE; ++ key.pushable = true; + key.teleportable = TELEPORT_NORMAL; + key.damagedbytriggers = autocvar_g_keyhunt_key_return_when_unreachable; + key.damagedbycontents = autocvar_g_keyhunt_key_return_when_unreachable; + key.velocity = '0 0 0'; + key.mangle = key.angles; + key.cnt = 360 * Team_TeamToNumber(teamnumber) / kh_teams; + key.reset = kh_Reset; + key.touch = kh_KeyTouch; + key.think = kh_KeyThink; + key.nextthink = time + KEY_THINKRATE; + key.kh_status = KEY_CARRY; + key.colormod = Team_ColorRGB(teamnumber) * KEY_BRIGHTNESS; + + // appearence + key.model = "models/keyhunt/key.md3"; + key.scale = KEY_SCALE; + key.toucheffectnum = ((teamnumber == NUM_TEAM_1) ? EFFECT_FLAG_RED_TOUCH : ((teamnumber == NUM_TEAM_2) ? EFFECT_FLAG_BLUE_TOUCH : ((teamnumber == NUM_TEAM_3) ? EFFECT_FLAG_YELLOW_TOUCH : EFFECT_FLAG_PINK_TOUCH))); + key.passeffectnum = ((teamnumber == NUM_TEAM_1) ? EFFECT_RED_PASS : ((teamnumber == NUM_TEAM_2) ? EFFECT_BLUE_PASS : ((teamnumber == NUM_TEAM_3) ? EFFECT_YELLOW_PASS : EFFECT_PINK_PASS))); + key.capeffectnum = ((teamnumber == NUM_TEAM_1) ? EFFECT_RED_CAP : ((teamnumber == NUM_TEAM_2) ? EFFECT_BLUE_CAP : ((teamnumber == NUM_TEAM_3) ? EFFECT_YELLOW_CAP : EFFECT_PINK_CAP))); + + // sound + key.snd_key_taken = "kh/collect.wav"; + key.snd_key_capture = "kh/capture.wav"; + key.snd_key_dropped = "kh/drop.wav"; + key.snd_key_touch = "ctf/touch.wav"; + key.snd_key_pass = "ctf/pass.wav"; + + // appearence + setmodel(key, key.model); // precision set below + setsize(key, KEY_MIN, KEY_MAX); + setorigin(key, (key.origin + KEY_SPAWN_OFFSET)); + + if(autocvar_g_keyhunt_key_glowtrails) + { + key.glow_color = ((teamnumber == NUM_TEAM_1) ? 251 : ((teamnumber == NUM_TEAM_2) ? 210 : ((teamnumber == NUM_TEAM_3) ? 110 : 145))); + key.glow_size = 25; + key.glow_trail = 1; + } + + key.effects |= EF_LOWPRECISION; + if(autocvar_g_keyhunt_fullbrightkeys) { key.effects |= EF_FULLBRIGHT; } + if(autocvar_g_keyhunt_dynamiclights) + { + switch(teamnumber) + { + case NUM_TEAM_1: key.effects |= EF_RED; break; + case NUM_TEAM_2: key.effects |= EF_BLUE; break; + case NUM_TEAM_3: key.effects |= EF_DIMLIGHT; break; + case NUM_TEAM_4: key.effects |= EF_RED; break; + } + } + + kh_Handle_Pickup(key, player, PICKUP_START); +} + + +// ================== +// Legacy Bot Logic +// ================== + +void() havocbot_role_kh_carrier; +void() havocbot_role_kh_defense; +void() havocbot_role_kh_offense; +void() havocbot_role_kh_freelancer; +void havocbot_goalrating_kh(float ratingscale_team, float ratingscale_dropped, float ratingscale_enemy) +{ + entity head; + for (head = kh_worldkeylist; head && !wasfreed(head); head = head.kh_worldkeynext) + { + if(head.owner == self) + continue; + if(!head.owner) + navigation_routerating(head, ratingscale_dropped * BOT_PICKUP_RATING_HIGH, 100000); + else if(head.team == self.team) + navigation_routerating(head.owner, ratingscale_team * BOT_PICKUP_RATING_HIGH, 100000); + else + navigation_routerating(head.owner, ratingscale_enemy * BOT_PICKUP_RATING_HIGH, 100000); + } + + havocbot_goalrating_items(1, self.origin, 10000); +} + +void havocbot_role_kh_carrier() +{ + if(self.deadflag != DEAD_NO) + return; + + entity key; - float is_carrier = FALSE; ++ float is_carrier = false; + KH_FOR_EACH_KEY(key) + if(key.owner == self) + { - is_carrier = TRUE; ++ is_carrier = true; + break; + } + + if (!is_carrier) + { + dprint("changing role to freelancer\n"); + self.havocbot_role = havocbot_role_kh_freelancer; + self.havocbot_role_timeout = 0; + return; + } + + if (self.bot_strategytime < time) + { + self.bot_strategytime = time + autocvar_bot_ai_strategyinterval; + navigation_goalrating_start(); + + float key_count = 0; + KH_FOR_EACH_KEY(key) { if(!wasfreed(key) && SAME_TEAM(key, self)) ++key_count; } + + if(key_count >= kh_teams) + havocbot_goalrating_kh(10, 0.1, 0.1); // bring home + else + havocbot_goalrating_kh(4, 4, 1); // play defensively + + navigation_goalrating_end(); + } +} + +void havocbot_role_kh_defense() +{ + if(self.deadflag != DEAD_NO) + return; + + entity key; - float is_carrier = FALSE; ++ float is_carrier = false; + KH_FOR_EACH_KEY(key) + if(key.owner == self) + { - is_carrier = TRUE; ++ is_carrier = true; + break; + } + + if (is_carrier) + { + dprint("changing role to carrier\n"); + self.havocbot_role = havocbot_role_kh_carrier; + self.havocbot_role_timeout = 0; + return; + } + + if (!self.havocbot_role_timeout) + self.havocbot_role_timeout = time + random() * 10 + 20; + if (time > self.havocbot_role_timeout) + { + dprint("changing role to freelancer\n"); + self.havocbot_role = havocbot_role_kh_freelancer; + self.havocbot_role_timeout = 0; + return; + } + + if (self.bot_strategytime < time) + { + self.bot_strategytime = time + autocvar_bot_ai_strategyinterval; + navigation_goalrating_start(); + + float key_count = 0; + KH_FOR_EACH_KEY(key) { if(!wasfreed(key) && SAME_TEAM(key, self)) ++key_count; } + + if(key_count >= kh_teams) + havocbot_goalrating_kh(10, 0.1, 0.1); // defend key carriers + else if(key_count == 0) + havocbot_goalrating_kh(4, 1, 0.1); // play defensively + else + havocbot_goalrating_kh(0.1, 0.1, 10); // ATTACK ANYWAY + + navigation_goalrating_end(); + } +} + +void havocbot_role_kh_offense() +{ + if(self.deadflag != DEAD_NO) + return; + + entity key; - float is_carrier = FALSE; ++ float is_carrier = false; + KH_FOR_EACH_KEY(key) + if(key.owner == self) + { - is_carrier = TRUE; ++ is_carrier = true; + break; + } + + if (is_carrier) + { + dprint("changing role to carrier\n"); + self.havocbot_role = havocbot_role_kh_carrier; + self.havocbot_role_timeout = 0; + return; + } + + if (!self.havocbot_role_timeout) + self.havocbot_role_timeout = time + random() * 10 + 20; + if (time > self.havocbot_role_timeout) + { + dprint("changing role to freelancer\n"); + self.havocbot_role = havocbot_role_kh_freelancer; + self.havocbot_role_timeout = 0; + return; + } + + if (self.bot_strategytime < time) + { + self.bot_strategytime = time + autocvar_bot_ai_strategyinterval; + navigation_goalrating_start(); + + float key_count = 0; + KH_FOR_EACH_KEY(key) { if(!wasfreed(key) && SAME_TEAM(key, self)) ++key_count; } + + if(key_count >= kh_teams) + havocbot_goalrating_kh(10, 0.1, 0.1); // defend key carriers + else if(key_count == 0) + havocbot_goalrating_kh(4, 1, 0.1); // play defensively + else + havocbot_goalrating_kh(0.1, 0.1, 10); // ATTACK ANYWAY + + navigation_goalrating_end(); + } +} + +void havocbot_role_kh_freelancer() +{ + if(self.deadflag != DEAD_NO) + return; + + entity key; - float is_carrier = FALSE; ++ float is_carrier = false; + KH_FOR_EACH_KEY(key) + if(key.owner == self) + { - is_carrier = TRUE; ++ is_carrier = true; + break; + } + + if (is_carrier) + { + dprint("changing role to carrier\n"); + self.havocbot_role = havocbot_role_kh_carrier; + self.havocbot_role_timeout = 0; + return; + } + + if (!self.havocbot_role_timeout) + self.havocbot_role_timeout = time + random() * 10 + 10; + if (time > self.havocbot_role_timeout) + { + if (random() < 0.5) + { + dprint("changing role to offense\n"); + self.havocbot_role = havocbot_role_kh_offense; + } + else + { + dprint("changing role to defense\n"); + self.havocbot_role = havocbot_role_kh_defense; + } + self.havocbot_role_timeout = 0; + return; + } + + if (self.bot_strategytime < time) + { + self.bot_strategytime = time + autocvar_bot_ai_strategyinterval; + navigation_goalrating_start(); + + float key_count = 0; + KH_FOR_EACH_KEY(key) { if(!wasfreed(key) && SAME_TEAM(key, self)) ++key_count; } + + if(key_count >= kh_teams) + havocbot_goalrating_kh(10, 0.1, 0.1); // defend key carriers + else if(key_count == 0) + havocbot_goalrating_kh(4, 1, 0.1); // play defensively + else + havocbot_goalrating_kh(0.1, 0.1, 10); // ATTACK ANYWAY + + navigation_goalrating_end(); + } +} + + +// ============== +// Hook Functions +// ============== + +MUTATOR_HOOKFUNCTION(kh_PlayerPreThink) +{ + entity key; + float s; + float f; + + s = 0; + KH_FOR_EACH_KEY(key) + { + if(key.owner) + f = key.team; + else + f = 30; + s |= pow(32, Team_TeamToNumber(key.team) - 1) * f; + } + + self.kh_keystatus = s; + + KH_FOR_EACH_KEY(key) + { + if(key.owner == self) + key.owner.kh_keystatus |= pow(32, Team_TeamToNumber(key.team) - 1) * 31; + } + + // update the health of the key carrier waypointsprite + if(self.wps_keycarrier) + WaypointSprite_UpdateHealth(self.wps_keycarrier, '1 0 0' * healtharmor_maxdamage(self.health, self.armorvalue, autocvar_g_balance_armor_blockpercent, DEATH_WEAPON)); + - return FALSE; ++ return false; +} + +MUTATOR_HOOKFUNCTION(kh_PlayerDamage) // for changing damage and force values that are applied to players in g_damage.qc +{ + entity key; - float targ_iscarrier = FALSE, attacker_iscarrier = FALSE; ++ float targ_iscarrier = false, attacker_iscarrier = false; + KH_FOR_EACH_KEY(key) + { - if(key.owner == frag_attacker) { attacker_iscarrier = TRUE; } - if(key.owner == frag_target) { targ_iscarrier = TRUE; } ++ if(key.owner == frag_attacker) { attacker_iscarrier = true; } ++ if(key.owner == frag_target) { targ_iscarrier = true; } + } + + if(attacker_iscarrier) // if the attacker is a keycarrier + { + if(frag_target == frag_attacker) // damage done to yourself + { + frag_damage *= autocvar_g_keyhunt_keycarrier_selfdamagefactor; + frag_force *= autocvar_g_keyhunt_keycarrier_selfforcefactor; + } + else // damage done to everyone else + { + frag_damage *= autocvar_g_keyhunt_keycarrier_damagefactor; + frag_force *= autocvar_g_keyhunt_keycarrier_forcefactor; + } + } + else if(targ_iscarrier && (frag_target.deadflag == DEAD_NO) && DIFF_TEAM(frag_target, frag_attacker)) // if the target is a keycarrier + { + if(autocvar_g_keyhunt_keycarrier_auto_helpme_damage > ('1 0 0' * healtharmor_maxdamage(frag_target.health, frag_target.armorvalue, autocvar_g_balance_armor_blockpercent, DEATH_WEAPON))) + if(time > frag_target.wps_helpme_time + autocvar_g_keyhunt_keycarrier_auto_helpme_time) + { + frag_target.wps_helpme_time = time; + WaypointSprite_HelpMePing(frag_target.wps_keycarrier); + } + // todo: add notification for when key carrier needs help? + } - return FALSE; ++ return false; } MUTATOR_HOOKFUNCTION(kh_PlayerDies) { - if(self == other) - kh_Key_DropAll(self, true); - else if(IS_PLAYER(other)) - kh_Key_DropAll(self, false); - else - kh_Key_DropAll(self, true); - return 0; + entity tmp_entity; - float targ_iscarrier = FALSE; ++ float targ_iscarrier = false; + - KH_FOR_EACH_KEY(tmp_entity) if(tmp_entity.owner == frag_target) { targ_iscarrier = TRUE; break; } ++ KH_FOR_EACH_KEY(tmp_entity) if(tmp_entity.owner == frag_target) { targ_iscarrier = true; break; } + + if((frag_attacker != frag_target) && (IS_PLAYER(frag_attacker)) && targ_iscarrier) + { + PlayerTeamScore_AddScore(frag_attacker, autocvar_g_keyhunt_score_kill); + PlayerScore_Add(frag_attacker, SP_KH_KCKILLS, 1); + } + + KH_FOR_EACH_KEY(tmp_entity) if(tmp_entity.owner == frag_target) { kh_Handle_Throw(frag_target, world, tmp_entity, DROP_NORMAL); } + + if(targ_iscarrier) + if(IS_PLAYER(frag_attacker)) + if(frag_attacker != frag_target) + frag_target.kh_lastkiller = frag_attacker; + + FOR_EACH_PLAYER(tmp_entity) if(tmp_entity.kh_lastkiller == frag_target) { tmp_entity.kh_lastkiller = frag_attacker; } + - return FALSE; ++ return false; } MUTATOR_HOOKFUNCTION(kh_GiveFragsForKill) { - frag_score = kh_HandleFrags(frag_attacker, frag_target, frag_score); - return 0; + frag_score = 0; + return (autocvar_g_keyhunt_ignore_frags); // no frags counted in keyhunt if this is true } -MUTATOR_HOOKFUNCTION(kh_finalize) +MUTATOR_HOOKFUNCTION(kh_RemovePlayer) { - kh_finalize(); - return 0; + entity key; // temporary entity for the search method + + KH_FOR_EACH_KEY(key) if(key.owner == self) { kh_Handle_Throw(self, world, key, DROP_NORMAL); } + + KH_FOR_EACH_KEY(key) // handle this separately, as the above may reset them + { + if(key.pass_sender == self) { key.pass_sender = world; } + if(key.pass_target == self) { key.pass_target = world; } + if(key.kh_dropper == self) { key.kh_dropper = world; } + } + - return FALSE; ++ return false; } -MUTATOR_HOOKFUNCTION(kh_GetTeamCount) +MUTATOR_HOOKFUNCTION(kh_PortalTeleport) { - ret_float = kh_teams; - return 0; + entity key; + + if(!autocvar_g_keyhunt_portalteleport) + KH_FOR_EACH_KEY(key) + if(key.owner == self) { kh_Handle_Throw(self, world, key, DROP_NORMAL); } + - return FALSE; ++ return false; } -MUTATOR_HOOKFUNCTION(kh_SpectateCopy) +MUTATOR_HOOKFUNCTION(kh_PlayerUseKey) { - if(MUTATOR_RETURNVALUE || gameover) { return FALSE; } - self.kh_state = other.kh_state; - return 0; ++ if(MUTATOR_RETURNVALUE || gameover) { return false; } + + entity player = self, key, player_key = world; + + KH_FOR_EACH_KEY(key) + if(key.owner == player) { player_key = key; } + + if((time > player.throw_antispam) && (player.deadflag == DEAD_NO) && (!player.vehicle || autocvar_g_keyhunt_allow_vehicle_touch)) + { + // pass the key to a team mate + if(autocvar_g_keyhunt_pass) + { + entity head, closest_target = world; - head = WarpZone_FindRadius(player.origin, autocvar_g_keyhunt_pass_radius, TRUE); ++ head = WarpZone_FindRadius(player.origin, autocvar_g_keyhunt_pass_radius, true); + + while(head) // find the closest acceptable target to pass to + { + if(IS_PLAYER(head) && head.deadflag == DEAD_NO) + if(head != player && SAME_TEAM(head, player)) + if(!head.vehicle) + { + // if it's a player, use the view origin as reference (stolen from RadiusDamage functions in g_damage.qc) + vector head_center = WarpZone_UnTransformOrigin(head, CENTER_OR_VIEWOFS(head)); + vector passer_center = CENTER_OR_VIEWOFS(player); + entity head_key = world; + + KH_FOR_EACH_KEY(key) + if(key.owner == head) { head_key = key; break; } + + if(kh_CheckPassDirection(head_center, passer_center, player.v_angle, head.WarpZone_findradius_nearest)) + { + if(autocvar_g_keyhunt_pass_request && !player_key && head_key) + { + if(IS_BOT_CLIENT(head)) + { + Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_KEYHUNT_PASS_REQUESTING, head.netname); + kh_Handle_Throw(head, player, head_key, DROP_PASS); + } + else + { + Send_Notification(NOTIF_ONE, head, MSG_CENTER, CENTER_KEYHUNT_PASS_REQUESTED, player.netname); + Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_KEYHUNT_PASS_REQUESTING, head.netname); + } + player.throw_antispam = time + autocvar_g_keyhunt_pass_wait; - return TRUE; ++ return true; + } + else if(player_key) + { + if(closest_target) + { + vector closest_target_center = WarpZone_UnTransformOrigin(closest_target, CENTER_OR_VIEWOFS(closest_target)); + if(vlen(passer_center - head_center) < vlen(passer_center - closest_target_center)) + { closest_target = head; } + } + else { closest_target = head; } + } + } + } + head = head.chain; + } + - if(closest_target) { kh_Handle_Throw(player, closest_target, player_key, DROP_PASS); return TRUE; } ++ if(closest_target) { kh_Handle_Throw(player, closest_target, player_key, DROP_PASS); return true; } + } + + // throw the key in front of you + if(autocvar_g_keyhunt_throw && player_key) + { + if(player.throw_count == -1) + { + if(time > player.throw_prevtime + autocvar_g_keyhunt_throw_punish_delay) + { + player.throw_prevtime = time; + player.throw_count = 1; + kh_Handle_Throw(player, world, player_key, DROP_THROW); - return TRUE; ++ return true; + } + else + { + Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_KEYHUNT_KEY_THROW_PUNISH, rint((player.throw_prevtime + autocvar_g_keyhunt_throw_punish_delay) - time)); - return FALSE; ++ return false; + } + } + else + { + if(time > player.throw_prevtime + autocvar_g_keyhunt_throw_punish_time) { player.throw_count = 1; } + else { player.throw_count += 1; } + if(player.throw_count >= autocvar_g_keyhunt_throw_punish_count) { player.throw_count = -1; } + + player.throw_prevtime = time; + kh_Handle_Throw(player, world, player_key, DROP_THROW); - return TRUE; ++ return true; + } + } + } + - return FALSE; ++ return false; } -MUTATOR_HOOKFUNCTION(kh_PlayerUseKey) +MUTATOR_HOOKFUNCTION(kh_ResetMapGlobal) { - if(MUTATOR_RETURNVALUE == 0) + entity e, key; + FOR_EACH_CLIENT(e) { - entity k; - k = self.kh_next; - if(k) + e.kh_lastkiller = world; + } + KH_FOR_EACH_KEY(key) { if(!wasfreed(key)) kh_RemoveKey(key); } + kh_worldkeylist = world; // reset key list - return FALSE; ++ return false; +} + +MUTATOR_HOOKFUNCTION(kh_ResetMap) +{ + // don't reset players - return TRUE; ++ return true; +} + +MUTATOR_HOOKFUNCTION(kh_HelpMePing) +{ + if(self.wps_keycarrier) // update the keycarrier waypointsprite with "NEEDING HELP" notification + { + self.wps_helpme_time = time; + WaypointSprite_HelpMePing(self.wps_keycarrier); + } + else // create a normal help me waypointsprite + { - WaypointSprite_Spawn("helpme", waypointsprite_deployed_lifetime, waypointsprite_limitedrange, self, KEY_WAYPOINT_OFFSET, world, self.team, self, wps_helpme, FALSE, RADARICON_HELPME, '1 0.5 0'); ++ WaypointSprite_Spawn("helpme", waypointsprite_deployed_lifetime, waypointsprite_limitedrange, self, KEY_WAYPOINT_OFFSET, world, self.team, self, wps_helpme, false, RADARICON_HELPME, '1 0.5 0'); + WaypointSprite_Ping(self.wps_helpme); + } + - return TRUE; ++ return true; +} + +MUTATOR_HOOKFUNCTION(kh_VehicleEnter) +{ + entity key; + if(!autocvar_g_keyhunt_allow_vehicle_carry && !autocvar_g_keyhunt_allow_vehicle_touch) + { + KH_FOR_EACH_KEY(key) + if(key.owner == vh_player) { kh_Handle_Throw(vh_player, world, key, DROP_NORMAL); } + } + else + { + KH_FOR_EACH_KEY(key) + if(key.owner == vh_player) { - kh_Key_DropOne(k); - return 1; + setattachment(key, vh_vehicle, ""); + setorigin(key, VEHICLE_KEY_OFFSET); + key.scale = VEHICLE_KEY_SCALE; + //key.angles = '0 0 0'; } } - return 0; + - return FALSE; ++ return false; +} + +MUTATOR_HOOKFUNCTION(kh_VehicleExit) +{ + entity key; + KH_FOR_EACH_KEY(key) + if(key.owner == vh_player) + { + setattachment(key, vh_player, ""); + setorigin(key, KEY_CARRY_OFFSET); + key.scale = KEY_SCALE; + key.angles = '0 0 0'; + } + - return FALSE; ++ return false; } +MUTATOR_HOOKFUNCTION(kh_MatchEnd) +{ + entity key; // temporary entity for the search method + + KH_FOR_EACH_KEY(key) + { + switch(key.kh_status) + { + case KEY_DROPPED: + case KEY_PASSING: + { + // lock the key, game is over + key.movetype = MOVETYPE_NONE; + key.takedamage = DAMAGE_NO; + key.solid = SOLID_NOT; - key.nextthink = FALSE; // stop thinking ++ key.nextthink = false; // stop thinking + + //dprint("stopping the ", key.netname, " from moving.\n"); + break; + } + + default: + case KEY_CARRY: + { + // do nothing for these keys + break; + } + } + } + - return FALSE; ++ return false; +} + +MUTATOR_HOOKFUNCTION(kh_BotRoles) +{ + float r = random() * 3; + if (r < 1) + self.havocbot_role = havocbot_role_kh_offense; + else if (r < 2) + self.havocbot_role = havocbot_role_kh_defense; + else + self.havocbot_role = havocbot_role_kh_freelancer; - return TRUE; ++ return true; +} + +MUTATOR_HOOKFUNCTION(kh_GetTeamCount) +{ + ret_float = kh_teams; - return FALSE; ++ return false; +} + + +// ============== +// Initialization +// ============== + +// scoreboard setup +void kh_ScoreRules(float teams) +{ + CheckAllowedTeams(world); - ScoreRules_basics(teams, SFL_SORT_PRIO_PRIMARY, 0, TRUE); ++ ScoreRules_basics(teams, SFL_SORT_PRIO_PRIMARY, 0, true); + ScoreInfo_SetLabel_TeamScore (ST_KH_CAPS, "caps", SFL_SORT_PRIO_PRIMARY); + ScoreInfo_SetLabel_PlayerScore(SP_KH_CAPS, "caps", SFL_SORT_PRIO_SECONDARY); + ScoreInfo_SetLabel_PlayerScore(SP_KH_CAPTIME, "captime", SFL_LOWER_IS_BETTER | SFL_TIME); + ScoreInfo_SetLabel_PlayerScore(SP_KH_PICKUPS, "pickups", 0); + ScoreInfo_SetLabel_PlayerScore(SP_KH_KCKILLS, "kckills", 0); + ScoreInfo_SetLabel_PlayerScore(SP_KH_DROPS, "drops", SFL_LOWER_IS_BETTER); + ScoreRules_basics_end(); +} + +void kh_DelayedInit() // Do this check with a delay so we can wait for teams to be set up. +{ + kh_teams = autocvar_g_keyhunt_teams_override; + if(kh_teams < 2 || kh_teams > 4) + kh_teams = autocvar_g_keyhunt_teams; + kh_teams = bound(2, kh_teams, 4); + + kh_ScoreRules(kh_teams); + + round_handler_Spawn(KH_CheckTeams, KH_CheckWinner, KH_RoundStart); + round_handler_Init(5, autocvar_g_keyhunt_warmup, autocvar_g_keyhunt_round_timelimit); +} + +void kh_Initialize() +{ + precache_model("models/keyhunt/key.md3"); + + precache_sound("kh/collect.wav"); + precache_sound("kh/capture.wav"); + precache_sound("kh/drop.wav"); + precache_sound("ctf/touch.wav"); + precache_sound("ctf/pass.wav"); + + addstat(STAT_KH_KEYSTATUS, AS_INT, kh_keystatus); + + InitializeEntity(world, kh_DelayedInit, INITPRIO_GAMETYPE); +} + + MUTATOR_DEFINITION(gamemode_keyhunt) { - MUTATOR_HOOK(MakePlayerObserver, kh_Key_DropAll, CBC_ORDER_ANY); - MUTATOR_HOOK(ClientDisconnect, kh_Key_DropAll, CBC_ORDER_ANY); + MUTATOR_HOOK(MakePlayerObserver, kh_RemovePlayer, CBC_ORDER_ANY); + MUTATOR_HOOK(ClientDisconnect, kh_RemovePlayer, CBC_ORDER_ANY); MUTATOR_HOOK(PlayerDies, kh_PlayerDies, CBC_ORDER_ANY); - MUTATOR_HOOK(GiveFragsForKill, kh_GiveFragsForKill, CBC_ORDER_FIRST); - MUTATOR_HOOK(MatchEnd, kh_finalize, CBC_ORDER_ANY); - MUTATOR_HOOK(GetTeamCount, kh_GetTeamCount, CBC_ORDER_EXCLUSIVE); - MUTATOR_HOOK(SpectateCopy, kh_SpectateCopy, CBC_ORDER_ANY); + MUTATOR_HOOK(MatchEnd, kh_MatchEnd, CBC_ORDER_ANY); + MUTATOR_HOOK(PortalTeleport, kh_PortalTeleport, CBC_ORDER_ANY); + MUTATOR_HOOK(GiveFragsForKill, kh_GiveFragsForKill, CBC_ORDER_ANY); + MUTATOR_HOOK(PlayerPreThink, kh_PlayerPreThink, CBC_ORDER_ANY); + MUTATOR_HOOK(PlayerDamage_Calculate, kh_PlayerDamage, CBC_ORDER_ANY); MUTATOR_HOOK(PlayerUseKey, kh_PlayerUseKey, CBC_ORDER_ANY); + MUTATOR_HOOK(reset_map_global, kh_ResetMapGlobal, CBC_ORDER_ANY); + MUTATOR_HOOK(reset_map_players, kh_ResetMap, CBC_ORDER_ANY); + MUTATOR_HOOK(HelpMePing, kh_HelpMePing, CBC_ORDER_ANY); + MUTATOR_HOOK(VehicleEnter, kh_VehicleEnter, CBC_ORDER_ANY); + MUTATOR_HOOK(VehicleExit, kh_VehicleExit, CBC_ORDER_ANY); + MUTATOR_HOOK(HavocBot_ChooseRole, kh_BotRoles, CBC_ORDER_ANY); + MUTATOR_HOOK(GetTeamCount, kh_GetTeamCount, CBC_ORDER_ANY); MUTATOR_ONADD { diff --cc qcsrc/server/mutators/gamemode_keyhunt.qh index d63b05685,6b27431c2..33b70a2c8 --- a/qcsrc/server/mutators/gamemode_keyhunt.qh +++ b/qcsrc/server/mutators/gamemode_keyhunt.qh @@@ -1,135 -1,14 +1,132 @@@ - // these are needed since mutators are compiled last + #ifndef GAMEMODE_KEYHUNT_H + #define GAMEMODE_KEYHUNT_H -// ALL OF THESE should be removed in the future, as other code should not have to care +#ifdef SVQC -// used by bots: -float kh_tracking_enabled; -.entity kh_next; -float kh_Key_AllOwnedByWhichTeam(); +// score rule declarations - #define ST_KH_CAPS 1 - #define SP_KH_CAPS 4 - #define SP_KH_CAPTIME 5 - #define SP_KH_PICKUPS 6 - #define SP_KH_DROPS 7 - #define SP_KH_KCKILLS 8 ++const int ST_KH_CAPS = 1; ++const int SP_KH_CAPS = 4; ++const int SP_KH_CAPTIME = 5; ++const int SP_KH_PICKUPS = 6; ++const int SP_KH_DROPS = 7; ++const int SP_KH_KCKILLS = 8; + +// key constants // for most of these, there is just one question to be asked: WHYYYYY? - #define KEY_MIN ('-10 -10 -46') - #define KEY_MAX ('10 10 3') ++const vector KEY_MIN = ('-10 -10 -46'); ++const vector KEY_MAX = ('10 10 3'); + - #define KH_KEY_ZSHIFT 22 - #define KH_KEY_XYDIST 24 - #define KH_KEY_XYSPEED 45 ++const float KH_KEY_ZSHIFT = 22; ++const float KH_KEY_XYDIST = 24; ++const float KH_KEY_XYSPEED = 45; + - #define KH_VEHICLE_KEY_XYDIST 100 - #define KH_VEHICLE_KEY_ZSHIFT 80 ++const float KH_VEHICLE_KEY_XYDIST = 100; ++const float KH_VEHICLE_KEY_ZSHIFT = 80; + - #define KEY_SCALE 1 ++const float KEY_SCALE = 1; + - #define KEY_BRIGHTNESS 2 ++const float KEY_BRIGHTNESS = 2; + - #define KEY_THINKRATE 0.1 - #define KEY_TOUCHRATE 0.5 ++const float KEY_THINKRATE = 0.1; ++const float KEY_TOUCHRATE = 0.5; + - #define KEY_DROP_OFFSET ('0 0 16') - #define KEY_CARRY_OFFSET ('-16 0 8') ++const vector KEY_DROP_OFFSET = ('0 0 16'); ++const vector KEY_CARRY_OFFSET = ('-16 0 8'); +#define KEY_SPAWN_OFFSET ('0 0 1' * (PL_MAX_z - 13)) - #define KEY_WAYPOINT_OFFSET ('0 0 64') - #define KEY_FLOAT_OFFSET ('0 0 16') - #define KEY_PASS_ARC_OFFSET ('0 0 -10') ++const vector KEY_WAYPOINT_OFFSET = ('0 0 64'); ++const vector KEY_FLOAT_OFFSET = ('0 0 16'); ++const vector KEY_PASS_ARC_OFFSET = ('0 0 -10'); + - #define VEHICLE_KEY_OFFSET ('0 0 96') - #define VEHICLE_KEY_SCALE 2 ++const vector VEHICLE_KEY_OFFSET = ('0 0 96'); ++const float VEHICLE_KEY_SCALE = 2; + +// waypoint colors - #define WPCOLOR_ENEMYKC(t) (colormapPaletteColor(t - 1, FALSE) * 0.75) ++#define WPCOLOR_ENEMYKC(t) (colormapPaletteColor(t - 1, false) * 0.75) +#define WPCOLOR_KEYCARRIER(t) ('0.8 0.8 0') - #define WPCOLOR_DROPPEDKEY(t) (('0.25 0.25 0.25' + colormapPaletteColor(t - 1, FALSE)) * 0.5) ++#define WPCOLOR_DROPPEDKEY(t) (('0.25 0.25 0.25' + colormapPaletteColor(t - 1, false)) * 0.5) + +// sounds +#define snd_key_taken noise +#define snd_key_returned noise1 +#define snd_key_capture noise2 +#define snd_key_respawn noise3 +.string snd_key_dropped; +.string snd_key_touch; +.string snd_key_pass; + +// effects - .float toucheffectnum; - .float passeffectnum; - .float capeffectnum; ++.int toucheffectnum; ++.int passeffectnum; ++.int capeffectnum; + +// list of keys on the map +entity kh_worldkeylist; +.entity kh_worldkeynext; +.entity kh_stalekeynext; + +// waypoint sprites - .entity wps_helpme; ++//.entity wps_helpme; +.entity wps_keycarrier; +.entity wps_keydropped; +.entity wps_enemykeycarrier; - .float wps_helpme_time; - float wpforenemy_announced; - float wpforenemy_nextthink; ++//.float wps_helpme_time; ++//bool wpforenemy_announced; ++//float wpforenemy_nextthink; + +// statuses - #define KEY_DROPPED 2 - #define KEY_CARRY 3 - #define KEY_PASSING 4 ++const int KEY_DROPPED = 2; ++const int KEY_CARRY = 3; ++const int KEY_PASSING = 4; + +// others defined in CTF code (TODO: separate?) - #define PICKUP_START 3 - #define PICKUP_KILLED 4 - - // carrier stats - .float stat_kh_redkey_team; - .float stat_kh_bluekey_team; - .float stat_kh_yellowkey_team; - .float stat_kh_pinkkey_team; ++const int PICKUP_START = 3; ++const int PICKUP_KILLED = 4; + +// key properties +.float kh_pickuptime; +.float kh_droptime; - .float kh_status; // status of the key (KEY_DROPPED, KEY_CARRY declared globally) ++.int kh_status; // status of the key (KEY_DROPPED, KEY_CARRY declared globally) +.entity kh_dropper; // don't allow spam of dropping the key +.entity kh_lastkiller; - .float max_key_health; ++.int max_key_health; +.float next_take_time; - float kh_teams; ++int kh_teams; + +// passing/throwing properties +.float pass_distance; +.entity pass_sender; +.entity pass_target; +.float throw_antispam; +.float throw_prevtime; - .float throw_count; ++.int throw_count; + +// alarms +float kh_alarm_time; +float kh_siren_time; + +// macro for checking all keys +#define KH_FOR_EACH_KEY(v) for(v = kh_worldkeylist; v; v = v.kh_worldkeynext) + +#endif + + +// networked key statuses +// bits 0- 4: team of key 1, or 0 for no such key, or 30 for dropped, or 31 for self +// bits 5- 9: team of key 2, or 0 for no such key, or 30 for dropped, or 31 for self +// bits 10-14: team of key 3, or 0 for no such key, or 30 for dropped, or 31 for self +// bits 15-19: team of key 4, or 0 for no such key, or 30 for dropped, or 31 for self - .float kh_keystatus; - - const float KH_RED_KEY_TAKEN = 1; - const float KH_RED_KEY_LOST = 2; - const float KH_RED_KEY_CARRYING = 3; - const float KH_BLUE_KEY_TAKEN = 4; - const float KH_BLUE_KEY_LOST = 8; - const float KH_BLUE_KEY_CARRYING = 12; - const float KH_YELLOW_KEY_TAKEN = 16; - const float KH_YELLOW_KEY_LOST = 32; - const float KH_YELLOW_KEY_CARRYING = 48; - const float KH_PINK_KEY_TAKEN = 64; - const float KH_PINK_KEY_LOST = 128; - const float KH_PINK_KEY_CARRYING = 192; ++.int kh_keystatus; ++ ++const int KH_RED_KEY_TAKEN = 1; ++const int KH_RED_KEY_LOST = 2; ++const int KH_RED_KEY_CARRYING = 3; ++const int KH_BLUE_KEY_TAKEN = 4; ++const int KH_BLUE_KEY_LOST = 8; ++const int KH_BLUE_KEY_CARRYING = 12; ++const int KH_YELLOW_KEY_TAKEN = 16; ++const int KH_YELLOW_KEY_LOST = 32; ++const int KH_YELLOW_KEY_CARRYING = 48; ++const int KH_PINK_KEY_TAKEN = 64; ++const int KH_PINK_KEY_LOST = 128; ++const int KH_PINK_KEY_CARRYING = 192; + -typedef void(void) kh_Think_t; -void kh_StartRound(); -void kh_Controller_SetThink(float t, kh_Think_t func); + #endif diff --cc qcsrc/server/mutators/gamemode_lms.qc index a548e1082,13b77305a..f11605a4d --- a/qcsrc/server/mutators/gamemode_lms.qc +++ b/qcsrc/server/mutators/gamemode_lms.qc @@@ -41,12 -41,9 +41,12 @@@ MUTATOR_HOOKFUNCTION(lms_PlayerPreSpawn // player is dead and becomes observer // FIXME fix LMS scoring for new system if(PlayerScore_Add(self, SP_LMS_RANK, 0) > 0) + { self.classname = "observer"; + Send_Notification(NOTIF_ONE, self, MSG_CENTER, CENTER_LMS_NOLIVES); + } - return FALSE; + return false; } MUTATOR_HOOKFUNCTION(lms_PlayerDies) @@@ -178,16 -171,6 +178,16 @@@ MUTATOR_HOOKFUNCTION(lms_ItemTouch return MUT_ITEMTOUCH_CONTINUE; } +MUTATOR_HOOKFUNCTION(lms_WantWeapon) +{ + if(other.spawnflags & WEP_FLAG_NORMAL) - ret_float = TRUE; ++ ret_float = true; + else - ret_float = FALSE; ++ ret_float = false; + - return FALSE; ++ return false; +} + // scoreboard stuff void lms_ScoreRules() { diff --cc qcsrc/server/mutators/gamemode_nexball.qc index 8c894cc53,afdb769cf..0f0746282 --- a/qcsrc/server/mutators/gamemode_nexball.qc +++ b/qcsrc/server/mutators/gamemode_nexball.qc @@@ -437,31 -437,31 +437,31 @@@ void nb_spawnteams(void switch(e.team) { case NUM_TEAM_1: - if(!t_r) + if(!t_red) { - nb_spawnteam("Red", e.team-1) ; + nb_spawnteam(NAME_TEAM_1, e.team-1); - t_r = 1; + t_red = true; } break; case NUM_TEAM_2: - if(!t_b) + if(!t_blue) { - nb_spawnteam("Blue", e.team-1) ; + nb_spawnteam(NAME_TEAM_2, e.team-1); - t_b = 1; + t_blue = true; } break; case NUM_TEAM_3: - if(!t_y) + if(!t_yellow) { - nb_spawnteam("Yellow", e.team-1); + nb_spawnteam(NAME_TEAM_3, e.team-1); - t_y = 1; + t_yellow = true; } break; case NUM_TEAM_4: - if(!t_p) + if(!t_pink) { - nb_spawnteam("Pink", e.team-1) ; + nb_spawnteam(NAME_TEAM_4, e.team-1); - t_p = 1; + t_pink = true; } break; } @@@ -771,7 -771,8 +771,7 @@@ void W_Nexball_Attack2(void entity missile; if(!(balls & BALL_BASKET)) return; - W_SetupShot(self, FALSE, 2, "nexball/shoot2.wav", CH_WEAPON_A, 0); + W_SetupShot(self, false, 2, "nexball/shoot2.wav", CH_WEAPON_A, 0); -// pointparticles(particleeffectnum("grenadelauncher_muzzleflash"), w_shotorg, w_shotdir * 1000, 1); missile = spawn(); missile.owner = self; @@@ -971,15 -972,9 +971,15 @@@ MUTATOR_HOOKFUNCTION(nexball_SetStartIt { start_items |= IT_UNLIMITED_SUPERWEAPONS; // FIXME BAD BAD BAD BAD HACK, NEXBALL SHOULDN'T ABUSE PORTO'S WEAPON SLOT - return FALSE; + return false; } +MUTATOR_HOOKFUNCTION(nexball_GetTeamCount) +{ + ret_string = "nexball_team"; - return TRUE; ++ return true; +} + MUTATOR_HOOKFUNCTION(nexball_ForbidThrowing) { if(self.weapon == WEP_MORTAR) @@@ -992,23 -987,11 +992,23 @@@ MUTATOR_HOOKFUNCTION(nexball_FilterItem { if(self.classname == "droppedweapon") if(self.weapon == WEP_MORTAR) - return TRUE; + return true; - return FALSE; + return false; } +MUTATOR_HOOKFUNCTION(nexball_WantWeapon) +{ + ret_float = 0; // weapon is set a few lines later - return FALSE; ++ return false; +} + +MUTATOR_HOOKFUNCTION(nexball_AllowMobSpawning) +{ + ret_string = "You cannot spawn monsters in nexball!"; - return TRUE; ++ return true; +} + MUTATOR_DEFINITION(gamemode_nexball) { MUTATOR_HOOK(PlayerDies, nexball_BallDrop, CBC_ORDER_ANY); diff --cc qcsrc/server/mutators/gamemode_onslaught.qc index 752a66387,d1f20f7ee..fbb4a04b0 --- a/qcsrc/server/mutators/gamemode_onslaught.qc +++ b/qcsrc/server/mutators/gamemode_onslaught.qc @@@ -1,115 -1,129 +1,116 @@@ - // ======================= - // CaptureShield Functions - // ======================= -float autocvar_g_onslaught_spawn_at_controlpoints; -float autocvar_g_onslaught_spawn_at_generator; -float autocvar_g_onslaught_cp_proxydecap; -float autocvar_g_onslaught_cp_proxydecap_distance = 512; -float autocvar_g_onslaught_cp_proxydecap_dps = 100; ++#include "../../common/effects.qh" ++#include "../round_handler.qh" ++#include "../controlpoint.qh" ++#include "../generator.qh" -void onslaught_generator_updatesprite(entity e); -void onslaught_controlpoint_updatesprite(entity e); -void onslaught_link_checkupdate(); - -.entity sprite; -.string target2; -.float iscaptured; -.float islinked; -.float isgenneighbor_red; -.float isgenneighbor_blue; -.float iscpneighbor_red; -.float iscpneighbor_blue; -.float isshielded; -.float lasthealth; -.float lastteam; -.float lastshielded; -.float lastcaptured; +float ons_CaptureShield_Customize() +{ + entity e = WaypointSprite_getviewentity(other); - if(!self.enemy.isshielded && (ons_ControlPoint_Attackable(self.enemy, e.team) > 0 || self.enemy.classname != "onslaught_controlpoint")) { return FALSE; } - if(SAME_TEAM(self, e)) { return FALSE; } -entity ons_red_generator; -entity ons_blue_generator; ++ if(!self.enemy.isshielded && (ons_ControlPoint_Attackable(self.enemy, e.team) > 0 || self.enemy.classname != "onslaught_controlpoint")) { return false; } ++ if(SAME_TEAM(self, e)) { return false; } - return TRUE; -void ons_gib_damage (entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector vforce) -{ - self.velocity = self.velocity + vforce; ++ return true; } -.float giblifetime; -void ons_throwgib_think() +void ons_CaptureShield_Touch() { - float d; + if(!self.enemy.isshielded && (ons_ControlPoint_Attackable(self.enemy, other.team) > 0 || self.enemy.classname != "onslaught_controlpoint")) { return; } + if(!IS_PLAYER(other)) { return; } + if(SAME_TEAM(other, self)) { return; } - self.nextthink = time + 0.05; + vector mymid = (self.absmin + self.absmax) * 0.5; + vector othermid = (other.absmin + other.absmax) * 0.5; - d = self.giblifetime - time; + Damage(other, self, self, 0, DEATH_HURTTRIGGER, mymid, normalize(othermid - mymid) * ons_captureshield_force); - if(d<0) + if(IS_REAL_CLIENT(other)) { - self.think = SUB_Remove; - return; + play2(other, "onslaught/damageblockedbyshield.wav"); + + if(self.enemy.classname == "onslaught_generator") + Send_Notification(NOTIF_ONE, other, MSG_CENTER, CENTER_ONS_GENERATOR_SHIELDED); + else + Send_Notification(NOTIF_ONE, other, MSG_CENTER, CENTER_ONS_CONTROLPOINT_SHIELDED); } - if(d<1) - self.alpha = d; +} - if(d>2) - if(random()<0.6) - pointparticles(particleeffectnum("onslaught_generator_gib_flame"), self.origin, '0 0 0', 1); +void ons_CaptureShield_Reset() +{ + self.colormap = self.enemy.colormap; + self.team = self.enemy.team; } -void ons_throwgib(vector v_from, vector v_to, string smodel, float f_lifetime, float b_burn) +void ons_CaptureShield_Spawn(entity generator, float is_generator) { - entity gib; + entity shield = spawn(); + + shield.enemy = generator; + shield.team = generator.team; + shield.colormap = generator.colormap; + shield.reset = ons_CaptureShield_Reset; + shield.touch = ons_CaptureShield_Touch; + shield.customizeentityforclient = ons_CaptureShield_Customize; + shield.classname = "ons_captureshield"; + shield.effects = EF_ADDITIVE; + shield.movetype = MOVETYPE_NOCLIP; + shield.solid = SOLID_TRIGGER; + shield.avelocity = '7 0 11'; + shield.scale = 1; + shield.model = ((is_generator) ? "models/onslaught/generator_shield.md3" : "models/onslaught/controlpoint_shield.md3"); + + precache_model(shield.model); + setorigin(shield, generator.origin); + setmodel(shield, shield.model); + setsize(shield, shield.scale * shield.mins, shield.scale * shield.maxs); +} - gib = spawn(); - setmodel(gib, smodel); - setorigin(gib, v_from); - gib.solid = SOLID_BBOX; - gib.movetype = MOVETYPE_BOUNCE; - gib.takedamage = DAMAGE_YES; - gib.event_damage = ons_gib_damage; - gib.health = -1; - gib.effects = EF_LOWPRECISION; - gib.flags = FL_NOTARGET; - gib.velocity = v_to; - gib.giblifetime = time + f_lifetime; +// ========== +// Junk Pile +// ========== - if (b_burn) +void ons_debug(string input) +{ + switch(autocvar_g_onslaught_debug) { - gib.think = ons_throwgib_think; - gib.nextthink = time + 0.05; + case 1: dprint(input); break; + case 2: print(input); break; } - else - SUB_SetFade(gib, gib.giblifetime, 2); } void onslaught_updatelinks() { - entity l, links; - float stop, t1, t2, t3, t4; + entity l; // first check if the game has ended - dprint("--- updatelinks ---\n"); - links = findchain(classname, "onslaught_link"); + ons_debug("--- updatelinks ---\n"); // mark generators as being shielded and networked - l = findchain(classname, "onslaught_generator"); - while (l) + for(l = ons_worldgeneratorlist; l; l = l.ons_worldgeneratornext) { if (l.iscaptured) - dprint(etos(l), " (generator) belongs to team ", ftos(l.team), "\n"); + ons_debug(strcat(etos(l), " (generator) belongs to team ", ftos(l.team), "\n")); else - dprint(etos(l), " (generator) is destroyed\n"); + ons_debug(strcat(etos(l), " (generator) is destroyed\n")); l.islinked = l.iscaptured; l.isshielded = l.iscaptured; - l = l.chain; + l.sprite.SendFlags |= 16; } // mark points as shielded and not networked - l = findchain(classname, "onslaught_controlpoint"); - while (l) + for(l = ons_worldcplist; l; l = l.ons_worldcpnext) { - l.islinked = FALSE; - l.isshielded = TRUE; + l.islinked = false; + l.isshielded = true; - l.isgenneighbor_red = false; - l.isgenneighbor_blue = false; - l.iscpneighbor_red = false; - l.iscpneighbor_blue = false; - dprint(etos(l), " (point) belongs to team ", ftos(l.team), "\n"); - l = l.chain; + float i; - for(i = 0; i < 17; ++i) { l.isgenneighbor[i] = FALSE; l.iscpneighbor[i] = FALSE; } ++ for(i = 0; i < 17; ++i) { l.isgenneighbor[i] = false; l.iscpneighbor[i] = false; } + ons_debug(strcat(etos(l), " (point) belongs to team ", ftos(l.team), "\n")); + l.sprite.SendFlags |= 16; } // flow power outward from the generators through the network - float stop = FALSE; - l = links; - while (l) - { - dprint(etos(l), " (link) connects ", etos(l.goalentity), " with ", etos(l.enemy), "\n"); - l = l.chain; - } - stop = false; ++ float stop = false; while (!stop) { - stop = TRUE; + stop = true; - l = links; - while (l) + for(l = ons_worldlinklist; l; l = l.ons_worldlinknext) { // if both points are captured by the same team, and only one of // them is powered, mark the other one as powered as well @@@ -119,17 -133,18 +120,17 @@@ { if (!l.goalentity.islinked) { - stop = FALSE; - l.goalentity.islinked = TRUE; + stop = false; + l.goalentity.islinked = true; - dprint(etos(l), " (link) is marking ", etos(l.goalentity), " (point) because its team matches ", etos(l.enemy), " (point)\n"); + ons_debug(strcat(etos(l), " (link) is marking ", etos(l.goalentity), " (point) because its team matches ", etos(l.enemy), " (point)\n")); } else if (!l.enemy.islinked) { - stop = FALSE; - l.enemy.islinked = TRUE; + stop = false; + l.enemy.islinked = true; - dprint(etos(l), " (link) is marking ", etos(l.enemy), " (point) because its team matches ", etos(l.goalentity), " (point)\n"); + ons_debug(strcat(etos(l), " (link) is marking ", etos(l.enemy), " (point) because its team matches ", etos(l.goalentity), " (point)\n")); } } - l = l.chain; } } // now that we know which points are powered we can mark their neighbors @@@ -138,49 -154,73 +139,49 @@@ { if (l.goalentity.islinked) { - if (l.goalentity.team != l.enemy.team) + if(DIFF_TEAM(l.goalentity, l.enemy)) { - dprint(etos(l), " (link) is unshielding ", etos(l.enemy), " (point) because its team does not match ", etos(l.goalentity), " (point)\n"); + ons_debug(strcat(etos(l), " (link) is unshielding ", etos(l.enemy), " (point) because its team does not match ", etos(l.goalentity), " (point)\n")); - l.enemy.isshielded = FALSE; + l.enemy.isshielded = false; } if(l.goalentity.classname == "onslaught_generator") - l.enemy.isgenneighbor[l.goalentity.team] = TRUE; - { - if(l.goalentity.team == NUM_TEAM_1) - l.enemy.isgenneighbor_red = true; - else if(l.goalentity.team == NUM_TEAM_2) - l.enemy.isgenneighbor_blue = true; - } ++ l.enemy.isgenneighbor[l.goalentity.team] = true; else - l.enemy.iscpneighbor[l.goalentity.team] = TRUE; - { - if(l.goalentity.team == NUM_TEAM_1) - l.enemy.iscpneighbor_red = true; - else if(l.goalentity.team == NUM_TEAM_2) - l.enemy.iscpneighbor_blue = true; - } ++ l.enemy.iscpneighbor[l.goalentity.team] = true; } if (l.enemy.islinked) { - if (l.goalentity.team != l.enemy.team) + if(DIFF_TEAM(l.goalentity, l.enemy)) { - dprint(etos(l), " (link) is unshielding ", etos(l.goalentity), " (point) because its team does not match ", etos(l.enemy), " (point)\n"); + ons_debug(strcat(etos(l), " (link) is unshielding ", etos(l.goalentity), " (point) because its team does not match ", etos(l.enemy), " (point)\n")); - l.goalentity.isshielded = FALSE; + l.goalentity.isshielded = false; } if(l.enemy.classname == "onslaught_generator") - l.goalentity.isgenneighbor[l.enemy.team] = TRUE; - { - if(l.enemy.team == NUM_TEAM_1) - l.goalentity.isgenneighbor_red = true; - else if(l.enemy.team == NUM_TEAM_2) - l.goalentity.isgenneighbor_blue = true; - } ++ l.goalentity.isgenneighbor[l.enemy.team] = true; else - l.goalentity.iscpneighbor[l.enemy.team] = TRUE; - { - if(l.enemy.team == NUM_TEAM_1) - l.goalentity.iscpneighbor_red = true; - else if(l.enemy.team == NUM_TEAM_2) - l.goalentity.iscpneighbor_blue = true; - } ++ l.goalentity.iscpneighbor[l.enemy.team] = true; } - l = l.chain; } - // now update the takedamage and alpha variables on generator shields - l = findchain(classname, "onslaught_generator"); - while (l) + // now update the generators + for(l = ons_worldgeneratorlist; l; l = l.ons_worldgeneratornext) { if (l.isshielded) { - dprint(etos(l), " (generator) is shielded\n"); - l.enemy.alpha = 1; + ons_debug(strcat(etos(l), " (generator) is shielded\n")); l.takedamage = DAMAGE_NO; - l.bot_attack = FALSE; + l.bot_attack = false; } else { - dprint(etos(l), " (generator) is not shielded\n"); - l.enemy.alpha = -1; + ons_debug(strcat(etos(l), " (generator) is not shielded\n")); l.takedamage = DAMAGE_AIM; - l.bot_attack = TRUE; + l.bot_attack = true; } - l = l.chain; + + ons_Generator_UpdateSprite(l); } // now update the takedamage and alpha variables on control point icons - l = findchain(classname, "onslaught_controlpoint"); - while (l) + for(l = ons_worldcplist; l; l = l.ons_worldcpnext) { if (l.isshielded) { @@@ -197,94 -239,100 +198,94 @@@ if (l.goalentity) { l.goalentity.takedamage = DAMAGE_AIM; - l.goalentity.bot_attack = TRUE; + l.goalentity.bot_attack = true; } } - onslaught_controlpoint_updatesprite(l); - l = l.chain; + ons_ControlPoint_UpdateSprite(l); } - // count generators owned by each team - t1 = t2 = t3 = t4 = 0; - l = findchain(classname, "onslaught_generator"); - while (l) + l = findchain(classname, "ons_captureshield"); + while(l) { - if (l.iscaptured) - { - if (l.team == NUM_TEAM_1) t1 = 1; - if (l.team == NUM_TEAM_2) t2 = 1; - if (l.team == NUM_TEAM_3) t3 = 1; - if (l.team == NUM_TEAM_4) t4 = 1; - } - onslaught_generator_updatesprite(l); + l.team = l.enemy.team; + l.colormap = l.enemy.colormap; l = l.chain; } - // see if multiple teams remain (if not, it's game over) - if (t1 + t2 + t3 + t4 < 2) - dprint("--- game over ---\n"); - else - dprint("--- done updating links ---\n"); } -float onslaught_controlpoint_can_be_linked(entity cp, float t) + +// =================== +// Main Link Functions +// =================== + +float ons_Link_Send(entity to, float sendflags) { - if(t == NUM_TEAM_1) + WriteByte(MSG_ENTITY, ENT_CLIENT_RADARLINK); + WriteByte(MSG_ENTITY, sendflags); + if(sendflags & 1) { - if(cp.isgenneighbor_red) - return 2; - if(cp.iscpneighbor_red) - return 1; + WriteCoord(MSG_ENTITY, self.goalentity.origin_x); + WriteCoord(MSG_ENTITY, self.goalentity.origin_y); + WriteCoord(MSG_ENTITY, self.goalentity.origin_z); } - else if(t == NUM_TEAM_2) + if(sendflags & 2) { - if(cp.isgenneighbor_blue) - return 2; - if(cp.iscpneighbor_blue) - return 1; + WriteCoord(MSG_ENTITY, self.enemy.origin_x); + WriteCoord(MSG_ENTITY, self.enemy.origin_y); + WriteCoord(MSG_ENTITY, self.enemy.origin_z); } - return 0; - /* - entity e; - // check to see if this player has a legitimate claim to capture this - // control point - more specifically that there is a captured path of - // points leading back to the team generator - e = findchain(classname, "onslaught_link"); - while (e) - { - if (e.goalentity == cp) - { - dprint(etos(e), " (link) connects to ", etos(e.enemy), " (point)"); - if (e.enemy.islinked) - { - dprint(" which is linked"); - if (e.enemy.team == t) + if(sendflags & 4) { - dprint(" and has the correct team!\n"); - return 1; - } - else - dprint(" but has the wrong team\n"); - } - else - dprint("\n"); + WriteByte(MSG_ENTITY, self.clientcolors); // which is goalentity's color + enemy's color * 16 } - return TRUE; - else if (e.enemy == cp) - { - dprint(etos(e), " (link) connects to ", etos(e.goalentity), " (point)"); - if (e.goalentity.islinked) - { - dprint(" which is linked"); - if (e.goalentity.team == t) ++ return true; +} + +void ons_Link_CheckUpdate() +{ + // TODO check if the two sides have moved (currently they won't move anyway) + float cc = 0, cc1 = 0, cc2 = 0; + + if(self.goalentity.islinked || self.goalentity.iscaptured) { cc1 = (self.goalentity.team - 1) * 0x01; } + if(self.enemy.islinked || self.enemy.iscaptured) { cc2 = (self.enemy.team - 1) * 0x10; } + + cc = cc1 + cc2; + + if(cc != self.clientcolors) { - dprint(" and has a team!\n"); - return 1; - } - else - dprint(" but has the wrong team\n"); - } - else - dprint("\n"); - } - e = e.chain; + self.clientcolors = cc; + self.SendFlags |= 4; } + + self.nextthink = time; +} + +void ons_DelayedLinkSetup() +{ + self.goalentity = find(world, targetname, self.target); + self.enemy = find(world, targetname, self.target2); + if(!self.goalentity) { objerror("can not find target\n"); } + if(!self.enemy) { objerror("can not find target2\n"); } + + ons_debug(strcat(etos(self.goalentity), " linked with ", etos(self.enemy), "\n")); + self.SendFlags |= 3; + self.think = ons_Link_CheckUpdate; + self.nextthink = time; +} + + +// ============================= +// Main Control Point Functions +// ============================= + +float ons_ControlPoint_CanBeLinked(entity cp, float teamnumber) +{ + if(cp.isgenneighbor[teamnumber]) { return 2; } + if(cp.iscpneighbor[teamnumber]) { return 1; } + return 0; - */ } -float onslaught_controlpoint_attackable(entity cp, float t) +float ons_ControlPoint_Attackable(entity cp, float teamnumber) // -2: SAME TEAM, attackable by enemy! // -1: SAME TEAM! // 0: off limits @@@ -331,292 -379,415 +332,293 @@@ return 0; } -float overtime_msg_time; -void onslaught_generator_think() +void ons_ControlPoint_Icon_Damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force) { - float d; - entity e; - self.nextthink = ceil(time + 1); - if (!gameover) + entity oself; + + if(damage <= 0) { return; } + + if (self.owner.isshielded) { - if (autocvar_timelimit && time > game_starttime + autocvar_timelimit * 60) - { - if (!overtime_msg_time) - { - Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_OVERTIME_CONTROLPOINT); - overtime_msg_time = time; - } - // self.max_health / 300 gives 5 minutes of overtime. - // control points reduce the overtime duration. - sound(self, CH_TRIGGER, "onslaught/generator_decay.wav", VOL_BASE, ATTEN_NORM); - d = 1; - e = findchain(classname, "onslaught_controlpoint"); - while (e) + // this is protected by a shield, so ignore the damage + if (time > self.pain_finished) + if (IS_PLAYER(attacker)) { - if (e.team != self.team) - if (e.islinked) - d = d + 1; - e = e.chain; + play2(attacker, "onslaught/damageblockedbyshield.wav"); + self.pain_finished = time + 1; + attacker.typehitsound += 1; // play both sounds (shield is way too quiet) } - if(autocvar_g_campaign && autocvar__campaign_testrun) - d = d * self.max_health; - else - d = d * self.max_health / max(30, 60 * autocvar_timelimit_suddendeath); - - Damage(self, self, self, d, DEATH_HURTTRIGGER, self.origin, '0 0 0'); - } - else if (overtime_msg_time) - overtime_msg_time = 0; - - if(!self.isshielded && self.wait < time) - { - self.wait = time + 5; - FOR_EACH_REALPLAYER(e) - { - if(SAME_TEAM(e, self)) - { - Send_Notification(NOTIF_ONE, e, MSG_CENTER, CENTER_ONS_NOTSHIELDED); - soundto(MSG_ONE, e, CHAN_AUTO, "kh/alarm.wav", VOL_BASE, ATTEN_NONE); // FIXME: Uniqe sound? - } - } - } + return; } -} - -void onslaught_generator_ring_spawn(vector org) -{ - modeleffect_spawn("models/onslaught/shockwavetransring.md3", 0, 0, org, '0 0 0', '0 0 0', '0 0 0', 0, -16, 0.1, 1.25, 0.25); -} -void onslaught_generator_ray_think() -{ - self.nextthink = time + 0.05; - if(self.count > 10) + if(IS_PLAYER(attacker)) + if(time - ons_notification_time[self.team] > 10) { - self.think = SUB_Remove; - return; + play2team(self.team, "onslaught/controlpoint_underattack.wav"); + ons_notification_time[self.team] = time; } - if(self.count > 5) - self.alpha -= 0.1; + self.health = self.health - damage; + if(self.owner.iscaptured) + WaypointSprite_UpdateHealth(self.owner.sprite, self.health); + else + WaypointSprite_UpdateBuildFinished(self.owner.sprite, time + (self.max_health - self.health) / (self.count / ONS_CP_THINKRATE)); + self.pain_finished = time + 1; + // particles on every hit + Send_Effect(EFFECT_SPARKS, hitloc, force * -1, 1); + //sound on every hit + if (random() < 0.5) + sound(self, CH_TRIGGER, "onslaught/ons_hit1.wav", VOL_BASE+0.3, ATTEN_NORM); else - self.alpha += 0.1; + sound(self, CH_TRIGGER, "onslaught/ons_hit2.wav", VOL_BASE+0.3, ATTEN_NORM); - self.scale += 0.2; - self.count +=1; -} + if (self.health < 0) + { + sound(self, CH_TRIGGER, W_Sound("grenade_impact"), VOL_BASE, ATTEN_NORM); + Send_Effect(EFFECT_ROCKET_EXPLODE, self.origin, '0 0 0', 1); + Send_Notification(NOTIF_ALL, world, MSG_INFO, APP_TEAM_NUM_4(self.team, INFO_ONSLAUGHT_CPDESTROYED_), self.owner.message, attacker.netname); + + PlayerScore_Add(attacker, SP_ONS_TAKES, 1); + PlayerScore_Add(attacker, SP_SCORE, 10); + + self.owner.goalentity = world; - self.owner.islinked = FALSE; - self.owner.iscaptured = FALSE; ++ self.owner.islinked = false; ++ self.owner.iscaptured = false; + self.owner.team = 0; + self.owner.colormap = 1024; -void onslaught_generator_ray_spawn(vector org) -{ - entity e; - e = spawn(); - setmodel(e, "models/onslaught/ons_ray.md3"); - setorigin(e, org); - e.angles = randomvec() * 360; - e.alpha = 0; - e.scale = random() * 5 + 8; - e.think = onslaught_generator_ray_think; - e.nextthink = time + 0.05; -} + WaypointSprite_UpdateMaxHealth(self.owner.sprite, 0); -void onslaught_generator_shockwave_spawn(vector org) -{ - shockwave_spawn("models/onslaught/shockwave.md3", org, -64, 0.75, 0.5); -} + onslaught_updatelinks(); -void onslaught_generator_damage_think() -{ - if(self.owner.health < 0) - { - self.think = SUB_Remove; - return; - } - self.nextthink = time+0.1; + // Use targets now (somebody make sure this is in the right place..) + oself = self; + self = self.owner; + activator = self; + SUB_UseTargets (); + self = oself; - // damaged fx (less probable the more damaged is the generator) - if(random() < 0.9 - self.owner.health / self.owner.max_health) - if(random() < 0.01) - { - pointparticles(particleeffectnum("electro_ballexplode"), self.origin + randompos('-50 -50 -20', '50 50 50'), '0 0 0', 1); - sound(self, CH_TRIGGER, "onslaught/electricity_explode.wav", VOL_BASE, ATTEN_NORM); - } - else - pointparticles(particleeffectnum("torch_small"), self.origin + randompos('-60 -60 -20', '60 60 60'), '0 0 0', 1); -} + self.owner.waslinked = self.owner.islinked; + if(self.owner.model != "models/onslaught/controlpoint_pad.md3") + setmodel_fixsize(self.owner, "models/onslaught/controlpoint_pad.md3"); + //setsize(self, '-32 -32 0', '32 32 8'); -void onslaught_generator_damage_spawn(entity gd_owner) -{ - entity e; - e = spawn(); - e.owner = gd_owner; - e.health = self.owner.health; - setorigin(e, gd_owner.origin); - e.think = onslaught_generator_damage_think; - e.nextthink = time+1; + remove(self); + } + + self.SendFlags |= CPSF_STATUS; } -void onslaught_generator_deaththink() +void ons_ControlPoint_Icon_Think() { - vector org; - float i; - - if (!self.count) - self.count = 40; + entity oself; + self.nextthink = time + ONS_CP_THINKRATE; - // White shockwave - if(self.count==40||self.count==20) + if(autocvar_g_onslaught_cp_proxydecap) { - onslaught_generator_ring_spawn(self.origin); - sound(self, CH_TRIGGER, "onslaught/shockwave.wav", VOL_BASE, ATTEN_NORM); - } + float _enemy_count = 0; + float _friendly_count = 0; + float _dist; + entity _player; - // Throw some gibs - if(random() < 0.3) - { - i = random(); - if(i < 0.3) - ons_throwgib(self.origin + '0 0 40', (100 * randomvec() - '1 1 1') * 11 + '0 0 20', "models/onslaught/gen_gib1.md3", 6, true); - else if(i > 0.7) - ons_throwgib(self.origin + '0 0 40', (100 * randomvec() - '1 1 1') * 12 + '0 0 20', "models/onslaught/gen_gib2.md3", 6, true); - else - ons_throwgib(self.origin + '0 0 40', (100 * randomvec() - '1 1 1') * 13 + '0 0 20', "models/onslaught/gen_gib3.md3", 6, true); - } + FOR_EACH_PLAYER(_player) + { + if(!_player.deadflag) + { + _dist = vlen(_player.origin - self.origin); + if(_dist < autocvar_g_onslaught_cp_proxydecap_distance) + { + if(SAME_TEAM(_player, self)) + ++_friendly_count; + else + ++_enemy_count; + } + } + } + + _friendly_count = _friendly_count * (autocvar_g_onslaught_cp_proxydecap_dps * ONS_CP_THINKRATE); + _enemy_count = _enemy_count * (autocvar_g_onslaught_cp_proxydecap_dps * ONS_CP_THINKRATE); - // Spawn fire balls - for(i=0;i < 10;++i) + self.health = bound(0, self.health + (_friendly_count - _enemy_count), self.max_health); + self.SendFlags |= CPSF_STATUS; + if(self.health <= 0) + { + ons_ControlPoint_Icon_Damage(self, self, 1, 0, self.origin, '0 0 0'); + return; + } + } + + if (time > self.pain_finished + 5) { - org = self.origin + randompos('-30 -30 -30' * i + '0 0 -20', '30 30 30' * i + '0 0 20'); - pointparticles(particleeffectnum("onslaught_generator_gib_explode"), org, '0 0 0', 1); + if(self.health < self.max_health) + { + self.health = self.health + self.count; + if (self.health >= self.max_health) + self.health = self.max_health; + WaypointSprite_UpdateHealth(self.owner.sprite, self.health); + } } - // Short explosion sound + small explosion - if(random() < 0.25) + if(self.owner.islinked != self.owner.waslinked) { - te_explosion(self.origin); - sound(self, CH_TRIGGER, "weapons/grenade_impact.wav", VOL_BASE, ATTEN_NORM); - } + // unteam the spawnpoint if needed + float t; + t = self.owner.team; + if(!self.owner.islinked) + self.owner.team = 0; + + oself = self; + self = self.owner; + activator = self; + SUB_UseTargets (); + self = oself; - // Particles - org = self.origin + randompos(self.mins + '8 8 8', self.maxs + '-8 -8 -8'); - pointparticles(particleeffectnum("onslaught_generator_smallexplosion"), org, '0 0 0', 1); + self.owner.team = t; - // rays - if(random() > 0.25 ) - { - onslaught_generator_ray_spawn(self.origin); + self.owner.waslinked = self.owner.islinked; } - // Final explosion - if(self.count==1) + // damaged fx + if(random() < 0.6 - self.health / self.max_health) { - org = self.origin; - te_explosion(org); - onslaught_generator_shockwave_spawn(org); - pointparticles(particleeffectnum("onslaught_generator_finalexplosion"), org, '0 0 0', 1); - sound(self, CH_TRIGGER, "weapons/rocket_impact.wav", VOL_BASE, ATTEN_NORM); - } - else - self.nextthink = time + 0.05; + Send_Effect(EFFECT_ELECTRIC_SPARKS, self.origin + randompos('-10 -10 -20', '10 10 20'), '0 0 0', 1); - self.count = self.count - 1; + if(random() > 0.8) + sound(self, CH_PAIN, "onslaught/ons_spark1.wav", VOL_BASE, ATTEN_NORM); + else if (random() > 0.5) + sound(self, CH_PAIN, "onslaught/ons_spark2.wav", VOL_BASE, ATTEN_NORM); + } } -void onslaught_generator_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force) +void ons_ControlPoint_Icon_BuildThink() { - float i; - if (damage <= 0) - return; - if(warmup_stage) + entity oself; + float a; + + self.nextthink = time + ONS_CP_THINKRATE; + + // only do this if there is power + a = ons_ControlPoint_CanBeLinked(self.owner, self.owner.team); + if(!a) return; - if (attacker != self) + + self.health = self.health + self.count; + + self.SendFlags |= CPSF_STATUS; + + if (self.health >= self.max_health) { - if (self.isshielded) + self.health = self.max_health; + self.count = autocvar_g_onslaught_cp_regen * ONS_CP_THINKRATE; // slow repair rate from now on + self.think = ons_ControlPoint_Icon_Think; + sound(self, CH_TRIGGER, "onslaught/controlpoint_built.wav", VOL_BASE, ATTEN_NORM); - self.owner.iscaptured = TRUE; ++ self.owner.iscaptured = true; + self.solid = SOLID_BBOX; + + float eff_team; + switch(self.owner.team) { - // this is protected by a shield, so ignore the damage - if (time > self.pain_finished) - if (IS_PLAYER(attacker)) - { - play2(attacker, "onslaught/damageblockedbyshield.wav"); - self.pain_finished = time + 1; - } - return; - } - if (time > self.pain_finished) - { - self.pain_finished = time + 10; - bprint(Team_ColoredFullName(self.team), " generator under attack!\n"); - play2team(self.team, "onslaught/generator_underattack.wav"); - } - } - self.health = self.health - damage; - WaypointSprite_UpdateHealth(self.sprite, self.health); - // choose an animation frame based on health - self.frame = 10 * bound(0, (1 - self.health / self.max_health), 1); - // see if the generator is still functional, or dying - if (self.health > 0) - { -#ifdef ONSLAUGHT_SPAM - float h, lh; - lh = ceil(self.lasthealth / 100) * 100; - h = ceil(self.health / 100) * 100; - if(lh != h) - bprint(Team_ColoredFullName(self.team), " generator has less than ", ftos(h), " health remaining\n"); -#endif - self.lasthealth = self.health; - } - else if (!warmup_stage) - { - if (attacker == self) - bprint(Team_ColoredFullName(self.team), " generator spontaneously exploded due to overtime!\n"); - else - { - string t; - t = Team_ColoredFullName(attacker.team); - bprint(Team_ColoredFullName(self.team), " generator destroyed by ", t, "!\n"); + case NUM_TEAM_1: eff_team = EFFECT_RED_CAP; break; + case NUM_TEAM_2: eff_team = EFFECT_BLUE_CAP; break; + case NUM_TEAM_3: eff_team = EFFECT_YELLOW_CAP; break; + case NUM_TEAM_4: eff_team = EFFECT_PINK_CAP; break; + default: eff_team = EFFECT_SPAWN_NEUTRAL; break; } - self.iscaptured = false; - self.islinked = false; - self.isshielded = false; - self.takedamage = DAMAGE_NO; // can't be hurt anymore - self.event_damage = func_null; // won't do anything if hurt - self.count = 0; // reset counter - self.think = onslaught_generator_deaththink; // explosion sequence - self.nextthink = time; // start exploding immediately - self.think(); // do the first explosion now - WaypointSprite_UpdateMaxHealth(self.sprite, 0); + Send_Effect(eff_team, self.owner.origin, '0 0 0', 1); - onslaught_updatelinks(); - } + WaypointSprite_UpdateMaxHealth(self.owner.sprite, self.max_health); + WaypointSprite_UpdateHealth(self.owner.sprite, self.health); - if(self.health <= 0) - setmodel(self, "models/onslaught/generator_dead.md3"); - else if(self.health < self.max_health * 0.10) - setmodel(self, "models/onslaught/generator_dmg9.md3"); - else if(self.health < self.max_health * 0.20) - setmodel(self, "models/onslaught/generator_dmg8.md3"); - else if(self.health < self.max_health * 0.30) - setmodel(self, "models/onslaught/generator_dmg7.md3"); - else if(self.health < self.max_health * 0.40) - setmodel(self, "models/onslaught/generator_dmg6.md3"); - else if(self.health < self.max_health * 0.50) - setmodel(self, "models/onslaught/generator_dmg5.md3"); - else if(self.health < self.max_health * 0.60) - setmodel(self, "models/onslaught/generator_dmg4.md3"); - else if(self.health < self.max_health * 0.70) - setmodel(self, "models/onslaught/generator_dmg3.md3"); - else if(self.health < self.max_health * 0.80) - setmodel(self, "models/onslaught/generator_dmg2.md3"); - else if(self.health < self.max_health * 0.90) - setmodel(self, "models/onslaught/generator_dmg1.md3"); - setsize(self, '-52 -52 -14', '52 52 75'); + if(IS_PLAYER(self.owner.ons_toucher)) + { + Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_ONSLAUGHT_CAPTURE, self.owner.ons_toucher.netname, self.owner.message); + Send_Notification(NOTIF_ALL_EXCEPT, self.owner.ons_toucher, MSG_CENTER, APP_TEAM_ENT_4(self.owner.ons_toucher, CENTER_ONS_CAPTURE_), self.owner.message); + Send_Notification(NOTIF_ONE, self.owner.ons_toucher, MSG_CENTER, CENTER_ONS_CAPTURE, self.owner.message); + PlayerScore_Add(self.owner.ons_toucher, SP_ONS_CAPS, 1); + PlayerTeamScore_AddScore(self.owner.ons_toucher, 10); + } + + self.owner.ons_toucher = world; - // Throw some flaming gibs on damage, more damage = more chance for gib - if(random() < damage/220) - { - sound(self, CH_TRIGGER, "weapons/rocket_impact.wav", VOL_BASE, ATTEN_NORM); - i = random(); - if(i < 0.3) - ons_throwgib(hitloc + '0 0 20', force * -1, "models/onslaught/gen_gib1.md3", 5, true); - else if(i > 0.7) - ons_throwgib(hitloc + '0 0 20', force * -1, "models/onslaught/gen_gib2.md3", 5, true); - else - ons_throwgib(hitloc + '0 0 20', force * -1, "models/onslaught/gen_gib3.md3", 5, true); - } - else - { - // particles on every hit - pointparticles(particleeffectnum("sparks"), hitloc, force * -1, 1); + onslaught_updatelinks(); - //sound on every hit - if (random() < 0.5) - sound(self, CH_TRIGGER, "onslaught/ons_hit1.wav", VOL_BASE, ATTEN_NORM); - else - sound(self, CH_TRIGGER, "onslaught/ons_hit2.wav", VOL_BASE, ATTEN_NORM); + // Use targets now (somebody make sure this is in the right place..) + oself = self; + self = self.owner; + activator = self; + SUB_UseTargets (); + self = oself; + + self.SendFlags |= CPSF_SETUP; } + - //throw some gibs on damage - if(random() < damage/200+0.2) - if(random() < 0.5) - ons_throwgib(hitloc + '0 0 20', randomvec()*360, "models/onslaught/gen_gib1.md3", 5, false); + if(self.owner.model != "models/onslaught/controlpoint_pad2.md3") + setmodel_fixsize(self.owner, "models/onslaught/controlpoint_pad2.md3"); + + if(random() < 0.9 - self.health / self.max_health) + Send_Effect(EFFECT_RAGE, self.origin + 10 * randomvec(), '0 0 -1', 1); } -// update links after a delay -void onslaught_generator_delayed() +void ons_ControlPoint_Icon_Spawn(entity cp, entity player) { - onslaught_updatelinks(); - // now begin normal thinking - self.think = onslaught_generator_think; - self.nextthink = time; -} + entity e = spawn(); + + setsize(e, CPICON_MIN, CPICON_MAX); + setorigin(e, cp.origin + CPICON_OFFSET); + + e.classname = "onslaught_controlpoint_icon"; + e.owner = cp; + e.max_health = autocvar_g_onslaught_cp_health; + e.health = autocvar_g_onslaught_cp_buildhealth; + e.solid = SOLID_NOT; + e.takedamage = DAMAGE_AIM; - e.bot_attack = TRUE; ++ e.bot_attack = true; + e.event_damage = ons_ControlPoint_Icon_Damage; + e.team = player.team; + e.colormap = 1024 + (e.team - 1) * 17; + e.count = (e.max_health - e.health) * ONS_CP_THINKRATE / autocvar_g_onslaught_cp_buildtime; // how long it takes to build + + sound(e, CH_TRIGGER, "onslaught/controlpoint_build.wav", VOL_BASE, ATTEN_NORM); + + cp.goalentity = e; + cp.team = e.team; + cp.colormap = e.colormap; -string onslaught_generator_waypointsprite_for_team(entity e, float t) -{ - if(t == e.team) + float eff_team; + switch(player.team) { - if(e.team == NUM_TEAM_1) - return "ons-gen-red"; - else if(e.team == NUM_TEAM_2) - return "ons-gen-blue"; + case NUM_TEAM_1: eff_team = EFFECT_FLAG_RED_TOUCH; break; + case NUM_TEAM_2: eff_team = EFFECT_FLAG_BLUE_TOUCH; break; + case NUM_TEAM_3: eff_team = EFFECT_FLAG_YELLOW_TOUCH; break; + case NUM_TEAM_4: eff_team = EFFECT_FLAG_PINK_TOUCH; break; + default: eff_team = EFFECT_FLAG_NEUTRAL_TOUCH; break; } - if(e.isshielded) - return "ons-gen-shielded"; - if(e.team == NUM_TEAM_1) - return "ons-gen-red"; - else if(e.team == NUM_TEAM_2) - return "ons-gen-blue"; - return ""; -} -void onslaught_generator_updatesprite(entity e) -{ - string s1, s2, s3; - s1 = onslaught_generator_waypointsprite_for_team(e, NUM_TEAM_1); - s2 = onslaught_generator_waypointsprite_for_team(e, NUM_TEAM_2); - s3 = onslaught_generator_waypointsprite_for_team(e, -1); - WaypointSprite_UpdateSprites(e.sprite, s1, s2, s3); + Send_Effect(eff_team, self.owner.origin, '0 0 0', 1); - if(e.lastteam != e.team + 2 || e.lastshielded != e.isshielded) - { - e.lastteam = e.team + 2; - e.lastshielded = e.isshielded; - if(e.lastshielded) - { - if(e.team == NUM_TEAM_1 || e.team == NUM_TEAM_2) - WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_GENERATOR, 0.5 * colormapPaletteColor(e.team - 1, false)); - else - WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_GENERATOR, '0.5 0.5 0.5'); - } - else - { - if(e.team == NUM_TEAM_1 || e.team == NUM_TEAM_2) - WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_GENERATOR, colormapPaletteColor(e.team - 1, false)); - else - WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_GENERATOR, '0.75 0.75 0.75'); - } - WaypointSprite_Ping(e.sprite); - } + WaypointSprite_UpdateBuildFinished(cp.sprite, time + (e.max_health - e.health) / (e.count / ONS_CP_THINKRATE)); + WaypointSprite_UpdateRule(cp.sprite,cp.team,SPRITERULE_TEAMPLAY); + cp.sprite.SendFlags |= 16; + + onslaught_controlpoint_icon_link(e, ons_ControlPoint_Icon_BuildThink); } -string onslaught_controlpoint_waypointsprite_for_team(entity e, float t) +string ons_ControlPoint_Waypoint(entity e) { float a; - if(t != -1) + if(e.team) { - a = onslaught_controlpoint_attackable(e, t); - if(a == 3 || a == 4) // ATTACK/TOUCH THIS ONE NOW - { - if(e.team == NUM_TEAM_1) - return "ons-cp-atck-red"; - else if(e.team == NUM_TEAM_2) - return "ons-cp-atck-blue"; - else - return "ons-cp-atck-neut"; - } - else if(a == -2) // DEFEND THIS ONE NOW - { - if(e.team == NUM_TEAM_1) - return "ons-cp-dfnd-red"; - else if(e.team == NUM_TEAM_2) - return "ons-cp-dfnd-blue"; - } - else if(e.team == t || a == -1 || a == 1) // own point, or fire at it - { - if(e.team == NUM_TEAM_1) - return "ons-cp-red"; - else if(e.team == NUM_TEAM_2) - return "ons-cp-blue"; - } - else if(a == 2) // touch it - return "ons-cp-neut"; + a = ons_ControlPoint_Attackable(e, e.team); + + if(a == -2) { return "ons-cp-dfnd"; } // defend now + if(a == -1 || a == 1 || a == 2) { return "ons-cp"; } // touch + if(a == 3 || a == 4) { return "ons-cp-atck"; } // attack } else - { - if(e.team == NUM_TEAM_1) - return "ons-cp-red"; - else if(e.team == NUM_TEAM_2) - return "ons-cp-blue"; - else - return "ons-cp-neut"; - } + return "ons-cp"; + return ""; } @@@ -645,15 -818,15 +647,15 @@@ void ons_ControlPoint_UpdateSprite(enti } if(e.lastshielded) { - if(e.team == NUM_TEAM_1 || e.team == NUM_TEAM_2) + if(e.team) - WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_CONTROLPOINT, 0.5 * colormapPaletteColor(e.team - 1, FALSE)); + WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_CONTROLPOINT, 0.5 * colormapPaletteColor(e.team - 1, false)); else WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_CONTROLPOINT, '0.5 0.5 0.5'); } else { - if(e.team == NUM_TEAM_1 || e.team == NUM_TEAM_2) + if(e.team) - WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_CONTROLPOINT, colormapPaletteColor(e.team - 1, FALSE)); + WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_CONTROLPOINT, colormapPaletteColor(e.team - 1, false)); else WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_CONTROLPOINT, '0.75 0.75 0.75'); } @@@ -665,1487 -838,855 +667,1488 @@@ } } -void onslaught_generator_reset() +void ons_ControlPoint_Touch() { - self.team = self.team_saved; - self.lasthealth = self.max_health = self.health = autocvar_g_onslaught_gen_health; - self.takedamage = DAMAGE_AIM; - self.bot_attack = true; - self.iscaptured = true; - self.islinked = true; - self.isshielded = true; - self.enemy.solid = SOLID_NOT; - self.think = onslaught_generator_delayed; - self.nextthink = time + 0.2; - setmodel(self, "models/onslaught/generator.md3"); - setsize(self, '-52 -52 -14', '52 52 75'); - - if(!self.noalign) - { - setorigin(self, self.origin + '0 0 20'); - droptofloor(); + entity toucher = other; + float attackable; + + if(IS_VEHICLE(toucher) && toucher.owner) + if(autocvar_g_onslaught_allow_vehicle_touch) + toucher = toucher.owner; + else + return; + + if(!IS_PLAYER(toucher)) { return; } + if(toucher.frozen) { return; } + if(toucher.deadflag != DEAD_NO) { return; } + + if ( SAME_TEAM(self,toucher) ) + if ( self.iscaptured ) + { + if(time <= toucher.teleport_antispam) + Send_Notification(NOTIF_ONE, toucher, MSG_CENTER, CENTER_ONS_TELEPORT_ANTISPAM, rint(toucher.teleport_antispam - time)); + else + Send_Notification(NOTIF_ONE, toucher, MSG_CENTER, CENTER_ONS_TELEPORT); } + + attackable = ons_ControlPoint_Attackable(self, toucher.team); + if(attackable != 2 && attackable != 4) + return; + // we've verified that this player has a legitimate claim to this point, + // so start building the captured point icon (which only captures this + // point if it successfully builds without being destroyed first) + ons_ControlPoint_Icon_Spawn(self, toucher); + + self.ons_toucher = toucher; - WaypointSprite_UpdateMaxHealth(self.sprite, self.max_health); - WaypointSprite_UpdateHealth(self.sprite, self.health); + onslaught_updatelinks(); } -/*QUAKED spawnfunc_onslaught_generator (0 .5 .8) (-32 -32 -24) (32 32 64) - Base generator. - - spawnfunc_onslaught_link entities can target this. +void ons_ControlPoint_Think() +{ + self.nextthink = time + ONS_CP_THINKRATE; + CSQCMODEL_AUTOUPDATE(); +} -keys: -"team" - team that owns this generator (5 = red, 14 = blue, etc), MUST BE SET. -"targetname" - name that spawnfunc_onslaught_link entities will use to target this. - */ -void spawnfunc_onslaught_generator() +void ons_ControlPoint_Reset() { - if (!g_onslaught) - { - remove(self); - return; - } + if(self.goalentity) + remove(self.goalentity); - //entity e; - precache_model("models/onslaught/generator.md3"); - precache_model("models/onslaught/generator_shield.md3"); - precache_model("models/onslaught/generator_dmg1.md3"); - precache_model("models/onslaught/generator_dmg2.md3"); - precache_model("models/onslaught/generator_dmg3.md3"); - precache_model("models/onslaught/generator_dmg4.md3"); - precache_model("models/onslaught/generator_dmg5.md3"); - precache_model("models/onslaught/generator_dmg6.md3"); - precache_model("models/onslaught/generator_dmg7.md3"); - precache_model("models/onslaught/generator_dmg8.md3"); - precache_model("models/onslaught/generator_dmg9.md3"); - precache_model("models/onslaught/generator_dead.md3"); - precache_model("models/onslaught/shockwave.md3"); - precache_model("models/onslaught/shockwavetransring.md3"); - precache_model("models/onslaught/gen_gib1.md3"); - precache_model("models/onslaught/gen_gib2.md3"); - precache_model("models/onslaught/gen_gib3.md3"); - precache_model("models/onslaught/ons_ray.md3"); - precache_sound("onslaught/generator_decay.wav"); - precache_sound("weapons/grenade_impact.wav"); - precache_sound("weapons/rocket_impact.wav"); - precache_sound("onslaught/generator_underattack.wav"); - precache_sound("onslaught/shockwave.wav"); - precache_sound("onslaught/ons_hit1.wav"); - precache_sound("onslaught/ons_hit2.wav"); - precache_sound("onslaught/electricity_explode.wav"); - if (!self.team) - objerror("team must be set"); + self.goalentity = world; + self.team = 0; + self.colormap = 1024; - self.iscaptured = FALSE; - self.islinked = FALSE; - self.isshielded = TRUE; ++ self.iscaptured = false; ++ self.islinked = false; ++ self.isshielded = true; + self.think = ons_ControlPoint_Think; + self.ons_toucher = world; + self.nextthink = time + ONS_CP_THINKRATE; + setmodel_fixsize(self, "models/onslaught/controlpoint_pad.md3"); - if(self.team == NUM_TEAM_1) - ons_red_generator = self; + WaypointSprite_UpdateMaxHealth(self.sprite, 0); + WaypointSprite_UpdateRule(self.sprite,self.team,SPRITERULE_TEAMPLAY); - if(self.team == NUM_TEAM_2) - ons_blue_generator = self; + onslaught_updatelinks(); - self.team_saved = self.team; - self.colormap = 1024 + (self.team - 1) * 17; - self.solid = SOLID_BBOX; - self.movetype = MOVETYPE_NONE; - self.lasthealth = self.max_health = self.health = autocvar_g_onslaught_gen_health; - setmodel(self, "models/onslaught/generator.md3"); - setsize(self, '-52 -52 -14', '52 52 75'); - setorigin(self, self.origin); - self.takedamage = DAMAGE_AIM; - self.bot_attack = true; - self.event_damage = onslaught_generator_damage; - self.iscaptured = true; - self.islinked = true; - self.isshielded = true; - // helper entity that create fx when generator is damaged - onslaught_generator_damage_spawn(self); - // spawn shield model which indicates whether this can be damaged - self.enemy = spawn(); - setattachment(self.enemy , self, ""); - self.enemy.classname = "onslaught_generator_shield"; - self.enemy.solid = SOLID_NOT; - self.enemy.movetype = MOVETYPE_NONE; - self.enemy.effects = EF_ADDITIVE; - setmodel(self.enemy, "models/onslaught/generator_shield.md3"); - //setorigin(e, self.origin); - self.enemy.colormap = self.colormap; - self.enemy.team = self.team; - //self.think = onslaught_generator_delayed; - //self.nextthink = time + 0.2; - InitializeEntity(self, onslaught_generator_delayed, INITPRIO_LAST); - - WaypointSprite_SpawnFixed(string_null, self.origin + '0 0 128', self, sprite, RADARICON_NONE, '0 0 0'); - WaypointSprite_UpdateRule(self.sprite, NUM_TEAM_2, SPRITERULE_TEAMPLAY); - WaypointSprite_UpdateMaxHealth(self.sprite, self.max_health); - WaypointSprite_UpdateHealth(self.sprite, self.health); + activator = self; + SUB_UseTargets(); // to reset the structures, playerspawns etc. - waypoint_spawnforitem(self); + CSQCMODEL_AUTOUPDATE(); +} +void ons_DelayedControlPoint_Setup(void) +{ onslaught_updatelinks(); + + // captureshield setup - ons_CaptureShield_Spawn(self, FALSE); ++ ons_CaptureShield_Spawn(self, false); + + CSQCMODEL_AUTOINIT(); +} - self.reset = onslaught_generator_reset; +void ons_ControlPoint_Setup(entity cp) +{ + // declarations + self = cp; // for later usage with droptofloor() + + // main setup + cp.ons_worldcpnext = ons_worldcplist; // link control point into ons_worldcplist + ons_worldcplist = cp; + + cp.netname = "Control point"; + cp.team = 0; + cp.solid = SOLID_BBOX; + cp.movetype = MOVETYPE_NONE; + cp.touch = ons_ControlPoint_Touch; + cp.think = ons_ControlPoint_Think; + cp.nextthink = time + ONS_CP_THINKRATE; + cp.reset = ons_ControlPoint_Reset; + cp.colormap = 1024; - cp.iscaptured = FALSE; - cp.islinked = FALSE; - cp.isshielded = TRUE; ++ cp.iscaptured = false; ++ cp.islinked = false; ++ cp.isshielded = true; + + if(cp.message == "") { cp.message = "a"; } + + // precache - TODO: clean up! + precache_model("models/onslaught/controlpoint_pad.md3"); + precache_model("models/onslaught/controlpoint_pad2.md3"); + precache_model("models/onslaught/controlpoint_shield.md3"); + precache_model("models/onslaught/controlpoint_icon.md3"); + precache_model("models/onslaught/controlpoint_icon_dmg1.md3"); + precache_model("models/onslaught/controlpoint_icon_dmg2.md3"); + precache_model("models/onslaught/controlpoint_icon_dmg3.md3"); + precache_model("models/onslaught/controlpoint_icon_gib1.md3"); + precache_model("models/onslaught/controlpoint_icon_gib2.md3"); + precache_model("models/onslaught/controlpoint_icon_gib4.md3"); + precache_sound("onslaught/controlpoint_build.wav"); + precache_sound("onslaught/controlpoint_built.wav"); + precache_sound(W_Sound("grenade_impact")); + precache_sound("onslaught/damageblockedbyshield.wav"); + precache_sound("onslaught/controlpoint_underattack.wav"); + precache_sound("onslaught/ons_spark1.wav"); + precache_sound("onslaught/ons_spark2.wav"); + + // appearence + setmodel_fixsize(cp, "models/onslaught/controlpoint_pad.md3"); + + // control point placement + if((cp.spawnflags & 1) || cp.noalign) // don't drop to floor, just stay at fixed location + { - cp.noalign = TRUE; ++ cp.noalign = true; + cp.movetype = MOVETYPE_NONE; + } + else // drop to floor, automatically find a platform and set that as spawn origin + { + setorigin(cp, cp.origin + '0 0 20'); - cp.noalign = FALSE; ++ cp.noalign = false; + self = cp; + droptofloor(); + cp.movetype = MOVETYPE_TOSS; + } + + // waypointsprites + WaypointSprite_SpawnFixed(string_null, self.origin + CPGEN_WAYPOINT_OFFSET, self, sprite, RADARICON_NONE, '0 0 0'); + WaypointSprite_UpdateRule(self.sprite, self.team, SPRITERULE_TEAMPLAY); + + InitializeEntity(cp, ons_DelayedControlPoint_Setup, INITPRIO_SETLOCATION); } -.float waslinked; -.float cp_bob_spd; -.vector cp_origin, cp_bob_origin, cp_bob_dmg; -float ons_notification_time_team1; -float ons_notification_time_team2; +// ========================= +// Main Generator Functions +// ========================= -void onslaught_controlpoint_icon_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force) +string ons_Generator_Waypoint(entity e) { - entity oself; - float nag; + if(e.isshielded) + return "ons-gen-shielded"; + return "ons-gen"; +} - if (damage <= 0) - return; - if (self.owner.isshielded) - { - // this is protected by a shield, so ignore the damage - if (time > self.pain_finished) - if (IS_PLAYER(attacker)) - { - play2(attacker, "onslaught/damageblockedbyshield.wav"); - self.pain_finished = time + 1; - } - return; - } +void ons_Generator_UpdateSprite(entity e) +{ + string s1; + s1 = ons_Generator_Waypoint(e); + WaypointSprite_UpdateSprites(e.sprite, s1, s1, s1); - if (IS_PLAYER(attacker)) + if(e.lastteam != e.team + 2 || e.lastshielded != e.isshielded) { - nag = false; - if(self.team == NUM_TEAM_1) + e.lastteam = e.team + 2; + e.lastshielded = e.isshielded; + if(e.lastshielded) { - if(time - ons_notification_time_team1 > 10) - { - nag = true; - ons_notification_time_team1 = time; - } + if(e.team) - WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_GENERATOR, 0.5 * colormapPaletteColor(e.team - 1, FALSE)); ++ WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_GENERATOR, 0.5 * colormapPaletteColor(e.team - 1, false)); + else + WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_GENERATOR, '0.5 0.5 0.5'); } - else if(self.team == NUM_TEAM_2) + else { - if(time - ons_notification_time_team2 > 10) - { - nag = true; - ons_notification_time_team2 = time; - } + if(e.team) - WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_GENERATOR, colormapPaletteColor(e.team - 1, FALSE)); ++ WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_GENERATOR, colormapPaletteColor(e.team - 1, false)); + else + WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_GENERATOR, '0.75 0.75 0.75'); } - else - nag = true; - - if(nag) - play2team(self.team, "onslaught/controlpoint_underattack.wav"); + WaypointSprite_Ping(e.sprite); } +} - self.health = self.health - damage; - if(self.owner.iscaptured) - WaypointSprite_UpdateHealth(self.owner.sprite, self.health); - else - WaypointSprite_UpdateBuildFinished(self.owner.sprite, time + (self.max_health - self.health) / (self.count / sys_frametime)); - self.pain_finished = time + 1; - self.punchangle = (2 * randomvec() - '1 1 1') * 45; - self.cp_bob_dmg_z = (2 * random() - 1) * 15; - // colormod flash when shot - self.colormod = '2 2 2'; - // particles on every hit - pointparticles(particleeffectnum("sparks"), hitloc, force*-1, 1); - //sound on every hit - if (random() < 0.5) - sound(self, CH_TRIGGER, "onslaught/ons_hit1.wav", VOL_BASE+0.3, ATTEN_NORM); - else - sound(self, CH_TRIGGER, "onslaught/ons_hit2.wav", VOL_BASE+0.3, ATTEN_NORM); +void ons_GeneratorDamage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force) +{ + if(damage <= 0) { return; } + if(warmup_stage || gameover) { return; } + if(!round_handler_IsRoundStarted()) { return; } - if (self.health < 0) + if (attacker != self) { - sound(self, CH_TRIGGER, "weapons/grenade_impact.wav", VOL_BASE, ATTEN_NORM); - pointparticles(particleeffectnum("rocket_explode"), self.origin, '0 0 0', 1); + if (self.isshielded) { - string t; - t = Team_ColoredFullName(attacker.team); - bprint(Team_ColoredFullName(self.team), " ", self.message, " control point destroyed by ", t, "\n"); - ons_throwgib(self.origin, (2 * randomvec() - '1 1 1') * 25, "models/onslaught/controlpoint_icon_gib1.md3", 3, false); - ons_throwgib(self.origin, (2 * randomvec() - '1 1 1') * 45, "models/onslaught/controlpoint_icon_gib2.md3", 3, false); - ons_throwgib(self.origin, (2 * randomvec() - '1 1 1') * 45, "models/onslaught/controlpoint_icon_gib2.md3", 3, false); - ons_throwgib(self.origin, (2 * randomvec() - '1 1 1') * 75, "models/onslaught/controlpoint_icon_gib4.md3", 3, false); - ons_throwgib(self.origin, (2 * randomvec() - '1 1 1') * 75, "models/onslaught/controlpoint_icon_gib4.md3", 3, false); - ons_throwgib(self.origin, (2 * randomvec() - '1 1 1') * 75, "models/onslaught/controlpoint_icon_gib4.md3", 3, false); - ons_throwgib(self.origin, (2 * randomvec() - '1 1 1') * 75, "models/onslaught/controlpoint_icon_gib4.md3", 3, false); + // this is protected by a shield, so ignore the damage + if (time > self.pain_finished) + if (IS_PLAYER(attacker)) + { + play2(attacker, "onslaught/damageblockedbyshield.wav"); + attacker.typehitsound += 1; + self.pain_finished = time + 1; + } + return; } - self.owner.goalentity = world; - self.owner.islinked = false; - self.owner.iscaptured = false; - self.owner.team = 0; - self.owner.colormap = 1024; + if (time > self.pain_finished) + { + self.pain_finished = time + 10; + entity head; + FOR_EACH_REALPLAYER(head) if(SAME_TEAM(head, self)) { Send_Notification(NOTIF_ONE, head, MSG_CENTER, CENTER_GENERATOR_UNDERATTACK); } + play2team(self.team, "onslaught/generator_underattack.wav"); + } + } + self.health = self.health - damage; + WaypointSprite_UpdateHealth(self.sprite, self.health); + // choose an animation frame based on health + self.frame = 10 * bound(0, (1 - self.health / self.max_health), 1); + // see if the generator is still functional, or dying + if (self.health > 0) + { + self.lasthealth = self.health; + } + else + { + if (attacker == self) + Send_Notification(NOTIF_ALL, world, MSG_INFO, APP_TEAM_NUM_4(self.team, INFO_ONSLAUGHT_GENDESTROYED_OVERTIME_)); + else + { + Send_Notification(NOTIF_ALL, world, MSG_INFO, APP_TEAM_NUM_4(self.team, INFO_ONSLAUGHT_GENDESTROYED_)); + PlayerScore_Add(attacker, SP_SCORE, 100); + } - self.iscaptured = FALSE; - self.islinked = FALSE; - self.isshielded = FALSE; ++ self.iscaptured = false; ++ self.islinked = false; ++ self.isshielded = false; + self.takedamage = DAMAGE_NO; // can't be hurt anymore + self.event_damage = func_null; // won't do anything if hurt + self.count = 0; // reset counter + self.think = func_null; + self.nextthink = 0; + //self.think(); // do the first explosion now - WaypointSprite_UpdateMaxHealth(self.owner.sprite, 0); + WaypointSprite_UpdateMaxHealth(self.sprite, 0); + WaypointSprite_Ping(self.sprite); + //WaypointSprite_Kill(self.sprite); // can't do this yet, code too poor onslaught_updatelinks(); + } - // Use targets now (somebody make sure this is in the right place..) - oself = self; - self = self.owner; - activator = self; - SUB_UseTargets (); - self = oself; - - - self.owner.waslinked = self.owner.islinked; - if(self.owner.model != "models/onslaught/controlpoint_pad.md3") - setmodel(self.owner, "models/onslaught/controlpoint_pad.md3"); - //setsize(self, '-32 -32 0', '32 32 8'); + // Throw some flaming gibs on damage, more damage = more chance for gib + if(random() < damage/220) + { + sound(self, CH_TRIGGER, W_Sound("rocket_impact"), VOL_BASE, ATTEN_NORM); + } + else + { + // particles on every hit + Send_Effect(EFFECT_SPARKS, hitloc, force * -1, 1); - remove(self); + //sound on every hit + if (random() < 0.5) + sound(self, CH_TRIGGER, "onslaught/ons_hit1.wav", VOL_BASE, ATTEN_NORM); + else + sound(self, CH_TRIGGER, "onslaught/ons_hit2.wav", VOL_BASE, ATTEN_NORM); } + + self.SendFlags |= GSF_STATUS; } -void onslaught_controlpoint_icon_think() +void ons_GeneratorThink() { - entity oself; - self.nextthink = time + sys_frametime; - - if(autocvar_g_onslaught_cp_proxydecap) + entity e; + self.nextthink = time + GEN_THINKRATE; + if (!gameover) { - float _enemy_count = 0; - float _friendly_count = 0; - float _dist; - entity _player; - - FOR_EACH_PLAYER(_player) + if(!self.isshielded && self.wait < time) { - if(!_player.deadflag) + self.wait = time + 5; + FOR_EACH_REALPLAYER(e) { - _dist = vlen(_player.origin - self.origin); - if(_dist < autocvar_g_onslaught_cp_proxydecap_distance) - { - if(_player.team == self.team) - ++_friendly_count; - else - ++_enemy_count; + if(SAME_TEAM(e, self)) + { + Send_Notification(NOTIF_ONE, e, MSG_CENTER, CENTER_ONS_NOTSHIELDED_TEAM); + soundto(MSG_ONE, e, CHAN_AUTO, "kh/alarm.wav", VOL_BASE, ATTEN_NONE); // FIXME: unique sound? } + else + Send_Notification(NOTIF_ONE, e, MSG_CENTER, APP_TEAM_NUM_4(self.team, CENTER_ONS_NOTSHIELDED_)); } } + } +} - _friendly_count = _friendly_count * (autocvar_g_onslaught_cp_proxydecap_dps * sys_frametime); - _enemy_count = _enemy_count * (autocvar_g_onslaught_cp_proxydecap_dps * sys_frametime); +void ons_GeneratorReset() +{ + self.team = self.team_saved; + self.lasthealth = self.max_health = self.health = autocvar_g_onslaught_gen_health; + self.takedamage = DAMAGE_AIM; - self.bot_attack = TRUE; - self.iscaptured = TRUE; - self.islinked = TRUE; - self.isshielded = TRUE; ++ self.bot_attack = true; ++ self.iscaptured = true; ++ self.islinked = true; ++ self.isshielded = true; + self.event_damage = ons_GeneratorDamage; + self.think = ons_GeneratorThink; + self.nextthink = time + GEN_THINKRATE; + - Net_LinkEntity(self, FALSE, 0, generator_send); ++ Net_LinkEntity(self, false, 0, generator_send); + + self.SendFlags = GSF_SETUP; // just incase + self.SendFlags |= GSF_STATUS; - self.health = bound(0, self.health + (_friendly_count - _enemy_count), self.max_health); - if(self.health <= 0) - { - onslaught_controlpoint_icon_damage(self, self, 1, 0, self.origin, '0 0 0'); - return; - } - } + WaypointSprite_UpdateMaxHealth(self.sprite, self.max_health); + WaypointSprite_UpdateHealth(self.sprite, self.health); + WaypointSprite_UpdateRule(self.sprite,self.team,SPRITERULE_TEAMPLAY); + + onslaught_updatelinks(); +} - if (time > self.pain_finished + 5) +void ons_DelayedGeneratorSetup() +{ + // bot waypoints + waypoint_spawnforitem_force(self, self.origin); + self.nearestwaypointtimeout = 0; // activate waypointing again + self.bot_basewaypoint = self.nearestwaypoint; + + // captureshield setup - ons_CaptureShield_Spawn(self, TRUE); ++ ons_CaptureShield_Spawn(self, true); + + onslaught_updatelinks(); + - Net_LinkEntity(self, FALSE, 0, generator_send); ++ Net_LinkEntity(self, false, 0, generator_send); +} + + +void onslaught_generator_touch() +{ + if ( IS_PLAYER(other) ) + if ( SAME_TEAM(self,other) ) + if ( self.iscaptured ) { - if(self.health < self.max_health) - { - self.health = self.health + self.count; - if (self.health >= self.max_health) - self.health = self.max_health; - WaypointSprite_UpdateHealth(self.owner.sprite, self.health); - } + Send_Notification(NOTIF_ONE, other, MSG_CENTER, CENTER_ONS_TELEPORT); } - if (self.health < self.max_health * 0.25) - setmodel(self, "models/onslaught/controlpoint_icon_dmg3.md3"); - else if (self.health < self.max_health * 0.50) - setmodel(self, "models/onslaught/controlpoint_icon_dmg2.md3"); - else if (self.health < self.max_health * 0.75) - setmodel(self, "models/onslaught/controlpoint_icon_dmg1.md3"); - else if (self.health < self.max_health * 0.90) - setmodel(self, "models/onslaught/controlpoint_icon.md3"); - // colormod flash when shot - self.colormod = '1 1 1' * (2 - bound(0, (self.pain_finished - time) / 10, 1)); +} - if(self.owner.islinked != self.owner.waslinked) - { - // unteam the spawnpoint if needed - float t; - t = self.owner.team; - if(!self.owner.islinked) - self.owner.team = 0; +void ons_GeneratorSetup(entity gen) // called when spawning a generator entity on the map as a spawnfunc +{ + // declarations + float teamnumber = gen.team; + self = gen; // for later usage with droptofloor() + + // main setup + gen.ons_worldgeneratornext = ons_worldgeneratorlist; // link generator into ons_worldgeneratorlist + ons_worldgeneratorlist = gen; + + gen.netname = sprintf("%s generator", Team_ColoredFullName(teamnumber)); + gen.classname = "onslaught_generator"; + gen.solid = SOLID_BBOX; + gen.team_saved = teamnumber; + gen.movetype = MOVETYPE_NONE; + gen.lasthealth = gen.max_health = gen.health = autocvar_g_onslaught_gen_health; + gen.takedamage = DAMAGE_AIM; - gen.bot_attack = TRUE; ++ gen.bot_attack = true; + gen.event_damage = ons_GeneratorDamage; + gen.reset = ons_GeneratorReset; + gen.think = ons_GeneratorThink; + gen.nextthink = time + GEN_THINKRATE; - gen.iscaptured = TRUE; - gen.islinked = TRUE; - gen.isshielded = TRUE; ++ gen.iscaptured = true; ++ gen.islinked = true; ++ gen.isshielded = true; + gen.touch = onslaught_generator_touch; + + // precache - TODO: clean up! + precache_model("models/onslaught/generator_shield.md3"); + precache_model("models/onslaught/gen_gib1.md3"); + precache_model("models/onslaught/gen_gib2.md3"); + precache_model("models/onslaught/gen_gib3.md3"); + precache_sound("onslaught/generator_decay.wav"); + precache_sound(W_Sound("grenade_impact")); + precache_sound(W_Sound("rocket_impact")); + precache_sound("onslaught/generator_underattack.wav"); + precache_sound("onslaught/shockwave.wav"); + precache_sound("onslaught/ons_hit1.wav"); + precache_sound("onslaught/ons_hit2.wav"); + precache_sound("onslaught/generator_underattack.wav"); + + // appearence + // model handled by CSQC + setsize(gen, GENERATOR_MIN, GENERATOR_MAX); + setorigin(gen, (gen.origin + CPGEN_SPAWN_OFFSET)); + gen.colormap = 1024 + (teamnumber - 1) * 17; + + // generator placement + self = gen; + droptofloor(); + + // waypointsprites + WaypointSprite_SpawnFixed(string_null, self.origin + CPGEN_WAYPOINT_OFFSET, self, sprite, RADARICON_NONE, '0 0 0'); + WaypointSprite_UpdateRule(self.sprite, self.team, SPRITERULE_TEAMPLAY); + WaypointSprite_UpdateMaxHealth(self.sprite, self.max_health); + WaypointSprite_UpdateHealth(self.sprite, self.health); + + InitializeEntity(gen, ons_DelayedGeneratorSetup, INITPRIO_SETLOCATION); +} - oself = self; - self = self.owner; - activator = self; - SUB_UseTargets (); - self = oself; - self.owner.team = t; +// =============== +// Round Handler +// =============== - float total_generators, redowned, blueowned, yellowowned, pinkowned; - self.owner.waslinked = self.owner.islinked; ++float total_generators; +void Onslaught_count_generators() +{ + entity e; + total_generators = redowned = blueowned = yellowowned = pinkowned = 0; + for(e = ons_worldgeneratorlist; e; e = e.ons_worldgeneratornext) + { + ++total_generators; + redowned += (e.team == NUM_TEAM_1 && e.health > 0); + blueowned += (e.team == NUM_TEAM_2 && e.health > 0); + yellowowned += (e.team == NUM_TEAM_3 && e.health > 0); + pinkowned += (e.team == NUM_TEAM_4 && e.health > 0); } +} - float Onslaught_GetWinnerTeam() - if (self.punchangle.x > 0) ++int Onslaught_GetWinnerTeam() +{ + float winner_team = 0; + if(redowned > 0) + winner_team = NUM_TEAM_1; + if(blueowned > 0) { - self.punchangle_x = self.punchangle.x - 60 * sys_frametime; - if (self.punchangle.x < 0) - self.punchangle_x = 0; + if(winner_team) return 0; + winner_team = NUM_TEAM_2; } - else if (self.punchangle.x < 0) + if(yellowowned > 0) { - self.punchangle_x = self.punchangle.x + 60 * sys_frametime; - if (self.punchangle.x > 0) - self.punchangle_x = 0; + if(winner_team) return 0; + winner_team = NUM_TEAM_3; } - - if (self.punchangle.y > 0) + if(pinkowned > 0) { - self.punchangle_y = self.punchangle.y - 60 * sys_frametime; - if (self.punchangle.y < 0) - self.punchangle_y = 0; + if(winner_team) return 0; + winner_team = NUM_TEAM_4; } - else if (self.punchangle.y < 0) + if(winner_team) + return winner_team; + return -1; // no generators left? +} + +#define ONS_OWNED_GENERATORS() ((redowned > 0) + (blueowned > 0) + (yellowowned > 0) + (pinkowned > 0)) +#define ONS_OWNED_GENERATORS_OK() (ONS_OWNED_GENERATORS() > 1) +float Onslaught_CheckWinner() +{ + entity e; + + if ((autocvar_timelimit && time > game_starttime + autocvar_timelimit * 60) || (round_handler_GetEndTime() > 0 && round_handler_GetEndTime() - time <= 0)) { - ons_stalemate = TRUE; - self.punchangle_y = self.punchangle.y + 60 * sys_frametime; - if (self.punchangle.y > 0) - self.punchangle_y = 0; ++ ons_stalemate = true; + + if (!wpforenemy_announced) + { + Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_OVERTIME_CONTROLPOINT); + sound(world, CH_INFO, "onslaught/generator_decay.wav", VOL_BASE, ATTEN_NONE); + - wpforenemy_announced = TRUE; ++ wpforenemy_announced = true; + } + + entity tmp_entity; // temporary entity + float d; + for(tmp_entity = ons_worldgeneratorlist; tmp_entity; tmp_entity = tmp_entity.ons_worldgeneratornext) if(time >= tmp_entity.ons_overtime_damagedelay) + { + // tmp_entity.max_health / 300 gives 5 minutes of overtime. + // control points reduce the overtime duration. + d = 1; + for(e = ons_worldcplist; e; e = e.ons_worldcpnext) + { + if(DIFF_TEAM(e, tmp_entity)) + if(e.islinked) + d = d + 1; + } + + if(autocvar_g_campaign && autocvar__campaign_testrun) + d = d * tmp_entity.max_health; + else + d = d * tmp_entity.max_health / max(30, 60 * autocvar_timelimit_suddendeath); + + Damage(tmp_entity, tmp_entity, tmp_entity, d, DEATH_HURTTRIGGER, tmp_entity.origin, '0 0 0'); + + tmp_entity.sprite.SendFlags |= 16; + + tmp_entity.ons_overtime_damagedelay = time + 1; + } } - else { wpforenemy_announced = FALSE; ons_stalemate = FALSE; } ++ else { wpforenemy_announced = false; ons_stalemate = false; } + + Onslaught_count_generators(); + + if(ONS_OWNED_GENERATORS_OK()) + return 0; + - float winner_team = Onslaught_GetWinnerTeam(); ++ int winner_team = Onslaught_GetWinnerTeam(); - if (self.punchangle.z > 0) + if(winner_team > 0) { - self.punchangle_z = self.punchangle.z - 60 * sys_frametime; - if (self.punchangle.z < 0) - self.punchangle_z = 0; + Send_Notification(NOTIF_ALL, world, MSG_CENTER, APP_TEAM_NUM_4(winner_team, CENTER_ROUND_TEAM_WIN_)); + Send_Notification(NOTIF_ALL, world, MSG_INFO, APP_TEAM_NUM_4(winner_team, INFO_ROUND_TEAM_WIN_)); + TeamScore_AddToTeam(winner_team, ST_ONS_CAPS, +1); } - else if (self.punchangle.z < 0) + else if(winner_team == -1) { - self.punchangle_z = self.punchangle.z + 60 * sys_frametime; - if (self.punchangle.z > 0) - self.punchangle_z = 0; + Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_ROUND_TIED); + Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_ROUND_TIED); } + - ons_stalemate = FALSE; ++ ons_stalemate = false; - self.angles_x = self.punchangle.x; - self.angles_y = self.punchangle.y + self.mangle.y; - self.angles_z = self.punchangle.z; - self.mangle_y = self.mangle.y + 45 * sys_frametime; - - self.cp_bob_origin_z = 4 * PI * (1 - cos(self.cp_bob_spd)); - self.cp_bob_spd = self.cp_bob_spd + 1.875 * sys_frametime; - if(self.cp_bob_dmg.z > 0) - self.cp_bob_dmg_z = self.cp_bob_dmg.z - 3 * sys_frametime; - else - self.cp_bob_dmg_z = 0; - setorigin(self,self.cp_origin + self.cp_bob_origin + self.cp_bob_dmg); - - // damaged fx - if(random() < 0.6 - self.health / self.max_health) + play2all(sprintf("ctf/%s_capture.wav", Static_Team_ColorName_Lower(winner_team))); + + round_handler_Init(7, autocvar_g_onslaught_warmup, autocvar_g_onslaught_round_timelimit); + + FOR_EACH_PLAYER(e) { - e.ons_roundlost = TRUE; - e.player_blocked = TRUE; - pointparticles(particleeffectnum("electricity_sparks"), self.origin + randompos('-10 -10 -20', '10 10 20'), '0 0 0', 1); - - if(random() > 0.8) - sound(self, CH_PAIN, "onslaught/ons_spark1.wav", VOL_BASE, ATTEN_NORM); - else if (random() > 0.5) - sound(self, CH_PAIN, "onslaught/ons_spark2.wav", VOL_BASE, ATTEN_NORM); ++ e.ons_roundlost = true; ++ e.player_blocked = true; } + - nades_Clear(world, TRUE); ++ nades_Clear(world, true); + + return 1; } - float Onslaught_CheckPlayers() -void onslaught_controlpoint_icon_buildthink() ++bool Onslaught_CheckPlayers() { - return 1; - entity oself; - float a; ++ return true; +} - self.nextthink = time + sys_frametime; +void Onslaught_RoundStart() +{ + entity tmp_entity; - FOR_EACH_PLAYER(tmp_entity) { tmp_entity.player_blocked = FALSE; } ++ FOR_EACH_PLAYER(tmp_entity) { tmp_entity.player_blocked = false; } - // only do this if there is power - a = onslaught_controlpoint_can_be_linked(self.owner, self.owner.team); - if(!a) - return; + for(tmp_entity = ons_worldcplist; tmp_entity; tmp_entity = tmp_entity.ons_worldcpnext) + tmp_entity.sprite.SendFlags |= 16; - self.health = self.health + self.count; + for(tmp_entity = ons_worldgeneratorlist; tmp_entity; tmp_entity = tmp_entity.ons_worldgeneratornext) + tmp_entity.sprite.SendFlags |= 16; +} - if (self.health >= self.max_health) - { - self.health = self.max_health; - self.count = autocvar_g_onslaught_cp_regen * sys_frametime; // slow repair rate from now on - self.think = onslaught_controlpoint_icon_think; - sound(self, CH_TRIGGER, "onslaught/controlpoint_built.wav", VOL_BASE, ATTEN_NORM); - bprint(Team_ColoredFullName(self.team), " captured ", self.owner.message, " control point\n"); - self.owner.iscaptured = true; - WaypointSprite_UpdateMaxHealth(self.owner.sprite, self.max_health); - WaypointSprite_UpdateHealth(self.owner.sprite, self.health); +// ================ +// Bot player logic +// ================ - onslaught_updatelinks(); +// NOTE: LEGACY CODE, needs to be re-written! - // Use targets now (somebody make sure this is in the right place..) - oself = self; - self = self.owner; - activator = self; - SUB_UseTargets (); - self = oself; - self.cp_origin = self.origin; - self.cp_bob_origin = '0 0 0.1'; - self.cp_bob_spd = 0; +void havocbot_goalrating_ons_offenseitems(float ratingscale, vector org, float sradius) +{ + entity head; - float t, i, c, needarmor = FALSE, needweapons = FALSE; ++ float t, i, c, needarmor = false, needweapons = false; + + // Needs armor/health? + if(self.health<100) - needarmor = TRUE; ++ needarmor = true; + + // Needs weapons? + c = 0; + for(i = WEP_FIRST; i <= WEP_LAST ; ++i) + { + // Find weapon + if(self.weapons & WepSet_FromWeapon(i)) + if(++c>=4) + break; } - self.alpha = self.health / self.max_health; - // colormod flash when shot - self.colormod = '1 1 1' * (2 - bound(0, (self.pain_finished - time) / 10, 1)); - if(self.owner.model != "models/onslaught/controlpoint_pad2.md3") - setmodel(self.owner, "models/onslaught/controlpoint_pad2.md3"); - //setsize(self, '-32 -32 0', '32 32 8'); - if(random() < 0.9 - self.health / self.max_health) - pointparticles(particleeffectnum("rage"), self.origin + 10 * randomvec(), '0 0 -1', 1); -} + if(c<4) - needweapons = TRUE; ++ needweapons = true; + if(!needweapons && !needarmor) + return; + ons_debug(strcat(self.netname, " needs weapons ", ftos(needweapons) , "\n")); + ons_debug(strcat(self.netname, " needs armor ", ftos(needarmor) , "\n")); + // See what is around - head = findchainfloat(bot_pickup, TRUE); ++ head = findchainfloat(bot_pickup, true); + while (head) + { + // gather health and armor only + if (head.solid) + if ( ((head.health || head.armorvalue) && needarmor) || (head.weapons && needweapons ) ) + if (vlen(head.origin - org) < sradius) + { + t = head.bot_pickupevalfunc(self, head); + if (t > 0) + navigation_routerating(head, t * ratingscale, 500); + } + head = head.chain; + } +} -void onslaught_controlpoint_touch() +void havocbot_role_ons_setrole(entity bot, float role) { - entity e; - float a; - if (!IS_PLAYER(other)) - return; - a = onslaught_controlpoint_attackable(self, other.team); - if(a != 2 && a != 4) - return; - // we've verified that this player has a legitimate claim to this point, - // so start building the captured point icon (which only captures this - // point if it successfully builds without being destroyed first) - self.goalentity = e = spawn(); - e.classname = "onslaught_controlpoint_icon"; - e.owner = self; - e.max_health = autocvar_g_onslaught_cp_health; - e.health = autocvar_g_onslaught_cp_buildhealth; - e.solid = SOLID_BBOX; - e.movetype = MOVETYPE_NONE; - setmodel(e, "models/onslaught/controlpoint_icon.md3"); - setsize(e, '-32 -32 -32', '32 32 32'); - setorigin(e, self.origin + '0 0 96'); - e.takedamage = DAMAGE_AIM; - e.bot_attack = true; - e.event_damage = onslaught_controlpoint_icon_damage; - e.team = other.team; - e.colormap = 1024 + (e.team - 1) * 17; - e.think = onslaught_controlpoint_icon_buildthink; - e.nextthink = time + sys_frametime; - e.count = (e.max_health - e.health) * sys_frametime / autocvar_g_onslaught_cp_buildtime; // how long it takes to build - sound(e, CH_TRIGGER, "onslaught/controlpoint_build.wav", VOL_BASE, ATTEN_NORM); - self.team = e.team; - self.colormap = e.colormap; - WaypointSprite_UpdateBuildFinished(self.sprite, time + (e.max_health - e.health) / (e.count / sys_frametime)); - onslaught_updatelinks(); + ons_debug(strcat(bot.netname," switched to ")); + switch(role) + { + case HAVOCBOT_ONS_ROLE_DEFENSE: + ons_debug("defense"); + bot.havocbot_role = havocbot_role_ons_defense; + bot.havocbot_role_flags = HAVOCBOT_ONS_ROLE_DEFENSE; + bot.havocbot_role_timeout = 0; + break; + case HAVOCBOT_ONS_ROLE_ASSISTANT: + ons_debug("assistant"); + bot.havocbot_role = havocbot_role_ons_assistant; + bot.havocbot_role_flags = HAVOCBOT_ONS_ROLE_ASSISTANT; + bot.havocbot_role_timeout = 0; + break; + case HAVOCBOT_ONS_ROLE_OFFENSE: + ons_debug("offense"); + bot.havocbot_role = havocbot_role_ons_offense; + bot.havocbot_role_flags = HAVOCBOT_ONS_ROLE_OFFENSE; + bot.havocbot_role_timeout = 0; + break; + } + ons_debug("\n"); } -void onslaught_controlpoint_think() +float havocbot_ons_teamcount(entity bot, float role) { - self.nextthink = time; - CSQCMODEL_AUTOUPDATE(); + float c = 0; + entity head; + + FOR_EACH_PLAYER(head) + if(SAME_TEAM(head, self)) + if(head.havocbot_role_flags & role) + ++c; + + return c; } -void onslaught_controlpoint_reset() +void havocbot_goalrating_ons_controlpoints_attack(float ratingscale) { - if(self.goalentity && self.goalentity != world) - remove(self.goalentity); - self.goalentity = world; - self.team = 0; - self.colormap = 1024; - self.iscaptured = false; - self.islinked = false; - self.isshielded = true; - self.enemy.solid = SOLID_NOT; - self.enemy.colormap = self.colormap; - self.think = onslaught_controlpoint_think; - self.enemy.think = func_null; - self.nextthink = time; // don't like func_null :P - setmodel(self, "models/onslaught/controlpoint_pad.md3"); - //setsize(self, '-32 -32 0', '32 32 8'); + entity cp, cp1, cp2, best, pl, wp; + float radius, found, bestvalue, c; - WaypointSprite_UpdateMaxHealth(self.sprite, 0); + // Filter control points + for(cp2 = ons_worldcplist; cp2; cp2 = cp2.ons_worldcpnext) + { + cp2.wpcost = c = 0; - cp2.wpconsidered = FALSE; ++ cp2.wpconsidered = false; - onslaught_updatelinks(); + if(cp2.isshielded) + continue; - activator = self; - SUB_UseTargets(); // to reset the structures, playerspawns etc. + // Ignore owned controlpoints + if(!(cp2.isgenneighbor[self.team] || cp2.iscpneighbor[self.team])) + continue; - CSQCMODEL_AUTOUPDATE(); + // Count team mates interested in this control point + // (easier and cleaner than keeping counters per cp and teams) + FOR_EACH_PLAYER(pl) + if(SAME_TEAM(pl, self)) + if(pl.havocbot_role_flags & HAVOCBOT_ONS_ROLE_OFFENSE) + if(pl.havocbot_ons_target==cp2) + ++c; + + // NOTE: probably decrease the cost of attackable control points + cp2.wpcost = c; - cp2.wpconsidered = TRUE; ++ cp2.wpconsidered = true; + } + + // We'll consider only the best case + bestvalue = 99999999999; + cp = world; + for(cp1 = ons_worldcplist; cp1; cp1 = cp1.ons_worldcpnext) + { + if (!cp1.wpconsidered) + continue; + + if(cp1.wpcost self.havocbot_role_timeout) { - setorigin(self, self.origin + '0 0 20'); - droptofloor(); + havocbot_ons_reset_role(self); + return; } - self.touch = onslaught_controlpoint_touch; - self.team = 0; - self.colormap = 1024; - self.iscaptured = false; - self.islinked = false; - self.isshielded = true; - // spawn shield model which indicates whether this can be damaged - self.enemy = spawn(); - self.enemy.classname = "onslaught_controlpoint_shield"; - self.enemy.solid = SOLID_NOT; - self.enemy.movetype = MOVETYPE_NONE; - self.enemy.effects = EF_ADDITIVE; - setmodel(self.enemy , "models/onslaught/controlpoint_shield.md3"); + if(self.havocbot_attack_time>time) + return; + + if (self.bot_strategytime < time) + { + navigation_goalrating_start(); + havocbot_goalrating_enemyplayers(20000, self.origin, 650); + if(!havocbot_goalrating_ons_generator_attack(20000)) + havocbot_goalrating_ons_controlpoints_attack(20000); + havocbot_goalrating_ons_offenseitems(10000, self.origin, 10000); + navigation_goalrating_end(); + + self.bot_strategytime = time + autocvar_bot_ai_strategyinterval; + } +} + +void havocbot_role_ons_assistant() +{ + havocbot_ons_reset_role(self); +} + +void havocbot_role_ons_defense() +{ + havocbot_ons_reset_role(self); +} - setattachment(self.enemy , self, ""); - //setsize(e, '-32 -32 0', '32 32 128'); +void havocbot_ons_reset_role(entity bot) +{ + entity head; + float c; - //setorigin(e, self.origin); - self.enemy.colormap = self.colormap; + if(self.deadflag != DEAD_NO) + return; - waypoint_spawnforitem(self); + bot.havocbot_ons_target = world; - self.think = onslaught_controlpoint_think; - self.nextthink = time; + // TODO: Defend control points or generator if necessary - WaypointSprite_SpawnFixed(string_null, self.origin + '0 0 128', self, sprite, RADARICON_NONE, '0 0 0'); - WaypointSprite_UpdateRule(self.sprite, NUM_TEAM_2, SPRITERULE_TEAMPLAY); + // if there is only me on the team switch to offense + c = 0; + FOR_EACH_PLAYER(head) + if(SAME_TEAM(head, self)) + ++c; - onslaught_updatelinks(); + if(c==1) + { + havocbot_role_ons_setrole(bot, HAVOCBOT_ONS_ROLE_OFFENSE); + return; + } + + havocbot_role_ons_setrole(bot, HAVOCBOT_ONS_ROLE_OFFENSE); +} - self.reset = onslaught_controlpoint_reset; - CSQCMODEL_AUTOINIT(); +/* + * Find control point or generator owned by the same team self which is nearest to pos + * if max_dist is positive, only control points within this range will be considered + */ +entity ons_Nearest_ControlPoint(vector pos, float max_dist) +{ + entity tmp_entity, closest_target = world; + tmp_entity = findchain(classname, "onslaught_controlpoint"); + while(tmp_entity) + { + if(SAME_TEAM(tmp_entity, self)) + if(tmp_entity.iscaptured) + if(max_dist <= 0 || vlen(tmp_entity.origin - pos) <= max_dist) + if(vlen(tmp_entity.origin - pos) <= vlen(closest_target.origin - pos) || closest_target == world) + closest_target = tmp_entity; + tmp_entity = tmp_entity.chain; + } + tmp_entity = findchain(classname, "onslaught_generator"); + while(tmp_entity) + { + if(SAME_TEAM(tmp_entity, self)) + if(max_dist <= 0 || vlen(tmp_entity.origin - pos) < max_dist) + if(vlen(tmp_entity.origin - pos) <= vlen(closest_target.origin - pos) || closest_target == world) + closest_target = tmp_entity; + tmp_entity = tmp_entity.chain; + } + + return closest_target; } -float onslaught_link_send(entity to, float sendflags) +/* + * Find control point or generator owned by the same team self which is nearest to pos + * if max_dist is positive, only control points within this range will be considered + * This function only check distances on the XY plane, disregarding Z + */ +entity ons_Nearest_ControlPoint_2D(vector pos, float max_dist) { - WriteByte(MSG_ENTITY, ENT_CLIENT_RADARLINK); - WriteByte(MSG_ENTITY, sendflags); - if(sendflags & 1) + entity tmp_entity, closest_target = world; + vector delta; + float smallest_distance = 0, distance; + + tmp_entity = findchain(classname, "onslaught_controlpoint"); + while(tmp_entity) + { + delta = tmp_entity.origin - pos; + delta_z = 0; + distance = vlen(delta); + + if(SAME_TEAM(tmp_entity, self)) + if(tmp_entity.iscaptured) + if(max_dist <= 0 || distance <= max_dist) + if(closest_target == world || distance <= smallest_distance ) + { + closest_target = tmp_entity; + smallest_distance = distance; + } + + tmp_entity = tmp_entity.chain; + } + tmp_entity = findchain(classname, "onslaught_generator"); + while(tmp_entity) + { + delta = tmp_entity.origin - pos; + delta_z = 0; + distance = vlen(delta); + + if(SAME_TEAM(tmp_entity, self)) + if(max_dist <= 0 || distance <= max_dist) + if(closest_target == world || distance <= smallest_distance ) + { + closest_target = tmp_entity; + smallest_distance = distance; + } + + tmp_entity = tmp_entity.chain; + } + + return closest_target; +} +/** + * find the number of control points and generators in the same team as self + */ +float ons_Count_SelfControlPoints() +{ + entity tmp_entity; + tmp_entity = findchain(classname, "onslaught_controlpoint"); + float n = 0; + while(tmp_entity) { - WriteCoord(MSG_ENTITY, self.goalentity.origin.x); - WriteCoord(MSG_ENTITY, self.goalentity.origin.y); - WriteCoord(MSG_ENTITY, self.goalentity.origin.z); + if(SAME_TEAM(tmp_entity, self)) + if(tmp_entity.iscaptured) + n++; + tmp_entity = tmp_entity.chain; } - if(sendflags & 2) + tmp_entity = findchain(classname, "onslaught_generator"); + while(tmp_entity) { - WriteCoord(MSG_ENTITY, self.enemy.origin.x); - WriteCoord(MSG_ENTITY, self.enemy.origin.y); - WriteCoord(MSG_ENTITY, self.enemy.origin.z); + if(SAME_TEAM(tmp_entity, self)) + n++; + tmp_entity = tmp_entity.chain; } - if(sendflags & 4) + return n; +} + +/** + * Teleport player to a random position near tele_target + * if tele_effects is true, teleport sound+particles are created - * return FALSE on failure ++ * return false on failure + */ +float ons_Teleport(entity player, entity tele_target, float range, float tele_effects) +{ + if ( !tele_target ) - return FALSE; ++ return false; + + float i; + vector loc; + float theta; + for(i = 0; i < 16; ++i) + { + theta = random() * 2 * M_PI; + loc_y = sin(theta); + loc_x = cos(theta); + loc_z = 0; + loc *= random() * range; + + loc += tele_target.origin + '0 0 128'; + + tracebox(loc, PL_MIN, PL_MAX, loc, MOVE_NORMAL, player); + if(trace_fraction == 1.0 && !trace_startsolid) + { + traceline(tele_target.origin, loc, MOVE_NOMONSTERS, tele_target); // double check to make sure we're not spawning outside the world + if(trace_fraction == 1.0 && !trace_startsolid) + { + if ( tele_effects ) + { + Send_Effect(EFFECT_TELEPORT, player.origin, '0 0 0', 1); + sound (player, CH_TRIGGER, "misc/teleport.wav", VOL_BASE, ATTEN_NORM); + } + setorigin(player, loc); + player.angles = '0 1 0' * ( theta * RAD2DEG + 180 ); + makevectors(player.angles); - player.fixangle = TRUE; ++ player.fixangle = true; + player.teleport_antispam = time + autocvar_g_onslaught_teleport_wait; + + if ( tele_effects ) + Send_Effect(EFFECT_TELEPORT, player.origin + v_forward * 32, '0 0 0', 1); - return TRUE; ++ return true; + } + } + } + - return FALSE; ++ return false; +} + ++ +// ============== +// Hook Functions +// ============== + +MUTATOR_HOOKFUNCTION(ons_ResetMap) +{ + FOR_EACH_PLAYER(self) { - self.ons_roundlost = FALSE; - WriteByte(MSG_ENTITY, self.clientcolors); // which is goalentity's color + enemy's color * 16 ++ self.ons_roundlost = false; + self.ons_deathloc = '0 0 0'; + PutClientInServer(); } - return FALSE; - return true; ++ return false; } -void onslaught_link_checkupdate() +MUTATOR_HOOKFUNCTION(ons_RemovePlayer) { - // TODO check if the two sides have moved (currently they won't move anyway) - float redpower, bluepower; + self.ons_deathloc = '0 0 0'; - return FALSE; ++ return false; +} - redpower = bluepower = 0; - if(self.goalentity.islinked) +MUTATOR_HOOKFUNCTION(ons_PlayerSpawn) +{ + if(!round_handler_IsRoundStarted()) { - self.player_blocked = TRUE; - return FALSE; - if(self.goalentity.team == NUM_TEAM_1) - redpower = 1; - else if(self.goalentity.team == NUM_TEAM_2) - bluepower = 1; ++ self.player_blocked = true; ++ return false; } - if(self.enemy.islinked) + + entity l; + for(l = ons_worldgeneratorlist; l; l = l.ons_worldgeneratornext) { - if(self.enemy.team == NUM_TEAM_1) - redpower = 2; - else if(self.enemy.team == NUM_TEAM_2) - bluepower = 2; + l.sprite.SendFlags |= 16; + } + for(l = ons_worldcplist; l; l = l.ons_worldcpnext) + { + l.sprite.SendFlags |= 16; } - float cc; - if(redpower == 1 && bluepower == 2) - cc = (NUM_TEAM_1 - 1) * 0x01 + (NUM_TEAM_2 - 1) * 0x10; - else if(redpower == 2 && bluepower == 1) - cc = (NUM_TEAM_1 - 1) * 0x10 + (NUM_TEAM_2 - 1) * 0x01; - else if(redpower) - cc = (NUM_TEAM_1 - 1) * 0x11; - else if(bluepower) - cc = (NUM_TEAM_2 - 1) * 0x11; - else - cc = 0; + if(ons_stalemate) { Send_Notification(NOTIF_ONE, self, MSG_CENTER, CENTER_OVERTIME_CONTROLPOINT); } + + if ( autocvar_g_onslaught_spawn_choose ) + if ( self.ons_spawn_by ) - if ( ons_Teleport(self,self.ons_spawn_by,autocvar_g_onslaught_teleport_radius,FALSE) ) ++ if ( ons_Teleport(self,self.ons_spawn_by,autocvar_g_onslaught_teleport_radius,false) ) + { + self.ons_spawn_by = world; - return FALSE; ++ return false; + } + + if(autocvar_g_onslaught_spawn_at_controlpoints) + if(random() <= autocvar_g_onslaught_spawn_at_controlpoints_chance) + { + float random_target = autocvar_g_onslaught_spawn_at_controlpoints_random; + entity tmp_entity, closest_target = world; + vector spawn_loc = self.ons_deathloc; + + // new joining player or round reset, don't bother checking - if(spawn_loc == '0 0 0') { return FALSE; } ++ if(spawn_loc == '0 0 0') { return false; } - //print(etos(self), " rp=", ftos(redpower), " bp=", ftos(bluepower), " "); - //print("cc=", ftos(cc), "\n"); + if(random_target) { RandomSelection_Init(); } - if(cc != self.clientcolors) + for(tmp_entity = ons_worldcplist; tmp_entity; tmp_entity = tmp_entity.ons_worldcpnext) + { + if(SAME_TEAM(tmp_entity, self)) + if(random_target) + RandomSelection_Add(tmp_entity, 0, string_null, 1, 1); + else if(vlen(tmp_entity.origin - spawn_loc) <= vlen(closest_target.origin - spawn_loc) || closest_target == world) + closest_target = tmp_entity; + } + + if(random_target) { closest_target = RandomSelection_chosen_ent; } + + if(closest_target) + { + float i; + vector loc; + for(i = 0; i < 10; ++i) + { + loc = closest_target.origin + '0 0 96'; + loc += ('0 1 0' * random()) * 128; + tracebox(loc, PL_MIN, PL_MAX, loc, MOVE_NORMAL, self); + if(trace_fraction == 1.0 && !trace_startsolid) + { + traceline(closest_target.origin, loc, MOVE_NOMONSTERS, closest_target); // double check to make sure we're not spawning outside the world + if(trace_fraction == 1.0 && !trace_startsolid) + { + setorigin(self, loc); + self.angles = normalize(loc - closest_target.origin) * RAD2DEG; - return FALSE; ++ return false; + } + } + } + } + } + + if(autocvar_g_onslaught_spawn_at_generator) + if(random() <= autocvar_g_onslaught_spawn_at_generator_chance) { - self.clientcolors = cc; - self.SendFlags |= 4; + float random_target = autocvar_g_onslaught_spawn_at_generator_random; + entity tmp_entity, closest_target = world; + vector spawn_loc = self.ons_deathloc; + + // new joining player or round reset, don't bother checking - if(spawn_loc == '0 0 0') { return FALSE; } ++ if(spawn_loc == '0 0 0') { return false; } + + if(random_target) { RandomSelection_Init(); } + + for(tmp_entity = ons_worldgeneratorlist; tmp_entity; tmp_entity = tmp_entity.ons_worldgeneratornext) + { + if(random_target) + RandomSelection_Add(tmp_entity, 0, string_null, 1, 1); + else + { + if(SAME_TEAM(tmp_entity, self)) + if(vlen(tmp_entity.origin - spawn_loc) <= vlen(closest_target.origin - spawn_loc) || closest_target == world) + closest_target = tmp_entity; + } + } + + if(random_target) { closest_target = RandomSelection_chosen_ent; } + + if(closest_target) + { + float i; + vector loc; + for(i = 0; i < 10; ++i) + { + loc = closest_target.origin + '0 0 128'; + loc += ('0 1 0' * random()) * 256; + tracebox(loc, PL_MIN, PL_MAX, loc, MOVE_NORMAL, self); + if(trace_fraction == 1.0 && !trace_startsolid) + { + traceline(closest_target.origin, loc, MOVE_NOMONSTERS, closest_target); // double check to make sure we're not spawning outside the world + if(trace_fraction == 1.0 && !trace_startsolid) + { + setorigin(self, loc); + self.angles = normalize(loc - closest_target.origin) * RAD2DEG; - return FALSE; ++ return false; + } + } + } + } } - return FALSE; - self.nextthink = time; ++ return false; } -void onslaught_link_delayed() +MUTATOR_HOOKFUNCTION(ons_PlayerDies) { - self.goalentity = find(world, targetname, self.target); - self.enemy = find(world, targetname, self.target2); - if (!self.goalentity) - objerror("can not find target\n"); - if (!self.enemy) - objerror("can not find target2\n"); - dprint(etos(self.goalentity), " linked with ", etos(self.enemy), "\n"); - self.SendFlags |= 3; - self.think = onslaught_link_checkupdate; - self.nextthink = time; + frag_target.ons_deathloc = frag_target.origin; + entity l; + for(l = ons_worldgeneratorlist; l; l = l.ons_worldgeneratornext) + { + l.sprite.SendFlags |= 16; + } + for(l = ons_worldcplist; l; l = l.ons_worldcpnext) + { + l.sprite.SendFlags |= 16; + } + + if ( autocvar_g_onslaught_spawn_choose ) + if ( ons_Count_SelfControlPoints() > 1 ) + stuffcmd(self, "qc_cmd_cl hud clickradar\n"); + - return FALSE; ++ return false; } -/*QUAKED spawnfunc_onslaught_link (0 .5 .8) (-16 -16 -16) (16 16 16) - Link between control points. +MUTATOR_HOOKFUNCTION(ons_MonsterThink) +{ + entity e = find(world, targetname, self.target); + if (e != world) + self.team = e.team; - return FALSE; - This entity targets two different spawnfunc_onslaught_controlpoint or spawnfunc_onslaught_generator entities, and suppresses shielding on both if they are owned by different teams. ++ return false; +} -keys: -"target" - first control point. -"target2" - second control point. - */ -void spawnfunc_onslaught_link() +void ons_MonsterSpawn_Delayed() { - if (!g_onslaught) + entity e, own = self.owner; + + if(!own) { remove(self); return; } + + if(own.targetname) { - remove(self); - return; + e = find(world, target, own.targetname); + if(e != world) + { + own.team = e.team; + + activator = e; + own.use(); + } } - if (self.target == "" || self.target2 == "") - objerror("target and target2 must be set\n"); - InitializeEntity(self, onslaught_link_delayed, INITPRIO_FINDTARGET); - Net_LinkEntity(self, false, 0, onslaught_link_send); + + remove(self); } -MUTATOR_HOOKFUNCTION(ons_BuildMutatorsString) +MUTATOR_HOOKFUNCTION(ons_MonsterSpawn) { - ret_string = strcat(ret_string, ":ONS"); - return 0; -} + entity e = spawn(); + e.owner = self; + InitializeEntity(e, ons_MonsterSpawn_Delayed, INITPRIO_FINDTARGET); - return FALSE; -MUTATOR_HOOKFUNCTION(ons_BuildMutatorsPrettyString) -{ - ret_string = strcat(ret_string, ", Onslaught"); - return 0; ++ return false; } -MUTATOR_HOOKFUNCTION(ons_Spawn_Score) +void ons_TurretSpawn_Delayed() { + entity e, own = self.owner; - /* - float _neer_home = (random() > 0.5 ? true : false); + if(!own) { remove(self); return; } - RandomSelection_Init(); + if(own.targetname) + { + e = find(world, target, own.targetname); + if(e != world) + { + own.team = e.team; + own.active = ACTIVE_NOT; + + activator = e; + own.use(); + } + } - if(self.team == NUM_TEAM_1) - RandomSelection_Add(ons_red_generator, 0, string_null, 1, 1); + remove(self); +} - if(self.team == NUM_TEAM_2) - RandomSelection_Add(ons_blue_generator, 0, string_null, 1, 1); +MUTATOR_HOOKFUNCTION(ons_TurretSpawn) +{ + entity e = spawn(); + e.owner = self; + InitializeEntity(e, ons_TurretSpawn_Delayed, INITPRIO_FINDTARGET); - return FALSE; - entity _cp = findchain(classname, "onslaught_controlpoint"): - while _cp; - { - if(_cp.team == self.team) - RandomSelection_Add(_cp, 0, string_null, 1, 1); ++ return false; +} - _cp = _cp.chain; - } +MUTATOR_HOOKFUNCTION(ons_BotRoles) +{ + havocbot_ons_reset_role(self); - return TRUE; ++ return true; +} - if(RandomSelection_chosen_ent) +MUTATOR_HOOKFUNCTION(ons_GetTeamCount) +{ + // onslaught is special + entity tmp_entity; + for(tmp_entity = ons_worldgeneratorlist; tmp_entity; tmp_entity = tmp_entity.ons_worldgeneratornext) { - self.tur_head = RandomSelection_chosen_ent; - spawn_score_x += SPAWN_PRIO_NEAR_TEAMMATE_FOUND; + switch(tmp_entity.team) + { + case NUM_TEAM_1: c1 = 0; break; + case NUM_TEAM_2: c2 = 0; break; + case NUM_TEAM_3: c3 = 0; break; + case NUM_TEAM_4: c4 = 0; break; + } } - else if(self.team == spawn_spot.team) - spawn_score_x += SPAWN_PRIO_NEAR_TEAMMATE_SAMETEAM; // prefer same team, if we can't find a spawn near teammate - return TRUE; - */ ++ return true; +} + +MUTATOR_HOOKFUNCTION(ons_SpectateCopy) +{ + self.ons_roundlost = other.ons_roundlost; // make spectators see it too - return FALSE; ++ return false; +} +MUTATOR_HOOKFUNCTION(ons_SV_ParseClientCommand) +{ + if(MUTATOR_RETURNVALUE) // command was already handled? - return FALSE; ++ return false; + + if ( cmd_name == "ons_spawn" ) + { + vector pos = self.origin; + if(cmd_argc > 1) + pos_x = stof(argv(1)); + if(cmd_argc > 2) + pos_y = stof(argv(2)); + if(cmd_argc > 3) + pos_z = stof(argv(3)); + + if ( IS_PLAYER(self) ) + { + if ( !self.frozen ) + { + entity source_point = ons_Nearest_ControlPoint(self.origin, autocvar_g_onslaught_teleport_radius); + + if ( !source_point && self.health > 0 ) + { + sprint(self, "\nYou need to be next to a control point\n"); + return 1; + } + + + entity closest_target = ons_Nearest_ControlPoint_2D(pos, autocvar_g_onslaught_click_radius); + + if ( closest_target == world ) + { + sprint(self, "\nNo control point found\n"); + return 1; + } + + if ( self.health <= 0 ) + { + self.ons_spawn_by = closest_target; + self.respawn_flags = self.respawn_flags | RESPAWN_FORCE; + } + else + { + if ( source_point == closest_target ) + { + sprint(self, "\nTeleporting to the same point\n"); + return 1; + } + - if ( !ons_Teleport(self,closest_target,autocvar_g_onslaught_teleport_radius,TRUE) ) ++ if ( !ons_Teleport(self,closest_target,autocvar_g_onslaught_teleport_radius,true) ) + sprint(self, "\nUnable to teleport there\n"); + } + + return 1; + } + + sprint(self, "\nNo teleportation for you\n"); + } + + return 1; + } return 0; } -MUTATOR_HOOKFUNCTION(ons_PlayerSpawn) +MUTATOR_HOOKFUNCTION(ons_PlayerUseKey) { - if(MUTATOR_RETURNVALUE || gameover) { return FALSE; } - if(!autocvar_g_onslaught_spawn_at_controlpoints) - return 0; ++ if(MUTATOR_RETURNVALUE || gameover) { return false; } + + if((time > self.teleport_antispam) && (self.deadflag == DEAD_NO) && !self.vehicle) + { + entity source_point = ons_Nearest_ControlPoint(self.origin, autocvar_g_onslaught_teleport_radius); + if ( source_point ) + { + stuffcmd(self, "qc_cmd_cl hud clickradar\n"); - return TRUE; ++ return true; + } + } + - return FALSE; ++ return false; +} - if(random() < 0.5) // 50/50 chane to use default spawnsystem. - return 0; +// ========== +// Spawnfuncs +// ========== - float _close_to_home = ((random() > 0.5) ? true : false); - entity _best = world, _trg_gen = world; - float _score, _best_score = MAX_SHOT_DISTANCE; +/*QUAKED spawnfunc_onslaught_link (0 .5 .8) (-16 -16 -16) (16 16 16) + Link between control points. - RandomSelection_Init(); + This entity targets two different spawnfunc_onslaught_controlpoint or spawnfunc_onslaught_generator entities, and suppresses shielding on both if they are owned by different teams. - if(self.team == NUM_TEAM_1) - { - if(!_close_to_home) - _trg_gen = ons_blue_generator; - else - _trg_gen = ons_red_generator; - } +keys: +"target" - first control point. +"target2" - second control point. + */ +void spawnfunc_onslaught_link() +{ + if(!g_onslaught) { remove(self); return; } - if(self.team == NUM_TEAM_2) - { - if(_close_to_home) - _trg_gen = ons_blue_generator; - else - _trg_gen = ons_red_generator; - } + if (self.target == "" || self.target2 == "") + objerror("target and target2 must be set\n"); - entity _cp = findchain(classname, "onslaught_controlpoint"); - while(_cp) - { - if(_cp.team == self.team) - { - _score = vlen(_trg_gen.origin - _cp.origin); - if(_score < _best_score) - { - _best = _cp; - _best_score = _score; - } - } - _cp = _cp.chain; - } + self.ons_worldlinknext = ons_worldlinklist; // link into ons_worldlinklist + ons_worldlinklist = self; - vector _loc; - float i; - if(_best) - { - for(i = 0; i < 10; ++i) - { - _loc = _best.origin + '0 0 96'; - _loc += ('0 1 0' * random()) * 128; - tracebox(_loc, PL_MIN, PL_MAX, _loc, MOVE_NORMAL, self); - if(trace_fraction == 1.0 && !trace_startsolid) - { - setorigin(self, _loc); - self.angles = normalize(_loc - _best.origin) * RAD2DEG; - return 0; - } - } - } - else - { - if(!autocvar_g_onslaught_spawn_at_generator) - return 0; + InitializeEntity(self, ons_DelayedLinkSetup, INITPRIO_FINDTARGET); - Net_LinkEntity(self, FALSE, 0, ons_Link_Send); ++ Net_LinkEntity(self, false, 0, ons_Link_Send); +} - _trg_gen = ((self.team == NUM_TEAM_1) ? ons_red_generator : ons_blue_generator); +/*QUAKED spawnfunc_onslaught_controlpoint (0 .5 .8) (-32 -32 0) (32 32 128) + Control point. Be sure to give this enough clearance so that the shootable part has room to exist - for(i = 0; i < 10; ++i) - { - _loc = _trg_gen.origin + '0 0 96'; - _loc += ('0 1 0' * random()) * 128; - tracebox(_loc, PL_MIN, PL_MAX, _loc, MOVE_NORMAL, self); - if(trace_fraction == 1.0 && !trace_startsolid) - { - setorigin(self, _loc); - self.angles = normalize(_loc - _trg_gen.origin) * RAD2DEG; - return 0; - } - } - } + This should link to an spawnfunc_onslaught_controlpoint entity or spawnfunc_onslaught_generator entity. + +keys: +"targetname" - name that spawnfunc_onslaught_link entities will use to target this. +"target" - target any entities that are tied to this control point, such as vehicles and buildable structure entities. +"message" - name of this control point (should reflect the location in the map, such as "center bridge", "north tower", etc) + */ - return 0; +void spawnfunc_onslaught_controlpoint() +{ + if(!g_onslaught) { remove(self); return; } + + ons_ControlPoint_Setup(self); } -MUTATOR_HOOKFUNCTION(ons_MonsterThink) +/*QUAKED spawnfunc_onslaught_generator (0 .5 .8) (-32 -32 -24) (32 32 64) + Base generator. + + spawnfunc_onslaught_link entities can target this. + +keys: +"team" - team that owns this generator (5 = red, 14 = blue, etc), MUST BE SET. +"targetname" - name that spawnfunc_onslaught_link entities will use to target this. + */ +void spawnfunc_onslaught_generator() { - entity e = find(world, targetname, self.target); - if (e != world) - self.team = e.team; + if(!g_onslaught) { remove(self); return; } + if(!self.team) { objerror("team must be set"); } - return false; + ons_GeneratorSetup(self); } -MUTATOR_HOOKFUNCTION(ons_MonsterSpawn) -{ - entity e, ee = world; - if(self.targetname) - { - e = find(world,target,self.targetname); - if(e != world) - { - self.team = e.team; - ee = e; - } - } +// scoreboard setup +void ons_ScoreRules() +{ + CheckAllowedTeams(world); - ScoreRules_basics(((c4>=0) ? 4 : (c3>=0) ? 3 : 2), SFL_SORT_PRIO_PRIMARY, 0, TRUE); ++ ScoreRules_basics(((c4>=0) ? 4 : (c3>=0) ? 3 : 2), SFL_SORT_PRIO_PRIMARY, 0, true); + ScoreInfo_SetLabel_TeamScore (ST_ONS_CAPS, "destroyed", SFL_SORT_PRIO_PRIMARY); + ScoreInfo_SetLabel_PlayerScore(SP_ONS_CAPS, "caps", SFL_SORT_PRIO_SECONDARY); + ScoreInfo_SetLabel_PlayerScore(SP_ONS_TAKES, "takes", 0); + ScoreRules_basics_end(); +} - if(ee) - { - activator = ee; - self.use(); - } +void ons_DelayedInit() // Do this check with a delay so we can wait for teams to be set up +{ + ons_ScoreRules(); + + round_handler_Spawn(Onslaught_CheckPlayers, Onslaught_CheckWinner, Onslaught_RoundStart); + round_handler_Init(5, autocvar_g_onslaught_warmup, autocvar_g_onslaught_round_timelimit); +} - return false; +void ons_Initialize() +{ + precache_sound("ctf/red_capture.wav"); + precache_sound("ctf/blue_capture.wav"); + precache_sound("ctf/yellow_capture.wav"); + precache_sound("ctf/pink_capture.wav"); + + ons_captureshield_force = autocvar_g_onslaught_shield_force; + + addstat(STAT_ROUNDLOST, AS_INT, ons_roundlost); + + InitializeEntity(world, ons_DelayedInit, INITPRIO_GAMETYPE); } MUTATOR_DEFINITION(gamemode_onslaught) @@@ -2184,5 -1710,5 +2187,5 @@@ return -1; } - return FALSE; - return 0; ++ return false; } diff --cc qcsrc/server/mutators/gamemode_onslaught.qh index cb4aeb4cf,000000000..58afe0a8c mode 100644,000000..100644 --- a/qcsrc/server/mutators/gamemode_onslaught.qh +++ b/qcsrc/server/mutators/gamemode_onslaught.qh @@@ -1,94 -1,0 +1,94 @@@ +// these are needed since mutators are compiled last + +#ifdef SVQC + +.entity ons_toucher; // player who touched the control point + +// control point / generator constants +#define ONS_CP_THINKRATE 0.2 +#define GEN_THINKRATE 1 +#define CPGEN_SPAWN_OFFSET ('0 0 1' * (PL_MAX_z - 13)) +#define CPGEN_WAYPOINT_OFFSET ('0 0 128') +#define CPICON_OFFSET ('0 0 96') + +// list of generators on the map +entity ons_worldgeneratorlist; +.entity ons_worldgeneratornext; +.entity ons_stalegeneratornext; + +// list of control points on the map +entity ons_worldcplist; +.entity ons_worldcpnext; +.entity ons_stalecpnext; + +// list of links on the map +entity ons_worldlinklist; +.entity ons_worldlinknext; +.entity ons_stalelinknext; + +// definitions +.entity sprite; +.string target2; +.float iscaptured; +.float islinked; +.float isshielded; +.float lasthealth; +.float lastteam; +.float lastshielded; +.float lastcaptured; + +.float waslinked; + +float ons_stalemate; + +.float teleport_antispam; + +.float ons_roundlost; + +// waypoint sprites +.entity bot_basewaypoint; // generator waypointsprite - float wpforenemy_announced; ++//float wpforenemy_announced; + +.float isgenneighbor[17]; +.float iscpneighbor[17]; +float ons_notification_time[17]; + +.float ons_overtime_damagedelay; + +.vector ons_deathloc; + +.entity ons_spawn_by; + +// declarations for functions used outside gamemode_onslaught.qc +void ons_Generator_UpdateSprite(entity e); +void ons_ControlPoint_UpdateSprite(entity e); +float ons_ControlPoint_Attackable(entity cp, float teamnumber); + +// CaptureShield: Prevent capturing or destroying control point/generator if it is not available yet +float ons_captureshield_force; // push force of the shield + +// bot player logic +#define HAVOCBOT_ONS_ROLE_NONE 0 +#define HAVOCBOT_ONS_ROLE_DEFENSE 2 +#define HAVOCBOT_ONS_ROLE_ASSISTANT 4 +#define HAVOCBOT_ONS_ROLE_OFFENSE 8 + +.entity havocbot_ons_target; + +.float havocbot_role_flags; +.float havocbot_attack_time; + +void havocbot_role_ons_defense(); +void havocbot_role_ons_offense(); +void havocbot_role_ons_assistant(); + +void havocbot_ons_reset_role(entity bot); +void havocbot_goalrating_items(float ratingscale, vector org, float sradius); +void havocbot_goalrating_enemyplayers(float ratingscale, vector org, float sradius); + +// score rule declarations +#define ST_ONS_CAPS 1 +#define SP_ONS_CAPS 4 +#define SP_ONS_TAKES 6 + +#endif diff --cc qcsrc/server/mutators/gamemode_race.qc index dacb24b80,7ba832846..63ac3047f --- a/qcsrc/server/mutators/gamemode_race.qc +++ b/qcsrc/server/mutators/gamemode_race.qc @@@ -263,24 -263,9 +263,24 @@@ MUTATOR_HOOKFUNCTION(race_ForbidClearPl MUTATOR_HOOKFUNCTION(race_GetTeamCount) { ret_float = race_teams; - return FALSE; + return false; } +MUTATOR_HOOKFUNCTION(race_CountFrags) +{ + // announce remaining frags if not in qualifying mode + if(!g_race_qualifying) - return TRUE; ++ return true; + - return FALSE; ++ return false; +} + +MUTATOR_HOOKFUNCTION(race_AllowMobSpawning) +{ + ret_string = "No monsters in a race!"; - return TRUE; ++ return true; +} + void race_Initialize() { race_ScoreRules(); diff --cc qcsrc/server/mutators/gamemode_tdm.qc index 8d50c654d,de172873e..95e6cfa2b --- a/qcsrc/server/mutators/gamemode_tdm.qc +++ b/qcsrc/server/mutators/gamemode_tdm.qc @@@ -51,21 -48,7 +51,21 @@@ void tdm_DelayedInit( MUTATOR_HOOKFUNCTION(tdm_GetTeamCount) { ret_string = "tdm_team"; + if(find(world, classname, "tdm_team") == world) + { + // make it at least half accurate + ret_float = bound(2, ((autocvar_g_tdm_teams_override < 2) ? autocvar_g_tdm_teams : autocvar_g_tdm_teams_override), 4); + ret_string = string_null; - return FALSE; ++ return false; + } + - return TRUE; ++ return true; +} + +MUTATOR_HOOKFUNCTION(tdm_CountFrags) +{ + // announce remaining frags - return TRUE; + return true; } MUTATOR_DEFINITION(gamemode_tdm) diff --cc qcsrc/server/mutators/gamemode_vip.qc index 39b755712,000000000..d4fb779c8 mode 100644,000000..100644 --- a/qcsrc/server/mutators/gamemode_vip.qc +++ b/qcsrc/server/mutators/gamemode_vip.qc @@@ -1,716 -1,0 +1,718 @@@ ++#include "../round_handler.qh" ++ ++int red_players, blue_players, yellow_players, pink_players; +void VIP_count_players() +{ + total_players = red_players = blue_players = yellow_players = pink_players = 0; + entity head; + + for(head = world; (head = find(head, classname, "player")); ) + { + ++total_players; + red_players += (head.team == NUM_TEAM_1); + blue_players += (head.team == NUM_TEAM_2); + yellow_players += (head.team == NUM_TEAM_3); + pink_players += (head.team == NUM_TEAM_4); + } +} + +void vip_ClearVIPs() +{ + entity head; + float i; + + FOR_EACH_PLAYER(head) + { + if(head.wps_vip) + { + WaypointSprite_Ping(head.wps_vip); + WaypointSprite_Kill(head.wps_vip); + } - head.isvip = FALSE; - head.gem_dropped = FALSE; ++ head.isvip = false; ++ head.gem_dropped = false; + } + + for(i = 0; i < VIP_TEAMS; ++i) + vip_count[i] = 0; +} + +#define VIP_TEAMS_COUNT() ((red_players > 0) + (blue_players > 0) + (yellow_players > 0) + (pink_players > 0)) +#define VIP_TEAMS_OK() (VIP_TEAMS_COUNT() == vip_teams) +#define VIP_COUNT() ((vip_count[NUM_TEAM_1] > 0) + (vip_count[NUM_TEAM_2] > 0) + (vip_count[NUM_TEAM_3] > 0) + (vip_count[NUM_TEAM_4] > 0)) + +float VIP_GetWinnerTeam() +{ + float winner_team = 0; + if(vip_count[NUM_TEAM_1] > 0) + winner_team = NUM_TEAM_1; + if(vip_count[NUM_TEAM_2] > 0) + { + if(winner_team) return 0; + winner_team = NUM_TEAM_2; + } + if(vip_count[NUM_TEAM_3] > 0) + { + if(winner_team) return 0; + winner_team = NUM_TEAM_3; + } + if(vip_count[NUM_TEAM_4] > 0) + { + if(winner_team) return 0; + winner_team = NUM_TEAM_4; + } + if(winner_team) + return winner_team; + return -1; // no player left +} + +float VIP_CheckWinner() +{ + entity head; + if(round_handler_GetEndTime() > 0 && round_handler_GetEndTime() - time <= 0) + { + Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_ROUND_OVER); + Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_ROUND_OVER); + - nades_Clear(world, TRUE); ++ nades_Clear(world, true); + + FOR_EACH_PLAYER(head) if(head.isvip && head.health > 0) + PlayerScore_Add(head, SP_VIP_SURVIVALS, 1); + + vip_ClearVIPs(); - allowed_to_spawn = FALSE; ++ allowed_to_spawn = false; + round_handler_Init(5, autocvar_g_vip_warmup, autocvar_g_vip_round_timelimit); + return 1; + } + + VIP_count_players(); + if(VIP_COUNT() > 1) + return 0; + + float winner_team = VIP_GetWinnerTeam(); + if(winner_team > 0) + { + Send_Notification(NOTIF_ALL, world, MSG_CENTER, APP_TEAM_NUM_4(winner_team, CENTER_ROUND_TEAM_WIN_)); + Send_Notification(NOTIF_ALL, world, MSG_INFO, APP_TEAM_NUM_4(winner_team, INFO_ROUND_TEAM_WIN_)); + TeamScore_AddToTeam(winner_team, ST_VIP_SCORE, +1); + + entity head; + FOR_EACH_PLAYER(head) + if(head.isvip && head.health > 0 && head.team == winner_team) + PlayerScore_Add(head, SP_VIP_SURVIVALS, 1); + + switch(winner_team) + { + case NUM_TEAM_1: play2all("ctf/red_capture.wav"); break; + case NUM_TEAM_2: play2all("ctf/blue_capture.wav"); break; + case NUM_TEAM_3: play2all("ctf/yellow_capture.wav"); break; + case NUM_TEAM_4: play2all("ctf/pink_capture.wav"); break; + } + } + else if(winner_team == -1) + { + Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_ROUND_TIED); + Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_ROUND_TIED); + + entity head; + FOR_EACH_PLAYER(head) + if(head.isvip && head.health > 0) + PlayerScore_Add(head, SP_VIP_SURVIVALS, 1); + } + + vip_ClearVIPs(); + - allowed_to_spawn = FALSE; ++ allowed_to_spawn = false; + + round_handler_Init(5, autocvar_g_vip_warmup, autocvar_g_vip_round_timelimit); + - nades_Clear(world, TRUE); ++ nades_Clear(world, true); + + return 1; +} + +void VIP_RoundStart() +{ + entity oldself = self; + FOR_EACH_PLAYER(self) + { + if(self.isvip) + Send_Notification(NOTIF_ONE, self, MSG_CENTER, APP_TEAM_ENT_4(self, CENTER_VIP_BEGIN_)); + PutClientInServer(); + } + self = oldself; +} + +void vip_SelectVIPs() +{ + float usegems = g_vip_soulgems; + entity head; + RandomSelection_Init(); + FOR_EACH_PLAYER(head) if(head.health > 0) + if((redgem_count < 1 || !usegems) && head.team == NUM_TEAM_1 && vip_count[NUM_TEAM_1] < 1) + RandomSelection_Add(head, 0, string_null, 1, ((IS_REAL_CLIENT(head) && !head.gem_dropped) ? 1 : 0.5)); + + vip_SetVIP(RandomSelection_chosen_ent, NUM_TEAM_1); + + RandomSelection_Init(); + FOR_EACH_PLAYER(head) if(head.health > 0) + if((bluegem_count < 1 || !usegems) && head.team == NUM_TEAM_2 && vip_count[NUM_TEAM_2] < 1) + RandomSelection_Add(head, 0, string_null, 1, ((IS_REAL_CLIENT(head) && !head.gem_dropped) ? 1 : 0.5)); + + vip_SetVIP(RandomSelection_chosen_ent, NUM_TEAM_2); + + RandomSelection_Init(); + FOR_EACH_PLAYER(head) if(head.health > 0) + if((yellowgem_count < 1 || !usegems) && head.team == NUM_TEAM_3 && vip_count[NUM_TEAM_3] < 1) + RandomSelection_Add(head, 0, string_null, 1, ((IS_REAL_CLIENT(head) && !head.gem_dropped) ? 1 : 0.5)); + + vip_SetVIP(RandomSelection_chosen_ent, NUM_TEAM_3); + + RandomSelection_Init(); + FOR_EACH_PLAYER(head) if(head.health > 0) + if((pinkgem_count < 1 || !usegems) && head.team == NUM_TEAM_4 && vip_count[NUM_TEAM_4] < 1) + RandomSelection_Add(head, 0, string_null, 1, ((IS_REAL_CLIENT(head) && !head.gem_dropped) ? 1 : 0.5)); + + vip_SetVIP(RandomSelection_chosen_ent, NUM_TEAM_4); +} - - float prev_missing_teams_mask; +float VIP_CheckTeams() +{ ++ static float prev_missing_teams_mask; + float rc, bc, yc, pc; + rc = ((redgem_count) ? redgem_count : 1); + bc = ((bluegem_count) ? bluegem_count : 1); + yc = ((yellowgem_count) ? yellowgem_count : 1); + pc = ((pinkgem_count) ? pinkgem_count : 1); - allowed_to_spawn = TRUE; ++ allowed_to_spawn = true; + VIP_count_players(); + if(vip_count[NUM_TEAM_1] < rc || vip_count[NUM_TEAM_2] < bc || (vip_teams >= 3 && vip_count[NUM_TEAM_3] < yc) || (vip_teams >= 4 && vip_count[NUM_TEAM_4] < pc)) + { + entity head; + if(!g_vip_soulgems || redgem_count < 1 || bluegem_count < 1 || (vip_teams >= 3 && yellowgem_count < 1) || (vip_teams >= 4 && pinkgem_count < 1)) + vip_SelectVIPs(); + + FOR_EACH_PLAYER(head) + { + if((head.team == NUM_TEAM_1 && vip_count[NUM_TEAM_1] > 0) || (head.team == NUM_TEAM_2 && vip_count[NUM_TEAM_2] > 0) || (vip_teams >= 3 && head.team == NUM_TEAM_3 && vip_count[NUM_TEAM_3] > 0) || (vip_teams >= 4 && head.team == NUM_TEAM_4 && vip_count[NUM_TEAM_4] > 0)) + Send_Notification(NOTIF_ONE, head, MSG_CENTER, CENTER_VIP_MISSING_ENEMY); + else + Send_Notification(NOTIF_ONE, head, MSG_CENTER, CENTER_VIP_MISSING); + } + return 0; + } + if(VIP_TEAMS_OK()) + { + if(prev_missing_teams_mask > 0) + Kill_Notification(NOTIF_ALL, world, MSG_CENTER_CPID, CPID_MISSING_TEAMS); + prev_missing_teams_mask = -1; + return 1; + } + if(total_players == 0) + { + if(prev_missing_teams_mask > 0) + Kill_Notification(NOTIF_ALL, world, MSG_CENTER_CPID, CPID_MISSING_TEAMS); + prev_missing_teams_mask = -1; + return 0; + } + float missing_teams_mask = (!redalive) + (!bluealive) * 2; + if(vip_teams >= 3) missing_teams_mask += (!yellowalive) * 4; + if(vip_teams >= 4) missing_teams_mask += (!pinkalive) * 8; + if(prev_missing_teams_mask != missing_teams_mask) + { + Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_MISSING_TEAMS, missing_teams_mask); + prev_missing_teams_mask = missing_teams_mask; + } + return 0; +} + +void vip_DropSoulGem(entity player) +{ + if(!player.isvip) + return; + + if(player.wps_vip) + { + WaypointSprite_Ping(player.wps_vip); + WaypointSprite_Kill(player.wps_vip); + } + + player.health = 100; // reset these now? + player.armorvalue = 0; - player.isvip = FALSE; - player.gem_dropped = TRUE; ++ player.isvip = false; ++ player.gem_dropped = true; + vip_count[player.team] -= 1; + + if(IS_REAL_CLIENT(player)) + Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_VIP_DROP_SELF); + + Send_Notification(NOTIF_ALL_EXCEPT, player, MSG_CENTER, CENTER_VIP_DROP, player.netname); + Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_VIP_DROP, player.netname); + + play2all("kh/destroy.wav"); +} + +MUTATOR_HOOKFUNCTION(vip_PlayerDies) +{ + if(frag_target.isvip) + { + if(IS_PLAYER(frag_attacker)) + { + PlayerScore_Add(frag_attacker, SP_VIP_VIPKILLS, 1); + PlayerScore_Add(frag_attacker, SP_SCORE, 1); + } + + vip_count[frag_target.team] -= 1; - frag_target.isvip = FALSE; ++ frag_target.isvip = false; + + play2all("kh/destroy.wav"); + + WaypointSprite_Ping(frag_target.wps_vip); + WaypointSprite_Kill(frag_target.wps_vip); + } + - return FALSE; ++ return false; +} + +MUTATOR_HOOKFUNCTION(vip_RemovePlayer) +{ + if(self.isvip) + { + vip_count[self.team] -= 1; - self.isvip = FALSE; ++ self.isvip = false; + WaypointSprite_Kill(self.wps_vip); + } + - return FALSE; ++ return false; +} + +MUTATOR_HOOKFUNCTION(vip_PlayerThink) +{ + if(self.wps_vip) + WaypointSprite_UpdateHealth(self.wps_vip, ((g_instagib) ? self.armorvalue : '1 0 0' * healtharmor_maxdamage(self.health, self.armorvalue, autocvar_g_balance_armor_blockpercent, DEATH_WEAPON))); + + self.stat_vip_red = vip_count[NUM_TEAM_1]; + self.stat_vip_blue = vip_count[NUM_TEAM_2]; + self.stat_vip_yellow = vip_count[NUM_TEAM_3]; + self.stat_vip_pink = vip_count[NUM_TEAM_4]; + - return FALSE; ++ return false; +} + +MUTATOR_HOOKFUNCTION(vip_ResetMap) +{ + FOR_EACH_PLAYER(self) + PutClientInServer(); + - return TRUE; ++ return true; +} + +MUTATOR_HOOKFUNCTION(vip_PlayerSpawn) +{ + if(self.isvip) + { + if(!g_instagib) + self.health = 200; + self.armorvalue = ((g_instagib) ? 5 : 200); // 5 lives in instagib + } + - return FALSE; ++ return false; +} + +MUTATOR_HOOKFUNCTION(vip_PlayerRegen) +{ + // VIP regens to 200 + if(self.isvip) + { + regen_mod_max *= 2; + regen_mod_regen = 0.7; // but with slower regen speed + } + - return FALSE; ++ return false; +} + +MUTATOR_HOOKFUNCTION(vip_PlayerUseKey) +{ - if(MUTATOR_RETURNVALUE || gameover || round_handler_IsRoundStarted()) { return FALSE; } ++ if(MUTATOR_RETURNVALUE || gameover || round_handler_IsRoundStarted()) { return false; } + + entity player = self; + + if((time > player.pickup_antispam) && (player.deadflag == DEAD_NO) && !player.vehicle) //&& (!player.vehicle || autocvar_g_vip_allow_vehicle_touch)) vehicle support coming soon(?) + { + if(autocvar_g_vip_drop && player.isvip) + { + if(player.drop_count == -1) + { + if(time > player.drop_prevtime + autocvar_g_vip_drop_punish_delay) + { + player.drop_prevtime = time; + player.drop_count = 1; + vip_DropSoulGem(player); - return TRUE; ++ return true; + } + else + { + Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_VIP_DROP_PUNISH, rint((player.drop_prevtime + autocvar_g_vip_drop_punish_delay) - time)); - return FALSE; ++ return false; + } + } + else + { + if(time > player.drop_prevtime + autocvar_g_vip_drop_punish_time) { player.drop_count = 1; } + else { player.drop_count += 1; } + if(player.drop_count >= autocvar_g_vip_drop_punish_count) { player.drop_count = -1; } + + player.drop_prevtime = time; + vip_DropSoulGem(player); - return TRUE; ++ return true; + } + } + } + - return TRUE; ++ return true; +} + +MUTATOR_HOOKFUNCTION(vip_OnEntityPreSpawn) +{ + if(!g_vip_soulgems) - return FALSE; ++ return false; + + if(self.classname == "item_flag_team1") + { + vip_SpawnSoulGem(self.origin, NUM_TEAM_1); - return TRUE; ++ return true; + } + + if(self.classname == "item_flag_team2") + { + vip_SpawnSoulGem(self.origin, NUM_TEAM_2); - return TRUE; ++ return true; + } + + if(self.classname == "item_flag_team3") + { + vip_SpawnSoulGem(self.origin, NUM_TEAM_3); - return TRUE; ++ return true; + } + + if(self.classname == "item_flag_team4") + { + vip_SpawnSoulGem(self.origin, NUM_TEAM_4); - return TRUE; ++ return true; + } + - return FALSE; ++ return false; +} + +MUTATOR_HOOKFUNCTION(vip_GetTeamCount) +{ + ret_float = vip_teams; - return FALSE; ++ return false; +} + +vector vip_SoulGemColormod(float theteam, float iseffect) +{ + if(iseffect) + { + switch(theteam) + { + case NUM_TEAM_1: return '1 0.4 0.4'; + case NUM_TEAM_2: return '0.4 0.4 1'; + case NUM_TEAM_3: return '1 1 0.4'; + case NUM_TEAM_4: return '1 0.4 1'; + } + } + else + { + switch(theteam) + { + case NUM_TEAM_1: return '1 0.8 0.8'; + case NUM_TEAM_2: return '0.8 0.8 1'; + case NUM_TEAM_3: return '1 1 0.8'; + case NUM_TEAM_4: return '1 0.8 1'; + } + } + + return (iseffect) ? '0.2 0.2 0.2' : '0.1 0.1 0.1'; +} + +void vip_VIPWaypoints(entity player) +{ - WaypointSprite_Spawn("vip", 0, 0, player, FLAG_WAYPOINT_OFFSET, world, 0, player, wps_vip, TRUE, RADARICON_FLAG, Team_ColorRGB(player.team)); ++ WaypointSprite_Spawn("vip", 0, 0, player, FLAG_WAYPOINT_OFFSET, world, 0, player, wps_vip, true, RADARICON_FLAG, Team_ColorRGB(player.team)); + WaypointSprite_UpdateMaxHealth(player.wps_vip, ((g_instagib) ? 5 : '1 0 0' * healtharmor_maxdamage(start_health, start_armorvalue, autocvar_g_balance_armor_blockpercent, DEATH_WEAPON) * 2)); + WaypointSprite_UpdateHealth(player.wps_vip, ((g_instagib) ? self.armorvalue : '1 0 0' * healtharmor_maxdamage(player.health, player.armorvalue, autocvar_g_balance_armor_blockpercent, DEATH_WEAPON))); + WaypointSprite_UpdateTeamRadar(player.wps_vip, RADARICON_FLAGCARRIER, Team_ColorRGB(player.team)); +} + +void vip_SetVIP(entity player, float tm) +{ + if(!IS_PLAYER(player) || player == world || player.isvip) + return; // TODO: check how this can be called at all for non players - player.isvip = TRUE; ++ player.isvip = true; + if(!g_instagib) + player.health = 200; + player.armorvalue = ((g_instagib) ? 5 : 200); + player.pickup_antispam = time + autocvar_g_vip_pickup_wait; + + vip_count[tm] += 1; + + vip_VIPWaypoints(player); + + if(IS_REAL_CLIENT(player)) + Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_VIP_PICKUP_SELF); + + Send_Notification(NOTIF_ALL_EXCEPT, player, MSG_CENTER, CENTER_VIP_PICKUP, player.netname); + Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_VIP_PICKUP, player.netname); +} + +void vip_SoulGem_touch() +{ + if(!IS_PLAYER(other)) + return; + + if(self.gem_pickedup) + return; + + if(other.isvip) + return; + + if(other.frozen) + return; + + if(!allowed_to_spawn) + return; + + if(other.deadflag != DEAD_NO) + return; + + if(DIFF_TEAM(other, self)) + return; + + if(round_handler_IsRoundStarted()) + return; + + self.gem_pickedup = 1; + self.owner = other; + self.alpha = 0; + self.enemy.alpha = 0; - self.colormod = vip_SoulGemColormod(-self.team, FALSE); - self.enemy.colormod = vip_SoulGemColormod(-self.team, TRUE); ++ self.colormod = vip_SoulGemColormod(-self.team, false); ++ self.enemy.colormod = vip_SoulGemColormod(-self.team, true); + + WaypointSprite_Kill(self.waypointsprite_attached); + + play2all("kh/capture.wav"); + + vip_SetVIP(other, other.team); +} + +void vip_SoulGem_think() +{ + self.glow_size = 256 + cos(time * 2) * 64; + self.nextthink = time + 0.1; + + if(vip_count[self.team] < 1 && self.gem_pickedup && allowed_to_spawn && self.owner && !self.owner.isvip && !round_handler_IsRoundStarted()) + self.reset(); +} + +void vip_SoulGem_reset() +{ + self.gem_pickedup = 0; + self.alpha = self.enemy.alpha = 0.8; - self.colormod = vip_SoulGemColormod(self.team, FALSE); - self.enemy.colormod = vip_SoulGemColormod(self.team, TRUE); ++ self.colormod = vip_SoulGemColormod(self.team, false); ++ self.enemy.colormod = vip_SoulGemColormod(self.team, true); + self.owner = world; + + // "run here" waypoint + if(!self.waypointsprite_attached) - WaypointSprite_Spawn("keycarrier-finish", 0, 0, self, FLAG_WAYPOINT_OFFSET, world, self.team, self, waypointsprite_attached, TRUE, RADARICON_FLAG, '0 1 1'); ++ WaypointSprite_Spawn("keycarrier-finish", 0, 0, self, FLAG_WAYPOINT_OFFSET, world, self.team, self, waypointsprite_attached, true, RADARICON_FLAG, '0 1 1'); +} + +void vip_DelayedGemSetup() // called after a gem is placed on a map +{ + // waypointsprites - WaypointSprite_Spawn("keycarrier-finish", 0, 0, self, FLAG_WAYPOINT_OFFSET, world, self.team, self, waypointsprite_attached, TRUE, RADARICON_FLAG, '0 1 1'); ++ WaypointSprite_Spawn("keycarrier-finish", 0, 0, self, FLAG_WAYPOINT_OFFSET, world, self.team, self, waypointsprite_attached, true, RADARICON_FLAG, '0 1 1'); +} + +void vip_SpawnSoulGem(vector orig, float theteam) +{ + entity e = spawn(); + e.classname = "vip_soulgem"; + e.target = "###item###"; + e.team = theteam; + e.movetype = MOVETYPE_NONE; + e.touch = vip_SoulGem_touch; + e.think = vip_SoulGem_think; + e.nextthink = time + random(); + e.solid = SOLID_TRIGGER; + e.flags = FL_ITEM; + e.reset = vip_SoulGem_reset; + setmodel(e, "models/runematch/rune.mdl"); + setsize(e, '0 0 -35', '0 0 0'); + + e.glow_size = 256; + e.glow_color = (theteam == NUM_TEAM_1) ? 251 : 250; + e.glow_trail = 1; + + e.enemy = spawn(); + e.enemy.enemy = e; + e.enemy.classname = "vip_soulgem_effect"; + setmodel(e.enemy, "models/runematch/curse.mdl"); + e.origin = orig; + e.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_PLAYERCLIP; + setorigin (e, e.origin); + setattachment(e.enemy, e, ""); + + move_out_of_solid(e); + + entity oldself; + oldself = self; + self = e; + droptofloor(); + self = oldself; + - e.colormod = vip_SoulGemColormod(theteam, FALSE); - e.enemy.colormod = vip_SoulGemColormod(theteam, TRUE); ++ e.colormod = vip_SoulGemColormod(theteam, false); ++ e.enemy.colormod = vip_SoulGemColormod(theteam, true); + + e.alpha = e.enemy.alpha = 0.8; + e.effects = e.enemy.effects = (EF_ADDITIVE | EF_FULLBRIGHT | EF_LOWPRECISION); + + // update gem counts + switch(theteam) + { + case NUM_TEAM_1: redgem_count += 1; break; + case NUM_TEAM_2: bluegem_count += 1; break; + case NUM_TEAM_3: yellowgem_count += 1; break; + case NUM_TEAM_4: pinkgem_count += 1; break; + } + + InitializeEntity(e, vip_DelayedGemSetup, INITPRIO_SETLOCATION); +} + +// for maps without flags +void spawnfunc_item_soulgem_team1() +{ + if(!g_vip || !g_vip_soulgems) { remove(self); return; } + + vip_SpawnSoulGem(self.origin, NUM_TEAM_1); + + self.think = SUB_Remove; + self.nextthink = time + 0.1; +} + +void spawnfunc_item_soulgem_team2() +{ + if(!g_vip || !g_vip_soulgems) { remove(self); return; } + + vip_SpawnSoulGem(self.origin, NUM_TEAM_2); + + self.think = SUB_Remove; + self.nextthink = time + 0.1; +} + +void spawnfunc_item_soulgem_team3() +{ + if(!g_vip || !g_vip_soulgems) { remove(self); return; } + + vip_SpawnSoulGem(self.origin, NUM_TEAM_3); + + self.think = SUB_Remove; + self.nextthink = time + 0.1; +} + +void spawnfunc_item_soulgem_team4() +{ + if(!g_vip || !g_vip_soulgems) { remove(self); return; } + + vip_SpawnSoulGem(self.origin, NUM_TEAM_4); + + self.think = SUB_Remove; + self.nextthink = time + 0.1; +} + +// scoreboard setup +void vip_ScoreRules() +{ + CheckAllowedTeams(world); - ScoreRules_basics(vip_teams, SFL_SORT_PRIO_PRIMARY, 0, TRUE); ++ ScoreRules_basics(vip_teams, SFL_SORT_PRIO_PRIMARY, 0, true); + ScoreInfo_SetLabel_PlayerScore(SP_VIP_SURVIVALS, "survivals", 0); + ScoreInfo_SetLabel_PlayerScore(SP_VIP_VIPKILLS, "vipkills", SFL_SORT_PRIO_SECONDARY); + ScoreInfo_SetLabel_TeamScore( ST_VIP_SCORE, "scores", SFL_SORT_PRIO_PRIMARY); + ScoreRules_basics_end(); +} + +void vip_DelayedInit() // Do this check with a delay so we can wait for teams to be set up. +{ + vip_teams = 2; + + if(yellowgem_count > 0) vip_teams = max(3, vip_teams); + if(pinkgem_count > 0) vip_teams = max(4, vip_teams); + + if(autocvar_g_vip_teams >= 2) + vip_teams = autocvar_g_vip_teams; + + vip_teams = bound(2, vip_teams, 4); + + vip_ScoreRules(); +} + +void vip_Initialize() +{ - allowed_to_spawn = TRUE; ++ allowed_to_spawn = true; + + g_vip_soulgems = cvar("g_vip_soulgems"); + + round_handler_Spawn(VIP_CheckTeams, VIP_CheckWinner, VIP_RoundStart); + round_handler_Init(5, autocvar_g_vip_warmup, autocvar_g_vip_round_timelimit); + + precache_sound("kh/capture.wav"); + precache_sound("kh/destroy.wav"); + precache_sound("ctf/red_capture.wav"); + precache_sound("ctf/blue_capture.wav"); + precache_sound("ctf/yellow_capture.wav"); + precache_sound("ctf/pink_capture.wav"); + + precache_model("models/runematch/rune.mdl"); + precache_model("models/runematch/curse.mdl"); + + addstat(STAT_VIP, AS_INT, isvip); + addstat(STAT_VIP_RED, AS_FLOAT, stat_vip_red); + addstat(STAT_VIP_BLUE, AS_FLOAT, stat_vip_blue); + addstat(STAT_VIP_YELLOW, AS_FLOAT, stat_vip_yellow); + addstat(STAT_VIP_PINK, AS_FLOAT, stat_vip_pink); + + InitializeEntity(world, vip_DelayedInit, INITPRIO_GAMETYPE); +} + +MUTATOR_DEFINITION(gamemode_vip) +{ + MUTATOR_HOOK(PlayerDies, vip_PlayerDies, CBC_ORDER_ANY); + MUTATOR_HOOK(ClientDisconnect, vip_RemovePlayer, CBC_ORDER_ANY); + MUTATOR_HOOK(MakePlayerObserver, vip_RemovePlayer, CBC_ORDER_ANY); + MUTATOR_HOOK(PlayerPreThink, vip_PlayerThink, CBC_ORDER_ANY); + MUTATOR_HOOK(reset_map_players, vip_ResetMap, CBC_ORDER_ANY); + MUTATOR_HOOK(PlayerSpawn, vip_PlayerSpawn, CBC_ORDER_ANY); + MUTATOR_HOOK(PlayerRegen, vip_PlayerRegen, CBC_ORDER_ANY); + MUTATOR_HOOK(PlayerUseKey, vip_PlayerUseKey, CBC_ORDER_ANY); + MUTATOR_HOOK(OnEntityPreSpawn, vip_OnEntityPreSpawn, CBC_ORDER_FIRST); + MUTATOR_HOOK(GetTeamCount, vip_GetTeamCount, CBC_ORDER_ANY); + + MUTATOR_ONADD + { + if(time > 1) // game loads at time 1 + error("This is a game type and it cannot be added at runtime."); + vip_Initialize(); + } + + MUTATOR_ONROLLBACK_OR_REMOVE + { + // we actually cannot roll back vip_Initialize here + // BUT: we don't need to! If this gets called, adding always + // succeeds. + } + + MUTATOR_ONREMOVE + { + print("This is a game type and it cannot be removed at runtime."); + return -1; + } + + return 0; +} diff --cc qcsrc/server/mutators/gamemode_vip.qh index 33e9483ff,000000000..c5430a581 mode 100644,000000..100644 --- a/qcsrc/server/mutators/gamemode_vip.qh +++ b/qcsrc/server/mutators/gamemode_vip.qh @@@ -1,34 -1,0 +1,31 @@@ +void vip_SpawnSoulGem(vector, float); +void vip_SetVIP(entity player, float tm); + +float g_vip_soulgems; + +.float gem_pickedup; +.float gem_dropped; + - float allowed_to_spawn; - float red_players, blue_players, yellow_players, pink_players; - float prev_total_players, total_players; +.float isvip; +.entity wps_vip; +float redgem_count, bluegem_count, yellowgem_count, pinkgem_count; + +.float drop_count; +.float drop_prevtime; +.float pickup_antispam; + +const float VIP_TEAMS = 17; +float vip_count[VIP_TEAMS]; + +// score rule declarations +#define SP_VIP_VIPKILLS 4 +#define SP_VIP_SURVIVALS 5 + +#define ST_VIP_SCORE 1 + +.float stat_vip_red; +.float stat_vip_blue; +.float stat_vip_yellow; +.float stat_vip_pink; + +float vip_teams; diff --cc qcsrc/server/mutators/mutator_bloodloss.qc index fb411ee33,08e14199e..c4cd8cf0a --- a/qcsrc/server/mutators/mutator_bloodloss.qc +++ b/qcsrc/server/mutators/mutator_bloodloss.qc @@@ -1,3 -1,3 +1,5 @@@ ++#include "../../common/vehicles/sv_vehicles.qh" ++ .float bloodloss_timer; MUTATOR_HOOKFUNCTION(bloodloss_PlayerThink) diff --cc qcsrc/server/mutators/mutator_buffs.qc index 60bd1d1a0,b2f800a27..93f022d3b --- a/qcsrc/server/mutators/mutator_buffs.qc +++ b/qcsrc/server/mutators/mutator_buffs.qc @@@ -1,3 -1,3 +1,7 @@@ ++#include "../../common/effects.qh" ++#include "../../common/buffs.qh" ++#include "../round_handler.qh" ++ float buffs_BuffModel_Customize() { entity player, myowner; @@@ -8,12 -8,8 +12,12 @@@ same_team = (SAME_TEAM(player, myowner) || SAME_TEAM(player, myowner)); if(myowner.alpha <= 0.5 && !same_team && myowner.alpha != 0) - return FALSE; + return false; + if(cvar("g_piggyback")) + if(player.pbhost == myowner || myowner.piggybacker) - return FALSE; // don't show to piggybacker's carrier, and don't show if carrier is carrying someone else ++ return false; // don't show to piggybacker's carrier, and don't show if carrier is carrying someone else + if(player == myowner || (IS_SPEC(other) && other.enemy == myowner)) { // somewhat hide the model, but keep the glow @@@ -25,15 -21,9 +29,15 @@@ self.effects = EF_FULLBRIGHT | EF_LOWPRECISION; self.alpha = 1; } - return TRUE; + return true; } +vector buff_GlowColor(entity buff) +{ + //if(buff.team) { return Team_ColorRGB(buff.team); } + return buff.color; +} + // buff item float buff_Waypoint_visible_for_player(entity plr) { @@@ -98,11 -88,11 +102,11 @@@ void buff_Respawn(entity ent if(autocvar_g_buffs_random_lifetime > 0) ent.lifetime = time + autocvar_g_buffs_random_lifetime; - pointparticles(particleeffectnum("electro_combo"), oldbufforigin + ((ent.mins + ent.maxs) * 0.5), '0 0 0', 1); - pointparticles(particleeffectnum("electro_combo"), CENTER_OR_VIEWOFS(ent), '0 0 0', 1); + Send_Effect(EFFECT_ELECTRO_COMBO, oldbufforigin + ((ent.mins + ent.maxs) * 0.5), '0 0 0', 1); + Send_Effect(EFFECT_ELECTRO_COMBO, CENTER_OR_VIEWOFS(ent), '0 0 0', 1); - + WaypointSprite_Ping(ent.buff_waypoint); - + sound(ent, CH_TRIGGER, "keepaway/respawn.wav", VOL_BASE, ATTEN_NONE); // ATTEN_NONE (it's a sound intended to be heard anywhere) } @@@ -200,9 -174,9 +204,9 @@@ void buff_Think( if(self.buffs != self.oldbuffs) { self.color = Buff_Color(self.buffs); - self.glowmod = ((self.team) ? Team_ColorRGB(self.team) + '0.1 0.1 0.1' : self.color); + self.glowmod = buff_GlowColor(self); self.skin = Buff_Skin(self.buffs); - + setmodel(self, "models/relics/relic.md3"); if(self.buff_waypoint) @@@ -245,17 -219,28 +249,17 @@@ if(!self.buff_activetime) { - self.buff_active = TRUE; + self.buff_active = true; sound(self, CH_TRIGGER, "misc/strength_respawn.wav", VOL_BASE, ATTN_NORM); - pointparticles(particleeffectnum("item_respawn"), CENTER_OR_VIEWOFS(self), '0 0 0', 1); + Send_Effect(EFFECT_ITEM_RESPAWN, CENTER_OR_VIEWOFS(self), '0 0 0', 1); } } - if(!self.buff_active) - { - self.alpha = 0.3; - self.effects &= ~(EF_FULLBRIGHT); - self.pflags = 0; - } - else + if(self.buff_active) { - self.alpha = 1; - self.effects |= EF_FULLBRIGHT; - self.light_lev = 220 + 36 * sin(time); - self.pflags = PFLAGS_FULLDYNAMIC; - if(self.team && !self.buff_waypoint) buff_Waypoint_Spawn(self); - + if(self.lifetime) if(time >= self.lifetime) buff_Respawn(self); @@@ -285,25 -270,6 +289,25 @@@ void buff_Reset( buff_Respawn(self); } +float buff_Customize() +{ + entity player = WaypointSprite_getviewentity(other); + if(!self.buff_active || (self.team && DIFF_TEAM(player, self))) + { + self.alpha = 0.3; + if(self.effects & EF_FULLBRIGHT) { self.effects &= ~(EF_FULLBRIGHT); } + self.pflags = 0; + } + else + { + self.alpha = 1; + if(!(self.effects & EF_FULLBRIGHT)) { self.effects |= EF_FULLBRIGHT; } + self.light_lev = 220 + 36 * sin(time); + self.pflags = PFLAGS_FULLDYNAMIC; + } - return TRUE; ++ return true; +} + void buff_Init(entity ent) { if(!cvar("g_buffs")) { remove(self); return; } @@@ -372,34 -337,30 +376,34 @@@ void buff_SpawnReplacement(entity ent, // mutator hooks MUTATOR_HOOKFUNCTION(buffs_PlayerDamage_SplitHealthArmor) { - if(frag_deathtype == DEATH_BUFF) { return FALSE; } - if(frag_deathtype == DEATH_BUFF_VENGEANCE) { return false; } // oh no you don't ++ if(frag_deathtype == DEATH_BUFF) { return false; } if(frag_target.buffs & BUFF_RESISTANCE) { - vector v = healtharmor_applydamage(50, autocvar_g_buffs_resistance_blockpercent, frag_deathtype, frag_damage); - damage_take = v.x; - damage_save = v.y; + float blockpercent = autocvar_g_buffs_resistance_blockpercent; +#ifdef CHAOS + if(DEATH_ISWEAPON(frag_deathtype, WEP_LIGHTSABRE) && autocvar_g_buffs_resistance_blockpercent > 0) { blockpercent *= 0.3; } +#endif + vector v = healtharmor_applydamage(50, blockpercent, frag_deathtype, frag_damage, autocvar_g_balance_armor_block_bycount); + damage_take = v_x; + damage_save = v_y; } - return FALSE; + return false; } void buff_Vengeance_DelayedDamage() { if(self.enemy) - Damage(self.enemy, self.owner, self.owner, self.dmg, DEATH_BUFF_VENGEANCE, self.enemy.origin, '0 0 0'); + Damage(self.enemy, self.owner, self.owner, self.dmg, DEATH_BUFF, self.enemy.origin, '0 0 0'); - + remove(self); return; } MUTATOR_HOOKFUNCTION(buffs_PlayerDamage_Calculate) { - if(frag_deathtype == DEATH_BUFF) { return FALSE; } - if(frag_deathtype == DEATH_BUFF_VENGEANCE) { return false; } // oh no you don't ++ if(frag_deathtype == DEATH_BUFF) { return false; } if(frag_target.buffs & BUFF_SPEED) if(frag_target != frag_attacker) @@@ -490,21 -450,16 +494,21 @@@ MUTATOR_HOOKFUNCTION(buffs_PlayerPhysic self.stat_sv_airspeedlimit_nonqw *= autocvar_g_buffs_disability_speed; } + if(self.buffs & BUFF_JUMP) + { + // automatically reset, no need to worry + self.stat_sv_jumpvelocity = autocvar_g_buffs_jump_height; + } + - return FALSE; + return false; } MUTATOR_HOOKFUNCTION(buffs_PlayerJump) { if(self.buffs & BUFF_JUMP) player_jumpheight = autocvar_g_buffs_jump_height; - self.stat_jumpheight = player_jumpheight; - return FALSE; + return false; } MUTATOR_HOOKFUNCTION(buffs_MonsterMove) @@@ -587,11 -542,10 +591,11 @@@ MUTATOR_HOOKFUNCTION(buffs_OnEntityPreS { entity e = spawn(); buff_SpawnReplacement(e, self); + self.classname = "item_removing"; - return TRUE; + return true; } } - return FALSE; + return false; } MUTATOR_HOOKFUNCTION(buffs_WeaponRate) @@@ -655,10 -612,13 +659,10 @@@ MUTATOR_HOOKFUNCTION(buffs_PlayerThink } else if(self.buffs & BUFF_INVISIBLE) { - if(time < self.strength_finished && g_instagib) - self.buff_invisible_prev_alpha = default_player_alpha; - else - self.buff_invisible_prev_alpha = self.alpha; + self.buff_invisible_prev_alpha = self.alpha; self.alpha = autocvar_g_buffs_invisible_alpha; } - + if(self.oldbuffs & BUFF_FLIGHT) self.gravity = self.buff_flight_prev_gravity; else if(self.buffs & BUFF_FLIGHT) @@@ -684,9 -644,9 +688,9 @@@ self.buff_model.customizeentityforclient = buffs_BuffModel_Customize; } self.buff_model.color = Buff_Color(self.buffs); - self.buff_model.glowmod = ((self.buff_model.team) ? Team_ColorRGB(self.buff_model.team) + '0.1 0.1 0.1' : self.buff_model.color); + self.buff_model.glowmod = buff_GlowColor(self.buff_model); self.buff_model.skin = Buff_Skin(self.buffs); - + self.effects |= EF_NOSHADOW; } else @@@ -789,7 -749,8 +793,7 @@@ void buffs_Initialize( precache_sound("keepaway/respawn.wav"); addstat(STAT_BUFFS, AS_INT, buffs); - - addstat(STAT_MOVEVARS_JUMPVELOCITY, AS_FLOAT, stat_jumpheight); + InitializeEntity(world, buffs_DelayedInit, INITPRIO_FINDTARGET); } diff --cc qcsrc/server/mutators/mutator_campcheck.qc index 59a7cc308,21dd71f98..793931815 --- a/qcsrc/server/mutators/mutator_campcheck.qc +++ b/qcsrc/server/mutators/mutator_campcheck.qc @@@ -1,11 -1,11 +1,13 @@@ ++#include "../round_handler.qh" ++ .float campcheck_nextcheck; .float campcheck_traveled_distance; MUTATOR_HOOKFUNCTION(campcheck_PlayerDies) { - Kill_Notification(NOTIF_ONE_ONLY, self, MSG_CENTER_CPID, CPID_CAMPCHECK); + Kill_Notification(NOTIF_ONE, self, MSG_CENTER_CPID, CPID_CAMPCHECK); - return FALSE; + return false; } MUTATOR_HOOKFUNCTION(campcheck_PlayerDamage) @@@ -58,13 -54,9 +60,12 @@@ MUTATOR_HOOKFUNCTION(campcheck_PlayerTh self.campcheck_nextcheck = time + autocvar_g_campcheck_interval; self.campcheck_traveled_distance = 0; } + - return FALSE; ++ return false; } + self.campcheck_nextcheck = time + autocvar_g_campcheck_interval; // one of the above checks failed, so keep the timer up to date - - return FALSE; + return false; } MUTATOR_HOOKFUNCTION(campcheck_PlayerSpawn) diff --cc qcsrc/server/mutators/mutator_dodging.qc index 5f8f09564,ad52d3e29..e4e492607 --- a/qcsrc/server/mutators/mutator_dodging.qc +++ b/qcsrc/server/mutators/mutator_dodging.qc @@@ -92,11 -78,12 +91,12 @@@ MUTATOR_HOOKFUNCTION(dodging_PlayerPhys // ramp up dodging speed by adding some velocity each frame.. TODO: do it! :D if (self.dodging_action == 1) { //disable jump key during dodge accel phase - if (self.movement_z > 0) self.movement_z = 0; + if (self.movement.z > 0) self.movement_z = 0; - self.velocity = + self.velocity += - + ((self.dodging_direction_y * velocity_difference) * v_right) - + ((self.dodging_direction_x * velocity_difference) * v_forward); + self.velocity + + ((self.dodging_direction.y * velocity_difference) * v_right) + + ((self.dodging_direction.x * velocity_difference) * v_forward); self.dodging_velocity_gain = self.dodging_velocity_gain - velocity_difference; } @@@ -173,14 -162,9 +173,10 @@@ MUTATOR_HOOKFUNCTION(dodging_GetPressed // print("dodging_PlayerPhysics\n"); float length; - float tap_direction_x; - float tap_direction_y; - - tap_direction_x = 0; - tap_direction_y = 0; + vector tap_direction = '0 0 0'; float frozen_dodging, frozen_no_doubletap; + float do_nothing = 0; frozen_dodging = (self.frozen && autocvar_sv_dodging_frozen); frozen_no_doubletap = (frozen_dodging && !autocvar_sv_dodging_frozen_doubletap); diff --cc qcsrc/server/mutators/mutator_freeze.qc index 4074a005b,000000000..6e9e4e3da mode 100644,000000..100644 --- a/qcsrc/server/mutators/mutator_freeze.qc +++ b/qcsrc/server/mutators/mutator_freeze.qc @@@ -1,303 -1,0 +1,305 @@@ ++#include "../round_handler.qh" ++ +void freeze_Unfreeze(entity ent) +{ + ent.freeze_flag = FROZEN_THAWING; // thawing is fine, as this is only checked when the player is frozen + + self.freezetag_frozen_time = 0; + self.freezetag_frozen_timeout = 0; + + Kill_Notification(NOTIF_ONE_ONLY, ent, MSG_CENTER_CPID, CPID_FREEZE); + + Unfreeze(ent); +} + + +MUTATOR_HOOKFUNCTION(freeze_PlayerSpawn) +{ + freeze_Unfreeze(self); + - return FALSE; ++ return false; +} + +MUTATOR_HOOKFUNCTION(freeze_PlayerDies) +{ + if(frag_deathtype == DEATH_HURTTRIGGER) - return FALSE; ++ return false; + + calculate_player_respawn_time(); // player doesn't actually die + - float can_freeze = FALSE; ++ float can_freeze = false; + + if(frag_target.cvar_cl_freeze) + if(cvar("g_freeze") == 1) - can_freeze = TRUE; ++ can_freeze = true; + + if(cvar("g_freeze") == 2) - can_freeze = TRUE; ++ can_freeze = true; + + if(cvar("g_freeze") == 3) - can_freeze = FALSE; // force off (easier than disabling & restarting, will fix later) ++ can_freeze = false; // force off (easier than disabling & restarting, will fix later) + + if(ITEM_DAMAGE_NEEDKILL(frag_deathtype)) + { + if(self.frozen) + self.freeze_flag = FROZEN_RESPAWNING; - return TRUE; ++ return true; + } + + if(can_freeze) + if(!frag_target.frozen) + { + if(!(teamplay && autocvar_g_freeze_noauto)) + if(IS_REAL_CLIENT(frag_target)) + Send_Notification(NOTIF_ONE_ONLY, frag_target, MSG_CENTER, CENTER_FREEZE_THAWING); + frag_target.health = 1; // "respawn" the player :P + + if(g_instagib) + frag_target.ammo_cells = start_ammo_cells; // we need more ammo in instagib, otherwise the player will defrost & die again + + if(frag_deathtype == DEATH_TEAMCHANGE || frag_deathtype == DEATH_AUTOTEAMCHANGE) + { + // let the player die, he will be automatically frozen when he respawns + if(self.frozen == 1) + freeze_Unfreeze(self); // remove ice + return 1; + } + + Freeze(frag_target, 0, 1, (teamplay && autocvar_g_freeze_noauto)); + if(autocvar_g_freeze_frozen_maxtime > 0) + self.freezetag_frozen_timeout = time + autocvar_g_freeze_frozen_maxtime; + self.freezetag_frozen_time = time; + } + - return FALSE; ++ return false; +} + +MUTATOR_HOOKFUNCTION(freeze_PlayerPreThink) +{ + if(self.deadflag != DEAD_NO || !IS_PLAYER(self) || gameover) - return FALSE; ++ return false; + + if(round_handler_IsActive()) + if(!round_handler_IsRoundStarted()) - return FALSE; ++ return false; + - float button_pressed = FALSE, n; ++ float button_pressed = false, n; + + if(teamplay && autocvar_g_freeze_noauto) + { + // copied from freezetag + if(self.frozen == 1) + { + // keep health = 1 + self.pauseregen_finished = time + autocvar_g_balance_pause_health_regen; + } + + if(self.frozen == 1) + { + button_pressed = (!autocvar_g_freeze_norespawn && (self.BUTTON_JUMP || self.BUTTON_USE)); // only detect jumps + if(g_lms || g_ca || g_jailbreak) { self.freeze_flag = FROZEN_RESPAWNING; } // these modes require player to die + if(self.freeze_flag == FROZEN_THAWING) + { + if(!button_pressed) + self.freeze_flag = FROZEN_WILLRESPAWN; + } + else if(self.freeze_flag == FROZEN_WILLRESPAWN) + { + if(button_pressed) + self.freeze_flag = FROZEN_RESPAWNING; + } + else if(self.freeze_flag == FROZEN_RESPAWNING) + { + if(time > self.respawn_time) + { + self.respawn_time = time + 1; // only retry once a second + if(self.nade) + toss_nade(self, '0 0 100', max(self.nade.wait, time + 0.05)); + PutClientInServer(); + } + + return 1; // if we get here, normal revivals have been cancelled + } + } + + if(self.frozen == 1) + { + float frozen_count = 0, nplayers = 0; + FOR_EACH_PLAYER(other) if(SAME_TEAM(self, other)) + { + ++nplayers; + if(other.frozen == 1) + ++frozen_count; + } + if(nplayers == frozen_count) + { + FOR_EACH_PLAYER(other) if(SAME_TEAM(self, other)) + { + other.freeze_flag = FROZEN_RESPAWNING; + other.respawn_time = time + autocvar_g_freeze_respawn_time; + PlayerScore_Add(other, SP_SCORE, -1); // lose score for this + } + + return 1; + } + } + + entity o; + o = world; + //if(self.freezetag_frozen_timeout > 0 && time < self.freezetag_frozen_timeout) + //if(self.iceblock) + //self.iceblock.alpha = ICE_MIN_ALPHA + (ICE_MAX_ALPHA - ICE_MIN_ALPHA) * (self.freezetag_frozen_timeout - time) / (self.freezetag_frozen_timeout - self.freezetag_frozen_time); + + if(self.freezetag_frozen_timeout > 0 && time >= self.freezetag_frozen_timeout) + n = -1; + else + { + vector revive_extra_size = '1 1 1' * autocvar_g_freezetag_revive_extra_size; + n = 0; + FOR_EACH_PLAYER(other) if(self != other) + { + if(other.deadflag == DEAD_NO) + if(other.frozen == 0) + { + if(SAME_TEAM(other, self)) + { + if(boxesoverlap(self.absmin - revive_extra_size, self.absmax + revive_extra_size, other.absmin, other.absmax)) + { + if(!o) + o = other; + if(self.frozen == 1) - other.reviving = TRUE; ++ other.reviving = true; + ++n; + } + } + } + } + } + + if(n && self.frozen == 1) // OK, there is at least one teammate reviving us + { + self.revive_progress = bound(0, self.revive_progress + frametime * max(1/60, autocvar_g_freezetag_revive_speed), 1); + self.health = max(1, self.revive_progress * start_health); + + if(self.revive_progress >= 1) + { + freeze_Unfreeze(self); + + if(n == -1) + { + Send_Notification(NOTIF_ONE, self, MSG_CENTER, CENTER_FREEZETAG_AUTO_REVIVED, autocvar_g_freeze_frozen_maxtime); + Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_FREEZETAG_AUTO_REVIVED, self.netname, autocvar_g_freeze_frozen_maxtime); + return 1; + } + + // EVERY team mate nearby gets a point (even if multiple!) + FOR_EACH_PLAYER(other) + { + if(other.reviving) + { + PlayerScore_Add(other, SP_SCORE, +1); + + nades_GiveBonus(other,autocvar_g_nades_bonus_score_low); + } + } + + Send_Notification(NOTIF_ONE, self, MSG_CENTER, CENTER_FREEZETAG_REVIVED, o.netname); + Send_Notification(NOTIF_ONE, o, MSG_CENTER, CENTER_FREEZETAG_REVIVE, self.netname); + Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_FREEZETAG_REVIVED, self.netname, o.netname); + } + + FOR_EACH_PLAYER(other) + { + if(other.reviving) + { + other.revive_progress = self.revive_progress; - other.reviving = FALSE; ++ other.reviving = false; + } + } + } + else if(!n && self.frozen == 1) // only if no teammate is nearby will we reset + { + self.revive_progress = bound(0, self.revive_progress - frametime * autocvar_g_freezetag_revive_clearspeed, 1); + self.health = max(1, self.revive_progress * start_health); + } + else if(!n && !self.frozen) + { + self.revive_progress = 0; // thawing nobody + } + } + else if(self.frozen == 1) // auto revive + { + float rspeed = autocvar_g_freeze_revive_speed; + if(autocvar_g_freeze_revive_speed_random) + rspeed *= (random() * autocvar_g_freeze_revive_speed_random); + + self.revive_progress = bound(0, self.revive_progress + frametime * rspeed, 1); + self.health = max(1, self.revive_progress * start_health); + + if(self.health >= autocvar_g_freeze_revive_minhealth) + button_pressed = (self.BUTTON_JUMP || self.BUTTON_USE); // we're about to defrost, only detect jumps + else + button_pressed = (self.BUTTON_ATCK || self.BUTTON_JUMP || self.BUTTON_ATCK2 || self.BUTTON_HOOK || self.BUTTON_USE); + + if(autocvar_g_freeze_norespawn) + button_pressed = 0; // don't allow re-spawning via jump/attack + + if(self.freeze_flag == FROZEN_THAWING) + { + if(!button_pressed) + self.freeze_flag = FROZEN_WILLRESPAWN; + } + else if(self.freeze_flag == FROZEN_WILLRESPAWN) + { + if(button_pressed) + self.freeze_flag = FROZEN_RESPAWNING; + } + else if(self.freeze_flag == FROZEN_RESPAWNING) + { + if(time > self.respawn_time) + { + self.respawn_time = time + 1; // only retry once a second + if(self.nade) + toss_nade(self, '0 0 100', max(self.nade.wait, time + 0.05)); + PutClientInServer(); + return 1; + } + } + + if(self.revive_progress >= 1) + freeze_Unfreeze(self); + } + - return FALSE; ++ return false; +} + +MUTATOR_HOOKFUNCTION(freeze_GetCvars) +{ + GetCvars_handleFloat(get_cvars_s, get_cvars_f, cvar_cl_freeze, "cl_freeze"); + - return FALSE; ++ return false; +} + +MUTATOR_HOOKFUNCTION(freeze_BuildMutatorsString) +{ + ret_string = strcat(ret_string, ":Freeze"); - return FALSE; ++ return false; +} + +MUTATOR_HOOKFUNCTION(freeze_BuildMutatorsPrettyString) +{ + ret_string = strcat(ret_string, ", Freeze"); - return FALSE; ++ return false; +} + +MUTATOR_DEFINITION(mutator_freeze) +{ + MUTATOR_HOOK(PlayerSpawn, freeze_PlayerSpawn, CBC_ORDER_ANY); + MUTATOR_HOOK(PlayerDies, freeze_PlayerDies, CBC_ORDER_ANY); + MUTATOR_HOOK(PlayerPreThink, freeze_PlayerPreThink, CBC_ORDER_FIRST); + MUTATOR_HOOK(GetCvars, freeze_GetCvars, CBC_ORDER_ANY); + MUTATOR_HOOK(BuildMutatorsString, freeze_BuildMutatorsString, CBC_ORDER_ANY); + MUTATOR_HOOK(BuildMutatorsPrettyString, freeze_BuildMutatorsPrettyString, CBC_ORDER_ANY); + - return FALSE; ++ return false; +} diff --cc qcsrc/server/mutators/mutator_freeze.qh index ad307ae65,000000000..3116a1715 mode 100644,000000..100644 --- a/qcsrc/server/mutators/mutator_freeze.qh +++ b/qcsrc/server/mutators/mutator_freeze.qh @@@ -1,6 -1,0 +1,11 @@@ ++#ifndef MUTATOR_FREEZE_H ++#define MUTATOR_FREEZE_H ++ +.float freeze_flag; // used to check if the player is going to respawn or thaw out +const float FROZEN_THAWING = 0; // player is thawing, don't take any action +const float FROZEN_WILLRESPAWN = 1; // waiting for player to release the trigger key +const float FROZEN_RESPAWNING = 2; // player released the key, respawn when we can + - .float cvar_cl_freeze; ++.float cvar_cl_freeze; ++ ++#endif diff --cc qcsrc/server/mutators/mutator_hats.qc index de107be52,000000000..98a7a38d7 mode 100644,000000..100644 --- a/qcsrc/server/mutators/mutator_hats.qc +++ b/qcsrc/server/mutators/mutator_hats.qc @@@ -1,180 -1,0 +1,180 @@@ +.entity hatentity; +.string cvar_cl_hat; +.float cvar_cl_nohats; +.string hatname; // only update when the player spawns + +void hats_Precache(string pattern) +{ + float globhandle, i, n; + string f; + - globhandle = search_begin(pattern, TRUE, FALSE); ++ globhandle = search_begin(pattern, true, false); + if (globhandle < 0) + return; + n = search_getsize(globhandle); + for (i = 0; i < n; ++i) + { + f = search_getfilename(globhandle, i); + precache_model(f); + } + search_end(globhandle); +} + +float hats_getscale(entity e) +{ + float s; + get_model_parameters(e.model, e.skin); + s = get_model_parameters_hat_scale; + get_model_parameters(string_null, 0); + + return s; +} + +vector hats_getheight(entity e) +{ + vector s; + get_model_parameters(e.model, e.skin); + s = get_model_parameters_hat_height; + get_model_parameters(string_null, 0); + + return s; +} +vector hats_getangles(entity e) +{ + vector s; + get_model_parameters(e.model, e.skin); + s = get_model_parameters_hat_angles; + get_model_parameters(string_null, 0); + + return s; +} + +void hat_Think() +{ + float tag_found; + self.nextthink = time; + if (self.owner.hatentity != self) + { + remove(self); + return; + } + if(Player_Trapped(self.owner)) + { + self.model = ""; + return; + } + if (self.hatname != self.owner.hatname || self.dmg != self.owner.modelindex || self.deadflag != self.owner.deadflag) + { + self.hatname = self.owner.hatname; + self.dmg = self.owner.modelindex; + self.deadflag = self.owner.deadflag; + if (self.owner.hatname != "" && fexists(strcat("models/hats/", self.owner.hatname, ".md3"))) + setmodel(self, strcat("models/hats/", self.owner.hatname, ".md3")); // precision set below + else + self.model = ""; + + if((tag_found = gettagindex(self.owner, "tag_head"))) + { + self.tag_index = tag_found; + self.tag_entity = self.owner; + setorigin(self, hats_getheight(self.owner)); + self.scale = hats_getscale(self.owner); + self.angles = hats_getangles(self.owner); + } + else + { + setattachment(self, self.owner, "head"); + setorigin(self, hats_getheight(self.owner)); + self.scale = hats_getscale(self.owner); + self.angles = hats_getangles(self.owner); + } + } + self.effects = self.owner.effects; + self.effects |= EF_LOWPRECISION; + self.effects = self.effects & EFMASK_CHEAP; // eat performance + if(self.scale < -1) + self.alpha = -1; + else if(self.owner.alpha == default_player_alpha) + self.alpha = default_weapon_alpha; + else if(self.owner.alpha != 0) + self.alpha = self.owner.alpha; + else + self.alpha = 1; + + self.glowmod = self.owner.glowmod; + self.colormap = self.owner.colormap; + + CSQCMODEL_AUTOUPDATE(); +} + +float hats_Customize() +{ - if(other.cvar_cl_nohats) { return FALSE; } - return TRUE; ++ if(other.cvar_cl_nohats) { return false; } ++ return true; +} + +void hats_SpawnHat() +{ + self.hatentity = spawn(); + self.hatentity.classname = "hatentity"; + self.hatentity.solid = SOLID_NOT; + self.hatentity.owner = self; + self.hatentity.hatentity = self.hatentity; + setorigin(self.hatentity, '0 0 0'); + self.hatentity.angles = '0 0 0'; + self.hatentity.think = hat_Think; + self.hatentity.nextthink = time; + self.hatentity.customizeentityforclient = hats_Customize; + + { + entity oldself = self; + self = self.hatentity; + CSQCMODEL_AUTOINIT(); + self = oldself; + } +} + +MUTATOR_HOOKFUNCTION(hats_PlayerSpawn) +{ + hats_SpawnHat(); + self.hatentity.alpha = default_weapon_alpha; +#ifdef ADAY + self.hatname = strzone("cowboy"); +#elif defined(XMAS) + self.hatname = strzone("santa"); +#else + self.hatname = strzone(self.cvar_cl_hat); +#endif + - return FALSE; ++ return false; +} + +MUTATOR_HOOKFUNCTION(hats_GetCvars) +{ + GetCvars_handleString(get_cvars_s, get_cvars_f, cvar_cl_hat, "cl_magical_hax"); + GetCvars_handleFloat(get_cvars_s, get_cvars_f, cvar_cl_nohats, "cl_nohats"); - return FALSE; ++ return false; +} + +MUTATOR_HOOKFUNCTION(hats_RemovePlayer) +{ + self.hatentity = world; + self.hatname = ""; + - return FALSE; ++ return false; +} + +MUTATOR_DEFINITION(mutator_hats) +{ + MUTATOR_HOOK(GetCvars, hats_GetCvars, CBC_ORDER_ANY); + MUTATOR_HOOK(PlayerSpawn, hats_PlayerSpawn, CBC_ORDER_ANY); + MUTATOR_HOOK(ClientDisconnect, hats_RemovePlayer, CBC_ORDER_ANY); + MUTATOR_HOOK(MakePlayerObserver, hats_RemovePlayer, CBC_ORDER_ANY); + + MUTATOR_ONADD + { + hats_Precache("models/hats/*.md3"); + } + - return FALSE; ++ return false; +} diff --cc qcsrc/server/mutators/mutator_instagib.qc index 671d49894,141172e2f..00396820c --- a/qcsrc/server/mutators/mutator_instagib.qc +++ b/qcsrc/server/mutators/mutator_instagib.qc @@@ -1,3 -1,3 +1,5 @@@ ++#include "../../common/buffs.qh" ++ void spawnfunc_item_minst_cells (void) { if (!g_instagib) { remove(self); return; } @@@ -60,17 -37,9 +62,17 @@@ void instagib_ammocheck( instagib_stop_countdown(self); else if (self.ammo_cells > 0 || (self.items & IT_UNLIMITED_WEAPON_AMMO) || (self.flags & FL_GODMODE)) instagib_stop_countdown(self); + else if(autocvar_g_rm) + { + if(!self.instagib_needammo) + { + Send_Notification(NOTIF_ONE, self, MSG_CENTER, CENTER_INSTAGIB_DOWNGRADE); - self.instagib_needammo = TRUE; ++ self.instagib_needammo = true; + } + } else { - self.instagib_needammo = TRUE; + self.instagib_needammo = true; if (self.health <= 5) { Damage(self, self, self, 5, DEATH_NOAMMO, self.origin, '0 0 0'); @@@ -142,9 -111,9 +144,9 @@@ MUTATOR_HOOKFUNCTION(instagib_MatchEnd MUTATOR_HOOKFUNCTION(instagib_MonsterLoot) { - other.monster_loot = spawnfunc_item_minst_cells; + other.monster_loot = ((random() > 0.5 && autocvar_g_instagib_use_normal_ammo && cvar("g_instagib_withmines")) ? spawnfunc_item_minst_rockets : spawnfunc_item_minst_cells); - return FALSE; + return false; } MUTATOR_HOOKFUNCTION(instagib_MonsterSpawn) @@@ -152,10 -121,18 +154,10 @@@ // always refill ammo if(self.monsterid == MON_MAGE) self.skin = 1; - - return FALSE; + + return false; } -MUTATOR_HOOKFUNCTION(instagib_BotShouldAttack) -{ - if(checkentity.items & IT_STRENGTH) - return true; - - return false; -} - MUTATOR_HOOKFUNCTION(instagib_MakePlayerObserver) { instagib_stop_countdown(self); @@@ -184,7 -161,57 +186,7 @@@ MUTATOR_HOOKFUNCTION(instagib_PlayerPow { if (!(self.effects & EF_FULLBRIGHT)) self.effects |= EF_FULLBRIGHT; - return FALSE; - - if (self.items & IT_STRENGTH) - { - play_countdown(self.strength_finished, "misc/poweroff.wav"); - if (time > self.strength_finished) - { - self.alpha = default_player_alpha; - self.exteriorweaponentity.alpha = default_weapon_alpha; - self.items &= ~IT_STRENGTH; - Send_Notification(NOTIF_ONE, self, MSG_CENTER, CENTER_POWERDOWN_INVISIBILITY); - } - } - else - { - if (time < self.strength_finished) - { - self.alpha = autocvar_g_instagib_invis_alpha; - self.exteriorweaponentity.alpha = autocvar_g_instagib_invis_alpha; - self.items |= IT_STRENGTH; - Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_POWERUP_INVISIBILITY, self.netname); - Send_Notification(NOTIF_ONE, self, MSG_CENTER, CENTER_POWERUP_INVISIBILITY); - } - } - - if (self.items & IT_INVINCIBLE) - { - play_countdown(self.invincible_finished, "misc/poweroff.wav"); - if (time > self.invincible_finished) - { - self.items &= ~IT_INVINCIBLE; - Send_Notification(NOTIF_ONE, self, MSG_CENTER, CENTER_POWERDOWN_SPEED); - } - } - else - { - if (time < self.invincible_finished) - { - self.items |= IT_INVINCIBLE; - Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_POWERUP_SPEED, self.netname); - Send_Notification(NOTIF_ONE, self, MSG_CENTER, CENTER_POWERUP_SPEED); - } - } - return false; -} - -MUTATOR_HOOKFUNCTION(instagib_PlayerPhysics) -{ - if(self.items & IT_INVINCIBLE) - self.stat_sv_maxspeed = self.stat_sv_maxspeed * autocvar_g_instagib_speed_highspeed; - + return false; } MUTATOR_HOOKFUNCTION(instagib_SplitHealthArmor) @@@ -204,17 -231,7 +206,17 @@@ MUTATOR_HOOKFUNCTION(instagib_ForbidThr MUTATOR_HOOKFUNCTION(instagib_PlayerDamage) { - if(autocvar_g_friendlyfire == 0 && SAME_TEAM(frag_target, frag_attacker) && IS_PLAYER(frag_target) && IS_PLAYER(frag_attacker)) + if(frag_deathtype == DEATH_NOAMMO) - return FALSE; ++ return false; + + if(autocvar_g_rm) + if(DEATH_ISWEAPON(frag_deathtype, WEP_DEVASTATOR)) + if(frag_attacker == frag_target || frag_target.classname == "nade") + frag_damage = 0; + + if((autocvar_g_rm && autocvar_g_rm_laser == 1) || autocvar_g_rm_laser == 2) + if(DEATH_ISWEAPON(frag_deathtype, WEP_ELECTRO)) + if(frag_attacker == frag_target || forbidWeaponUse(frag_attacker) == 2) frag_damage = 0; if(IS_PLAYER(frag_target)) @@@ -237,14 -249,12 +239,14 @@@ { if(frag_deathtype & HITTYPE_SECONDARY) { - frag_damage = frag_mirrordamage = 0; + if(!autocvar_g_instagib_blaster_keepdamage) + frag_damage = frag_mirrordamage = 0; - + if(frag_target != frag_attacker) { - if(frag_target.health > 0) { Send_Notification(NOTIF_ONE, frag_attacker, MSG_CENTER, CENTER_SECONDARY_NODAMAGE); } - frag_force = '0 0 0'; + if(frag_damage <= 0 && frag_target.health > 0) { Send_Notification(NOTIF_ONE, frag_attacker, MSG_CENTER, CENTER_SECONDARY_NODAMAGE); } + if(!autocvar_g_instagib_blaster_keepforce) + frag_force = '0 0 0'; } } else if(frag_target.armorvalue) @@@ -271,10 -281,10 +273,10 @@@ frag_mirrordamage = 0; } - if(frag_target.items & IT_STRENGTH) + if(frag_target.buffs & BUFF_INVISIBLE) yoda = 1; - return FALSE; + return false; } MUTATOR_HOOKFUNCTION(instagib_SetStartItems) @@@ -286,24 -296,19 +288,24 @@@ start_ammo_nails = warmup_start_ammo_nails = 0; start_ammo_cells = warmup_start_ammo_cells = cvar("g_instagib_ammo_start"); start_ammo_plasma = warmup_start_ammo_plasma = 0; - start_ammo_rockets = warmup_start_ammo_rockets = 0; + start_ammo_rockets = warmup_start_ammo_rockets = cvar("g_instagib_withmines_ammo_start");; start_ammo_fuel = warmup_start_ammo_fuel = 0; - start_weapons = warmup_start_weapons = WEPSET_VAPORIZER; + start_weapons = warmup_start_weapons = ((cvar("g_instagib_withmines")) ? (WEPSET_VAPORIZER | WEPSET_MINE_LAYER) : WEPSET_VAPORIZER); start_items |= IT_UNLIMITED_SUPERWEAPONS; - return FALSE; + return false; } MUTATOR_HOOKFUNCTION(instagib_FilterItem) { if(self.classname == "item_cells") - return true; // no normal cells? + if(!autocvar_g_instagib_use_normal_ammo) - return TRUE; ++ return true; + + if(self.classname == "item_rockets") + if(!autocvar_g_instagib_use_normal_ammo || !cvar("g_instagib_withmines")) - return TRUE; ++ return true; if(self.weapon == WEP_VAPORIZER && self.classname == "droppedweapon") { @@@ -313,16 -318,14 +315,16 @@@ if(self.weapon == WEP_DEVASTATOR || self.weapon == WEP_VORTEX) { - entity e = spawn(); + entity e = spawn(), oldself; setorigin(e, self.origin); - entity oldself; oldself = self; self = e; + self.noalign = oldself.noalign; + self.cnt = oldself.cnt; + self.team = oldself.team; spawnfunc_item_minst_cells(); self = oldself; - return TRUE; + return true; } if(self.flags & FL_POWERUP) @@@ -331,43 -334,39 +333,43 @@@ if(self.ammo_cells > autocvar_g_instagib_ammo_drop && self.classname != "item_minst_cells") self.ammo_cells = autocvar_g_instagib_ammo_drop; - if(self.ammo_cells && !self.weapon) - return false; - - return true; -} + if(self.classname != "item_minst_rockets") + self.ammo_rockets = bound(0, self.ammo_rockets, autocvar_g_instagib_ammo_rockets); -MUTATOR_HOOKFUNCTION(instagib_CustomizeWaypoint) -{ - entity e = WaypointSprite_getviewentity(other); + if((self.ammo_cells || self.ammo_supercells) && !self.weapon) - return FALSE; ++ return false; - // if you have the invisibility powerup, sprites ALWAYS are restricted to your team - // but only apply this to real players, not to spectators - if((self.owner.flags & FL_CLIENT) && (self.owner.items & IT_STRENGTH) && (e == other)) - if(DIFF_TEAM(self.owner, e)) - return true; + if(self.ammo_rockets && !self.weapon) - return FALSE; ++ return false; - return TRUE; - return false; ++ return true; } MUTATOR_HOOKFUNCTION(instagib_ItemCountdown) { switch(self.items) { - case IT_STRENGTH: item_name = "item-invis"; item_color = '0 0 1'; break; case IT_NAILS: item_name = "item-extralife"; item_color = '1 0 0'; break; - case IT_INVINCIBLE: item_name = "item-speed"; item_color = '1 0 1'; break; } - return FALSE; + return false; } +MUTATOR_HOOKFUNCTION(instagib_PlayerDies) +{ + if( (DEATH_ISWEAPON(frag_deathtype, WEP_VAPORIZER)) + || (autocvar_g_rm && DEATH_ISWEAPON(frag_deathtype, WEP_DEVASTATOR)) + || (((autocvar_g_rm && autocvar_g_rm_laser == 1) || autocvar_g_rm_laser == 2) && DEATH_ISWEAPON(frag_deathtype, WEP_ELECTRO)) + ) + frag_damage = 1000; // always gib if it was a vaporizer death + + (get_weaponinfo(WEP_VAPORIZER)).weaponthrowable = 1; // throwing is forbidden by a mutator hook, enabling this for drop on death + - return FALSE; ++ return false; +} + MUTATOR_HOOKFUNCTION(instagib_ItemTouch) { - if(self.ammo_cells) + if(self.ammo_cells || self.ammo_supercells) { // play some cool sounds ;) if (IS_CLIENT(other)) @@@ -396,12 -395,25 +398,12 @@@ MUTATOR_HOOKFUNCTION(instagib_OnEntityPreSpawn) { - if (!autocvar_g_powerups) { return false; } - if (!(self.classname == "item_strength" || self.classname == "item_invincible" || self.classname == "item_health_mega")) - return false; - - entity e = spawn(); - - if(random() < 0.3) - e.think = spawnfunc_item_strength; - else if(random() < 0.6) - e.think = instagib_health_mega; - else - e.think = spawnfunc_item_invincible; + if(WEP_CVAR_PRI(vaporizer, charge)) + if(self.classname == "item_cells" || self.classname == "item_minst_cells") + if(random() <= 0.5) + instagib_item_supercells(); - return FALSE; - e.nextthink = time + 0.1; - e.spawnflags = self.spawnflags; - e.noalign = self.noalign; - setorigin(e, self.origin); - - return true; ++ return false; } MUTATOR_HOOKFUNCTION(instagib_BuildMutatorsString) @@@ -445,23 -459,5 +447,23 @@@ MUTATOR_DEFINITION(mutator_instagib MUTATOR_HOOK(BuildMutatorsPrettyString, instagib_BuildMutatorsPrettyString, CBC_ORDER_ANY); MUTATOR_HOOK(SetModname, instagib_SetModname, CBC_ORDER_ANY); + MUTATOR_ONADD + { + precache_sound(W_Sound("rocket_impact")); + + if(cvar("g_instagib_withmines")) + (get_weaponinfo(WEP_MINE_LAYER)).spawnflags &= ~WEP_FLAG_MUTATORBLOCKED; + + g_instagib = cvar("g_instagib"); + } + + MUTATOR_ONREMOVE + { + if(cvar("g_instagib_withmines")) + (get_weaponinfo(WEP_MINE_LAYER)).spawnflags |= WEP_FLAG_MUTATORBLOCKED; + + g_instagib = 0; + } + - return FALSE; + return false; } diff --cc qcsrc/server/mutators/mutator_instagib.qh index 0fbd3f47e,000000000..3a1e4b868 mode 100644,000000..100644 --- a/qcsrc/server/mutators/mutator_instagib.qh +++ b/qcsrc/server/mutators/mutator_instagib.qh @@@ -1,3 -1,0 +1,8 @@@ ++#ifndef MUTATOR_INSTAGIB_H ++#define MUTATOR_INSTAGIB_H ++ +float g_instagib; + - void() spawnfunc_item_minst_cells; ++void() spawnfunc_item_minst_cells; ++ ++#endif diff --cc qcsrc/server/mutators/mutator_itemeditor.qc index 10276843b,000000000..b9134a3bf mode 100644,000000..100644 --- a/qcsrc/server/mutators/mutator_itemeditor.qc +++ b/qcsrc/server/mutators/mutator_itemeditor.qc @@@ -1,419 -1,0 +1,419 @@@ +float ie_itemcount; +.string ie_itemname; + +float ie_enabled; + +float ie_database_loaded; + +string ie_port_string; + +vector rintvec(vector vec) +{ + vector rinted; + rinted_x = rint(vec_x); + rinted_y = rint(vec_y); + rinted_z = rint(vec_z); + return rinted; +} + +void ie_Debug(string input) +{ + switch(autocvar_g_itemeditor_debug) + { + case 1: dprint(input); break; + case 2: print(input); break; + } +} + +entity ie_SpawnItem(float database); +string ie_ItemPort_Save(entity e, float database) +{ + // save item properties, and return them as a string + string s; + entity head = e; + + if(head) + { + // ---------------- ITEM PROPERTY STORAGE: SAVE ---------------- + if(database) { ie_port_string = strcat(ie_port_string, sprintf("\"%.9v\"", rintvec(head.origin)), " "); } + ie_port_string = strcat(ie_port_string, sprintf("\"%.9f\"", head.team), " "); + ie_port_string = strcat(ie_port_string, sprintf("\"%.9f\"", head.cnt), " "); + ie_port_string = strcat(ie_port_string, sprintf("\"%.9f\"", head.noalign), " "); + ie_port_string = strcat(ie_port_string, sprintf("\"%s\"", head.ie_itemname), " "); + } + + // now apply the array to a simple string, with the ; symbol separating items + s = ""; + if(ie_port_string) { s = strcat(s, ie_port_string, "; "); ie_port_string = string_null; } + + return s; +} + +// this will be removed when we have ID based item handling +string ie_FixItemName(string itname) +{ + switch(itname) + { + case "item_armor_small": case "smallarmor": case "armor_small": case "armorshard": + return "item_armor_small"; + case "item_armor_medium": case "mediumarmor": case "armor_medium": + return "item_armor_medium"; + case "item_armor_big": case "bigarmor": case "armor_big": + return "item_armor_big"; + case "item_armor_large": case "largearmor": case "armor_large": case "megaarmor": case "ma": + return "item_armor_large"; + case "item_health_small": case "smallhealth": case "health_small": + return "item_health_small"; + case "item_health_medium": case "mediumhealth": case "health_medium": + return "item_health_medium"; + case "item_health_large": case "largehealth": case "health_large": + return "item_health_large"; + case "item_health_mega": case "megahealth": case "health_mega": case "mh": case "megahealth": + return "item_health_mega"; + case "cells": case "item_cells": case "ammo_cells": + return "item_cells"; + case "bullets": case "nails": case "item_bullets": case "item_nails": case "ammo_bullets": case "ammo_nails": + return "item_bullets"; + case "rockets": case "explosives": case "item_rockets": case "item_explosives": case "ammo_rockets": case "ammo_explosives": + return "item_rockets"; + case "strength": case "item_strength": case "powerup_strength": + return "item_strength"; + case "invincible": case "item_invincible": case "powerup_invincible": + return "item_strength"; + default: + { + float i; + for(i = WEP_FIRST; i <= WEP_LAST; ++i) + if((get_weaponinfo(i)).netname == itname || (get_weaponinfo(i)).netname == substring(itname, 7, strlen(itname))) + return ((get_weaponinfo(i)).netname == substring(itname, 7, strlen(itname)) ? strcat("weapon_", itname) : itname); + return ""; + } + } +} + +void ie_SetItemType(entity e) +{ + string fixed_name = ie_FixItemName(e.ie_itemname); + if(fixed_name == "") { return; } + print("Attempting to spawn ", fixed_name, "\n"); + initialize_field_db(); + target_spawn_edit_entity(e, strcat("$ spawnfunc_", fixed_name), world, world, world, world, world); + + e.classname = "itemeditor_item"; +} + +entity ie_ItemPort_Load(string s, float database) +{ + // load item properties, and spawn a new item with them + float n; + entity e = world; + + // separate items between the ; symbols + n = tokenizebyseparator(s, "; "); + ie_port_string = argv(0); + + // now separate and apply the properties of each item + float argv_num = 0; + + tokenize_console(ie_port_string); + e = ie_SpawnItem(database); + + if(database) { setorigin(e, stov(argv(argv_num))); ++argv_num; } + e.team = stof(argv(argv_num)); ++argv_num; + e.cnt = stof(argv(argv_num)); ++argv_num; + e.noalign = stof(argv(argv_num)); ++argv_num; + e.ie_itemname = strzone(argv(argv_num)); ++argv_num; + + print(e.ie_itemname, "\n"); + ie_SetItemType(e); + + ie_port_string = string_null; // fully clear the string + + return e; +} + +void ie_Database_Save() +{ + // saves all items to the database file + entity head; + string file_name; + float file_get; + + file_name = strcat("itemeditor/storage_", autocvar_g_itemeditor_storage_name, "_", GetMapname(), ".txt"); + file_get = fopen(file_name, FILE_WRITE); + fputs(file_get, strcat("// itemeditor storage \"", autocvar_g_itemeditor_storage_name, "\" for map \"", GetMapname(), "\"")); + fputs(file_get, strcat(" containing ", ftos(ie_itemcount), " items\n")); + + for(head = world; (head = find(head, classname, "itemeditor_item")); ) + { + // use a line of text for each item, listing all properties - fputs(file_get, strcat(ie_ItemPort_Save(head, TRUE), "\n")); ++ fputs(file_get, strcat(ie_ItemPort_Save(head, true), "\n")); + } + fclose(file_get); +} + +void ie_Database_Load() +{ + // loads all items from the database file + string file_read, file_name; + float file_get; + + file_name = strcat("itemeditor/storage_", autocvar_g_itemeditor_storage_name, "_", GetMapname(), ".txt"); + file_get = fopen(file_name, FILE_READ); + if(file_get < 0) + { + ie_Debug(strcat("^3ITEMEDITOR - Server: ^7could not find storage file ^3", file_name, "^7, no items were loaded\n")); + } + else + { + for(;;) + { + file_read = fgets(file_get); + if(file_read == "") + break; + if(substring(file_read, 0, 2) == "//") + continue; + if(substring(file_read, 0, 1) == "#") + continue; + + entity e; - e = ie_ItemPort_Load(file_read, TRUE); ++ e = ie_ItemPort_Load(file_read, true); + } + ie_Debug(strcat("^3ITEMEDITOR - SERVER: ^7successfully loaded storage file ^3", file_name, "\n")); + } + fclose(file_get); + - ie_database_loaded = TRUE; ++ ie_database_loaded = true; +} + +void ie_Remove(entity e); +void ie_Database_Unload() +{ + entity head; + for(head = world; (head = find(head, classname, "itemeditor_item")); ) + ie_Remove(head); - ie_database_loaded = FALSE; ++ ie_database_loaded = false; +} + +void ie_Think() +{ + self.nextthink = time; + + // decide if and how this item can be grabbed + if(autocvar_g_itemeditor_readonly) + self.grab = 0; // no grabbing + else + self.grab = 3; // anyone +} + +entity ie_SpawnItem(float database) +{ + entity e = spawn(); + e.classname = "itemeditor_item"; + + if(!database) + { + // set origin and direction based on player position and view angle + makevectors(self.v_angle); + WarpZone_TraceLine(self.origin + self.view_ofs, self.origin + self.view_ofs + v_forward * autocvar_g_itemeditor_spawn_distance, MOVE_NORMAL, self); + setorigin(e, trace_endpos); + } + + if(IS_REAL_CLIENT(self)) { print_to(self, "Spawned new item entity"); } + + ie_itemcount += 1; + + return e; +} + +void ie_Remove(entity e) +{ + if(e.ie_itemname) { strunzone(e.ie_itemname); e.ie_itemname = string_null; } + RemoveItem(e); + e = world; + + ie_itemcount -= 1; +} + +float ie_CheckItem(entity e) +{ - if(!e || e.classname != "itemeditor_item") { return FALSE; } - return TRUE; ++ if(!e || e.classname != "itemeditor_item") { return false; } ++ return true; +} + +MUTATOR_HOOKFUNCTION(ie_ClientCommand) +{ + if(cmd_name == "itemeditor") + { - if(!ie_enabled || autocvar_g_itemeditor_readonly) { sprint(self, "Item editing is currently disabled\n"); return TRUE; } ++ if(!ie_enabled || autocvar_g_itemeditor_readonly) { sprint(self, "Item editing is currently disabled\n"); return true; } + + if(argv(1) == "spawn") + { - if(!argv(2) || argv(2) == "") { sprint(self, "You must specify an item name\n"); return TRUE; } - if(ie_itemcount >= autocvar_g_itemeditor_max) { sprint(self, "Too many items!\n"); return TRUE; } ++ if(!argv(2) || argv(2) == "") { sprint(self, "You must specify an item name\n"); return true; } ++ if(ie_itemcount >= autocvar_g_itemeditor_max) { sprint(self, "Too many items!\n"); return true; } + + string item_name = strzone(argv(2)); - if(ie_FixItemName(item_name) == "") { sprint(self, "Invalid item\n"); strunzone(item_name); return TRUE; } ++ if(ie_FixItemName(item_name) == "") { sprint(self, "Invalid item\n"); strunzone(item_name); return true; } + + WarpZone_TraceLine(self.origin + self.view_ofs, self.origin + self.view_ofs + v_forward * 100, MOVE_NORMAL, self); - entity e = ie_SpawnItem(FALSE); ++ entity e = ie_SpawnItem(false); + setorigin(e, trace_endpos); + e.ie_itemname = item_name; + ie_SetItemType(e); - return TRUE; ++ return true; + } + + if(argv(1) == "remove") + { + entity e, theitem = world; - for(e = WarpZone_FindRadius(self.origin + self.view_ofs + v_forward * 50, 50, FALSE); e; e = e.chain) ++ for(e = WarpZone_FindRadius(self.origin + self.view_ofs + v_forward * 50, 50, false); e; e = e.chain) + if(e.classname == "itemeditor_item") + { + print("Got one\n"); + theitem = e; + break; + } + + + if(ie_CheckItem(theitem)) + { + ie_Remove(theitem); + sprint(self, "Successfully removed an item\n"); - return TRUE; ++ return true; + } + else + { + sprint(self, "Item not found\n"); - return TRUE; ++ return true; + } + } + + if(argv(1) == "edit") + { - if(!argv(2)) { sprint(self, "You must specify a property edit\n"); return TRUE; } - if(!ie_enabled) { sprint(self, "Editing is not enabled\n"); return TRUE; } ++ if(!argv(2)) { sprint(self, "You must specify a property edit\n"); return true; } ++ if(!ie_enabled) { sprint(self, "Editing is not enabled\n"); return true; } + + entity e, theitem = world; - for(e = WarpZone_FindRadius(self.origin + self.view_ofs + v_forward * 50, 50, FALSE); e; e = e.chain) ++ for(e = WarpZone_FindRadius(self.origin + self.view_ofs + v_forward * 50, 50, false); e; e = e.chain) + if(e.classname == "itemeditor_item") + { + theitem = e; + break; + } + + if(ie_CheckItem(theitem)) + if(argv(3)) + switch(argv(2)) + { - case "cnt": theitem.cnt = stof(argv(3)); return TRUE; - case "team": theitem.team = stof(argv(3)); return TRUE; - case "noalign": case "float": theitem.noalign = stof(argv(3)); return TRUE; - default: print_to(self, "Unknown option"); return TRUE; ++ case "cnt": theitem.cnt = stof(argv(3)); return true; ++ case "team": theitem.team = stof(argv(3)); return true; ++ case "noalign": case "float": theitem.noalign = stof(argv(3)); return true; ++ default: print_to(self, "Unknown option"); return true; + } + } + + sprint(self, "Command was not handled\n"); - return TRUE; ++ return true; + } + - return FALSE; ++ return false; +} + +MUTATOR_HOOKFUNCTION(ie_ServerCommand) +{ + if(cmd_name == "itemeditor") + { + switch(argv(1)) + { + case "enable": + case "start": + { - if(!ie_database_loaded) { print("Enabling editing while database is unloaded could cause chaos, stopping\n"); return TRUE; } - ie_enabled = TRUE; ++ if(!ie_database_loaded) { print("Enabling editing while database is unloaded could cause chaos, stopping\n"); return true; } ++ ie_enabled = true; + bprint("Item editing has been enabled!\n"); - return TRUE; ++ return true; + } + case "disable": + case "stop": + { - ie_enabled = FALSE; ++ ie_enabled = false; + bprint("Item editing has been disabled!\n"); - return TRUE; ++ return true; + } + case "load": + { - if(ie_itemcount > 0 || ie_database_loaded) { print("Item database has already been loaded\n"); return TRUE; } ++ if(ie_itemcount > 0 || ie_database_loaded) { print("Item database has already been loaded\n"); return true; } + + ie_Database_Load(); + bprint("Item database has been loaded!\n"); - return TRUE; ++ return true; + } + case "unload": + { - if(ie_itemcount <= 0 || !ie_database_loaded) { print("Item database has already been unloaded\n"); return TRUE; } ++ if(ie_itemcount <= 0 || !ie_database_loaded) { print("Item database has already been unloaded\n"); return true; } + - ie_enabled = FALSE; // we must disable this, so as to not break stuff ++ ie_enabled = false; // we must disable this, so as to not break stuff + ie_Database_Unload(); + bprint("Item database has been unloaded!\n"); - return TRUE; ++ return true; + } + case "removeitems": + { + entity head; + for(head = world; (head = findflags(head, flags, FL_ITEM)); ) + if(head.items || head.weapon) + if((head.classname != "itemeditor_item" && head.classname != "droppedweapon") || argv(2) == "all") + RemoveItem(head); + + bprint("Regular items removed!\n"); - return TRUE; ++ return true; + } + } + print("Command was not handled\n"); - return TRUE; ++ return true; + } - return FALSE; ++ return false; +} + +float ie_autosave_time; +MUTATOR_HOOKFUNCTION(ie_StartFrame) +{ + entity head; + for(head = world; (head = find(head, classname, "itemeditor_item")); ) + head.grab = (autocvar_g_itemeditor_readonly || !ie_enabled) ? 0 : 3; + + if(!ie_enabled) - return FALSE; ++ return false; + if(!ie_database_loaded) - return FALSE; ++ return false; + if(!autocvar_g_itemeditor_storage_autosave) - return FALSE; ++ return false; + if(time < ie_autosave_time) - return FALSE; ++ return false; + ie_autosave_time = time + autocvar_g_itemeditor_storage_autosave; + + ie_Database_Save(); + - return TRUE; ++ return true; +} + +void ie_DelayedInit() +{ + ie_Database_Load(); +} + +MUTATOR_DEFINITION(mutator_itemeditor) +{ + MUTATOR_HOOK(SV_ParseClientCommand, ie_ClientCommand, CBC_ORDER_ANY); + MUTATOR_HOOK(SV_ParseServerCommand, ie_ServerCommand, CBC_ORDER_ANY); + MUTATOR_HOOK(SV_StartFrame, ie_StartFrame, CBC_ORDER_ANY); + + MUTATOR_ONADD + { + ie_autosave_time = time + autocvar_g_itemeditor_storage_autosave; // don't save the first server frame + if(autocvar_g_itemeditor_storage_autoload) + InitializeEntity(world, ie_DelayedInit, INITPRIO_LAST); + } + - return FALSE; ++ return false; +} diff --cc qcsrc/server/mutators/mutator_multijump.qc index 090bdfe2a,d36f3ecbe..7a837e836 --- a/qcsrc/server/mutators/mutator_multijump.qc +++ b/qcsrc/server/mutators/mutator_multijump.qc @@@ -5,28 -4,21 +5,28 @@@ MUTATOR_HOOKFUNCTION(multijump_PlayerPhysics) { if(self.flags & FL_ONGROUND) - self.multijump_count = 0; + { + if (autocvar_g_multijump > 0) + self.multijump_count = 0; + else + self.multijump_count = -2; // the cvar value for infinite jumps is -1, so this needs to be smaller + } - return FALSE; + return false; } MUTATOR_HOOKFUNCTION(multijump_PlayerJump) { + if (self.cvar_cl_multijump) if (self.flags & FL_JUMPRELEASED && !(self.flags & FL_ONGROUND)) // jump button pressed this frame and we are in midair - self.multijump_ready = TRUE; // this is necessary to check that we released the jump button and pressed it again + self.multijump_ready = true; // this is necessary to check that we released the jump button and pressed it again else - self.multijump_ready = FALSE; + self.multijump_ready = false; - if(!player_multijump && self.multijump_ready && self.multijump_count < autocvar_g_multijump && self.velocity_z > autocvar_g_multijump_speed && vlen(self.velocity) <= autocvar_g_multijump_maxspeed) - if(!player_multijump && self.multijump_ready && (autocvar_g_multijump == -1 || self.multijump_count < autocvar_g_multijump) && self.velocity.z > autocvar_g_multijump_speed) ++ if(!player_multijump && self.multijump_ready && (autocvar_g_multijump == -1 || self.multijump_count < autocvar_g_multijump) && self.velocity.z > autocvar_g_multijump_speed && vlen(self.velocity) <= autocvar_g_multijump_maxspeed) { if (autocvar_g_multijump) + if (self.cvar_cl_multijump) { if (autocvar_g_multijump_add == 0) // in this case we make the z velocity == jumpvelocity { @@@ -50,30 -42,23 +50,30 @@@ vlen(vec2(self.velocity)), // current xy speed vlen(vec2(antilag_takebackavgvelocity(self, max(self.lastteleporttime + sys_frametime, time - 0.25), time))) // average xy topspeed over the last 0.25 secs ); - makevectors(self.v_angle_y * '0 1 0'); - wishvel = v_forward * self.movement_x + v_right * self.movement_y; + makevectors(self.v_angle.y * '0 1 0'); + wishvel = v_forward * self.movement.x + v_right * self.movement.y; wishdir = normalize(wishvel); - self.velocity_x = wishdir_x * curspeed; // allow "dodging" at a multijump - self.velocity_y = wishdir_y * curspeed; + self.velocity_x = wishdir.x * curspeed; // allow "dodging" at a multijump + self.velocity_y = wishdir.y * curspeed; // keep velocity_z unchanged! } - self.multijump_count += 1; + if (autocvar_g_multijump > 0) + self.multijump_count += 1; } } - self.multijump_ready = FALSE; // require releasing and pressing the jump button again for the next jump + self.multijump_ready = false; // require releasing and pressing the jump button again for the next jump } - return FALSE; + return false; } +MUTATOR_HOOKFUNCTION(multijump_GetCvars) +{ + GetCvars_handleFloat(get_cvars_s, get_cvars_f, cvar_cl_multijump, "cl_multijump"); - return FALSE; ++ return false; +} + MUTATOR_HOOKFUNCTION(multijump_BuildMutatorsString) { ret_string = strcat(ret_string, ":multijump"); diff --cc qcsrc/server/mutators/mutator_nades.qc index 72db5fde8,3932ce407..3de5608a6 --- a/qcsrc/server/mutators/mutator_nades.qc +++ b/qcsrc/server/mutators/mutator_nades.qc @@@ -1,3 -1,3 +1,6 @@@ ++#include "../../common/effects.qh" ++#include "../round_handler.qh" ++ .entity nade_spawnloc; void nade_timer_think() @@@ -211,8 -212,8 +214,8 @@@ void nade_napalm_boom( void nade_ice_freeze(entity freezefield, entity frost_target, float freeze_time) { frost_target.frozen_by = freezefield.realowner; - pointparticles(particleeffectnum("electro_impact"), frost_target.origin, '0 0 0', 1); + Send_Effect(EFFECT_ELECTRO_IMPACT, frost_target.origin, '0 0 0', 1); - Freeze(frost_target, 1/freeze_time, 3, FALSE); + Freeze(frost_target, 1/freeze_time, 3, false); if(frost_target.ballcarried) if(g_keepaway) { ka_DropEvent(frost_target); } else { DropBall(frost_target.ballcarried, frost_target.origin, frost_target.velocity);} @@@ -268,10 -268,10 +271,10 @@@ void nade_ice_think( float randomw; randomw = random()*M_PI*2; vector randomp; - randomp_x = randomr*cos(randomw); - randomp_y = randomr*sin(randomw); - randomp_z = 1; + randomp.x = randomr*cos(randomw); + randomp.y = randomr*sin(randomw); + randomp.z = 1; - pointparticles(particleeffectnum("electro_muzzleflash"), self.origin + randomp, '0 0 0', 1); + Send_Effect(EFFECT_ELECTRO_MUZZLEFLASH, self.origin + randomp, '0 0 0', 1); if(time >= self.nade_special_time) { @@@ -416,21 -417,21 +419,21 @@@ void nade_heal_touch( if ( other.health < maxhealth ) { if ( self.nade_show_particles ) - pointparticles(particleeffectnum("healing_fx"), other.origin, '0 0 0', 1); + Send_Effect(EFFECT_HEALING, other.origin, '0 0 0', 1); other.health = min(other.health+health_factor, maxhealth); } - other.pauserothealth_finished = max(other.pauserothealth_finished, time + autocvar_g_balance_pause_health_rot); + other.pauserothealth_finished = max(other.pauserothealth_finished, time + autocvar_g_balance_pause_health_rot); } else if ( health_factor < 0 ) { Damage(other,self,self.realowner,-health_factor,DEATH_NADE_HEAL,other.origin,'0 0 0'); } - + } - - if ( IS_REAL_CLIENT(other) || (other.vehicle_flags & VHF_ISVEHICLE) ) + + if ( IS_REAL_CLIENT(other) || IS_VEHICLE(other) ) { - entity show_red = (other.vehicle_flags & VHF_ISVEHICLE) ? other.owner : other; + entity show_red = IS_VEHICLE(other) ? other.owner : other; show_red.stat_healing_orb = time+0.1; show_red.stat_healing_orb_alpha = 0.75 * (self.ltime - time) / self.healer_lifetime; } @@@ -832,24 -831,24 +835,24 @@@ void nade_prime( float CanThrowNade() { if(self.vehicle) - return FALSE; + return false; if(gameover) - return FALSE; + return false; if(self.deadflag != DEAD_NO) - return FALSE; + return false; if (!autocvar_g_nades) - return FALSE; // allow turning them off mid match + return false; // allow turning them off mid match - if(forbidWeaponUse()) + if(forbidWeaponUse(self)) - return FALSE; + return false; if (!IS_PLAYER(self)) - return FALSE; + return false; - return TRUE; + return true; } void nades_CheckThrow() @@@ -1143,9 -1127,9 +1146,9 @@@ MUTATOR_HOOKFUNCTION(nades_MonsterDies MUTATOR_HOOKFUNCTION(nades_RemovePlayer) { - nades_Clear(self, FALSE); - nades_Clear(self); ++ nades_Clear(self, false); nades_RemoveBonus(self); - return FALSE; + return false; } MUTATOR_HOOKFUNCTION(nades_SpectateCopy) diff --cc qcsrc/server/mutators/mutator_nades.qh index 35c98fca3,1a0dd5252..49f31515a --- a/qcsrc/server/mutators/mutator_nades.qh +++ b/qcsrc/server/mutators/mutator_nades.qh @@@ -17,8 -20,8 +20,8 @@@ void toss_nade(entity e, vector _velocity, float _time); - // Remove nades that are being thrown, if allplayers is TRUE, all clients EXCEPT player are checked -// Remove nades that are being thrown -void(entity player) nades_Clear; ++// Remove nades that are being thrown, if allplayers is true, all clients EXCEPT player are checked +void(entity player, float allplayers) nades_Clear; // Give a bonus grenade to a player void(entity player, float score) nades_GiveBonus; diff --cc qcsrc/server/mutators/mutator_new_toys.qc index 5a6342999,5aa94b187..f5d1f1a3d --- a/qcsrc/server/mutators/mutator_new_toys.qc +++ b/qcsrc/server/mutators/mutator_new_toys.qc @@@ -68,11 -68,10 +68,11 @@@ roflsound "New toys, new toys!" sound .string new_toys; --float autocvar_g_new_toys_autoreplace; - float autocvar_g_new_toys_use_pickupsound; - #define NT_AUTOREPLACE_NEVER 0 - #define NT_AUTOREPLACE_ALWAYS 1 - #define NT_AUTOREPLACE_RANDOM 2 -const float NT_AUTOREPLACE_NEVER = 0; -const float NT_AUTOREPLACE_ALWAYS = 1; -const float NT_AUTOREPLACE_RANDOM = 2; ++int autocvar_g_new_toys_autoreplace; ++bool autocvar_g_new_toys_use_pickupsound; ++const int NT_AUTOREPLACE_NEVER = 0; ++const int NT_AUTOREPLACE_ALWAYS = 1; ++const int NT_AUTOREPLACE_RANDOM = 2; MUTATOR_HOOKFUNCTION(nt_SetModname) { @@@ -80,7 -79,7 +80,7 @@@ return 0; } --float nt_IsNewToy(float w) ++bool nt_IsNewToy(int w) { switch(w) { diff --cc qcsrc/server/mutators/mutator_overkill.qc index 7c3a35fe4,1d151fc3e..119c776ef --- a/qcsrc/server/mutators/mutator_overkill.qc +++ b/qcsrc/server/mutators/mutator_overkill.qc @@@ -199,44 -197,45 +199,44 @@@ MUTATOR_HOOKFUNCTION(ok_PlayerSpawn MUTATOR_HOOKFUNCTION(ok_OnEntityPreSpawn) { - if(autocvar_g_powerups) - if(autocvar_g_overkill_powerups_replace) + // powerups don't actually exist, so we can abuse them as much as we want + if(self.classname == "item_strength") { - if(self.classname == "item_strength") - { - entity wep = spawn(); - setorigin(wep, self.origin); - setmodel(wep, "models/weapons/g_ok_hmg.md3"); - wep.classname = "weapon_hmg"; - wep.ok_item = true; - wep.noalign = self.noalign; - wep.cnt = self.cnt; - wep.team = self.team; - wep.respawntime = autocvar_g_overkill_superguns_respawn_time; - wep.pickup_anyway = true; - wep.think = spawnfunc_weapon_hmg; - wep.nextthink = time + 0.1; - return true; - } + self.classname = "item_removing"; // avoid letting other mutators use it + entity wep = spawn(); + setorigin(wep, self.origin); + setmodel(wep, "models/weapons/g_ok_hmg.md3"); + wep.classname = "weapon_hmg"; - wep.ok_item = TRUE; ++ wep.ok_item = true; + wep.noalign = self.noalign; + wep.cnt = self.cnt; + wep.team = self.team; + wep.respawntime = autocvar_g_overkill_superguns_respawn_time; - wep.pickup_anyway = TRUE; ++ wep.pickup_anyway = true; + wep.think = spawnfunc_weapon_hmg; + wep.nextthink = time + 0.1; - return TRUE; ++ return true; + } - if(self.classname == "item_invincible") - { - entity wep = spawn(); - setorigin(wep, self.origin); - setmodel(wep, "models/weapons/g_ok_rl.md3"); - wep.classname = "weapon_rpc"; - wep.ok_item = true; - wep.noalign = self.noalign; - wep.cnt = self.cnt; - wep.team = self.team; - wep.respawntime = autocvar_g_overkill_superguns_respawn_time; - wep.pickup_anyway = true; - wep.think = spawnfunc_weapon_rpc; - wep.nextthink = time + 0.1; - return true; - } + if(self.classname == "item_invincible") + { + self.classname = "item_removing"; // avoid letting other mutators use it + entity wep = spawn(); + setorigin(wep, self.origin); + setmodel(wep, "models/weapons/g_ok_rl.md3"); + wep.classname = "weapon_rpc"; - wep.ok_item = TRUE; ++ wep.ok_item = true; + wep.noalign = self.noalign; + wep.cnt = self.cnt; + wep.team = self.team; + wep.respawntime = autocvar_g_overkill_superguns_respawn_time; - wep.pickup_anyway = TRUE; ++ wep.pickup_anyway = true; + wep.think = spawnfunc_weapon_rpc; + wep.nextthink = time + 0.1; - return TRUE; ++ return true; } - return FALSE; + return false; } MUTATOR_HOOKFUNCTION(ok_ItemRemove) diff --cc qcsrc/server/mutators/mutator_physical_items.qc index 1b7815716,dd2d8556d..f92d3625c --- a/qcsrc/server/mutators/mutator_physical_items.qc +++ b/qcsrc/server/mutators/mutator_physical_items.qc @@@ -107,9 -94,8 +107,9 @@@ MUTATOR_HOOKFUNCTION(item_spawning self.effects |= EF_NODRAW; // hide the original weapon self.movetype = MOVETYPE_FOLLOW; self.aiment = wep; // attach the original weapon + self.SendEntity = func_null; - return FALSE; + return false; } MUTATOR_DEFINITION(mutator_physical_items) diff --cc qcsrc/server/mutators/mutator_piggyback.qc index 668766767,000000000..da8aacc75 mode 100644,000000..100644 --- a/qcsrc/server/mutators/mutator_piggyback.qc +++ b/qcsrc/server/mutators/mutator_piggyback.qc @@@ -1,295 -1,0 +1,295 @@@ +.float cvar_cl_nocarry; + +entity pb_TailOf(entity p) +{ + while(p.piggybacker) + p = p.piggybacker; + return p; +} + +entity pb_RootOf(entity p) +{ + while(p.pbhost) + p = p.pbhost; + return p; +} + +.float pb_oldmoveflags; +.float pb_oldverticalfly; +void pb_Attach(entity host, entity slave) +{ + entity root = pb_RootOf(host); + host = pb_TailOf(host); + + if(host == slave || root == slave || host == slave.piggybacker || slave == host.piggybacker) + return; + + host.piggybacker = slave; + slave.movetype = MOVETYPE_FOLLOW; + slave.aiment = host.pbent; + slave.pbhost = host; + slave.pb_oldsolid = slave.solid; + slave.solid = SOLID_CORPSE; + slave.pb_canattach = 0; + + if(IS_MONSTER(host)) + { + host.pb_oldmoveflags = host.monster_moveflags; + host.pb_oldverticalfly = (host.spawnflags & MONSTERFLAG_FLY_VERTICAL); + host.spawnflags |= MONSTERFLAG_FLY_VERTICAL; + host.monster_moveflags = MONSTER_MOVE_NOMOVE; + } + + RemoveGrapplingHook(slave); + + Send_Notification(NOTIF_ONE, slave, MSG_CENTER, CENTER_PIGGYBACK_RIDING, (IS_MONSTER(host)) ? host.monster_name : host.netname); + if(IS_PLAYER(host)) + Send_Notification(NOTIF_ONE, host, MSG_CENTER, CENTER_PIGGYBACK_CARRYING, slave.netname); +} + +void pb_Detach(entity host) +{ + entity slave = host.piggybacker; + + if(!slave) + return; + + slave.aiment = world; + slave.pbhost = world; + + if(IS_MONSTER(host)) + { + host.monster_moveto = '0 0 0'; + host.monster_moveflags = host.pb_oldmoveflags; + if(!host.pb_oldverticalfly) { host.spawnflags &= ~MONSTERFLAG_FLY_VERTICAL; } + } + + if(IS_PLAYER(slave)) + { + // this doesn't happen when we're fixing a broken reference + + if(slave.movetype == MOVETYPE_FOLLOW) // don't reset if player was killed + slave.movetype = MOVETYPE_WALK; + slave.velocity = '0 0 0'; + slave.solid = slave.pb_oldsolid; + + tracebox(host.origin, slave.mins, slave.maxs, slave.origin, MOVE_NOMONSTERS, slave); + + if(trace_fraction < 1) + setorigin(slave, trace_endpos); // fix player glitching out of the world + + Kill_Notification(NOTIF_ONE, slave, MSG_CENTER_CPID, CPID_PIGGYBACK); + if(IS_PLAYER(host)) + Kill_Notification(NOTIF_ONE, host, MSG_CENTER_CPID, CPID_PIGGYBACK); + } + + host.piggybacker = world; +} + +void pb_PBEntThink() +{ + setorigin(self, self.owner.origin + '0 0 0.82' * self.owner.maxs_z); + self.nextthink = time; +} + +void pb_FixPBEnt(entity p) +{ + entity e = spawn(); + e.owner = p; + e.classname = "pb_ent"; + e.think = pb_PBEntThink; + e.nextthink = time; + p.pbent = e; +} + +#define BROKEN_PBREF(e) ((e).piggybacker && ((e).piggybacker.pbhost != (e) || (e).piggybacker.movetype != MOVETYPE_FOLLOW)) + +MUTATOR_HOOKFUNCTION(pb_MatchEnd) +{ + entity head; + FOR_EACH_PLAYER(head) { pb_Detach(head); } + FOR_EACH_MONSTER(head) { pb_Detach(head); } - return FALSE; ++ return false; +} + +MUTATOR_HOOKFUNCTION(pb_PlayerUseKey) +{ - if(MUTATOR_RETURNVALUE || gameover) { return FALSE; } ++ if(MUTATOR_RETURNVALUE || gameover) { return false; } + + if(self.pbhost) + { + pb_Detach(self.pbhost); - return TRUE; ++ return true; + } + if(self.piggybacker) + { + pb_Detach(self); - return TRUE; ++ return true; + } + + if(!self.vehicle) + if(!Player_Trapped(self)) + { + entity head, closest_target = world; + - head = WarpZone_FindRadius(self.origin, autocvar_g_vehicles_enter_radius, TRUE); ++ head = WarpZone_FindRadius(self.origin, autocvar_g_vehicles_enter_radius, true); + while(head) + { + if(IS_PLAYER(head) || (IS_MONSTER(head) && ((get_monsterinfo(head.monsterid)).spawnflags & MON_FLAG_RIDE))) + if(SAME_TEAM(head, self) || autocvar_g_piggyback_ride_enemies || (IS_MONSTER(head) && head.realowner == self)) + if(head != self) + if(!head.cvar_cl_nocarry) + if(head.deadflag == DEAD_NO) // we check for trapped status here, but better to be safe than sorry, considering the madness happening + if(!head.vehicle) + if((!Player_Trapped(head) && !Player_Trapped(self)) || (Player_Trapped(head) || Player_Trapped(self))) + { + if(closest_target) + { + if(vlen(self.origin - head.origin) < vlen(self.origin - closest_target.origin)) + { closest_target = head; } + } + else { closest_target = head; } + } + + head = head.chain; + } + + if(closest_target) - if(IS_BOT_CLIENT(closest_target)) { pb_Attach(self, closest_target); return TRUE; } - else { pb_Attach(closest_target, self); return TRUE; } ++ if(IS_BOT_CLIENT(closest_target)) { pb_Attach(self, closest_target); return true; } ++ else { pb_Attach(closest_target, self); return true; } + } + - return FALSE; ++ return false; +} + +MUTATOR_HOOKFUNCTION(pb_PlayerThink) +{ + if(BROKEN_PBREF(self)) { pb_Detach(self); } + + if(!self.pbent) + pb_FixPBEnt(self); + + if(IS_MONSTER(self.pbhost)) + if(self.movement || (self.BUTTON_JUMP || self.BUTTON_CROUCH)) + if(!self.pbhost.enemy) + { + float forw, rit, updown = 0; + vector wishvel = '0 0 0'; + + makevectors(self.angles); + if(self.BUTTON_JUMP) + updown = 500; + else if(self.BUTTON_CROUCH) + updown = -500; + + if(self.movement) + { + forw = self.movement_x * 500; + rit = self.movement_y * 500; + //updown = self.movement_z * 100; + + wishvel = v_forward * forw + v_right * rit; + } + + //vector wishvel = normalize(('10 0 0' + v_forward * self.movement_x) + ('0 10 0' + v_right * self.movement_y) + ('0 0 1' * self.movement_z)); + //print(vtos(self.origin), vtos(self.origin + wishvel), "\n"); + self.pbhost.monster_moveto = self.origin + wishvel; + self.pbhost.monster_moveto_z = v_up_z * updown; + } + - return FALSE; ++ return false; +} + +MUTATOR_HOOKFUNCTION(pb_MonsterThink) +{ + if(BROKEN_PBREF(self)) { pb_Detach(self); } + + if(!self.pbent) + pb_FixPBEnt(self); + - return FALSE; ++ return false; +} + +MUTATOR_HOOKFUNCTION(pb_ResetMap) +{ + FOR_EACH_PLAYER(self) { pb_Detach(self); } - return FALSE; ++ return false; +} + +MUTATOR_HOOKFUNCTION(pb_RemovePlayer) +{ + if(self.piggybacker) + { + self.piggybacker.pb_canattach = 1; + pb_Detach(self); + } + + if(self.pbhost) + pb_Detach(self.pbhost); + self.pb_canattach = 0; + - return FALSE; ++ return false; +} + +MUTATOR_HOOKFUNCTION(pb_PlayerDies) +{ + if(!self.pbhost && self.pb_canattach) + self.pb_canattach = 0; + + if(self.piggybacker) + { + self.piggybacker.pb_canattach = 1; + pb_Detach(self); + } + + if(self.pbhost) + pb_Detach(self.pbhost); + self.pb_canattach = 0; + - return FALSE; ++ return false; +} + +MUTATOR_HOOKFUNCTION(pb_ItemTouch) +{ + if(self.weapon) + if(other.piggybacker) + if(other.weapons & self.weapons) + { + entity p = other; + while(p.piggybacker) + { + p = p.piggybacker; + if(!(p.weapons & self.weapons)) + { + other = p; + break; + } + } + } + - return FALSE; ++ return false; +} + +MUTATOR_HOOKFUNCTION(pb_GetCvars) +{ + GetCvars_handleFloat(get_cvars_s, get_cvars_f, cvar_cl_nocarry, "cl_nocarry"); - return FALSE; ++ return false; +} + +MUTATOR_DEFINITION(mutator_piggyback) +{ + MUTATOR_HOOK(MatchEnd, pb_MatchEnd, CBC_ORDER_ANY); + MUTATOR_HOOK(PlayerUseKey, pb_PlayerUseKey, CBC_ORDER_LAST); + MUTATOR_HOOK(PlayerPreThink, pb_PlayerThink, CBC_ORDER_ANY); + MUTATOR_HOOK(MonsterMove, pb_MonsterThink, CBC_ORDER_ANY); + MUTATOR_HOOK(reset_map_players, pb_ResetMap, CBC_ORDER_FIRST); + MUTATOR_HOOK(MakePlayerObserver, pb_RemovePlayer, CBC_ORDER_ANY); + MUTATOR_HOOK(ClientDisconnect, pb_RemovePlayer, CBC_ORDER_ANY); + MUTATOR_HOOK(PlayerDies, pb_PlayerDies, CBC_ORDER_ANY); + MUTATOR_HOOK(MonsterDies, pb_PlayerDies, CBC_ORDER_ANY); + MUTATOR_HOOK(MonsterRemove, pb_RemovePlayer, CBC_ORDER_ANY); + MUTATOR_HOOK(ItemTouch, pb_ItemTouch, CBC_ORDER_ANY); + MUTATOR_HOOK(GetCvars, pb_GetCvars, CBC_ORDER_ANY); + - return FALSE; ++ return false; +} diff --cc qcsrc/server/mutators/mutator_piggyback.qh index d9cd8e883,000000000..07990b20c mode 100644,000000..100644 --- a/qcsrc/server/mutators/mutator_piggyback.qh +++ b/qcsrc/server/mutators/mutator_piggyback.qh @@@ -1,12 -1,0 +1,17 @@@ ++#ifndef MUTATOR_PIGGYBACK_H ++#define MUTATOR_PIGGYBACK_H ++ +.entity piggybacker; +.entity pbent; +.entity pbhost; +.float pb_canattach; +//.float pb_attachblock; +.float pb_oldsolid; + +void pb_Attach(entity host, entity slave); +void pb_Detach(entity host); + +entity pb_TailOf(entity p); +entity pb_RootOf(entity p); ++ ++#endif diff --cc qcsrc/server/mutators/mutator_random_gravity.qc index 184047189,f70ee4ba3..3d29a4274 --- a/qcsrc/server/mutators/mutator_random_gravity.qc +++ b/qcsrc/server/mutators/mutator_random_gravity.qc @@@ -1,7 -1,7 +1,6 @@@ --// Random Gravity --// --// Mutator by Mario --// Inspired by Player 2 ++#include "../round_handler.qh" ++ ++// inspired by Player 2's Nexuiz gravity mod float gravity_delay; diff --cc qcsrc/server/mutators/mutator_random_vehicles.qc index 14af7589f,000000000..b1d3838a3 mode 100644,000000..100644 --- a/qcsrc/server/mutators/mutator_random_vehicles.qc +++ b/qcsrc/server/mutators/mutator_random_vehicles.qc @@@ -1,43 -1,0 +1,45 @@@ ++#include "../../common/vehicles/sv_vehicles.qh" ++ +MUTATOR_HOOKFUNCTION(rvehicles_OnEntityPreSpawn) +{ + if(startsWith(self.classname, "vehicle_")) + { + entity e = spawn(), oldself = self; + float i; + e.classname = self.classname; + setorigin(e, self.origin); + e.angles = self.angles; + e.team = self.team; + e.target = self.target; + e.targetname = self.targetname; + self = e; + RandomSelection_Init(); + for(i = VEH_FIRST; i <= VEH_LAST; ++i) + { + tracebox(self.origin, (get_vehicleinfo(i)).mins * 1.1, (get_vehicleinfo(i)).maxs * 1.1, self.origin, MOVE_NORMAL, self); + if(trace_fraction == 1.0 && !trace_startsolid) + { + if(i == VEH_RAPTOR) // temp hack + { + traceline(self.origin, self.origin + '0 0 700', MOVE_NOMONSTERS, self); + if(!(trace_fraction == 1.0 && !trace_startsolid)) + continue; + } + RandomSelection_Add(world, i, string_null, 1, 1); + } + } + - if(!vehicle_initialize(RandomSelection_chosen_float, FALSE)) { self = oldself; remove(e); return FALSE; } ++ if(!vehicle_initialize(RandomSelection_chosen_float, false)) { self = oldself; remove(e); return false; } + self = oldself; - return TRUE; ++ return true; + } + - return FALSE; ++ return false; +} + +MUTATOR_DEFINITION(mutator_random_vehicles) +{ + MUTATOR_HOOK(OnEntityPreSpawn, rvehicles_OnEntityPreSpawn, CBC_ORDER_ANY); + - return FALSE; ++ return false; +} diff --cc qcsrc/server/mutators/mutator_riflearena.qc index 9e70d7e65,000000000..17eea9135 mode 100644,000000..100644 --- a/qcsrc/server/mutators/mutator_riflearena.qc +++ b/qcsrc/server/mutators/mutator_riflearena.qc @@@ -1,108 -1,0 +1,108 @@@ +void ra_SetCvars() +{ + cvar_settemp("g_balance_rifle_secondary_spread", ftos(cvar("g_riflearena_rifle_secondary_spread"))); + cvar_settemp("g_balance_rifle_secondary_shots", ftos(cvar("g_riflearena_rifle_secondary_shots"))); + cvar_settemp("g_balance_rifle_secondary_animtime", ftos(cvar("g_riflearena_rifle_secondary_animtime"))); + cvar_settemp("g_balance_rifle_secondary_refire", ftos(cvar("g_riflearena_rifle_secondary_refire"))); + cvar_settemp("g_balance_rifle_secondary_damage", ftos(cvar("g_riflearena_rifle_secondary_damage"))); +} + +MUTATOR_HOOKFUNCTION(ra_PlayerDamage) +{ + if(IS_PLAYER(frag_attacker)) + if(IS_PLAYER(frag_target)) + { + if (DEATH_ISWEAPON(frag_deathtype, WEP_BLASTER)) + { + if(frag_attacker == frag_target) + frag_damage = 5; + else + frag_damage = 0; + frag_mirrordamage = 0; + if (frag_target != frag_attacker) + { + if (frag_target.health >= 1 && IS_PLAYER(frag_target) && !frag_target.frozen && frag_target.deadflag == DEAD_NO) + Send_Notification(NOTIF_ONE, frag_attacker, MSG_CENTER, CENTER_SECONDARY_NODAMAGE); + frag_force = '0 0 0'; + } + } + } + - return FALSE; ++ return false; +} + +MUTATOR_HOOKFUNCTION(ra_FilterItem) +{ + switch (self.items) + { + case IT_5HP: + case IT_ARMOR_SHARD: - return FALSE; ++ return false; + } + - return TRUE; ++ return true; +} + +MUTATOR_HOOKFUNCTION(ra_StartItems) +{ + start_items |= IT_UNLIMITED_AMMO; + start_ammo_nails = warmup_start_ammo_nails = 100; + warmup_start_weapons = start_weapons = WEPSET_RIFLE; + + if(autocvar_g_riflearena_withlaser) + { + start_weapons |= WEPSET_BLASTER; + warmup_start_weapons |= WEPSET_BLASTER; + } + - return FALSE; ++ return false; +} + +MUTATOR_HOOKFUNCTION(ra_ForbidThrowCurrentWeapon) +{ - return TRUE; ++ return true; +} + +MUTATOR_HOOKFUNCTION(ra_BuildMutatorsString) +{ + ret_string = strcat(ret_string, ":RA"); - return FALSE; ++ return false; +} + +MUTATOR_HOOKFUNCTION(ra_BuildMutatorsPrettyString) +{ + ret_string = strcat(ret_string, ", Rifle Arena"); - return FALSE; ++ return false; +} + +MUTATOR_HOOKFUNCTION(ra_SetModname) +{ + modname = "Rifle Arena"; - return TRUE; ++ return true; +} + +MUTATOR_DEFINITION(mutator_riflearena) +{ + MUTATOR_HOOK(PlayerDamage_Calculate, ra_PlayerDamage, CBC_ORDER_ANY); + MUTATOR_HOOK(FilterItem, ra_FilterItem, CBC_ORDER_ANY); + MUTATOR_HOOK(SetStartItems, ra_StartItems, CBC_ORDER_ANY); + MUTATOR_HOOK(ForbidThrowCurrentWeapon, ra_ForbidThrowCurrentWeapon, CBC_ORDER_ANY); + MUTATOR_HOOK(BuildMutatorsString, ra_BuildMutatorsString, CBC_ORDER_ANY); + MUTATOR_HOOK(BuildMutatorsPrettyString, ra_BuildMutatorsPrettyString, CBC_ORDER_ANY); + MUTATOR_HOOK(SetModname, ra_SetModname, CBC_ORDER_ANY); + + MUTATOR_ONADD + { + ra_SetCvars(); + + WEP_ACTION(WEP_BLASTER, WR_INIT); + WEP_ACTION(WEP_RIFLE, WR_INIT); + } + MUTATOR_ONREMOVE + { + print("This cannot be removed at runtime\n"); + return -1; + } + - return FALSE; ++ return false; +} diff --cc qcsrc/server/mutators/mutator_spawn_near_teammate.qc index 9e69ebd13,8dc3bbc6c..b8b399e43 --- a/qcsrc/server/mutators/mutator_spawn_near_teammate.qc +++ b/qcsrc/server/mutators/mutator_spawn_near_teammate.qc @@@ -157,12 -154,6 +157,12 @@@ MUTATOR_HOOKFUNCTION(msnt_PlayerDies return 0; } +MUTATOR_HOOKFUNCTION(msnt_GetCvars) +{ + GetCvars_handleFloat(get_cvars_s, get_cvars_f, cvar_cl_spawn_near_teammate, "cl_spawn_near_teammate"); - return FALSE; ++ return false; +} + MUTATOR_DEFINITION(mutator_spawn_near_teammate) { MUTATOR_HOOK(Spawn_Score, msnt_Spawn_Score, CBC_ORDER_ANY); diff --cc qcsrc/server/mutators/mutator_superspec.qc index e6ff99419,1fd1022e7..9a8ff07c4 --- a/qcsrc/server/mutators/mutator_superspec.qc +++ b/qcsrc/server/mutators/mutator_superspec.qc @@@ -1,19 -1,21 +1,21 @@@ ++#include "../../common/buffs.qh" ++ #define _SSMAGIX "SUPERSPEC_OPTIONSFILE_V1" - #define _ISLOCAL ((edict_num(1) == self) ? TRUE : FALSE) - - #define ASF_MEGA_AR 4 - #define ASF_MEGA_HP 8 - #define ASF_FLAG_GRAB 16 - #define ASF_OBSERVER_ONLY 32 - #define ASF_SHOWWHAT 64 - #define ASF_SSIM 128 - #define ASF_FOLLOWKILLER 256 - #define ASF_ALL 0xFFFFFF + #define _ISLOCAL ((edict_num(1) == self) ? true : false) + -const float ASF_STRENGTH = 1; -const float ASF_SHIELD = 2; + const float ASF_MEGA_AR = 4; + const float ASF_MEGA_HP = 8; + const float ASF_FLAG_GRAB = 16; + const float ASF_OBSERVER_ONLY = 32; + const float ASF_SHOWWHAT = 64; + const float ASF_SSIM = 128; + const float ASF_FOLLOWKILLER = 256; + const float ASF_ALL = 0xFFFFFF; .float autospec_flags; - #define SSF_SILENT 1 - #define SSF_VERBOSE 2 - #define SSF_ITEMMSG 4 + const float SSF_SILENT = 1; + const float SSF_VERBOSE = 2; + const float SSF_ITEMMSG = 4; .float superspec_flags; .string superspec_itemfilter; //"classname1 classname2 ..." @@@ -338,9 -348,35 +340,9 @@@ MUTATOR_HOOKFUNCTION(superspec_SV_Parse } superspec_msg("", "", self, "No active powerup\n", 1); - return TRUE; + return true; } - if(cmd_name == "followstrength") - { - entity _player; - FOR_EACH_PLAYER(_player) - { - if(_player.strength_finished > time) - return _spectate(_player); - } - - superspec_msg("", "", self, "No active Strength\n", 1); - return true; - } - - if(cmd_name == "followshield") - { - entity _player; - FOR_EACH_PLAYER(_player) - { - if(_player.invincible_finished > time) - return _spectate(_player); - } - - superspec_msg("", "", self, "No active Shield\n", 1); - return true; - } - if(cmd_name == "followfc") { if(!g_ctf) @@@ -348,18 -384,14 +350,18 @@@ entity _player; float _team = 0; - float found = FALSE; + float found = false; if(cmd_argc == 2) + if(!ctf_oneflag) // no team flags in 1 flag CTF { - if(argv(1) == "red") - _team = NUM_TEAM_1; - else - _team = NUM_TEAM_2; + switch(argv(1)) + { + case "red": _team = NUM_TEAM_1; break; + case "blue": _team = NUM_TEAM_2; break; + case "yellow": if(ctf_teams >= 3) _team = NUM_TEAM_3; break; + case "pink": if(ctf_teams >= 4) _team = NUM_TEAM_4; break; + } } FOR_EACH_PLAYER(_player) diff --cc qcsrc/server/mutators/mutator_touchexplode.qc index d50536cc5,fb58b3955..27a6ae95d --- a/qcsrc/server/mutators/mutator_touchexplode.qc +++ b/qcsrc/server/mutators/mutator_touchexplode.qc @@@ -1,13 -1,13 +1,15 @@@ ++#include "../../common/effects.qh" ++ .float touchexplode_time; void PlayerTouchExplode(entity p1, entity p2) { vector org; org = (p1.origin + p2.origin) * 0.5; - org_z += (p1.mins_z + p2.mins_z) * 0.5; + org.z += (p1.mins.z + p2.mins.z) * 0.5; - sound(self, CH_TRIGGER, "weapons/grenade_impact.wav", VOL_BASE, ATTEN_NORM); - pointparticles(particleeffectnum("explosion_small"), org, '0 0 0', 1); + sound(self, CH_TRIGGER, W_Sound("grenade_impact"), VOL_BASE, ATTEN_NORM); + Send_Effect(EFFECT_EXPLOSION_SMALL, org, '0 0 0', 1); entity e; e = spawn(); diff --cc qcsrc/server/mutators/mutator_walljump.qc index 489e55b0f,000000000..efda70a6b mode 100644,000000..100644 --- a/qcsrc/server/mutators/mutator_walljump.qc +++ b/qcsrc/server/mutators/mutator_walljump.qc @@@ -1,75 -1,0 +1,77 @@@ ++#include "../../common/effects.qh" ++ +.float lastwj; + +vector PlayerTouchWall () +{ + local float dist, max_normal; + local vector start, end; + dist = 10; + max_normal = 0.2; + start = self.origin; + end = start + v_forward * 100; - tracebox (start, self.mins, self.maxs, end, TRUE, self); ++ tracebox (start, self.mins, self.maxs, end, true, self); + if (trace_fraction < 1 && vlen (self.origin - trace_endpos) < dist && trace_plane_normal_z < max_normal) + if (!(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)) + return trace_plane_normal; + end = start - v_forward * 100; - tracebox (start, self.mins, self.maxs, end, TRUE, self); ++ tracebox (start, self.mins, self.maxs, end, true, self); + if (trace_fraction < 1 && vlen (self.origin - trace_endpos) < dist && trace_plane_normal_z < max_normal) + if (!(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)) + return trace_plane_normal; + end = start + v_right * 100; - tracebox (start, self.mins, self.maxs, end, TRUE, self); ++ tracebox (start, self.mins, self.maxs, end, true, self); + if (trace_fraction < 1 && vlen (self.origin - trace_endpos) < dist && trace_plane_normal_z < max_normal) + if (!(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)) + return trace_plane_normal; + end = start - v_right * 100; - tracebox (start, self.mins, self.maxs, end, TRUE, self); ++ tracebox (start, self.mins, self.maxs, end, true, self); + if (trace_fraction < 1 && vlen (self.origin - trace_endpos) < dist && trace_plane_normal_z < max_normal) + if (!(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)) + return trace_plane_normal; + return '0 0 0'; +} + +MUTATOR_HOOKFUNCTION(walljump_PlayerJump) +{ + if(autocvar_g_walljump) + if(time - self.lastwj > autocvar_g_walljump_delay) + if(!(self.flags & FL_ONGROUND)) + if(self.movetype != MOVETYPE_NONE && self.movetype != MOVETYPE_FOLLOW && self.movetype != MOVETYPE_FLY && self.movetype != MOVETYPE_NOCLIP) + if(self.flags & FL_JUMPRELEASED) + if(!self.frozen) + if(self.deadflag == DEAD_NO) + { + vector plane_normal = PlayerTouchWall(); + + if(plane_normal != '0 0 0') + { + self.lastwj = time; + float wj_force = autocvar_g_walljump_force; + float wj_xy_factor = autocvar_g_walljump_velocity_xy_factor; + float wj_z_factor = autocvar_g_walljump_velocity_z_factor; + self.velocity_x += plane_normal_x * wj_force; + self.velocity_x /= wj_xy_factor; + self.velocity_y += plane_normal_y * wj_force; + self.velocity_y /= wj_xy_factor; + self.velocity_z = autocvar_sv_jumpvelocity * wj_z_factor; + if (self.crouch) self.velocity_z *= -1; + self.oldvelocity = self.velocity; + + Send_Effect(EFFECT_SMOKE_RING, trace_endpos, plane_normal, 5); + PlayerSound(playersound_jump, CH_PLAYER, VOICETYPE_PLAYERSOUND); - animdecide_setaction(self, ANIMACTION_JUMP, TRUE); ++ animdecide_setaction(self, ANIMACTION_JUMP, true); + - player_multijump = TRUE; ++ player_multijump = true; + } + } + - return FALSE; ++ return false; +} + +MUTATOR_DEFINITION(mutator_walljump) +{ + MUTATOR_HOOK(PlayerJump, walljump_PlayerJump, CBC_ORDER_ANY); + - return FALSE; ++ return false; +} diff --cc qcsrc/server/mutators/mutator_zombie_apocalypse.qc index ce2f11962,000000000..bd771b277 mode 100644,000000..100644 --- a/qcsrc/server/mutators/mutator_zombie_apocalypse.qc +++ b/qcsrc/server/mutators/mutator_zombie_apocalypse.qc @@@ -1,48 -1,0 +1,48 @@@ +float za_spawn_delay; + +void za_SpawnMonster() +{ + if(gameover) { return; } + + entity e = spawn(), mon; + + if(MoveToRandomMapLocation(e, DPCONTENTS_SOLID | DPCONTENTS_CORPSE | DPCONTENTS_PLAYERCLIP, DPCONTENTS_SLIME | DPCONTENTS_LAVA | DPCONTENTS_SKY | DPCONTENTS_BODY | DPCONTENTS_DONOTENTER, Q3SURFACEFLAG_SKY, 10, 1024, 256)) - mon = spawnmonster(autocvar_g_za_spawnmonster, 0, self, self, e.origin, FALSE, FALSE, 2); ++ mon = spawnmonster(autocvar_g_za_spawnmonster, 0, self, self, e.origin, false, false, 2); + + e.think = SUB_Remove; + e.nextthink = time + 0.1; +} + +MUTATOR_HOOKFUNCTION(za_StartFrame) +{ + if(time < za_spawn_delay || autocvar_g_za_max_monsters <= 0 || !autocvar_g_za) - return FALSE; ++ return false; + + float n_monsters = 0, maxmon = autocvar_g_za_max_monsters; + entity head; + + // count dead monsters too (zombies) + FOR_EACH_MONSTER(head) ++n_monsters; + + while(n_monsters < maxmon) + { + ++n_monsters; + za_SpawnMonster(); + } + + za_spawn_delay = time + autocvar_g_za_spawn_delay; + - return FALSE; ++ return false; +} + +MUTATOR_DEFINITION(mutator_zombie_apocalypse) +{ + MUTATOR_HOOK(SV_StartFrame, za_StartFrame, CBC_ORDER_ANY); + + MUTATOR_ONADD + { + za_spawn_delay = time + game_starttime; + } + - return FALSE; ++ return false; +} diff --cc qcsrc/server/mutators/mutators.qh index 9e78011d1,6d889e24e..e2579e9e6 --- a/qcsrc/server/mutators/mutators.qh +++ b/qcsrc/server/mutators/mutators.qh @@@ -38,14 -34,10 +41,16 @@@ MUTATOR_DECLARATION(mutator_random_grav MUTATOR_DECLARATION(mutator_multijump); MUTATOR_DECLARATION(mutator_melee_only); MUTATOR_DECLARATION(mutator_nades); +MUTATOR_DECLARATION(mutator_riflearena); MUTATOR_DECLARATION(mutator_campcheck); +MUTATOR_DECLARATION(mutator_zombie_apocalypse); +MUTATOR_DECLARATION(mutator_walljump); +MUTATOR_DECLARATION(mutator_hats); +MUTATOR_DECLARATION(mutator_random_vehicles); +MUTATOR_DECLARATION(mutator_piggyback); MUTATOR_DECLARATION(mutator_buffs); +MUTATOR_DECLARATION(mutator_itemeditor); MUTATOR_DECLARATION(sandbox); -MUTATOR_DECLARATION(mutator_overkill); + + #endif diff --cc qcsrc/server/mutators/mutators_include.qc index 3727924c6,a0170e480..dd0430251 --- a/qcsrc/server/mutators/mutators_include.qc +++ b/qcsrc/server/mutators/mutators_include.qc @@@ -1,3 -1,85 +1,84 @@@ + #if defined(CSQC) + #elif defined(MENUQC) + #elif defined(SVQC) + #include "../../dpdefs/progsdefs.qh" + #include "../../dpdefs/dpextensions.qh" + #include "../../warpzonelib/anglestransform.qh" + #include "../../warpzonelib/mathlib.qh" + #include "../../warpzonelib/common.qh" + #include "../../warpzonelib/util_server.qh" + #include "../../warpzonelib/server.qh" + #include "../../common/constants.qh" + #include "../../common/stats.qh" + #include "../../common/teams.qh" + #include "../../common/util.qh" + #include "../../common/nades.qh" + #include "../../common/buffs.qh" + #include "../../common/test.qh" + #include "../../common/counting.qh" + #include "../../common/urllib.qh" + #include "../../common/command/markup.qh" + #include "../../common/command/rpn.qh" + #include "../../common/command/generic.qh" + #include "../../common/command/shared_defs.qh" + #include "../../common/net_notice.qh" + #include "../../common/animdecide.qh" + #include "../../common/monsters/monsters.qh" + #include "../../common/monsters/sv_monsters.qh" + #include "../../common/monsters/spawn.qh" + #include "../../common/weapons/config.qh" + #include "../../common/weapons/weapons.qh" + #include "../weapons/accuracy.qh" + #include "../weapons/common.qh" + #include "../weapons/csqcprojectile.qh" + #include "../weapons/hitplot.qh" + #include "../weapons/selection.qh" + #include "../weapons/spawning.qh" + #include "../weapons/throwing.qh" + #include "../weapons/tracing.qh" + #include "../weapons/weaponstats.qh" + #include "../weapons/weaponsystem.qh" + #include "../t_items.qh" + #include "../autocvars.qh" + #include "../constants.qh" + #include "../defs.qh" + #include "../../common/notifications.qh" + #include "../../common/deathtypes.qh" + #include "mutators_include.qh" - #include "../tturrets/include/turrets_early.qh" - #include "../vehicles/vehicles_def.qh" ++ #include "../../common/turrets/sv_turrets.qh" ++ #include "../../common/vehicles/sv_vehicles.qh" + #include "../campaign.qh" + #include "../../common/campaign_common.qh" + #include "../../common/mapinfo.qh" + #include "../command/common.qh" + #include "../command/banning.qh" + #include "../command/radarmap.qh" + #include "../command/vote.qh" + #include "../command/getreplies.qh" + #include "../command/cmd.qh" + #include "../command/sv_cmd.qh" + #include "../../common/csqcmodel_settings.qh" + #include "../../csqcmodellib/common.qh" + #include "../../csqcmodellib/sv_model.qh" + #include "../anticheat.qh" + #include "../cheats.qh" + #include "../../common/playerstats.qh" + #include "../portals.qh" + #include "../g_hook.qh" + #include "../scores.qh" + #include "../spawnpoints.qh" + #include "../mapvoting.qh" + #include "../ipban.qh" + #include "../race.qh" + #include "../antilag.qh" + #include "../playerdemo.qh" + #include "../round_handler.qh" + #include "../item_key.qh" + #include "../secret.qh" + #include "../pathlib/pathlib.qh" - #include "../tturrets/include/turrets.qh" - #include "../vehicles/vehicles.qh" ++ #include "../../common/vehicles/vehicles.qh" + #endif + #include "base.qc" #include "gamemode_assault.qc" #include "gamemode_ca.qc" diff --cc qcsrc/server/mutators/mutators_include.qh index 4c27cbe86,da723d927..bf5319624 --- a/qcsrc/server/mutators/mutators_include.qh +++ b/qcsrc/server/mutators/mutators_include.qh @@@ -20,8 -17,6 +23,9 @@@ #include "mutator_dodging.qh" #include "mutator_overkill.qh" +#include "mutator_freeze.qh" +#include "mutator_instagib.qh" #include "mutator_nades.qh" +#include "mutator_piggyback.qh" #include "mutator_buffs.qh" + #endif diff --cc qcsrc/server/mutators/sandbox.qc index e8db22137,f66b5019a..346d85299 --- a/qcsrc/server/mutators/sandbox.qc +++ b/qcsrc/server/mutators/sandbox.qc @@@ -503,13 -478,8 +503,13 @@@ MUTATOR_HOOKFUNCTION(sandbox_PlayerComm if(time < self.object_flood) { print_to(self, strcat("^1SANDBOX - WARNING: ^7Flood protection active. Please wait ^3", ftos(self.object_flood - time), " ^7seconds beofore spawning another object")); - return TRUE; + return true; } + if(IS_SPEC(self) || IS_OBSERVER(self)) + { + print_to(self, "^1SANDBOX - WARNING: ^7Cannot spawn objects while spectating"); - return TRUE; ++ return true; + } self.object_flood = time + autocvar_g_sandbox_editor_flood; if(object_count >= autocvar_g_sandbox_editor_maxobjects) { @@@ -524,11 -494,11 +524,11 @@@ if (!(fexists(argv(2)))) { print_to(self, "^1SANDBOX - WARNING: ^7Attempted to spawn an object with a non-existent model. Make sure the path to your model file is correct"); - return TRUE; + return true; } - e = sandbox_ObjectSpawn(FALSE); + e = sandbox_ObjectSpawn(false); - setmodel(e, argv(2)); + setmodel_fixsize(e, argv(2)); if(autocvar_g_sandbox_info > 0) print(strcat("^3SANDBOX - SERVER: ^7", self.netname, " spawned an object at origin ^3", vtos(e.origin), "\n")); @@@ -674,11 -644,6 +674,11 @@@ e.frame = stof(argv(3)); break; case "scale": + if(e.solid == SOLID_BSP) + { + print_to(self, "^1SANDBOX - WARNING: ^7BSP solid objects cannot be resized"); - return TRUE; ++ return true; + } sandbox_ObjectEdit_Scale(e, stof(argv(3))); break; case "solidity": @@@ -843,12 -802,13 +843,12 @@@ MUTATOR_HOOKFUNCTION(sandbox_StartFrame sandbox_Database_Save(); - return TRUE; + return true; } -MUTATOR_HOOKFUNCTION(sandbox_SetModname) +void sandbox_DelayedInit() { - modname = "Sandbox"; - return true; + sandbox_Database_Load(); } MUTATOR_DEFINITION(sandbox) @@@ -860,9 -821,19 +860,9 @@@ { autosave_time = time + autocvar_g_sandbox_storage_autosave; // don't save the first server frame if(autocvar_g_sandbox_storage_autoload) - sandbox_Database_Load(); - } - - MUTATOR_ONROLLBACK_OR_REMOVE - { - // nothing to roll back - } - - MUTATOR_ONREMOVE - { - // nothing to remove + InitializeEntity(world, sandbox_DelayedInit, INITPRIO_LAST); } - return FALSE; + return false; } diff --cc qcsrc/server/portals.qc index 540ae1a36,53c77ecce..e6e606f5f --- a/qcsrc/server/portals.qc +++ b/qcsrc/server/portals.qc @@@ -1,7 -1,27 +1,28 @@@ + #if defined(CSQC) + #elif defined(MENUQC) + #elif defined(SVQC) + #include "../dpdefs/progsdefs.qh" + #include "../dpdefs/dpextensions.qh" + #include "../warpzonelib/anglestransform.qh" + #include "../warpzonelib/util_server.qh" + #include "../common/constants.qh" ++ #include "../common/effects.qh" + #include "../common/util.qh" + #include "../common/weapons/weapons.qh" + #include "autocvars.qh" + #include "defs.qh" + #include "../common/notifications.qh" + #include "../common/deathtypes.qh" + #include "mutators/mutators_include.qh" + #include "../csqcmodellib/sv_model.qh" + #include "portals.qh" + #include "g_hook.qh" + #endif + #define PORTALS_ARE_NOT_SOLID - #define SAFENUDGE '1 1 1' - #define SAFERNUDGE '8 8 8' + const vector SAFENUDGE = '1 1 1'; + const vector SAFERNUDGE = '8 8 8'; .vector portal_transform; .vector portal_safe_origin; diff --cc qcsrc/server/progs.src index 7c1374d9c,85e75cda9..3ce98cebf --- a/qcsrc/server/progs.src +++ b/qcsrc/server/progs.src @@@ -2,165 -2,76 +2,78 @@@ ../common/util-pre.qh sys-pre.qh - ../dpdefs/progsdefs.qc - ../dpdefs/dpextensions.qc + ../dpdefs/progsdefs.qh + ../dpdefs/dpextensions.qh sys-post.qh - ../warpzonelib/anglestransform.qh - ../warpzonelib/mathlib.qh - ../warpzonelib/common.qh - ../warpzonelib/util_server.qh - ../warpzonelib/server.qh - ../common/constants.qh - ../common/stats.qh - ../common/teams.qh - ../common/util.qh - ../common/nades.qh - ../common/buffs.qh - ../common/test.qh - ../common/counting.qh - ../common/urllib.qh - ../common/command/markup.qh - ../common/command/rpn.qh - ../common/command/generic.qh - ../common/command/shared_defs.qh - ../common/command/script.qh - ../common/net_notice.qh - ../common/animdecide.qh - - ../common/weapons/config.qh - ../common/weapons/weapons.qh // TODO - weapons/accuracy.qh - weapons/common.qh - weapons/csqcprojectile.qh // TODO - weapons/hitplot.qh - weapons/selection.qh - weapons/spawning.qh - weapons/throwing.qh - weapons/tracing.qh - weapons/weaponstats.qh - weapons/weaponsystem.qh - - t_items.qh - - autocvars.qh - constants.qh - defs.qh // Should rename this, it has fields and globals - - ../common/jeff.qh - - ../common/notifications.qh // must be after autocvars - ../common/deathtypes.qh // must be after notifications - - ../common/effects.qh - - ../common/monsters/monsters.qh - ../common/monsters/sv_monsters.qh - ../common/monsters/spawn.qh - - ../common/minigames/sv_minigames.qh - ../common/minigames/minigames.qh - - ../common/turrets/turrets.qh - ../common/turrets/sv_turrets.qh - ../common/turrets/util.qc - - ../common/vehicles/vehicles_include.qh - - mutators/mutators_include.qh - - generator.qh - controlpoint.qh - - teamplay.qh - - campaign.qh - ../common/campaign_common.qh - ../common/mapinfo.qh - - command/common.qh - command/banning.qh - command/radarmap.qh - command/vote.qh - command/getreplies.qh - command/cmd.qh - command/sv_cmd.qh - - - ../common/csqcmodel_settings.qh - ../csqcmodellib/common.qh - ../csqcmodellib/sv_model.qh + anticheat.qc + antilag.qc + // assault.qc + campaign.qc + cheats.qc + cl_client.qc + cl_impulse.qc + cl_physics.qc + cl_player.qc ++controlpoint.qc csqceffects.qc - - anticheat.qh - cheats.qh - ../common/playerstats.qh - - portals.qh - - g_hook.qh // TODO - - scores.qh - - spawnpoints.qh - - mapvoting.qh - - ipban.qh - - race.qh - - antilag.qh - - playerdemo.qh - - round_handler.qh - - // singleplayer stuff - item_key.qh - secret.qh - - scores_rules.qc - - miscfunctions.qc - - mutators/mutators.qc - - waypointsprites.qc - - bot/bot.qc - + // ctf.qc + // domination.qc + ent_cs.qc + func_breakable.qc ++generator.qc + g_casings.qc + g_damage.qc + g_hook.qc + g_models.qc g_subs.qc - - jeff.qc - -g_tetris.qc + g_triggers.qc g_violence.qc - g_damage.qc - - teamplay.qc - - cl_physics.qc - - // tZork's libs - movelib.qc - steerlib.qc - g_world.qc - g_casings.qc - + ipban.qc + item_key.qc ++jeff.qc mapvoting.qc - + miscfunctions.qc + // mode_onslaught.qc + movelib.qc + // nexball.qc + playerdemo.qc + portals.qc + race.qc + round_handler.qc + // runematch.qc + scores.qc + scores_rules.qc + secret.qc + spawnpoints.qc + steerlib.qc + sv_main.qc + target_music.qc + target_spawn.qc + teamplay.qc + t_halflife.qc + t_items.qc t_jumppads.qc + t_plats.qc + t_quake3.qc + t_quake.qc + t_swamp.qc t_teleporters.qc + waypointsprites.qc - sv_main.qc + bot/bot.qc - g_triggers.qc - g_models.qc + command/banning.qc + command/cmd.qc + command/common.qc + command/getreplies.qc + command/radarmap.qc + command/sv_cmd.qc + command/vote.qc - // singleplayer stuff - item_key.qc - secret.qc + mutators/mutators_include.qc + mutators/mutators.qc weapons/accuracy.qc weapons/common.qc @@@ -172,105 -83,32 +85,41 @@@ weapons/throwing.q weapons/tracing.qc weapons/weaponstats.qc weapons/weaponsystem.qc - ../common/weapons/config.qc - ../common/weapons/weapons.qc // TODO - - t_items.qc - cl_impulse.qc - - ent_cs.qc - - cl_player.qc - cl_client.qc - t_plats.qc - antilag.qc - - g_hook.qc - t_swamp.qc - - campaign.qc + ../common/animdecide.qc + ../common/buffs.qc ../common/campaign_file.qc ../common/campaign_setup.qc - ../common/urllib.qc - + ../common/command/generic.qc ../common/command/markup.qc ../common/command/rpn.qc - ../common/command/generic.qc +../common/command/script.qc - ../common/net_notice.qc - - command/common.qc - command/banning.qc - command/radarmap.qc - command/vote.qc - command/getreplies.qc - command/cmd.qc - command/sv_cmd.qc - - ipban.qc - ++../common/effects.qc ../common/mapinfo.qc - - t_quake3.qc - t_halflife.qc - t_quake.qc - - race.qc - - scores.qc - - spawnpoints.qc - - portals.qc - - generator.qc - controlpoint.qc - - target_spawn.qc - func_breakable.qc - target_music.qc - - ../common/vehicles/vehicles_include.qc - - ../common/nades.qc - - ../common/buffs.qc - - ../csqcmodellib/sv_model.qc - - playerdemo.qc - - anticheat.qc - cheats.qc - ../common/playerstats.qc - - round_handler.qc - - ../common/monsters/sv_monsters.qc ../common/monsters/monsters.qc - ../common/monsters/spawn.qc - + ../common/monsters/sv_monsters.qc ++../common/minigames/minigames.qc ++../common/minigames/sv_minigames.qc + ../common/nades.qc + ../common/net_notice.qc + ../common/notifications.qc + ../common/playerstats.qc + ../common/test.qc ++../common/turrets/targettrigger.qc +../common/turrets/sv_turrets.qc +../common/turrets/turrets.qc - ../common/turrets/checkpoint.qc - ../common/turrets/targettrigger.qc ++../common/turrets/util.qc ++../common/vehicles/vehicles_include.qc + ../common/urllib.qc + ../common/util.qc + ../common/weapons/config.qc + ../common/weapons/weapons.qc // TODO - mutators/mutators_include.qc + ../csqcmodellib/sv_model.qc ../warpzonelib/anglestransform.qc - ../warpzonelib/mathlib.qc ../warpzonelib/common.qc - ../warpzonelib/util_server.qc + ../warpzonelib/mathlib.qc ../warpzonelib/server.qc - - ../common/animdecide.qc - ../common/test.qc - ../common/util.qc - ../common/notifications.qc - ../common/effects.qc - - - ../common/minigames/minigames.qc - ../common/minigames/sv_minigames.qc + ../warpzonelib/util_server.qc diff --cc qcsrc/server/race.qh index a18e9773e,bfdc9be43..7d163cc94 --- a/qcsrc/server/race.qh +++ b/qcsrc/server/race.qh @@@ -39,6 -29,4 +42,6 @@@ float race_GetFractionalLapCount(entit float race_readTime(string map, float pos); string race_readUID(string map, float pos); string race_readName(string map, float pos); - +void race_send_speedaward(float msg); +void race_send_speedaward_alltimebest(float msg); + #endif diff --cc qcsrc/server/scores.qc index 08497eb8e,8f9da8b06..9aa2c186c --- a/qcsrc/server/scores.qc +++ b/qcsrc/server/scores.qc @@@ -205,15 -208,16 +208,15 @@@ void ScoreInfo_Init(float teams { scores_initialized = spawn(); scores_initialized.classname = "ent_client_scoreinfo"; - Net_LinkEntity(scores_initialized, FALSE, 0, ScoreInfo_SendEntity); + Net_LinkEntity(scores_initialized, false, 0, ScoreInfo_SendEntity); } - if(teams >= 1) - TeamScore_Spawn(NUM_TEAM_1, "Red"); - if(teams >= 2) - TeamScore_Spawn(NUM_TEAM_2, "Blue"); - if(teams >= 3) - TeamScore_Spawn(NUM_TEAM_3, "Yellow"); - if(teams >= 4) - TeamScore_Spawn(NUM_TEAM_4, "Pink"); + if(teams) + { + teams = min(4, teams); + float i; + for(i = 1; i <= teams; ++i) + TeamScore_Spawn(Team_NumberToTeam(i), Team_ColorName(Team_NumberToTeam(i))); + } } /* diff --cc qcsrc/server/scores_rules.qc index 2fb6740dc,4d5296c81..e7b26d1b3 --- a/qcsrc/server/scores_rules.qc +++ b/qcsrc/server/scores_rules.qc @@@ -1,3 -1,16 +1,13 @@@ + #if defined(CSQC) + #elif defined(MENUQC) + #elif defined(SVQC) + #include "../dpdefs/progsdefs.qh" + #include "../common/constants.qh" + #include "autocvars.qh" + #include "defs.qh" + #include "scores.qh" + #endif + -//float c1, c2, c3, c4; -void CheckAllowedTeams (entity for_whom); - // NOTE: SP_ constants may not be >= MAX_SCORE; ST_constants may not be >= MAX_TEAMSCORE // scores that should be in all modes: float ScoreRules_teams; diff --cc qcsrc/server/spawnpoints.qh index eb9b186bd,beab71c7a..5dbce4427 --- a/qcsrc/server/spawnpoints.qh +++ b/qcsrc/server/spawnpoints.qh @@@ -2,6 -5,5 +5,7 @@@ float spawnpoint_nag; float SpawnEvent_Send(entity to, float sf); entity Spawn_FilterOutBadSpots(entity firstspot, float mindist, float teamcheck); +void spawnfunc_info_player_deathmatch(); +void spawnpoint_use(); entity SelectSpawnPoint (float anypoint); + #endif diff --cc qcsrc/server/steerlib.qc index 77e97fa2f,ec76dfef2..d2b8e564e --- a/qcsrc/server/steerlib.qc +++ b/qcsrc/server/steerlib.qc @@@ -1,3 -1,10 +1,11 @@@ + #if defined(CSQC) + #elif defined(MENUQC) + #elif defined(SVQC) + #include "../dpdefs/progsdefs.qh" + #include "../dpdefs/dpextensions.qh" ++ #include "../common/effects.qh" + #endif + .vector steerto; /** diff --cc qcsrc/server/sv_main.qc index 13aa61902,c2d3dc7dd..268639c30 --- a/qcsrc/server/sv_main.qc +++ b/qcsrc/server/sv_main.qc @@@ -1,3 -1,27 +1,27 @@@ + #if defined(CSQC) + #elif defined(MENUQC) + #elif defined(SVQC) + #include "../dpdefs/progsdefs.qh" + #include "../dpdefs/dpextensions.qh" + #include "../warpzonelib/common.qh" + #include "../warpzonelib/server.qh" + #include "../common/constants.qh" + #include "../common/util.qh" + #include "../common/weapons/weapons.qh" + #include "weapons/csqcprojectile.qh" + #include "autocvars.qh" + #include "constants.qh" + #include "defs.qh" + #include "../common/deathtypes.qh" + #include "mutators/mutators_include.qh" - #include "vehicles/vehicles_def.qh" ++ #include "../common/vehicles/sv_vehicles.qh" + #include "../common/mapinfo.qh" + #include "command/common.qh" + #include "../csqcmodellib/sv_model.qh" + #include "anticheat.qh" + #include "g_hook.qh" + #endif + void CreatureFrame (void) { entity oldself; @@@ -207,13 -231,13 +231,13 @@@ void StartFrame (void if (timeout_status == TIMEOUT_LEADTIME) // just before the timeout (when timeout_status will be TIMEOUT_ACTIVE) orig_slowmo = autocvar_slowmo; // slowmo will be restored after the timeout - skill = autocvar_skill; + bot_skill = autocvar_skill; // detect when the pre-game countdown (if any) has ended and the game has started - game_delay = (time < game_starttime) ? TRUE : FALSE; + game_delay = (time < game_starttime) ? true : false; - if(game_delay_last == TRUE) - if(game_delay == FALSE) + if(game_delay_last == true) + if(game_delay == false) if(autocvar_sv_eventlog) GameLogEcho(":startdelay_ended"); diff --cc qcsrc/server/t_halflife.qc index de208120c,681c5dc7d..d6130beaf --- a/qcsrc/server/t_halflife.qc +++ b/qcsrc/server/t_halflife.qc @@@ -1,3 -1,11 +1,11 @@@ + #if defined(CSQC) + #elif defined(MENUQC) + #elif defined(SVQC) + #include "../dpdefs/progsdefs.qh" + #include "../warpzonelib/util_server.qh" + #include "defs.qh" - #include "vehicles/vehicles_def.qh" ++ #include "../common/vehicles/sv_vehicles.qh" + #endif .float roomtype; .float radius; diff --cc qcsrc/server/t_items.qc index 75281cc50,03af802e7..5e33698b3 --- a/qcsrc/server/t_items.qc +++ b/qcsrc/server/t_items.qc @@@ -1,3 -1,33 +1,35 @@@ + #if defined(CSQC) + #include "../dpdefs/csprogsdefs.qh" + #include "../common/util.qh" + #include "../common/buffs.qh" + #include "../common/weapons/weapons.qh" + #include "../client/autocvars.qh" + #include "../client/movetypes.qh" + #include "../client/main.qh" + #include "../csqcmodellib/common.qh" + #include "../csqcmodellib/cl_model.qh" + #include "t_items.qh" + #elif defined(MENUQC) + #elif defined(SVQC) + #include "../dpdefs/progsdefs.qh" + #include "../dpdefs/dpextensions.qh" + #include "../warpzonelib/util_server.qh" + #include "../common/constants.qh" ++ #include "../common/effects.qh" + #include "../common/util.qh" + #include "../common/monsters/monsters.qh" + #include "../common/weapons/weapons.qh" + #include "weapons/weaponsystem.qh" + #include "t_items.qh" + #include "autocvars.qh" + #include "constants.qh" + #include "defs.qh" ++ #include "jeff.qh" + #include "../common/notifications.qh" + #include "../common/deathtypes.qh" + #include "mutators/mutators_include.qh" + #endif + #ifdef CSQC void ItemDraw() { @@@ -254,16 -295,26 +286,16 @@@ void ItemUpdate(entity item item.SendFlags |= ISF_LOCATION; } --float have_pickup_item(void) ++bool have_pickup_item(void) { - if(self.flags & FL_POWERUP) - { - if(autocvar_g_powerups > 0) - return true; - if(autocvar_g_powerups == 0) - return false; - } - else - { - if(autocvar_g_pickup_items > 0) - return true; - if(autocvar_g_pickup_items == 0) - return false; - if(g_weaponarena) - if(self.weapons || (self.items & IT_AMMO)) // no item or ammo pickups in weaponarena - return false; - } + if(autocvar_g_pickup_items > 0) - return TRUE; ++ return true; + if(autocvar_g_pickup_items == 0) - return FALSE; ++ return false; + if(g_weaponarena) + if(self.weapons || (self.items & IT_AMMO)) - return FALSE; - return TRUE; ++ return false; + return true; } /* @@@ -570,9 -632,19 +602,9 @@@ float Item_GiveTo(entity item, entity p Send_Notification(NOTIF_ONE, player, MSG_INFO, INFO_ITEM_WEAPON_GOT, item.netname); } - if (item.strength_finished) - { - pickedup = true; - player.strength_finished = max(player.strength_finished, time) + item.strength_finished; - } - if (item.invincible_finished) - { - pickedup = true; - player.invincible_finished = max(player.invincible_finished, time) + item.invincible_finished; - } if (item.superweapons_finished) { - pickedup = TRUE; + pickedup = true; player.superweapons_finished = max(player.superweapons_finished, time) + item.superweapons_finished; } @@@ -788,7 -862,7 +820,7 @@@ float weapon_pickupevalfunc(entity play float commodity_pickupevalfunc(entity player, entity item) { float c, i; - float need_shells = FALSE, need_nails = FALSE, need_rockets = FALSE, need_cells = FALSE, need_plasma = FALSE, need_fuel = FALSE, need_supercells = FALSE; - float need_shells = false, need_nails = false, need_rockets = false, need_cells = false, need_plasma = false, need_fuel = false; ++ float need_shells = false, need_nails = false, need_rockets = false, need_cells = false, need_plasma = false, need_fuel = false, need_supercells = false; entity wi; c = 0; @@@ -801,19 -875,17 +833,19 @@@ continue; if(wi.items & IT_SHELLS) - need_shells = TRUE; + need_shells = true; else if(wi.items & IT_NAILS) - need_nails = TRUE; + need_nails = true; else if(wi.items & IT_ROCKETS) - need_rockets = TRUE; + need_rockets = true; else if(wi.items & IT_CELLS) - need_cells = TRUE; + need_cells = true; else if(wi.items & IT_PLASMA) - need_plasma = TRUE; + need_plasma = true; else if(wi.items & IT_FUEL) - need_cells = TRUE; - need_fuel = true; ++ need_cells = true; + else if(wi.items & IT_SUPERCELLS) - need_supercells = TRUE; ++ need_supercells = true; } // TODO: figure out if the player even has the weapon this ammo is for? @@@ -959,8 -1035,8 +991,8 @@@ void StartItem (string itemmodel, strin setsize (self, '-16 -16 0', '16 16 48'); else setsize (self, '-16 -16 0', '16 16 32'); - + self.SendFlags |= ISF_SIZE; - // note droptofloor returns FALSE if stuck/or would fall too far + // note droptofloor returns false if stuck/or would fall too far droptofloor(); waypoint_spawnforitem(self); } @@@ -1007,12 -1088,10 +1039,12 @@@ self.target = "###item###"; // for finding the nearest item using find() } - self.bot_pickup = TRUE; + self.bot_pickup = true; self.bot_pickupevalfunc = pickupevalfunc; self.bot_pickupbasevalue = pickupbasevalue; - self.mdl = self.model; + //if(self.mdl) { strunzone(self.mdl); } + self.mdl = ""; + self.mdl = strzone(self.model); self.netname = itemname; self.touch = Item_Touch; setmodel(self, "null"); // precision set below @@@ -1542,11 -1618,13 +1571,11 @@@ float GiveItems(entity e, float beginar got = 0; - _switchweapon = FALSE; + _switchweapon = false; if (e.autoswitch) if (e.switchweapon == w_getbestweapon(e)) - _switchweapon = TRUE; + _switchweapon = true; - e.strength_finished = max(0, e.strength_finished - time); - e.invincible_finished = max(0, e.invincible_finished - time); e.superweapons_finished = max(0, e.superweapons_finished - time); PREGIVE(e, items); diff --cc qcsrc/server/t_items.qh index 7f6caefd0,bb914234e..59239c7f5 --- a/qcsrc/server/t_items.qh +++ b/qcsrc/server/t_items.qh @@@ -1,59 -1,67 +1,62 @@@ + #ifndef T_ITEMS_H + #define T_ITEMS_H + // constants - #define WANT_CONST /* we WANT these to be constant, but it conflicts with the declaration in dpdefs/progsdefs.qc */ - const float IT_UNLIMITED_WEAPON_AMMO = 1; // when this bit is set, using a weapon does not reduce ammo. Checkpoints can give this powerup. - const float IT_UNLIMITED_SUPERWEAPONS = 2; // when this bit is set, superweapons don't expire. Checkpoints can give this powerup. - const float IT_CTF_SHIELDED = 4; // set for the flag shield - const float IT_USING_JETPACK = 8; // confirmation that button is pressed - const float IT_JETPACK = 16; // actual item - const float IT_FUEL_REGEN = 32; // fuel regeneration trigger + const int IT_UNLIMITED_WEAPON_AMMO = 1; // when this bit is set, using a weapon does not reduce ammo. Checkpoints can give this powerup. + const int IT_UNLIMITED_SUPERWEAPONS = 2; // when this bit is set, superweapons don't expire. Checkpoints can give this powerup. + const int IT_CTF_SHIELDED = 4; // set for the flag shield + const int IT_USING_JETPACK = 8; // confirmation that button is pressed + const int IT_JETPACK = 16; // actual item + const int IT_FUEL_REGEN = 32; // fuel regeneration trigger // where is 64... ? - const float IT_FUEL = 128; - WANT_CONST float IT_SHELLS = 256; - WANT_CONST float IT_NAILS = 512; - WANT_CONST float IT_ROCKETS = 1024; - WANT_CONST float IT_CELLS = 2048; - const float IT_SUPERWEAPON = 4096; - const float IT_STRENGTH = 8192; - const float IT_INVINCIBLE = 16384; - const float IT_HEALTH = 32768; - const float IT_SUPERCELLS = 8388608; - const float IT_PLASMA = 16777216; + const int IT_FUEL = 128; + const int IT_SHELLS = 256; + const int IT_NAILS = 512; + const int IT_ROCKETS = 1024; + const int IT_CELLS = 2048; + const int IT_SUPERWEAPON = 4096; + const int IT_STRENGTH = 8192; + const int IT_INVINCIBLE = 16384; + const int IT_HEALTH = 32768; ++ + const int IT_PLASMA = 65536; ++const int IT_SUPERCELLS = 8388608; // shared value space (union): // for items: - WANT_CONST float IT_KEY1 = 131072; - WANT_CONST float IT_KEY2 = 262144; + const int IT_KEY1 = 131072; + const int IT_KEY2 = 262144; - // for players: - const int IT_RED_FLAG_TAKEN = 32768; - const int IT_RED_FLAG_LOST = 65536; - const int IT_RED_FLAG_CARRYING = 98304; - const int IT_BLUE_FLAG_TAKEN = 131072; - const int IT_BLUE_FLAG_LOST = 262144; - const int IT_BLUE_FLAG_CARRYING = 393216; // end - const float IT_5HP = 524288; - const float IT_25HP = 1048576; - const float IT_ARMOR_SHARD = 2097152; - const float IT_ARMOR = 4194304; + const int IT_5HP = 524288; + const int IT_25HP = 1048576; + const int IT_ARMOR_SHARD = 2097152; + const int IT_ARMOR = 4194304; // item masks - const float IT_AMMO = 3968; // IT_FUEL | IT_SHELLS | IT_NAILS | IT_ROCKETS | IT_CELLS | IT_PLASMA; - const float IT_PICKUPMASK = 51; // IT_FUEL_REGEN | IT_JETPACK | IT_UNLIMITED_AMMO; // strength and invincible are handled separately - const float IT_UNLIMITED_AMMO = 3; // IT_UNLIMITED_SUPERWEAPONS | IT_UNLIMITED_WEAPON_AMMO; + const int IT_AMMO = 3968; // IT_FUEL | IT_SHELLS | IT_NAILS | IT_ROCKETS | IT_CELLS | IT_PLASMA; + const int IT_PICKUPMASK = 51; // IT_FUEL_REGEN | IT_JETPACK | IT_UNLIMITED_AMMO; // strength and invincible are handled separately + const int IT_UNLIMITED_AMMO = 3; // IT_UNLIMITED_SUPERWEAPONS | IT_UNLIMITED_WEAPON_AMMO; - const float AMMO_COUNT = 4; // amount of ammo types to show in the inventory panel + const int AMMO_COUNT = 4; // amount of ammo types to show in the inventory panel // item networking - #define ISF_LOCATION 2 - #define ISF_MODEL 4 - #define ISF_STATUS 8 - #define ITS_STAYWEP 1 - #define ITS_ANIMATE1 2 - #define ITS_ANIMATE2 4 - #define ITS_AVAILABLE 8 - #define ITS_ALLOWFB 16 - #define ITS_ALLOWSI 32 - #define ITS_POWERUP 64 - #define ISF_COLORMAP 16 - #define ISF_DROP 32 - #define ISF_ANGLES 64 - #define ISF_SIZE 128 - - .float ItemStatus; + const int ISF_LOCATION = 2; + const int ISF_MODEL = 4; + const int ISF_STATUS = 8; + const int ITS_STAYWEP = 1; + const int ITS_ANIMATE1 = 2; + const int ITS_ANIMATE2 = 4; + const int ITS_AVAILABLE = 8; + const int ITS_ALLOWFB = 16; + const int ITS_ALLOWSI = 32; + const int ITS_POWERUP = 64; + const int ISF_COLORMAP = 16; + const int ISF_DROP = 32; + const int ISF_ANGLES = 64; + const int ISF_SIZE = 128; + + .int ItemStatus; #ifdef CSQC diff --cc qcsrc/server/t_jumppads.qc index 76f90e958,92af1cdd0..e77ac9bf9 --- a/qcsrc/server/t_jumppads.qc +++ b/qcsrc/server/t_jumppads.qc @@@ -1,13 -1,4 +1,5 @@@ - const float PUSH_ONCE = 1; - const float PUSH_SILENT = 2; - - .float pushltime; - .float istypefrag; - .float height; - - void() SUB_UseTargets; - - float trigger_push_calculatevelocity_flighttime; + #include "t_jumppads.qh" ++#include "../common/effects.qh" void trigger_push_use() { diff --cc qcsrc/server/t_teleporters.qc index 78d79c708,46df0eb78..6b1186855 --- a/qcsrc/server/t_teleporters.qc +++ b/qcsrc/server/t_teleporters.qc @@@ -1,3 -1,24 +1,25 @@@ + #include "t_teleporters.qh" + + #if defined(CSQC) + #elif defined(MENUQC) + #elif defined(SVQC) + #include "../warpzonelib/common.qh" + #include "../warpzonelib/util_server.qh" + #include "../warpzonelib/server.qh" + #include "../common/constants.qh" ++ #include "../common/effects.qh" + #include "../common/util.qh" + #include "weapons/csqcprojectile.qh" + #include "autocvars.qh" + #include "constants.qh" + #include "defs.qh" + #include "../common/deathtypes.qh" - #include "tturrets/include/turrets_early.qh" - #include "vehicles/vehicles_def.qh" ++ #include "../common/turrets/sv_turrets.qh" ++ #include "../common/vehicles/sv_vehicles.qh" + #include "../common/mapinfo.qh" + #include "anticheat.qh" + #endif + void trigger_teleport_use() { if(teamplay) diff --cc qcsrc/server/weapons/common.qc index 623aedca6,c37fce0ab..dfb232388 --- a/qcsrc/server/weapons/common.qc +++ b/qcsrc/server/weapons/common.qc @@@ -63,10 -89,9 +77,10 @@@ void W_PrepareExplosionByDamage(entity { self.takedamage = DAMAGE_NO; self.event_damage = func_null; - + if(IS_CLIENT(attacker) && !autocvar_g_projectiles_keep_owner) { + self.jeff_projowner = self.realowner; self.owner = attacker; self.realowner = attacker; } diff --cc qcsrc/server/weapons/common.qh index 563a65784,341f4ff35..a0f99999d --- a/qcsrc/server/weapons/common.qh +++ b/qcsrc/server/weapons/common.qh @@@ -1,8 -1,10 +1,11 @@@ + #ifndef WEAPONS_COMMON_H + #define WEAPONS_COMMON_H void W_GiveWeapon (entity e, float wep); -.float prevstrengthsound; -.float prevstrengthsoundattempt; -void W_PlayStrengthSound(entity player); float W_CheckProjectileDamage(entity inflictor, entity projowner, float deathtype, float exception); void W_PrepareExplosionByDamage(entity attacker, void() explode); + - +// jeff +.entity jeff_projowner; ++ + #endif diff --cc qcsrc/server/weapons/selection.qc index 107d52e1a,5ea0a5d9d..efe525504 --- a/qcsrc/server/weapons/selection.qc +++ b/qcsrc/server/weapons/selection.qc @@@ -56,13 -71,13 +71,13 @@@ float client_hasweapon(entity cl, floa if (complain) if(IS_REAL_CLIENT(cl)) { - play2(cl, "weapons/unavailable.wav"); + play2(cl, W_Sound("unavailable")); Send_WeaponComplain (cl, wpn, 0); } - return FALSE; + return false; } } - return TRUE; + return true; } if (complain) { @@@ -99,9 -114,9 +114,9 @@@ Send_WeaponComplain (cl, wpn, 2); } - play2(cl, "weapons/unavailable.wav"); + play2(cl, W_Sound("unavailable")); } - return FALSE; + return false; } float W_GetCycleWeapon(entity pl, string weaponorder, float dir, float imp, float complain, float skipmissing) diff --cc qcsrc/server/weapons/tracing.qc index 9355d716b,c3532d3d6..fd1d2f4ca --- a/qcsrc/server/weapons/tracing.qc +++ b/qcsrc/server/weapons/tracing.qc @@@ -1,3 -1,18 +1,20 @@@ + #if defined(CSQC) + #elif defined(MENUQC) + #elif defined(SVQC) + #include "../../dpdefs/progsdefs.qh" + #include "../../dpdefs/dpextensions.qh" + #include "../../warpzonelib/common.qh" + #include "../../common/constants.qh" ++ #include "../../common/effects.qh" + #include "../../common/util.qh" + #include "../../common/weapons/weapons.qh" + #include "tracing.qh" + #include "../autocvars.qh" + #include "../defs.qh" + #include "../antilag.qh" ++ #include "../jeff.qh" + #endif + // this function calculates w_shotorg and w_shotdir based on the weapon model // offset, trueaim and antilag, and won't put w_shotorg inside a wall. // make sure you call makevectors first (FIXME?) @@@ -169,27 -185,6 +186,31 @@@ void W_SetupProjVelocity_Explicit(entit proj.velocity = W_CalculateProjectileVelocity(proj.owner.velocity, pSpeed * dir, forceAbsolute); } +float Headshot(entity targ, entity ent, vector hitloc, vector start, vector end) +{ + if(!autocvar_sv_headshot) - return FALSE; - if(ent.weapon != WEP_RIFLE && ent.weapon != WEP_VAPORIZER && ent.weapon != WEP_VORTEX && ent.weapon != WEP_REVOLVER) - return FALSE; ++ return false; ++ if(ent.weapon != WEP_RIFLE && ent.weapon != WEP_VAPORIZER && ent.weapon != WEP_VORTEX ++#ifdef CHAOS ++ && ent.weapon != WEP_REVOLVER ++#endif ++ ) ++ return false; + if(!IS_PLAYER(targ)) - return FALSE; ++ return false; + if(Player_Trapped(targ) || !targ.takedamage) - return FALSE; ++ return false; + vector headmins, headmaxs, org; + org = antilag_takebackorigin(targ, time - ANTILAG_LATENCY(ent)); + headmins = org + '0.6 0 0' * targ.mins_x + '0 0.6 0' * targ.mins_y + '0 0 1' * (1.3 * targ.view_ofs_z - 0.3 * targ.maxs_z); + headmaxs = org + '0.6 0 0' * targ.maxs_x + '0 0.6 0' * targ.maxs_y + '0 0 1' * targ.maxs_z; + if(trace_hits_box(start, end, headmins, headmaxs)) + { - return TRUE; ++ return true; + } - return FALSE; ++ return false; +} + // ==================== // Ballistics Tracing diff --cc qcsrc/server/weapons/weaponsystem.qc index 16936196e,ab85389c5..2c4120027 --- a/qcsrc/server/weapons/weaponsystem.qc +++ b/qcsrc/server/weapons/weaponsystem.qc @@@ -619,7 -633,7 +633,11 @@@ void weapon_thinkf(float fr, float t, v if((fr == WFRAME_FIRE1 || fr == WFRAME_FIRE2) && t) { - if(((self.weapon == WEP_SHOCKWAVE || self.weapon == WEP_SHOTGUN) && fr == WFRAME_FIRE2) || self.weapon == WEP_LIGHTSABRE) - if((self.weapon == WEP_SHOCKWAVE || self.weapon == WEP_SHOTGUN) && fr == WFRAME_FIRE2) ++ if(((self.weapon == WEP_SHOCKWAVE || self.weapon == WEP_SHOTGUN) && fr == WFRAME_FIRE2) ++#ifdef CHAOS ++ || self.weapon == WEP_LIGHTSABRE ++#endif ++ ) animdecide_setaction(self, ANIMACTION_MELEE, restartanim); else animdecide_setaction(self, ANIMACTION_SHOOT, restartanim); diff --cc qcsrc/server/weapons/weaponsystem.qh index 03dd69bbe,bb0cea798..86e52c89b --- a/qcsrc/server/weapons/weaponsystem.qh +++ b/qcsrc/server/weapons/weaponsystem.qh @@@ -1,6 -1,43 +1,43 @@@ - float weaponswapping; + #ifndef WEAPONSYSTEM_H + #define WEAPONSYSTEM_H + + .float wframe; + float internalteam; + float weaponswapping; + entity weapon_dropevent_item; - void W_DropEvent(float event, entity player, float weapon_type, entity weapon_item); + // VorteX: static frame globals + const float WFRAME_DONTCHANGE = -1; + const float WFRAME_FIRE1 = 0; + const float WFRAME_FIRE2 = 1; + const float WFRAME_IDLE = 2; + const float WFRAME_RELOAD = 3; + + void CL_SpawnWeaponentity(); + + vector CL_Weapon_GetShotOrg(float wpn); -float forbidWeaponUse(); +float forbidWeaponUse(entity player); + + void W_AttachToShotorg(entity flash, vector offset); + + void W_DecreaseAmmo(float ammo_use); + + void W_DropEvent(float event, entity player, float weapon_type, entity weapon_item); + + void W_Reload(float sent_ammo_min, string sent_sound); + + void W_WeaponFrame(); + + float W_WeaponRateFactor(); + + float weapon_prepareattack(float secondary, float attacktime); + + float weapon_prepareattack_check(float secondary, float attacktime); + + float weapon_prepareattack_do(float secondary, float attacktime); + + void weapon_thinkf(float fr, float t, void() func); + + #endif diff --cc qcsrc/warpzonelib/common.qc index f12b1d17a,aa0de9119..5dc4a0a9f --- a/qcsrc/warpzonelib/common.qc +++ b/qcsrc/warpzonelib/common.qc @@@ -564,45 -573,6 +573,45 @@@ vector WarpZoneLib_NearestPointOnBox(ve return nearest; } +float WarpZoneLib_BadClassname(string myclassname) +{ + switch(myclassname) + { + case "weapon_info": + case "monster_info": + case "deathtype": + case "callback": + case "callbackchain": + case "weaponentity": + case "exteriorweaponentity": + case "csqc_score_team": + case "pingplreport": + case "ent_client_scoreinfo": + case "saved_cvar_value": + case "accuracy": + case "entcs_sender_v2": + case "entcs_receiver_v2": + case "clientinit": + case "sprite_waypoint": + case "waypoint": + case "gibsplash": + //case "net_linked": // actually some real entities are linked without classname, fail + case "": - return TRUE; ++ return true; + } + + if(startsWith(myclassname, "msg_")) - return TRUE; ++ return true; + + if(startsWith(myclassname, "target_")) - return TRUE; ++ return true; + + if(startsWith(myclassname, "info_")) - return TRUE; ++ return true; + - return FALSE; ++ return false; +} + .float WarpZone_findradius_hit; .entity WarpZone_findradius_next; void WarpZone_FindRadius_Recurse(vector org, float rad, vector org0, vector transform, vector shift, float needlineofsight)