QCCFLAGS ?= \
-std=gmqcc \
-O3 -flno \
+ -DSTUFFTO_ENABLED \
-Werror -fno-bail-on-werror -Wall \
-fftepp -fftepp-predefs -Wcpp -futf8 \
$(QCCFLAGS_WTFS) \
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");
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;
// 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();
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 (!(calledhooks & HOOK_END))
localcmd("\ncl_hook_gameend\n");
}
+
+ deactivate_minigame();
+ HUD_MinigameMenu_Close();
}
.float has_team;
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)
Porto_Init();
TrueAim_Init();
+ FPSReporter_Init();
+
+ fovlock = -1;
postinit = true;
}
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;
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
}
}
+.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));
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);
}
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));
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();
}
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;
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())
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;
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))
}
}
+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)
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);
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;
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)
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;
}
}
+float fps_start;
+float fps_frames;
+
+
+void FPSCounter_Update()
+{
+ if(time - fps_start >= 1) {
+ cl_fps = fps_frames;
+ fps_frames = 0;
+ fps_start = time;
+ }
+
+ fps_frames++;
+}
+
#define BUTTON_3 4
#define BUTTON_4 8
float cl_notice_run();
float prev_myteam;
+float oldfov;
+.float health;
void CSQC_UpdateView(float w, float h)
{
entity e;
float fov;
float f, i;
vector vf_size, vf_min;
- float a;
+ float pink_noise;
execute_next_frame();
else
view_quality = 1;
+ button_attack = (input_buttons & 1);
button_attack2 = (input_buttons & BUTTON_3);
button_zoom = (input_buttons & BUTTON_4);
// 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); }
}
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);
}
// 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);
Announcer();
fov = autocvar_fov;
+
+ if(fov != oldfov)
+ {
+ localcmd(strcat("\ncmd report fov ", ftos(fov), "\n"));
+ oldfov = fov;
+ }
+
if(fov <= 59.5)
{
if(!zoomscript_caught)
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();
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_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();
}
}
// 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_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();
float announcer_1min;
float 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);
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;
float autocvar_cl_orthoview_nofog;
float autocvar_cl_particlegibs;
float autocvar_cl_particles_oldvortexbeam;
+float autocvar_cl_particles_newvortexbeam;
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_spawnzoom_speed = 1;
var float autocvar_cl_spawnzoom_factor = 2;
float autocvar_cl_stripcolorcodes;
-var float autocvar_cl_vehicle_spiderbot_cross_alpha = 0.6;
-var float autocvar_cl_vehicle_spiderbot_cross_size = 1;
-var float autocvar_cl_vehicles_hud_tactical = 1;
float autocvar_cl_velocityzoom_enabled;
float autocvar_cl_velocityzoom_factor;
var float autocvar_cl_velocityzoom_type = 3;
float autocvar_crosshair_ring_reload_alpha;
float autocvar_crosshair_ring_reload_size;
float autocvar_crosshair_size;
+float autocvar_cl_vaporizerbeam;
float autocvar_ekg;
float autocvar_fov;
float autocvar_g_balance_damagepush_speedfactor;
float autocvar_hud_panel_powerups_flip;
float autocvar_hud_panel_powerups_iconalign;
float autocvar_hud_panel_powerups_progressbar;
-float autocvar_hud_panel_buffs;
+var float 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;
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;
float autocvar_hud_panel_radar_foreground_alpha;
float autocvar_hud_panel_weapons_ammo_alpha;
string autocvar_hud_panel_weapons_ammo_color;
float autocvar_hud_panel_weapons_ammo_full_cells;
-float autocvar_hud_panel_weapons_ammo_full_plasma;
+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_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;
vector autocvar_hud_progressbar_acceleration_color;
vector autocvar_hud_progressbar_acceleration_neg_color;
float autocvar_hud_progressbar_alpha;
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_r_fakelight;
float autocvar_r_fullbright;
float autocvar_r_letterbox;
-float autocvar_scoreboard_accuracy;
-float autocvar_scoreboard_accuracy_doublerows;
+var float autocvar_scoreboard_accuracy = 1;
float 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_vid_pixelheight;
float autocvar_viewsize;
float autocvar_cl_hitsound;
-var float autocvar_cl_hitsound_min_pitch = 0.75; // minimal difference in minsta
+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;
string autocvar__togglezoom;
float autocvar_cl_damageeffect;
float autocvar_cl_forceplayermodels;
float 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_deathglow;
float 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 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_cl_nade_timer;
+float autocvar_r_drawviewmodel;
+float autocvar_cl_crouch;
+var float autocvar_cl_nade_timer = 1;
+float autocvar_cl_specfov; // disable by default?
return;
}
+ case "quickmenu":
+ {
+ if(HUD_QuickMenu_IsOpened())
+ HUD_QuickMenu_Close();
+ else
+ HUD_QuickMenu_Open();
+ return;
+ }
+
+ case "minigame":
+ {
+ if(HUD_MinigameMenu_IsOpened())
+ HUD_MinigameMenu_Close();
+ else
+ HUD_MinigameMenu_Open();
+ return;
+ }
+
case "save":
{
if(argv(2))
case "radar":
{
if(argv(2))
- hud_panel_radar_maximized = InterpretBoolean(argv(2));
+ HUD_Radar_Show_Maximized(InterpretBoolean(argv(2)),0);
else
- hud_panel_radar_maximized = !hud_panel_radar_maximized;
+ HUD_Radar_Show_Maximized(!hud_panel_radar_maximized,0);
+ return;
+ }
+
+ case "clickradar":
+ {
+ HUD_Radar_Show_Maximized(!hud_panel_radar_mouse,1);
return;
}
}
print(" 'configname' is the name to save to for \"save\" action,\n");
print(" 'radartoggle' is to control hud_panel_radar_maximized for \"radar\" action,\n");
print(" and 'layout' is how to organize the scoreboard columns for the set action.\n");
- print(" Full list of commands here: \"configure, save, scoreboard_columns_help, scoreboard_columns_set, radar.\"\n");
+ print(" Full list of commands here: \"configure, quickmenu, save, scoreboard_columns_help, scoreboard_columns_set, radar.\"\n");
return;
}
}
--- /dev/null
+.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());
+ }
+}
--- /dev/null
+//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)
+
+.float cq_status;
+#define CP_NEUTRAL 1
+#define CP_CAPTURED 2
+
+.float cq_capdistance;
+
+#define CQ_CP_MIN ('-35 -35 -3')
+#define CQ_CP_MAX ('35 35 195')
+
+.float health;
--- /dev/null
+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;
+}
+
+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;
+ }
+}
--- /dev/null
+const vector CPICON_MIN = '-32 -32 -9';
+const vector CPICON_MAX = '32 32 25';
+
+float CPSF_STATUS = 4;
+float CPSF_SETUP = 8;
+
+void ent_cpicon();
+void cpicon_precache();
else
isfriend = islocalplayer;
- if(autocvar_cl_forcemyplayermodel != "" && forceplayermodels_myisgoodmodel && isfriend)
+ if(autocvar_cl_forcemyplayermodel != "" && forceplayermodels_myisgoodmodel && (isfriend || autocvar_cl_forcemyplayermodel_always))
{
self.model = forceplayermodels_mymodel;
self.modelindex = forceplayermodels_mymodelindex;
if(self.tag_entity)
{
// the best part is: IT EXISTS
- if(substring(self.model, 0, 17) == "models/weapons/v_")
+ if(substring(self.model, 0, 14) == "models/weapons")
{
- if(substring(self.tag_entity.model, 0, 17) == "models/weapons/h_")
+ if(substring(self.tag_entity.model, 0, 14) == "models/weapons")
{
self.tag_index = gettagindex(self.tag_entity, "weapon");
if(!self.tag_index)
self.tag_index = self.tag_entity.bone_weapon;
}
}
+ else if(substring(self.model, 0, 12) == "models/hats/")
+ {
+ if(substring(self.tag_entity.model, 0, 12) == "models/hats/")
+ {
+ self.tag_index = gettagindex(self.tag_entity, "tag_head");
+ if(!self.tag_index)
+ self.tag_index = gettagindex(self.tag_entity, "head");
+ if(!self.tag_index)
+ {
+ // we need to prevent this from 'appening
+ self.tag_entity = world;
+ self.drawmask = 0;
+ }
+ }
+ else if(self.tag_entity.isplayermodel)
+ {
+ skeleton_loadinfo(self.tag_entity);
+ self.tag_index = self.tag_entity.bone_hat;
+ }
+ }
- if(substring(self.tag_entity.model, 0, 17) == "models/weapons/v_")
+ if(substring(self.tag_entity.model, 0, 14) == "models/weapons")
{
self.tag_index = gettagindex(self.tag_entity, "shot");
if(!self.tag_index)
void CSQCPlayer_Precache()
{
precache_sound("misc/jetpack_fly.wav");
+ if(autocvar_cl_forcemyplayermodel != "")
+ precache_model(autocvar_cl_forcemyplayermodel);
}
// general functions
sound(self, CH_SHOTS, "weapons/rocket_impact.wav", VOL_BASE, ATTEN_MIN);
pointparticles(particleeffectnum("explosion_big"), self.origin, w_backoff * 1000, 1);
break;
+ case DEATH_VH_TANK_DEATH:
+ case DEATH_VH_TANKLL48:
+ sound(self, CH_SHOTS, "weapons/rocket_impact.wav", VOL_BASE, ATTEN_MIN);
+ pointparticles(particleeffectnum("explosion_big"), self.origin, w_backoff * 1000, 1);
+ break;
case DEATH_VH_RAPT_CANNON:
sound(self, CH_SHOTS, "weapons/laserimpact.wav", VOL_BASE, ATTEN_NORM);
pointparticles(particleeffectnum("electro_impact"), self.origin, w_backoff * 1000, 1);
break;
- case DEATH_TURRET_WALK_MEELE:
+ case DEATH_TURRET_WALK_MELEE:
sound(self, CH_SHOTS, "weapons/ric1.wav", VOL_BASE, ATTEN_MIN);
pointparticles(particleeffectnum("TE_SPARK"), self.origin, w_backoff * 1000, 1);
break;
--- /dev/null
+float generator_precached;
+.float count;
+.float 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;
+}
+
+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;
+ }
+}
--- /dev/null
+const vector GENERATOR_MIN = '-52 -52 -14';
+const vector GENERATOR_MAX = '52 52 75';
+
+float GSF_STATUS = 4;
+float GSF_SETUP = 8;
+
+void ent_generator();
+void generator_precache();
\ No newline at end of file
{
default:
case ENT_CLIENT_HOOK:
+ string suffix = "";
+ if(autocvar_cl_nexuiz_hook)
+ suffix = "_nexuiz";
intensity = 1;
offset = 0;
switch(t)
case NUM_TEAM_4: tex = "particles/hook_pink"; rgb = '1 0.3 1'; break;
default: tex = "particles/hook_white"; rgb = getcsqcplayercolor(self.sv_entnum); break;
}
+ if(suffix) { tex = strcat(tex, suffix); }
break;
case ENT_CLIENT_ARC_BEAM: // todo
intensity = bound(0.2, 1 + Noise_Pink(self, frametime) * 1 + Noise_Burst(self, frametime, 0.03) * 0.3, 2);
case ammo_shells: ammo_full = autocvar_hud_panel_weapons_ammo_full_shells; break;
case ammo_nails: ammo_full = autocvar_hud_panel_weapons_ammo_full_nails; break;
case ammo_rockets: ammo_full = autocvar_hud_panel_weapons_ammo_full_rockets; break;
+ case ammo_supercells:
case ammo_cells: ammo_full = autocvar_hud_panel_weapons_ammo_full_cells; break;
case ammo_plasma: ammo_full = autocvar_hud_panel_weapons_ammo_full_plasma; break;
case ammo_fuel: ammo_full = autocvar_hud_panel_weapons_ammo_full_fuel; break;
//
void HUD_Powerups(void)
{
- float strength_time, shield_time, superweapons_time;
+ float superweapons_time;
if(!autocvar__hud_configure)
{
if(!autocvar_hud_panel_powerups) return;
if(spectatee_status == -1) return;
- if(!(getstati(STAT_ITEMS, 0, 24) & (IT_STRENGTH | IT_INVINCIBLE | IT_SUPERWEAPON))) return;
+ if(!(getstati(STAT_ITEMS, 0, 24) & IT_SUPERWEAPON)) return;
if (getstati(STAT_HEALTH) <= 0) return;
- strength_time = bound(0, getstatf(STAT_STRENGTH_FINISHED) - time, 99);
- shield_time = bound(0, getstatf(STAT_INVINCIBLE_FINISHED) - time, 99);
superweapons_time = bound(0, getstatf(STAT_SUPERWEAPONS_FINISHED) - time, 99);
if (getstati(STAT_ITEMS, 0, 24) & IT_UNLIMITED_SUPERWEAPONS)
}
else
{
- strength_time = 15;
- shield_time = 27;
superweapons_time = 13;
}
pos = panel_pos;
mySize = panel_size;
- HUD_Panel_DrawBg(bound(0, max(strength_time, shield_time, superweapons_time), 1));
+ HUD_Panel_DrawBg(bound(0, superweapons_time, 1));
if(panel_bg_padding)
{
pos += '1 1 0' * panel_bg_padding;
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(superweapons_is == 0)
+ if (panel_ar >= 4 || (panel_ar >= 1/4 && panel_ar < 1))
{
- 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;
- }
+ mySize_x *= 0.5;
}
else
{
- 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;
- }
+ mySize_y *= 0.5;
}
- 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)
{
- strength_baralign = (autocvar_hud_panel_powerups_baralign == 2 || autocvar_hud_panel_powerups_baralign == 1);
- shield_baralign = (autocvar_hud_panel_powerups_baralign == 3 || autocvar_hud_panel_powerups_baralign == 1);
- strength_iconalign = (autocvar_hud_panel_powerups_iconalign == 2 || autocvar_hud_panel_powerups_iconalign == 1);
- shield_iconalign = (autocvar_hud_panel_powerups_iconalign == 3 || autocvar_hud_panel_powerups_iconalign == 1);
+ superweapons_baralign = (autocvar_hud_panel_powerups_baralign == 3 || autocvar_hud_panel_powerups_baralign == 1);
+ superweapons_iconalign = (autocvar_hud_panel_powerups_iconalign == 3 || autocvar_hud_panel_powerups_iconalign == 1);
}
else
{
- shield_baralign = (autocvar_hud_panel_powerups_baralign == 2 || autocvar_hud_panel_powerups_baralign == 1);
- strength_baralign = (autocvar_hud_panel_powerups_baralign == 3 || autocvar_hud_panel_powerups_baralign == 1);
- shield_iconalign = (autocvar_hud_panel_powerups_iconalign == 2 || autocvar_hud_panel_powerups_iconalign == 1);
- strength_iconalign = (autocvar_hud_panel_powerups_iconalign == 3 || autocvar_hud_panel_powerups_iconalign == 1);
- }
-
- if(superweapons_is == 0)
- {
- superweapons_iconalign = strength_iconalign;
- superweapons_baralign = 2;
- }
- else if(superweapons_is == 1)
- {
- superweapons_offset = strength_offset;
- superweapons_iconalign = strength_iconalign;
- superweapons_baralign = strength_baralign;
- }
- else // if(superweapons_is == 2)
- {
- superweapons_offset = shield_offset;
- superweapons_iconalign = shield_iconalign;
- superweapons_baralign = shield_baralign;
- }
-
- if(shield_time)
- {
- const float maxshield = 30;
- float shield = ceil(shield_time);
- if(autocvar_hud_panel_powerups_progressbar)
- HUD_Panel_DrawProgressBar(pos + shield_offset, mySize, autocvar_hud_panel_powerups_progressbar_shield, shield/maxshield, is_vertical, shield_baralign, autocvar_hud_progressbar_shield_color, autocvar_hud_progressbar_alpha * panel_fg_alpha, DRAWFLAG_NORMAL);
- if(autocvar_hud_panel_powerups_text)
- {
- if(shield > 1)
- DrawNumIcon(pos + shield_offset, mySize, shield, "shield", is_vertical, shield_iconalign, '1 1 1', 1);
- if(shield <= 5)
- DrawNumIcon_expanding(pos + shield_offset, mySize, shield, "shield", is_vertical, shield_iconalign, '1 1 1', 1, bound(0, (shield - shield_time) / 0.5, 1));
- }
- }
-
- if(strength_time)
- {
- const float maxstrength = 30;
- float strength = ceil(strength_time);
- if(autocvar_hud_panel_powerups_progressbar)
- HUD_Panel_DrawProgressBar(pos + strength_offset, mySize, autocvar_hud_panel_powerups_progressbar_strength, strength/maxstrength, is_vertical, strength_baralign, autocvar_hud_progressbar_strength_color, autocvar_hud_progressbar_alpha * panel_fg_alpha, DRAWFLAG_NORMAL);
- if(autocvar_hud_panel_powerups_text)
- {
- if(strength > 1)
- DrawNumIcon(pos + strength_offset, mySize, strength, "strength", is_vertical, strength_iconalign, '1 1 1', 1);
- if(strength <= 5)
- DrawNumIcon_expanding(pos + strength_offset, mySize, strength, "strength", is_vertical, strength_iconalign, '1 1 1', 1, bound(0, (strength - strength_time) / 0.5, 1));
- }
+ superweapons_baralign = (autocvar_hud_panel_powerups_baralign == 2 || autocvar_hud_panel_powerups_baralign == 1);
+ superweapons_iconalign = (autocvar_hud_panel_powerups_iconalign == 2 || autocvar_hud_panel_powerups_iconalign == 1);
}
if(superweapons_time)
{
- const float maxsuperweapons = 30;
+ float maxsuperweapons = 30;
float superweapons = ceil(superweapons_time);
if(autocvar_hud_panel_powerups_progressbar)
HUD_Panel_DrawProgressBar(pos + superweapons_offset, mySize, autocvar_hud_panel_powerups_progressbar_superweapons, superweapons/maxsuperweapons, is_vertical, superweapons_baralign, autocvar_hud_progressbar_superweapons_color, autocvar_hud_progressbar_alpha * panel_fg_alpha, DRAWFLAG_NORMAL);
// Radar (#6)
//
+
+float HUD_Radar_Clickable()
+{
+ return hud_panel_radar_mouse && !hud_panel_radar_temp_hidden;
+}
+
+void HUD_Radar_Show_Maximized(float show,float clickable)
+{
+ hud_panel_radar_maximized = show;
+ hud_panel_radar_temp_hidden = 0;
+
+ if ( show )
+ {
+ if (clickable)
+ {
+ if(autocvar_hud_cursormode)
+ setcursormode(1);
+ hud_panel_radar_mouse = 1;
+ }
+ }
+ else if ( hud_panel_radar_mouse )
+ {
+ hud_panel_radar_mouse = 0;
+ mouseClicked = 0;
+ if(autocvar_hud_cursormode)
+ if(!mv_active)
+ setcursormode(0);
+ }
+}
+void HUD_Radar_Hide_Maximized()
+{
+ HUD_Radar_Show_Maximized(false,false);
+}
+
+
+float HUD_Radar_InputEvent(float bInputType, float nPrimary, float nSecondary)
+{
+ if(!hud_panel_radar_maximized || !hud_panel_radar_mouse ||
+ autocvar__hud_configure || mv_active)
+ return false;
+
+ if(bInputType == 3)
+ {
+ mousepos_x = nPrimary;
+ mousepos_y = nSecondary;
+ return true;
+ }
+
+ 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 ( nPrimary == K_ESCAPE && bInputType == 0 )
+ {
+ HUD_Radar_Hide_Maximized();
+ }
+ else
+ {
+ // allow console/use binds to work without hiding the map
+ string con_keys;
+ float keys;
+ float i;
+ con_keys = strcat(findkeysforcommand("toggleconsole", 0)," ",findkeysforcommand("+use", 0)) ;
+ keys = tokenize(con_keys); // findkeysforcommand returns data for this
+ for (i = 0; i < keys; ++i)
+ {
+ if(nPrimary == stof(argv(i)))
+ return false;
+ }
+
+ if ( getstati(STAT_HEALTH) <= 0 )
+ {
+ // Show scoreboard
+ if ( bInputType < 2 )
+ {
+ con_keys = findkeysforcommand("+showscores", 0);
+ keys = tokenize(con_keys);
+ for (i = 0; i < keys; ++i)
+ {
+ if ( nPrimary == stof(argv(i)) )
+ {
+ hud_panel_radar_temp_hidden = bInputType == 0;
+ return false;
+ }
+ }
+ }
+ }
+ else if ( bInputType == 0 )
+ HUD_Radar_Hide_Maximized();
+
+ return false;
+ }
+
+ return true;
+}
+
+void HUD_Radar_Mouse()
+{
+ if ( !hud_panel_radar_mouse ) return;
+ if(mv_active) return;
+
+ if ( intermission )
+ {
+ HUD_Radar_Hide_Maximized();
+ return;
+ }
+
+ if(mouseClicked & S_MOUSE2)
+ {
+ HUD_Radar_Hide_Maximized();
+ 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();
+
+
+ panel_size = autocvar_hud_panel_radar_maximized_size;
+ panel_size_x = bound(0.2, panel_size_x, 1) * vid_conwidth;
+ panel_size_y = bound(0.2, panel_size_y, 1) * vid_conheight;
+ panel_pos_x = (vid_conwidth - panel_size_x) / 2;
+ panel_pos_y = (vid_conheight - panel_size_y) / 2;
+
+ if(mouseClicked & S_MOUSE1)
+ {
+ // click outside
+ if ( mousepos_x < panel_pos_x || mousepos_x > panel_pos_x + panel_size_x ||
+ mousepos_y < panel_pos_y || mousepos_y > panel_pos_y + panel_size_y )
+ {
+ HUD_Radar_Hide_Maximized();
+ return;
+ }
+ vector pos = teamradar_texcoord_to_3dcoord(teamradar_2dcoord_to_texcoord(mousepos),view_origin_z);
+ string thecommand = ((gametype == MAPINFO_TYPE_CONQUEST) ? "cq_spawn" : "ons_spawn");
+ localcmd(sprintf("cmd %s %f %f %f",thecommand,pos_x,pos_y,pos_z));
+
+ HUD_Radar_Hide_Maximized();
+ return;
+ }
+
+
+ vector cursorsize = '32 32 0';
+ drawpic(mousepos-'8 4 0', strcat("gfx/menu/", autocvar_menu_skin, "/cursor.tga"), cursorsize, '1 1 1', 0.8, DRAWFLAG_NORMAL);
+}
+
void HUD_Radar(void)
{
if (!autocvar__hud_configure)
}
}
+ if ( hud_panel_radar_temp_hidden )
+ return;
+
HUD_Panel_UpdateCvars();
float f = 0;
for(tm = world; (tm = find(tm, classname, "radarlink")); )
draw_teamradar_link(tm.origin, tm.velocity, tm.team);
+
+ vector coord;
+ vector brightcolor;
for(tm = world; (tm = findflags(tm, teamradar_icon, 0xFFFFFF)); )
+ {
+ if ( hud_panel_radar_mouse )
+ if ( tm.health > 0 )
+ if ( tm.team == myteam+1 )
+ {
+ coord = teamradar_texcoord_to_2dcoord(teamradar_3dcoord_to_texcoord(tm.origin));
+ if ( vlen(mousepos-coord) < 8 )
+ {
+ brightcolor_x = min(1,tm.teamradar_color_x*1.5);
+ brightcolor_y = min(1,tm.teamradar_color_y*1.5);
+ brightcolor_z = min(1,tm.teamradar_color_z*1.5);
+ drawpic(coord - '8 8 0', "gfx/teamradar_icon_glow", '16 16 0', brightcolor, panel_fg_alpha, 0);
+ }
+ }
+
draw_teamradar_icon(tm.origin, tm.teamradar_icon, tm, tm.teamradar_color, panel_fg_alpha);
+ }
for(tm = world; (tm = find(tm, classname, "entcs_receiver")); )
{
color2 = GetPlayerColor(tm.sv_entnum);
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,
+ message, hud_fontsize, hud_panel_radar_foreground_alpha, DRAWFLAG_NORMAL);
+
+ hud_panel_radar_bottom = pos_y + mySize_y + hud_fontsize_y;
+ }
}
// Score (#7)
float name_size = mySize_x*0.75;
float spacing_size = mySize_x*0.04;
- const float highlight_alpha = 0.2;
+ float highlight_alpha = 0.2;
float i = 0, me_printed = 0, first_pl = 0;
string s;
if (autocvar__hud_configure)
if(!autocvar_hud_panel_racetimer) return;
if(!(gametype == MAPINFO_TYPE_RACE || gametype == MAPINFO_TYPE_CTS)) return;
if(spectatee_status == -1) return;
+ if(getstati(STAT_HEALTH) <= 0)
+ {
+ // reset timers if player died (double check in the case that serverside command fails)
+ race_laptime = 0;
+ race_checkpointtime = 0;
+ return;
+ }
}
HUD_Panel_UpdateCvars();
}
}
+// VIP mod icons
+float redvip_prevframe, bluevip_prevframe, yellowvip_prevframe, pinkvip_prevframe; // status during previous frame
+float redvip_prevstatus, bluevip_prevstatus, yellowvip_prevstatus, pinkvip_prevstatus; // last remembered status
+float redvip_statuschange_time, bluevip_statuschange_time, yellowvip_statuschange_time, pinkvip_statuschange_time; // time when the status changed
+void HUD_Mod_VIP_Reset(void)
+{
+ redvip_prevstatus = bluevip_prevstatus = yellowvip_prevstatus = pinkvip_prevstatus = redvip_prevframe = bluevip_prevframe = yellowvip_prevframe = pinkvip_prevframe = redvip_statuschange_time = bluevip_statuschange_time = yellowvip_statuschange_time = pinkvip_statuschange_time = 0;
+}
+void HUD_Mod_VIP(vector pos, vector mySize)
+{
+ vector redvip_pos, bluevip_pos, yellowvip_pos, pinkvip_pos;
+ vector vip_size;
+ vector e1, e2;
+ float size1, size2;
+ float fs, fs2, fs3;
+ float f; // every function should have that
+
+ float redvip, bluevip, yellowvip, pinkvip, is_vip; // current status
+ float redvip_statuschange_elapsedtime, bluevip_statuschange_elapsedtime, yellowvip_statuschange_elapsedtime, pinkvip_statuschange_elapsedtime; // time since the status changed
+
+ redvip = (getstatf(STAT_VIP_RED) != 0);
+ bluevip = (getstatf(STAT_VIP_BLUE) != 0);
+ yellowvip = (getstatf(STAT_VIP_YELLOW) != 0);
+ pinkvip = (getstatf(STAT_VIP_PINK) != 0);
+ is_vip = (getstati(STAT_VIP) != 0);
+
+ if(redvip || bluevip || yellowvip || pinkvip)
+ mod_active = 1;
+ else
+ mod_active = 0;
+
+ // when status CHANGES, set old status into prevstatus and current status into status
+ if (redvip != redvip_prevframe)
+ {
+ redvip_statuschange_time = time;
+ redvip_prevstatus = redvip_prevframe;
+ redvip_prevframe = redvip;
+ }
+
+ if (bluevip != bluevip_prevframe)
+ {
+ bluevip_statuschange_time = time;
+ bluevip_prevstatus = bluevip_prevframe;
+ bluevip_prevframe = bluevip;
+ }
+
+ if (yellowvip != yellowvip_prevframe)
+ {
+ yellowvip_statuschange_time = time;
+ yellowvip_prevstatus = yellowvip_prevframe;
+ yellowvip_prevframe = yellowvip;
+ }
+
+ if (pinkvip != pinkvip_prevframe)
+ {
+ pinkvip_statuschange_time = time;
+ pinkvip_prevstatus = pinkvip_prevframe;
+ pinkvip_prevframe = pinkvip;
+ }
+
+ redvip_statuschange_elapsedtime = time - redvip_statuschange_time;
+ bluevip_statuschange_elapsedtime = time - bluevip_statuschange_time;
+ yellowvip_statuschange_elapsedtime = time - yellowvip_statuschange_time;
+ pinkvip_statuschange_elapsedtime = time - pinkvip_statuschange_time;
+
+ switch(team_count)
+ {
+ default:
+ case 2: fs = 0.5; fs2 = 0.5; fs3 = 0.5; break;
+ case 3: fs = 1; fs2 = 0.35; fs3 = 0.35; break;
+ case 4: fs = 0.75; fs2 = 0.25; fs3 = 0.5; break;
+ }
+
+ if(mySize_x > mySize_y)
+ {
+ size1 = mySize_x;
+ size2 = mySize_y;
+ e1 = eX;
+ e2 = eY;
+ }
+ else
+ {
+ size1 = mySize_y;
+ size2 = mySize_x;
+ e1 = eY;
+ e2 = eX;
+ }
+
+ switch(myteam)
+ {
+ default:
+ case NUM_TEAM_1:
+ {
+ redvip_pos = pos;
+ bluevip_pos = pos + eX * fs2 * size1;
+ yellowvip_pos = pos - eX * fs2 * size1;
+ pinkvip_pos = pos + eX * fs3 * size1;
+ break;
+ }
+ case NUM_TEAM_2:
+ {
+ redvip_pos = pos + eX * fs2 * size1;
+ bluevip_pos = pos;
+ yellowvip_pos = pos - eX * fs2 * size1;
+ pinkvip_pos = pos + eX * fs3 * size1;
+ break;
+ }
+ case NUM_TEAM_3:
+ {
+ redvip_pos = pos + eX * fs3 * size1;
+ bluevip_pos = pos - eX * fs2 * size1;
+ yellowvip_pos = pos;
+ pinkvip_pos = pos + eX * fs2 * size1;
+ break;
+ }
+ case NUM_TEAM_4:
+ {
+ redvip_pos = pos - eX * fs2 * size1;
+ bluevip_pos = pos + eX * fs3 * size1;
+ yellowvip_pos = pos + eX * fs2 * size1;
+ pinkvip_pos = pos;
+ break;
+ }
+ }
+ vip_size = e1 * fs * size1 + e2 * size2;
+
+ string red_icon = "", red_icon_prevstatus = "";
+ string blue_icon = "", blue_icon_prevstatus = "";
+ string yellow_icon = "", yellow_icon_prevstatus = "";
+ string pink_icon = "", pink_icon_prevstatus = "";
+
+ if(redvip) { red_icon = "player_red"; }
+ if(bluevip) { blue_icon = "player_blue"; }
+ if(yellowvip) { yellow_icon = "player_yellow"; }
+ if(pinkvip) { pink_icon = "player_pink"; }
+
+ if(redvip_prevframe) { red_icon_prevstatus = "player_red"; }
+ if(bluevip_prevframe) { blue_icon_prevstatus = "player_blue"; }
+ if(yellowvip_prevframe) { yellow_icon_prevstatus = "player_yellow"; }
+ if(pinkvip_prevframe) { pink_icon_prevstatus = "player_pink"; }
+
+ if(is_vip)
+ switch(myteam)
+ {
+ case NUM_TEAM_1: red_icon = "notify_balldropped"; break;
+ case NUM_TEAM_2: blue_icon = "notify_balldropped"; break;
+ case NUM_TEAM_3: yellow_icon = "notify_balldropped"; break;
+ case NUM_TEAM_4: pink_icon = "notify_balldropped"; break;
+ }
+
+ f = bound(0, redvip_statuschange_elapsedtime*2, 1);
+ if(red_icon_prevstatus && f < 1)
+ drawpic_aspect_skin_expanding(redvip_pos, red_icon_prevstatus, vip_size, '1 1 1', panel_fg_alpha * 1, DRAWFLAG_NORMAL, f);
+ if(red_icon)
+ drawpic_aspect_skin(redvip_pos, red_icon, vip_size, '1 1 1', panel_fg_alpha * 1 * f, DRAWFLAG_NORMAL);
+
+ f = bound(0, bluevip_statuschange_elapsedtime*2, 1);
+ if(blue_icon_prevstatus && f < 1)
+ drawpic_aspect_skin_expanding(bluevip_pos, blue_icon_prevstatus, vip_size, '1 1 1', panel_fg_alpha * 1, DRAWFLAG_NORMAL, f);
+ if(blue_icon)
+ drawpic_aspect_skin(bluevip_pos, blue_icon, vip_size, '1 1 1', panel_fg_alpha * 1 * f, DRAWFLAG_NORMAL);
+
+ f = bound(0, yellowvip_statuschange_elapsedtime*2, 1);
+ if(yellow_icon_prevstatus && f < 1)
+ drawpic_aspect_skin_expanding(yellowvip_pos, yellow_icon_prevstatus, vip_size, '1 1 1', panel_fg_alpha * 1, DRAWFLAG_NORMAL, f);
+ if(yellow_icon)
+ drawpic_aspect_skin(yellowvip_pos, yellow_icon, vip_size, '1 1 1', panel_fg_alpha * 1 * f, DRAWFLAG_NORMAL);
+
+ f = bound(0, pinkvip_statuschange_elapsedtime*2, 1);
+ if(pink_icon_prevstatus && f < 1)
+ drawpic_aspect_skin_expanding(pinkvip_pos, pink_icon_prevstatus, vip_size, '1 1 1', panel_fg_alpha * 1, DRAWFLAG_NORMAL, f);
+ if(pink_icon)
+ drawpic_aspect_skin(pinkvip_pos, pink_icon, vip_size, '1 1 1', panel_fg_alpha * 1 * f, DRAWFLAG_NORMAL);
+}
+
// CTF HUD modicon section
-float redflag_prevframe, blueflag_prevframe; // status during previous frame
-float redflag_prevstatus, blueflag_prevstatus; // last remembered status
-float redflag_statuschange_time, blueflag_statuschange_time; // time when the status changed
+float redflag_prevframe, blueflag_prevframe, yellowflag_prevframe, pinkflag_prevframe, neutralflag_prevframe; // status during previous frame
+float redflag_prevstatus, blueflag_prevstatus, yellowflag_prevstatus, pinkflag_prevstatus, neutralflag_prevstatus; // last remembered status
+float redflag_statuschange_time, blueflag_statuschange_time, yellowflag_statuschange_time, pinkflag_statuschange_time, neutralflag_statuschange_time; // time when the status changed
void HUD_Mod_CTF_Reset(void)
{
- redflag_prevstatus = blueflag_prevstatus = redflag_prevframe = blueflag_prevframe = redflag_statuschange_time = blueflag_statuschange_time = 0;
+ redflag_prevstatus = blueflag_prevstatus = yellowflag_prevstatus = pinkflag_prevstatus = neutralflag_prevstatus = 0;
+ redflag_prevframe = blueflag_prevframe = yellowflag_prevframe = pinkflag_prevframe = neutralflag_prevframe = 0;
+ redflag_statuschange_time = blueflag_statuschange_time = yellowflag_statuschange_time = pinkflag_statuschange_time = neutralflag_statuschange_time = 0;
}
void HUD_Mod_CTF(vector pos, vector mySize)
{
- vector redflag_pos, blueflag_pos;
+ vector redflag_pos, blueflag_pos, yellowflag_pos, pinkflag_pos, neutralflag_pos;
vector flag_size;
float f; // every function should have that
- float redflag, blueflag; // current status
- float redflag_statuschange_elapsedtime, blueflag_statuschange_elapsedtime; // time since the status changed
- float stat_items;
-
- stat_items = getstati(STAT_ITEMS, 0, 24);
- redflag = (stat_items/IT_RED_FLAG_TAKEN) & 3;
- blueflag = (stat_items/IT_BLUE_FLAG_TAKEN) & 3;
+ float redflag, blueflag, yellowflag, pinkflag, neutralflag; // current status
+ float redflag_statuschange_elapsedtime, blueflag_statuschange_elapsedtime, yellowflag_statuschange_elapsedtime, pinkflag_statuschange_elapsedtime, neutralflag_statuschange_elapsedtime; // time since the status changed
+ float ctf_oneflag; // one-flag CTF mode enabled/disabled
+ float stat_items = getstati(STAT_CTF_FLAGSTATUS, 0, 24);
+ float fs, fs2, fs3, size1, size2;
+ vector e1, e2;
+
+ redflag = (stat_items/CTF_RED_FLAG_TAKEN) & 3;
+ blueflag = (stat_items/CTF_BLUE_FLAG_TAKEN) & 3;
+ yellowflag = (stat_items/CTF_YELLOW_FLAG_TAKEN) & 3;
+ pinkflag = (stat_items/CTF_PINK_FLAG_TAKEN) & 3;
+ neutralflag = (stat_items/CTF_NEUTRAL_FLAG_TAKEN) & 3;
+
+ ctf_oneflag = (stat_items & CTF_FLAG_NEUTRAL);
- if(redflag || blueflag)
+ if(redflag || blueflag || yellowflag || pinkflag || neutralflag)
mod_active = 1;
else
mod_active = 0;
{
redflag = 1;
blueflag = 2;
+ if(team_count >= 3)
+ yellowflag = 2;
+ if(team_count >= 4)
+ pinkflag = 3;
+ ctf_oneflag = neutralflag = 0; // disable neutral flag in hud editor?
}
// when status CHANGES, set old status into prevstatus and current status into status
blueflag_prevframe = blueflag;
}
+ if (yellowflag != yellowflag_prevframe)
+ {
+ yellowflag_statuschange_time = time;
+ yellowflag_prevstatus = yellowflag_prevframe;
+ yellowflag_prevframe = yellowflag;
+ }
+
+ if (pinkflag != pinkflag_prevframe)
+ {
+ pinkflag_statuschange_time = time;
+ pinkflag_prevstatus = pinkflag_prevframe;
+ pinkflag_prevframe = pinkflag;
+ }
+
+ if (neutralflag != neutralflag_prevframe)
+ {
+ neutralflag_statuschange_time = time;
+ neutralflag_prevstatus = neutralflag_prevframe;
+ neutralflag_prevframe = neutralflag;
+ }
+
redflag_statuschange_elapsedtime = time - redflag_statuschange_time;
blueflag_statuschange_elapsedtime = time - blueflag_statuschange_time;
+ yellowflag_statuschange_elapsedtime = time - yellowflag_statuschange_time;
+ pinkflag_statuschange_elapsedtime = time - pinkflag_statuschange_time;
+ neutralflag_statuschange_elapsedtime = time - neutralflag_statuschange_time;
float BLINK_FACTOR = 0.15;
float BLINK_BASE = 0.85;
case 2: red_icon = "flag_red_lost"; break;
case 3: red_icon = "flag_red_carrying"; red_alpha = BLINK_BASE + BLINK_FACTOR * cos(time * BLINK_FREQ); break;
default:
- if((stat_items & IT_CTF_SHIELDED) && (myteam == NUM_TEAM_2))
+ if((stat_items & CTF_SHIELDED) && (myteam != NUM_TEAM_1))
red_icon = "flag_red_shielded";
else
red_icon = string_null;
default:
if(redflag == 3)
red_icon_prevstatus = "flag_red_carrying"; // make it more visible
- else if((stat_items & IT_CTF_SHIELDED) && (myteam == NUM_TEAM_2))
+ else if((stat_items & CTF_SHIELDED) && (myteam != NUM_TEAM_1))
red_icon_prevstatus = "flag_red_shielded";
else
red_icon_prevstatus = string_null;
case 2: blue_icon = "flag_blue_lost"; break;
case 3: blue_icon = "flag_blue_carrying"; blue_alpha = BLINK_BASE + BLINK_FACTOR * cos(time * BLINK_FREQ); break;
default:
- if((stat_items & IT_CTF_SHIELDED) && (myteam == NUM_TEAM_1))
+ if((stat_items & CTF_SHIELDED) && (myteam != NUM_TEAM_2))
blue_icon = "flag_blue_shielded";
else
blue_icon = string_null;
default:
if(blueflag == 3)
blue_icon_prevstatus = "flag_blue_carrying"; // make it more visible
- else if((stat_items & IT_CTF_SHIELDED) && (myteam == NUM_TEAM_1))
+ else if((stat_items & CTF_SHIELDED) && (myteam != NUM_TEAM_2))
blue_icon_prevstatus = "flag_blue_shielded";
else
blue_icon_prevstatus = string_null;
break;
}
- if(mySize_x > mySize_y) {
- if (myteam == NUM_TEAM_1) { // always draw own flag on left
- redflag_pos = pos;
- blueflag_pos = pos + eX * 0.5 * mySize_x;
- } else {
- blueflag_pos = pos;
- redflag_pos = pos + eX * 0.5 * mySize_x;
- }
- flag_size = eX * 0.5 * mySize_x + eY * mySize_y;
- } else {
- if (myteam == NUM_TEAM_1) { // always draw own flag on left
- redflag_pos = pos;
- blueflag_pos = pos + eY * 0.5 * mySize_y;
- } else {
- blueflag_pos = pos;
- redflag_pos = pos + eY * 0.5 * mySize_y;
- }
- flag_size = eY * 0.5 * mySize_y + eX * mySize_x;
+ string yellow_icon, yellow_icon_prevstatus;
+ float yellow_alpha, yellow_alpha_prevstatus;
+ yellow_alpha = yellow_alpha_prevstatus = 1;
+ switch(yellowflag) {
+ case 1: yellow_icon = "flag_yellow_taken"; break;
+ case 2: yellow_icon = "flag_yellow_lost"; break;
+ case 3: yellow_icon = "flag_yellow_carrying"; yellow_alpha = BLINK_BASE + BLINK_FACTOR * cos(time * BLINK_FREQ); break;
+ default:
+ if((stat_items & CTF_SHIELDED) && (myteam != NUM_TEAM_3))
+ yellow_icon = "flag_yellow_shielded";
+ else
+ yellow_icon = string_null;
+ break;
}
+ switch(yellowflag_prevstatus) {
+ case 1: yellow_icon_prevstatus = "flag_yellow_taken"; break;
+ case 2: yellow_icon_prevstatus = "flag_yellow_lost"; break;
+ case 3: yellow_icon_prevstatus = "flag_yellow_carrying"; yellow_alpha_prevstatus = BLINK_BASE + BLINK_FACTOR * cos(time * BLINK_FREQ); break;
+ default:
+ if(yellowflag == 3)
+ yellow_icon_prevstatus = "flag_yellow_carrying"; // make it more visible
+ else if((stat_items & CTF_SHIELDED) && (myteam != NUM_TEAM_3))
+ yellow_icon_prevstatus = "flag_yellow_shielded";
+ else
+ yellow_icon_prevstatus = string_null;
+ break;
+ }
+
+ string pink_icon, pink_icon_prevstatus;
+ float pink_alpha, pink_alpha_prevstatus;
+ pink_alpha = pink_alpha_prevstatus = 1;
+ switch(pinkflag) {
+ case 1: pink_icon = "flag_pink_taken"; break;
+ case 2: pink_icon = "flag_pink_lost"; break;
+ case 3: pink_icon = "flag_pink_carrying"; pink_alpha = BLINK_BASE + BLINK_FACTOR * cos(time * BLINK_FREQ); break;
+ default:
+ if((stat_items & CTF_SHIELDED) && (myteam != NUM_TEAM_4))
+ pink_icon = "flag_pink_shielded";
+ else
+ pink_icon = string_null;
+ break;
+ }
+ switch(pinkflag_prevstatus) {
+ case 1: pink_icon_prevstatus = "flag_pink_taken"; break;
+ case 2: pink_icon_prevstatus = "flag_pink_lost"; break;
+ case 3: pink_icon_prevstatus = "flag_pink_carrying"; pink_alpha_prevstatus = BLINK_BASE + BLINK_FACTOR * cos(time * BLINK_FREQ); break;
+ default:
+ if(pinkflag == 3)
+ pink_icon_prevstatus = "flag_pink_carrying"; // make it more visible
+ else if((stat_items & CTF_SHIELDED) && (myteam != NUM_TEAM_4))
+ pink_icon_prevstatus = "flag_pink_shielded";
+ else
+ pink_icon_prevstatus = string_null;
+ break;
+ }
+
+ string neutral_icon, neutral_icon_prevstatus;
+ float neutral_alpha, neutral_alpha_prevstatus;
+ neutral_alpha = neutral_alpha_prevstatus = 1;
+ switch(neutralflag) {
+ case 1: neutral_icon = "flag_neutral_taken"; break;
+ case 2: neutral_icon = "flag_neutral_lost"; break;
+ case 3: neutral_icon = "flag_neutral_carrying"; neutral_alpha = BLINK_BASE + BLINK_FACTOR * cos(time * BLINK_FREQ); break;
+ default:
+ if((stat_items & CTF_SHIELDED))
+ neutral_icon = "flag_neutral_shielded";
+ else
+ neutral_icon = string_null;
+ break;
+ }
+ switch(neutralflag_prevstatus) {
+ case 1: neutral_icon_prevstatus = "flag_neutral_taken"; break;
+ case 2: neutral_icon_prevstatus = "flag_neutral_lost"; break;
+ case 3: neutral_icon_prevstatus = "flag_neutral_carrying"; neutral_alpha_prevstatus = BLINK_BASE + BLINK_FACTOR * cos(time * BLINK_FREQ); break;
+ default:
+ if(neutralflag == 3)
+ neutral_icon_prevstatus = "flag_neutral_carrying"; // make it more visible
+ else if((stat_items & CTF_SHIELDED))
+ neutral_icon_prevstatus = "flag_neutral_shielded";
+ else
+ neutral_icon_prevstatus = string_null;
+ break;
+ }
+
+ if(ctf_oneflag)
+ {
+ // hacky, but these aren't needed
+ red_icon = red_icon_prevstatus = blue_icon = blue_icon_prevstatus = yellow_icon = yellow_icon_prevstatus = pink_icon = pink_icon_prevstatus = string_null;
+ fs = fs2 = fs3 = 1;
+ }
+ else switch(team_count)
+ {
+ default:
+ case 2: fs = 0.5; fs2 = 0.5; fs3 = 0.5; break;
+ case 3: fs = 1; fs2 = 0.35; fs3 = 0.35; break;
+ case 4: fs = 0.75; fs2 = 0.25; fs3 = 0.5; break;
+ }
+
+ if(mySize_x > mySize_y)
+ {
+ size1 = mySize_x;
+ size2 = mySize_y;
+ e1 = eX;
+ e2 = eY;
+ }
+ else
+ {
+ size1 = mySize_y;
+ size2 = mySize_x;
+ e1 = eY;
+ e2 = eX;
+ }
+
+ switch(myteam)
+ {
+ default:
+ case NUM_TEAM_1:
+ {
+ redflag_pos = pos;
+ blueflag_pos = pos + eX * fs2 * size1;
+ yellowflag_pos = pos - eX * fs2 * size1;
+ pinkflag_pos = pos + eX * fs3 * size1;
+ break;
+ }
+ case NUM_TEAM_2:
+ {
+ redflag_pos = pos + eX * fs2 * size1;
+ blueflag_pos = pos;
+ yellowflag_pos = pos - eX * fs2 * size1;
+ pinkflag_pos = pos + eX * fs3 * size1;
+ break;
+ }
+ case NUM_TEAM_3:
+ {
+ redflag_pos = pos + eX * fs3 * size1;
+ blueflag_pos = pos - eX * fs2 * size1;
+ yellowflag_pos = pos;
+ pinkflag_pos = pos + eX * fs2 * size1;
+ break;
+ }
+ case NUM_TEAM_4:
+ {
+ redflag_pos = pos - eX * fs2 * size1;
+ blueflag_pos = pos + eX * fs3 * size1;
+ yellowflag_pos = pos + eX * fs2 * size1;
+ pinkflag_pos = pos;
+ break;
+ }
+ }
+ neutralflag_pos = pos;
+ flag_size = e1 * fs * size1 + e2 * size2;
+
+ f = bound(0, redflag_statuschange_elapsedtime*2, 1);
+ if(red_icon_prevstatus && f < 1)
+ drawpic_aspect_skin_expanding(redflag_pos, red_icon_prevstatus, flag_size, '1 1 1', panel_fg_alpha * red_alpha_prevstatus, DRAWFLAG_NORMAL, f);
+ if(red_icon)
+ drawpic_aspect_skin(redflag_pos, red_icon, flag_size, '1 1 1', panel_fg_alpha * red_alpha * f, DRAWFLAG_NORMAL);
+
+ f = bound(0, blueflag_statuschange_elapsedtime*2, 1);
+ if(blue_icon_prevstatus && f < 1)
+ drawpic_aspect_skin_expanding(blueflag_pos, blue_icon_prevstatus, flag_size, '1 1 1', panel_fg_alpha * blue_alpha_prevstatus, DRAWFLAG_NORMAL, f);
+ if(blue_icon)
+ drawpic_aspect_skin(blueflag_pos, blue_icon, flag_size, '1 1 1', panel_fg_alpha * blue_alpha * f, DRAWFLAG_NORMAL);
+
+ f = bound(0, yellowflag_statuschange_elapsedtime*2, 1);
+ if(yellow_icon_prevstatus && f < 1)
+ drawpic_aspect_skin_expanding(yellowflag_pos, yellow_icon_prevstatus, flag_size, '1 1 1', panel_fg_alpha * yellow_alpha_prevstatus, DRAWFLAG_NORMAL, f);
+ if(yellow_icon)
+ drawpic_aspect_skin(yellowflag_pos, yellow_icon, flag_size, '1 1 1', panel_fg_alpha * yellow_alpha * f, DRAWFLAG_NORMAL);
+
+ f = bound(0, pinkflag_statuschange_elapsedtime*2, 1);
+ if(pink_icon_prevstatus && f < 1)
+ drawpic_aspect_skin_expanding(pinkflag_pos, pink_icon_prevstatus, flag_size, '1 1 1', panel_fg_alpha * pink_alpha_prevstatus, DRAWFLAG_NORMAL, f);
+ if(pink_icon)
+ drawpic_aspect_skin(pinkflag_pos, pink_icon, flag_size, '1 1 1', panel_fg_alpha * pink_alpha * f, DRAWFLAG_NORMAL);
+
+ f = bound(0, neutralflag_statuschange_elapsedtime*2, 1);
+ if(neutral_icon_prevstatus && f < 1)
+ drawpic_aspect_skin_expanding(neutralflag_pos, neutral_icon_prevstatus, flag_size, '1 1 1', panel_fg_alpha * neutral_alpha_prevstatus, DRAWFLAG_NORMAL, f);
+ if(neutral_icon)
+ drawpic_aspect_skin(neutralflag_pos, neutral_icon, flag_size, '1 1 1', panel_fg_alpha * neutral_alpha * f, DRAWFLAG_NORMAL);
+}
+
+// Keyhunt HUD modicon section
+vector KH_SLOTS[4];
- f = bound(0, redflag_statuschange_elapsedtime*2, 1);
- if(red_icon_prevstatus && f < 1)
- drawpic_aspect_skin_expanding(redflag_pos, red_icon_prevstatus, flag_size, '1 1 1', panel_fg_alpha * red_alpha_prevstatus, DRAWFLAG_NORMAL, f);
- if(red_icon)
- drawpic_aspect_skin(redflag_pos, red_icon, flag_size, '1 1 1', panel_fg_alpha * red_alpha * f, DRAWFLAG_NORMAL);
-
- f = bound(0, blueflag_statuschange_elapsedtime*2, 1);
- if(blue_icon_prevstatus && f < 1)
- drawpic_aspect_skin_expanding(blueflag_pos, blue_icon_prevstatus, flag_size, '1 1 1', panel_fg_alpha * blue_alpha_prevstatus, DRAWFLAG_NORMAL, f);
- if(blue_icon)
- drawpic_aspect_skin(blueflag_pos, blue_icon, flag_size, '1 1 1', panel_fg_alpha * blue_alpha * f, DRAWFLAG_NORMAL);
-}
-
-// Keyhunt HUD modicon section
-vector KH_SLOTS[4];
-
-void HUD_Mod_KH(vector pos, vector mySize)
-{
- mod_active = 1; // keyhunt should never hide the mod icons panel
-
- // Read current state
-
- float state = getstati(STAT_KH_KEYS);
+void HUD_Mod_KH(vector pos, vector mySize)
+{
+ // Read current state
+
+ float state = getstati(STAT_KH_KEYSTATUS);
float i, key_state;
float all_keys, team1_keys, team2_keys, team3_keys, team4_keys, dropped_keys, carrying_keys;
all_keys = team1_keys = team2_keys = team3_keys = team4_keys = dropped_keys = carrying_keys = 0;
+ if(state)
+ mod_active = 1;
+ else
+ mod_active = 0;
+
for(i = 0; i < 4; ++i)
{
key_state = (bitshift(state, i * -5) & 31) - 1;
case MAPINFO_TYPE_CTS:
case MAPINFO_TYPE_RACE: HUD_ModIcons_GameType = HUD_Mod_Race; break;
case MAPINFO_TYPE_CA:
+ case MAPINFO_TYPE_JAILBREAK:
case MAPINFO_TYPE_FREEZETAG: HUD_ModIcons_GameType = HUD_Mod_CA; break;
case MAPINFO_TYPE_DOMINATION: HUD_ModIcons_GameType = HUD_Mod_Dom; break;
case MAPINFO_TYPE_KEEPAWAY: HUD_ModIcons_GameType = HUD_Mod_Keepaway; break;
+ case MAPINFO_TYPE_VIP: HUD_ModIcons_GameType = HUD_Mod_VIP; break;
}
}
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');
+ drawcolorcodedstring(dmpos + offset, resultstr, '24 24 0', dmalpha * 0.8, DRAWFLAG_ADDITIVE);
+ }
+ }
+
+ draw_endBoldFont();
+}
+
// Draw pressed keys (#11)
//
void HUD_PressedKeys(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)
if(spectatee_status == -1)
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)
+ }
+ }
+
string blinkcolor;
if(time % 1 >= 0.5)
blinkcolor = "^1";
}
HUD_Panel_UpdateCvars();
- if(scoreboard_fade_alpha)
+ if ( HUD_Radar_Clickable() )
+ {
+ if (hud_panel_radar_bottom >= 0.96 * vid_conheight)
+ return;
+
+ panel_pos = eY * hud_panel_radar_bottom + eX * 0.5 * (vid_conwidth - panel_size_x);
+ panel_size_y = min(panel_size_y, vid_conheight - hud_panel_radar_bottom);
+ }
+ else if(scoreboard_fade_alpha)
{
hud_fade_alpha = hud_fade_alpha_save;
}
}
+// 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_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;
+ 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()
+
+ 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;
+ }
+ }
+
+ 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)
}
+// Minigame
+//
+#include "../common/minigames/cl_minigames_hud.qc"
+
/*
==================
Main HUD system
==================
*/
+float HUD_Panel_CheckFlags(float showflags)
+{
+ if ( HUD_Minigame_Showpanels() )
+ return showflags & PANEL_SHOW_MINIGAME;
+ return showflags & PANEL_SHOW_MAINGAME;
+}
+
+void HUD_Panel_Draw(entity panent)
+{
+ panel = panent;
+ if ( HUD_Panel_CheckFlags(panel.panel_showflags) )
+ panel.panel_draw();
+}
+
void HUD_Reset (void)
{
// reset gametype specific icons
- if(gametype == MAPINFO_TYPE_CTF)
- HUD_Mod_CTF_Reset();
+ switch(gametype)
+ {
+ case MAPINFO_TYPE_CTF: HUD_Mod_CTF_Reset(); break;
+ case MAPINFO_TYPE_VIP: HUD_Mod_VIP_Reset(); break;
+ }
}
void HUD_Main (void)
if(intermission == 2) // no hud during mapvote
hud_fade_alpha = 0;
+ if ( getstati(STAT_DISCO_MODE) )
+ HUD_DiscoMode();
+
// panels that we want to be active together with the scoreboard
// they must fade only when the menu does
if(scoreboard_fade_alpha == 1)
{
- (panel = HUD_PANEL(CENTERPRINT)).panel_draw();
+ HUD_Panel_Draw(HUD_PANEL(CENTERPRINT));
return;
}
if(!autocvar__hud_configure && !hud_fade_alpha)
+ {
+ hud_fade_alpha = 1;
+ HUD_Panel_Draw(HUD_PANEL(VOTE));
+ hud_fade_alpha = 0;
return;
+ }
// Drawing stuff
if (hud_skin_prev != autocvar_hud_skin)
hud_draw_maximized = 0;
// draw panels in order specified by panel_order array
for(i = HUD_PANEL_NUM - 1; i >= 0; --i)
- (panel = hud_panel[panel_order[i]]).panel_draw();
+ HUD_Panel_Draw(hud_panel[panel_order[i]]);
hud_draw_maximized = 1; // panels that may be maximized must check this var
// draw maximized panels on top
if(hud_panel_radar_maximized)
- (panel = HUD_PANEL(RADAR)).panel_draw();
+ HUD_Panel_Draw(HUD_PANEL(RADAR));
if(autocvar__con_chat_maximized)
- (panel = HUD_PANEL(CHAT)).panel_draw();
+ HUD_Panel_Draw(HUD_PANEL(CHAT));
+ if(hud_panel_quickmenu)
+ HUD_Panel_Draw(HUD_PANEL(QUICKMENU));
HUD_Configure_PostDraw();
float hud_draw_maximized;
float hud_panel_radar_maximized;
+float hud_panel_radar_mouse;
+float hud_panel_radar_bottom;
+float hud_panel_radar_temp_hidden;
+float hud_panel_quickmenu;
float chat_panel_modified;
float radar_panel_modified;
+void HUD_Radar_Hide_Maximized();
+
vector mousepos;
vector panel_click_distance; // mouse cursor distance from the top left corner of the panel (saved only upon a click)
vector panel_click_resizeorigin; // coordinates for opposite point when resizing
float current_player;
+float mv_active;
+
+.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) \
- 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) \
float HUD_PANEL_##NAME; \
void draw_func(void); \
void RegisterHUD_Panel_##NAME() \
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; \
} \
ACCUMULATE_FUNCTION(RegisterHUD_Panels, RegisterHUD_Panel_##NAME);
HUD_Write("\n");
HUD_Write_Cvar_q("hud_progressbar_alpha");
- HUD_Write_Cvar_q("hud_progressbar_strength_color");
- HUD_Write_Cvar_q("hud_progressbar_shield_color");
HUD_Write_Cvar_q("hud_progressbar_health_color");
HUD_Write_Cvar_q("hud_progressbar_armor_color");
HUD_Write_Cvar_q("hud_progressbar_fuel_color");
HUD_Write_PanelCvar_q("_iconalign");
HUD_Write_PanelCvar_q("_baralign");
HUD_Write_PanelCvar_q("_progressbar");
- HUD_Write_PanelCvar_q("_progressbar_strength");
- HUD_Write_PanelCvar_q("_progressbar_shield");
HUD_Write_PanelCvar_q("_text");
break;
case HUD_PANEL_HEALTHARMOR:
HUD_Write_PanelCvar_q("_fade_subsequent_minfontsize");
HUD_Write_PanelCvar_q("_fade_minfontsize");
break;
+ case HUD_PANEL_QUICKMENU:
+ HUD_Write_PanelCvar_q("_align");
+ break;
}
HUD_Write("\n");
}
j += 1;
panel = hud_panel[i];
+ if ( ! HUD_Panel_CheckFlags(panel.panel_showflags) )
+ continue;
+
HUD_Panel_UpdatePosSize()
border = max(8, panel_bg_border); // FORCED border so a small border size doesn't mean you can't resize
j += 1;
panel = hud_panel[i];
+ if ( ! HUD_Panel_CheckFlags(panel.panel_showflags) )
+ continue;
+
HUD_Panel_UpdatePosSize()
border = max(8, panel_bg_border); // FORCED border so a small border size doesn't mean you can't resize
float button_zoom;
float spectatorbutton_zoom;
+float button_attack;
float button_attack2;
float activeweapon;
float hud;
float view_quality;
float framecount;
+
+float vaporizer_delay;
+
+float fovlock;
+
+float cl_fps;
+float sv_showfps;
+
+string sv_announcer;
+
+float num_spectators;
+#define MAX_SPECTATORS 7
+float spectatorlist[MAX_SPECTATORS];
+
+float camera_drawviewmodel_locked;
+float camera_drawviewmodel_backup;
float mv_num_maps;
-float mv_active;
string mv_maps[MAPVOTE_COUNT];
string mv_pics[MAPVOTE_COUNT];
string mv_pk3[MAPVOTE_COUNT];
pos += autocvar_scoreboard_border_thickness * '1 1 0';
img_size -= (autocvar_scoreboard_border_thickness * 2) * '1 1 0';
+
+#ifdef XMAS
+ string xpic = "gfx/menu/default/xmaspic.tga";
+#endif
+
if(pic == "")
{
drawfill(pos, img_size, '.5 .5 .5', .7 * theAlpha, DRAWFLAG_NORMAL);
else
{
if(drawgetimagesize(pic) == '0 0 0')
+#ifdef APRILFOOLS
+ drawpic_rotated(pos, draw_UseSkinFor("nopreview_map"), img_size, '1 1 1', M_PI, theAlpha, DRAWFLAG_NORMAL);
+#else
drawpic(pos, draw_UseSkinFor("nopreview_map"), img_size, '1 1 1', theAlpha, DRAWFLAG_NORMAL);
+#endif
else
+#ifdef APRILFOOLS
+ drawpic_rotated(pos, pic, img_size, '1 1 1', M_PI, theAlpha, DRAWFLAG_NORMAL);
+#else
drawpic(pos, pic, img_size, '1 1 1', theAlpha, DRAWFLAG_NORMAL);
+#endif
+#ifdef XMAS
+ if(drawgetimagesize(xpic) != '0 0 0')
+ drawpic(pos, xpic, img_size, '1 1 1', theAlpha, DRAWFLAG_NORMAL);
+#endif
}
if(id == mv_ownvote)
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)
{
charge = sqrt(charge); // divide evenly among trail spacing and alpha
particles_alphamin = particles_alphamax = particles_fade = charge;
- if (autocvar_cl_particles_oldvortexbeam && (getstati(STAT_ALLOW_OLDVORTEXBEAM) || isdemo()))
- WarpZone_TrailParticles_WithMultiplier(world, particleeffectnum("TE_TEI_G3"), shotorg, endpos, 1, PARTICLES_USEALPHA | PARTICLES_USEFADE);
+ string s = ((charge >= 0.95 && particleeffectnum("nex_beam_charged") >= 0) ? "nex_beam_charged" : "nex_beam");
+
+ if(autocvar_cl_particles_newvortexbeam && (getstati(STAT_ALLOW_OLDVORTEXBEAM) || isdemo()))
+ WarpZone_TrailParticles_WithMultiplier(world, particleeffectnum("nex_beam_new"), shotorg, endpos, 1, PARTICLES_USEALPHA | PARTICLES_USEFADE);
+ else if(autocvar_cl_particles_oldvortexbeam && (getstati(STAT_ALLOW_OLDVORTEXBEAM) || isdemo()))
+ WarpZone_TrailParticles_WithMultiplier(world, particleeffectnum("TE_TEI_G3NEUTRAL"), shotorg, endpos, 1, PARTICLES_USEALPHA | PARTICLES_USEFADE);
else
- WarpZone_TrailParticles_WithMultiplier(world, particleeffectnum("nex_beam"), shotorg, endpos, 1, PARTICLES_USEALPHA | PARTICLES_USEFADE);
+ WarpZone_TrailParticles_WithMultiplier(world, particleeffectnum(s), shotorg, endpos, 1, PARTICLES_USEALPHA | PARTICLES_USEFADE);
}
return;
e.bone_upperbody = 0;
e.bone_weapon = gettagindex(e, "weapon");
+ e.bone_hat = gettagindex(e, "head");
+ if(!e.bone_hat)
+ e.bone_hat = gettagindex(e, "tag_head");
+ if(!e.bone_hat)
+ e.bone_hat = gettagindex(e, "bip01 head");
if(!e.bone_weapon)
e.bone_weapon = gettagindex(e, "tag_weapon");
if(!e.bone_weapon)
e.fixbone = get_model_parameters_fixbone;
if(get_model_parameters_bone_weapon)
e.bone_weapon = gettagindex(e, get_model_parameters_bone_weapon);
+ if(get_model_parameters_bone_head)
+ e.bone_hat = gettagindex(e, get_model_parameters_bone_head);
for(i = 0; i < MAX_AIM_BONES; ++i)
{
if(get_model_parameters_bone_aim[i])
}
else
dprint("No model parameters for ", e.model, "\n");
- dprint(e.model, " uses ", ftos(e.bone_upperbody), " ", ftos(e.fixbone), "\n");
+ //dprint(e.model, " uses ", ftos(e.bone_upperbody), " ", ftos(e.fixbone), "\n");
get_model_parameters(string_null, 0);
e.skeleton_info_modelindex = e.modelindex;
e.skeleton_info_skin = e.skin;
skel_set_boneabs(s, e.(bone_aim[i]), org);
}
}
+
+ e.angles_x = e.angles_z = 0; // bad hax, fix later
}
}
.float bone_aim[MAX_AIM_BONES];
.float bone_aimweight[MAX_AIM_BONES];
.float fixbone;
+.float bone_hat;
+.vector hat_height;
+.float hat_scale;
../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
-../common/monsters/monsters.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
prandom.qh
bgmscript.qh
noise.qh
-tturrets.qh
-../server/tturrets/include/turrets_early.qh
../server/movelib.qc
+generator.qh
+controlpoint.qh
main.qh
-vehicles/vehicles.qh
+../common/vehicles/vehicles_include.qh
../common/csqcmodel_settings.qh
../csqcmodellib/common.qh
../csqcmodellib/cl_model.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
tuba.qc
target_music.qc
-vehicles/vehicles.qc
-../server/vehicles/bumblebee.qc
+../common/vehicles/vehicles_include.qc
shownames.qh
shownames.qc
+conquest.qc
announcer.qc
Main.qc
View.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/monsters/monsters.qc
+../common/turrets/cl_turrets.qc
+../common/turrets/turrets.qc
+
../common/nades.qc
../common/buffs.qc
../warpzonelib/mathlib.qc
../warpzonelib/common.qc
../warpzonelib/client.qc
-tturrets.qc
+
+generator.qc
+controlpoint.qc
player_skeleton.qc
../common/animdecide.qc
+
+
+../common/minigames/minigames.qc
+../common/minigames/cl_minigames.qc
case "drops": return CTX(_("SCO^drops"));
case "faults": return CTX(_("SCO^faults"));
case "fckills": return CTX(_("SCO^fckills"));
+ case "kckills": return CTX(_("SCO^kckills"));
case "goals": return CTX(_("SCO^goals"));
case "kckills": return CTX(_("SCO^kckills"));
case "kdratio": return CTX(_("SCO^kdratio"));
case "suicides": return CTX(_("SCO^suicides"));
case "takes": return CTX(_("SCO^takes"));
case "ticks": return CTX(_("SCO^ticks"));
+ case "liberated": return CTX(_("SCO^liberated"));
+ case "captured": return CTX(_("SCO^captured"));
default: return l;
}
}
"or in all but these game types. You can also specify 'all' as a\n"
"field to show all fields available for the current game mode.\n\n"));
+ print(_("You can also put a ~ sign before them to make the field show up\n"
+ "only if scoreboard_extras is enabled.\n\n"));
+
print(_("The special game type names 'teams' and 'noteams' can be used to\n"
"include/exclude ALL teams/noteams game modes.\n\n"));
}
#define HUD_DefaultColumnLayout() \
-"ping pl name | " \
-"-teams,race,lms/kills +ft,tdm/kills -teams,lms/deaths +ft,tdm/deaths -teams,lms,race,ka/suicides +ft,tdm/suicides -race,dm,tdm,ka,ft/frags " /* tdm already has this in "score" */ \
-"+ctf/caps +ctf/pickups +ctf/fckills +ctf/returns " \
+"ping pl ~fps name | " \
+"-teams,rc,lms/kills +ft,tdm/kills -teams,lms/deaths +ft,tdm/deaths -teams,lms,rc,ka/suicides +ft,tdm/suicides -rc,dm,tdm,ka,ft/frags " /* tdm already has this in "score" */ \
+"~+ctf/deaths ~+ctf/suicides +ctf/caps +ctf/pickups +ctf/fckills +ctf/returns " \
+"+cq/captured +cq/liberated " \
+"+ons/caps +ons/takes " \
+"+inf/survivals " \
"+lms/lives +lms/rank " \
-"+kh/caps +kh/pushes +kh/destroyed " \
-"?+race/laps ?+race/time ?+race/fastest " \
+"~+kh/deaths ~+kh/suicides +kh/caps +kh/pushes +kh/destroyed " \
+"+vip/survivals +vip/vipkills +dom/caps " \
+"?+rc/laps ?+rc/time ?+rc/fastest " \
"+as/objectives +nb/faults +nb/goals +ka/pickups +ka/bckills +ka/bctime +ft/revivals " \
-"-lms,race,nb/score"
+"-lms,rc,nb/score"
void Cmd_HUD_SetFields(float argc)
{
str = substring(str, 1, strlen(str) - 1);
}
+ if(substring(str, 0, 1) == "~")
+ {
+ nocomplain = TRUE;
+
+ if(autocvar_scoreboard_extras)
+ str = substring(str, 1, strlen(str) - 1);
+ else
+ {
+ str = "";
+ continue;
+ }
+ }
+
slash = strstrofs(str, "/", 0);
if(slash >= 0)
{
hud_field[hud_num_fields] = SP_PING;
} else if(str == "pl") {
hud_field[hud_num_fields] = SP_PL;
+ } else if(str == "fps") {
+ hud_field[hud_num_fields] = SP_FPS;
} else if(str == "kd" || str == "kdr" || str == "kdratio" || str == "k/d") {
hud_field[hud_num_fields] = SP_KDRATIO;
} else if(str == "sum" || str == "diff" || str == "k-d") {
hud_field_rgb = '1 0.5 0.5' - '0 0.5 0.5'*tmp;
return str;
+ case SP_FPS:
+ if (!pl.gotscores)
+ return _("N/A");
+
+ tmp = pl.(scores[field]);
+ if(tmp == 0)
+ return "";
+
+ hud_field_rgb = '1 0 0' + '0 1 1' * (bound(0, tmp, 60) / 60);
+ return ftos(tmp);
+
case SP_NAME:
if(ready_waiting && pl.ready)
{
float HUD_WouldDrawScoreboard() {
if (autocvar__hud_configure)
return 0;
+ else if (HUD_QuickMenu_IsOpened())
+ return 0;
+ else if (HUD_Radar_Clickable())
+ return 0;
else if (scoreboard_showscores)
return 1;
else if (intermission == 1)
return 1;
else if (intermission == 2)
return 0;
- else if (spectatee_status != -1 && getstati(STAT_HEALTH) <= 0 && autocvar_cl_deathscoreboard && gametype != MAPINFO_TYPE_CTS)
+ else if (spectatee_status != -1 && getstati(STAT_HEALTH) <= 0 && autocvar_cl_deathscoreboard && gametype != MAPINFO_TYPE_CTS && !active_minigame)
return 1;
else if (scoreboard_showscores_force)
return 1;
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;
- float 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;
+ 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;
float height = 40;
float fontsize = height * 1/3;
float weapon_height = height * 2/3;
if(switchweapon == WEP_VAPORIZER)
g_instagib = 1; // TODO: real detection for instagib?
- float weapon_stats;
if(autocvar_scoreboard_accuracy_nocolors)
rgb = '1 1 1';
else
self = get_weaponinfo(i);
if (!self.weapon)
continue;
- if ((i == WEP_VORTEX && g_instagib) || i == WEP_PORTO || (i == WEP_VAPORIZER && !g_instagib) || i == WEP_TUBA) // skip port-o-launch, vortex || vaporizer and tuba
- continue;
weapon_stats = weapon_accuracy[i-WEP_FIRST];
+ if(weapon_stats < 0 && !(weapons_stat & WepSet_FromWeapon(i) || weapons_inmap & WepSet_FromWeapon(i)))
+ continue;
+
float weapon_alpha;
if(weapon_stats >= 0)
weapon_alpha = scoreboard_alpha_fg;
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;
.float maxdistance;
.float hideflags;
.float spawntime;
-.float health;
.float build_started;
.float build_starthealth;
.float build_finished;
{
switch(s)
{
- case "ons-cp-atck-neut": return 2;
- case "ons-cp-atck-red": return 2;
- case "ons-cp-atck-blue": return 2;
- case "ons-cp-dfnd-red": return 0.5;
- case "ons-cp-dfnd-blue": return 0.5;
+ case "ons-cp-atck": return 2;
+ case "ons-cp-dfnd": return 0.5;
case "item-invis": return 2;
case "item-extralife": return 2;
case "item-speed": return 2;
- case "item-strength": return 2;
- case "item-shield": return 2;
case "item-fuelregen": return 2;
case "item-jetpack": return 2;
case "tagged-target": return 2;
case "enemyflagcarrier": return _("Enemy carrier");
case "flagcarrier": return _("Flag carrier");
case "flagdropped": return _("Dropped flag");
+ case "keycarrier": return _("Key carrier");
+ case "keydropped": return _("Dropped key");
case "helpme": return _("Help me!");
case "here": return _("Here");
case "key-dropped": return _("Dropped key");
case "keycarrier-red": return _("Key carrier");
case "keycarrier-yellow": return _("Key carrier");
case "redbase": return _("Red base");
+ case "yellowbase": return _("Yellow base");
+ case "neutralbase": return _("White base");
+ case "pinkbase": return _("Pink base");
case "waypoint": return _("Waypoint");
- case "ons-gen-red": return _("Generator");
- case "ons-gen-blue": return _("Generator");
+ case "ons-gen": return _("Generator");
case "ons-gen-shielded": return _("Generator");
- case "ons-cp-neut": return _("Control point");
- case "ons-cp-red": return _("Control point");
- case "ons-cp-blue": return _("Control point");
- case "ons-cp-atck-neut": return _("Control point");
- case "ons-cp-atck-red": return _("Control point");
- case "ons-cp-atck-blue": return _("Control point");
- case "ons-cp-dfnd-red": return _("Control point");
- case "ons-cp-dfnd-blue": return _("Control point");
+ case "ons-cp": return _("Control point");
+ case "ons-cp-atck": return _("Control point");
+ case "ons-cp-dfnd": return _("Control point");
case "race-checkpoint": return _("Checkpoint");
case "race-finish": return _("Finish");
case "race-start": return _("Start");
case "item-invis": return _("Invisibility");
case "item-extralife": return _("Extra life");
case "item-speed": return _("Speed");
- case "item-strength": return _("Strength");
- case "item-shield": return _("Shield");
case "item-fuelregen": return _("Fuel regen");
case "item-jetpack": return _("Jet Pack");
case "frozen": return _("Frozen!");
+ case "enemy": return _("Enemy");
case "tagged-target": return _("Tagged");
case "vehicle": return _("Vehicle");
+ case "survivals": return _("Survivals");
+ case "vipkills": return _("VIP kills");
+ case "vip": return _("VIP");
+ case "intruder": return _("Intruder!");
default: return s;
}
}
// they are drawn using a .draw function
void Ent_WaypointSprite();
void Ent_RemoveWaypointSprite();
+
+
+.float health;
if(f & 2)
{
+ string rm_suffix = strcat("rocketminsta_laser_", Static_Team_ColorName_Lower(self.team));
+ if(particleeffectnum(rm_suffix) < 0 || Team_TeamToNumber(self.team) == -1) { rm_suffix = "TR_NEXUIZPLASMA"; }
+
self.cnt = ReadByte();
self.silent = (self.cnt & 0x80);
self.traileffect = 0;
switch(self.cnt)
{
- case PROJECTILE_ELECTRO: setmodel(self, "models/ebomb.mdl");self.traileffect = particleeffectnum("TR_NEXUIZPLASMA"); break;
+ case PROJECTILE_ELECTRO: setmodel(self, "models/ebomb.mdl");self.traileffect = particleeffectnum(rm_suffix); break;
case PROJECTILE_ROCKET: setmodel(self, "models/rocket.md3");self.traileffect = particleeffectnum("TR_ROCKET"); self.scale = 2; break;
+ case PROJECTILE_SUPERROCKET: setmodel(self, "models/rocket.md3");self.traileffect = particleeffectnum("TR_ROCKET"); self.scale = 15; break;
+ case PROJECTILE_ROCKETMINSTA_LASER: setmodel(self, "models/elaser.mdl");self.traileffect = particleeffectnum(rm_suffix); break;
case PROJECTILE_CRYLINK: setmodel(self, "models/plasmatrail.mdl");self.traileffect = particleeffectnum("TR_CRYLINKPLASMA"); break;
case PROJECTILE_CRYLINK_BOUNCING: setmodel(self, "models/plasmatrail.mdl");self.traileffect = particleeffectnum("TR_CRYLINKPLASMA"); break;
- case PROJECTILE_ELECTRO_BEAM: setmodel(self, "models/elaser.mdl");self.traileffect = particleeffectnum("TR_NEXUIZPLASMA"); break;
+ case PROJECTILE_ELECTRO_BEAM: setmodel(self, "models/elaser.mdl");self.traileffect = particleeffectnum(rm_suffix); break;
case PROJECTILE_GRENADE: setmodel(self, "models/grenademodel.md3");self.traileffect = particleeffectnum("TR_GRENADE"); break;
case PROJECTILE_GRENADE_BOUNCING: setmodel(self, "models/grenademodel.md3");self.traileffect = particleeffectnum("TR_GRENADE"); break;
case PROJECTILE_MINE: setmodel(self, "models/mine.md3");self.traileffect = particleeffectnum("TR_GRENADE"); break;
case PROJECTILE_MAGE_SPIKE: setmodel(self, "models/ebomb.mdl"); self.traileffect = particleeffectnum("TR_VORESPIKE"); break;
case PROJECTILE_SHAMBLER_LIGHTNING: setmodel(self, "models/ebomb.mdl"); self.traileffect = particleeffectnum("TR_NEXUIZPLASMA"); break;
+ case PROJECTILE_SCRAG_SPIKE: setmodel(self, "models/plasmatrail.mdl"); self.traileffect = particleeffectnum("TR_WIZSPIKE"); break;
case PROJECTILE_RAPTORBOMB: setmodel(self, "models/vehicles/clusterbomb.md3"); self.gravity = 1; self.avelocity = '0 0 180'; self.traileffect = particleeffectnum(""); break;
case PROJECTILE_RAPTORBOMBLET: setmodel(self, "models/vehicles/bomblet.md3"); self.gravity = 1; self.avelocity = '0 0 180'; self.traileffect = particleeffectnum(""); break;
case PROJECTILE_BUMBLE_BEAM: setmodel(self, "models/elaser.mdl");self.traileffect = particleeffectnum("TR_NEXUIZPLASMA"); break;
case PROJECTILE_RPC: setmodel(self, "models/weapons/ok_rocket.md3");self.traileffect = particleeffectnum("TR_ROCKET"); break;
+ case PROJECTILE_CANNONBALL: setmodel(self, "models/sphere/sphere.md3");self.traileffect = particleeffectnum("TR_ROCKET"); break;
default:
if(Nade_IDFromProjectile(self.cnt) != 0) { setmodel(self, "models/weapons/v_ok_grenade.md3");self.traileffect = particleeffectnum(Nade_TrailEffect(self.cnt, self.team)); break; }
self.mins = '-3 -3 -3';
self.maxs = '3 3 3';
break;
+ case PROJECTILE_CANNONBALL:
+ self.colormod = '1 1 1';
+ self.scale = 0.4;
+ loopsound(self, CH_SHOTS_SINGLE, "weapons/fireball_fly2.wav", VOL_BASE, ATTEN_NORM);
+ self.mins = '-3 -3 -3';
+ self.maxs = '3 3 3';
+ break;
+ case PROJECTILE_SUPERROCKET:
+ loopsound(self, CH_SHOTS_SINGLE, "weapons/rocket_fly.wav", VOL_BASE, ATTEN_NORM);
+ self.mins = '-32 -32 -32';
+ self.maxs = '32 32 32';
+ break;
case PROJECTILE_GRENADE:
self.mins = '-3 -3 -3';
self.maxs = '3 3 3';
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);
if(e.anim_state & ANIMSTATE_DEAD1)
#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
// implicit anim states (inferred from velocity, etc.)
#define ANIMIMPLICITSTATE_INAIR 1
return 0;
}
-
float Buff_Skin(float buff_id)
{
entity e;
+// 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;
.float items; // buff ID
.string netname; // buff name
{ \
BUFF_##NAME = BUFF_LAST * 2; \
BUFF_LAST = BUFF_##NAME; \
+ BUFF_ALL |= BUFF_##NAME; \
Buff_Type##sname = spawn(); \
Buff_Type##sname.items = BUFF_##NAME; \
Buff_Type##sname.netname = #sname; \
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);
GENERIC_COMMAND("settemp", GenericCommand_settemp(request, arguments), "Temporarily set a value to a cvar which is restored later") \
GENERIC_COMMAND("settemp_restore", GenericCommand_settemp_restore(request, arguments), "Restore all cvars set by settemp command") \
GENERIC_COMMAND("runtest", GenericCommand_runtest(request, arguments), "Run unit tests") \
+ GENERIC_COMMAND("script", GenericCommand_script(request, arguments, command), "Script interpreter") \
/* nothing */
void GenericCommand_macro_help()
--- /dev/null
+.float script_type;
+.string script_value;
+.entity script_next;
+
+#include "script/string.qh"
+#include "script/ast.qh"
+#include "script/lex.qh"
+#include "script/parse.qh"
+
+// ========================================================================
+// Command
+// ========================================================================
+
+void GenericCommand_script(float request, float argc, string command)
+{
+ switch(request)
+ {
+ case CMD_REQUEST_COMMAND:
+ {
+ if ( argc >= 3 && argv(1) == "file" )
+ {
+ float file = fopen(argv(2),FILE_READ);
+ if ( file < 0 )
+ {
+ dprint("Script: unable to open file ",argv(2),"\n");
+ return;
+ }
+ dprint("Script: executing ",argv(2),"\n");
+
+
+ string source = "";
+ string line;
+ while (( line = fgets(file) ))
+ source = strcat(source,line,"\n");
+ fclose(file);
+ entity ast_root = script_compile(source);
+
+ context_push();
+ context_define_variable(current_context,"argc",ftos(argc-2));
+ float i;
+ for ( i = 2; i < argc; i++ )
+ context_define_variable(current_context,strcat("arg",ftos(i-2)),argv(i));
+ script_evaluate(ast_root);
+ context_pop();
+
+ script_cleanup(ast_root);
+ }
+ else
+ {
+ entity ast_root = script_compile(substring(command, 7, strlen(command)));
+ print(strcat(script_evaluate(ast_root),"\n"));
+ script_cleanup(ast_root);
+ }
+ return;
+ }
+
+ default:
+ case CMD_REQUEST_USAGE:
+ print(strcat("\nUsage:^3 ", GetProgramCommandPrefix(), " script EXPRESSION...\n"));
+ print(strcat("\nUsage:^3 ", GetProgramCommandPrefix(), " script file filename\n"));
+ return;
+ }
+}
\ No newline at end of file
--- /dev/null
+
+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);
--- /dev/null
+
+// ========================================================================
+// Abstract syntax Tree
+// ========================================================================
+
+.entity ast_operand1;
+.entity ast_operand2;
+.entity ast_operand3;
+.entity() ast_evaluate;
+.float ast_lvalue;
+.string ast_lvalue_reference;
+.float ast_max_iterations;
+entity current_context;
+.entity context_variables;
+.entity context_functions;
+
+#ifdef MENUQC
+.entity owner;
+#endif
+
+const float AST_TYPE_VALUE = 0x00;
+const float AST_TYPE_BREAK = 0x01;
+const float AST_TYPE_RETURN = 0x02;
+const float AST_TYPE_CONTINUE = 0x04;
+
+//---------------------------------------------------------------------
+// Global function definitions
+//---------------------------------------------------------------------
+
+#define SCRIPT_FLOAT_FUNCTION(name) \
+ SCRIPT_GLOBAL_FUNCTION(name,script_ftos(name(stof(arg))),1 )
+
+#define SCRIPT_STRING_FUNCTION(name) \
+ SCRIPT_GLOBAL_FUNCTION(name,name(arg),1 )
+
+#define SCRIPT_GLOBAL_FUNCTIONS \
+ SCRIPT_FLOAT_FUNCTION(sin) \
+ SCRIPT_FLOAT_FUNCTION(cos) \
+ SCRIPT_FLOAT_FUNCTION(tan) \
+ SCRIPT_FLOAT_FUNCTION(asin) \
+ SCRIPT_FLOAT_FUNCTION(acos) \
+ SCRIPT_FLOAT_FUNCTION(atan) \
+ SCRIPT_FLOAT_FUNCTION(floor) \
+ SCRIPT_FLOAT_FUNCTION(ceil) \
+ SCRIPT_FLOAT_FUNCTION(sqrt) \
+ SCRIPT_FLOAT_FUNCTION(log) \
+ SCRIPT_STRING_FUNCTION(strtolower) \
+ SCRIPT_STRING_FUNCTION(strtoupper) \
+ SCRIPT_STRING_FUNCTION(strdecolorize) \
+ SCRIPT_GLOBAL_FUNCTION(strlen,script_ftos(strlen(arg)),1 ) \
+ SCRIPT_GLOBAL_FUNCTION(print,(dprint(arg,"\n"),""),1) \
+ SCRIPT_GLOBAL_FUNCTION(localcmd,(localcmd("\n",arg,"\n"),""),1) \
+ SCRIPT_GLOBAL_FUNCTION(random,(arg,script_ftos(random())),0 ) \
+ SCRIPT_GLOBAL_FUNCTION(round,script_ftos(rint(stof(arg))),1 ) \
+ SCRIPT_GLOBAL_FUNCTION(substring,substring(get_arg(1),stof(get_arg(2)),stof(get_arg(3))),3 ) \
+ SCRIPT_GLOBAL_FUNCTION(spawn,script_ftos(num_for_edict(spawn())),0) \
+ SCRIPT_GLOBAL_FUNCTION(remove,(remove(string_to_entity(arg)),""), 1) \
+ SCRIPT_GLOBAL_FUNCTION(edict_get,entity_get(string_to_entity(get_arg(1)),get_arg(2)), 2 ) \
+ SCRIPT_GLOBAL_FUNCTION(edict_set, \
+ script_ftos(entity_set(string_to_entity(get_arg(1)),get_arg(2),get_arg(3))), 3 ) \
+ SCRIPT_GLOBAL_FUNCTION(get,context_get_variable(current_context,arg),1) \
+ SCRIPT_GLOBAL_FUNCTION(rgb_to_hexcolor,rgb_to_hexcolor(stov(arg)),1) \
+ SCRIPT_GLOBAL_FUNCTION(vector,strcat("'",\
+ script_ftos(stof(get_arg(1)))," ",\
+ script_ftos(stof(get_arg(2)))," ",\
+ script_ftos(stof(get_arg(3))),"'" ), 3 ) \
+
+
+#define SCRIPT_GLOBAL_FUNCTION(name,code,nargs) \
+ entity script_global_function_##name () { \
+ return ast_tempvalue(code); \
+ }
+#define arg \
+ context_get_variable(current_context,"arg")
+#define get_arg(n) \
+ context_get_variable(current_context,strcat("arg",#n))
+string context_get_variable(entity context,string varname);
+entity ast_tempvalue(string value);
+SCRIPT_GLOBAL_FUNCTIONS
+#undef get_arg
+#undef arg
+#undef SCRIPT_GLOBAL_FUNCTION
+
+
+entity context_define_function(entity context, string funcname, entity executor, entity param_chain);
+void script_global_functions_init(entity context)
+{
+ entity next_func;
+ entity param;
+ #define SCRIPT_GLOBAL_FUNCTION(name,code,nargs) \
+ next_func = spawn(); \
+ next_func.ast_evaluate = script_global_function_##name; \
+ if ( nargs == 1 ) { \
+ param = spawn(); \
+ param.script_value = "arg"; \
+ } else { \
+ float i; \
+ entity nextparam; \
+ param = world; \
+ for ( i = nargs; i > 0; i-- ) { \
+ nextparam = spawn(); \
+ nextparam.script_value = strcat("arg",ftos(i));\
+ nextparam.script_next = param; \
+ param = nextparam; \
+ } \
+ } \
+ context_define_function(context,#name,next_func,param).classname = "script_global_function";
+ SCRIPT_GLOBAL_FUNCTIONS
+ #undef SCRIPT_GLOBAL_FUNCTION
+}
+
+//---------------------------------------------------------------------
+// Context stack operations
+//---------------------------------------------------------------------
+
+string context_get_variable(entity context,string varname)
+{
+ if ( strlen(varname) > 5 )
+ if ( substring(varname,0,5) == "cvar_" )
+ {
+ varname = substring(varname,5,strlen(varname)-5);
+ if ( cvar_type(varname) & CVAR_TYPEFLAG_EXISTS )
+ return cvar_string(varname);
+ return "";
+ }
+ entity currentvar = context.context_variables;
+ while ( currentvar )
+ {
+ if ( currentvar.netname == varname )
+ return currentvar.script_value;
+ currentvar = currentvar.script_next;
+ }
+ if ( context.owner )
+ return context_get_variable(context.owner,varname);
+ return "";
+}
+float context_set_variable(entity context,string varname, string newvalue)
+{
+ if ( strlen(varname) > 5 )
+ if ( substring(varname,0,5) == "cvar_" )
+ {
+ varname = substring(varname,5,strlen(varname)-5);
+#ifdef MENUQC
+ registercvar(varname, "", 0);
+#else
+ registercvar(varname, "");
+#endif
+ cvar_set(varname,newvalue);
+ return 1;
+ }
+ entity currentvar = context.context_variables;
+ while ( currentvar )
+ {
+ if ( currentvar.netname == varname )
+ {
+ currentvar.script_value = newvalue;
+ return 1;
+ }
+ currentvar = currentvar.script_next;
+ }
+ if ( context.owner )
+ if ( context_set_variable(context.owner,varname,newvalue) )
+ return 1;
+ return 0;
+}
+void context_define_variable(entity context,string varname, string newvalue)
+{
+ entity newvar = spawn();
+ newvar.script_value = newvalue;
+ newvar.netname = varname;
+ newvar.classname = "script_variable";
+ newvar.script_next = context.context_variables;
+ context.context_variables = newvar;
+}
+entity context_get_function(entity context, string funcname)
+{
+ entity currentfunc = context.context_functions;
+ while ( currentfunc )
+ {
+ if ( currentfunc.netname == funcname )
+ {
+ return currentfunc;
+ }
+ currentfunc = currentfunc.script_next;
+ }
+ if ( context.owner )
+ return context_get_function(context.owner,funcname);
+ dprint("Script: called undefined function: ",funcname,"\n");
+ return world;
+}
+entity context_define_function(entity context, string funcname, entity executor, entity param_chain)
+{
+ entity function = spawn();
+ function.netname = funcname;
+ function.ast_operand1 = param_chain;
+ function.ast_operand2 = executor;
+ function.script_next = context.context_functions;
+ function.classname = "script_function";
+ context.context_functions = function;
+ return function;
+}
+void context_push()
+{
+ entity new_context = spawn();
+ new_context.classname = "script_context";
+ new_context.owner = current_context;
+ if ( current_context )
+ {
+ new_context.ast_max_iterations = current_context.ast_max_iterations;
+ }
+ else
+ {
+ new_context.ast_max_iterations = 100; // max recursion depth TODO: cvar
+ script_global_functions_init(new_context);
+ }
+ current_context = new_context;
+}
+void context_pop()
+{
+ if ( current_context )
+ {
+ entity old_context = current_context;
+ current_context = current_context.owner;
+ script_cleanup(old_context.context_variables);
+ entity func = old_context.context_functions;
+ entity func_old;
+ while(func)
+ {
+ func_old = func;
+ func = func.script_next;
+ if ( func_old.classname == "script_global_function" )
+ {
+ func_old.script_next = world;
+ script_cleanup(func_old.ast_operand1);
+ remove(func_old.ast_operand2);
+ }
+ remove(func_old);
+ }
+ old_context.context_functions = world;
+ remove(old_context);
+ }
+}
+
+//---------------------------------------------------------------------
+// Recursive operations
+//---------------------------------------------------------------------
+
+entity ast_tempnull();
+
+// Evaluate a tree and return the resulting entity
+// Use this when you need to forward the result
+entity script_evaluate_entity(entity ast_root)
+{
+ if ( !ast_root )
+ return ast_tempnull();
+ entity oldself = self;
+ self = ast_root;
+ entity result = self.ast_evaluate();
+ self = oldself;
+ return result;
+}
+
+// Evaluate a tree and return the resulting value
+// Use this when you will use just the value for further operations
+string script_evaluate(entity ast_root)
+{
+ entity e = script_evaluate_entity(ast_root);
+ string result = e.script_value;
+ if ( e.classname == "ast_tempvalue" )
+ remove(e);
+ return result;
+}
+
+void script_cleanup(entity ast_root)
+{
+ if ( !ast_root || wasfreed(ast_root) )
+ return;
+ if ( ast_root.ast_operand1 )
+ script_cleanup(ast_root.ast_operand1);
+ if ( ast_root.ast_operand2 )
+ script_cleanup(ast_root.ast_operand2);
+ if ( ast_root.ast_operand3 )
+ script_cleanup(ast_root.ast_operand3);
+ if ( ast_root.script_next )
+ script_cleanup(ast_root.script_next);
+ remove(ast_root);
+}
+
+void script_debug_ast_recursive(entity ast_root,string prefix)
+{
+ dprint(prefix,"^3",ast_root.classname,"^7: ",ast_root.script_value,"\n");
+ if ( ast_root.ast_operand1 )
+ script_debug_ast_recursive(ast_root.ast_operand1,strcat(prefix," op1 "));
+ if ( ast_root.ast_operand2 )
+ script_debug_ast_recursive(ast_root.ast_operand2,strcat(prefix," op2 "));
+ if ( ast_root.ast_operand3 )
+ script_debug_ast_recursive(ast_root.ast_operand3,strcat(prefix," op3 "));
+
+ if ( ast_root.script_next )
+ script_debug_ast_recursive(ast_root.script_next,prefix);
+}
+void script_debug_ast(entity ast_root)
+{
+ if ( cvar("developer") )
+ {
+ if ( !ast_root )
+ dprint("Empty tree\n");
+ else
+ {
+ dprint("(ast begin)\n");
+ script_debug_ast_recursive(ast_root,"^8");
+ dprint("(ast end)\n");
+ }
+ }
+}
+
+//---------------------------------------------------------------------
+// Tree structure
+//---------------------------------------------------------------------
+
+// simple string value
+entity ast_tempvalue_evaluate()
+{
+ return self;
+}
+entity ast_tempvalue(string newvalue)
+{
+ entity ast_temp = spawn();
+ ast_temp.script_value = newvalue;
+ ast_temp.ast_evaluate = ast_tempvalue_evaluate;
+ ast_temp.classname="ast_tempvalue";
+ return ast_temp;
+}
+entity ast_tempnull()
+{
+ return ast_tempvalue("");
+}
+
+entity ast_simplevalue_evaluate()
+{
+ return ast_tempvalue(self.script_value);
+}
+entity ast_simplevalue(string newvalue)
+{
+ entity ast_temp = spawn();
+ ast_temp.script_value = newvalue;
+ ast_temp.ast_evaluate = ast_simplevalue_evaluate;
+ ast_temp.classname="ast_simplevalue";
+ return ast_temp;
+}
+
+// variable reference
+entity ast_variable_evaluate()
+{
+ entity val = ast_tempvalue(context_get_variable(current_context,self.script_value));
+ val.ast_lvalue = self.ast_lvalue;
+ val.ast_lvalue_reference = self.ast_lvalue_reference;
+ return val;
+}
+entity ast_variable(string var_name)
+{
+ entity ast_temp = spawn();
+ ast_temp.script_value = var_name;
+ ast_temp.ast_evaluate = ast_variable_evaluate;
+ ast_temp.classname="ast_variable";
+ ast_temp.ast_lvalue = -1;
+ ast_temp.ast_lvalue_reference = var_name;
+ return ast_temp;
+}
+
+entity ast_field_evaluate()
+{
+ string entname = script_evaluate(self.ast_operand1);
+ entity ent = string_to_entity(entname);
+ if ( ent )
+ {
+ entity val = ast_tempvalue(entity_get(ent,self.ast_lvalue_reference));
+ val.ast_lvalue = stof(entname);
+ val.ast_lvalue_reference = self.ast_lvalue_reference;
+ return val;
+ }
+
+ dprint("Access to invalid entity field: #",entname,".",self.ast_lvalue_reference,"\n");
+ return ast_tempnull();
+}
+
+// entity field access
+entity ast_field(entity operand,string field_name)
+{
+ entity ast_temp = spawn();
+ ast_temp.script_value = field_name;
+ ast_temp.ast_operand1 = operand;
+ ast_temp.ast_evaluate = ast_field_evaluate;
+ ast_temp.classname="ast_field";
+ ast_temp.ast_lvalue = -1;
+ ast_temp.ast_lvalue_reference = field_name;
+ return ast_temp;
+}
+
+// variable assignment
+entity ast_assign_evaluate()
+{
+ entity lval = script_evaluate_entity(self.ast_operand1);
+ entity rval = script_evaluate_entity(self.ast_operand2);
+
+ if ( lval.ast_lvalue < 0 )
+ {
+ if ( !context_set_variable(current_context,lval.ast_lvalue_reference,rval.script_value) )
+ context_define_variable(current_context,lval.ast_lvalue_reference,rval.script_value);
+ }
+ else if ( lval.ast_lvalue > 0 )
+ {
+ if ( !entity_set(float_to_entity(lval.ast_lvalue),lval.ast_lvalue_reference,rval.script_value) )
+ dprint("Assignment to invalid entity field: #",
+ ftos(lval.ast_lvalue),".",lval.ast_lvalue_reference,"\n");
+ }
+ else
+ dprint("Assignment ro rvalue: ",lval.script_value," = ",rval.script_value,"\n");
+ rval.ast_lvalue = lval.ast_lvalue;
+ rval.ast_lvalue_reference = lval.ast_lvalue_reference;
+ remove(lval);
+ return rval;
+}
+entity ast_assign(entity lvalue,entity rvalue)
+{
+ entity ast_temp = spawn();
+ ast_temp.ast_operand1 = lvalue;
+ ast_temp.ast_operand2 = rvalue;
+ ast_temp.ast_evaluate = ast_assign_evaluate;
+ ast_temp.ast_lvalue = -1;
+ ast_temp.classname="ast_assign";
+ return ast_temp;
+}
+entity ast_assign_operator_evaluate()
+{
+ entity lval_tree = self.ast_operand1;
+ self.ast_operand1 = script_evaluate_entity(lval_tree);
+ self.ast_operand1.classname = "ast_assign_operator_lval";
+ self.ast_operand2.ast_operand1 = self.ast_operand1;
+ entity result = ast_assign_evaluate();
+ self.ast_operand2.ast_operand1 = world;
+ self.ast_operand1 = lval_tree;
+ return result;
+}
+entity ast_assign_operator(entity lvalue,entity rvalue,entity(entity,entity) operator)
+{
+ entity ast_temp = spawn();
+ ast_temp.ast_operand1 = lvalue;
+ ast_temp.ast_operand2 = operator(world,rvalue);
+ ast_temp.ast_evaluate = ast_assign_operator_evaluate;
+ ast_temp.ast_lvalue = -1;
+ ast_temp.classname="ast_assign_operator";
+ return ast_temp;
+}
+
+// function call
+entity ast_function_evaluate()
+{
+ entity function = context_get_function(current_context,self.script_value);
+ entity next_arg = self.ast_operand1;
+ entity next_param = function.ast_operand1;
+ if ( function && current_context.ast_max_iterations > 0 )
+ {
+ context_push();
+ current_context.ast_max_iterations -= 1;
+
+ while ( next_arg && next_param)
+ {
+ context_define_variable(current_context,next_param.script_value,script_evaluate(next_arg));
+ next_arg = next_arg.script_next;
+ next_param = next_param.script_next;
+ }
+ while ( next_param )
+ {
+ context_define_variable(current_context,next_param.script_value,
+ script_evaluate(next_param.ast_operand1) );
+ next_param = next_param.script_next;
+ }
+ entity newvalue = script_evaluate_entity(function.ast_operand2);
+ context_pop();
+ newvalue.script_type &= ~AST_TYPE_RETURN;
+ return newvalue;
+ }
+ return ast_tempnull();
+}
+entity ast_function(string newname)
+{
+ entity ast_temp = spawn();
+ ast_temp.script_value = newname;
+ ast_temp.ast_evaluate = ast_function_evaluate;
+ ast_temp.classname="ast_function";
+ return ast_temp;
+}
+
+entity ast_function_declaration_evaluate()
+{
+ context_define_function(current_context,self.script_value,self.ast_operand2,self.ast_operand1);
+ return ast_tempnull();
+}
+entity ast_function_declaration(string newname)
+{
+ entity ast_temp = spawn();
+ ast_temp.script_value = newname;
+ ast_temp.ast_evaluate = ast_function_declaration_evaluate;
+ ast_temp.classname="ast_function_declaration";
+ return ast_temp;
+}
+
+// control structure
+entity ast_if_evaluate()
+{
+ if ( stof(script_evaluate(self.ast_operand1)) )
+ return script_evaluate_entity(self.ast_operand2);
+ return script_evaluate_entity(self.ast_operand3);
+}
+entity ast_if(entity condition, entity branch_true, entity branch_false)
+{
+ entity ast_temp = spawn();
+ ast_temp.ast_operand1 = condition;
+ ast_temp.ast_operand2 = branch_true;
+ ast_temp.ast_operand3 = branch_false;
+ ast_temp.ast_evaluate = ast_if_evaluate;
+ ast_temp.classname="ast_if";
+ return ast_temp;
+}
+
+entity ast_while_evaluate()
+{
+ entity val = world;
+ float it = 0;
+ while ( stof(script_evaluate(self.ast_operand1)) &&
+ it < self.ast_max_iterations )
+ {
+ if ( val )
+ remove(val);
+ val = script_evaluate_entity(self.ast_operand2);
+ if ( val.script_type & (AST_TYPE_BREAK|AST_TYPE_RETURN) )
+ {
+ val.script_type &= ~ AST_TYPE_BREAK;
+ break;
+ }
+ it++;
+ }
+ return val;
+}
+entity ast_while(entity condition, entity body)
+{
+ entity ast_temp = spawn();
+ ast_temp.ast_operand1 = condition;
+ ast_temp.ast_operand2 = body;
+ ast_temp.ast_evaluate = ast_while_evaluate;
+ ast_temp.ast_max_iterations = 1000; // TODO: cvar
+ ast_temp.classname="ast_while";
+ return ast_temp;
+}
+
+entity ast_block_evaluate()
+{
+ entity val = world;
+ entity next = self.ast_operand1;
+ context_push();
+ while ( next )
+ {
+ if ( val )
+ remove(val);
+ val = script_evaluate_entity(next);
+ if ( val.script_type & (AST_TYPE_BREAK|AST_TYPE_RETURN|AST_TYPE_CONTINUE) )
+ {
+ val.script_type &= ~AST_TYPE_CONTINUE;
+ break;
+ }
+ next = next.script_next;
+ }
+ context_pop();
+ return val;
+}
+entity ast_block()
+{
+ entity ast_temp = spawn();
+ ast_temp.ast_evaluate = ast_block_evaluate;
+ ast_temp.classname="ast_block";
+ return ast_temp;
+}
+
+entity ast_break_evaluate()
+{
+ entity val = ast_tempvalue(self.script_value);
+ val.script_type = self.script_type;
+ return val;
+}
+entity ast_break()
+{
+ entity ast_temp = spawn();
+ ast_temp.script_type = AST_TYPE_BREAK;
+ ast_temp.classname="ast_break";
+ ast_temp.ast_evaluate = ast_break_evaluate;
+ return ast_temp;
+}
+entity ast_continue()
+{
+ entity ast_temp = spawn();
+ ast_temp.script_type = AST_TYPE_CONTINUE;
+ ast_temp.classname="ast_continue";
+ ast_temp.ast_evaluate = ast_break_evaluate;
+ return ast_temp;
+}
+
+entity ast_return_evaluate()
+{
+ entity val = script_evaluate_entity(self.ast_operand1);
+ val.script_type = self.script_type;
+ return val;
+}
+entity ast_return(entity newvalue)
+{
+ entity ast_temp = spawn();
+ ast_temp.script_type = AST_TYPE_RETURN;
+ ast_temp.classname="ast_return";
+ ast_temp.ast_evaluate = ast_return_evaluate;
+ ast_temp.ast_operand1 = newvalue;
+ return ast_temp;
+}
+
+
+// Simple unary oprators
+entity ast_unaryminus_evaluate()
+{
+ return ast_tempvalue(script_ftos(-stof( script_evaluate(self.ast_operand1) )));
+}
+entity ast_unaryminus(entity operand)
+{
+ entity ast_temp = spawn();
+ ast_temp.ast_operand1 = operand;
+ ast_temp.ast_evaluate = ast_unaryminus_evaluate;
+ ast_temp.classname="ast_unaryminus";
+ return ast_temp;
+}
+entity ast_bitnot_evaluate()
+{
+ return ast_tempvalue(script_ftos( ~ stof(
+ script_evaluate(self.ast_operand1) )));
+}
+entity ast_bitnot(entity operand)
+{
+ entity ast_temp = spawn();
+ ast_temp.ast_operand1 = operand;
+ ast_temp.ast_evaluate = ast_bitnot_evaluate;
+ ast_temp.classname="ast_bitnot";
+ return ast_temp;
+}
+entity ast_not_evaluate()
+{
+ return ast_tempvalue(script_ftos( ! stof(
+ script_evaluate(self.ast_operand1) )));
+}
+entity ast_not(entity operand)
+{
+ entity ast_temp = spawn();
+ ast_temp.ast_operand1 = operand;
+ ast_temp.ast_evaluate = ast_not_evaluate;
+ ast_temp.classname="ast_not";
+ return ast_temp;
+}
+
+// Simple binary operators
+entity ast_mul_evaluate()
+{
+ return ast_tempvalue(script_ftos(
+ stof(script_evaluate(self.ast_operand1)) *
+ stof(script_evaluate(self.ast_operand2))
+ ));
+}
+entity ast_mul(entity operand1, entity operand2)
+{
+ entity ast_temp = spawn();
+ ast_temp.ast_operand1 = operand1;
+ ast_temp.ast_operand2 = operand2;
+ ast_temp.ast_evaluate = ast_mul_evaluate;
+ ast_temp.classname="ast_mul";
+ return ast_temp;
+}
+entity ast_div_evaluate()
+{
+ return ast_tempvalue(script_ftos(
+ stof(script_evaluate(self.ast_operand1)) /
+ stof(script_evaluate(self.ast_operand2))
+ ));
+}
+entity ast_div(entity operand1, entity operand2)
+{
+ entity ast_temp = spawn();
+ ast_temp.ast_operand1 = operand1;
+ ast_temp.ast_operand2 = operand2;
+ ast_temp.ast_evaluate = ast_div_evaluate;
+ ast_temp.classname="ast_div";
+ return ast_temp;
+}
+entity ast_mod_evaluate()
+{
+ return ast_tempvalue(script_ftos(
+ stof(script_evaluate(self.ast_operand1)) %
+ stof(script_evaluate(self.ast_operand2))
+ ));
+}
+entity ast_mod(entity operand1, entity operand2)
+{
+ entity ast_temp = spawn();
+ ast_temp.ast_operand1 = operand1;
+ ast_temp.ast_operand2 = operand2;
+ ast_temp.ast_evaluate = ast_mod_evaluate;
+ ast_temp.classname="ast_mod";
+ return ast_temp;
+}
+entity ast_sum_evaluate()
+{
+ string op1 = script_evaluate(self.ast_operand1);
+ string op2 = script_evaluate(self.ast_operand2);
+ if ( is_numeric(op1) && is_numeric(op2) )
+ return ast_tempvalue(script_ftos(stof(op1)+stof(op2)));
+ return ast_tempvalue(strcat(op1,op2));
+}
+entity ast_sum(entity operand1, entity operand2)
+{
+ entity ast_temp = spawn();
+ ast_temp.ast_operand1 = operand1;
+ ast_temp.ast_operand2 = operand2;
+ ast_temp.ast_evaluate = ast_sum_evaluate;
+ ast_temp.classname="ast_sum";
+ return ast_temp;
+}
+entity ast_sub_evaluate()
+{
+ return ast_tempvalue(script_ftos(
+ stof(script_evaluate(self.ast_operand1)) -
+ stof(script_evaluate(self.ast_operand2))
+ ));
+}
+entity ast_sub(entity operand1, entity operand2)
+{
+ entity ast_temp = spawn();
+ ast_temp.ast_operand1 = operand1;
+ ast_temp.ast_operand2 = operand2;
+ ast_temp.ast_evaluate = ast_sub_evaluate;
+ ast_temp.classname="ast_sub";
+ return ast_temp;
+}
+entity ast_bitand_evaluate()
+{
+ return ast_tempvalue(script_ftos(
+ stof(script_evaluate(self.ast_operand1)) &
+ stof(script_evaluate(self.ast_operand2))
+ ));
+}
+entity ast_bitand(entity operand1, entity operand2)
+{
+ entity ast_temp = spawn();
+ ast_temp.ast_operand1 = operand1;
+ ast_temp.ast_operand2 = operand2;
+ ast_temp.ast_evaluate = ast_bitand_evaluate;
+ ast_temp.classname="ast_bitand";
+ return ast_temp;
+}
+entity ast_bitor_evaluate()
+{
+ return ast_tempvalue(script_ftos(
+ stof(script_evaluate(self.ast_operand1)) |
+ stof(script_evaluate(self.ast_operand2))
+ ));
+}
+entity ast_bitor(entity operand1, entity operand2)
+{
+ entity ast_temp = spawn();
+ ast_temp.ast_operand1 = operand1;
+ ast_temp.ast_operand2 = operand2;
+ ast_temp.ast_evaluate = ast_bitor_evaluate;
+ ast_temp.classname="ast_bitor";
+ return ast_temp;
+}
+entity ast_bitxor_evaluate()
+{
+ return ast_tempvalue(script_ftos(
+ stof(script_evaluate(self.ast_operand1)) ^
+ stof(script_evaluate(self.ast_operand2))
+ ));
+}
+entity ast_bitxor(entity operand1, entity operand2)
+{
+ entity ast_temp = spawn();
+ ast_temp.ast_operand1 = operand1;
+ ast_temp.ast_operand2 = operand2;
+ ast_temp.ast_evaluate = ast_bitxor_evaluate;
+ ast_temp.classname="ast_bitxor";
+ return ast_temp;
+}
+entity ast_bitlshift_evaluate()
+{
+ return ast_tempvalue(script_ftos(
+ stof(script_evaluate(self.ast_operand1)) <<
+ stof(script_evaluate(self.ast_operand2))
+ ));
+}
+entity ast_bitlshift(entity operand1, entity operand2)
+{
+ entity ast_temp = spawn();
+ ast_temp.ast_operand1 = operand1;
+ ast_temp.ast_operand2 = operand2;
+ ast_temp.ast_evaluate = ast_bitlshift_evaluate;
+ ast_temp.classname="ast_bitlshift";
+ return ast_temp;
+}
+entity ast_bitrshift_evaluate()
+{
+ return ast_tempvalue(script_ftos(
+ stof(script_evaluate(self.ast_operand1)) >>
+ stof(script_evaluate(self.ast_operand2))
+ ));
+}
+entity ast_bitrshift(entity operand1, entity operand2)
+{
+ entity ast_temp = spawn();
+ ast_temp.ast_operand1 = operand1;
+ ast_temp.ast_operand2 = operand2;
+ ast_temp.ast_evaluate = ast_bitrshift_evaluate;
+ ast_temp.classname="ast_bitrshift";
+ return ast_temp;
+}
+entity ast_eq_evaluate()
+{
+ return ast_tempvalue(script_ftos(
+ script_evaluate(self.ast_operand1) ==
+ script_evaluate(self.ast_operand2)
+ ));
+}
+entity ast_eq(entity operand1, entity operand2)
+{
+ entity ast_temp = spawn();
+ ast_temp.ast_operand1 = operand1;
+ ast_temp.ast_operand2 = operand2;
+ ast_temp.ast_evaluate = ast_eq_evaluate;
+ ast_temp.classname="ast_eq";
+ return ast_temp;
+}
+entity ast_ne_evaluate()
+{
+ string op1 = script_evaluate(self.ast_operand1);
+ string op2 = script_evaluate(self.ast_operand2);
+ if ( is_numeric(op1) && is_numeric(op2) )
+ return ast_tempvalue(script_ftos(stof(op1) != stof(op2)));
+ return ast_tempvalue(script_ftos(strcmp(op1,op2)!=0));
+}
+entity ast_ne(entity operand1, entity operand2)
+{
+ entity ast_temp = spawn();
+ ast_temp.ast_operand1 = operand1;
+ ast_temp.ast_operand2 = operand2;
+ ast_temp.ast_evaluate = ast_ne_evaluate;
+ ast_temp.classname="ast_ne";
+ return ast_temp;
+}
+entity ast_lt_evaluate()
+{
+ string op1 = script_evaluate(self.ast_operand1);
+ string op2 = script_evaluate(self.ast_operand2);
+ if ( is_numeric(op1) && is_numeric(op2) )
+ return ast_tempvalue(script_ftos(stof(op1)<stof(op2)));
+ return ast_tempvalue(script_ftos(strcmp(op1,op2)<0));
+}
+entity ast_lt(entity operand1, entity operand2)
+{
+ entity ast_temp = spawn();
+ ast_temp.ast_operand1 = operand1;
+ ast_temp.ast_operand2 = operand2;
+ ast_temp.ast_evaluate = ast_lt_evaluate;
+ ast_temp.classname="ast_lt";
+ return ast_temp;
+}
+entity ast_le_evaluate()
+{
+ string op1 = script_evaluate(self.ast_operand1);
+ string op2 = script_evaluate(self.ast_operand2);
+ if ( is_numeric(op1) && is_numeric(op2) )
+ return ast_tempvalue(script_ftos(stof(op1) <= stof(op2)));
+ return ast_tempvalue(script_ftos(strcmp(op1,op2)<=0));
+}
+entity ast_le(entity operand1, entity operand2)
+{
+ entity ast_temp = spawn();
+ ast_temp.ast_operand1 = operand1;
+ ast_temp.ast_operand2 = operand2;
+ ast_temp.ast_evaluate = ast_le_evaluate;
+ ast_temp.classname="ast_le";
+ return ast_temp;
+}
+entity ast_gt_evaluate()
+{
+ string op1 = script_evaluate(self.ast_operand1);
+ string op2 = script_evaluate(self.ast_operand2);
+ if ( is_numeric(op1) && is_numeric(op2) )
+ return ast_tempvalue(script_ftos(stof(op1)>stof(op2)));
+ return ast_tempvalue(script_ftos(strcmp(op1,op2)>0));
+}
+entity ast_gt(entity operand1, entity operand2)
+{
+ entity ast_temp = spawn();
+ ast_temp.ast_operand1 = operand1;
+ ast_temp.ast_operand2 = operand2;
+ ast_temp.ast_evaluate = ast_gt_evaluate;
+ ast_temp.classname="ast_gt";
+ return ast_temp;
+}
+entity ast_ge_evaluate()
+{
+ string op1 = script_evaluate(self.ast_operand1);
+ string op2 = script_evaluate(self.ast_operand2);
+ if ( is_numeric(op1) && is_numeric(op2) )
+ return ast_tempvalue(script_ftos(stof(op1)>=stof(op2)));
+ return ast_tempvalue(script_ftos(strcmp(op1,op2)>=0));
+}
+entity ast_ge(entity operand1, entity operand2)
+{
+ entity ast_temp = spawn();
+ ast_temp.ast_operand1 = operand1;
+ ast_temp.ast_operand2 = operand2;
+ ast_temp.ast_evaluate = ast_ge_evaluate;
+ ast_temp.classname="ast_ge";
+ return ast_temp;
+}
+entity ast_and_evaluate()
+{
+ return ast_tempvalue(script_ftos(
+ stof(script_evaluate(self.ast_operand1)) &&
+ stof(script_evaluate(self.ast_operand2))
+ ));
+}
+entity ast_and(entity operand1, entity operand2)
+{
+ entity ast_temp = spawn();
+ ast_temp.ast_operand1 = operand1;
+ ast_temp.ast_operand2 = operand2;
+ ast_temp.ast_evaluate = ast_and_evaluate;
+ ast_temp.classname="ast_and";
+ return ast_temp;
+}
+entity ast_or_evaluate()
+{
+ return ast_tempvalue(script_ftos(
+ stof(script_evaluate(self.ast_operand1)) ||
+ stof(script_evaluate(self.ast_operand2))
+ ));
+}
+entity ast_or(entity operand1, entity operand2)
+{
+ entity ast_temp = spawn();
+ ast_temp.ast_operand1 = operand1;
+ ast_temp.ast_operand2 = operand2;
+ ast_temp.ast_evaluate = ast_or_evaluate;
+ ast_temp.classname="ast_or";
+ return ast_temp;
+}
\ No newline at end of file
--- /dev/null
+
+// ========================================================================
+// Lexer
+// ========================================================================
+
+const float LEX_UNKNOWN = 0;
+const float LEX_VALUE_NUMBER = 1;
+const float LEX_VALUE_STRING = 2;
+const float LEX_IDENTIFIER = 3;
+const float LEX_KEYWORD = 4;
+const float LEX_EOF = 5;
+const float LEX_OPERATOR_OTHER = 100;
+const float LEX_OPERATOR_ARITH = 101;
+const float LEX_OPERATOR_MULT = 102;
+const float LEX_OPERATOR_ASSIGN = 103;
+const float LEX_OPERATOR_BIT = 104;
+const float LEX_OPERATOR_LOGICAL = 105;
+const float LEX_OPERATOR_COMPARE = 106;
+const float LEX_OPERATOR_COMMA = 107;
+const float LEX_OPERATOR_INCREMENT=108;
+
+float lex_string_index;
+float lex_string_end;
+string lex_string;
+entity lex_lookahead;
+
+entity lex_move_lookahead()
+{
+ entity tmp = lex_lookahead;
+ lex_lookahead = world;
+ return tmp;
+}
+void lex_token(float type, string lexeme)
+{
+ if ( lex_lookahead )
+ remove(lex_lookahead);
+ lex_lookahead = spawn();
+ lex_lookahead.script_value = lexeme;
+ lex_lookahead.script_type = type;
+ lex_lookahead.classname = "lex_token";
+}
+
+void lex_skipws()
+{
+ while ( lex_string_index < lex_string_end &&
+ ctype_space(substring(lex_string,lex_string_index,1)) )
+ {
+ lex_string_index++;
+ }
+}
+
+string lex_identifier()
+{
+ float start_index = lex_string_index;
+ while( lex_string_index < lex_string_end &&
+ ctype_identifier(substring(lex_string,lex_string_index,1)) )
+ {
+ lex_string_index++;
+ }
+ return substring(lex_string,start_index,lex_string_index-start_index);
+}
+
+// TODO: allow exponent 1.2e-3
+string lex_number()
+{
+ string nextch;
+ float start_index = lex_string_index;
+ float candot = 1;
+ while( lex_string_index < lex_string_end )
+ {
+ nextch = substring(lex_string,lex_string_index,1);
+ if ( ctype_digit(nextch) )
+ {
+ lex_string_index++;
+ }
+ else if ( candot && nextch == "." )
+ {
+ candot = 0;
+ lex_string_index++;
+ }
+ else
+ {
+ break;
+ }
+ }
+ // ensure that trailing fractional zeros are truncated
+ return script_ftos(stof(substring(lex_string,start_index,lex_string_index-start_index)));
+}
+
+string lex_string_literal()
+{
+ string nextch;
+ lex_string_index++; // skip "
+ string lexeme = "";
+ while( lex_string_index < lex_string_end )
+ {
+ nextch = substring(lex_string,lex_string_index,1);
+
+ lex_string_index++;
+ if ( nextch == "\"" )
+ break;
+
+ if ( nextch == "\\" && lex_string_index < lex_string_end )
+ {
+ nextch = substring(lex_string,lex_string_index,1);
+ if ( nextch == "\\" || nextch == "\"" )
+ lex_string_index++;
+ else if ( nextch == "n" )
+ {
+ nextch = "\n";
+ lex_string_index++;
+ }
+ else
+ nextch = "\\";
+ }
+
+ lexeme = strcat(lexeme,nextch);
+ }
+ return lexeme;
+}
+
+void lex_get_token()
+{
+ lex_skipws();
+ if ( lex_string_index >= lex_string_end )
+ return lex_token(LEX_EOF,"");
+
+ string nextch = substring(lex_string,lex_string_index,1);
+
+ if ( ctype_digit(nextch) ||
+ ( nextch == "." && ctype_digit(substring(lex_string,lex_string_index+1,1)) ) )
+ {
+ return lex_token(LEX_VALUE_NUMBER,lex_number());
+ }
+
+ if ( ctype_identifier(nextch) )
+ {
+ string id = lex_identifier();
+ if ( id == "if" || id == "else" || id == "while" || id == "for" ||
+ id == "function" || id == "return" || id == "break" || id == "continue" )
+ return lex_token(LEX_KEYWORD,id);
+ return lex_token(LEX_IDENTIFIER,id);
+ }
+
+ if ( nextch == "\"" )
+ {
+ return lex_token(LEX_VALUE_STRING,lex_string_literal());
+ }
+
+ lex_string_index++;
+
+ if ( nextch == "(" || nextch == "{" )
+ return lex_token(LEX_OPERATOR_OTHER,"(");
+ if ( nextch == ")" || nextch == "}" )
+ return lex_token(LEX_OPERATOR_OTHER,")");
+ if ( nextch == "?" || nextch == ":" || nextch == "." )
+ return lex_token(LEX_OPERATOR_OTHER,nextch);
+ if ( nextch == "," || nextch == ";" )
+ return lex_token(LEX_OPERATOR_COMMA,nextch);
+
+ if ( lex_string_index < lex_string_end && substring(lex_string,lex_string_index,1) == "=" )
+ {
+ lex_string_index++;
+
+ if ( nextch == "=" || nextch == "!" || nextch == "<" || nextch == ">" )
+ return lex_token(LEX_OPERATOR_COMPARE,strcat(nextch,"="));
+
+ if ( nextch == "&" || nextch == "^" || nextch == "|" ||
+ nextch == "+" || nextch == "-" || nextch == "*" || nextch == "/" || nextch == "%" )
+ return lex_token(LEX_OPERATOR_ASSIGN,strcat(nextch,"="));
+
+ lex_string_index--;
+ }
+
+ if ( nextch == "+" || nextch == "-" )
+ {
+ if ( substring(lex_string,lex_string_index,1) == nextch )
+ {
+ lex_string_index++;
+ return lex_token(LEX_OPERATOR_INCREMENT,strcat(nextch,nextch));
+ }
+ return lex_token(LEX_OPERATOR_ARITH,nextch);
+ }
+ if ( nextch == "*" || nextch == "/" || nextch == "%" )
+ return lex_token(LEX_OPERATOR_MULT,nextch);
+ if ( nextch == "^" || nextch == "~" )
+ return lex_token(LEX_OPERATOR_BIT,nextch);
+ if ( nextch == "|" || nextch == "&" )
+ {
+ if ( substring(lex_string,lex_string_index,1) == nextch )
+ {
+ lex_string_index++;
+ return lex_token(LEX_OPERATOR_LOGICAL,strcat(nextch,nextch));
+ }
+ return lex_token(LEX_OPERATOR_BIT,nextch);
+ }
+ if ( nextch == "<" || nextch == ">" )
+ {
+ if ( substring(lex_string,lex_string_index,1) == nextch )
+ {
+ lex_string_index++;
+ return lex_token(LEX_OPERATOR_BIT,strcat(nextch,nextch));
+ }
+ return lex_token(LEX_OPERATOR_COMPARE,nextch);
+ }
+ if ( nextch == "!" )
+ return lex_token(LEX_OPERATOR_LOGICAL,nextch);
+ if ( nextch == "=" )
+ return lex_token(LEX_OPERATOR_ASSIGN,nextch);
+
+ return lex_token(LEX_UNKNOWN,substring(lex_string,lex_string_index-1,1));
+}
+
--- /dev/null
+
+// ========================================================================
+// Parser
+// ========================================================================
+entity parse_block();
+entity parse_expression();
+
+entity parse_identifier(string id)
+{
+ if ( lex_lookahead.script_type == LEX_OPERATOR_OTHER &&
+ lex_lookahead.script_value == "(" )
+ {
+ entity function_call = ast_function(id);
+ lex_get_token();
+ if ( lex_lookahead.script_type == LEX_OPERATOR_OTHER && lex_lookahead.script_value == ")" )
+ lex_get_token();
+ else
+ {
+ entity param = world;
+ entity nextparam;
+ while ( lex_lookahead.script_type != LEX_EOF )
+ {
+ nextparam = parse_expression();
+ if ( !nextparam )
+ break;
+ if ( !param )
+ function_call.ast_operand1 = nextparam;
+ else
+ param.script_next = nextparam;
+ param = nextparam;
+
+ if ( lex_lookahead.script_type == LEX_OPERATOR_OTHER &&
+ lex_lookahead.script_value == ")" )
+ {
+ lex_get_token();
+ break;
+ }
+
+ lex_get_token();
+ }
+ }
+ return function_call;
+ }
+ return ast_variable(id);
+}
+
+entity parse_primary()
+{
+ entity lex_prev = lex_move_lookahead();
+ lex_get_token();
+ entity result = world;
+ switch(lex_prev.script_type)
+ {
+ case LEX_IDENTIFIER:
+ result = parse_identifier(lex_prev.script_value);
+ break;
+ case LEX_VALUE_NUMBER:
+ case LEX_VALUE_STRING:
+ result = ast_simplevalue(lex_prev.script_value);
+ break;
+ case LEX_OPERATOR_ARITH:
+ result = parse_primary();
+ if ( lex_prev.script_value == "-" )
+ result = ast_unaryminus(result);
+ break;
+ case LEX_OPERATOR_OTHER:
+ if ( lex_prev.script_value == "(" )
+ {
+ result = parse_block();
+ lex_get_token(); // remove )
+ }
+ break;
+ case LEX_OPERATOR_BIT:
+ if ( lex_prev.script_value == "~" )
+ {
+ result = parse_primary();
+ result = ast_bitnot(result);
+ }
+ break;
+ case LEX_OPERATOR_LOGICAL:
+ if ( lex_prev.script_value == "!" )
+ {
+ result = parse_primary();
+ result = ast_not(result);
+ }
+ break;
+ case LEX_OPERATOR_INCREMENT:
+ if ( lex_lookahead.script_type == LEX_IDENTIFIER )
+ {
+ entity (entity, entity) operator = ast_sum;
+ if ( lex_prev.script_value == "--" )
+ operator = ast_sub;
+ result = ast_assign_operator(ast_variable(lex_lookahead.script_value),
+ ast_simplevalue("1"),operator);
+ lex_get_token();
+ }
+ break;
+ case LEX_KEYWORD:
+ if ( lex_prev.script_value == "return" )
+ {
+ if ( lex_lookahead.script_type == LEX_OPERATOR_COMMA )
+ lex_get_token(); // remove ,;
+ else
+ result = parse_expression();
+ result = ast_return(result);
+ }
+ else if ( lex_prev.script_value == "break" )
+ {
+ result = ast_break();
+ }
+ else if ( lex_prev.script_value == "continue" )
+ {
+ result = ast_continue();
+ }
+ break;
+ }
+ remove(lex_prev);
+ return result;
+}
+
+entity parse_field()
+{
+ entity val = parse_primary();
+ while(lex_lookahead.script_type == LEX_OPERATOR_OTHER &&
+ lex_lookahead.script_value == "." && val.ast_lvalue )
+ {
+ lex_get_token();
+ val = ast_field(val,lex_lookahead.script_value);
+ lex_get_token();
+ }
+ return val;
+}
+
+entity parse_multiplication()
+{
+ entity val = parse_field();
+ entity lex_prev;
+ while(lex_lookahead.script_type == LEX_OPERATOR_MULT)
+ {
+ lex_prev = lex_move_lookahead();
+ lex_get_token();
+ if ( lex_prev.script_value == "/" )
+ val = ast_div(val,parse_field());
+ else if ( lex_prev.script_value == "%" )
+ val = ast_mod(val,parse_field());
+ else
+ val = ast_mul(val,parse_field());
+ remove(lex_prev);
+ }
+ return val;
+}
+
+entity parse_arithmetic()
+{
+ entity val = parse_multiplication();
+ entity lex_prev;
+ while(lex_lookahead.script_type == LEX_OPERATOR_ARITH)
+ {
+ lex_prev = lex_move_lookahead();
+ lex_get_token();
+ if ( lex_prev.script_value == "-" )
+ val = ast_sub(val,parse_multiplication());
+ else
+ val = ast_sum(val,parse_multiplication());
+ remove(lex_prev);
+ }
+ return val;
+}
+
+entity parse_bitwise()
+{
+ entity val = parse_arithmetic();
+ entity lex_prev;
+ while(lex_lookahead.script_type == LEX_OPERATOR_BIT)
+ {
+ lex_prev = lex_move_lookahead();
+ lex_get_token();
+ if ( lex_prev.script_value == "<<" )
+ val = ast_bitlshift(val,parse_arithmetic());
+ else if ( lex_prev.script_value == ">>" )
+ val = ast_bitrshift(val,parse_arithmetic());
+ else if ( lex_prev.script_value == "&" )
+ val = ast_bitand(val,parse_arithmetic());
+ else if ( lex_prev.script_value == "^" )
+ val = ast_bitxor(val,parse_arithmetic());
+ else
+ val = ast_bitor(val,parse_arithmetic());
+ remove(lex_prev);
+ }
+ return val;
+}
+
+entity parse_comparison()
+{
+ entity val = parse_bitwise();
+ entity lex_prev;
+ while(lex_lookahead.script_type == LEX_OPERATOR_COMPARE)
+ {
+ lex_prev = lex_move_lookahead();
+ lex_get_token();
+ if ( lex_prev.script_value == "<" )
+ val = ast_lt(val,parse_bitwise());
+ else if ( lex_prev.script_value == "<=" )
+ val = ast_le(val,parse_bitwise());
+ else if ( lex_prev.script_value == "!=" )
+ val = ast_ne(val,parse_bitwise());
+ else if ( lex_prev.script_value == ">=" )
+ val = ast_ge(val,parse_bitwise());
+ else if ( lex_prev.script_value == ">" )
+ val = ast_gt(val,parse_bitwise());
+ else
+ val = ast_eq(val,parse_bitwise());
+ remove(lex_prev);
+ }
+ return val;
+}
+
+entity parse_boolean()
+{
+ entity val = parse_comparison();
+ entity lex_prev;
+ while(lex_lookahead.script_type == LEX_OPERATOR_LOGICAL)
+ {
+ lex_prev = lex_move_lookahead();
+ lex_get_token();
+ if ( lex_prev.script_value == "||" )
+ val = ast_or(val,parse_comparison());
+ else
+ val = ast_and(val,parse_comparison());
+ remove(lex_prev);
+ }
+ return val;
+}
+
+entity parse_expression()
+{
+ entity val = parse_boolean();
+
+ if ( lex_lookahead.script_type == LEX_OPERATOR_OTHER &&
+ lex_lookahead.script_value == "?" )
+ {
+ lex_get_token();
+ entity branch_true = parse_boolean();
+ entity branch_false;
+ if ( lex_lookahead.script_type == LEX_OPERATOR_OTHER &&
+ lex_lookahead.script_value == ":" )
+ {
+ lex_get_token();
+ branch_false = parse_boolean();
+ }
+ else
+ branch_false = ast_simplevalue("");
+
+ return ast_if(val,branch_true,branch_false);
+ }
+
+ return val;
+}
+
+entity parse_assignment()
+{
+ entity lval = parse_expression();
+ if ( lex_lookahead.script_type == LEX_OPERATOR_ASSIGN && lval.ast_lvalue )
+ {
+ string oper = substring(lex_lookahead.script_value,0,1);
+ lex_get_token();
+ entity rval = parse_expression();
+ entity(entity,entity) operator;
+ if ( oper == "+" )
+ operator = ast_sum;
+ else if ( oper == "-" )
+ operator = ast_sub;
+ else if ( oper == "*" )
+ operator = ast_mul;
+ else if ( oper == "/" )
+ operator = ast_div;
+ else if ( oper == "%" )
+ operator = ast_mod;
+ else if ( oper == "^" )
+ operator = ast_bitxor;
+ else if ( oper == "&" )
+ operator = ast_bitand;
+ else if ( oper == "|" )
+ operator = ast_bitor;
+ else
+ return ast_assign(lval,rval);
+ return ast_assign_operator(lval,rval,operator);
+ }
+ else if ( lex_lookahead.script_type == LEX_OPERATOR_INCREMENT && lval.ast_lvalue )
+ {
+ entity (entity, entity) operator = ast_sum;
+ if ( lex_lookahead.script_value == "--" )
+ operator = ast_sub;
+ lex_get_token();
+ return ast_assign_operator(lval,ast_simplevalue("1"),operator);
+ }
+
+ return lval;
+}
+
+entity parse_statement()
+{
+ if ( lex_lookahead.script_type == LEX_KEYWORD )
+ {
+ if ( lex_lookahead.script_value == "if" )
+ {
+ lex_get_token(); // remove if
+ lex_get_token(); // remove (
+ entity condition = parse_assignment();
+ lex_get_token(); // remove )
+ entity branch_true = parse_statement();
+ entity branch_false;
+ if ( lex_lookahead.script_type == LEX_KEYWORD && lex_lookahead.script_value == "else" )
+ {
+ lex_get_token(); // remove else
+ branch_false = parse_statement();
+ }
+ else
+ branch_false = ast_simplevalue("");
+
+ return ast_if(condition,branch_true,branch_false);
+ }
+ else if ( lex_lookahead.script_value == "while" )
+ {
+ lex_get_token(); // remove while
+ lex_get_token(); // remove (
+ entity condition = parse_assignment();
+ lex_get_token(); // remove )
+
+ return ast_while(condition,parse_statement());
+ }
+ else if ( lex_lookahead.script_value == "for" )
+ {
+ lex_get_token(); // remove for
+ lex_get_token(); // remove (
+ entity start = parse_assignment();
+ lex_get_token(); // remove ;
+ entity condition = parse_assignment();
+ lex_get_token(); // remove ;
+ entity increment = parse_assignment();
+ lex_get_token(); // remove )
+
+ // { block; increment }
+ entity for_inner_block = ast_block();
+ for_inner_block.ast_operand1 = parse_statement();
+ if ( for_inner_block.ast_operand1 )
+ for_inner_block.ast_operand1.script_next = increment;
+ else
+ for_inner_block.ast_operand1 = increment;
+
+ // { start; while ( condition ) inner_block }
+ entity for_outer_block = ast_block();
+ for_outer_block.ast_operand1 = start;
+ start.script_next = ast_while(condition,for_inner_block);
+
+ return for_outer_block;
+ }
+ else if ( lex_lookahead.script_value == "function" )
+ {
+ lex_get_token(); // remove function
+ string id = lex_lookahead.script_value;
+ lex_get_token(); // remove id
+ lex_get_token(); // remove (
+ entity func_declaration = ast_function_declaration(id);
+
+ if ( lex_lookahead.script_type == LEX_OPERATOR_OTHER && lex_lookahead.script_value == ")" )
+ lex_get_token();
+ else
+ {
+ entity param = world;
+ entity nextparam;
+ while ( lex_lookahead.script_type != LEX_EOF )
+ {
+ nextparam = spawn();
+ nextparam.script_value = lex_lookahead.script_value;
+ lex_get_token(); // remove param name
+ if ( lex_lookahead.script_type == LEX_OPERATOR_ASSIGN &&
+ lex_lookahead.script_value == "=" )
+ {
+ lex_get_token(); // remove =
+ nextparam.ast_operand1 = parse_expression();
+ }
+ if ( !param )
+ func_declaration.ast_operand1 = nextparam;
+ else
+ param.script_next = nextparam;
+ param = nextparam;
+
+ if ( lex_lookahead.script_type == LEX_OPERATOR_OTHER &&
+ lex_lookahead.script_value == ")" )
+ {
+ lex_get_token();
+ break;
+ }
+
+ lex_get_token();
+
+ }
+ }
+ func_declaration.ast_operand2 = parse_statement();
+
+ return func_declaration;
+ }
+ }
+
+
+ entity val = parse_assignment();
+ if ( lex_lookahead.script_type == LEX_OPERATOR_COMMA )
+ lex_get_token(); // skip ; and ,
+ return val;
+}
+
+entity parse_block()
+{
+ entity val = ast_block();
+ entity prev = world;
+ entity next;
+ while ( lex_lookahead.script_type != LEX_EOF &&
+ !( lex_lookahead.script_type == LEX_OPERATOR_OTHER && lex_lookahead.script_value == ")" ) )
+ {
+ next = parse_statement();
+ if ( !next )
+ break;
+ if ( prev )
+ prev.script_next = next;
+ else
+ val.ast_operand1 = next;
+ prev = next;
+ }
+ return val;
+}
+
+entity script_compile(string source)
+{
+ lex_string_index = 0;
+ lex_string_end = strlen(source);
+ lex_string = source;
+ lex_get_token();
+ entity ast = parse_block();
+ remove(lex_lookahead);
+ lex_lookahead = world;
+ //script_debug_ast(ast);
+ return ast;
+}
+
--- /dev/null
+
+// ========================================================================
+// String functions
+// ========================================================================
+
+float ctype_space(string char)
+{
+ return char == " " || char == "\n" || char == "\t";
+}
+
+float ctype_upper(string char)
+{
+ return char == "A" || char == "B" || char == "C" || char == "D" ||
+ char == "E" || char == "F" || char == "G" || char == "H" ||
+ char == "I" || char == "J" || char == "K" || char == "L" ||
+ char == "M" || char == "N" || char == "O" || char == "P" ||
+ char == "Q" || char == "R" || char == "S" || char == "T" ||
+ char == "U" || char == "V" || char == "W" || char == "X" ||
+ char == "Y" || char == "Z";
+}
+
+float ctype_lower(string char)
+{
+ return char == "a" || char == "b" || char == "c" || char == "d" ||
+ char == "e" || char == "f" || char == "g" || char == "h" ||
+ char == "i" || char == "j" || char == "k" || char == "l" ||
+ char == "m" || char == "n" || char == "o" || char == "p" ||
+ char == "q" || char == "r" || char == "s" || char == "t" ||
+ char == "u" || char == "v" || char == "w" || char == "x" ||
+ char == "y" || char == "z";
+}
+
+float ctype_alpha(string char)
+{
+ return ctype_upper(char) || ctype_lower(char);
+}
+
+float ctype_digit(string char)
+{
+ return char == "0" || char == "1" || char == "2" || char == "3" ||
+ char == "4" || char == "5" || char == "6" || char == "7" ||
+ char == "8" || char == "9";
+}
+
+float ctype_alnum(string char)
+{
+ return ctype_alpha(char) || ctype_digit(char);
+}
+
+float ctype_identifier(string char)
+{
+ return ctype_alnum(char) || char == "_";
+}
+
+string script_ftos(float f)
+{
+ return sprintf("%.9g", f);
+}
+
+float is_numeric(string s)
+{
+ float candot = 1;
+ float canexp = 1;
+ float i;
+ string c;
+ for ( i = 0; i < strlen(s); i++ )
+ {
+ c = substring(s,i,1);
+ if ( !ctype_digit(c) )
+ {
+ if ( candot && c == "." )
+ candot = 0;
+ else if ( canexp == 1 && (c == "e" || c == "E") && i+1 < strlen(s) )
+ {
+ canexp = candot = 0;
+ c = substring(s,i+1,1);
+ if ( c == "+" || c == "-" )
+ i++;
+ }
+ else
+ return 0;
+ }
+ }
+ return 1;
+}
+
+entity float_to_entity(float n)
+{
+#ifdef SVQC
+ if ( n ) return edict_num(n);
+#elif defined(CSQC)
+ if ( n ) return entitybyindex(n);
+#elif defined(MENUQC)
+ if ( n ) return ftoe(n);
+#endif
+ return world;
+}
+entity string_to_entity(string s)
+{
+ return float_to_entity(stof(s));
+}
+string entity_get(entity e, string newfield)
+{
+ float n = numentityfields();
+ float i;
+ for ( i = 0; i < n; i++ )
+ {
+ if ( entityfieldname(i) == newfield )
+ {
+ return getentityfieldstring(i,e);
+ }
+ }
+ return "";
+}
+
+float entity_set(entity e, string newfield, string newvalue)
+{
+ if ( !e )
+ return 0;
+ float n = numentityfields();
+ float i;
+ for ( i = 0; i < n; i++ )
+ {
+ if ( entityfieldname(i) == newfield )
+ {
+ return putentityfieldstring(i,e,newvalue);
+ }
+ }
+ return 0;
+}
+
// 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
-#define CSQC_REVISION 22
+// Revision 23: variable hit sound
+#define CSQC_REVISION 23
const float AS_STRING = 1;
const float AS_INT = 2;
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 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;
///////////////////////////
// csqc communication stuff
-const float CTF_STATE_ATTACK = 1;
-const float CTF_STATE_DEFEND = 2;
-const float CTF_STATE_COMMANDER = 3;
-
const float HUD_NORMAL = 0;
-const float HUD_VEHICLE_FIRST = 10;
-const float HUD_SPIDERBOT = 10;
-const float HUD_WAKIZASHI = 11;
-const float HUD_RAPTOR = 12;
-const float HUD_BUMBLEBEE = 13;
-const float HUD_BUMBLEBEE_GUN = 14;
-const float HUD_VEHICLE_LAST = 14;
+const float HUD_BUMBLEBEE_GUN = 25;
const vector eX = '1 0 0';
const vector eY = '0 1 0';
/**
* Score indices
*/
-#define MAX_SCORE 10
+#define MAX_SCORE 11
#define MAX_TEAMSCORE 2
#define ST_SCORE 0
#define SP_DEATHS 1
#define SP_SUICIDES 2
#define SP_SCORE 3
+#define SP_FPS 10
// game mode specific indices are not in common/, but in server/scores_rules.qc!
const float CH_INFO = 0;
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 = 60;
+const float PROJECTILE_RPC = 37;
+const float PROJECTILE_CANNONBALL = 38;
const float SPECIES_HUMAN = 0;
const float SPECIES_ROBOT_SOLID = 1;
//#define CSQCMODEL_SUPPORT_GETTAGINFO_BEFORE_DRAW
// server decides crouching, this lags, but so be it
-#define CSQCMODEL_SERVERSIDE_CROUCH
+//#define CSQCMODEL_SERVERSIDE_CROUCH
// a hack for Xonotic
#ifdef CSQC
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) \
#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) \
DEATHTYPE(DEATH_KILL, DEATH_SELF_SUICIDE, NO_MSG, NORMAL_POS) \
DEATHTYPE(DEATH_LAVA, DEATH_SELF_LAVA, DEATH_MURDER_LAVA, NORMAL_POS) \
DEATHTYPE(DEATH_MIRRORDAMAGE, DEATH_SELF_BETRAYAL, NO_MSG, NORMAL_POS) \
- DEATHTYPE(DEATH_MONSTER_MAGE, DEATH_SELF_MON_MAGE, DEATH_MURDER_MONSTER, DEATH_MONSTER_FIRST) \
- DEATHTYPE(DEATH_MONSTER_SHAMBLER_CLAW, DEATH_SELF_MON_SHAMBLER_CLAW, DEATH_MURDER_MONSTER, NORMAL_POS) \
- DEATHTYPE(DEATH_MONSTER_SHAMBLER_SMASH, DEATH_SELF_MON_SHAMBLER_SMASH, DEATH_MURDER_MONSTER, NORMAL_POS) \
- DEATHTYPE(DEATH_MONSTER_SHAMBLER_ZAP, DEATH_SELF_MON_SHAMBLER_ZAP, DEATH_MURDER_MONSTER, NORMAL_POS) \
- DEATHTYPE(DEATH_MONSTER_SPIDER, DEATH_SELF_MON_SPIDER, DEATH_MURDER_MONSTER, NORMAL_POS) \
- DEATHTYPE(DEATH_MONSTER_WYVERN, DEATH_SELF_MON_WYVERN, DEATH_MURDER_MONSTER, NORMAL_POS) \
- DEATHTYPE(DEATH_MONSTER_ZOMBIE_JUMP, DEATH_SELF_MON_ZOMBIE_JUMP, DEATH_MURDER_MONSTER, NORMAL_POS) \
- DEATHTYPE(DEATH_MONSTER_ZOMBIE_MELEE, DEATH_SELF_MON_ZOMBIE_MELEE, DEATH_MURDER_MONSTER, DEATH_MONSTER_LAST) \
+ DEATHTYPE(DEATH_MONSTER_AFRIT, DEATH_SELF_MON_AFRIT, DEATH_MURDER_MONSTER, DEATH_MONSTER_FIRST) \
+ DEATHTYPE(DEATH_MONSTER_CREEPER, DEATH_SELF_MON_CREEPER, DEATH_MURDER_MONSTER, NORMAL_POS) \
+ DEATHTYPE(DEATH_MONSTER_DEMON_JUMP, DEATH_SELF_MON_DEMON_JUMP, DEATH_MURDER_MONSTER, NORMAL_POS) \
+ DEATHTYPE(DEATH_MONSTER_DEMON_MELEE, DEATH_SELF_MON_DEMON_MELEE, DEATH_MURDER_MONSTER, NORMAL_POS) \
+ DEATHTYPE(DEATH_MONSTER_MAGE, DEATH_SELF_MON_MAGE, DEATH_MURDER_MONSTER, NORMAL_POS) \
+ DEATHTYPE(DEATH_MONSTER_OGRE_GRENADE, DEATH_SELF_MON_OGRE_GRENADE, DEATH_MURDER_MONSTER, NORMAL_POS) \
+ DEATHTYPE(DEATH_MONSTER_OGRE_MACHINEGUN,DEATH_SELF_MON_OGRE_MACHINEGUN, DEATH_MURDER_MONSTER, NORMAL_POS) \
+ DEATHTYPE(DEATH_MONSTER_OGRE_MELEE, DEATH_SELF_MON_OGRE_MELEE, DEATH_MURDER_MONSTER, NORMAL_POS) \
+ DEATHTYPE(DEATH_MONSTER_ROTFISH_MELEE, DEATH_SELF_MON_ROTFISH, DEATH_MURDER_MONSTER, NORMAL_POS) \
+ DEATHTYPE(DEATH_MONSTER_ROTTWEILER, DEATH_SELF_MON_ROTTWEILER, DEATH_MURDER_MONSTER, NORMAL_POS) \
+ DEATHTYPE(DEATH_MONSTER_SCRAG, DEATH_SELF_MON_SCRAG, DEATH_MURDER_MONSTER, NORMAL_POS) \
+ DEATHTYPE(DEATH_MONSTER_SHAMBLER_CLAW, DEATH_SELF_MON_SHAMBLER_CLAW, DEATH_MURDER_MONSTER, NORMAL_POS) \
+ DEATHTYPE(DEATH_MONSTER_SHAMBLER_SMASH, DEATH_SELF_MON_SHAMBLER_SMASH, DEATH_MURDER_MONSTER, NORMAL_POS) \
+ DEATHTYPE(DEATH_MONSTER_SHAMBLER_ZAP, DEATH_SELF_MON_SHAMBLER_ZAP, DEATH_MURDER_MONSTER, NORMAL_POS) \
+ DEATHTYPE(DEATH_MONSTER_SPIDER, DEATH_SELF_MON_SPIDER, DEATH_MURDER_MONSTER, NORMAL_POS) \
+ DEATHTYPE(DEATH_MONSTER_VORE, DEATH_SELF_MON_VORE, DEATH_MURDER_MONSTER, NORMAL_POS) \
+ DEATHTYPE(DEATH_MONSTER_VORE_MELEE, DEATH_SELF_MON_VORE_MELEE, DEATH_MURDER_MONSTER, NORMAL_POS) \
+ DEATHTYPE(DEATH_MONSTER_WYVERN, DEATH_SELF_MON_WYVERN, DEATH_MURDER_MONSTER, NORMAL_POS) \
+ DEATHTYPE(DEATH_MONSTER_ZOMBIE_JUMP, DEATH_SELF_MON_ZOMBIE_JUMP, DEATH_MURDER_MONSTER, NORMAL_POS) \
+ DEATHTYPE(DEATH_MONSTER_ZOMBIE_MELEE, DEATH_SELF_MON_ZOMBIE_MELEE, DEATH_MURDER_MONSTER, DEATH_MONSTER_LAST) \
DEATHTYPE(DEATH_NADE, DEATH_SELF_NADE, DEATH_MURDER_NADE, NORMAL_POS) \
DEATHTYPE(DEATH_NADE_NAPALM, DEATH_SELF_NADE_NAPALM, DEATH_MURDER_NADE_NAPALM, NORMAL_POS) \
DEATHTYPE(DEATH_NADE_ICE, DEATH_SELF_NADE_ICE, DEATH_MURDER_NADE_ICE, NORMAL_POS) \
DEATHTYPE(DEATH_TURRET_PLASMA, DEATH_SELF_TURRET_PLASMA, NO_MSG, NORMAL_POS) \
DEATHTYPE(DEATH_TURRET_TESLA, DEATH_SELF_TURRET_TESLA, NO_MSG, NORMAL_POS) \
DEATHTYPE(DEATH_TURRET_WALK_GUN, DEATH_SELF_TURRET_WALK_GUN, NO_MSG, NORMAL_POS) \
- DEATHTYPE(DEATH_TURRET_WALK_MEELE, DEATH_SELF_TURRET_WALK_MEELE, NO_MSG, NORMAL_POS) \
+ DEATHTYPE(DEATH_TURRET_WALK_MELEE, DEATH_SELF_TURRET_WALK_MELEE, NO_MSG, NORMAL_POS) \
DEATHTYPE(DEATH_TURRET_WALK_ROCKET, DEATH_SELF_TURRET_WALK_ROCKET, NO_MSG, DEATH_TURRET_LAST) \
DEATHTYPE(DEATH_VH_BUMB_DEATH, DEATH_SELF_VH_BUMB_DEATH, DEATH_MURDER_VH_BUMB_DEATH, DEATH_VHFIRST) \
DEATHTYPE(DEATH_VH_BUMB_GUN, NO_MSG, DEATH_MURDER_VH_BUMB_GUN, NORMAL_POS) \
DEATHTYPE(DEATH_VH_SPID_DEATH, DEATH_SELF_VH_SPID_DEATH, DEATH_MURDER_VH_SPID_DEATH, NORMAL_POS) \
DEATHTYPE(DEATH_VH_SPID_MINIGUN, NO_MSG, DEATH_MURDER_VH_SPID_MINIGUN, NORMAL_POS) \
DEATHTYPE(DEATH_VH_SPID_ROCKET, DEATH_SELF_VH_SPID_ROCKET, DEATH_MURDER_VH_SPID_ROCKET, NORMAL_POS) \
+ DEATHTYPE(DEATH_VH_TANK_DEATH, DEATH_SELF_VH_TANK_DEATH, DEATH_MURDER_VH_TANK_DEATH, NORMAL_POS) \
+ DEATHTYPE(DEATH_VH_TANKLL48, NO_MSG, DEATH_MURDER_VH_TANKLL48, NORMAL_POS) \
DEATHTYPE(DEATH_VH_WAKI_DEATH, DEATH_SELF_VH_WAKI_DEATH, DEATH_MURDER_VH_WAKI_DEATH, NORMAL_POS) \
DEATHTYPE(DEATH_VH_WAKI_GUN, NO_MSG, DEATH_MURDER_VH_WAKI_GUN, NORMAL_POS) \
DEATHTYPE(DEATH_VH_WAKI_ROCKET, DEATH_SELF_VH_WAKI_ROCKET, DEATH_MURDER_VH_WAKI_ROCKET, DEATH_VHLAST) \
const float HITTYPE_SECONDARY = 0x100;
const float HITTYPE_SPLASH = 0x200; // automatically set by RadiusDamage
const float HITTYPE_BOUNCE = 0x400;
-const float HITTYPE_RESERVED2 = 0x800;
+const float HITTYPE_HEADSHOT = 0x800; // automatically set by Damage (if headshotbonus is set)
const float HITTYPE_RESERVED = 0x1000; // unused yet
--- /dev/null
+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);
+ 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); }
+
+ if(!self.eent_eff_trail) { WriteByte(MSG_ENTITY, self.eent_net_count); }
+ 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);
+}
+#endif
\ No newline at end of file
--- /dev/null
+// 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
+.vector eent_net_location;
+.vector eent_net_velocity;
+.float eent_eff_trail;
+.string eent_eff_name;
+.float eent_net_count;
+
+#ifdef CSQC
+void Read_Effect(float is_new);
+#endif
+
+#ifdef SVQC
+void Send_Effect(float eff_name, vector eff_loc, vector eff_vel, float eff_cnt);
+#endif
+
+#define EFFECT_FIRST 1
+float EFFECT_COUNT;
+
+#define MAX_EFFECTS 512
+entity effects_ent[MAX_EFFECTS];
+
+void Create_Effect_Entity(float eff_name, string eff_string, float eff_trail);
+
+#define EFFECT(istrail,name,realname) \
+ float 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
--- /dev/null
+// sadly, we can't put this stuff inside an #ifdef, as it is always called
+
+#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)
if(v == "dom_controlpoint")
MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_DOMINATION;
else if(v == "item_flag_team2")
- MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_CTF;
+ MapInfo_Map_supportedGametypes |= (MAPINFO_TYPE_CTF | MAPINFO_TYPE_VIP);
else if(v == "team_CTF_blueflag")
- MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_CTF;
+ MapInfo_Map_supportedGametypes |= (MAPINFO_TYPE_CTF | MAPINFO_TYPE_VIP);
+ else if(v == "conquest_controlpoint")
+ MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_CONQUEST;
else if(v == "invasion_spawnpoint")
MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_INVASION;
else if(v == "target_assault_roundend")
MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_ASSAULT;
+ else if(startsWith(v, "jailbreak_"))
+ MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_JAILBREAK;
else if(v == "onslaught_generator")
MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_ONSLAUGHT;
else if(substring(v, 0, 8) == "nexball_" || substring(v, 0, 4) == "ball")
}
diameter = vlen(mapMaxs - mapMins);
- twoBaseModes = MapInfo_Map_supportedGametypes & (MAPINFO_TYPE_CTF | MAPINFO_TYPE_ASSAULT | MAPINFO_TYPE_RACE | MAPINFO_TYPE_NEXBALL);
+ twoBaseModes = MapInfo_Map_supportedGametypes & (MAPINFO_TYPE_CTF | MAPINFO_TYPE_ASSAULT | MAPINFO_TYPE_RACE | MAPINFO_TYPE_NEXBALL | MAPINFO_TYPE_VIP);
if(twoBaseModes && (MapInfo_Map_supportedGametypes == twoBaseModes))
{
// we have a CTF-only or Assault-only map. Don't add other modes then,
MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_TEAM_DEATHMATCH;
MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_FREEZETAG;
MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_CA;
+ MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_VIP;
}
if(spawnpoints >= 12 && diameter > 5120)
MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_KEYHUNT;
s = cdr(s);
}
+ if(pWantedType == MAPINFO_TYPE_VIP)
+ {
+ sa = car(s);
+ if(sa != "")
+ cvar_set("fraglimit", sa);
+ s = cdr(s);
+ }
+
+ if(pWantedType == MAPINFO_TYPE_CONQUEST)
+ {
+ sa = car(s);
+ if(sa != "")
+ cvar_set("g_conquest_teams", sa);
+ s = cdr(s);
+ }
+
+ if(pWantedType == MAPINFO_TYPE_JAILBREAK)
+ {
+ sa = car(s);
+ if(sa != "")
+ cvar_set("g_jailbreak_teams", sa);
+ s = cdr(s);
+ }
+
/* keepaway wuz here
if(pWantedType == MAPINFO_TYPE_KEEPAWAY)
{
cvar_set("g_tdm_teams", cvar_defstring("g_tdm_teams"));
cvar_set("g_ca_teams", cvar_defstring("g_ca_teams"));
cvar_set("g_freezetag_teams", cvar_defstring("g_freezetag_teams"));
+ cvar_set("g_conquest_teams", cvar_defstring("g_conquest_teams"));
+ cvar_set("g_jailbreak_teams", cvar_defstring("g_jailbreak_teams"));
+ cvar_set("g_infection_teams", cvar_defstring("g_infection_teams"));
+ cvar_set("g_invasion_teams", cvar_defstring("g_invasion_teams"));
cvar_set("g_keyhunt_teams", cvar_defstring("g_keyhunt_teams"));
cvar_set("g_domination_default_teams", cvar_defstring("g_domination_default_teams"));
cvar_set("g_race_qualifying_timelimit", cvar_defstring("g_race_qualifying_timelimit"));
cvar_set("g_keyhunt_teams", v);
cvar_set("g_domination_default_teams", v);
cvar_set("g_invasion_teams", v);
+ cvar_set("g_conquest_teams", v);
+ cvar_set("g_jailbreak_teams", v);
+ cvar_set("g_infection_teams", v);
}
else if(k == "qualifying_timelimit")
{
if (!cvar_value_issafe(t))
print("Map ", pFilename, " contains a potentially harmful cdtrack, ignored\n");
else
- MapInfo_Map_clientstuff = strcat(
- MapInfo_Map_clientstuff, "cd loop \"", t, "\"\n"
- );
+ {
+#ifdef XMAS
+ if(fexists("tis_xmas"))
+ MapInfo_Map_clientstuff = sprintf("%s%s%d\n", MapInfo_Map_clientstuff, "cd loop xmas", floor(random() * 5));
+ else
+#endif
+ MapInfo_Map_clientstuff = strcat(
+ MapInfo_Map_clientstuff, "cd loop \"", t, "\"\n"
+ );
+ }
}
}
else
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=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"));
#define g_assault IS_GAMETYPE(ASSAULT)
-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(_("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(_("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"));
#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"))
+#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"));
+#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"));
+#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;
--- /dev/null
+// 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"), "");
+ }
+}
\ No newline at end of file
--- /dev/null
+// 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)) )
+
--- /dev/null
+#include "minigames.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()
+
+ 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() );
+}
--- /dev/null
+/**
+
+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_<id>(entity minigame, string event, ...count)
+ see ../minigames.qh for a detailed explanation
+CSQC:
+ void minigame_hud_board_<id>(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_<id>(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_<id>(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*/
--- /dev/null
+const float NMM_TURN_PLACE = 0x0100; // player has to place a piece on the board
+const float NMM_TURN_MOVE = 0x0200; // player has to move a piece by one tile
+const float NMM_TURN_FLY = 0x0400; // player has to move a piece anywhere
+const float NMM_TURN_TAKE = 0x0800; // player has to take a non-mill piece
+const float NMM_TURN_TAKEANY=0x1000; // combine with NMM_TURN_TAKE, can take mill pieces
+const float NMM_TURN_WIN = 0x2000; // player has won
+const float NMM_TURN_TYPE = 0xff00;
+const float NMM_TURN_TEAM1 = 0x0001;
+const float NMM_TURN_TEAM2 = 0x0002;
+const float NMM_TURN_TEAM = 0x00ff;
+
+const float NMM_PIECE_DEAD = 0x0; // captured by the enemy
+const float NMM_PIECE_HOME = 0x1; // not yet placed
+const float NMM_PIECE_BOARD = 0x2; // placed on the board
+
+.float nmm_tile_distance;
+.entity nmm_tile_piece;
+.string nmm_tile_hmill;
+.string nmm_tile_vmill;
+
+// build a string containing the indices of the tile to check for a horizontal mill
+string nmm_tile_build_hmill(entity tile)
+{
+ float number = minigame_tile_number(tile.netname);
+ float letter = minigame_tile_letter(tile.netname);
+ if ( number == letter || number+letter == 6 )
+ {
+ float add = letter < 3 ? 1 : -1;
+ return strcat(tile.netname," ",
+ minigame_tile_buildname(letter+add*tile.nmm_tile_distance,number)," ",
+ minigame_tile_buildname(letter+add*2*tile.nmm_tile_distance,number) );
+ }
+ else if ( letter == 3 )
+ return strcat(minigame_tile_buildname(letter-tile.nmm_tile_distance,number)," ",
+ tile.netname," ",
+ minigame_tile_buildname(letter+tile.nmm_tile_distance,number) );
+ else if ( letter < 3 )
+ return strcat(minigame_tile_buildname(0,number)," ",
+ minigame_tile_buildname(1,number)," ",
+ minigame_tile_buildname(2,number) );
+ else
+ return strcat(minigame_tile_buildname(4,number)," ",
+ minigame_tile_buildname(5,number)," ",
+ minigame_tile_buildname(6,number) );
+}
+
+// build a string containing the indices of the tile to check for a vertical mill
+string nmm_tile_build_vmill(entity tile)
+{
+ float letter = minigame_tile_letter(tile.netname);
+ float number = minigame_tile_number(tile.netname);
+ if ( letter == number || letter+number == 6 )
+ {
+ float add = number < 3 ? 1 : -1;
+ return strcat(tile.netname," ",
+ minigame_tile_buildname(letter,number+add*tile.nmm_tile_distance)," ",
+ minigame_tile_buildname(letter,number+add*2*tile.nmm_tile_distance) );
+ }
+ else if ( number == 3 )
+ return strcat(minigame_tile_buildname(letter,number-tile.nmm_tile_distance)," ",
+ tile.netname," ",
+ minigame_tile_buildname(letter,number+tile.nmm_tile_distance) );
+ else if ( number < 3 )
+ return strcat(minigame_tile_buildname(letter,0)," ",
+ minigame_tile_buildname(letter,1)," ",
+ minigame_tile_buildname(letter,2) );
+ else
+ return strcat(minigame_tile_buildname(letter,4)," ",
+ minigame_tile_buildname(letter,5)," ",
+ minigame_tile_buildname(letter,6) );
+}
+
+// Create an new tile
+// \param id Tile index (eg: a1)
+// \param minig Owner minigame instance
+// \param distance Distance from adjacent tiles
+void nmm_spawn_tile(string id, entity minig, float distance)
+{
+ // TODO global variable + list_next for simpler tile loops
+ entity e = spawn();
+ e.origin = minigame_tile_pos(id,7,7);
+ e.classname = "minigame_nmm_tile";
+ e.netname = id;
+ e.owner = minig;
+ e.team = 0;
+ e.nmm_tile_distance = distance;
+ e.nmm_tile_hmill = strzone(nmm_tile_build_hmill(e));
+ e.nmm_tile_vmill = strzone(nmm_tile_build_vmill(e));
+}
+
+// Create a tile square and recursively create inner squares
+// \param minig Owner minigame instance
+// \param offset Index offset (eg: 1 to start the square at b2, 0 at a1 etc.)
+// \param skip Number of indices to skip between tiles (eg 1: a1, a3)
+void nmm_spawn_tile_square( entity minig, float offset, float skip )
+{
+ float letter = offset;
+ float number = offset;
+ float i, j;
+ for ( i = 0; i < 3; i++ )
+ {
+ number = offset;
+ for ( j = 0; j < 3; j++ )
+ {
+ if ( i != 1 || j != 1 )
+ nmm_spawn_tile(strzone(minigame_tile_buildname(letter,number)),minig, skip+1);
+ number += skip+1;
+ }
+ letter += skip+1;
+ }
+
+ if ( skip > 0 )
+ nmm_spawn_tile_square(minig,offset+1,skip-1);
+}
+
+// Remove tiles of a NMM minigame
+void nmm_kill_tiles(entity minig)
+{
+ entity e = world;
+ while ( ( e = findentity(e,owner,minig) ) )
+ if ( e.classname == "minigame_nmm_tile" )
+ {
+ strunzone(e.netname);
+ strunzone(e.nmm_tile_hmill);
+ strunzone(e.nmm_tile_vmill);
+ remove(e);
+ }
+}
+
+// Create the tiles of a NMM minigame
+void nmm_init_tiles(entity minig)
+{
+ nmm_spawn_tile_square(minig,0,2);
+}
+
+// Find a tile by its id
+entity nmm_find_tile(entity minig, string id)
+{
+ entity e = world;
+ while ( ( e = findentity(e,owner,minig) ) )
+ if ( e.classname == "minigame_nmm_tile" && e.netname == id )
+ return e;
+ return world;
+}
+
+// Check whether two tiles are adjacent
+float nmm_tile_adjacent(entity tile1, entity tile2)
+{
+
+ float dnumber = fabs ( minigame_tile_number(tile1.netname) - minigame_tile_number(tile2.netname) );
+ float dletter = fabs ( minigame_tile_letter(tile1.netname) - minigame_tile_letter(tile2.netname) );
+
+ return ( dnumber == 0 && ( dletter == 1 || dletter == tile1.nmm_tile_distance ) ) ||
+ ( dletter == 0 && ( dnumber == 1 || dnumber == tile1.nmm_tile_distance ) );
+}
+
+// Returns 1 if there is at least 1 free adjacent tile
+float nmm_tile_canmove(entity tile)
+{
+ entity e = world;
+ while ( ( e = findentity(e,owner,tile.owner) ) )
+ if ( e.classname == "minigame_nmm_tile" && !e.nmm_tile_piece
+ && nmm_tile_adjacent(e,tile) )
+ {
+ return 1;
+ }
+ return 0;
+}
+
+// Check if the given tile id appears in the string
+float nmm_in_mill_string(entity tile, string s)
+{
+ float argc = tokenize(s);
+ float i;
+ for ( i = 0; i < argc; i++ )
+ {
+ entity e = nmm_find_tile(tile.owner,argv(i));
+ if ( !e || !e.nmm_tile_piece || e.nmm_tile_piece.team != tile.nmm_tile_piece.team )
+ return 0;
+ }
+ return 1;
+}
+
+// Check if a tile is in a mill
+float nmm_in_mill(entity tile)
+{
+ return tile.nmm_tile_piece && (
+ nmm_in_mill_string(tile,tile.nmm_tile_hmill) ||
+ nmm_in_mill_string(tile,tile.nmm_tile_vmill) );
+}
+
+
+#ifdef SVQC
+// Find a NMM piece matching some of the given flags and team number
+entity nmm_find_piece(entity start, entity minigame, float teamn, float pieceflags)
+{
+ entity e = start;
+ while ( ( e = findentity(e,owner,minigame) ) )
+ if ( e.classname == "minigame_board_piece" &&
+ (e.minigame_flags & pieceflags) && e.team == teamn )
+ return e;
+ return world;
+}
+
+// Count NMM pieces matching flags and team number
+float nmm_count_pieces(entity minigame, float teamn, float pieceflags)
+{
+ float n = 0;
+ entity e = world;
+ while (( e = nmm_find_piece(e,minigame, teamn, pieceflags) ))
+ n++;
+ return n;
+}
+
+// required function, handle server side events
+float minigame_event_nmm(entity minigame, string event, ...)
+{
+ if ( event == "start" )
+ {
+ minigame.minigame_flags = NMM_TURN_PLACE|NMM_TURN_TEAM1;
+ nmm_init_tiles(minigame);
+ float i;
+ entity e;
+ for ( i = 0; i < 7; i++ )
+ {
+ e = msle_spawn(minigame,"minigame_board_piece");
+ e.team = 1;
+ e.minigame_flags = NMM_PIECE_HOME;
+ e = msle_spawn(minigame,"minigame_board_piece");
+ e.team = 2;
+ e.minigame_flags = NMM_PIECE_HOME;
+ }
+
+ return 1;
+ }
+ else if ( event == "end" )
+ {
+ nmm_kill_tiles(minigame);
+ }
+ else if ( event == "join" )
+ {
+ float n = 0;
+ entity e;
+ for ( e = minigame.minigame_players; e; e = e.list_next )
+ n++;
+ if ( n >= 2 )
+ return 0;
+ if ( minigame.minigame_players && minigame.minigame_players.team == 1 )
+ return 2;
+ return 1;
+ }
+ else if ( event == "cmd" )
+ {
+ entity e = ...(0,entity);
+ float argc = ...(1,float);
+ entity tile = world;
+ entity piece = world;
+ float move_ok = 0;
+
+ if ( e && argc >= 2 && argv(0) == "move" &&
+ ( minigame.minigame_flags & NMM_TURN_TEAM ) == e.team )
+ {
+ tile = nmm_find_tile(minigame,argv(1));
+ if ( !tile )
+ {
+ move_ok = 0;
+ }
+ else if ( minigame.minigame_flags & NMM_TURN_PLACE )
+ {
+ piece = nmm_find_piece(world,minigame,e.team,NMM_PIECE_HOME);
+ if ( !tile.nmm_tile_piece && piece )
+ {
+ tile.nmm_tile_piece = piece;
+ piece.minigame_flags = NMM_PIECE_BOARD;
+ piece.origin = tile.origin;
+ piece.SendFlags |= MINIG_SF_UPDATE;
+ move_ok = 1;
+ }
+ }
+ else if ( minigame.minigame_flags & NMM_TURN_MOVE )
+ {
+ if ( tile.nmm_tile_piece && tile.nmm_tile_piece.team == e.team )
+ {
+ piece = tile.nmm_tile_piece;
+ entity tile2 = nmm_find_tile(minigame,argv(2));
+ if ( tile2 && nmm_tile_adjacent(tile,tile2) && !tile2.nmm_tile_piece )
+ {
+ tile.nmm_tile_piece = world;
+ tile2.nmm_tile_piece = piece;
+ piece.origin = tile2.origin;
+ piece.SendFlags |= MINIG_SF_UPDATE;
+ tile = tile2;
+ move_ok = 1;
+ }
+ }
+
+ }
+ else if ( minigame.minigame_flags & NMM_TURN_FLY )
+ {
+ if ( tile.nmm_tile_piece && tile.nmm_tile_piece.team == e.team )
+ {
+ piece = tile.nmm_tile_piece;
+ entity tile2 = nmm_find_tile(minigame,argv(2));
+ if ( tile2 && !tile2.nmm_tile_piece )
+ {
+ tile.nmm_tile_piece = world;
+ tile2.nmm_tile_piece = piece;
+ piece.origin = tile2.origin;
+ piece.SendFlags |= MINIG_SF_UPDATE;
+ tile = tile2;
+ move_ok = 1;
+ }
+ }
+
+ }
+ else if ( minigame.minigame_flags & NMM_TURN_TAKE )
+ {
+ piece = tile.nmm_tile_piece;
+ if ( piece && piece.nmm_tile_piece.team != e.team )
+ {
+ tile.nmm_tile_piece = world;
+ piece.minigame_flags = NMM_PIECE_DEAD;
+ piece.SendFlags |= MINIG_SF_UPDATE;
+ move_ok = 1;
+ }
+ }
+
+ float nextteam = e.team % 2 + 1;
+ float npieces = nmm_count_pieces(minigame,nextteam,NMM_PIECE_HOME|NMM_PIECE_BOARD);
+
+ if ( npieces < 3 )
+ {
+ minigame.minigame_flags = NMM_TURN_WIN | e.team;
+ minigame.SendFlags |= MINIG_SF_UPDATE;
+ }
+ else if ( move_ok)
+ {
+ if ( !(minigame.minigame_flags & NMM_TURN_TAKE) && nmm_in_mill(tile) )
+ {
+ minigame.minigame_flags = NMM_TURN_TAKE|e.team;
+ float takemill = NMM_TURN_TAKEANY;
+ entity f = world;
+ while ( ( f = findentity(f,owner,minigame) ) )
+ if ( f.classname == "minigame_nmm_tile" && f.nmm_tile_piece &&
+ f.nmm_tile_piece.team == nextteam && !nmm_in_mill(f) )
+ {
+ takemill = 0;
+ break;
+ }
+ minigame.minigame_flags |= takemill;
+ }
+ else
+ {
+ if ( nmm_find_piece(world,minigame,nextteam,NMM_PIECE_HOME) )
+ minigame.minigame_flags = NMM_TURN_PLACE|nextteam;
+ else if ( npieces == 3 )
+ minigame.minigame_flags = NMM_TURN_FLY|nextteam;
+ else
+ {
+ minigame.minigame_flags = NMM_TURN_WIN|e.team;
+ entity f = world;
+ while ( ( f = findentity(f,owner,minigame) ) )
+ if ( f.classname == "minigame_nmm_tile" && f.nmm_tile_piece &&
+ f.nmm_tile_piece.team == nextteam && nmm_tile_canmove(f) )
+ {
+ minigame.minigame_flags = NMM_TURN_MOVE|nextteam;
+ break;
+ }
+ }
+ }
+ minigame.SendFlags |= MINIG_SF_UPDATE;
+ }
+ else
+ dprint("Invalid move: ",...(2,string),"\n");
+ return 1;
+ }
+ }
+ return 0;
+}
+
+#elif defined(CSQC)
+
+entity nmm_currtile;
+entity nmm_fromtile;
+
+vector nmm_boardpos;
+vector nmm_boardsize;
+
+// whether the given tile is a valid selection
+float nmm_valid_selection(entity tile)
+{
+ if ( ( tile.owner.minigame_flags & NMM_TURN_TEAM ) != minigame_self.team )
+ return 0; // not our turn
+ if ( tile.owner.minigame_flags & NMM_TURN_PLACE )
+ return !tile.nmm_tile_piece; // need to put a piece on an empty spot
+ if ( tile.owner.minigame_flags & NMM_TURN_MOVE )
+ {
+ if ( tile.nmm_tile_piece && tile.nmm_tile_piece.team == minigame_self.team &&
+ nmm_tile_canmove(tile) )
+ return 1; // movable tile
+ if ( nmm_fromtile ) // valid destination
+ return !tile.nmm_tile_piece && nmm_tile_adjacent(nmm_fromtile,tile);
+ return 0;
+ }
+ if ( tile.owner.minigame_flags & NMM_TURN_FLY )
+ {
+ if ( nmm_fromtile )
+ return !tile.nmm_tile_piece;
+ else
+ return tile.nmm_tile_piece && tile.nmm_tile_piece.team == minigame_self.team;
+ }
+ if ( tile.owner.minigame_flags & NMM_TURN_TAKE )
+ return tile.nmm_tile_piece && tile.nmm_tile_piece.team != minigame_self.team &&
+ ( (tile.owner.minigame_flags & NMM_TURN_TAKEANY) || !nmm_in_mill(tile) );
+ return 0;
+}
+
+// whether it should highlight valid tile selections
+float nmm_draw_avaliable(entity tile)
+{
+ if ( ( tile.owner.minigame_flags & NMM_TURN_TEAM ) != minigame_self.team )
+ return 0;
+ if ( (tile.owner.minigame_flags & NMM_TURN_TAKE) )
+ return 1;
+ if ( (tile.owner.minigame_flags & (NMM_TURN_FLY|NMM_TURN_MOVE)) && nmm_fromtile )
+ return !tile.nmm_tile_piece;
+ return 0;
+}
+
+// Required function, draw the game board
+void minigame_hud_board_nmm(vector pos, vector mySize)
+{
+ minigame_hud_fitsqare(pos, mySize);
+ nmm_boardpos = pos;
+ nmm_boardsize = mySize;
+ minigame_hud_simpleboard(pos,mySize,minigame_texture("nmm/board"));
+
+ vector tile_size = minigame_hud_denormalize_size('1 1 0'/7,pos,mySize);
+ vector tile_pos;
+ entity e;
+ FOREACH_MINIGAME_ENTITY(e)
+ {
+ if ( e.classname == "minigame_nmm_tile" )
+ {
+ tile_pos = minigame_hud_denormalize(e.origin,pos,mySize);
+
+ if ( e == nmm_fromtile )
+ {
+ minigame_drawpic_centered( tile_pos, minigame_texture("nmm/tile_active"),
+ tile_size, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL );
+ }
+ else if ( nmm_draw_avaliable(e) && nmm_valid_selection(e) )
+ {
+ minigame_drawpic_centered( tile_pos, minigame_texture("nmm/tile_available"),
+ tile_size, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL );
+ }
+
+ if ( e == nmm_currtile )
+ {
+ minigame_drawpic_centered( tile_pos, minigame_texture("nmm/tile_selected"),
+ tile_size, '1 1 1', panel_fg_alpha, DRAWFLAG_ADDITIVE );
+ }
+
+ if ( e.nmm_tile_piece )
+ {
+ minigame_drawpic_centered( tile_pos,
+ minigame_texture(strcat("nmm/piece",ftos(e.nmm_tile_piece.team))),
+ tile_size*0.8, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL );
+ }
+
+ //drawstring(tile_pos, e.netname, hud_fontsize, '1 0 0', 1, DRAWFLAG_NORMAL);
+ }
+ }
+
+ if ( active_minigame.minigame_flags & NMM_TURN_WIN )
+ {
+ vector winfs = hud_fontsize*2;
+ string playername = "";
+ FOREACH_MINIGAME_ENTITY(e)
+ if ( e.classname == "minigame_player" &&
+ e.team == (active_minigame.minigame_flags & NMM_TURN_TEAM) )
+ playername = GetPlayerName(e.minigame_playerslot-1);
+
+ vector win_pos = pos+eY*(mySize_y-winfs_y)/2;
+ vector win_sz;
+ win_sz = minigame_drawcolorcodedstring_wrapped(mySize_x,win_pos,
+ sprintf("%s^7 won the game!",playername),
+ winfs, 0, DRAWFLAG_NORMAL, 0.5);
+
+ drawfill(win_pos-eY*hud_fontsize_y,win_sz+2*eY*hud_fontsize_y,'1 1 1',0.5,DRAWFLAG_ADDITIVE);
+
+ minigame_drawcolorcodedstring_wrapped(mySize_x,win_pos,
+ sprintf("%s^7 won the game!",playername),
+ winfs, panel_fg_alpha, DRAWFLAG_NORMAL, 0.5);
+ }
+}
+
+// Required function, draw the game status panel
+void minigame_hud_status_nmm(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;
+
+ float player1x = 0;
+ float player2x = 0;
+ vector piece_sz = '48 48 0';
+ float piece_space = piece_sz_x + ( ts_x - 7 * piece_sz_x ) / 6;
+ vector mypos;
+ float piece_light = 1;
+ entity e = world;
+
+ mypos = pos;
+ if ( (active_minigame.minigame_flags&NMM_TURN_TEAM) == 2 )
+ mypos_y += player_fontsize_y + ts_y;
+ drawfill(mypos,eX*mySize_x+eY*player_fontsize_y,'1 1 1',0.5,DRAWFLAG_ADDITIVE);
+ mypos_y += player_fontsize_y;
+ drawfill(mypos,eX*mySize_x+eY*piece_sz_y,'1 1 1',0.25,DRAWFLAG_ADDITIVE);
+
+ 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,
+ GetPlayerName(e.minigame_playerslot-1),
+ player_fontsize, panel_fg_alpha, DRAWFLAG_NORMAL);
+ }
+ else if ( e.classname == "minigame_board_piece" )
+ {
+ mypos = pos;
+ mypos_y += player_fontsize_y;
+ if ( e.team == 2 )
+ {
+ mypos_x += player2x;
+ player2x += piece_space;
+ mypos_y += player_fontsize_y + ts_y;
+ }
+ else
+ {
+ mypos_x += player1x;
+ player1x += piece_space;
+ }
+ if ( e.minigame_flags == NMM_PIECE_HOME )
+ piece_light = 0.5;
+ else if ( e.minigame_flags == NMM_PIECE_BOARD )
+ piece_light = 1;
+ else
+ piece_light = 0.15;
+
+ drawpic(mypos, minigame_texture(strcat("nmm/piece",ftos(e.team))), piece_sz,
+ '1 1 1'*piece_light, panel_fg_alpha, DRAWFLAG_NORMAL );
+ }
+ }
+}
+
+// Make the correct move
+void nmm_make_move(entity minigame)
+{
+ if ( nmm_currtile )
+ {
+ if ( minigame.minigame_flags & (NMM_TURN_PLACE|NMM_TURN_TAKE) )
+ {
+ minigame_cmd("move ",nmm_currtile.netname);
+ nmm_fromtile = world;
+ }
+ else if ( (minigame.minigame_flags & (NMM_TURN_MOVE|NMM_TURN_FLY)) )
+ {
+ if ( nmm_fromtile == nmm_currtile )
+ {
+ nmm_fromtile = world;
+ }
+ else if ( nmm_currtile.nmm_tile_piece && nmm_currtile.nmm_tile_piece.team == minigame_self.team )
+ {
+ nmm_fromtile = nmm_currtile;
+ }
+ else if ( nmm_fromtile )
+ {
+ minigame_cmd("move ",nmm_fromtile.netname," ",nmm_currtile.netname);
+ nmm_fromtile = world;
+ }
+ }
+ }
+ else
+ nmm_fromtile = world;
+}
+
+string nmm_turn_to_string(float turnflags)
+{
+ if ( turnflags & NMM_TURN_WIN )
+ {
+ if ( (turnflags&NMM_TURN_TEAM) != minigame_self.team )
+ return _("You lost the game!");
+ return _("You win!");
+ }
+
+ if ( (turnflags&NMM_TURN_TEAM) != minigame_self.team )
+ return _("Wait for your opponent to make their move");
+ if ( turnflags & NMM_TURN_PLACE )
+ return _("Click on the game board to place your piece");
+ if ( turnflags & NMM_TURN_MOVE )
+ return _("You can select one of your pieces to move it in one of the surrounding places");
+ if ( turnflags & NMM_TURN_FLY )
+ return _("You can select one of your pieces to move it anywhere on the board");
+ if ( turnflags & NMM_TURN_TAKE )
+ return _("You can take one of the opponent's pieces");
+
+ return "";
+}
+
+// Required function, handle client events
+float minigame_event_nmm(entity minigame, string event, ...)
+{
+ if ( event == "activate" )
+ {
+ nmm_fromtile = world;
+ nmm_init_tiles(minigame);
+ minigame.message = nmm_turn_to_string(minigame.minigame_flags);
+ }
+ else if ( event == "deactivate" )
+ {
+ nmm_fromtile = world;
+ nmm_kill_tiles(minigame);
+ }
+ else if ( event == "key_pressed" && (minigame.minigame_flags&NMM_TURN_TEAM) == minigame_self.team )
+ {
+ switch ( ...(0,float) )
+ {
+ case K_RIGHTARROW:
+ case K_KP_RIGHTARROW:
+ if ( ! nmm_currtile )
+ nmm_currtile = nmm_find_tile(active_minigame,"a7");
+ else
+ {
+ string tileid = nmm_currtile.netname;
+ nmm_currtile = world;
+ while ( !nmm_currtile )
+ {
+ tileid = minigame_relative_tile(tileid,1,0,7,7);
+ nmm_currtile = nmm_find_tile(active_minigame,tileid);
+ }
+ }
+ return 1;
+ case K_LEFTARROW:
+ case K_KP_LEFTARROW:
+ if ( ! nmm_currtile )
+ nmm_currtile = nmm_find_tile(active_minigame,"g7");
+ else
+ {
+ string tileid = nmm_currtile.netname;
+ nmm_currtile = world;
+ while ( !nmm_currtile )
+ {
+ tileid = minigame_relative_tile(tileid,-1,0,7,7);
+ nmm_currtile = nmm_find_tile(active_minigame,tileid);
+ }
+ }
+ return 1;
+ case K_UPARROW:
+ case K_KP_UPARROW:
+ if ( ! nmm_currtile )
+ nmm_currtile = nmm_find_tile(active_minigame,"a1");
+ else
+ {
+ string tileid = nmm_currtile.netname;
+ nmm_currtile = world;
+ while ( !nmm_currtile )
+ {
+ tileid = minigame_relative_tile(tileid,0,1,7,7);
+ nmm_currtile = nmm_find_tile(active_minigame,tileid);
+ }
+ }
+ return 1;
+ case K_DOWNARROW:
+ case K_KP_DOWNARROW:
+ if ( ! nmm_currtile )
+ nmm_currtile = nmm_find_tile(active_minigame,"a7");
+ else
+ {
+ string tileid = nmm_currtile.netname;
+ nmm_currtile = world;
+ while ( !nmm_currtile )
+ {
+ tileid = minigame_relative_tile(tileid,0,-1,7,7);
+ nmm_currtile = nmm_find_tile(active_minigame,tileid);
+ }
+ }
+ return 1;
+ case K_ENTER:
+ case K_KP_ENTER:
+ case K_SPACE:
+ nmm_make_move(minigame);
+ return 1;
+ }
+ return 0;
+ }
+ else if ( event == "mouse_pressed" && ...(0,float) == K_MOUSE1 )
+ {
+ nmm_make_move(minigame);
+ return 1;
+ }
+ else if ( event == "mouse_moved" )
+ {
+ nmm_currtile = world;
+ vector tile_pos;
+ vector tile_size = minigame_hud_denormalize_size('1 1 0'/7,nmm_boardpos,nmm_boardsize);
+ entity e;
+ FOREACH_MINIGAME_ENTITY(e)
+ {
+ if ( e.classname == "minigame_nmm_tile" )
+ {
+ tile_pos = minigame_hud_denormalize(e.origin,nmm_boardpos,nmm_boardsize)-tile_size/2;
+ if ( minigame_hud_mouse_in(tile_pos, tile_size) && nmm_valid_selection(e) )
+ {
+ nmm_currtile = e;
+ break;
+ }
+ }
+ }
+ return 1;
+ }
+ else if ( event == "network_receive" )
+ {
+ if ( self.classname == "minigame_board_piece" && ( ...(1,float) & MINIG_SF_UPDATE ) )
+ {
+ entity e;
+ string tileid = "";
+ if ( self.minigame_flags & NMM_PIECE_BOARD )
+ tileid = minigame_tile_name(self.origin,7,7);
+ FOREACH_MINIGAME_ENTITY(e)
+ {
+ if ( e.classname == "minigame_nmm_tile" )
+ {
+ if ( e.nmm_tile_piece == self )
+ e.nmm_tile_piece = world;
+ if ( e.netname == tileid )
+ e.nmm_tile_piece = self;
+ }
+ }
+ }
+ else if ( self.classname == "minigame" && ( ...(1,float) & MINIG_SF_UPDATE ) )
+ {
+ self.message = nmm_turn_to_string(self.minigame_flags);
+ if ( self.minigame_flags & minigame_self.team )
+ minigame_prompt();
+ }
+ }
+
+ return 0;
+}
+
+#endif
--- /dev/null
+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;
+ }
+ 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;
+ }
+ 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;
+
+ // Don't allow more than 2 players
+ 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;
+ case "next":
+ ttt_next_match(minigame,...(0,entity));
+ 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 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;
+}
+
+
+#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;
+ }
+ 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;
+ 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;
+ 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;
+ 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;
+ case K_ENTER:
+ case K_KP_ENTER:
+ case K_SPACE:
+ ttt_make_move(minigame);
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+ }
+ case "mouse_pressed":
+ {
+ if(...(0,float) == K_MOUSE1)
+ {
+ ttt_make_move(minigame);
+ return TRUE;
+ }
+
+ 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;
+ }
+ 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;
+ }
+ case "menu_show":
+ {
+ HUD_MinigameMenu_CustomEntry(...(0,entity),_("Next Match"),"next");
+ HUD_MinigameMenu_CustomEntry(...(0,entity),_("Single Player"),"singleplayer");
+ 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;
+}
+
+#endif
\ No newline at end of file
--- /dev/null
+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);
+ #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;
+}
\ No newline at end of file
--- /dev/null
+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);
\ No newline at end of file
--- /dev/null
+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);
+
+ 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);
+
+ 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) )
+ 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 <minigame>\n");
+ sprint(self, " Start a new minigame session\n");
+ sprint(self, "Usage:^3 cmd minigame join <session>\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 <player>\n");
+ sprint(self, " Invite the given player to join you in a minigame\n");
+}
\ No newline at end of file
--- /dev/null
+
+
+/// 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);
#include "monster/zombie.qc"
#include "monster/spider.qc"
#include "monster/mage.qc"
+#ifndef MONSTERS_EXTRA
#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"
+#endif
--- /dev/null
+#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
+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);
+}
+
+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 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;
+ }
+ 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;
+ }
+ case MR_DEATH:
+ {
+ self.frame = (random() >= 0.5) ? afrit_anim_die1 : afrit_anim_die2;
+ self.superweapons_finished = time + 20;
+ 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
+
+ return TRUE;
+ }
+ case MR_PRECACHE:
+ {
+ return TRUE;
+ }
+ }
+
+ return TRUE;
+}
+
+#endif // SVQC
+#endif // REGISTER_MONSTER
--- /dev/null
+#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);
+ 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;
+
+ 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;
+ }
+ case MONSTER_ATTACK_RANGED:
+ {
+ // creeper has no ranged attacks
+ 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;
+ if(self.health > 0 && time > self.attack_finished_single) { self.colormod = '1 1 1'; }
+ return TRUE;
+ }
+ case MR_PAIN:
+ {
+ return TRUE;
+ }
+ case MR_DEATH:
+ {
+ self.frame = creeper_anim_die;
+ 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;
+ }
+ case MR_PRECACHE:
+ {
+ return TRUE;
+ }
+ }
+
+ return TRUE;
+}
+
+#endif // SVQC
+#endif // REGISTER_MONSTER
--- /dev/null
+#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);
+ }
+ case MONSTER_ATTACK_RANGED:
+ {
+ 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;
+}
+
+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;
+ }
+ case MR_PAIN:
+ {
+ self.pain_finished = time + 0.5;
+ self.frame = demon_anim_pain;
+ return TRUE;
+ }
+ case MR_DEATH:
+ {
+ self.frame = demon_anim_death;
+ 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;
+ }
+ case MR_PRECACHE:
+ {
+ return TRUE;
+ }
+ }
+
+ return TRUE;
+}
+
+#endif // SVQC
+#endif // REGISTER_MONSTER
--- /dev/null
+#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_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);
+
+ gren.flags = FL_PROJECTILE;
+
+ 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 FALSE;
+}
+
+void spawnfunc_monster_enforcer() { Monster_Spawn(MON_ENFORCER); }
+
+float M_Enforcer(float req)
+{
+ switch(req)
+ {
+ case MR_THINK:
+ {
+ 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;
+ }
+ case MR_DEATH:
+ {
+ self.frame = ((random() > 0.5) ? enforcer_anim_death1 : enforcer_anim_death2);
+ 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;
+ }
+ case MR_PRECACHE:
+ {
+ precache_sound(W_Sound("tag_impact"));
+ precache_sound(W_Sound("lasergun_fire"));
+ return TRUE;
+ }
+ }
+
+ return TRUE;
+}
+
+#endif // SVQC
+#endif // REGISTER_MONSTER
#ifdef REGISTER_MONSTER
REGISTER_MONSTER(
/* MON_##id */ MAGE,
-/* function */ m_mage,
+/* functions */ M_Mage, M_Mage_Attack,
/* spawnflags */ MON_FLAG_MELEE | MON_FLAG_RANGED,
/* mins,maxs */ '-36 -36 -24', '36 36 50',
/* model */ "mage.dpm",
#else
#ifdef SVQC
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;
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;
if(e.health <= 0)
return FALSE;
- if(DIFF_TEAM(e, self) && e != self.monster_owner)
+ if(DIFF_TEAM(e, self) && e != self.monster_follow)
return FALSE;
if(e.frozen)
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)
{
return FALSE;
}
-void mage_spike_explode()
+void M_Mage_Attack_Spike_Explode()
{
self.event_damage = func_null;
- sound(self, CH_SHOTS, "weapons/grenade_impact.wav", VOL_BASE, ATTEN_NORM);
+ sound(self, CH_SHOTS, W_Sound("grenade_impact"), VOL_BASE, ATTEN_NORM);
self.realowner.mage_spike = world;
- pointparticles(particleeffectnum("explosion_small"), self.origin, '0 0 0', 1);
+ Send_Effect(EFFECT_EXPLOSION_SMALL, self.origin, '0 0 0', 1);
RadiusDamage (self, self.realowner, (autocvar_g_monster_mage_attack_spike_damage), (autocvar_g_monster_mage_attack_spike_damage) * 0.5, (autocvar_g_monster_mage_attack_spike_radius), world, world, 0, DEATH_MONSTER_MAGE, other);
remove (self);
}
-void mage_spike_touch()
+void M_Mage_Attack_Spike_Touch()
{
PROJECTILE_TOUCH;
- mage_spike_explode();
+ M_Mage_Attack_Spike_Explode();
}
// copied from W_Seeker_Think
-void mage_spike_think()
+void M_Mage_Attack_Spike_Think()
{
entity e;
vector desireddir, olddir, newdir, eorg;
if (time > self.ltime || self.enemy.health <= 0 || self.owner.health <= 0)
{
self.projectiledeathtype |= HITTYPE_SPLASH;
- mage_spike_explode();
+ M_Mage_Attack_Spike_Explode();
}
spd = vlen(self.velocity);
UpdateCSQCProjectile(self);
}
-void mage_attack_spike()
+void M_Mage_Attack_Spike()
{
entity missile;
vector dir = normalize((self.enemy.origin + '0 0 10') - self.origin);
missile = spawn ();
missile.owner = missile.realowner = self;
- missile.think = mage_spike_think;
+ missile.think = M_Mage_Attack_Spike_Think;
missile.ltime = time + 7;
missile.nextthink = time;
missile.solid = SOLID_BBOX;
missile.velocity = dir * 400;
missile.avelocity = '300 300 300';
missile.enemy = self.enemy;
- missile.touch = mage_spike_touch;
+ missile.touch = M_Mage_Attack_Spike_Touch;
self.mage_spike = missile;
CSQCProjectile(missile, TRUE, PROJECTILE_MAGE_SPIKE, TRUE);
}
-void mage_heal()
+void M_Mage_Defend_Heal()
{
entity head;
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;
string fx = "";
{
pointparticles(particleeffectnum("healing_fx"), head.origin, '0 0 0', 1);
head.health = bound(0, head.health + (autocvar_g_monster_mage_heal_allies), head.max_health);
- if(!(head.spawnflags & MONSTERFLAG_INVINCIBLE))
+ if(!(head.spawnflags & MONSTERFLAG_INVINCIBLE) && head.sprite)
WaypointSprite_UpdateHealth(head.sprite, head.health);
}
}
{
self.frame = mage_anim_attack;
self.attack_finished_single = time + (autocvar_g_monster_mage_heal_delay);
+ self.anim_finished = time + 1.5;
}
}
-void mage_push()
+void M_Mage_Attack_Push()
{
- sound(self, CH_SHOTS, "weapons/tagexp1.wav", 1, ATTEN_NORM);
+ sound(self, CH_SHOTS, W_Sound("tagexp1"), 1, ATTEN_NORM);
RadiusDamage (self, self, (autocvar_g_monster_mage_attack_push_damage), (autocvar_g_monster_mage_attack_push_damage), (autocvar_g_monster_mage_attack_push_radius), world, world, (autocvar_g_monster_mage_attack_push_force), DEATH_MONSTER_MAGE, self.enemy);
pointparticles(particleeffectnum("TE_EXPLOSION"), self.origin, '0 0 0', 1);
self.attack_finished_single = time + (autocvar_g_monster_mage_attack_push_delay);
}
-void mage_teleport()
+void M_Mage_Attack_Teleport()
{
if(vlen(self.enemy.origin - self.origin) >= 500)
return;
self.attack_finished_single = time + 0.2;
}
-void mage_shield_remove()
+void M_Mage_Defend_Shield_Remove()
{
self.effects &= ~(EF_ADDITIVE | EF_BLUE);
- self.armorvalue = 0;
- self.m_armor_blockpercent = autocvar_g_monsters_armor_blockpercent;
+ self.armorvalue = autocvar_g_monsters_armor_blockpercent;
}
-void mage_shield()
+void M_Mage_Defend_Shield()
{
self.effects |= (EF_ADDITIVE | EF_BLUE);
- self.lastshielded = time + (autocvar_g_monster_mage_shield_delay);
- self.m_armor_blockpercent = (autocvar_g_monster_mage_shield_blockpercent);
- self.armorvalue = self.health;
- self.shield_ltime = time + (autocvar_g_monster_mage_shield_time);
+ self.mage_shield_delay = time + (autocvar_g_monster_mage_shield_delay);
+ self.armorvalue = (autocvar_g_monster_mage_shield_blockpercent);
+ self.mage_shield_time = time + (autocvar_g_monster_mage_shield_time);
self.frame = mage_anim_attack;
self.attack_finished_single = time + 1;
+ self.anim_finished = time + 1;
}
-float mage_attack(float attack_type)
+float M_Mage_Attack(float attack_type)
{
switch(attack_type)
{
{
if(random() <= 0.7)
{
- mage_push();
+ M_Mage_Attack_Push();
return TRUE;
}
{
if(random() <= 0.4)
{
- mage_teleport();
+ M_Mage_Attack_Teleport();
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 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)
{
entity head;
float need_help = FALSE;
- 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;
break;
if(self.health < (autocvar_g_monster_mage_heal_minhealth) || need_help)
if(time >= self.attack_finished_single)
if(random() < 0.5)
- mage_heal();
+ M_Mage_Defend_Heal();
- if(time >= self.shield_ltime && self.armorvalue)
- mage_shield_remove();
+ if(time >= self.mage_shield_time && self.armorvalue)
+ M_Mage_Defend_Shield_Remove();
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();
- 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;
}
case MR_DEATH:
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;
}
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;
}
}
}
#endif // SVQC
-#ifdef CSQC
-float m_mage(float req)
-{
- switch(req)
- {
- case MR_PRECACHE:
- {
- return TRUE;
- }
- }
-
- return TRUE;
-}
-
-#endif // CSQC
#endif // REGISTER_MONSTER
--- /dev/null
+#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
+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_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.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);
+
+ gren.angles = vectoangles (gren.velocity);
+ gren.flags = FL_PROJECTILE;
+
+ 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);
+
+ // 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;
+ }
+ 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 FALSE;
+}
+
+void spawnfunc_monster_ogre() { Monster_Spawn(MON_OGRE); }
+
+float M_Ogre(float req)
+{
+ switch(req)
+ {
+ case MR_THINK:
+ {
+ 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;
+ }
+ case MR_DEATH:
+ {
+ self.frame = ((random() >= 0.5) ? ogre_anim_death1 : ogre_anim_death2);
+ 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;
+ }
+ case MR_PRECACHE:
+ {
+ return TRUE;
+ }
+ }
+
+ return TRUE;
+}
+
+#endif // SVQC
+#endif // REGISTER_MONSTER
--- /dev/null
+#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);
+ }
+ case MONSTER_ATTACK_RANGED:
+ {
+ // rotfish has no ranged attack yet
+ 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;
+ }
+ case MR_PAIN:
+ {
+ self.pain_finished = 0.8;
+ self.frame = rotfish_anim_pain;
+ return TRUE;
+ }
+ case MR_DEATH:
+ {
+ self.frame = rotfish_anim_death;
+ 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;
+ }
+ case MR_PRECACHE:
+ {
+ return TRUE;
+ }
+ }
+
+ return TRUE;
+}
+
+#endif // SVQC
+#endif // REGISTER_MONSTER
--- /dev/null
+#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);
+ }
+ case MONSTER_ATTACK_RANGED:
+ {
+ // rottweiler has no ranged attack yet!
+ return FALSE;
+ }
+ }
+
+ return FALSE;
+}
+
+void spawnfunc_monster_rottweiler() { Monster_Spawn(MON_ROTTWEILER); }
+
+float M_Rottweiler(float req)
+{
+ switch(req)
+ {
+ case MR_THINK:
+ {
+ return TRUE;
+ }
+ case MR_PAIN:
+ {
+ if(random() <= 0.3)
+ {
+ self.pain_finished = time + 1.5;
+ self.frame = rottweiler_anim_pain;
+ }
+ return TRUE;
+ }
+ case MR_DEATH:
+ {
+ self.frame = (random() >= 0.5) ? rottweiler_anim_death1 : rottweiler_anim_death2;
+ 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;
+ }
+ case MR_PRECACHE:
+ {
+ return TRUE;
+ }
+ }
+
+ return TRUE;
+}
+
+#endif // SVQC
+#endif // REGISTER_MONSTER
--- /dev/null
+#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);
+}
+
+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 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;
+ }
+ case MR_PAIN:
+ {
+ self.pain_finished = time + 0.3;
+ self.frame = scrag_anim_pain;
+ 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;
+ }
+ 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;
+ }
+ case MR_PRECACHE:
+ {
+ return TRUE;
+ }
+ }
+
+ return TRUE;
+}
+
+#endif // SVQC
+#endif // REGISTER_MONSTER
#ifdef REGISTER_MONSTER
REGISTER_MONSTER(
/* MON_##id */ SHAMBLER,
-/* function */ m_shambler,
+/* functions */ M_Shambler, M_Shambler_Attack,
/* spawnflags */ MONSTER_SIZE_BROKEN | MON_FLAG_SUPERMONSTER | MON_FLAG_MELEE | MON_FLAG_RANGED,
/* mins,maxs */ '-41 -41 -31', '41 41 65',
/* model */ "shambler.mdl",
#else
#ifdef SVQC
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;
.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);
- tracebox(self.origin + v_forward * 50, self.mins * 0.5, self.maxs * 0.5, self.origin + v_forward * 500, MOVE_NORMAL, self);
+ // 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 * 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_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;
}
}
-void shambler_lightning_explode()
+void M_Shambler_Attack_Lightning_Explode()
{
entity head;
- sound(self, CH_SHOTS, "weapons/electro_impact.wav", VOL_BASE, ATTEN_NORM);
- pointparticles(particleeffectnum("electro_impact"), '0 0 0', '0 0 0', 1);
+ sound(self, CH_SHOTS, W_Sound("electro_impact"), VOL_BASE, ATTEN_NORM);
+ Send_Effect(EFFECT_ELECTRO_IMPACT, self.origin, '0 0 0', 1);
self.event_damage = func_null;
self.takedamage = DAMAGE_NO;
for(head = findradius(self.origin, (autocvar_g_monster_shambler_attack_lightning_radius_zap)); head; head = head.chain) if(head != self.realowner) if(head.takedamage)
{
te_csqc_lightningarc(self.origin, head.origin);
- Damage(head, self, self.realowner, (autocvar_g_monster_shambler_attack_lightning_damage) * Monster_SkillModifier(), DEATH_MONSTER_SHAMBLER_ZAP, head.origin, '0 0 0');
+ Damage(head, self, self.realowner, (autocvar_g_monster_shambler_attack_lightning_damage) * MONSTER_SKILLMOD(self), DEATH_MONSTER_SHAMBLER_ZAP, head.origin, '0 0 0');
}
self.think = SUB_Remove;
self.nextthink = time + 0.2;
}
-void shambler_lightning_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
+void M_Shambler_Attack_Lightning_Damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
{
if (self.health <= 0)
return;
W_PrepareExplosionByDamage(attacker, self.use);
}
-void shambler_lightning_touch()
+void M_Shambler_Attack_Lightning_Touch()
{
PROJECTILE_TOUCH;
self.use ();
}
-void shambler_lightning_think()
+void M_Shambler_Attack_Lightning_Think()
{
self.nextthink = time;
if (time > self.cnt)
{
other = world;
- shambler_lightning_explode();
+ M_Shambler_Attack_Lightning_Explode();
return;
}
}
-void shambler_lightning()
+void M_Shambler_Attack_Lightning()
{
entity gren;
gren.cnt = time + 5;
gren.nextthink = time;
- gren.think = shambler_lightning_think;
- gren.use = shambler_lightning_explode;
- gren.touch = shambler_lightning_touch;
+ gren.think = M_Shambler_Attack_Lightning_Think;
+ gren.use = M_Shambler_Attack_Lightning_Explode;
+ gren.touch = M_Shambler_Attack_Lightning_Touch;
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.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);
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;
}
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;
}
- 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 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:
{
- 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;
}
case MR_DEATH:
{
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;
}
case MR_PRECACHE:
{
- precache_model("models/monsters/shambler.mdl");
return TRUE;
}
}
}
#endif // SVQC
-#ifdef CSQC
-float m_shambler(float req)
-{
- switch(req)
- {
- case MR_PRECACHE:
- {
- return TRUE;
- }
- }
-
- return TRUE;
-}
-
-#endif // CSQC
#endif // REGISTER_MONSTER
--- /dev/null
+#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
+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;
+}
+
+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;
+ }
+ case MR_PAIN:
+ {
+ return TRUE;
+ }
+ case MR_DEATH:
+ {
+ // KABOOM!
+ self.frame = 0;
+ defer(0.05, M_Spawn_Attack_Explode); // simply defer to prevent recursion
+ 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;
+ }
+ case MR_PRECACHE:
+ {
+ return TRUE;
+ }
+ }
+
+ return TRUE;
+}
+
+#endif // SVQC
+#endif // REGISTER_MONSTER
#ifdef REGISTER_MONSTER
REGISTER_MONSTER(
/* MON_##id */ SPIDER,
-/* function */ m_spider,
-/* spawnflags */ MON_FLAG_MELEE | MON_FLAG_RANGED,
+/* functions */ M_Spider, M_Spider_Attack,
+/* spawnflags */ MON_FLAG_MELEE | MON_FLAG_RANGED | MON_FLAG_RIDE,
/* mins,maxs */ '-18 -18 -25', '18 18 30',
/* model */ "spider.dpm",
/* netname */ "spider",
#else
#ifdef SVQC
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;
.float spider_web_delay;
-void spider_web_explode()
+void M_Spider_Attack_Web_Explode()
{
entity e;
if(self)
{
- pointparticles(particleeffectnum("electro_impact"), self.origin, '0 0 0', 1);
+ Send_Effect(EFFECT_ELECTRO_IMPACT, self.origin, '0 0 0', 1);
RadiusDamage(self, self.realowner, 0, 0, 25, world, world, 25, self.projectiledeathtype, world);
- for(e = findradius(self.origin, 25); e; e = e.chain) if(e != self) if(e.takedamage && e.deadflag == DEAD_NO) if(e.health > 0) if(e.monsterid != MON_SPIDER)
+ for(e = findradius(self.origin, 25); e; e = e.chain) if(e != self) if(e.takedamage && e.deadflag == DEAD_NO) if(e.health > 0) if(e.monsterid != self.realowner.monsterid)
e.spider_slowness = time + (autocvar_g_monster_spider_attack_web_damagetime);
remove(self);
}
}
-void spider_web_touch()
+void M_Spider_Attack_Web_Touch()
{
PROJECTILE_TOUCH;
- spider_web_explode();
+ M_Spider_Attack_Web_Explode();
}
-void spider_shootweb()
+void M_Spider_Attack_Web()
{
monster_makevectors(self.enemy);
- sound(self, CH_SHOTS, "weapons/electro_fire2.wav", VOL_BASE, ATTEN_NORM);
+ sound(self, CH_SHOTS, W_Sound("electro_fire2"), VOL_BASE, ATTEN_NORM);
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_dodgerating = 0;
//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);
- 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;
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_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:
{
{
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 FALSE;
}
-void spawnfunc_monster_spider()
-{
- self.classname = "monster_spider";
-
- if(!monster_initialize(MON_SPIDER)) { remove(self); return; }
-}
+void spawnfunc_monster_spider() { Monster_Spawn(MON_SPIDER); }
-float m_spider(float req)
+float M_Spider(float req)
{
switch(req)
{
case MR_THINK:
{
- 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;
}
case MR_DEATH:
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;
}
case MR_PRECACHE:
{
- precache_model("models/monsters/spider.dpm");
- precache_sound ("weapons/electro_fire2.wav");
+ precache_sound (W_Sound("electro_fire2"));
return TRUE;
}
}
}
#endif // SVQC
-#ifdef CSQC
-float m_spider(float req)
-{
- switch(req)
- {
- case MR_PRECACHE:
- {
- return TRUE;
- }
- }
-
- return TRUE;
-}
-
-#endif // CSQC
#endif // REGISTER_MONSTER
--- /dev/null
+#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
+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);
+ else
+ 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);
+}
+
+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);
+ }
+ 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 (self.vore_spike) ? 2 : 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;
+ }
+ case MR_PAIN:
+ {
+ self.pain_finished = time + 0.4;
+ self.frame = vore_anim_pain;
+ return TRUE;
+ }
+ case MR_DEATH:
+ {
+ self.frame = vore_anim_death;
+ 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;
+ }
+ case MR_PRECACHE:
+ {
+ precache_sound (W_Sound("grenade_impact"));
+ precache_sound (W_Sound("tagexp1"));
+ return TRUE;
+ }
+ }
+
+ return TRUE;
+}
+
+#endif // SVQC
+#endif // REGISTER_MONSTER
#ifdef REGISTER_MONSTER
REGISTER_MONSTER(
/* MON_##id */ WYVERN,
-/* function */ m_wyvern,
-/* spawnflags */ MONSTER_TYPE_FLY | MONSTER_SIZE_BROKEN | MON_FLAG_RANGED,
+/* functions */ M_Wyvern, M_Wyvern_Attack,
+/* spawnflags */ MONSTER_TYPE_FLY | MONSTER_SIZE_BROKEN | MON_FLAG_RANGED | MON_FLAG_RIDE,
/* mins,maxs */ '-20 -20 -58', '20 20 20',
/* model */ "wizard.mdl",
/* netname */ "wyvern",
#else
#ifdef SVQC
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;
const float wyvern_anim_pain = 3;
const float wyvern_anim_death = 4;
-void wyvern_fireball_explode()
+void M_Wyvern_Attack_Fireball_Explode()
{
entity e;
if(self)
{
- pointparticles(particleeffectnum("fireball_explode"), self.origin, '0 0 0', 1);
+ Send_Effect(EFFECT_FIREBALL_EXPLODE, self.origin, '0 0 0', 1);
RadiusDamage(self, self.realowner, (autocvar_g_monster_wyvern_attack_fireball_damage), (autocvar_g_monster_wyvern_attack_fireball_edgedamage), (autocvar_g_monster_wyvern_attack_fireball_force), world, world, (autocvar_g_monster_wyvern_attack_fireball_radius), self.projectiledeathtype, world);
for(e = world; (e = findfloat(e, takedamage, DAMAGE_AIM)); ) if(vlen(e.origin - self.origin) <= (autocvar_g_monster_wyvern_attack_fireball_radius))
- Fire_AddDamage(e, self, 5 * Monster_SkillModifier(), (autocvar_g_monster_wyvern_attack_fireball_damagetime), self.projectiledeathtype);
+ Fire_AddDamage(e, self, 5 * MONSTER_SKILLMOD(self), (autocvar_g_monster_wyvern_attack_fireball_damagetime), self.projectiledeathtype);
remove(self);
}
}
-void wyvern_fireball_touch()
+void M_Wyvern_Attack_Fireball_Touch()
{
PROJECTILE_TOUCH;
- wyvern_fireball_explode();
+ M_Wyvern_Attack_Fireball_Explode();
}
-void wyvern_fireball()
+void M_Wyvern_Attack_Fireball()
{
entity missile = spawn();
vector dir = normalize((self.enemy.origin + '0 0 10') - self.origin);
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);
}
-float wyvern_attack(float attack_type)
+float M_Wyvern_Attack(float attack_type)
{
switch(attack_type)
{
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 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:
{
- 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;
}
case MR_DEATH:
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;
}
case MR_PRECACHE:
{
- precache_model("models/monsters/wizard.mdl");
return TRUE;
}
}
}
#endif // SVQC
-#ifdef CSQC
-float m_wyvern(float req)
-{
- switch(req)
- {
- case MR_PRECACHE:
- {
- return TRUE;
- }
- }
-
- return TRUE;
-}
-
-#endif // CSQC
#endif // REGISTER_MONSTER
#ifdef REGISTER_MONSTER
REGISTER_MONSTER(
/* MON_##id */ ZOMBIE,
-/* function */ m_zombie,
-/* spawnflags */ MON_FLAG_MELEE,
+/* functions */ M_Zombie, M_Zombie_Attack,
+/* spawnflags */ MON_FLAG_MELEE | MON_FLAG_RIDE,
/* mins,maxs */ '-18 -18 -25', '18 18 47',
/* model */ "zombie.dpm",
/* netname */ "zombie",
#else
#ifdef SVQC
float autocvar_g_monster_zombie_health;
+var float autocvar_g_monster_zombie_damageforcescale = 0.55;
float autocvar_g_monster_zombie_attack_melee_damage;
float autocvar_g_monster_zombie_attack_melee_delay;
float autocvar_g_monster_zombie_attack_leap_damage;
const float zombie_anim_runforwardright = 29;
const float zombie_anim_spawn = 30;
-void zombie_attack_leap_touch()
+void M_Zombie_Attack_Leap_Touch()
{
if (self.health <= 0)
return;
{
angles_face = vectoangles(self.moveto - self.origin);
angles_face = normalize(angles_face) * (autocvar_g_monster_zombie_attack_leap_force);
- Damage(other, self, self, (autocvar_g_monster_zombie_attack_leap_damage) * Monster_SkillModifier(), DEATH_MONSTER_ZOMBIE_JUMP, other.origin, angles_face);
- self.touch = MonsterTouch; // instantly turn it off to stop damage spam
+ Damage(other, self, self, (autocvar_g_monster_zombie_attack_leap_damage) * MONSTER_SKILLMOD(self), DEATH_MONSTER_ZOMBIE_JUMP, other.origin, angles_face);
+ self.touch = Monster_Touch; // instantly turn it off to stop damage spam
+ self.state = 0;
}
if (trace_dphitcontents)
- self.touch = MonsterTouch;
+ {
+ self.state = 0;
+ self.touch = Monster_Touch;
+ }
}
-void zombie_blockend()
+void M_Zombie_Defend_Block_End()
{
if(self.health <= 0)
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;
}
-float zombie_attack(float attack_type)
+float M_Zombie_Attack(float attack_type)
{
switch(attack_type)
{
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_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:
{
makevectors(self.angles);
- return monster_leap(zombie_anim_attackleap, zombie_attack_leap_touch, v_forward * (autocvar_g_monster_zombie_attack_leap_speed) + '0 0 200', (autocvar_g_monster_zombie_attack_leap_delay));
+ return Monster_Attack_Leap(zombie_anim_attackleap, M_Zombie_Attack_Leap_Touch, v_forward * (autocvar_g_monster_zombie_attack_leap_speed) + '0 0 200', (autocvar_g_monster_zombie_attack_leap_delay));
}
}
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;
+ }
+ case MR_PAIN:
+ {
+ self.pain_finished = time + 0.34;
+ self.frame = (random() >= 0.5) ? zombie_anim_painfront1 : zombie_anim_painfront2;
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;
}
case MR_SETUP:
{
if(!self.health) self.health = (autocvar_g_monster_zombie_health);
+ if(!self.speed) { self.speed = (autocvar_g_monster_zombie_speed_walk); }
+ if(!self.speed2) { self.speed2 = (autocvar_g_monster_zombie_speed_run); }
+ if(!self.stopspeed) { self.stopspeed = (autocvar_g_monster_zombie_speed_stop); }
+
+ self.m_anim_walk = zombie_anim_runforward;
+ self.m_anim_run = zombie_anim_runforward;
+ self.m_anim_idle = zombie_anim_idle;
if(self.spawnflags & MONSTERFLAG_NORESPAWN)
self.spawnflags &= ~MONSTERFLAG_NORESPAWN; // zombies always respawn
self.spawnflags |= MONSTER_RESPAWN_DEATHPOINT;
self.monster_loot = spawnfunc_item_health_medium;
- self.monster_attackfunc = zombie_attack;
self.frame = zombie_anim_spawn;
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;
}
case MR_PRECACHE:
{
- precache_model("models/monsters/zombie.dpm");
return TRUE;
}
}
}
#endif // SVQC
-#ifdef CSQC
-float m_zombie(float req)
-{
- switch(req)
- {
- case MR_PRECACHE:
- {
- return TRUE;
- }
- }
-
- return TRUE;
-}
-
-#endif // CSQC
#endif // REGISTER_MONSTER
entity monster_info[MON_MAXCOUNT];
entity dummy_monster_info;
-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)
{
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;
e.netname = shortname;
e.monster_name = mname;
e.monster_func = func;
+ e.monster_attackfunc = attackfunc;
e.mdl = modelname;
+ if(fexists(strcat("models/monsters/", noext, "_new", ext)))
+ {
+ e.mdl = strcat(noext, "_new", ext);
+ noext = strcat(noext, "_new");
+ }
e.spawnflags = monsterflags;
e.mins = min_s;
e.maxs = max_s;
- e.model = strzone(strcat("models/monsters/", modelname));
+ e.model = strzone(strcat("models/monsters/", e.mdl));
+
+#ifndef MENUQC
+ precache_model(e.model);
+ func(MR_PRECACHE); // it had to be done eventually
+
+ string trymodel; // check 5 skins
+ trymodel = sprintf("models/monsters/%s_%d%s", noext, 1, ext);
+ if(fexists(trymodel)) { precache_model(trymodel); }
+ trymodel = sprintf("models/monsters/%s_%d%s", noext, 2, ext);
+ if(fexists(trymodel)) { precache_model(trymodel); }
+ trymodel = sprintf("models/monsters/%s_%d%s", noext, 3, ext);
+ if(fexists(trymodel)) { precache_model(trymodel); }
+ trymodel = sprintf("models/monsters/%s_%d%s", noext, 4, ext);
+ if(fexists(trymodel)) { precache_model(trymodel); }
+ trymodel = sprintf("models/monsters/%s_%d%s", noext, 5, ext);
+ if(fexists(trymodel)) { precache_model(trymodel); }
+#endif
}
float m_null(float dummy) { return 0; }
void register_monsters_done()
dummy_monster_info.netname = "";
dummy_monster_info.monster_name = "Monster";
dummy_monster_info.monster_func = m_null;
+ dummy_monster_info.monster_attackfunc = m_null;
dummy_monster_info.mdl = "";
dummy_monster_info.mins = '-0 -0 -0';
dummy_monster_info.maxs = '0 0 0';
#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
-// functions:
+// functions
entity get_monsterinfo(float id);
// special spawn flags
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
-// 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;
// other useful macros
#define MON_ACTION(monstertype,mrequest) (get_monsterinfo(monstertype)).monster_func(mrequest)
-#define M_NAME(monstertype) (get_monsterinfo(monstertype)).monster_name
// =====================
// 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;
+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;
-#define REGISTER_MONSTER_2(id,func,monsterflags,min_s,max_s,modelname,shortname,mname) \
+#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); \
++MON_COUNT; \
- register_monster(id,func,monsterflags,min_s,max_s,modelname,shortname,mname); \
+ register_monster(id,func,attackfunc,monsterflags,min_s,max_s,modelname,shortname,mname); \
} \
ACCUMULATE_FUNCTION(RegisterMonsters, RegisterMonsters_##id)
-#ifdef MENUQC
-#define REGISTER_MONSTER(id,func,monsterflags,min_s,max_s,modelname,shortname,mname) \
- REGISTER_MONSTER_2(MON_##id,m_null,monsterflags,min_s,max_s,modelname,shortname,mname)
+#ifdef SVQC
+#define REGISTER_MONSTER(id,func,attackfunc,monsterflags,min_s,max_s,modelname,shortname,mname) \
+ REGISTER_MONSTER_2(MON_##id,func,attackfunc,monsterflags,min_s,max_s,modelname,shortname,mname)
+#elif defined(CSQC)
+ #define REGISTER_MONSTER(id,func,attackfunc,monsterflags,min_s,max_s,modelname,shortname,mname) \
+ REGISTER_MONSTER_2(MON_##id,m_null,m_null,monsterflags,min_s,max_s,modelname,shortname,mname)
#else
-#define REGISTER_MONSTER(id,func,monsterflags,min_s,max_s,modelname,shortname,mname) \
- REGISTER_MONSTER_2(MON_##id,func,monsterflags,min_s,max_s,modelname,shortname,mname)
+#define REGISTER_MONSTER(id,func,attackfunc,monsterflags,min_s,max_s,modelname,shortname,mname) \
+ REGISTER_MONSTER_2(MON_##id,m_null,m_null,monsterflags,min_s,max_s,modelname,shortname,mname)
#endif
#include "all.qh"
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;
- if(!respwn)
- e.spawnflags |= MONSTERFLAG_NORESPAWN;
-
- if(invincible)
- e.spawnflags |= MONSTERFLAG_INVINCIBLE;
+ if(!respwn) { e.spawnflags |= MONSTERFLAG_NORESPAWN; }
+ if(invincible) { e.spawnflags |= MONSTERFLAG_INVINCIBLE; }
setorigin(e, orig);
{
RandomSelection_Init();
for(i = MON_FIRST; i <= MON_LAST; ++i)
- RandomSelection_Add(world, 0, (get_monsterinfo(i)).netname, 1, 1);
+ RandomSelection_Add(world, i, string_null, 1, 1);
- monster = RandomSelection_chosen_string;
+ monster_id = RandomSelection_chosen_float;
}
-
- if(monster != "")
+ else if(monster != "")
{
float found = 0;
entity mon;
}
}
if(!found)
- monster = (get_monsterinfo(MON_FIRST)).netname;
+ monster_id = ((monster_id > 0) ? monster_id : MON_FIRST);
}
- if(monster == "")
- if(monster_id)
- monster = (get_monsterinfo(monster_id)).netname;
-
e.realowner = spawnedby;
if(moveflag)
e.team = spawnedby.team; // colors handled in spawn code
if(autocvar_g_monsters_owners)
- e.monster_owner = own; // using .owner makes the monster non-solid for its master
+ e.monster_follow = own; // using .owner makes the monster non-solid for its master
- e.angles = spawnedby.angles;
+ e.angles_y = spawnedby.angles_y;
}
-
- //monster = strcat("$ spawnfunc_monster_", monster);
+ // Monster_Spawn checks if monster is valid
entity oldself = self;
self = e;
- monster_initialize(monster_id);
+ Monster_Spawn(monster_id);
self = oldself;
- //target_spawn_edit_entity(e, monster, world, world, world, world, world);
-
return e;
}
-// =========================
-// SVQC Monster Properties
-// =========================
+// ==========
+// Monsters
+// ==========
+void monsters_setstatus()
+{
+ self.stat_monsters_total = monsters_total;
+ self.stat_monsters_killed = monsters_killed;
+}
+
void monster_dropitem()
{
if(!self.candrop || !self.monster_loot)
}
}
-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
+// ===============
- if(!autocvar_g_monsters_typefrag)
- if(targ.BUTTON_CHAT)
- return FALSE; // no typefragging!
+float Monster_ValidTarget(entity mon, entity player)
+{
+ // ensure we're not checking nonexistent monster/target
+ 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;
+ }
- 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;
- 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; }
}
- 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
{
if(head.monster_attack)
- if(monster_isvalidtarget(head, ent))
+ if(Monster_ValidTarget(mon, head))
{
// if it's a player, use the view origin as reference (stolen from RadiusDamage functions in g_damage.qc)
vector head_center = CENTER_OR_VIEWOFS(head);
- //vector head_center = WarpZone_UnTransformOrigin(head, CENTER_OR_VIEWOFS(head));
- vector ent_center = CENTER_OR_VIEWOFS(ent);
+ vector ent_center = CENTER_OR_VIEWOFS(mon);
- traceline(ent_center, head_center, MOVE_NORMAL, ent);
-
- if(trace_ent == head)
if(closest_target)
{
vector closest_target_center = CENTER_OR_VIEWOFS(closest_target);
- //vector closest_target_center = WarpZone_UnTransformOrigin(closest_target, CENTER_OR_VIEWOFS(closest_target));
if(vlen(ent_center - head_center) < vlen(ent_center - closest_target_center))
{ closest_target = head; }
}
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
+ 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)
return strcat(m, ".", fil);
}
-void PrecacheMonsterSounds(string f)
+void Monster_Sound_Precache(string f)
{
float fh;
string s;
fclose(fh);
}
-void precache_monstersounds()
+void Monster_Sounds_Precache()
{
string m = (get_monsterinfo(self.monsterid)).model;
float globhandle, n, i;
{
//print(search_getfilename(globhandle, i), "\n");
f = search_getfilename(globhandle, i);
- PrecacheMonsterSounds(f);
+ Monster_Sound_Precache(f);
}
search_end(globhandle);
}
-void ClearMonsterSounds()
+void Monster_Sounds_Clear()
{
#define _MSOUND(m) if(self.monstersound_##m) { strunzone(self.monstersound_##m); self.monstersound_##m = string_null; }
ALLMONSTERSOUNDS
#undef _MSOUND
}
-.string GetMonsterSoundSampleField(string type)
+.string Monster_Sound_SampleField(string type)
{
GetMonsterSoundSampleField_notFound = 0;
switch(type)
return string_null;
}
-float LoadMonsterSounds(string f, float first)
+float Monster_Sounds_Load(string f, float first)
{
float fh;
string s;
if(fh < 0)
{
dprint("Monster sound file not found: ", f, "\n");
- return 0;
+ return FALSE;
}
while((s = fgets(fh)))
{
if(tokenize_console(s) != 3)
continue;
- field = GetMonsterSoundSampleField(argv(0));
+ field = Monster_Sound_SampleField(argv(0));
if(GetMonsterSoundSampleField_notFound)
continue;
if(self.field)
self.field = strzone(strcat(argv(1), " ", argv(2)));
}
fclose(fh);
- return 1;
+ return TRUE;
}
.float 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; }
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;
+}
+
+float Monster_Attack_Leap_Check(vector vel)
+{
+ if(self.state)
+ return FALSE; // already attacking
+ if(!(self.flags & FL_ONGROUND))
+ return FALSE; // not on the ground
+ if(self.health <= 0)
+ return FALSE; // called when dead?
+ if(time < self.attack_finished_single)
+ 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 TRUE;
+}
+
+float Monster_Attack_Leap(float anm, void() touchfunc, vector vel, float animtime)
+{
+ if(!Monster_Attack_Leap_Check(vel))
+ 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;
}
-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; }
+
+ float targ_vlen = vlen(targ.origin - e.origin);
- if(dostop)
+ 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);
+ 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);
+ 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;
}
}
-float Monster_CanRespawn(entity ent)
+float Monster_Respawn_Check()
{
- 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;
}
-float monster_initialize(float mon_id);
-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;
}
}
-float Monster_CanJump (vector vel)
-{
- if(self.state)
- return FALSE; // already attacking
- if(!(self.flags & FL_ONGROUND))
- return FALSE; // not on the ground
- if(self.health <= 0)
- return FALSE; // called when dead?
- if(time < self.attack_finished_single)
- 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 TRUE;
-}
-
-float monster_leap (float anm, void() touchfunc, vector vel, float anim_finished)
-{
- if(!Monster_CanJump(vel))
- return FALSE;
-
- self.frame = anm;
- self.state = MONSTER_STATE_ATTACK_LEAP;
- self.touch = touchfunc;
- self.origin_z += 1;
- self.velocity = vel;
- self.flags &= ~FL_ONGROUND;
-
- self.attack_finished_single = time + anim_finished;
-
- return TRUE;
-}
-
-void monster_checkattack(entity e, entity targ)
-{
- if(e == world)
- return;
- if(targ == world)
- return;
-
- if(!e.monster_attackfunc)
- return;
-
- if(time < e.attack_finished_single)
- return;
-
- if(vlen(targ.origin - e.origin) <= e.attack_range)
- if(e.monster_attackfunc(MONSTER_ATTACK_MELEE))
- {
- MonsterSound(monstersound_melee, 0, FALSE, CH_VOICE);
- return;
- }
-
- if(vlen(targ.origin - e.origin) > e.attack_range)
- if(e.monster_attackfunc(MONSTER_ATTACK_RANGED))
- {
- MonsterSound(monstersound_ranged, 0, FALSE, CH_VOICE);
- return;
- }
-}
-
-void monster_use ()
+void Monster_Use()
{
- if(!self.enemy)
- if(self.health > 0)
- if(monster_isvalidtarget(activator, self))
- self.enemy = activator;
+ if(Monster_ValidTarget(self, activator)) { self.enemy = activator; }
}
-.float last_trace;
-.float last_enemycheck; // for checking enemy
-vector monster_pickmovetarget(entity targ)
+vector Monster_Move_Target(entity targ)
{
// enemy is always preferred target
if(self.enemy)
targ_origin = WarpZone_RefSys_TransformOrigin(self.enemy, self, targ_origin); // origin of target as seen by the monster (us)
WarpZone_TraceLine(self.origin, targ_origin, MOVE_NOMONSTERS, self);
- if((self.enemy == world)
- || (self.enemy.deadflag != DEAD_NO || self.enemy.health < 1)
- || (self.enemy.frozen)
+ if( (self.enemy == world)
+ || (Player_Trapped(self.enemy))
|| (self.enemy.flags & FL_NOTARGET)
|| (self.enemy.alpha < 0.5)
|| (self.enemy.takedamage == DAMAGE_NO)
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);
switch(self.monster_moveflags)
{
- case MONSTER_MOVE_OWNER:
+ case MONSTER_MOVE_FOLLOW:
{
- self.monster_movestate = MONSTER_MOVE_OWNER;
+ self.monster_movestate = MONSTER_MOVE_FOLLOW;
self.last_trace = time + 0.3;
- return (self.monster_owner) ? self.monster_owner.origin : self.origin;
+ return (self.monster_follow) ? self.monster_follow.origin : self.origin;
}
case MONSTER_MOVE_SPAWNLOC:
{
}
case MONSTER_MOVE_NOMOVE:
{
- self.monster_movestate = MONSTER_MOVE_NOMOVE;
- self.last_trace = time + 2;
+ if(self.monster_moveto)
+ {
+ self.last_trace = time + 0.5;
+ return self.monster_moveto;
+ }
+ else
+ {
+ self.monster_movestate = MONSTER_MOVE_NOMOVE;
+ self.last_trace = time + 2;
+ }
return self.origin;
}
default:
vector pos;
self.monster_movestate = MONSTER_MOVE_WANDER;
- if(targ)
+ if(self.monster_moveto)
+ {
+ self.last_trace = time + 0.5;
+ pos = self.monster_moveto;
+ }
+ else if(targ)
{
self.last_trace = time + 0.5;
pos = targ.origin;
}
}
-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 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;
//mon.angles = vectoangles(mon.velocity);
}
-void monster_move(float runspeed, float walkspeed, float stopspeed, float manim_run, float manim_walk, float manim_idle)
+void Monster_Move(float runspeed, float walkspeed, float stpspeed, float manim_run, float manim_walk, float manim_idle)
{
- //fixedmakevectors(self.angles);
-
- if(self.target2)
- self.goalentity = find(world, targetname, self.target2);
+ if(self.target2) { self.goalentity = find(world, targetname, self.target2); }
entity targ;
self.health = max(1, self.revive_progress * self.max_health);
self.iceblock.alpha = bound(0.2, 1 - self.revive_progress, 1);
- WaypointSprite_UpdateHealth(self.sprite, self.health);
+ if(!(self.spawnflags & MONSTERFLAG_INVINCIBLE) && self.sprite)
+ WaypointSprite_UpdateHealth(self.sprite, self.health);
- movelib_beak_simple(stopspeed);
+ movelib_beak_simple(stpspeed);
self.frame = manim_idle;
self.enemy = world;
self.revive_progress = bound(0, self.revive_progress - self.ticrate * self.revive_speed, 1);
self.health = max(0, autocvar_g_nades_ice_health + (self.max_health-autocvar_g_nades_ice_health) * self.revive_progress );
- WaypointSprite_UpdateHealth(self.sprite, self.health);
+ if(!(self.spawnflags & MONSTERFLAG_INVINCIBLE) && self.sprite)
+ WaypointSprite_UpdateHealth(self.sprite, self.health);
- movelib_beak_simple(stopspeed);
+ movelib_beak_simple(stpspeed);
self.frame = manim_idle;
self.enemy = world;
{
if(time >= self.last_trace)
{
- self.fish_wasdrowning = TRUE;
self.last_trace = time + 0.4;
Damage (self, world, world, 2, DEATH_DROWN, self.origin, '0 0 0');
return;
}
- else if(self.fish_wasdrowning)
+ else if(self.movetype == MOVETYPE_BOUNCE)
{
- self.fish_wasdrowning = FALSE;
self.angles_x = 0;
self.movetype = MOVETYPE_WALK;
}
monster_speed_run = runspeed;
monster_speed_walk = walkspeed;
- if(MUTATOR_CALLHOOK(MonsterMove) || gameover || self.draggedby != world || (round_handler_IsActive() && !round_handler_IsRoundStarted()) || time < game_starttime || (autocvar_g_campaign && !campaign_bots_may_start) || time < self.spawn_time)
+ if((MUTATOR_CALLHOOK(MonsterMove))
+ || (gameover)
+ || (self.draggedby != world)
+ || (round_handler_IsActive() && !round_handler_IsRoundStarted())
+ || (time < game_starttime)
+ || (autocvar_g_campaign && !campaign_bots_may_start)
+ || (time < self.spawn_time)
+ )
{
runspeed = walkspeed = 0;
if(time >= self.spawn_time)
self.frame = manim_idle;
- movelib_beak_simple(stopspeed);
+ movelib_beak_simple(stpspeed);
return;
}
targ = monster_target;
- runspeed = bound(0, monster_speed_run * Monster_SkillModifier(), runspeed * 2); // limit maxspeed to prevent craziness
- walkspeed = bound(0, monster_speed_walk * Monster_SkillModifier(), walkspeed * 2); // limit maxspeed to prevent craziness
+ runspeed = bound(0, monster_speed_run * MONSTER_SKILLMOD(self), runspeed * 2.5); // limit maxspeed to prevent craziness
+ walkspeed = bound(0, monster_speed_walk * MONSTER_SKILLMOD(self), walkspeed * 2.5); // limit maxspeed to prevent craziness
if(time < self.spider_slowness)
{
if(teamplay)
if(autocvar_g_monsters_teams)
- if(DIFF_TEAM(self.monster_owner, self))
- self.monster_owner = world;
+ if(DIFF_TEAM(self.monster_follow, self))
+ self.monster_follow = world;
if(time >= self.last_enemycheck)
{
if(!self.enemy)
{
- self.enemy = FindTarget(self);
+ self.enemy = Monster_FindTarget(self);
if(self.enemy)
{
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.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)));
- MonsterSound(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);
- 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
+ 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, ((self.enemy) ? 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); */
+ Monster_CalculateVelocity(self, self.moveto, self.origin, TRUE, ((do_run) ? runspeed : walkspeed));
- 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;
}
else if(e.target)
self.target2 = e.target;
- movelib_beak_simple(stopspeed);
- if(time > self.attack_finished_single)
+ movelib_beak_simple(stpspeed);
+ if(time > self.anim_finished)
if(time > self.pain_finished)
- if (vlen(self.velocity) <= 30)
+ if(!self.state)
+ if(vlen(self.velocity) <= 30)
self.frame = manim_idle;
}
-
- self.steerto = steerlib_attract2(self.moveto, 0.5, 500, 0.95);
-
+
+ self.steerto = steerlib_attract2(((self.monster_face) ? self.monster_face : self.moveto), 0.5, 500, 0.95);
+
vector real_angle = vectoangles(self.steerto) - self.angles;
float turny = 25;
- if(self.state == MONSTER_STATE_ATTACK_MELEE)
+ if(self.state == MONSTER_ATTACK_MELEE)
turny = 0;
if(turny)
{
self.angles_y += turny;
}
- monster_checkattack(self, self.enemy);
+ Monster_Attack_Check(self, self.enemy);
}
-void monster_remove(entity mon)
+void Monster_Remove(entity mon)
{
- if(!mon)
- return; // nothing to remove
-
- pointparticles(particleeffectnum("item_pickup"), mon.origin, '0 0 0', 1);
+ if(!mon) { return; }
- if(mon.weaponentity)
- remove(mon.weaponentity);
-
- if(mon.iceblock)
- remove(mon.iceblock);
+ if(!MUTATOR_CALLHOOK(MonsterRemove))
+ Send_Effect(EFFECT_SPAWN_NEUTRAL, mon.origin, '0 0 0', 1);
+ if(mon.weaponentity) { remove(mon.weaponentity); }
+ if(mon.iceblock) { remove(mon.iceblock); }
WaypointSprite_Kill(mon.sprite);
-
remove(mon);
}
-void monster_dead_think()
+void Monster_Dead_Think()
{
self.nextthink = time + self.ticrate;
- CSQCMODEL_AUTOUPDATE();
-
if(self.monster_lifetime != 0)
if(time >= self.monster_lifetime)
{
- Monster_Fade();
+ Monster_Dead_Fade();
return;
}
}
-void monsters_setstatus()
-{
- self.stat_monsters_total = monsters_total;
- self.stat_monsters_killed = monsters_killed;
-}
-
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 TRUE;
}
-void monsters_reset()
+void Monster_Reset()
{
setorigin(self, self.pos1);
self.angles = self.pos2;
self.moveto = self.origin;
}
-void monsters_corpse_damage (entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
+void Monster_Dead_Damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
{
self.health -= damage;
}
}
-void monster_die(entity attacker, float gibbed)
+void Monster_Dead(entity attacker, float gibbed)
{
- self.think = monster_dead_think;
+ self.think = Monster_Dead_Think;
self.nextthink = time;
self.monster_lifetime = time + 5;
monster_dropitem();
- 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;
totalspawned -= 1;
}
- if(self.candrop && self.weapon)
- W_ThrowNewWeapon(self, self.weapon, 0, self.origin, randomvec() * 150 + '0 0 325');
-
- self.event_damage = ((gibbed) ? func_null : monsters_corpse_damage);
+ self.event_damage = ((gibbed) ? func_null : Monster_Dead_Damage);
self.solid = SOLID_CORPSE;
self.takedamage = DAMAGE_AIM;
self.deadflag = DEAD_DEAD;
self.enemy = world;
self.movetype = MOVETYPE_TOSS;
self.moveto = self.origin;
- self.touch = MonsterTouch; // reset incase monster was pouncing
+ self.touch = Monster_Touch; // reset incase monster was pouncing
self.reset = func_null;
self.state = 0;
self.attack_finished_single = 0;
+ self.effects = 0;
if(!((self.flags & FL_FLY) || (self.flags & FL_SWIM)))
self.velocity = '0 0 0';
+ CSQCModel_UnlinkEntity();
+
MON_ACTION(self.monsterid, MR_DEATH);
+
+ if(self.candrop && self.weapon)
+ W_ThrowNewWeapon(self, self.weapon, 0, self.origin, randomvec() * 150 + '0 0 325');
}
-void monsters_damage (entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
+void Monster_Damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
{
- if(self.frozen && deathtype != DEATH_KILL && deathtype != DEATH_NADE_ICE_FREEZE)
- return;
-
if((self.spawnflags & MONSTERFLAG_INVINCIBLE) && deathtype != DEATH_KILL)
return;
- if(time < self.pain_finished && deathtype != DEATH_KILL)
+ if(self.frozen && deathtype != DEATH_KILL && deathtype != DEATH_NADE_ICE_FREEZE)
return;
+ //if(time < self.pain_finished && deathtype != DEATH_KILL)
+ //return;
+
if(time < self.spawnshieldtime && deathtype != DEATH_KILL)
return;
vector v;
float take, save;
- v = healtharmor_applydamage(self.armorvalue, self.m_armor_blockpercent, deathtype, damage);
+ 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;
+
+ if(take)
+ {
+ self.health -= take;
+ Monster_Sound(monstersound_pain, 1.2, TRUE, CH_PAIN);
+ }
- WaypointSprite_UpdateHealth(self.sprite, self.health);
+ if(self.sprite)
+ WaypointSprite_UpdateHealth(self.sprite, self.health);
self.dmg_time = time;
self.velocity += force * self.damageforcescale;
- if(deathtype != DEATH_DROWN)
+ if(deathtype != DEATH_DROWN && take)
{
Violence_GibSplash_At(hitloc, force, 2, bound(0, take, 200) / 16, self, attacker);
if (take > 50)
SUB_UseTargets();
self.target2 = self.oldtarget2; // reset to original target on death, incase we respawn
- monster_die(attacker, (self.health <= -100 || deathtype == DEATH_KILL));
+ Monster_Dead(attacker, (self.health <= -100 || deathtype == DEATH_KILL));
WaypointSprite_Kill(self.sprite);
}
}
-void monster_setupcolors(entity mon)
-{
- if(IS_PLAYER(mon.monster_owner))
- mon.colormap = mon.monster_owner.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;
- }
-}
-
-void monster_changeteam(entity ent, float newteam)
-{
- if(!teamplay) { return; }
-
- ent.team = newteam;
- 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_think()
+void Monster_Think()
{
- self.think = monster_think;
+ self.think = Monster_Think;
self.nextthink = self.ticrate;
if(self.monster_lifetime)
return;
}
+ if(self.skin != self.oldskin) { Monster_Skin_Check(); }
+
MON_ACTION(self.monsterid, MR_THINK);
+ Monster_Move(self.speed2, self.speed, self.stopspeed, self.m_anim_run, self.m_anim_walk, self.m_anim_idle);
+
CSQCMODEL_AUTOUPDATE();
}
-float monster_spawn()
+float Monster_Spawn_Setup()
{
MON_ACTION(self.monsterid, MR_SETUP);
+ // ensure some basic needs are met
+ if(!self.health) { self.health = 100; }
+ if(!self.armorvalue) { self.armorvalue = bound(0.2, 0.5 * MONSTER_SKILLMOD(self), 0.9); }
+ if(!self.target_range) { self.target_range = autocvar_g_monsters_target_range; }
+ if(!self.respawntime) { self.respawntime = autocvar_g_monsters_respawn_delay; }
+ if(!self.monster_moveflags) { self.monster_moveflags = MONSTER_MOVE_WANDER; }
+ if(!self.attack_range) { self.attack_range = autocvar_g_monsters_attack_range; }
+ if(!self.damageforcescale) { self.damageforcescale = autocvar_g_monsters_damageforcescale; }
+
if(!(self.spawnflags & MONSTERFLAG_RESPAWNED))
{
- Monster_CheckMinibossFlag();
- self.health *= Monster_SkillModifier();
+ Monster_Miniboss_Check();
+ self.health *= MONSTER_SKILLMOD(self);
+
+ if(!self.skin)
+ self.skin = rint(random() * 4);
+
+ Monster_Skin_Check();
}
self.max_health = self.health;
self.pain_finished = self.nextthink;
- if(IS_PLAYER(self.monster_owner))
+ if(IS_PLAYER(self.monster_follow))
self.effects |= EF_DIMLIGHT;
- if(!(self.spawnflags & MONSTERFLAG_RESPAWNED))
- if(!self.skin)
- self.skin = rint(random() * 4);
-
- if(!self.attack_range)
- self.attack_range = autocvar_g_monsters_attack_range;
-
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
- 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'));
+
+ 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 TRUE;
}
-float monster_initialize(float mon_id)
+float Monster_Spawn(float 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(!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(!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) { 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;
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.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;
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.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;
self.spawn_time = time;
self.spider_slowness = 0;
self.gravity = 1;
+ self.monster_moveto = '0 0 0';
+ self.monster_face = '0 0 0';
self.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_BOTCLIP | DPCONTENTS_MONSTERCLIP;
-
- if(!self.scale)
- self.scale = 1;
-
- if(autocvar_g_monsters_edit)
- self.grab = 1; // owner may carry their monster
-
- if(autocvar_g_fullbrightplayers)
- self.effects |= EF_FULLBRIGHT;
-
- if(autocvar_g_nodepthtestplayers)
- self.effects |= EF_NODEPTHTEST;
-
- if(mon.spawnflags & MONSTER_TYPE_SWIM)
- self.flags |= FL_SWIM;
+
+ if(!self.scale) { self.scale = 1; }
+ if(autocvar_g_monsters_edit) { self.grab = 1; }
+ if(autocvar_g_fullbrightplayers) { self.effects |= EF_FULLBRIGHT; }
+ if(autocvar_g_nodepthtestplayers) { self.effects |= EF_NODEPTHTEST; }
+ if(mon.spawnflags & MONSTER_TYPE_SWIM) { self.flags |= FL_SWIM; }
if(mon.spawnflags & MONSTER_TYPE_FLY)
{
if(mon.spawnflags & MONSTER_SIZE_BROKEN)
if(!(self.spawnflags & MONSTERFLAG_RESPAWNED))
self.scale *= 1.3;
-
- 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;
+ setsize(self, mon.mins * self.scale, mon.maxs * self.scale);
- 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;
+ }
if(!self.noalign)
{
setorigin(self, trace_endpos);
}
- if(!monster_spawn())
- return FALSE;
-
if(!(self.spawnflags & MONSTERFLAG_RESPAWNED))
monster_setupcolors(self);
-.string spawnmob;
-.float monster_attack;
+// =====================
+// Monster definitions
+// For server (SVQC)
+// =====================
-.entity monster_owner; // new monster owner entity, fixes non-solid monsters
-.float stat_monsters_killed; // stats
+// stats networking
+.float stat_monsters_killed;
.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
-.float wander_delay;
-.float wander_distance;
-
-.float monster_lifetime;
-
-.float spider_slowness; // special spider timer
-
-void monster_remove(entity mon); // removes a monster
-
-.float(float attack_type) monster_attackfunc;
-const float MONSTER_ATTACK_MELEE = 1;
-const float MONSTER_ATTACK_RANGED = 2;
+// monster properties
+.float 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;
+.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;
+
+#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
+.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;
-.float monster_skill;
+// 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;
-.float fish_wasdrowning; // used to reset a drowning fish's angles if it reaches water again
-
-.float candrop;
-
-.float attack_range;
-
-.float spawn_time; // stop monster from moving around right after spawning
+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
-.string oldtarget2;
-.float lastshielded;
+// 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
-.vector oldangles;
+// compatibility with old maps (soon to be removed)
+#define monster_lifetime lifetime
+#define monster_skill skill
-.float m_armor_blockpercent;
+// functions used elsewhere
+void Monster_Remove(entity mon); // removes a monster
+void monsters_setstatus();
+float Monster_Spawn(float mon_id);
// monster sounds
-// copied from player sounds
.float msound_delay; // temporary antilag system
#define ALLMONSTERSOUNDS \
_MSOUND(death) \
_MSOUND(melee) \
_MSOUND(pain) \
_MSOUND(spawn) \
- _MSOUND(idle)
+ _MSOUND(idle) \
+ _MSOUND(attack)
#define _MSOUND(m) .string monstersound_##m;
ALLMONSTERSOUNDS
#undef _MSOUND
float GetMonsterSoundSampleField_notFound;
-
-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
-
-// new 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
-
-.float monster_movestate; // used to tell what the monster is currently doing
-const float MONSTER_MOVE_OWNER = 1; // monster will move to owner 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_STATE_ATTACK_LEAP = 1;
-const float MONSTER_STATE_ATTACK_MELEE = 2;
{
if(notif.nent_enabled)
{
- precache_sound(sprintf("announcer/%s/%s.wav", autocvar_cl_announcer, snd));
+ precache_sound(sprintf("announcer/%s/%s.wav", AnnouncerOption(), snd));
notif.nent_channel = channel;
notif.nent_snd = strzone(snd);
notif.nent_vol = vol;
soundchannel,
sprintf(
"announcer/%s/%s.wav",
- autocvar_cl_announcer,
+ AnnouncerOption(),
soundfile
),
soundvolume,
soundchannel,
sprintf(
"announcer/%s/%s.wav",
- autocvar_cl_announcer,
+ AnnouncerOption(),
soundfile
),
soundvolume,
soundchannel,
sprintf(
"announcer/%s/%s.wav",
- autocvar_cl_announcer,
+ AnnouncerOption(),
soundfile
),
soundvolume,
#ifdef CSQC
if(notif.nent_icon != "")
{
+ if ( notif.nent_iconargs != "" )
+ {
+ notif.nent_icon = Local_Notification_sprintf(
+ notif.nent_icon,notif.nent_iconargs,
+ s1, s2, s3, s4, f1, f2, f3, f4);
+ // remove the newline added by Local_Notification_sprintf
+ notif.nent_icon = strzone(substring(notif.nent_icon,0,strlen(notif.nent_icon)-1));
+ }
Local_Notification_HUD_Notify_Push(
notif.nent_icon,
notif.nent_hudargs,
MSG_ANNCE_NOTIF(1, ANNCE_ACHIEVEMENT_IMPRESSIVE, CH_INFO, "impressive", VOL_BASEVOICE, ATTEN_NONE) \
MSG_ANNCE_NOTIF(1, ANNCE_ACHIEVEMENT_YODA, CH_INFO, "yoda", VOL_BASEVOICE, ATTEN_NONE) \
MSG_ANNCE_NOTIF(2, ANNCE_BEGIN, CH_INFO, "begin", VOL_BASEVOICE, ATTEN_NONE) \
+ MSG_ANNCE_NOTIF(2, ANNCE_BONUSNADE, CH_INFO, "bonusnade", VOL_BASEVOICE, ATTEN_NONE) \
+ MSG_ANNCE_NOTIF(2, ANNCE_HEADSHOT, CH_INFO, "headshot", VOL_BASEVOICE, ATTEN_NONE) \
+ MSG_ANNCE_NOTIF(1, ANNCE_KILLSTREAK_02, CH_INFO, "02kills", VOL_BASEVOICE, ATTEN_NONE) \
MSG_ANNCE_NOTIF(1, ANNCE_KILLSTREAK_03, CH_INFO, "03kills", VOL_BASEVOICE, ATTEN_NONE) \
MSG_ANNCE_NOTIF(1, ANNCE_KILLSTREAK_05, CH_INFO, "05kills", VOL_BASEVOICE, ATTEN_NONE) \
+ MSG_ANNCE_NOTIF(1, ANNCE_KILLSTREAK_08, CH_INFO, "08kills", VOL_BASEVOICE, ATTEN_NONE) \
MSG_ANNCE_NOTIF(1, ANNCE_KILLSTREAK_10, CH_INFO, "10kills", VOL_BASEVOICE, ATTEN_NONE) \
MSG_ANNCE_NOTIF(1, ANNCE_KILLSTREAK_15, CH_INFO, "15kills", VOL_BASEVOICE, ATTEN_NONE) \
MSG_ANNCE_NOTIF(1, ANNCE_KILLSTREAK_20, CH_INFO, "20kills", VOL_BASEVOICE, ATTEN_NONE) \
MSG_ANNCE_NOTIF(2, ANNCE_TIMEOUT, CH_INFO, "timeoutcalled", VOL_BASEVOICE, ATTEN_NONE) \
MSG_ANNCE_NOTIF(2, ANNCE_VOTE_ACCEPT, CH_INFO, "voteaccept", VOL_BASEVOICE, ATTEN_NONE) \
MSG_ANNCE_NOTIF(2, ANNCE_VOTE_CALL, CH_INFO, "votecall", VOL_BASEVOICE, ATTEN_NONE) \
- MSG_ANNCE_NOTIF(2, ANNCE_VOTE_FAIL, CH_INFO, "votefail", VOL_BASEVOICE, ATTEN_NONE)
+ MSG_ANNCE_NOTIF(2, ANNCE_VOTE_FAIL, CH_INFO, "votefail", VOL_BASEVOICE, ATTEN_NONE) \
+ JEFF_ANCE_NOTIF
#define MULTITEAM_INFO2(default,prefix,strnum,flnum,args,hudargs,icon,normal,gentle) \
MSG_INFO_NOTIF(default, prefix##RED, strnum, flnum, args, hudargs, sprintf(icon, strtolower(STATIC_NAME_TEAM_1)), TCR(normal, COL_TEAM_1, strtoupper(NAME_TEAM_1)), TCR(gentle, COL_TEAM_1, strtoupper(NAME_TEAM_1))) \
#define MSG_INFO_NOTIFICATIONS \
MSG_INFO_NOTIF(2, INFO_CHAT_NOSPECTATORS, 0, 0, "", "", "", _("^F4NOTE: ^BGSpectator chat is not sent to players during the match"), "") \
MSG_INFO_NOTIF(2, INFO_COINTOSS, 1, 0, "s1", "", "", _("^F2Throwing coin... Result: %s^F2!"), "") \
+ MULTITEAM_INFO(1, INFO_CONQUEST_CAPTURE_, 4, 1, 0, "s1", "", "", _("^BG%s^BG was captured by the ^TC^TT^BG team"), "") \
+ MULTITEAM_INFO(1, INFO_CONQUEST_LIBERATE_, 4, 1, 0, "s1", "", "", _("^BG%s^BG was liberated from the ^TC^TT^BG team"), "") \
MSG_INFO_NOTIF(1, INFO_JETPACK_NOFUEL, 0, 0, "", "", "", _("^BGYou don't have any fuel for the ^F1Jetpack"), "") \
MSG_INFO_NOTIF(2, INFO_SUPERSPEC_MISSING_UID, 0, 0, "", "", "", _("^F2You lack a UID, superspec options will not be saved/restored"), "") \
MSG_INFO_NOTIF(1, INFO_CA_JOIN_LATE, 0, 0, "", "", "", _("^F1Round already started, you will join the game in the next round"), "") \
MSG_INFO_NOTIF(1, INFO_CA_LEAVE, 0, 0, "", "", "", _("^F2You will spectate in the next round"), "") \
- MULTITEAM_INFO(1, INFO_CTF_CAPTURE_, 2, 1, 0, "s1", "s1", "notify_%s_captured", _("^BG%s^BG captured the ^TC^TT^BG flag"), "") \
- MULTITEAM_INFO(1, INFO_CTF_CAPTURE_BROKEN_, 2, 2, 2, "s1 f1p2dec s2 f2p2dec", "s1", "notify_%s_captured", _("^BG%s^BG captured the ^TC^TT^BG flag in ^F1%s^BG seconds, breaking ^BG%s^BG's previous record of ^F2%s^BG seconds"), "") \
- MULTITEAM_INFO(1, INFO_CTF_CAPTURE_TIME_, 2, 1, 1, "s1 f1p2dec", "s1", "notify_%s_captured", _("^BG%s^BG captured the ^TC^TT^BG flag in ^F1%s^BG seconds"), "") \
- MULTITEAM_INFO(1, INFO_CTF_CAPTURE_UNBROKEN_, 2, 2, 2, "s1 f1p2dec s2 f2p2dec", "s1", "notify_%s_captured", _("^BG%s^BG captured the ^TC^TT^BG flag in ^F2%s^BG seconds, failing to break ^BG%s^BG's previous record of ^F1%s^BG seconds"), "") \
- MULTITEAM_INFO(1, INFO_CTF_FLAGRETURN_ABORTRUN_, 2, 0, 0, "", "", "", _("^BGThe ^TC^TT^BG flag was returned to base by its owner"), "") \
- MULTITEAM_INFO(1, INFO_CTF_FLAGRETURN_DAMAGED_, 2, 0, 0, "", "", "", _("^BGThe ^TC^TT^BG flag was destroyed and returned to base"), "") \
- MULTITEAM_INFO(1, INFO_CTF_FLAGRETURN_DROPPED_, 2, 0, 0, "", "", "", _("^BGThe ^TC^TT^BG flag was dropped in the base and returned itself"), "") \
- MULTITEAM_INFO(1, INFO_CTF_FLAGRETURN_NEEDKILL_, 2, 0, 0, "", "", "", _("^BGThe ^TC^TT^BG flag fell somewhere it couldn't be reached and returned to base"), "") \
- MULTITEAM_INFO(1, INFO_CTF_FLAGRETURN_SPEEDRUN_, 2, 0, 1, "f1p2dec", "", "", _("^BGThe ^TC^TT^BG flag became impatient after ^F1%.2f^BG seconds and returned itself"), "") \
- MULTITEAM_INFO(1, INFO_CTF_FLAGRETURN_TIMEOUT_, 2, 0, 0, "", "", "", _("^BGThe ^TC^TT^BG flag has returned to the base"), "") \
- MULTITEAM_INFO(1, INFO_CTF_LOST_, 2, 1, 0, "s1", "s1", "notify_%s_lost", _("^BG%s^BG lost the ^TC^TT^BG flag"), "") \
- MULTITEAM_INFO(1, INFO_CTF_PICKUP_, 2, 1, 0, "s1", "s1", "notify_%s_taken", _("^BG%s^BG got the ^TC^TT^BG flag"), "") \
- MULTITEAM_INFO(1, INFO_CTF_RETURN_, 2, 1, 0, "s1", "s1", "notify_%s_returned", _("^BG%s^BG returned the ^TC^TT^BG flag"), "") \
- MULTITEAM_INFO(1, INFO_CTF_RETURN_MONSTER_, 2, 1, 0, "s1", "s1", "notify_%s_returned", _("^BG%s^BG returned the ^TC^TT^BG flag"), "") \
+ MULTITEAM_INFO(1, INFO_CTF_CAPTURE_, 4, 1, 0, "s1", "s1", "notify_%s_captured", _("^BG%s^BG captured the ^TC^TT^BG flag"), "") \
+ MULTITEAM_INFO(1, INFO_CTF_CAPTURE_BROKEN_, 4, 2, 2, "s1 f1p2dec s2 f2p2dec", "s1", "notify_%s_captured", _("^BG%s^BG captured the ^TC^TT^BG flag in ^F1%s^BG seconds, breaking ^BG%s^BG's previous record of ^F2%s^BG seconds"), "") \
+ MSG_INFO_NOTIF(1, INFO_CTF_CAPTURE_NEUTRAL, 1, 0, "s1", "s1", "notify_%s_captured", _("^BG%s^BG captured the flag"), "") \
+ MULTITEAM_INFO(1, INFO_CTF_CAPTURE_TIME_, 4, 1, 1, "s1 f1p2dec", "s1", "notify_%s_captured", _("^BG%s^BG captured the ^TC^TT^BG flag in ^F1%s^BG seconds"), "") \
+ MULTITEAM_INFO(1, INFO_CTF_CAPTURE_UNBROKEN_, 4, 2, 2, "s1 f1p2dec s2 f2p2dec", "s1", "notify_%s_captured", _("^BG%s^BG captured the ^TC^TT^BG flag in ^F2%s^BG seconds, failing to break ^BG%s^BG's previous record of ^F1%s^BG seconds"), "") \
+ MULTITEAM_INFO(1, INFO_CTF_FLAGRETURN_ABORTRUN_, 4, 0, 0, "", "", "", _("^BGThe ^TC^TT^BG flag was returned to base by its owner"), "") \
+ MSG_INFO_NOTIF(1, INFO_CTF_FLAGRETURN_ABORTRUN_NEUTRAL,0, 0, "", "", "", _("^BGThe flag was returned by its owner"), "") \
+ MULTITEAM_INFO(1, INFO_CTF_FLAGRETURN_DAMAGED_, 4, 0, 0, "", "", "", _("^BGThe ^TC^TT^BG flag was destroyed and returned to base"), "") \
+ MSG_INFO_NOTIF(1, INFO_CTF_FLAGRETURN_DAMAGED_NEUTRAL, 0, 0, "", "", "", _("^BGThe flag was destroyed and returned to base"), "") \
+ MULTITEAM_INFO(1, INFO_CTF_FLAGRETURN_DROPPED_, 4, 0, 0, "", "", "", _("^BGThe ^TC^TT^BG flag was dropped in the base and returned itself"), "") \
+ MSG_INFO_NOTIF(1, INFO_CTF_FLAGRETURN_DROPPED_NEUTRAL, 0, 0, "", "", "", _("^BGThe flag was dropped in the base and returned itself"), "") \
+ MULTITEAM_INFO(1, INFO_CTF_FLAGRETURN_NEEDKILL_, 4, 0, 0, "", "", "", _("^BGThe ^TC^TT^BG flag fell somewhere it couldn't be reached and returned to base"), "") \
+ MSG_INFO_NOTIF(1, INFO_CTF_FLAGRETURN_NEEDKILL_NEUTRAL,0, 0, "", "", "", _("^BGThe flag fell somewhere it couldn't be reached and returned to base"), "") \
+ MULTITEAM_INFO(1, INFO_CTF_FLAGRETURN_SPEEDRUN_, 4, 0, 1, "f1p2dec", "", "", _("^BGThe ^TC^TT^BG flag became impatient after ^F1%.2f^BG seconds and returned itself"), "") \
+ MSG_INFO_NOTIF(1, INFO_CTF_FLAGRETURN_SPEEDRUN_NEUTRAL,0, 1, "f1p2dec", "", "", _("^BGThe flag became impatient after ^F1%.2f^BG seconds and returned itself"), "") \
+ MULTITEAM_INFO(1, INFO_CTF_FLAGRETURN_TIMEOUT_, 4, 0, 0, "", "", "", _("^BGThe ^TC^TT^BG flag has returned to the base"), "") \
+ MSG_INFO_NOTIF(1, INFO_CTF_FLAGRETURN_TIMEOUT_NEUTRAL, 0, 0, "", "", "", _("^BGThe flag has returned to the base"), "") \
+ MULTITEAM_INFO(1, INFO_CTF_LOST_, 4, 1, 0, "s1", "s1", "notify_%s_lost", _("^BG%s^BG lost the ^TC^TT^BG flag"), "") \
+ MSG_INFO_NOTIF(1, INFO_CTF_LOST_NEUTRAL, 1, 0, "s1", "s1", "notify_%s_lost", _("^BG%s^BG lost the flag"), "") \
+ MULTITEAM_INFO(1, INFO_CTF_PICKUP_, 4, 1, 0, "s1", "s1", "notify_%s_taken", _("^BG%s^BG got the ^TC^TT^BG flag"), "") \
+ MSG_INFO_NOTIF(1, INFO_CTF_PICKUP_NEUTRAL, 1, 0, "s1", "s1", "notify_%s_taken", _("^BG%s^BG got the flag"), "") \
+ MULTITEAM_INFO(1, INFO_CTF_RETURN_, 4, 1, 0, "s1", "s1", "notify_%s_returned", _("^BG%s^BG returned the ^TC^TT^BG flag"), "") \
+ MULTITEAM_INFO(1, INFO_CTF_RETURN_MONSTER_, 4, 1, 0, "s1", "s1", "notify_%s_returned", _("^BG%s^BG returned the ^TC^TT^BG flag"), "") \
+ MSG_INFO_NOTIF(1, INFO_DEATH_MURDER_BUFF, 3, 3, "spree_inf s1 s2 f3buffname s3loc spree_end", "s2 s1", "notify_death", _("^BG%s%s^K1 was killed by ^BG%s^K1's ^BG%s^K1 buff ^K1%s%s"), _("^BG%s%s^K1 was scored against by ^BG%s^K1's ^BG%s^K1 buff ^K1%s%s")) \
MSG_INFO_NOTIF(1, INFO_DEATH_MURDER_CHEAT, 3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1", "notify_death", _("^BG%s%s^K1 was unfairly eliminated by ^BG%s^K1%s%s"), "") \
+ MSG_INFO_NOTIF(1, INFO_DEATH_MURDER_CRUSH, 3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1", "notify_crush", _("^BG%s%s^K1 was crushed by ^BG%s^K1%s%s"), "") \
MSG_INFO_NOTIF(1, INFO_DEATH_MURDER_DROWN, 3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1", "notify_water", _("^BG%s%s^K1 was drowned by ^BG%s^K1%s%s"), "") \
MSG_INFO_NOTIF(1, INFO_DEATH_MURDER_FALL, 3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1", "notify_fall", _("^BG%s%s^K1 was grounded by ^BG%s^K1%s%s"), "") \
MSG_INFO_NOTIF(1, INFO_DEATH_MURDER_FIRE, 3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1", "notify_death", _("^BG%s%s^K1 was burnt up into a crisp by ^BG%s^K1%s%s"), _("^BG%s%s^K1 felt a little hot from ^BG%s^K1's fire^K1%s%s")) \
MSG_INFO_NOTIF(1, INFO_DEATH_MURDER_VH_SPID_DEATH, 3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1", "notify_death", _("^BG%s%s^K1 got caught in the blast when ^BG%s^K1's Spiderbot exploded%s%s"), "") \
MSG_INFO_NOTIF(1, INFO_DEATH_MURDER_VH_SPID_MINIGUN, 3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1", "notify_death", _("^BG%s%s^K1 got shredded by ^BG%s^K1's Spiderbot%s%s"), "") \
MSG_INFO_NOTIF(1, INFO_DEATH_MURDER_VH_SPID_ROCKET, 3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1", "notify_death", _("^BG%s%s^K1 was blasted to bits by ^BG%s^K1's Spiderbot%s%s"), "") \
+ MSG_INFO_NOTIF(1, INFO_DEATH_MURDER_VH_TANK_DEATH, 3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1", "notify_death", _("^BG%s%s^K1 was caught in the blast when ^BG%s^K1's Tank exploded%s%s"), "") \
+ MSG_INFO_NOTIF(1, INFO_DEATH_MURDER_VH_TANKLL48, 3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1", "notify_death", _("^BG%s%s^K1 was obliterated by ^BG%s^K1's LL48 Tank%s%s"), "") \
MSG_INFO_NOTIF(1, INFO_DEATH_MURDER_VH_WAKI_DEATH, 3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1", "notify_death", _("^BG%s%s^K1 got caught in the blast when ^BG%s^K1's Racer exploded%s%s"), "") \
MSG_INFO_NOTIF(1, INFO_DEATH_MURDER_VH_WAKI_GUN, 3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1", "notify_death", _("^BG%s%s^K1 was bolted down by ^BG%s^K1's Racer%s%s"), "") \
MSG_INFO_NOTIF(1, INFO_DEATH_MURDER_VH_WAKI_ROCKET, 3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1", "notify_death", _("^BG%s%s^K1 couldn't find shelter from ^BG%s^K1's Racer%s%s"), "") \
- MSG_INFO_NOTIF(1, INFO_DEATH_MURDER_VENGEANCE, 3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1", "notify_death", _("^BG%s%s^K1 was destroyed by the vengeful ^BG%s^K1%s%s"), "") \
MSG_INFO_NOTIF(1, INFO_DEATH_MURDER_VOID, 3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1", "notify_void", _("^BG%s%s^K1 was thrown into a world of hurt by ^BG%s^K1%s%s"), "") \
MSG_INFO_NOTIF(1, INFO_DEATH_SELF_AUTOTEAMCHANGE, 2, 1, "s1 s2loc death_team", "", "", _("^BG%s^K1 was moved into the %s%s"), "") \
MSG_INFO_NOTIF(1, INFO_DEATH_SELF_BETRAYAL, 2, 1, "s1 s2loc spree_lost", "s1", "notify_teamkill_red", _("^BG%s^K1 became enemies with the Lord of Teamplay%s%s"), "") \
MSG_INFO_NOTIF(1, INFO_DEATH_SELF_FIRE, 2, 1, "s1 s2loc spree_lost", "s1", "notify_death", _("^BG%s^K1 became a bit too crispy%s%s"), _("^BG%s^K1 felt a little hot%s%s")) \
MSG_INFO_NOTIF(1, INFO_DEATH_SELF_GENERIC, 2, 1, "s1 s2loc spree_lost", "s1", "notify_selfkill", _("^BG%s^K1 died%s%s"), "") \
MSG_INFO_NOTIF(1, INFO_DEATH_SELF_LAVA, 2, 1, "s1 s2loc spree_lost", "s1", "notify_lava", _("^BG%s^K1 turned into hot slag%s%s"), _("^BG%s^K1 found a hot place%s%s")) \
+ MSG_INFO_NOTIF(1, INFO_DEATH_SELF_MON_AFRIT, 2, 1, "s1 s2loc spree_lost", "s1", "notify_death", _("^BG%s^K1 was turned to ash by an Afrit%s%s"), "") \
+ MSG_INFO_NOTIF(1, INFO_DEATH_SELF_MON_CREEPER, 2, 1, "s1 s2loc spree_lost", "s1", "notify_death", _("^BG%s^K1 was blown up by Creeper%s%s"), "") \
+ MSG_INFO_NOTIF(1, INFO_DEATH_SELF_MON_DEMON_JUMP, 2, 1, "s1 s2loc spree_lost", "s1", "notify_death", _("^BG%s^K1 was chopped in half by a pouncing Demon%s%s"), "") \
+ MSG_INFO_NOTIF(1, INFO_DEATH_SELF_MON_DEMON_MELEE, 2, 1, "s1 s2loc spree_lost", "s1", "notify_death", _("^BG%s^K1 was eviscerated by a Demon%s%s"), "") \
MSG_INFO_NOTIF(1, INFO_DEATH_SELF_MON_MAGE, 2, 1, "s1 s2loc spree_lost", "s1", "notify_death", _("^BG%s^K1 was exploded by a Mage%s%s"), "") \
+ MSG_INFO_NOTIF(1, INFO_DEATH_SELF_MON_OGRE_GRENADE, 2, 1, "s1 s2loc spree_lost", "s1", "notify_death", _("^BG%s^K1 couldn't dodge the Ogre grenade%s%s"), "") \
+ MSG_INFO_NOTIF(1, INFO_DEATH_SELF_MON_OGRE_MACHINEGUN, 2, 1, "s1 s2loc spree_lost", "s1", "notify_death", _("^BG%s^K1 was riddled full of holes by an Ogre%s%s"), "") \
+ MSG_INFO_NOTIF(1, INFO_DEATH_SELF_MON_OGRE_MELEE, 2, 1, "s1 s2loc spree_lost", "s1", "notify_death", _("^BG%s^K1 was chopped to bits by an Ogre%s%s"), "") \
+ MSG_INFO_NOTIF(1, INFO_DEATH_SELF_MON_ROTFISH_MELEE, 2, 1, "s1 s2loc spree_lost", "s1", "notify_death", _("^BG%s^K1 was fed to the Rotfish%s%s"), "") \
+ MSG_INFO_NOTIF(1, INFO_DEATH_SELF_MON_ROTTWEILER, 2, 1, "s1 s2loc spree_lost", "s1", "notify_death", _("^BG%s^K1 was turned to dogmeat by a Rottweiler%s%s"), "") \
+ MSG_INFO_NOTIF(1, INFO_DEATH_SELF_MON_SCRAG, 2, 1, "s1 s2loc spree_lost", "s1", "notify_death", _("^BG%s^K1 was bombed from above by a Scrag%s%s"), "") \
MSG_INFO_NOTIF(1, INFO_DEATH_SELF_MON_SHAMBLER_CLAW, 2, 1, "s1 s2loc spree_lost", "s1", "notify_death", _("^BG%s^K1's innards became outwards by a Shambler%s%s"), "") \
MSG_INFO_NOTIF(1, INFO_DEATH_SELF_MON_SHAMBLER_SMASH, 2, 1, "s1 s2loc spree_lost", "s1", "notify_death", _("^BG%s^K1 was smashed by a Shambler%s%s"), "") \
MSG_INFO_NOTIF(1, INFO_DEATH_SELF_MON_SHAMBLER_ZAP, 2, 1, "s1 s2loc spree_lost", "s1", "notify_death", _("^BG%s^K1 was zapped to death by a Shambler%s%s"), "") \
MSG_INFO_NOTIF(1, INFO_DEATH_SELF_MON_SPIDER, 2, 1, "s1 s2loc spree_lost", "s1", "notify_death", _("^BG%s^K1 was bitten by a Spider%s%s"), "") \
+ MSG_INFO_NOTIF(1, INFO_DEATH_SELF_MON_VORE, 2, 1, "s1 s2loc spree_lost", "s1", "notify_death", _("^BG%s^K1 was exploded by a Vore%s%s"), "") \
+ MSG_INFO_NOTIF(1, INFO_DEATH_SELF_MON_VORE_MELEE, 2, 1, "s1 s2loc spree_lost", "s1", "notify_death", _("^BG%s^K1 was ripped apart by a Vore%s%s"), "") \
MSG_INFO_NOTIF(1, INFO_DEATH_SELF_MON_WYVERN, 2, 1, "s1 s2loc spree_lost", "s1", "notify_death", _("^BG%s^K1 was fireballed by a Wyvern%s%s"), "") \
MSG_INFO_NOTIF(1, INFO_DEATH_SELF_MON_ZOMBIE_JUMP, 2, 1, "s1 s2loc spree_lost", "s1", "notify_death", _("^BG%s^K1 joins the Zombies%s%s"), "") \
MSG_INFO_NOTIF(1, INFO_DEATH_SELF_MON_ZOMBIE_MELEE, 2, 1, "s1 s2loc spree_lost", "s1", "notify_death", _("^BG%s^K1 was given kung fu lessons by a Zombie%s%s"), "") \
MSG_INFO_NOTIF(1, INFO_DEATH_SELF_TURRET_PLASMA, 2, 1, "s1 s2loc spree_lost", "s1", "notify_death", _("^BG%s^K1 got served some superheated plasma from a turret%s%s"), "") \
MSG_INFO_NOTIF(1, INFO_DEATH_SELF_TURRET_TESLA, 2, 1, "s1 s2loc spree_lost", "s1", "notify_death", _("^BG%s^K1 was electrocuted by a Tesla turret%s%s"), "") \
MSG_INFO_NOTIF(1, INFO_DEATH_SELF_TURRET_WALK_GUN, 2, 1, "s1 s2loc spree_lost", "s1", "notify_death", _("^BG%s^K1 got served a lead enrichment by a Walker turret%s%s"), "") \
- MSG_INFO_NOTIF(1, INFO_DEATH_SELF_TURRET_WALK_MEELE, 2, 1, "s1 s2loc spree_lost", "s1", "notify_death", _("^BG%s^K1 was impaled by a Walker turret%s%s"), "") \
+ MSG_INFO_NOTIF(1, INFO_DEATH_SELF_TURRET_WALK_MELEE, 2, 1, "s1 s2loc spree_lost", "s1", "notify_death", _("^BG%s^K1 was impaled by a Walker turret%s%s"), "") \
MSG_INFO_NOTIF(1, INFO_DEATH_SELF_TURRET_WALK_ROCKET, 2, 1, "s1 s2loc spree_lost", "s1", "notify_death", _("^BG%s^K1 was blasted away by a Walker turret%s%s"), "") \
MSG_INFO_NOTIF(1, INFO_DEATH_SELF_VH_BUMB_DEATH, 2, 1, "s1 s2loc spree_lost", "s1", "notify_death", _("^BG%s^K1 got caught in the blast of a Bumblebee explosion%s%s"), "") \
MSG_INFO_NOTIF(1, INFO_DEATH_SELF_VH_CRUSH, 2, 1, "s1 s2loc spree_lost", "s1", "notify_death", _("^BG%s^K1 was crushed by a vehicle%s%s"), "") \
MSG_INFO_NOTIF(1, INFO_DEATH_SELF_VH_RAPT_DEATH, 2, 1, "s1 s2loc spree_lost", "s1", "notify_death", _("^BG%s^K1 got caught in the blast of a Raptor explosion%s%s"), "") \
MSG_INFO_NOTIF(1, INFO_DEATH_SELF_VH_SPID_DEATH, 2, 1, "s1 s2loc spree_lost", "s1", "notify_death", _("^BG%s^K1 got caught in the blast of a Spiderbot explosion%s%s"), "") \
MSG_INFO_NOTIF(1, INFO_DEATH_SELF_VH_SPID_ROCKET, 2, 1, "s1 s2loc spree_lost", "s1", "notify_death", _("^BG%s^K1 was blasted to bits by a Spiderbot rocket%s%s"), "") \
+ MSG_INFO_NOTIF(1, INFO_DEATH_SELF_VH_TANK_DEATH, 2, 1, "s1 s2loc spree_lost", "s1", "notify_death", _("^BG%s^K1 was caught in the blast of a Tank explosion%s%s"), "") \
MSG_INFO_NOTIF(1, INFO_DEATH_SELF_VH_WAKI_DEATH, 2, 1, "s1 s2loc spree_lost", "s1", "notify_death", _("^BG%s^K1 got caught in the blast of a Racer explosion%s%s"), "") \
MSG_INFO_NOTIF(1, INFO_DEATH_SELF_VH_WAKI_ROCKET, 2, 1, "s1 s2loc spree_lost", "s1", "notify_death", _("^BG%s^K1 couldn't find shelter from a Racer rocket%s%s"), "") \
- MSG_INFO_NOTIF(1, INFO_DEATH_SELF_VOID, 2, 1, "s1 s2loc spree_lost", "s1", "notify_void", _("^BG%s^K1 was in the wrong place%s%s"), "") \
+ MSG_INFO_NOTIF(1, INFO_DEATH_SELF_VOID, 3, 1, "s1 s2 s3loc spree_lost", "s1", "notify_void", "^BG%s^K1 %s^K1%s%s", "") \
MULTITEAM_INFO(1, INFO_DEATH_TEAMKILL_, 4, 3, 1, "s1 s2 s3loc spree_end", "s2 s1", "notify_teamkill_%s", _("^BG%s^K1 was betrayed by ^BG%s^K1%s%s"), "") \
- MSG_INFO_NOTIF(1, INFO_DOMINATION_CAPTURE_TIME, 2, 2, "s1 s2 f1 f2", "", "", _("^BG%s^BG%s^BG (%s points every %s seconds)"), "") \
+ MULTITEAM_INFO(1, INFO_DOMINATION_CAPTURE_, 4, 0, 0, "", "", "", _("^TC^TT^BG team has captured a control point"), "") \
+ MULTITEAM_INFO(1, INFO_DOMINATION_CAPTURE_TIME_, 4, 1, 2, "s1 f1 f2", "", "", _("^TC^TT^BG team%s^BG (%s points every %s seconds)"), "") \
+ MULTITEAM_INFO(1, INFO_DOMINATION_CAPTURE_TIME_NOMSG_,4,0,2, "f1 f2", "", "", _("^TC^TT^BG team has captured a control point (%s points every %s seconds)"), "") \
MSG_INFO_NOTIF(1, INFO_FREEZETAG_FREEZE, 2, 0, "s1 s2", "", "", _("^BG%s^K1 was frozen by ^BG%s"), "") \
MSG_INFO_NOTIF(1, INFO_FREEZETAG_REVIVED, 2, 0, "s1 s2", "", "", _("^BG%s^K3 was revived by ^BG%s"), "") \
MSG_INFO_NOTIF(1, INFO_FREEZETAG_REVIVED_FALL, 1, 0, "s1", "", "", _("^BG%s^K3 was revived by falling"), "") \
MSG_INFO_NOTIF(1, INFO_FREEZETAG_REVIVED_NADE, 1, 0, "s1", "", "", _("^BG%s^K3 was revived by their Nade explosion"), "") \
MSG_INFO_NOTIF(1, INFO_FREEZETAG_AUTO_REVIVED, 1, 1, "s1 f1", "", "", _("^BG%s^K3 was automatically revived after %s second(s)"), "") \
+ MSG_INFO_NOTIF(1, INFO_ROUND_INFECTION_WIN, 0, 0, "", "", "", _("^BGRound over, all players have been infected"), "") \
MULTITEAM_INFO(1, INFO_ROUND_TEAM_WIN_, 4, 0, 0, "", "", "", _("^TC^TT^BG team wins the round"), "") \
MSG_INFO_NOTIF(1, INFO_ROUND_PLAYER_WIN, 1, 0, "s1", "", "", _("^BG%s^BG wins the round"), "") \
MSG_INFO_NOTIF(1, INFO_ROUND_TIED, 0, 0, "", "", "", _("^BGRound tied"), "") \
MSG_INFO_NOTIF(1, INFO_ROUND_OVER, 0, 0, "", "", "", _("^BGRound over, there's no winner"), "") \
MSG_INFO_NOTIF(1, INFO_FREEZETAG_SELF, 1, 0, "s1", "", "", _("^BG%s^K1 froze themself"), "") \
MSG_INFO_NOTIF(1, INFO_GODMODE_OFF, 0, 1, "f1", "", "", _("^BGGodmode saved you %s units of damage, cheater!"), "") \
+ MSG_INFO_NOTIF(1, INFO_INFECTION_INFECTED, 2, 0, "s1 s2", "", "", _("^BG%s^BG was infected by ^BG%s"), "") \
MSG_INFO_NOTIF(1, INFO_ITEM_BUFF, 1, 1, "s1 item_buffname", "", "", _("^BG%s^BG got the %s^BG buff!"), "") \
MSG_INFO_NOTIF(1, INFO_ITEM_BUFF_LOST, 1, 1, "s1 item_buffname", "", "", _("^BG%s^BG lost the %s^BG buff!"), "") \
MSG_INFO_NOTIF(1, INFO_ITEM_BUFF_DROP, 0, 1, "item_buffname", "", "", _("^BGYou dropped the %s^BG buff!"), "") \
MSG_INFO_NOTIF(0, INFO_ITEM_WEAPON_NOAMMO, 0, 1, "item_wepname", "", "", _("^BGYou don't have enough ammo for the ^F1%s"), "") \
MSG_INFO_NOTIF(0, INFO_ITEM_WEAPON_PRIMORSEC, 0, 3, "item_wepname f2primsec f3primsec", "", "", _("^F1%s %s^BG is unable to fire, but its ^F1%s^BG can"), "") \
MSG_INFO_NOTIF(0, INFO_ITEM_WEAPON_UNAVAILABLE, 0, 1, "item_wepname", "", "", _("^F1%s^BG is ^F4not available^BG on this map"), "") \
+ MSG_INFO_NOTIF(1, INFO_JAILBREAK_CAPTURE, 2, 0, "s1 s2", "", "", _("^BG%s^BG captured %s"), "") \
+ MSG_INFO_NOTIF(1, INFO_JAILBREAK_FREE, 1, 0, "s1", "", "", _("^BG%s^3 has broken free!"), "") \
MSG_INFO_NOTIF(2, INFO_JOIN_CONNECT, 1, 0, "s1", "", "", _("^BG%s^F3 connected%s"), "") \
MULTITEAM_INFO(2, INFO_JOIN_CONNECT_TEAM_, 4, 1, 0, "s1", "", "", _("^BG%s^F3 connected and joined the ^TC^TT team"), "") \
MSG_INFO_NOTIF(1, INFO_JOIN_PLAY, 1, 0, "s1", "", "", _("^BG%s^F3 is now playing"), "") \
MSG_INFO_NOTIF(1, INFO_KEEPAWAY_DROPPED, 1, 0, "s1", "s1", "notify_balldropped", _("^BG%s^BG has dropped the ball!"), "") \
MSG_INFO_NOTIF(1, INFO_KEEPAWAY_PICKUP, 1, 0, "s1", "s1", "notify_ballpickedup", _("^BG%s^BG has picked up the ball!"), "") \
- MULTITEAM_INFO(1, INFO_KEYHUNT_CAPTURE_, 4, 1, 0, "s1", "", "", _("^BG%s^BG captured the keys for the ^TC^TT team"), "") \
+ MULTITEAM_INFO(1, INFO_KEYHUNT_CAPTURE_, 4, 1, 0, "s1", "", "", _("^BG%s^BG captured the ^TC^TT key"), "") \
MULTITEAM_INFO(1, INFO_KEYHUNT_DROP_, 4, 1, 0, "s1", "", "", _("^BG%s^BG dropped the ^TC^TT Key"), "") \
+ MULTITEAM_INFO(1, INFO_KEYHUNT_KEYRETURN_DAMAGED_, 4, 0, 0, "", "", "", _("^BGThe ^TC^TT^BG key was destroyed"), "") \
+ MULTITEAM_INFO(1, INFO_KEYHUNT_KEYRETURN_NEEDKILL_, 4, 0, 0, "", "", "", _("^BGThe ^TC^TT^BG key fell somewhere it couldn't be reached"), "") \
+ MULTITEAM_INFO(1, INFO_KEYHUNT_KEYRETURN_TIMEOUT_, 4, 0, 0, "", "", "", _("^BGThe ^TC^TT^BG key was lost"), "") \
MULTITEAM_INFO(1, INFO_KEYHUNT_LOST_, 4, 1, 0, "s1", "", "", _("^BG%s^BG lost the ^TC^TT Key"), "") \
MULTITEAM_INFO(1, INFO_KEYHUNT_PICKUP_, 4, 1, 0, "s1", "", "", _("^BG%s^BG picked up the ^TC^TT Key"), "") \
+ MULTITEAM_INFO(1, INFO_KEYHUNT_START_, 4, 1, 0, "s1", "", "", _("^BG%s^BG is starting with the ^TC^TT Key"), "") \
MSG_INFO_NOTIF(1, INFO_LMS_FORFEIT, 1, 0, "s1", "", "", _("^BG%s^F3 forfeited"), "") \
MSG_INFO_NOTIF(1, INFO_LMS_NOLIVES, 1, 0, "s1", "", "", _("^BG%s^F3 has no more lives left"), "") \
MSG_INFO_NOTIF(1, INFO_MONSTERS_DISABLED, 0, 0, "", "", "", _("^BGMonsters are currently disabled"), "") \
- MSG_INFO_NOTIF(1, INFO_POWERUP_INVISIBILITY, 1, 0, "s1", "s1", "strength", _("^BG%s^K1 picked up Invisibility"), "") \
- MSG_INFO_NOTIF(1, INFO_POWERUP_SHIELD, 1, 0, "s1", "s1", "shield", _("^BG%s^K1 picked up Shield"), "") \
- MSG_INFO_NOTIF(1, INFO_POWERUP_SPEED, 1, 0, "s1", "s1", "shield", _("^BG%s^K1 picked up Speed"), "") \
- MSG_INFO_NOTIF(1, INFO_POWERUP_STRENGTH, 1, 0, "s1", "s1", "strength", _("^BG%s^K1 picked up Strength"), "") \
+ MSG_INFO_NOTIF(1, INFO_ONSLAUGHT_CAPTURE, 2, 0, "s1 s2", "", "", _("^BG%s^BG captured %s^BG control point"), "") \
+ MULTITEAM_INFO(1, INFO_ONSLAUGHT_CPDESTROYED_, 4, 2, 0, "s1 s2", "", "", _("^TC^TT^BG team %s^BG control point has been destroyed by %s"), "") \
+ MULTITEAM_INFO(1, INFO_ONSLAUGHT_GENDESTROYED_, 4, 0, 0, "", "", "", _("^TC^TT^BG generator has been destroyed"), "") \
+ MULTITEAM_INFO(1, INFO_ONSLAUGHT_GENDESTROYED_OVERTIME_, 4, 0, 0, "", "", "", _("^TC^TT^BG generator spontaneously combusted due to overtime!"), "") \
MSG_INFO_NOTIF(2, INFO_QUIT_DISCONNECT, 1, 0, "s1", "", "", _("^BG%s^F3 disconnected"), "") \
MSG_INFO_NOTIF(2, INFO_QUIT_KICK_IDLING, 1, 0, "s1", "", "", _("^BG%s^F3 was kicked for idling"), "") \
MSG_INFO_NOTIF(1, INFO_QUIT_KICK_SPECTATING, 0, 0, "", "", "", _("^F2You were kicked from the server because you are a spectator and spectators aren't allowed at the moment."), "") \
MSG_INFO_NOTIF(1, INFO_RACE_NEW_IMPROVED, 1, 3, "s1 race_col f1ord race_col f2race_time race_diff", "s1 f2race_time", "race_newtime", _("^BG%s^BG improved their %s%s^BG place record with %s%s %s"), "") \
MSG_INFO_NOTIF(1, INFO_RACE_NEW_MISSING_UID, 1, 1, "s1 f1race_time", "s1 f1race_time", "race_newfail", _("^BG%s^BG scored a new record with ^F2%s^BG, but unfortunately lacks a UID and will be lost."), "") \
MSG_INFO_NOTIF(1, INFO_RACE_NEW_SET, 1, 2, "s1 race_col f1ord race_col f2race_time", "s1 f2race_time", "race_newrecordserver", _("^BG%s^BG set the %s%s^BG place record with %s%s"), "") \
+ MULTIICON_INFO(1, INFO_MINIGAME_INVITE, 2, 0, "s2 minigame1_name s1","s2", "minigame1_d", "minigames/%s/icon_notif",_("^F4You have been invited by ^BG%s^F4 to join their game of ^F2%s^F4 (^F1%s^F4)"), "") \
MULTITEAM_INFO(1, INFO_SCORES_, 4, 0, 0, "", "", "", _("^TC^TT ^BGteam scores!"), "") \
MSG_INFO_NOTIF(1, INFO_SPECTATE_WARNING, 0, 1, "f1secs", "", "", _("^F2You have to become a player within the next %s, otherwise you will be kicked, because spectating isn't allowed at this time!"), "") \
MSG_INFO_NOTIF(1, INFO_SUPERWEAPON_PICKUP, 1, 0, "s1", "s1", "superweapons", _("^BG%s^K1 picked up a Superweapon"), "") \
MSG_INFO_NOTIF(2, INFO_VERSION_BETA, 2, 0, "s1 s2", "", "", _("^F4NOTE: ^BGThe server is running ^F1Xonotic %s (beta)^BG, you have ^F2Xonotic %s"), "") \
MSG_INFO_NOTIF(2, INFO_VERSION_OLD, 2, 0, "s1 s2", "", "", _("^F4NOTE: ^BGThe server is running ^F1Xonotic %s^BG, you have ^F2Xonotic %s"), "") \
MSG_INFO_NOTIF(2, INFO_VERSION_OUTDATED, 2, 0, "s1 s2", "", "", _("^F4NOTE: ^F1Xonotic %s^BG is out, and you still have ^F2Xonotic %s^BG - get the update from ^F3http://www.xonotic.org/^BG!"), "") \
+ MSG_INFO_NOTIF(1, INFO_VIP_DROP, 1, 0, "s1", "", "", _("^BG%s^K1 dropped the soul gem"), "") \
+ MSG_INFO_NOTIF(1, INFO_VIP_PICKUP, 1, 0, "s1", "", "", _("^BG%s^K1 picked up the soul gem"), "") \
MSG_INFO_NOTIF(1, INFO_WATERMARK, 1, 0, "s1", "", "", _("^F3SVQC Build information: ^F4%s"), "") \
MSG_INFO_NOTIF(1, INFO_WEAPON_ACCORDEON_MURDER, 3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1", "weapontuba", _("^BG%s%s^K1 died of ^BG%s^K1's great playing on the @!#%%'n Accordeon%s%s"), "") \
MSG_INFO_NOTIF(1, INFO_WEAPON_ACCORDEON_SUICIDE, 2, 1, "s1 s2loc spree_lost", "s1", "weapontuba", _("^BG%s^K1 hurt their own ears with the @!#%%'n Accordeon%s%s"), "") \
MSG_INFO_NOTIF(1, INFO_WEAPON_HLAC_SUICIDE, 2, 1, "s1 s2loc spree_lost", "s1", "weaponhlac", _("^BG%s^K1 got a little jumpy with their HLAC%s%s"), "") \
MSG_INFO_NOTIF(1, INFO_WEAPON_HMG_MURDER_SNIPE, 3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1", "weaponhmg", _("^BG%s%s^K1 was sniped by ^BG%s^K1's Heavy Machine Gun%s%s"), "") \
MSG_INFO_NOTIF(1, INFO_WEAPON_HMG_MURDER_SPRAY, 3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1", "weaponhmg", _("^BG%s%s^K1 was torn to bits by ^BG%s^K1's Heavy Machine Gun%s%s"), "") \
- MSG_INFO_NOTIF(1, INFO_WEAPON_HOOK_MURDER, 3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1", "weaponhook", _("^BG%s%s^K1 was caught in ^BG%s^K1's Hook gravity bomb%s%s"), "") \
+ MSG_INFO_NOTIF(1, INFO_WEAPON_HOOK_MURDER, 3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1", "weaponhook", _("^BG%s%s^K1 was hookfragged by ^BG%s^K1%s%s"), "") \
+ MSG_INFO_NOTIF(1, INFO_WEAPON_HOOK_MURDER_LASER, 3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1", "weaponhook", _("^BG%s%s^K1's hook was lasered by ^BG%s^K1%s%s"), "") \
+ MSG_INFO_NOTIF(1, INFO_WEAPON_HOOK_MURDER_GRAVITYBOMB, 3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1", "weaponhook", _("^BG%s%s^K1 was caught in ^BG%s^K1's Hook gravity bomb%s%s"), "") \
MSG_INFO_NOTIF(1, INFO_WEAPON_KLEINBOTTLE_MURDER, 3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1", "weapontuba", _("^BG%s%s^K1 died of ^BG%s^K1's great playing on the @!#%%'n Klein Bottle%s%s"), "") \
MSG_INFO_NOTIF(1, INFO_WEAPON_KLEINBOTTLE_SUICIDE, 2, 1, "s1 s2loc spree_lost", "s1", "weapontuba", _("^BG%s^K1 hurt their own ears with the @!#%%'n Klein Bottle%s%s"), "") \
+ MSG_INFO_NOTIF(1, INFO_WEAPON_LIGHTSABRE_MURDER, 3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1", "weaponlightsabre", _("^BG%s%s^K1's hand was zapped off by ^BG%s^K1's Lightsabre%s%s"), "") \
MSG_INFO_NOTIF(1, INFO_WEAPON_MACHINEGUN_MURDER_SNIPE, 3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1", "weaponuzi", _("^BG%s%s^K1 was sniped by ^BG%s^K1's Machine Gun%s%s"), "") \
MSG_INFO_NOTIF(1, INFO_WEAPON_MACHINEGUN_MURDER_SPRAY, 3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1", "weaponuzi", _("^BG%s%s^K1 was riddled full of holes by ^BG%s^K1's Machine Gun%s%s"), "") \
MSG_INFO_NOTIF(1, INFO_WEAPON_MINELAYER_LIMIT, 0, 1, "f1", "", "", _("^BGYou cannot place more than ^F2%s^BG mines at a time"), "") \
MSG_INFO_NOTIF(1, INFO_WEAPON_MORTAR_MURDER_EXPLODE, 3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1", "weapongrenadelauncher", _("^BG%s%s^K1 ate ^BG%s^K1's Mortar grenade%s%s"), "") \
MSG_INFO_NOTIF(1, INFO_WEAPON_MORTAR_SUICIDE_BOUNCE, 2, 1, "s1 s2loc spree_lost", "s1", "weapongrenadelauncher", _("^BG%s^K1 didn't see their own Mortar grenade%s%s"), "") \
MSG_INFO_NOTIF(1, INFO_WEAPON_MORTAR_SUICIDE_EXPLODE, 2, 1, "s1 s2loc spree_lost", "s1", "weapongrenadelauncher", _("^BG%s^K1 blew themself up with their own Mortar%s%s"), "") \
+ MSG_INFO_NOTIF(1, INFO_WEAPON_REVOLVER_MURDER, 3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1", "weaponrevolver", _("^BG%s%s^K1 was put down by ^BG%s^K1's Revolver%s%s"), "") \
MSG_INFO_NOTIF(1, INFO_WEAPON_RIFLE_MURDER, 3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1", "weaponrifle", _("^BG%s%s^K1 was sniped with a Rifle by ^BG%s^K1%s%s"), "") \
MSG_INFO_NOTIF(1, INFO_WEAPON_RIFLE_MURDER_HAIL, 3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1", "weaponrifle", _("^BG%s%s^K1 died in ^BG%s^K1's Rifle bullet hail%s%s"), "") \
MSG_INFO_NOTIF(1, INFO_WEAPON_RIFLE_MURDER_HAIL_PIERCING, 3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1", "weaponrifle", _("^BG%s%s^K1 failed to hide from ^BG%s^K1's Rifle bullet hail%s%s"), "") \
MSG_INFO_NOTIF(1, INFO_WEAPON_TUBA_MURDER, 3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1", "weapontuba", _("^BG%s%s^K1 died of ^BG%s^K1's great playing on the @!#%%'n Tuba%s%s"), "") \
MSG_INFO_NOTIF(1, INFO_WEAPON_TUBA_SUICIDE, 2, 1, "s1 s2loc spree_lost", "s1", "weapontuba", _("^BG%s^K1 hurt their own ears with the @!#%%'n Tuba%s%s"), "") \
MSG_INFO_NOTIF(1, INFO_WEAPON_VAPORIZER_MURDER, 3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1", "weaponminstanex", _("^BG%s%s^K1 has been sublimated by ^BG%s^K1's Vaporizer%s%s"), "") \
+ MSG_INFO_NOTIF(1, INFO_WEAPON_VAPORIZER_MURDER_CHARGE, 3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1", "weaponminstanex", _("^BG%s%s^K1 was blasted by ^BG%s^K1's charged Vaporizer%s%s"), "") \
+ MSG_INFO_NOTIF(1, INFO_WEAPON_VAPORIZER_SUICIDE, 2, 1, "s1 s2loc spree_lost", "s1", "weaponminstanex", _("^BG%s^K1 blasted themself with their charged Vaporizer%s%s"), "") \
MSG_INFO_NOTIF(1, INFO_WEAPON_VORTEX_MURDER, 3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1", "weaponnex", _("^BG%s%s^K1 has been vaporized by ^BG%s^K1's Vortex%s%s"), "")
#define MULTITEAM_CENTER2(default,prefix,strnum,flnum,args,cpid,durcnt,normal,gentle) \
MSG_CENTER_NOTIF(1, CENTER_ALONE, 0, 0, "", NO_CPID, "0 0", _("^F4You are now alone!"), "") \
MSG_CENTER_NOTIF(1, CENTER_ASSAULT_ATTACKING, 0, 0, "", CPID_ASSAULT_ROLE, "0 0", _("^BGYou are attacking!"), "") \
MSG_CENTER_NOTIF(1, CENTER_ASSAULT_DEFENDING, 0, 0, "", CPID_ASSAULT_ROLE, "0 0", _("^BGYou are defending!"), "") \
+ MULTITEAM_CENTER(1, CENTER_CONQUEST_CAPTURE_, 4, 1, 0, "s1", CPID_CONQUEST, "0 0", _("^BG%s^BG has been captured by the ^TC^TT^BG team"), "") \
+ MSG_CENTER_NOTIF(1, CENTER_CONQUEST_CAPTURE_TEAM, 1, 0, "s1", CPID_CONQUEST, "0 0", _("^BGYour team has captured %s"), "") \
+ MULTITEAM_CENTER(1, CENTER_CONQUEST_LIBERATE_, 4, 1, 0, "s1", CPID_CONQUEST, "0 0", _("^BG%s^BG has been liberated from the ^TC^TT^BG team"), "") \
+ MULTITEAM_CENTER(1, CENTER_CONQUEST_LIBERATE_TEAM_, 4, 1, 0, "s1", CPID_CONQUEST, "0 0", _("^BGYour team has liberated %s^BG from the ^TC^TT^BG team"), "") \
+ MULTITEAM_CENTER(1, CENTER_CONQUEST_LOST_, 4, 1, 0, "s1", CPID_CONQUEST, "0 0", _("^BG%s^BG has been lost to the ^TC^TT^BG team"), "") \
MSG_CENTER_NOTIF(1, CENTER_COUNTDOWN_BEGIN, 0, 0, "", CPID_ROUND, "2 0", _("^F4Begin!"), "") \
MSG_CENTER_NOTIF(1, CENTER_COUNTDOWN_GAMESTART, 0, 1, "", CPID_ROUND, "1 f1", _("^F4Game starts in ^COUNT"), "") \
MSG_CENTER_NOTIF(1, CENTER_COUNTDOWN_ROUNDSTART, 0, 1, "", CPID_ROUND, "1 f1", _("^F4Round starts in ^COUNT"), "") \
MSG_CENTER_NOTIF(1, CENTER_CAMPCHECK, 0, 0, "", CPID_CAMPCHECK, "0 0", _("^F2Don't camp!"), "") \
MSG_CENTER_NOTIF(1, CENTER_COINTOSS, 1, 0, "s1", NO_CPID, "0 0", _("^F2Throwing coin... Result: %s^F2!"), "") \
MSG_CENTER_NOTIF(1, CENTER_CTF_CAPTURESHIELD_FREE, 0, 0, "", CPID_CTF_CAPSHIELD, "0 0", _("^BGYou are now free.\n^BGFeel free to ^F2try to capture^BG the flag again\n^BGif you think you will succeed."), "") \
- MSG_CENTER_NOTIF(1, CENTER_CTF_CAPTURESHIELD_SHIELDED, 0, 0, "", CPID_CTF_CAPSHIELD, "0 0", _("^BGYou are now ^F1shielded^BG from the flag\n^BGfor ^F2too many unsuccessful attempts^BG to capture.\n^BGMake some defensive scores before trying again."), "") \
- MULTITEAM_CENTER(1, CENTER_CTF_CAPTURE_, 2, 0, 0, "", CPID_CTF_LOWPRIO, "0 0", _("^BGYou captured the ^TC^TT^BG flag!"), "") \
+ MSG_CENTER_NOTIF(1, CENTER_CTF_CAPTURESHIELD_INACTIVE, 0, 0, "", CPID_CTF_CAPSHIELD, "0 0", _("^BGThis flag is currently inactive"), "") \
+ MSG_CENTER_NOTIF(1, CENTER_CTF_CAPTURESHIELD_SHIELDED, 0, 0, "", CPID_CTF_CAPSHIELD, "0 0", _("^BGYou are now ^F1shielded^BG from the flag(s)\n^BGfor ^F2too many unsuccessful attempts^BG to capture.\n^BGMake some defensive scores before trying again."), "") \
+ MULTITEAM_CENTER(1, CENTER_CTF_CAPTURE_, 4, 0, 0, "", CPID_CTF_LOWPRIO, "0 0", _("^BGYou captured the ^TC^TT^BG flag!"), "") \
+ MSG_CENTER_NOTIF(1, CENTER_CTF_CAPTURE_NEUTRAL, 0, 0, "", CPID_CTF_LOWPRIO, "0 0", _("^BGYou captured the flag!"), "") \
MSG_CENTER_NOTIF(1, CENTER_CTF_FLAG_THROW_PUNISH, 0, 1, "f1secs", CPID_CTF_LOWPRIO, "0 0", _("^BGToo many flag throws! Throwing disabled for %s."), "") \
- MULTITEAM_CENTER(1, CENTER_CTF_PASS_OTHER_, 2, 2, 0, "s1 s2", CPID_CTF_PASS, "0 0", _("^BG%s^BG passed the ^TC^TT^BG flag to %s"), "") \
- MULTITEAM_CENTER(1, CENTER_CTF_PASS_RECEIVED_, 2, 1, 0, "s1", CPID_CTF_PASS, "0 0", _("^BGYou received the ^TC^TT^BG flag from %s"), "") \
+ MULTITEAM_CENTER(1, CENTER_CTF_PASS_OTHER_, 4, 2, 0, "s1 s2", CPID_CTF_PASS, "0 0", _("^BG%s^BG passed the ^TC^TT^BG flag to %s"), "") \
+ MSG_CENTER_NOTIF(1, CENTER_CTF_PASS_OTHER_NEUTRAL, 2, 0, "s1 s2", CPID_CTF_PASS, "0 0", _("^BG%s^BG passed the flag to %s"), "") \
+ MULTITEAM_CENTER(1, CENTER_CTF_PASS_RECEIVED_, 4, 1, 0, "s1", CPID_CTF_PASS, "0 0", _("^BGYou received the ^TC^TT^BG flag from %s"), "") \
+ MSG_CENTER_NOTIF(1, CENTER_CTF_PASS_RECEIVED_NEUTRAL, 1, 0, "s1", CPID_CTF_PASS, "0 0", _("^BGYou received the flag from %s"), "") \
MSG_CENTER_NOTIF(1, CENTER_CTF_PASS_REQUESTED, 1, 0, "s1 pass_key", CPID_CTF_PASS, "0 0", _("^BG%s^BG requests you to pass the flag%s"), "") \
MSG_CENTER_NOTIF(1, CENTER_CTF_PASS_REQUESTING, 1, 0, "s1", CPID_CTF_PASS, "0 0", _("^BGRequesting %s^BG to pass you the flag"), "") \
- MULTITEAM_CENTER(1, CENTER_CTF_PASS_SENT_, 2, 1, 0, "s1", CPID_CTF_PASS, "0 0", _("^BGYou passed the ^TC^TT^BG flag to %s"), "") \
- MULTITEAM_CENTER(1, CENTER_CTF_PICKUP_, 2, 0, 0, "", CPID_CTF_LOWPRIO, "0 0", _("^BGYou got the ^TC^TT^BG flag!"), "") \
+ MULTITEAM_CENTER(1, CENTER_CTF_PASS_SENT_, 4, 1, 0, "s1", CPID_CTF_PASS, "0 0", _("^BGYou passed the ^TC^TT^BG flag to %s"), "") \
+ MSG_CENTER_NOTIF(1, CENTER_CTF_PASS_SENT_NEUTRAL, 1, 0, "s1", CPID_CTF_PASS, "0 0", _("^BGYou passed the flag to %s"), "") \
+ MULTITEAM_CENTER(1, CENTER_CTF_PICKUP_, 4, 0, 0, "", CPID_CTF_LOWPRIO, "0 0", _("^BGYou got the ^TC^TT^BG flag!"), "") \
+ MSG_CENTER_NOTIF(1, CENTER_CTF_PICKUP_NEUTRAL, 0, 0, "", CPID_CTF_LOWPRIO, "0 0", _("^BGYou got the flag!"), "") \
+ MSG_CENTER_NOTIF(1, CENTER_CTF_PICKUP_TEAM, 1, 0, "s1", CPID_CTF_LOWPRIO, "0 0", _("^BGYou got your %steam^BG's flag, return it!"), "") \
+ MSG_CENTER_NOTIF(1, CENTER_CTF_PICKUP_TEAM_ENEMY, 1, 0, "s1", CPID_CTF_LOWPRIO, "0 0", _("^BGYou got the %senemy^BG's flag, return it!"), "") \
MSG_CENTER_NOTIF(1, CENTER_CTF_PICKUP_ENEMY, 1, 0, "s1", CPID_CTF_LOWPRIO, "0 0", _("^BGThe %senemy^BG got your flag! Retrieve it!"), "") \
MSG_CENTER_NOTIF(1, CENTER_CTF_PICKUP_ENEMY_VERBOSE, 2, 0, "s1 s2 s1", CPID_CTF_LOWPRIO, "0 0", _("^BGThe %senemy (^BG%s%s)^BG got your flag! Retrieve it!"), "") \
- MSG_CENTER_NOTIF(1, CENTER_CTF_PICKUP_TEAM, 1, 0, "s1", CPID_CTF_LOWPRIO, "0 0", _("^BGYour %steam mate^BG got the flag! Protect them!"), "") \
- MSG_CENTER_NOTIF(1, CENTER_CTF_PICKUP_TEAM_VERBOSE, 2, 0, "s1 s2 s1", CPID_CTF_LOWPRIO, "0 0", _("^BGYour %steam mate (^BG%s%s)^BG got the flag! Protect them!"), "") \
- MULTITEAM_CENTER(1, CENTER_CTF_RETURN_, 2, 0, 0, "", CPID_CTF_LOWPRIO, "0 0", _("^BGYou returned the ^TC^TT^BG flag!"), "") \
+ MSG_CENTER_NOTIF(1, CENTER_CTF_PICKUP_ENEMY_NEUTRAL, 1, 0, "s1", CPID_CTF_LOWPRIO, "0 0", _("^BGThe %senemy^BG got the flag! Retrieve it!"), "") \
+ MSG_CENTER_NOTIF(1, CENTER_CTF_PICKUP_ENEMY_NEUTRAL_VERBOSE, 2, 0, "s1 s2 s1",CPID_CTF_LOWPRIO, "0 0", _("^BGThe %senemy (^BG%s%s)^BG got the flag! Retrieve it!"), "") \
+ MSG_CENTER_NOTIF(1, CENTER_CTF_PICKUP_ENEMY_TEAM, 1, 0, "s1", CPID_CTF_LOWPRIO, "0 0", _("^BGThe %senemy^BG got their flag! Retrieve it!"), "") \
+ MSG_CENTER_NOTIF(1, CENTER_CTF_PICKUP_ENEMY_TEAM_VERBOSE,2, 0, "s1 s2 s1", CPID_CTF_LOWPRIO, "0 0", _("^BGThe %senemy (^BG%s%s)^BG got their flag! Retrieve it!"), "") \
+ MULTITEAM_CENTER(1, CENTER_CTF_PICKUP_TEAM_, 4, 1, 0, "s1", CPID_CTF_LOWPRIO, "0 0", _("^BGYour %steam mate^BG got the ^TC^TT^BG flag! Protect them!"), "") \
+ MULTITEAM_CENTER(1, CENTER_CTF_PICKUP_TEAM_VERBOSE_, 4, 2, 0, "s1 s2 s1", CPID_CTF_LOWPRIO, "0 0", _("^BGYour %steam mate (^BG%s%s)^BG got the ^TC^TT^BG flag! Protect them!"), "") \
+ MSG_CENTER_NOTIF(1, CENTER_CTF_PICKUP_TEAM_NEUTRAL, 1, 0, "s1", CPID_CTF_LOWPRIO, "0 0", _("^BGYour %steam mate^BG got the flag! Protect them!"), "") \
+ MSG_CENTER_NOTIF(1, CENTER_CTF_PICKUP_TEAM_VERBOSE_NEUTRAL, 2, 0, "s1 s2 s1", CPID_CTF_LOWPRIO, "0 0", _("^BGYour %steam mate (^BG%s%s)^BG got the flag! Protect them!"), "") \
+ MULTITEAM_CENTER(1, CENTER_CTF_RETURN_, 4, 0, 0, "", CPID_CTF_LOWPRIO, "0 0", _("^BGYou returned the ^TC^TT^BG flag!"), "") \
MSG_CENTER_NOTIF(1, CENTER_CTF_STALEMATE_CARRIER, 0, 0, "", CPID_STALEMATE, "0 0", _("^BGStalemate! Enemies can now see you on radar!"), "") \
MSG_CENTER_NOTIF(1, CENTER_CTF_STALEMATE_OTHER, 0, 0, "", CPID_STALEMATE, "0 0", _("^BGStalemate! Flag carriers can now be seen by enemies on radar!"), "") \
MSG_CENTER_NOTIF(1, CENTER_DEATH_MURDER_FRAG, 1, 1, "spree_cen s1", NO_CPID, "0 0", _("^K3%sYou fragged ^BG%s"), _("^K3%sYou scored against ^BG%s")) \
MSG_CENTER_NOTIF(1, CENTER_DEATH_SELF_VH_RAPT_DEATH, 0, 0, "", NO_CPID, "0 0", _("^K1You got caught in the blast of a Raptor explosion!"), "") \
MSG_CENTER_NOTIF(1, CENTER_DEATH_SELF_VH_SPID_DEATH, 0, 0, "", NO_CPID, "0 0", _("^K1You got caught in the blast of a Spiderbot explosion!"), "") \
MSG_CENTER_NOTIF(1, CENTER_DEATH_SELF_VH_SPID_ROCKET, 0, 0, "", NO_CPID, "0 0", _("^K1You were blasted to bits by a Spiderbot rocket!"), "") \
+ MSG_CENTER_NOTIF(1, CENTER_DEATH_SELF_VH_TANK_DEATH, 0, 0, "", NO_CPID, "0 0", _("^K1You were caught in the blast of a Tank explosion!"), "") \
MSG_CENTER_NOTIF(1, CENTER_DEATH_SELF_VH_WAKI_DEATH, 0, 0, "", NO_CPID, "0 0", _("^K1You got caught in the blast of a Racer explosion!"), "") \
MSG_CENTER_NOTIF(1, CENTER_DEATH_SELF_VH_WAKI_ROCKET, 0, 0, "", NO_CPID, "0 0", _("^K1You couldn't find shelter from a Racer rocket!"), "") \
MSG_CENTER_NOTIF(1, CENTER_DEATH_SELF_VOID, 0, 0, "", NO_CPID, "0 0", _("^K1Watch your step!"), "") \
MSG_CENTER_NOTIF(1, CENTER_DOOR_LOCKED_ALSONEED, 0, 0, "", NO_CPID, "0 0", _("^BGYou also need %s^BG!"), "") \
MSG_CENTER_NOTIF(1, CENTER_DOOR_UNLOCKED, 0, 0, "", NO_CPID, "0 0", _("^BGDoor unlocked!"), "") \
MSG_CENTER_NOTIF(1, CENTER_EXTRALIVES, 0, 0, "", NO_CPID, "0 0", _("^F2You picked up some extra lives"), "") \
+ MSG_CENTER_NOTIF(1, CENTER_FREEZE_THAWING, 0, 0, "", CPID_FREEZE, "5 0", _("^BGThawing slowly now\n^BGJump to re-spawn at base instantly"), "") \
MSG_CENTER_NOTIF(1, CENTER_FREEZETAG_FREEZE, 1, 0, "s1", NO_CPID, "0 0", _("^K3You froze ^BG%s"), "") \
MSG_CENTER_NOTIF(1, CENTER_FREEZETAG_FROZEN, 1, 0, "s1", NO_CPID, "0 0", _("^K1You were frozen by ^BG%s"), "") \
MSG_CENTER_NOTIF(1, CENTER_FREEZETAG_REVIVE, 1, 0, "s1", NO_CPID, "0 0", _("^K3You revived ^BG%s"), "") \
MSG_CENTER_NOTIF(1, CENTER_FREEZETAG_REVIVE_SELF, 0, 0, "", NO_CPID, "0 0", _("^K3You revived yourself"), "") \
MSG_CENTER_NOTIF(1, CENTER_FREEZETAG_REVIVED, 1, 0, "s1", NO_CPID, "0 0", _("^K3You were revived by ^BG%s"), "") \
MSG_CENTER_NOTIF(1, CENTER_FREEZETAG_AUTO_REVIVED, 0, 1, "f1", NO_CPID, "0 0", _("^K3You were automatically revived after %s second(s)"), "") \
+ MSG_CENTER_NOTIF(1, CENTER_FREEZETAG_STALEMATE, 0, 0, "", CPID_STALEMATE, "0 0", _("^BGStalemate! Enemies can now be seen on the radar!"), "") \
+ MSG_CENTER_NOTIF(1, CENTER_GENERATOR_UNDERATTACK, 0, 0, "", NO_CPID, "0 0", _("^BGThe generator is under attack!"), "") \
+ MSG_CENTER_NOTIF(1, CENTER_HEADSHOT, 0, 0, "", NO_CPID, "0 0", _("^F2Headshot!"), "") \
+ MSG_CENTER_NOTIF(1, CENTER_ROUND_INFECTION_WIN, 0, 0, "", CPID_ROUND, "0 0", _("^BGRound over, all players have been infected"), "") \
MULTITEAM_CENTER(1, CENTER_ROUND_TEAM_WIN_, 4, 0, 0, "", CPID_ROUND, "0 0", _("^TC^TT^BG team wins the round"), "") \
MSG_CENTER_NOTIF(1, CENTER_ROUND_PLAYER_WIN, 1, 0, "s1", CPID_ROUND, "0 0", _("^BG%s^BG wins the round"), "") \
MSG_CENTER_NOTIF(1, CENTER_FREEZETAG_SELF, 0, 0, "", NO_CPID, "0 0", _("^K1You froze yourself"), "") \
MSG_CENTER_NOTIF(1, CENTER_FREEZETAG_SPAWN_LATE, 0, 0, "", NO_CPID, "0 0", _("^K1Round already started, you spawn as frozen"), "") \
+ MSG_CENTER_NOTIF(1, CENTER_INFECTION_INFECTED, 1, 0, "s1", NO_CPID, "0 0", _("^BGYou have been infected by ^BG%s"), "") \
MSG_CENTER_NOTIF(1, CENTER_INVASION_SUPERMONSTER, 1, 0, "s1", NO_CPID, "0 0", _("^K1A %s has arrived!"), "") \
MSG_CENTER_NOTIF(1, CENTER_ITEM_BUFF_DROP, 0, 1, "item_buffname", CPID_ITEM, "item_centime 0", _("^BGYou dropped the %s^BG buff!"), "") \
MSG_CENTER_NOTIF(1, CENTER_ITEM_BUFF_GOT, 0, 1, "item_buffname", CPID_ITEM, "item_centime 0", _("^BGYou got the %s^BG buff!"), "") \
MSG_CENTER_NOTIF(1, CENTER_ITEM_WEAPON_NOAMMO, 0, 1, "item_wepname", CPID_ITEM, "item_centime 0", _("^BGYou don't have enough ammo for the ^F1%s"), "") \
MSG_CENTER_NOTIF(1, CENTER_ITEM_WEAPON_PRIMORSEC, 0, 3, "item_wepname f2primsec f3primsec", CPID_ITEM, "item_centime 0", _("^F1%s %s^BG is unable to fire, but its ^F1%s^BG can"), "") \
MSG_CENTER_NOTIF(1, CENTER_ITEM_WEAPON_UNAVAILABLE, 0, 1, "item_wepname", CPID_ITEM, "item_centime 0", _("^F1%s^BG is ^F4not available^BG on this map"), "") \
+ MSG_CENTER_NOTIF(1, CENTER_JAILBREAK_DEFENSE, 0, 0, "", CPID_JAILBREAK, "0 0", _("^F2You gained some bonus score for defending the control point!"), "") \
+ MSG_CENTER_NOTIF(1, CENTER_JAILBREAK_ESCAPE, 0, 1, "death_team", CPID_JAILBREAK, "0 0", _("^K1The %s^K1 is escaping!"), "") \
+ MSG_CENTER_NOTIF(1, CENTER_JAILBREAK_FREE, 0, 0, "", CPID_JAILBREAK, "0 0", _("^F2You're free! Run away!"), "") \
+ MSG_CENTER_NOTIF(1, CENTER_JAILBREAK_FREED, 0, 0, "", CPID_JAILBREAK, "0 0", _("^K3You bailed your teammates out of jail!"), "") \
+ MSG_CENTER_NOTIF(1, CENTER_JAILBREAK_IMPRISON, 0, 0, "", CPID_JAILBREAK, "0 0", _("^K1You're in jail, prisoner!"), "") \
+ MSG_CENTER_NOTIF(1, CENTER_JAILBREAK_NOENTRY, 0, 0, "", CPID_JAILBREAK, "0 0", _("^K1No sneaking into jail!"), "") \
+ MSG_CENTER_NOTIF(1, CENTER_JAILBREAK_NOTREADY, 0, 0, "", CPID_JAILBREAK, "0 0", _("^BGThis control point is not ready yet"), "") \
+ MSG_CENTER_NOTIF(1, CENTER_JAILBREAK_TEAMALIVE, 0, 0, "", CPID_JAILBREAK, "0 0", _("^BGYour team is already free"), "") \
+ MSG_CENTER_NOTIF(1, CENTER_JAILBREAK_TOOLATE, 0, 0, "", CPID_JAILBREAK, "0 0", _("^BGSomeone is already capturing this control point"), "") \
+ MSG_CENTER_NOTIF(1, CENTER_JAILBREAK_WRONGTEAM, 0, 0, "", CPID_JAILBREAK, "0 0", _("^BGYou can't capture your team's control points"), "") \
MSG_CENTER_NOTIF(1, CENTER_JOIN_NOSPAWNS, 0, 0, "", CPID_PREVENT_JOIN, "0 0", _("^K1No spawnpoints available!\nHope your team can fix it..."), "") \
MSG_CENTER_NOTIF(1, CENTER_JOIN_PREVENT, 0, 0, "", CPID_PREVENT_JOIN, "0 0", _("^K1You may not join the game at this time.\nThe player limit reached maximum capacity."), "") \
MSG_CENTER_NOTIF(1, CENTER_KEEPAWAY_DROPPED, 1, 0, "s1", CPID_KEEPAWAY, "0 0", _("^BG%s^BG has dropped the ball!"), "") \
MSG_CENTER_NOTIF(1, CENTER_KEEPAWAY_PICKUP, 1, 0, "s1", CPID_KEEPAWAY, "0 0", _("^BG%s^BG has picked up the ball!"), "") \
MSG_CENTER_NOTIF(1, CENTER_KEEPAWAY_PICKUP_SELF, 0, 0, "", CPID_KEEPAWAY, "0 0", _("^BGYou picked up the ball"), "") \
MSG_CENTER_NOTIF(1, CENTER_KEEPAWAY_WARN, 0, 0, "", CPID_KEEPAWAY_WARN, "0 0", _("^BGKilling people while you don't have the ball gives no points!"), "") \
+ MSG_CENTER_NOTIF(1, CENTER_KEYHUNT_CAPTURE, 0, 0, "", CPID_KEYHUNT_LOWPRIO, "0 0", _("^BGYou captured the keys!"), "") \
MSG_CENTER_NOTIF(1, CENTER_KEYHUNT_HELP, 0, 0, "", CPID_KEYHUNT, "0 0", _("^BGAll keys are in your team's hands!\nHelp the key carriers to meet!"), "") \
MULTITEAM_CENTER(1, CENTER_KEYHUNT_INTERFERE_, 4, 0, 0, "", CPID_KEYHUNT, "0 0", _("^BGAll keys are in ^TC^TT team^BG's hands!\nInterfere ^F4NOW^BG!"), "") \
MSG_CENTER_NOTIF(1, CENTER_KEYHUNT_MEET, 0, 0, "", CPID_KEYHUNT, "0 0", _("^BGAll keys are in your team's hands!\nMeet the other key carriers ^F4NOW^BG!"), "") \
- MSG_CENTER_NOTIF(1, CENTER_KEYHUNT_ROUNDSTART, 0, 1, "", CPID_KEYHUNT_OTHER, "1 f1", _("^F4Round will start in ^COUNT"), "") \
- MSG_CENTER_NOTIF(1, CENTER_KEYHUNT_SCAN, 0, 1, "", CPID_KEYHUNT_OTHER, "f1 0", _("^BGScanning frequency range..."), "") \
+ MSG_CENTER_NOTIF(1, CENTER_KEYHUNT_KEY_THROW_PUNISH, 0, 1, "f1secs", CPID_KEYHUNT_LOWPRIO, "0 0", _("^BGToo many key throws! Throwing disabled for %s."), "") \
+ MULTITEAM_CENTER(1, CENTER_KEYHUNT_PASS_OTHER_, 4, 2, 0, "s1 s2", CPID_KEYHUNT_PASS, "0 0", _("^BG%s^BG passed the ^TC^TT^BG key to %s"), "") \
+ MULTITEAM_CENTER(1, CENTER_KEYHUNT_PASS_RECEIVED_, 4, 1, 0, "s1", CPID_KEYHUNT_PASS, "0 0", _("^BGYou received the ^TC^TT^BG key from %s"), "") \
+ MSG_CENTER_NOTIF(1, CENTER_KEYHUNT_PASS_REQUESTED, 1, 0, "s1 pass_key", CPID_KEYHUNT_PASS, "0 0", _("^BG%s^BG requests you to pass the key%s"), "") \
+ MSG_CENTER_NOTIF(1, CENTER_KEYHUNT_PASS_REQUESTING, 1, 0, "s1", CPID_KEYHUNT_PASS, "0 0", _("^BGRequesting %s^BG to pass you the key"), "") \
+ MULTITEAM_CENTER(1, CENTER_KEYHUNT_PASS_SENT_, 4, 1, 0, "s1", CPID_KEYHUNT_PASS, "0 0", _("^BGYou passed the ^TC^TT^BG key to %s"), "") \
+ MULTITEAM_CENTER(1, CENTER_KEYHUNT_PICKUP_, 4, 0, 0, "", CPID_KEYHUNT_LOWPRIO, "0 0", _("^BGYou got the ^TC^TT^BG key!"), "") \
+ MULTITEAM_CENTER(1, CENTER_KEYHUNT_PICKUP_TEAM_, 4, 1, 0, "s1", CPID_KEYHUNT_LOWPRIO, "0 0", _("^BGYour %steam mate^BG got the ^TC^TT^BG key! Protect them!"), "") \
+ MULTITEAM_CENTER(1, CENTER_KEYHUNT_PICKUP_TEAM_VERBOSE_,4, 2, 0, "s1 s2 s1", CPID_KEYHUNT_LOWPRIO, "0 0", _("^BGYour %steam mate (^BG%s%s)^BG got the ^TC^TT^BG key! Protect them!"), "") \
MULTITEAM_CENTER(1, CENTER_KEYHUNT_START_, 4, 0, 0, "", CPID_KEYHUNT, "0 0", _("^BGYou are starting with the ^TC^TT Key"), "") \
- MSG_CENTER_NOTIF(1, CENTER_KEYHUNT_WAIT, 0, 1, "missing_teams", CPID_KEYHUNT_OTHER, "0 0", _("^BGWaiting for players to join...\nNeed active players for: %s"), "") \
+ MULTITEAM_CENTER(1, CENTER_KEYHUNT_START_TEAM_, 4, 1, 0, "s1", CPID_KEYHUNT_LOWPRIO, "0 0", _("^BGYour %steam mate^BG is starting with the ^TC^TT^BG key!\nProtect them!"), "") \
+ MULTITEAM_CENTER(1, CENTER_KEYHUNT_START_TEAM_VERBOSE_, 4, 2, 0, "s1 s2 s1", CPID_KEYHUNT_LOWPRIO, "0 0", _("^BGYour %steam mate (^BG%s%s)^BG is starting with the ^TC^TT^BG key!\nProtect them!"), "") \
MSG_CENTER_NOTIF(1, CENTER_LMS_NOLIVES, 0, 0, "", CPID_LMS, "0 0", _("^BGYou have no lives left, you must wait until the next match"), "") \
MSG_CENTER_NOTIF(1, CENTER_MISSING_TEAMS, 0, 1, "missing_teams", CPID_MISSING_TEAMS, "-1 0", _("^BGWaiting for players to join...\nNeed active players for: %s"), "") \
MSG_CENTER_NOTIF(1, CENTER_MISSING_PLAYERS, 0, 1, "f1", CPID_MISSING_PLAYERS, "-1 0", _("^BGWaiting for %s player(s) to join..."), "") \
+ MSG_CENTER_NOTIF(1, CENTER_INSTAGIB_DOWNGRADE, 0, 0, "", CPID_INSTAGIB_FINDAMMO,"5 0", _("^BGYour weapon has been downgraded until you find some ammo!"), "") \
MSG_CENTER_NOTIF(1, CENTER_INSTAGIB_FINDAMMO, 0, 0, "", CPID_INSTAGIB_FINDAMMO,"1 9", _("^F4^COUNT^BG left to find some ammo!"), "") \
MSG_CENTER_NOTIF(1, CENTER_INSTAGIB_FINDAMMO_FIRST, 0, 0, "", CPID_INSTAGIB_FINDAMMO,"1 10", _("^BGGet some ammo or you'll be dead in ^F4^COUNT^BG!"), _("^BGGet some ammo! ^F4^COUNT^BG left!")) \
MSG_CENTER_NOTIF(1, CENTER_INSTAGIB_LIVES_REMAINING, 0, 1, "f1", NO_CPID, "0 0", _("^F2Extra lives remaining: ^K1%s"), "") \
- MSG_CENTER_NOTIF(1, CENTER_MOTD, 1, 0, "s1", CPID_MOTD, "-1 0", "^BG%s", "") \
+ MSG_CENTER_NOTIF(1, CENTER_MOTD, 1, 0, "s1", CPID_MOTD, "5 0", "^BG%s", "") \
MSG_CENTER_NOTIF(1, CENTER_NIX_COUNTDOWN, 0, 2, "item_wepname", CPID_NIX, "1 f2", _("^F2^COUNT^BG until weapon change...\nNext weapon: ^F1%s"), "") \
MSG_CENTER_NOTIF(1, CENTER_NIX_NEWWEAPON, 0, 1, "item_wepname", CPID_NIX, "0 0", _("^F2Active weapon: ^F1%s"), "") \
MSG_CENTER_NOTIF(1, CENTER_NADE, 0, 0, "", NO_CPID, "0 0", _("^BGPress ^F2DROPWEAPON^BG again to toss the grenade!"), "") \
- MSG_CENTER_NOTIF(1, CENTER_ONS_NOTSHIELDED, 0, 0, "", CPID_ONSLAUGHT, "0 0", _("^K1Your generator is NOT shielded!\n^BGRe-capture controlpoints to shield it!"), "") \
+ MSG_CENTER_NOTIF(1, CENTER_ONS_CAPTURE, 1, 0, "s1", CPID_ONSLAUGHT, "0 0", _("^BGYou captured %s^BG control point"), "") \
+ MULTITEAM_CENTER(1, CENTER_ONS_CAPTURE_, 4, 1, 0, "s1", CPID_ONSLAUGHT, "0 0", _("^TC^TT^BG team captured %s^BG control point"), "") \
+ MSG_CENTER_NOTIF(1, CENTER_ONS_CONTROLPOINT_SHIELDED, 0, 0, "", CPID_ONS_CAPSHIELD, "0 0", _("^BGThis control point currently cannot be captured"), "") \
+ MSG_CENTER_NOTIF(1, CENTER_ONS_GENERATOR_SHIELDED, 0, 0, "", CPID_ONS_CAPSHIELD, "0 0", _("^BGThe enemy generator cannot be destroyed yet\n^F2Capture some control points to unshield it"), "") \
+ MULTITEAM_CENTER(1, CENTER_ONS_NOTSHIELDED_, 4, 0, 0, "", CPID_ONSLAUGHT, "0 0", _("^BGThe ^TCenemy^BG generator is no longer shielded!"), "") \
+ MSG_CENTER_NOTIF(1, CENTER_ONS_NOTSHIELDED_TEAM, 0, 0, "", CPID_ONSLAUGHT, "0 0", _("^K1Your generator is NOT shielded!\n^BGRe-capture control points to shield it!"), "") \
+ MSG_CENTER_NOTIF(1, CENTER_ONS_TELEPORT, 0, 0, "pass_key", CPID_ONSLAUGHT, "0 0", _("^BGPress ^F2DROPFLAG%s^BG to teleport"), "") \
+ MSG_CENTER_NOTIF(1, CENTER_ONS_TELEPORT_ANTISPAM, 0, 1, "f1secs", CPID_ONSLAUGHT, "0 0", _("^BGTeleporting disabled for %s"), "") \
+ MSG_CENTER_NOTIF(1, CENTER_OVERKILL_CHARGE, 0, 0, "", CPID_OVERKILL, "0 0", _("^BGYour weapon's charge is too low, release the attack button & wait for it to charge"), "") \
MSG_CENTER_NOTIF(1, CENTER_OVERTIME_FRAG, 0, 0, "", CPID_OVERTIME, "0 0", _("^F2Now playing ^F4OVERTIME^F2!\nKeep fragging until we have a winner!"), _("^F2Now playing ^F4OVERTIME^F2!\nKeep scoring until we have a winner!")) \
MSG_CENTER_NOTIF(1, CENTER_OVERTIME_CONTROLPOINT, 0, 0, "", CPID_OVERTIME, "5 0", _("^F2Now playing ^F4OVERTIME^F2!\n\nGenerators are now decaying.\nThe more control points your team holds,\nthe faster the enemy generator decays"), "") \
MSG_CENTER_NOTIF(1, CENTER_OVERTIME_TIME, 0, 1, "f1time", CPID_OVERTIME, "0 0", _("^F2Now playing ^F4OVERTIME^F2!\n^BGAdded ^F4%s^BG to the game!"), "") \
+ MSG_CENTER_NOTIF(1, CENTER_PIGGYBACK_CARRYING, 1, 0, "s1", CPID_PIGGYBACK, "0 0", _("^BGYou are now carrying %s^BG!"), "") \
+ MSG_CENTER_NOTIF(1, CENTER_PIGGYBACK_RIDING, 1, 0, "s1", CPID_PIGGYBACK, "0 0", _("^BGYou are now riding %s^BG!"), "") \
+ MSG_CENTER_NOTIF(1, CENTER_PORTO_FAILED, 0, 0, "", NO_CPID, "0 0", _("^K1Portal deployment failed.\n\n^F2Catch it to try again!"), "") \
MSG_CENTER_NOTIF(1, CENTER_PORTO_CREATED_IN, 0, 0, "", NO_CPID, "0 0", _("^K1In^BG-portal created"), "") \
MSG_CENTER_NOTIF(1, CENTER_PORTO_CREATED_OUT, 0, 0, "", NO_CPID, "0 0", _("^F3Out^BG-portal created"), "") \
- MSG_CENTER_NOTIF(1, CENTER_PORTO_FAILED, 0, 0, "", NO_CPID, "0 0", _("^K1Portal deployment failed.\n\n^F2Catch it to try again!"), "") \
- MSG_CENTER_NOTIF(1, CENTER_POWERDOWN_INVISIBILITY, 0, 0, "", CPID_POWERUP, "0 0", _("^F2Invisibility has worn off"), "") \
- MSG_CENTER_NOTIF(1, CENTER_POWERDOWN_SHIELD, 0, 0, "", CPID_POWERUP, "0 0", _("^F2Shield has worn off"), "") \
- MSG_CENTER_NOTIF(1, CENTER_POWERDOWN_SPEED, 0, 0, "", CPID_POWERUP, "0 0", _("^F2Speed has worn off"), "") \
- MSG_CENTER_NOTIF(1, CENTER_POWERDOWN_STRENGTH, 0, 0, "", CPID_POWERUP, "0 0", _("^F2Strength has worn off"), "") \
- MSG_CENTER_NOTIF(1, CENTER_POWERUP_INVISIBILITY, 0, 0, "", CPID_POWERUP, "0 0", _("^F2You are invisible"), "") \
- MSG_CENTER_NOTIF(1, CENTER_POWERUP_SHIELD, 0, 0, "", CPID_POWERUP, "0 0", _("^F2Shield surrounds you"), "") \
- MSG_CENTER_NOTIF(1, CENTER_POWERUP_SPEED, 0, 0, "", CPID_POWERUP, "0 0", _("^F2You are on speed"), "") \
- MSG_CENTER_NOTIF(1, CENTER_POWERUP_STRENGTH, 0, 0, "", CPID_POWERUP, "0 0", _("^F2Strength infuses your weapons with devastating power"), "") \
MSG_CENTER_NOTIF(1, CENTER_RACE_FINISHLAP, 0, 0, "", CPID_RACE_FINISHLAP, "0 0", _("^F2The race is over, finish your lap!"), "") \
MSG_CENTER_NOTIF(1, CENTER_SECONDARY_NODAMAGE, 0, 0, "", NO_CPID, "0 0", _("^BGSecondary fire inflicts no damage!"), "") \
MSG_CENTER_NOTIF(1, CENTER_SEQUENCE_COMPLETED, 0, 0, "", NO_CPID, "0 0", _("^BGSequence completed!"), "") \
MSG_CENTER_NOTIF(1, CENTER_TEAMCHANGE_SUICIDE, 0, 1, "", CPID_TEAMCHANGE, "1 f1", _("^K1Suicide in ^COUNT"), "") \
MSG_CENTER_NOTIF(1, CENTER_TIMEOUT_BEGINNING, 0, 1, "", CPID_TIMEOUT, "1 f1", _("^F4Timeout begins in ^COUNT"), "") \
MSG_CENTER_NOTIF(1, CENTER_TIMEOUT_ENDING, 0, 1, "", CPID_TIMEOUT, "1 f1", _("^F4Timeout ends in ^COUNT"), "") \
- MSG_CENTER_NOTIF(1, CENTER_WEAPON_MINELAYER_LIMIT, 0, 1, "f1", NO_CPID, "0 0", _("^BGYou cannot place more than ^F2%s^BG mines at a time"), "")
+ MSG_CENTER_NOTIF(1, CENTER_VEHICLE_ENTER, 0, 0, "pass_key", CPID_VEHICLES, "0 0", _("^BGPress ^F2DROPFLAG%s^BG to enter/exit the vehicle"), "") \
+ MSG_CENTER_NOTIF(1, CENTER_VEHICLE_ENTER_GUNNER, 0, 0, "pass_key", CPID_VEHICLES, "0 0", _("^BGPress ^F2DROPFLAG%s^BG to enter the vehicle gunner"), "") \
+ MSG_CENTER_NOTIF(1, CENTER_VEHICLE_ENTER_STEAL, 0, 0, "pass_key", CPID_VEHICLES, "0 0", _("^BGPress ^F2DROPFLAG%s^BG to steal this vehicle"), "") \
+ MSG_CENTER_NOTIF(1, CENTER_VEHICLE_STEAL, 0, 0, "", CPID_VEHICLES_OTHER, "0 0", _("^F2The enemy is stealing one of your vehicles!\n^F4Stop them!"), "") \
+ MSG_CENTER_NOTIF(1, CENTER_VEHICLE_STEAL_SELF, 0, 0, "", CPID_VEHICLES_OTHER, "4 0", _("^F2You have stolen the enemy's vehicle, you are now visible on their radar!"), "") \
+ MULTITEAM_CENTER(1, CENTER_VIP_BEGIN_, 4, 0, 0, "", CPID_VIP, "4 0", _("^BGYou are the ^TC^TT^BG VIP!\n^F2Survive or your team will lose!"), "") \
+ MSG_CENTER_NOTIF(1, CENTER_VIP_MISSING, 0, 0, "", CPID_MISSING_TEAMS, "0 0", _("^BGWaiting for VIPs...\nPickup your Soul Gem to become a VIP"), "") \
+ MSG_CENTER_NOTIF(1, CENTER_VIP_MISSING_ENEMY, 0, 0, "", CPID_MISSING_TEAMS, "0 0", _("^BGWaiting for an enemy VIP"), "") \
+ MSG_CENTER_NOTIF(1, CENTER_VIP_DROP, 1, 0, "s1", CPID_VIP, "0 0", _("^BG%s^F2 is no longer a VIP!"), "") \
+ MSG_CENTER_NOTIF(1, CENTER_VIP_DROP_PUNISH, 0, 1, "f1secs", CPID_VIP, "0 0", _("^BGToo many dropped gems! Dropping disabled for %s."), "") \
+ MSG_CENTER_NOTIF(1, CENTER_VIP_DROP_SELF, 0, 0, "", CPID_VIP, "0 0", _("^F2You are no longer a VIP!"), "") \
+ MSG_CENTER_NOTIF(1, CENTER_VIP_PICKUP, 1, 0, "s1", CPID_VIP, "0 0", _("^BG%s^F2 is now a VIP!"), "") \
+ MSG_CENTER_NOTIF(1, CENTER_VIP_PICKUP_SELF, 0, 0, "", CPID_VIP, "0 0", _("^F2You are now a VIP!"), "") \
+ MSG_CENTER_NOTIF(1, CENTER_WEAPON_MINELAYER_LIMIT, 0, 1, "f1", NO_CPID, "0 0", _("^BGYou cannot place more than ^F2%s^BG mines at a time"), "") \
+ MSG_CENTER_NOTIF(1, CENTER_JOIN_PREVENT_MINIGAME, 0, 0, "", NO_CPID, "0 0", _("^K1Cannot join given minigame session!"), "" )
#define MULTITEAM_MULTI2(default,prefix,anncepre,infopre,centerpre) \
MSG_MULTI_NOTIF(default, prefix##RED, anncepre##RED, infopre##RED, centerpre##RED) \
MULTITEAM_MULTI##teams(default,prefix,anncepre,infopre,centerpre)
#define MSG_MULTI_NOTIFICATIONS \
+ MSG_MULTI_NOTIF(1, DEATH_MURDER_BUFF, NO_MSG, INFO_DEATH_MURDER_BUFF, NO_MSG) \
MSG_MULTI_NOTIF(1, DEATH_MURDER_CHEAT, NO_MSG, INFO_DEATH_MURDER_CHEAT, NO_MSG) \
+ MSG_MULTI_NOTIF(1, DEATH_MURDER_CRUSH, NO_MSG, INFO_DEATH_MURDER_CRUSH, NO_MSG) \
MSG_MULTI_NOTIF(1, DEATH_MURDER_DROWN, NO_MSG, INFO_DEATH_MURDER_DROWN, NO_MSG) \
MSG_MULTI_NOTIF(1, DEATH_MURDER_FALL, NO_MSG, INFO_DEATH_MURDER_FALL, NO_MSG) \
MSG_MULTI_NOTIF(1, DEATH_MURDER_FIRE, NO_MSG, INFO_DEATH_MURDER_FIRE, NO_MSG) \
MSG_MULTI_NOTIF(1, DEATH_MURDER_VH_SPID_DEATH, NO_MSG, INFO_DEATH_MURDER_VH_SPID_DEATH, NO_MSG) \
MSG_MULTI_NOTIF(1, DEATH_MURDER_VH_SPID_MINIGUN, NO_MSG, INFO_DEATH_MURDER_VH_SPID_MINIGUN, NO_MSG) \
MSG_MULTI_NOTIF(1, DEATH_MURDER_VH_SPID_ROCKET, NO_MSG, INFO_DEATH_MURDER_VH_SPID_ROCKET, NO_MSG) \
+ MSG_MULTI_NOTIF(1, DEATH_MURDER_VH_TANK_DEATH, NO_MSG, INFO_DEATH_MURDER_VH_TANK_DEATH, NO_MSG) \
+ MSG_MULTI_NOTIF(1, DEATH_MURDER_VH_TANKLL48, NO_MSG, INFO_DEATH_MURDER_VH_TANKLL48, NO_MSG) \
MSG_MULTI_NOTIF(1, DEATH_MURDER_VH_WAKI_DEATH, NO_MSG, INFO_DEATH_MURDER_VH_WAKI_DEATH, NO_MSG) \
MSG_MULTI_NOTIF(1, DEATH_MURDER_VH_WAKI_GUN, NO_MSG, INFO_DEATH_MURDER_VH_WAKI_GUN, NO_MSG) \
MSG_MULTI_NOTIF(1, DEATH_MURDER_VH_WAKI_ROCKET, NO_MSG, INFO_DEATH_MURDER_VH_WAKI_ROCKET, NO_MSG) \
- MSG_MULTI_NOTIF(1, DEATH_MURDER_VENGEANCE, NO_MSG, INFO_DEATH_MURDER_VENGEANCE, NO_MSG) \
MSG_MULTI_NOTIF(1, DEATH_MURDER_VOID, NO_MSG, INFO_DEATH_MURDER_VOID, NO_MSG) \
MSG_MULTI_NOTIF(1, DEATH_SELF_AUTOTEAMCHANGE, NO_MSG, INFO_DEATH_SELF_AUTOTEAMCHANGE, CENTER_DEATH_SELF_AUTOTEAMCHANGE) \
MSG_MULTI_NOTIF(1, DEATH_SELF_BETRAYAL, NO_MSG, INFO_DEATH_SELF_BETRAYAL, CENTER_DEATH_SELF_BETRAYAL) \
MSG_MULTI_NOTIF(1, DEATH_SELF_FIRE, NO_MSG, INFO_DEATH_SELF_FIRE, CENTER_DEATH_SELF_FIRE) \
MSG_MULTI_NOTIF(1, DEATH_SELF_GENERIC, NO_MSG, INFO_DEATH_SELF_GENERIC, CENTER_DEATH_SELF_GENERIC) \
MSG_MULTI_NOTIF(1, DEATH_SELF_LAVA, NO_MSG, INFO_DEATH_SELF_LAVA, CENTER_DEATH_SELF_LAVA) \
+ MSG_MULTI_NOTIF(1, DEATH_SELF_MON_AFRIT, NO_MSG, INFO_DEATH_SELF_MON_AFRIT, CENTER_DEATH_SELF_MONSTER) \
+ MSG_MULTI_NOTIF(1, DEATH_SELF_MON_CREEPER, NO_MSG, INFO_DEATH_SELF_MON_CREEPER, CENTER_DEATH_SELF_MONSTER) \
+ MSG_MULTI_NOTIF(1, DEATH_SELF_MON_DEMON_JUMP, NO_MSG, INFO_DEATH_SELF_MON_DEMON_JUMP, CENTER_DEATH_SELF_MONSTER) \
+ MSG_MULTI_NOTIF(1, DEATH_SELF_MON_DEMON_MELEE, NO_MSG, INFO_DEATH_SELF_MON_DEMON_MELEE, CENTER_DEATH_SELF_MONSTER) \
MSG_MULTI_NOTIF(1, DEATH_SELF_MON_MAGE, NO_MSG, INFO_DEATH_SELF_MON_MAGE, CENTER_DEATH_SELF_MONSTER) \
+ MSG_MULTI_NOTIF(1, DEATH_SELF_MON_OGRE_GRENADE, NO_MSG, INFO_DEATH_SELF_MON_OGRE_GRENADE, CENTER_DEATH_SELF_MONSTER) \
+ MSG_MULTI_NOTIF(1, DEATH_SELF_MON_OGRE_MACHINEGUN, NO_MSG, INFO_DEATH_SELF_MON_OGRE_MACHINEGUN, CENTER_DEATH_SELF_MONSTER) \
+ MSG_MULTI_NOTIF(1, DEATH_SELF_MON_OGRE_MELEE, NO_MSG, INFO_DEATH_SELF_MON_OGRE_MELEE, CENTER_DEATH_SELF_MONSTER) \
+ MSG_MULTI_NOTIF(1, DEATH_SELF_MON_ROTFISH, NO_MSG, INFO_DEATH_SELF_MON_ROTFISH_MELEE, CENTER_DEATH_SELF_MONSTER) \
+ MSG_MULTI_NOTIF(1, DEATH_SELF_MON_ROTTWEILER, NO_MSG, INFO_DEATH_SELF_MON_ROTTWEILER, CENTER_DEATH_SELF_MONSTER) \
+ MSG_MULTI_NOTIF(1, DEATH_SELF_MON_SCRAG, NO_MSG, INFO_DEATH_SELF_MON_SCRAG, CENTER_DEATH_SELF_MONSTER) \
MSG_MULTI_NOTIF(1, DEATH_SELF_MON_SHAMBLER_CLAW, NO_MSG, INFO_DEATH_SELF_MON_SHAMBLER_CLAW, CENTER_DEATH_SELF_MONSTER) \
MSG_MULTI_NOTIF(1, DEATH_SELF_MON_SHAMBLER_SMASH, NO_MSG, INFO_DEATH_SELF_MON_SHAMBLER_SMASH, CENTER_DEATH_SELF_MONSTER) \
MSG_MULTI_NOTIF(1, DEATH_SELF_MON_SHAMBLER_ZAP, NO_MSG, INFO_DEATH_SELF_MON_SHAMBLER_ZAP, CENTER_DEATH_SELF_MONSTER) \
MSG_MULTI_NOTIF(1, DEATH_SELF_MON_SPIDER, NO_MSG, INFO_DEATH_SELF_MON_SPIDER, CENTER_DEATH_SELF_MONSTER) \
+ MSG_MULTI_NOTIF(1, DEATH_SELF_MON_VORE, NO_MSG, INFO_DEATH_SELF_MON_VORE, CENTER_DEATH_SELF_MONSTER) \
+ MSG_MULTI_NOTIF(1, DEATH_SELF_MON_VORE_MELEE, NO_MSG, INFO_DEATH_SELF_MON_VORE_MELEE, CENTER_DEATH_SELF_MONSTER) \
MSG_MULTI_NOTIF(1, DEATH_SELF_MON_WYVERN, NO_MSG, INFO_DEATH_SELF_MON_WYVERN, CENTER_DEATH_SELF_MONSTER) \
MSG_MULTI_NOTIF(1, DEATH_SELF_MON_ZOMBIE_JUMP, NO_MSG, INFO_DEATH_SELF_MON_ZOMBIE_JUMP, CENTER_DEATH_SELF_MONSTER) \
MSG_MULTI_NOTIF(1, DEATH_SELF_MON_ZOMBIE_MELEE, NO_MSG, INFO_DEATH_SELF_MON_ZOMBIE_MELEE, CENTER_DEATH_SELF_MONSTER) \
MSG_MULTI_NOTIF(1, DEATH_SELF_TURRET_PLASMA, NO_MSG, INFO_DEATH_SELF_TURRET_PLASMA, CENTER_DEATH_SELF_TURRET) \
MSG_MULTI_NOTIF(1, DEATH_SELF_TURRET_TESLA, NO_MSG, INFO_DEATH_SELF_TURRET_TESLA, CENTER_DEATH_SELF_TURRET) \
MSG_MULTI_NOTIF(1, DEATH_SELF_TURRET_WALK_GUN, NO_MSG, INFO_DEATH_SELF_TURRET_WALK_GUN, CENTER_DEATH_SELF_TURRET_WALK) \
- MSG_MULTI_NOTIF(1, DEATH_SELF_TURRET_WALK_MEELE, NO_MSG, INFO_DEATH_SELF_TURRET_WALK_MEELE, CENTER_DEATH_SELF_TURRET_WALK) \
+ MSG_MULTI_NOTIF(1, DEATH_SELF_TURRET_WALK_MELEE, NO_MSG, INFO_DEATH_SELF_TURRET_WALK_MELEE, CENTER_DEATH_SELF_TURRET_WALK) \
MSG_MULTI_NOTIF(1, DEATH_SELF_TURRET_WALK_ROCKET, NO_MSG, INFO_DEATH_SELF_TURRET_WALK_ROCKET, CENTER_DEATH_SELF_TURRET_WALK) \
MSG_MULTI_NOTIF(1, DEATH_SELF_VH_BUMB_DEATH, NO_MSG, INFO_DEATH_SELF_VH_BUMB_DEATH, CENTER_DEATH_SELF_VH_BUMB_DEATH) \
MSG_MULTI_NOTIF(1, DEATH_SELF_VH_CRUSH, NO_MSG, INFO_DEATH_SELF_VH_CRUSH, CENTER_DEATH_SELF_VH_CRUSH) \
MSG_MULTI_NOTIF(1, DEATH_SELF_VH_RAPT_DEATH, NO_MSG, INFO_DEATH_SELF_VH_RAPT_DEATH, CENTER_DEATH_SELF_VH_RAPT_DEATH) \
MSG_MULTI_NOTIF(1, DEATH_SELF_VH_SPID_DEATH, NO_MSG, INFO_DEATH_SELF_VH_SPID_DEATH, CENTER_DEATH_SELF_VH_SPID_DEATH) \
MSG_MULTI_NOTIF(1, DEATH_SELF_VH_SPID_ROCKET, NO_MSG, INFO_DEATH_SELF_VH_SPID_ROCKET, CENTER_DEATH_SELF_VH_SPID_ROCKET) \
+ MSG_MULTI_NOTIF(1, DEATH_SELF_VH_TANK_DEATH, NO_MSG, INFO_DEATH_SELF_VH_TANK_DEATH, CENTER_DEATH_SELF_VH_TANK_DEATH) \
MSG_MULTI_NOTIF(1, DEATH_SELF_VH_WAKI_DEATH, NO_MSG, INFO_DEATH_SELF_VH_WAKI_DEATH, CENTER_DEATH_SELF_VH_WAKI_DEATH) \
MSG_MULTI_NOTIF(1, DEATH_SELF_VH_WAKI_ROCKET, NO_MSG, INFO_DEATH_SELF_VH_WAKI_ROCKET, CENTER_DEATH_SELF_VH_WAKI_ROCKET) \
MSG_MULTI_NOTIF(1, DEATH_SELF_VOID, NO_MSG, INFO_DEATH_SELF_VOID, CENTER_DEATH_SELF_VOID) \
MSG_MULTI_NOTIF(1, ITEM_WEAPON_UNAVAILABLE, NO_MSG, INFO_ITEM_WEAPON_UNAVAILABLE, CENTER_ITEM_WEAPON_UNAVAILABLE) \
MSG_MULTI_NOTIF(1, MULTI_COINTOSS, NO_MSG, INFO_COINTOSS, CENTER_COINTOSS) \
MSG_MULTI_NOTIF(1, MULTI_COUNTDOWN_BEGIN, ANNCE_BEGIN, NO_MSG, CENTER_COUNTDOWN_BEGIN) \
+ MSG_MULTI_NOTIF(1, MULTI_HEADSHOT, ANNCE_HEADSHOT,NO_MSG, CENTER_HEADSHOT) \
MSG_MULTI_NOTIF(1, MULTI_INSTAGIB_FINDAMMO, ANNCE_NUM_10, NO_MSG, CENTER_INSTAGIB_FINDAMMO_FIRST) \
MSG_MULTI_NOTIF(1, WEAPON_ACCORDEON_MURDER, NO_MSG, INFO_WEAPON_ACCORDEON_MURDER, NO_MSG) \
MSG_MULTI_NOTIF(1, WEAPON_ACCORDEON_SUICIDE, NO_MSG, INFO_WEAPON_ACCORDEON_SUICIDE, CENTER_DEATH_SELF_GENERIC) \
MSG_MULTI_NOTIF(1, WEAPON_HMG_MURDER_SNIPE, NO_MSG, INFO_WEAPON_HMG_MURDER_SNIPE, NO_MSG) \
MSG_MULTI_NOTIF(1, WEAPON_HMG_MURDER_SPRAY, NO_MSG, INFO_WEAPON_HMG_MURDER_SPRAY, NO_MSG) \
MSG_MULTI_NOTIF(1, WEAPON_HOOK_MURDER, NO_MSG, INFO_WEAPON_HOOK_MURDER, NO_MSG) \
+ MSG_MULTI_NOTIF(1, WEAPON_HOOK_MURDER_GRAVITYBOMB, NO_MSG, INFO_WEAPON_HOOK_MURDER_GRAVITYBOMB, NO_MSG) \
+ MSG_MULTI_NOTIF(1, WEAPON_HOOK_MURDER_LASER, NO_MSG, INFO_WEAPON_HOOK_MURDER_LASER, NO_MSG) \
MSG_MULTI_NOTIF(1, WEAPON_KLEINBOTTLE_MURDER, NO_MSG, INFO_WEAPON_KLEINBOTTLE_MURDER, NO_MSG) \
MSG_MULTI_NOTIF(1, WEAPON_KLEINBOTTLE_SUICIDE, NO_MSG, INFO_WEAPON_KLEINBOTTLE_SUICIDE, CENTER_DEATH_SELF_GENERIC) \
+ MSG_MULTI_NOTIF(1, WEAPON_LIGHTSABRE_MURDER, NO_MSG, INFO_WEAPON_LIGHTSABRE_MURDER, NO_MSG) \
MSG_MULTI_NOTIF(1, WEAPON_MACHINEGUN_MURDER_SNIPE, NO_MSG, INFO_WEAPON_MACHINEGUN_MURDER_SNIPE, NO_MSG) \
MSG_MULTI_NOTIF(1, WEAPON_MACHINEGUN_MURDER_SPRAY, NO_MSG, INFO_WEAPON_MACHINEGUN_MURDER_SPRAY, NO_MSG) \
MSG_MULTI_NOTIF(1, WEAPON_MINELAYER_LIMIT, NO_MSG, INFO_WEAPON_MINELAYER_LIMIT, CENTER_WEAPON_MINELAYER_LIMIT) \
MSG_MULTI_NOTIF(1, WEAPON_MORTAR_MURDER_EXPLODE, NO_MSG, INFO_WEAPON_MORTAR_MURDER_EXPLODE, NO_MSG) \
MSG_MULTI_NOTIF(1, WEAPON_MORTAR_SUICIDE_BOUNCE, NO_MSG, INFO_WEAPON_MORTAR_SUICIDE_BOUNCE, CENTER_DEATH_SELF_GENERIC) \
MSG_MULTI_NOTIF(1, WEAPON_MORTAR_SUICIDE_EXPLODE, NO_MSG, INFO_WEAPON_MORTAR_SUICIDE_EXPLODE, CENTER_DEATH_SELF_GENERIC) \
+ MSG_MULTI_NOTIF(1, WEAPON_REVOLVER_MURDER, NO_MSG, INFO_WEAPON_REVOLVER_MURDER, NO_MSG) \
MSG_MULTI_NOTIF(1, WEAPON_RIFLE_MURDER, NO_MSG, INFO_WEAPON_RIFLE_MURDER, NO_MSG) \
MSG_MULTI_NOTIF(1, WEAPON_RIFLE_MURDER_HAIL, NO_MSG, INFO_WEAPON_RIFLE_MURDER_HAIL, NO_MSG) \
MSG_MULTI_NOTIF(1, WEAPON_RIFLE_MURDER_HAIL_PIERCING, NO_MSG, INFO_WEAPON_RIFLE_MURDER_HAIL_PIERCING, NO_MSG) \
MSG_MULTI_NOTIF(1, WEAPON_TUBA_MURDER, NO_MSG, INFO_WEAPON_TUBA_MURDER, NO_MSG) \
MSG_MULTI_NOTIF(1, WEAPON_TUBA_SUICIDE, NO_MSG, INFO_WEAPON_TUBA_SUICIDE, CENTER_DEATH_SELF_GENERIC) \
MSG_MULTI_NOTIF(1, WEAPON_VAPORIZER_MURDER, NO_MSG, INFO_WEAPON_VAPORIZER_MURDER, NO_MSG) \
+ MSG_MULTI_NOTIF(1, WEAPON_VAPORIZER_MURDER_CHARGE, NO_MSG, INFO_WEAPON_VAPORIZER_MURDER_CHARGE, NO_MSG) \
+ MSG_MULTI_NOTIF(1, WEAPON_VAPORIZER_SUICIDE, NO_MSG, INFO_WEAPON_VAPORIZER_SUICIDE, NO_MSG) \
MSG_MULTI_NOTIF(1, WEAPON_VORTEX_MURDER, NO_MSG, INFO_WEAPON_VORTEX_MURDER, NO_MSG)
#define MULTITEAM_CHOICE2(default,challow,prefix,chtype,optiona,optionb) \
MULTITEAM_CHOICE##teams(default,challow,prefix,chtype,optiona,optionb)
#define MSG_CHOICE_NOTIFICATIONS \
- MULTITEAM_CHOICE(1, 2, CHOICE_CTF_CAPTURE_BROKEN_, 2, MSG_INFO, INFO_CTF_CAPTURE_, INFO_CTF_CAPTURE_BROKEN_) \
- MULTITEAM_CHOICE(1, 2, CHOICE_CTF_CAPTURE_TIME_, 2, MSG_INFO, INFO_CTF_CAPTURE_, INFO_CTF_CAPTURE_TIME_) \
- MULTITEAM_CHOICE(1, 2, CHOICE_CTF_CAPTURE_UNBROKEN_, 2, MSG_INFO, INFO_CTF_CAPTURE_, INFO_CTF_CAPTURE_UNBROKEN_) \
- MSG_CHOICE_NOTIF(1, 2, CHOICE_CTF_PICKUP_TEAM, MSG_CENTER, CENTER_CTF_PICKUP_TEAM, CENTER_CTF_PICKUP_TEAM_VERBOSE) \
+ MULTITEAM_CHOICE(1, 2, CHOICE_CTF_CAPTURE_BROKEN_, 4, MSG_INFO, INFO_CTF_CAPTURE_, INFO_CTF_CAPTURE_BROKEN_) \
+ MULTITEAM_CHOICE(1, 2, CHOICE_CTF_CAPTURE_TIME_, 4, MSG_INFO, INFO_CTF_CAPTURE_, INFO_CTF_CAPTURE_TIME_) \
+ MULTITEAM_CHOICE(1, 2, CHOICE_CTF_CAPTURE_UNBROKEN_, 4, MSG_INFO, INFO_CTF_CAPTURE_, INFO_CTF_CAPTURE_UNBROKEN_) \
+ MULTITEAM_CHOICE(1, 2, CHOICE_CTF_PICKUP_TEAM_, 4, MSG_CENTER, CENTER_CTF_PICKUP_TEAM_, CENTER_CTF_PICKUP_TEAM_VERBOSE_) \
+ MSG_CHOICE_NOTIF(1, 2, CHOICE_CTF_PICKUP_TEAM_NEUTRAL, MSG_CENTER, CENTER_CTF_PICKUP_TEAM_NEUTRAL, CENTER_CTF_PICKUP_TEAM_VERBOSE_NEUTRAL) \
MSG_CHOICE_NOTIF(1, 2, CHOICE_CTF_PICKUP_ENEMY, MSG_CENTER, CENTER_CTF_PICKUP_ENEMY, CENTER_CTF_PICKUP_ENEMY_VERBOSE) \
+ MSG_CHOICE_NOTIF(1, 2, CHOICE_CTF_PICKUP_ENEMY_NEUTRAL, MSG_CENTER, CENTER_CTF_PICKUP_ENEMY_NEUTRAL, CENTER_CTF_PICKUP_ENEMY_NEUTRAL_VERBOSE) \
+ MSG_CHOICE_NOTIF(1, 2, CHOICE_CTF_PICKUP_ENEMY_TEAM, MSG_CENTER, CENTER_CTF_PICKUP_ENEMY_TEAM, CENTER_CTF_PICKUP_ENEMY_TEAM_VERBOSE) \
MSG_CHOICE_NOTIF(1, 1, CHOICE_FRAG, MSG_CENTER, CENTER_DEATH_MURDER_FRAG, CENTER_DEATH_MURDER_FRAG_VERBOSE) \
MSG_CHOICE_NOTIF(1, 1, CHOICE_FRAGGED, MSG_CENTER, CENTER_DEATH_MURDER_FRAGGED, CENTER_DEATH_MURDER_FRAGGED_VERBOSE) \
+ MULTITEAM_CHOICE(1, 2, CHOICE_KEYHUNT_PICKUP_TEAM_, 4, MSG_CENTER, CENTER_KEYHUNT_PICKUP_TEAM_, CENTER_KEYHUNT_PICKUP_TEAM_VERBOSE_) \
+ MULTITEAM_CHOICE(1, 2, CHOICE_KEYHUNT_START_TEAM_, 4, MSG_CENTER, CENTER_KEYHUNT_START_TEAM_, CENTER_KEYHUNT_START_TEAM_VERBOSE_) \
MSG_CHOICE_NOTIF(1, 1, CHOICE_TYPEFRAG, MSG_CENTER, CENTER_DEATH_MURDER_TYPEFRAG, CENTER_DEATH_MURDER_TYPEFRAG_VERBOSE) \
MSG_CHOICE_NOTIF(1, 1, CHOICE_TYPEFRAGGED, MSG_CENTER, CENTER_DEATH_MURDER_TYPEFRAGGED, CENTER_DEATH_MURDER_TYPEFRAGGED_VERBOSE)
//MSG_CHOICE_NOTIF(2, CHOICE_)
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
ARG_CASE(ARG_CS_SV, "spree_lost", (autocvar_notification_show_sprees ? notif_arg_spree_inf(-2, "", "", f1) : "")) \
ARG_CASE(ARG_CS_SV, "item_wepname", WEP_NAME(f1)) \
ARG_CASE(ARG_CS_SV, "item_buffname", sprintf("%s%s", rgb_to_hexcolor(Buff_Color(f1)), Buff_PrettyName(f1))) \
+ ARG_CASE(ARG_CS_SV, "f3buffname", sprintf("%s%s", rgb_to_hexcolor(Buff_Color(f3)), Buff_PrettyName(f3))) \
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_UNKNOWN(token,funcname) { backtrace(sprintf("%s: Hit unknown token in selected string! '%s'\n", funcname, selected)); break; }
#define KILL_SPREE_LIST \
+ SPREE_ITEM(2, 02, _("MULTI FRAG! "), _("%s^K1 made a MULTI FRAG! %s^BG"), _("%s^K1 made a DOUBLE SCORE! %s^BG")) \
SPREE_ITEM(3, 03, _("TRIPLE FRAG! "), _("%s^K1 made a TRIPLE FRAG! %s^BG"), _("%s^K1 made a TRIPLE SCORE! %s^BG")) \
SPREE_ITEM(5, 05, _("RAGE! "), _("%s^K1 unlocked RAGE! %s^BG"), _("%s^K1 made FIVE SCORES IN A ROW! %s^BG")) \
+ SPREE_ITEM(8, 08, _("RAMPAGE! "), _("%s^K1 is on a RAMPAGE! %s^BG"), _("%s^K1 made EIGHT SCORES IN A ROW! %s^BG")) \
SPREE_ITEM(10, 10, _("MASSACRE! "), _("%s^K1 started a MASSACRE! %s^BG"), _("%s^K1 made TEN SCORES IN A ROW! %s^BG")) \
SPREE_ITEM(15, 15, _("MAYHEM! "), _("%s^K1 executed MAYHEM! %s^BG"), _("%s^K1 made FIFTEEN SCORES IN A ROW! %s^BG")) \
SPREE_ITEM(20, 20, _("BERSERKER! "), _("%s^K1 is a BERSERKER! %s^BG"), _("%s^K1 made TWENTY SCORES IN A ROW! %s^BG")) \
float NOTIF_CPID_COUNT;
// notification limits -- INCREASE AS NECESSARY
-#define NOTIF_ANNCE_MAX 100
-#define NOTIF_INFO_MAX 300
-#define NOTIF_CENTER_MAX 200
+#define NOTIF_ANNCE_MAX 200
+#define NOTIF_INFO_MAX 400
+#define NOTIF_CENTER_MAX 350
#define NOTIF_MULTI_MAX 200
-#define NOTIF_CHOICE_MAX 20
+#define NOTIF_CHOICE_MAX 50
// notification entities
entity msg_annce_notifs[NOTIF_ANNCE_MAX];
} \
ACCUMULATE_FUNCTION(RegisterNotifications, RegisterNotification_##name);
+.string nent_iconargs;
+#define MULTIICON_INFO(default,name,strnum,flnum,args,hudargs,iconargs,icon,normal,gentle) \
+ NOTIF_ADD_AUTOCVAR(name, default) \
+ float name; \
+ void RegisterNotification_##name() \
+ { \
+ SET_FIELD_COUNT(name, NOTIF_FIRST, NOTIF_INFO_COUNT) \
+ CHECK_MAX_COUNT(name, NOTIF_INFO_MAX, NOTIF_INFO_COUNT, "MSG_INFO") \
+ Create_Notification_Entity( \
+ /* COMMON ======================== */ \
+ default, /* var_default */ \
+ ACVNN(name), /* var_cvar */ \
+ MSG_INFO, /* typeid */ \
+ name, /* nameid */ \
+ strtoupper(#name), /* namestring */ \
+ strnum, /* strnum */ \
+ flnum, /* flnum */ \
+ /* ANNCE =========== */ \
+ NO_MSG, /* channel */ \
+ "", /* snd */ \
+ NO_MSG, /* vol */ \
+ NO_MSG, /* position */ \
+ /* INFO & CENTER === */ \
+ args, /* args */ \
+ hudargs, /* hudargs */ \
+ icon, /* icon */ \
+ NO_MSG, /* cpid */ \
+ "", /* durcnt */ \
+ normal, /* normal */ \
+ gentle, /* gentle */ \
+ /* MULTI ============= */ \
+ NO_MSG, /* anncename */ \
+ NO_MSG, /* infoname */ \
+ NO_MSG, /* centername */ \
+ /* CHOICE ============== */ \
+ NO_MSG, /* challow_def */ \
+ NO_MSG, /* challow_var */ \
+ NO_MSG, /* chtype */ \
+ NO_MSG, /* optiona */ \
+ NO_MSG); /* optionb */ \
+ msg_info_notifs[name - 1].nent_iconargs = iconargs; \
+ } \
+ ACCUMULATE_FUNCTION(RegisterNotifications, RegisterNotification_##name);
+
#define MSG_CENTER_NOTIF(default,name,strnum,flnum,args,cpid,durcnt,normal,gentle) \
NOTIF_ADD_AUTOCVAR(name, default) \
float name; \
// referred to by PS_GR_P_ADDVAL and PS_GR_T_ADDVAL
float PlayerStats_GameReport_Event(string prefix, string event_id, float value)
{
- if((prefix == "") || PS_GR_OUT_DB < 0) { return 0; }
+ if((prefix == "") || (event_id == "") || PS_GR_OUT_DB < 0) { return 0; }
string key = sprintf("%s:%s", prefix, event_id);
float val = stof(db_get(PS_GR_OUT_DB, key));
const string PLAYERSTATS_TOTAL = "total-";
const string PLAYERSTATS_SCOREBOARD = "scoreboard-";
+const string PLAYERSTATS_ACHIEVEMENT_KILL_SPREE_2 = ""; // empty string, we don't want this sent
const string PLAYERSTATS_ACHIEVEMENT_KILL_SPREE_3 = "achievement-kill-spree-3";
const string PLAYERSTATS_ACHIEVEMENT_KILL_SPREE_5 = "achievement-kill-spree-5";
+const string PLAYERSTATS_ACHIEVEMENT_KILL_SPREE_8 = ""; // same story here
const string PLAYERSTATS_ACHIEVEMENT_KILL_SPREE_10 = "achievement-kill-spree-10";
const string PLAYERSTATS_ACHIEVEMENT_KILL_SPREE_15 = "achievement-kill-spree-15";
const string PLAYERSTATS_ACHIEVEMENT_KILL_SPREE_20 = "achievement-kill-spree-20";
const float STAT_WEAPONS = 35;
const float STAT_SWITCHWEAPON = 36;
const float STAT_GAMESTARTTIME = 37;
-const float STAT_STRENGTH_FINISHED = 38;
-const float STAT_INVINCIBLE_FINISHED = 39;
+// 38 empty?
+// 39 empty?
// 40 empty?
const float STAT_ARC_HEAT = 41;
const float STAT_PRESSED_KEYS = 42;
const float STAT_WEAPONS3 = 75;
const float STAT_MONSTERS_TOTAL = 76;
const float STAT_MONSTERS_KILLED = 77;
-const float STAT_BUFFS = 78;
-const float STAT_NADE_BONUS = 79;
-const float STAT_NADE_BONUS_TYPE = 80;
-const float STAT_NADE_BONUS_SCORE = 81;
-const float STAT_HEALING_ORB = 82;
-const float STAT_HEALING_ORB_ALPHA = 83;
-const float STAT_PLASMA = 84;
-const float STAT_OK_AMMO_CHARGE = 85;
-const float STAT_OK_AMMO_CHARGEPOOL = 86;
-// 87 empty?
-// 88 empty?
-// 89 empty?
-// 90 empty?
-// 91 empty?
-// 92 empty?
-// 93 empty?
-// 94 empty?
-// 95 empty?
-// 96 empty?
-// 97 empty?
-// 98 empty?
-// 99 empty?
+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;
/* The following stats change depending on the gamemode, so can share the same ID */
const float STAT_VIP_YELLOW = 103;
const float STAT_VIP_PINK = 104;
-// key hunt
-const float STAT_KH_REDKEY_TEAM = 100;
-const float STAT_KH_BLUEKEY_TEAM = 101;
-const float STAT_KH_YELLOWKEY_TEAM = 102;
-const float STAT_KH_PINKKEY_TEAM = 103;
-
/* Gamemode-specific stats end here */
const float STAT_FROZEN = 105;
const float STAT_REVIVE_PROGRESS = 106;
-// 107 empty?
+const float STAT_PLASMA = 107;
// 108 empty?
// 109 empty?
// 110 empty?
#define TCR(input,teamcolor,teamtext) strreplace("^TC", teamcolor, strreplace("^TT", teamtext, input))
// safe team comparisons
-#define SAME_TEAM(a,b) (teamplay ? ((a.team == b.team) ? 1 : 0) : ((a == b) ? 1 : 0))
-#define DIFF_TEAM(a,b) (teamplay ? ((a.team != b.team) ? 1 : 0) : ((a != b) ? 1 : 0))
+#define SAME_TEAM(a,b) (g_infection ? ((a.inf_team == b.inf_team) ? 1 : 0) : (teamplay ? ((a.team == b.team) ? 1 : 0) : ((a == b) ? 1 : 0)))
+#define DIFF_TEAM(a,b) (g_infection ? ((a.inf_team != b.inf_team) ? 1 : 0) : (teamplay ? ((a.team != b.team) ? 1 : 0) : ((a != b) ? 1 : 0)))
// used for notification system multi-team identifiers
#define APP_TEAM_NUM_2(num,prefix) ((num == NUM_TEAM_1) ? prefix##RED : prefix##BLUE)
--- /dev/null
+#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"
--- /dev/null
+/**
+ turret_checkpoint
+**/
+
+
+//.entity checkpoint_target;
+
+/*
+#define checkpoint_cache_who flagcarried
+#define checkpoint_cache_from lastrocket
+#define checkpoint_cache_to selected_player
+*/
+
+.entity pathgoal;
+
+/*
+entity path_makeorcache(entity forwho,entity start, entity end)
+{
+ entity oldself;
+ entity pth;
+ oldself = self;
+ self = forwho;
+
+ //pth = pathlib_makepath(start.origin,end.origin,PFL_GROUNDSNAP,500,1.5,PT_QUICKSTAR);
+
+ self = oldself;
+ return pth;
+}
+*/
+
+void turret_checkpoint_use()
+{
+}
+
+#if 0
+void turret_checkpoint_think()
+{
+ if(self.enemy)
+ te_lightning1(self,self.origin, self.enemy.origin);
+
+ self.nextthink = time + 0.25;
+}
+#endif
+/*QUAKED turret_checkpoint (1 0 1) (-32 -32 -32) (32 32 32)
+-----------KEYS------------
+target: .targetname of next waypoint in chain.
+wait: Pause at this point # seconds.
+-----------SPAWNFLAGS-----------
+---------NOTES----------
+If a loop is of targets are formed, any unit entering this loop will patrol it indefinitly.
+If the checkpoint chain in not looped, the unit will go "Roaming" when the last point is reached.
+*/
+//float tc_acum;
+void turret_checkpoint_init()
+{
+ traceline(self.origin + '0 0 16', self.origin - '0 0 1024', MOVE_WORLDONLY, self);
+ setorigin(self, trace_endpos + '0 0 32');
+
+ if(self.target != "")
+ {
+ self.enemy = find(world, targetname, self.target);
+ if(self.enemy == world)
+ dprint("A turret_checkpoint faild to find its target!\n");
+ }
+ //self.think = turret_checkpoint_think;
+ //self.nextthink = time + tc_acum + 0.25;
+ //tc_acum += 0.25;
+}
+
+void spawnfunc_turret_checkpoint()
+{
+ setorigin(self,self.origin);
+ self.think = turret_checkpoint_init;
+ self.nextthink = time + 0.2;
+}
+
+// Compat.
+void spawnfunc_walker_checkpoint()
+{
+ self.classname = "turret_checkpoint";
+ spawnfunc_turret_checkpoint();
+}
--- /dev/null
+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);
+}
+
+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);
+ 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);
+ 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);
+ 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);
+ }
+ else
+ 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);
+ 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;
+}
--- /dev/null
+void ent_turret();
--- /dev/null
+// =========================
+// SVQC Turret Properties
+// =========================
+
+
+// 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;
+}
+
+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_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);
+ 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;
+
+ entity e;
+ entity tur = get_turretinfo(tur_id);
+ if(tur.turretid == 0)
+ 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.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 TRUE;
+}
\ No newline at end of file
--- /dev/null
+// 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;
+
+/// 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
--- /dev/null
+void spawnfunc_turret_targettrigger();
+void turret_targettrigger_touch();
+
+void turret_targettrigger_touch()
+{
+ entity e;
+ if (self.cnt > time) return;
+ entity oldself;
+ oldself = self;
+
+ e = find(world, targetname, self.target);
+ while (e)
+ {
+ if (e.turret_flags & TUR_FLAG_RECIEVETARGETS)
+ {
+ self = e;
+ if(e.turret_addtarget)
+ e.turret_addtarget(other,oldself);
+ }
+
+ e = find(e, targetname, oldself.target);
+ }
+
+ oldself.cnt = time + 0.5;
+
+ self = oldself;
+}
+
+/*QUAKED turret_targettrigger (.5 .5 .5) ?
+*/
+void spawnfunc_turret_targettrigger()
+{
+ if(!autocvar_g_turrets) { remove(self); return; }
+
+ InitTrigger ();
+
+ self.touch = turret_targettrigger_touch;
+}
--- /dev/null
+#include "all.qh"
+
+// TURRET PLUGIN SYSTEM
+entity turret_info[TUR_MAXCOUNT];
+entity dummy_turret_info;
+
+void turrets_common_precache()
+{
+ 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/base.md3");
+ precache_model ("models/turrets/rocket.md3");
+
+ precache_model ("models/turrets/c512.md3");
+ precache_model ("models/marker.md3");
+
+#ifdef TURRET_DEBUG
+ precache_model ("models/turrets/terrainbase.md3");
+ precache_model ("models/turrets/c512.md3");
+ precache_model ("models/pathlib/goodsquare.md3");
+ precache_model ("models/pathlib/badsquare.md3");
+ precache_model ("models/pathlib/square.md3");
+ precache_model ("models/pathlib/edge.md3");
+#endif
+}
+
+void register_turret(float id, float(float) func, float turretflags, vector min_s, vector max_s, string modelname, string headmodelname, string shortname, string mname)
+{
+ entity e;
+ turret_info[id - 1] = e = spawn();
+ e.classname = "turret_info";
+ e.turretid = id;
+ e.netname = shortname;
+ e.turret_name = mname;
+ e.turret_func = func;
+ e.mdl = modelname;
+ e.cvar_basename = shortname;
+ e.spawnflags = turretflags;
+ e.mins = min_s;
+ e.maxs = max_s;
+ e.model = strzone(strcat("models/turrets/", modelname));
+ e.head_model = strzone(strcat("models/turrets/", headmodelname));
+
+ #ifndef MENUQC
+ turrets_common_precache();
+ #endif
+}
+float t_null(float dummy) { return 0; }
+void register_turrets_done()
+{
+ dummy_turret_info = spawn();
+ dummy_turret_info.classname = "turret_info";
+ dummy_turret_info.turretid = 0; // you can recognize dummies by this
+ dummy_turret_info.netname = "";
+ dummy_turret_info.turret_name = "Turret";
+ dummy_turret_info.turret_func = t_null;
+ dummy_turret_info.mdl = "";
+ dummy_turret_info.mins = '-0 -0 -0';
+ dummy_turret_info.maxs = '0 0 0';
+ dummy_turret_info.model = "";
+}
+entity get_turretinfo(float id)
+{
+ entity m;
+ if(id < TUR_FIRST || id > TUR_LAST)
+ return dummy_turret_info;
+ m = turret_info[id - 1];
+ if(m)
+ return m;
+ return dummy_turret_info;
+}
--- /dev/null
+// 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);
+
+// 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
+
+// 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
+
+// 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
+
+// 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
+
+// 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
+
+// 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
+
+// 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
+
+// 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
+
+// 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
+
+// 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;
+.float anim_start_time;
+const float TNSF_ANIM = 128;
+
+const float TNSF_FULL_UPDATE = 16777215;
+
+
+// entity properties of turretinfo:
+.float 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);
--- /dev/null
+#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
+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);
+}
+
+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.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;
+ }
+ 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;
+ }
+ 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;
+ }
+ 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.teleportable = TELEPORT_NORMAL;
+ 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;
+ }
+ case TR_PRECACHE:
+ {
+ precache_model ("models/turrets/ewheel-base2.md3");
+ precache_model ("models/turrets/ewheel-gun1.md3");
+ 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;
+ }
+ case TR_PRECACHE:
+ {
+ return TRUE;
+ }
+ }
+
+ return TRUE;
+}
+
+#endif // CSQC
+#endif // REGISTER_TURRET
--- /dev/null
+#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
+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);
+ 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;
+ }
+ case TR_THINK:
+ {
+ return TRUE;
+ }
+ case TR_DEATH:
+ {
+ 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;
+ }
+ case TR_PRECACHE:
+ {
+ precache_model ("models/turrets/base.md3");
+ precache_model ("models/turrets/flac.md3");
+ return TRUE;
+ }
+ }
+
+ return TRUE;
+}
+
+#endif // SVQC
+#ifdef CSQC
+float t_flac(float req)
+{
+ switch(req)
+ {
+ case TR_SETUP:
+ {
+ return TRUE;
+ }
+ case TR_PRECACHE:
+ {
+ return TRUE;
+ }
+ }
+
+ return TRUE;
+}
+
+#endif // CSQC
+#endif // REGISTER_TURRET
--- /dev/null
+#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;
+ }
+ case TR_THINK:
+ {
+ self.tur_head.avelocity = '0 250 0' * (self.ammo / self.ammo_max);
+
+ return TRUE;
+ }
+ case TR_DEATH:
+ {
+ 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;
+ }
+ case TR_PRECACHE:
+ {
+ precache_model ("models/turrets/base.md3");
+ precache_model ("models/turrets/reactor.md3");
+ return TRUE;
+ }
+ }
+
+ return TRUE;
+}
+
+#endif // SVQC
+#ifdef CSQC
+float t_fusionreactor(float req)
+{
+ switch(req)
+ {
+ case TR_SETUP:
+ {
+ return TRUE;
+ }
+ case TR_PRECACHE:
+ {
+ return TRUE;
+ }
+ }
+
+ return TRUE;
+}
+
+#endif // CSQC
+#endif // REGISTER_TURRET
--- /dev/null
+#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);
+ 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;
+ }
+ 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;
+ }
+ case TR_DEATH:
+ {
+ 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;
+ }
+ case TR_PRECACHE:
+ {
+ precache_model ("models/turrets/base.md3");
+ precache_model ("models/turrets/hellion.md3");
+ return TRUE;
+ }
+ }
+
+ return TRUE;
+}
+
+#endif // SVQC
+#ifdef CSQC
+float t_hellion(float req)
+{
+ switch(req)
+ {
+ case TR_SETUP:
+ {
+ return TRUE;
+ }
+ case TR_PRECACHE:
+ {
+ return TRUE;
+ }
+ }
+
+ return TRUE;
+}
+
+#endif // CSQC
+#endif // REGISTER_TURRET
--- /dev/null
+#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);
+ 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);
+ 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);
+ vl = trace_endpos;
+ fl = trace_fraction;
+
+ // Trace right
+ 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);
+ 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);
+ 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);
+ 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;
+ }
+ 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;
+ }
+ case TR_DEATH:
+ {
+ 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;
+ }
+ case TR_PRECACHE:
+ {
+ precache_model ("models/turrets/base.md3");
+ precache_model ("models/turrets/hk.md3");
+ return TRUE;
+ }
+ }
+
+ return TRUE;
+}
+
+#endif // SVQC
+#ifdef CSQC
+float t_hk(float req)
+{
+ switch(req)
+ {
+ case TR_SETUP:
+ {
+ return TRUE;
+ }
+ case TR_PRECACHE:
+ {
+ return TRUE;
+ }
+ }
+
+ return TRUE;
+}
+
+#endif // CSQC
+#endif // REGISTER_TURRET
--- /dev/null
+#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
+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;
+ }
+ case TR_THINK:
+ {
+ return TRUE;
+ }
+ case TR_DEATH:
+ {
+ 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;
+ }
+ 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;
+}
+
+#endif // SVQC
+#ifdef CSQC
+float t_machinegun(float req)
+{
+ switch(req)
+ {
+ case TR_SETUP:
+ {
+ return TRUE;
+ }
+ case TR_PRECACHE:
+ {
+ return TRUE;
+ }
+ }
+
+ return TRUE;
+}
+
+#endif // CSQC
+#endif // REGISTER_TURRET
--- /dev/null
+#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.nextthink = time + max(self.tur_impacttime,(self.shot_radius * 2) / self.shot_speed);
+ missile.missile_flags = MIF_SPLASH;
+ te_explosion (missile.origin);
+
+ 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;
+ }
+ case TR_DEATH:
+ {
+ 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;
+ }
+ case TR_PRECACHE:
+ {
+ precache_model ("models/turrets/base.md3");
+ precache_model ("models/turrets/mlrs.md3");
+ return TRUE;
+ }
+ }
+
+ return TRUE;
+}
+
+#endif // SVQC
+#ifdef CSQC
+float t_mlrs(float req)
+{
+ switch(req)
+ {
+ case TR_SETUP:
+ {
+ return TRUE;
+ }
+ case TR_PRECACHE:
+ {
+ return TRUE;
+ }
+ }
+
+ return TRUE;
+}
+
+#endif // CSQC
+#endif // REGISTER_TURRET
--- /dev/null
+#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_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;
+ }
+ 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;
+ }
+ case TR_DEATH:
+ {
+ 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;
+ }
+ 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;
+}
+
+#endif // SVQC
+#ifdef CSQC
+float t_phaser(float req)
+{
+ switch(req)
+ {
+ case TR_SETUP:
+ {
+ return TRUE;
+ }
+ case TR_PRECACHE:
+ {
+ return TRUE;
+ }
+ }
+
+ return TRUE;
+}
+
+#endif // CSQC
+#endif // REGISTER_TURRET
--- /dev/null
+#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
+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);
+ 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;
+ }
+ 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;
+ }
+ case TR_DEATH:
+ {
+ 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;
+ }
+ case TR_PRECACHE:
+ {
+ precache_model ("models/turrets/base.md3");
+ precache_model ("models/turrets/plasma.md3");
+ return TRUE;
+ }
+ }
+
+ return TRUE;
+}
+
+#endif // SVQC
+#ifdef CSQC
+float t_plasma(float req)
+{
+ switch(req)
+ {
+ case TR_SETUP:
+ {
+ return TRUE;
+ }
+ case TR_PRECACHE:
+ {
+ return TRUE;
+ }
+ }
+
+ return TRUE;
+}
+
+#endif // CSQC
+#endif // REGISTER_TURRET
--- /dev/null
+#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
+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);
+ 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;
+ }
+ 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;
+ }
+ case TR_DEATH:
+ {
+ 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;
+ }
+ case TR_PRECACHE:
+ {
+ precache_model ("models/turrets/base.md3");
+ precache_model ("models/turrets/plasmad.md3");
+ return TRUE;
+ }
+ }
+
+ return TRUE;
+}
+
+#endif // SVQC
+#ifdef CSQC
+float t_plasma_dual(float req)
+{
+ switch(req)
+ {
+ case TR_SETUP:
+ {
+ return TRUE;
+ }
+ case TR_PRECACHE:
+ {
+ return TRUE;
+ }
+ }
+
+ return TRUE;
+}
+
+#endif // CSQC
+#endif // REGISTER_TURRET
--- /dev/null
+#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;
+
+ 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;
+ }
+ case TR_THINK:
+ {
+ if(!self.active)
+ {
+ self.tur_head.avelocity = '0 0 0';
+ 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;
+
+ 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;
+ }
+ case TR_DEATH:
+ {
+ 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;
+ }
+ case TR_PRECACHE:
+ {
+ precache_model ("models/turrets/tesla_base.md3");
+ precache_model ("models/turrets/tesla_head.md3");
+ return TRUE;
+ }
+ }
+
+ return TRUE;
+}
+
+#endif // SVQC
+#ifdef CSQC
+float t_tesla(float req)
+{
+ switch(req)
+ {
+ case TR_SETUP:
+ {
+ return TRUE;
+ }
+ case TR_PRECACHE:
+ {
+ return TRUE;
+ }
+ }
+
+ return TRUE;
+}
+
+#endif // CSQC
+#endif // REGISTER_TURRET
--- /dev/null
+#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
+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);
+ 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_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
+}
+
+.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;
+ }
+ 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);
+
+ return TRUE;
+ }
+ case TR_DEATH:
+ {
+#ifdef WALKER_FANCYPATHING
+ if (self.pathcurrent)
+ pathlib_deletepath(self.pathcurrent.owner);
+#endif
+ self.pathcurrent = world;
+
+ 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.teleportable = TELEPORT_NORMAL;
+ 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;
+ }
+ 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;
+}
+
+#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;
+ }
+ case TR_PRECACHE:
+ {
+ return TRUE;
+ }
+ }
+
+ return TRUE;
+}
+
+#endif // CSQC
+#endif // REGISTER_TURRET
--- /dev/null
+/*
+* 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;
+ }
+
+ self.tur_shotorg = gettaginfo(self.tur_head, gettagindex(self.tur_head, "tag_fire"));
+ v_forward = normalize(v_forward);
+
+ 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);
+
+ // 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.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);
+ while (ent)
+ {
+ // restore their solid type
+ ent.solid = ent.railgunhitsolidbackup;
+ ent = findfloat(ent, railgunhit, TRUE);
+ }
+
+ // find all the entities the railgun hit and hurt them
+ 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;
+
+ // 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);
+ }
+ 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
float healthdamage, armordamage, armorideal;
if (deathtype == DEATH_DROWN) // Why should armor help here...
armorblock = 0;
+#ifdef CHAOS
+ if(DEATH_ISWEAPON(deathtype, WEP_LIGHTSABRE))
+ armorblock = 0; // unique ability
+#endif
vector v;
healthdamage = (h - 1) / (1 - armorblock); // damage we can take if we could use more health
armordamage = a + (h - 1); // damage we can take if we could use more armor
return v;
}
-vector healtharmor_applydamage(float a, float armorblock, float deathtype, float damage)
+vector healtharmor_applydamage(float a, float armorblock, float deathtype, float damage, float bycount)
{
vector v;
if (deathtype == DEATH_DROWN) // Why should armor help here...
armorblock = 0;
- v_y = bound(0, damage * armorblock, a); // save
+ // block based on amount of carried armor (idea: CUdyin)
+ if(bycount && armorblock >= 1)
+ v_y = bound(0, damage * bound(0, ((armorblock*(a/100))) / 100, armorblock / 100), a); // save
+ else
+ v_y = bound(0, damage * armorblock, a); // save
v_x = bound(0, damage - v_y, damage); // take
v_z = 0;
return v;
string fn, s, c;
float fh, i;
+ get_model_parameters_description = string_null;
get_model_parameters_modelname = string_null;
get_model_parameters_modelskin = -1;
get_model_parameters_name = string_null;
get_model_parameters_bone_aimweight[i] = 0;
}
get_model_parameters_fixbone = 0;
+ get_model_parameters_bone_head = string_null;
+ get_model_parameters_hat_height = '0 0 0';
+ get_model_parameters_hat_scale = 0;
+ get_model_parameters_hat_angles = '0 0 0';
if (!m)
return 1;
}
if(c == "fixbone")
get_model_parameters_fixbone = stof(s);
+ if(c == "bone_head")
+ get_model_parameters_bone_head = s;
+ if(c == "hat_height")
+ get_model_parameters_hat_height = stov(s);
+ if(c == "hat_scale")
+ get_model_parameters_hat_scale = stof(s);
+ if(c == "hat_angles")
+ get_model_parameters_hat_angles = stov(s);
}
while((s = fgets(fh)))
+#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
#ifndef MENUQC
vector healtharmor_maxdamage(float h, float a, float armorblock, float deathtype); // returns vector: maxdamage, armorideal, 1 if fully armored
-vector healtharmor_applydamage(float a, float armorblock, float deathtype, float damage); // returns vector: take, save, 0
+vector healtharmor_applydamage(float a, float armorblock, float deathtype, float damage, float bycount); // returns vector: take, save, 0
#endif
string getcurrentmod();
string get_model_parameters_bone_aim[MAX_AIM_BONES];
float get_model_parameters_bone_aimweight[MAX_AIM_BONES];
float get_model_parameters_fixbone;
+string get_model_parameters_bone_head;
+vector get_model_parameters_hat_height;
+float get_model_parameters_hat_scale;
+vector get_model_parameters_hat_angles;
string get_model_parameters_desc;
float get_model_parameters(string mod, float skn); // call with string_null to clear; skin -1 means mod is the filename of the txt file and is to be split
#endif
// allow writing to also pass through to spectators (like so spectators see the same centerprints as players for example)
-#define WRITESPECTATABLE_MSG_ONE_VARNAME(varname,statement) entity varname; varname = msg_entity; FOR_EACH_REALCLIENT(msg_entity) if(msg_entity == varname || (msg_entity.classname == STR_SPECTATOR && msg_entity.enemy == varname)) statement msg_entity = varname
+#define WRITESPECTATABLE_MSG_ONE_VARNAME(varname,statement) entity varname; varname = msg_entity; FOR_EACH_REALCLIENT(msg_entity) if(msg_entity == varname || (IS_SPEC(msg_entity) && msg_entity.enemy == varname)) statement msg_entity = varname
#define WRITESPECTATABLE_MSG_ONE(statement) WRITESPECTATABLE_MSG_ONE_VARNAME(oldmsg_entity, statement)
#define WRITESPECTATABLE(msg,statement) if(msg == MSG_ONE) { WRITESPECTATABLE_MSG_ONE(statement); } else statement float WRITESPECTATABLE_workaround = 0
#define APPEND_TO_STRING(list,sep,add) ((list) = (((list) != "") ? strcat(list, sep, add) : (add)))
// Returns the correct difference between two always increasing numbers
-#define COMPARE_INCREASING(to,from) (to < from ? from + to + 2 : to - from)
\ No newline at end of file
+#define COMPARE_INCREASING(to,from) (to < from ? from + to + 2 : to - from)
--- /dev/null
+#include "unit/spiderbot.qc"
+#include "unit/raptor.qc"
+#include "unit/racer.qc"
+#ifndef VEHICLES_NO_UNSTABLE
+#include "unit/bumblebee.qc"
+#include "unit/yugo.qc"
+#include "unit/tankll48.qc"
+#endif
--- /dev/null
+#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;
+
+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)
+{
+ float 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); }
+}
--- /dev/null
+// vehicle cvars
+var float autocvar_cl_vehicles_alarm = 1;
+var float autocvar_cl_vehicles_hud_tactical = 1;
+
+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);
--- /dev/null
+// =========================
+// SVQC Vehicle Properties
+// =========================
+
+
+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;
+}
+
+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);
+ }
+
+ 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_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;
+}
+
+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);
+ 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);
+ 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;
+
+ if(IS_MONSTER(e))
+ return TRUE;
+
+ 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;
+ if(IS_CLIENT(self))
+ {
+ _vehicle = self.vehicle;
+
+ if (_vehicle.vehicle_flags & VHF_PLAYERSLOT)
+ {
+ _vehicle.vehicle_exit(eject);
+ self = _oldself;
+ 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;
+}
+
+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));
+ }
+ 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;
+ 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.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.movetype = MOVETYPE_WALK;
+ self.solid = SOLID_SLIDEBOX;
+ self.takedamage = DAMAGE_AIM;
+ self.deadflag = DEAD_NO;
+ 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;
+
+ entity veh = get_vehicleinfo(vehicle_id);
+
+ if(!veh.vehicleid)
+ 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.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 TRUE;
+}
--- /dev/null
+// #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;
+
+// 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 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
+
+.float hud;
+.float dmg_time;
+
+.float volly_counter;
+
+const float 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;
+
+float force_fromtag_power;
+float force_fromtag_normpower;
+vector force_fromtag_origin;
+
+float vehicles_exit_running;
+
+#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)
+
+// macros
+#define VEHICLE_UPDATE_PLAYER(ply,fld,vhname) \
+ ply.vehicle_##fld = (self.vehicle_##fld / autocvar_g_vehicle_##vhname##_##fld) * 100
+
+#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)
\ No newline at end of file
--- /dev/null
+#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
+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_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;
+
+
+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);
+}
+
+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;
+
+
+ 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, 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;
+ }
+
+ _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.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;
+}
+
+float vehicles_valid_pilot()
+{
+ if (!IS_PLAYER(other))
+ return FALSE;
+
+ if(other.deadflag != DEAD_NO)
+ return FALSE;
+
+ if(other.vehicle != world)
+ return FALSE;
+
+ if (!IS_REAL_CLIENT(other))
+ if(!autocvar_g_vehicles_allow_bots)
+ return FALSE;
+
+ if(teamplay && other.team != self.team)
+ return FALSE;
+
+ 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);
+
+ 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);
+
+ 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);
+
+}
+
+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);
+ 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);
+
+ // Pitch
+ ftmp = 0;
+ 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)
+ 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);
+
+ 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);
+ newvel = vehic.velocity * -autocvar_g_vehicle_bumblebee_friction;
+
+ 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)
+ newvel -= v_forward * autocvar_g_vehicle_bumblebee_speed_forward;
+ }
+
+ 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)
+ 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);
+ }
+ else
+ {
+ vehic.angles_z *= 0.95;
+ 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;
+}
+
+void spawnfunc_vehicle_bumblebee()
+{
+ if(!autocvar_g_vehicle_bumblebee) { 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;
+ }
+ case VR_ENTER:
+ {
+ self.touch = bumblebee_touch;
+ self.nextthink = 0;
+ self.movetype = MOVETYPE_BOUNCEMISSILE;
+ 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;
+ }
+
+ 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;
+ }
+ 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;
+ }
+ 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);
+ 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;
+ }
+ 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;
+ }
+ 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;
+}
+
+#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)
+{
+ float 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;
+
+ 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));
+ 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));
+ 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;
+ }
+ 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;
+ }
+ case VR_PRECACHE:
+ {
+ return TRUE;
+ }
+ }
+
+ return TRUE;
+}
+
+#endif // CSQC
+#endif // REGISTER_VEHICLE
--- /dev/null
+#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
+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);
+
+ // 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);
+
+ 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);
+
+ 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);
+
+ 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);
+
+
+ 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; }
+}
+
+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;
+ }
+ 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;
+ }
+ case VR_THINK:
+ {
+ 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;
+ }
+ 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;
+ }
+ 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;
+ }
+ 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;
+}
+
+#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;
+
+ 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;
+ }
+ case VR_SETUP:
+ {
+ AuxiliaryXhair[0].axh_image = "gfx/vehicles/axh-bracket.tga";
+ AuxiliaryXhair[0].axh_scale = 0.25;
+ return TRUE;
+ }
+ case VR_PRECACHE:
+ {
+ return TRUE;
+ }
+ }
+
+ return TRUE;
+}
+
+#endif // CSQC
+#endif // REGISTER_VEHICLE
--- /dev/null
+#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
+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);
+ }
+
+ 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);
+}
+
+
+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);
+}
+
+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);
+
+ 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);
+
+ 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);
+
+ 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);
+
+ 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);
+
+ 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);
+
+
+ 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;
+ 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;
+
+ /*
+ case 17: // toss gun, could be used to exit?
+ break;
+ case 20: // Manual minigun reload?
+ break;
+ */
+ }
+ return FALSE;
+}
+
+void spawnfunc_vehicle_raptor()
+{
+ if(!autocvar_g_vehicle_raptor) { 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;
+ }
+ 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;
+ }
+ case VR_THINK:
+ {
+ 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;
+ }
+ 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;
+ }
+ 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;
+ }
+ 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;
+}
+
+#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;
+
+ 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;
+ }
+ 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;
+ }
+ case VR_PRECACHE:
+ {
+ return TRUE;
+ }
+ }
+
+ return TRUE;
+}
+
+#endif // CSQC
+#endif // REGISTER_VEHICLE
--- /dev/null
+#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
+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);
+ 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);
+ 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);
+
+ 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);
+ //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);
+
+
+ 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);
+
+ 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);
+
+ 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;
+ 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;
+
+ /*
+ case 17: // toss gun, could be used to exit?
+ break;
+ case 20: // Manual minigun reload?
+ break;
+ */
+ }
+ return FALSE;
+}
+
+void spawnfunc_vehicle_spiderbot()
+{
+ if(!autocvar_g_vehicle_spiderbot) { 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;
+ }
+ 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;
+ }
+ case VR_THINK:
+ {
+ if(self.flags & FL_ONGROUND)
+ movelib_beak_simple(autocvar_g_vehicle_spiderbot_speed_stop);
+
+ 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;
+ }
+ 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;
+ }
+ 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
+
+ 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;
+}
+
+#endif // SVQC
+#ifdef CSQC
+var float autocvar_cl_vehicle_spiderbot_cross_alpha = 0.6;
+var 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;
+
+ 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;
+ }
+ 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;
+ }
+ case VR_PRECACHE:
+ {
+ return TRUE;
+ }
+ }
+
+ return TRUE;
+}
+
+#endif // CSQC
+#endif // REGISTER_VEHICLE
--- /dev/null
+#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
+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);
+ //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);
+ 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_dodgerating = autocvar_g_vehicle_tankll48_cannon_damage * 2;
+
+ missile.takedamage = DAMAGE_NO;
+ missile.event_damage = func_null;
+ 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);
+ }
+ 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);
+
+ 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);
+
+ 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);
+
+ 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; }
+}
+
+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;
+ }
+ 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;
+ }
+ case VR_THINK:
+ {
+ if(self.flags & FL_ONGROUND)
+ movelib_beak_simple(autocvar_g_vehicle_tankll48_speed_stop);
+
+ 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;
+ }
+ 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;
+ }
+ 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
+ setorigin(self.tur_head, '0 0 110');
+ setorigin(self.vehicle_hudmodel, '0 0 50');
+
+ 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;
+}
+
+#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;
+
+ 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;
+ }
+ 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;
+ }
+ case VR_PRECACHE:
+ {
+ return TRUE;
+ }
+ }
+
+ return TRUE;
+}
+
+#endif // CSQC
+#endif // REGISTER_VEHICLE
--- /dev/null
+#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
+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);
+
+ 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);
+
+ 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);
+
+
+ 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; }
+}
+
+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;
+ }
+ 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;
+ }
+ case VR_THINK:
+ {
+
+ 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;
+ }
+ 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;
+ }
+ 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;
+ }
+ 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;
+}
+
+#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;
+
+ 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;
+ }
+ case VR_SETUP:
+ {
+ AuxiliaryXhair[0].axh_image = "gfx/vehicles/axh-bracket.tga";
+ AuxiliaryXhair[0].axh_scale = 0.25;
+ return TRUE;
+ }
+ case VR_PRECACHE:
+ {
+ return TRUE;
+ }
+ }
+
+ return TRUE;
+}
+
+#endif // CSQC
+#endif // REGISTER_VEHICLE
--- /dev/null
+#include "all.qh"
+
+// VEHICLE PLUGIN SYSTEM
+entity vehicle_info[VEH_MAXCOUNT];
+entity dummy_vehicle_info;
+
+void vehicles_common_initialize()
+{
+#ifdef CSQC
+ 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");
+#endif // CSQC
+#ifdef SVQC
+ precache_sound("onslaught/ons_hit2.wav");
+ precache_sound("onslaught/electricity_explode.wav");
+
+ addstat(STAT_HUD, AS_INT, hud);
+ addstat(STAT_VEHICLESTAT_HEALTH, AS_INT, vehicle_health);
+ addstat(STAT_VEHICLESTAT_SHIELD, AS_INT, vehicle_shield);
+ addstat(STAT_VEHICLESTAT_ENERGY, AS_INT, vehicle_energy);
+
+ addstat(STAT_VEHICLESTAT_W2MODE, AS_INT, vehicle_weapon2mode);
+
+ addstat(STAT_VEHICLESTAT_AMMO1, AS_INT, vehicle_ammo1);
+ addstat(STAT_VEHICLESTAT_RELOAD1, AS_INT, vehicle_reload1);
+
+ addstat(STAT_VEHICLESTAT_AMMO2, AS_INT, vehicle_ammo2);
+ addstat(STAT_VEHICLESTAT_RELOAD2, AS_INT, vehicle_reload2);
+#endif // SVQC
+}
+
+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)
+{
+ entity e;
+ vehicle_info[id - 1] = e = spawn();
+ e.classname = "vehicle_info";
+ e.vehicleid = id;
+ e.netname = shortname;
+ e.vehicle_name = vname;
+ e.vehicle_func = func;
+ e.mdl = modelname;
+ e.spawnflags = vehicleflags;
+ e.mins = min_s;
+ e.maxs = max_s;
+ e.model = modelname;
+ e.head_model = headmodelname;
+ e.hud_model = hudmodelname;
+ e.tag_head = headtag;
+ e.tag_hud = hudtag;
+ e.tag_view = viewtag;
+
+ #ifndef MENUQC
+ vehicles_common_initialize();
+ #endif
+}
+float v_null(float dummy) { return 0; }
+void register_vehicles_done()
+{
+ dummy_vehicle_info = spawn();
+ dummy_vehicle_info.classname = "vehicle_info";
+ dummy_vehicle_info.vehicleid = 0; // you can recognize dummies by this
+ dummy_vehicle_info.netname = "";
+ dummy_vehicle_info.vehicle_name = "Vehicle";
+ dummy_vehicle_info.vehicle_func = v_null;
+ dummy_vehicle_info.mdl = "";
+ dummy_vehicle_info.mins = '-0 -0 -0';
+ dummy_vehicle_info.maxs = '0 0 0';
+ dummy_vehicle_info.model = "";
+ dummy_vehicle_info.head_model = "";
+ dummy_vehicle_info.hud_model = "";
+}
+entity get_vehicleinfo(float id)
+{
+ entity m;
+ if(id < VEH_FIRST || id > VEH_LAST)
+ return dummy_vehicle_info;
+ m = vehicle_info[id - 1];
+ if(m)
+ return m;
+ return dummy_vehicle_info;
+}
--- /dev/null
+// 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
+
+// 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);
--- /dev/null
+#ifdef CSQC
+#include "cl_vehicles.qc"
+#include "vehicles.qc"
+#endif // CSQC
+#ifdef SVQC
+#include "sv_vehicles.qc"
+#include "vehicles.qc"
+#endif // SVQC
--- /dev/null
+#ifdef CSQC
+#include "vehicles.qh"
+#include "cl_vehicles.qh"
+#endif // CSQC
+#ifdef SVQC
+#include "vehicles.qh"
+#include "sv_vehicles.qh"
+#endif // SVQC
#include "w_arc.qc"
#include "w_hmg.qc"
#include "w_rpc.qc"
+
+// more other weapons
+#include "w_revolver.qc"
+#include "w_lightsabre.qc"
||
(self.owner.WEP_AMMO(ARC) <= 0 && !(self.owner.items & IT_UNLIMITED_WEAPON_AMMO))
||
- self.owner.deadflag != DEAD_NO
+ Player_Trapped(self.owner)
||
(!self.owner.BUTTON_ATCK && !burst )
||
- self.owner.frozen
- ||
(WEP_CVAR(arc, overheat_max) > 0 && self.beam_heat >= WEP_CVAR(arc, overheat_max))
)
{
{
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);
}
}
// only play fire sound if 1 sec has passed since player let go the fire button
if(time - self.beam_prev > 1)
- {
- sound(self, CH_WEAPON_A, "weapons/arc_fire.wav", VOL_BASE, ATTN_NORM);
- }
+ sound(self, CH_WEAPON_A, W_Sound("arc_fire"), VOL_BASE, ATTN_NORM);
entity beam = self.arc_beam = spawn();
beam.classname = "W_Arc_Beam";
if ( !self.arc_smoke_sound )
{
self.arc_smoke_sound = 1;
- sound(self, CH_SHOTS_SINGLE, "weapons/arc_loop_overheat.wav", VOL_BASE, ATTN_NORM);
+ sound(self, CH_SHOTS_SINGLE, W_Sound("arc_loop_overheat"), VOL_BASE, ATTN_NORM);
}
}
}
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();
}
}
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);
{
vector s_forward = v_forward * cos(atk_shotangle * DEG2RAD) + v_up * sin(atk_shotangle * DEG2RAD);
- 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;
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");
+ 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;
}
e.projectiledeathtype,
other
);
- pointparticles(particleeffectnum("crylink_joinexplode"), self.origin, '0 0 0', n);
+ Send_Effect(EFFECT_CRYLINK_JOINEXPLODE, self.origin, '0 0 0', n);
}
}
}
if(WEP_CVAR_PRI(crylink, joinexplode))
maxdmg += WEP_CVAR_PRI(crylink, joinexplode_damage);
- 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;
shots = WEP_CVAR_PRI(crylink, shots);
- pointparticles(particleeffectnum("crylink_muzzleflash"), w_shotorg, w_shotdir * 1000, shots);
+ Send_Effect(EFFECT_CRYLINK_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, shots);
proj = prevproj = firstproj = world;
for(counter = 0; counter < shots; ++counter)
{
if(WEP_CVAR_SEC(crylink, joinexplode))
maxdmg += WEP_CVAR_SEC(crylink, joinexplode_damage);
- 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;
shots = WEP_CVAR_SEC(crylink, shots);
- pointparticles(particleeffectnum("crylink_muzzleflash"), w_shotorg, w_shotdir * 1000, shots);
+ Send_Effect(EFFECT_CRYLINK_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, shots);
proj = prevproj = firstproj = world;
for(counter = 0; counter < shots; ++counter)
{
}
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");
+ 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;
}
}
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;
}
case WR_SUICIDEMESSAGE:
if(!self.count)
{
- pointparticles(particleeffectnum("rocket_guide"), self.origin, self.velocity, 1);
+ Send_Effect(EFFECT_ROCKET_GUIDE, self.origin, self.velocity, 1);
// TODO add a better sound here
- sound(self.realowner, CH_WEAPON_B, "weapons/rocket_mode.wav", VOL_BASE, ATTN_NORM);
+ sound(self.realowner, CH_WEAPON_B, W_Sound("rocket_mode"), VOL_BASE, ATTN_NORM);
self.count = 1;
}
}
W_DecreaseAmmo(WEP_CVAR(devastator, ammo));
- 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;
{
// aim and decide to fire if appropriate
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;
}
makevectors(missile.v_angle);
targ = targetlist;
- if(skill > 9) // normal players only do this for the target they are tracking
+ if(bot_skill > 9) // normal players only do this for the target they are tracking
{
targ = targetlist;
while(targ)
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;
// dprint(ftos(random()/distance*300),">");dprint(ftos(frametime*bound(0,(10-skill)*0.2,1)),"\n");
}
// 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;
- if((skill > 6.5) && (selfdamage > self.health))
+ if((bot_skill > 6.5) && (selfdamage > self.health))
self.BUTTON_ATCK2 = FALSE;
//if(self.BUTTON_ATCK2 == TRUE)
// dprint(ftos(desirabledamage),"\n");
}
}
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);
}
}
//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;
}
case WR_RELOAD:
{
- W_Reload(WEP_CVAR(devastator, ammo), "weapons/reload.wav");
+ W_Reload(WEP_CVAR(devastator, ammo), W_Sound("reload"));
return TRUE;
}
case WR_SUICIDEMESSAGE:
w_cvar(id, sn, NONE, combo_edgedamage) \
w_cvar(id, sn, NONE, combo_force) \
w_cvar(id, sn, NONE, combo_radius) \
+ w_cvar(id, sn, NONE, combo_simple) \
w_cvar(id, sn, NONE, combo_speed) \
w_cvar(id, sn, NONE, combo_safeammocheck) \
w_prop(id, sn, float, reloading_ammo, reload_ammo) \
#ifdef SVQC
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)
}
}
+void W_Electro_TriggerCombo(vector org, float rad, entity own)
+{
+ // TODO: remove when we have unlagged findradius!
+
+ if(WEP_CVAR(electro, combo_simple))
+ W_Electro_TriggerCombo_Simple(org, rad, own);
+ else
+ W_Electro_TriggerCombo_WarpZone(org, rad, own);
+}
+
void W_Electro_ExplodeCombo(void)
{
W_Electro_TriggerCombo(self.origin, WEP_CVAR(electro, combo_comboradius), self.realowner);
'0 0 -3',
FALSE,
2,
- "weapons/electro_fire.wav",
+ W_Sound("electro_fire"),
CH_WEAPON_A,
WEP_CVAR_PRI(electro, damage)
);
- pointparticles(particleeffectnum("electro_muzzleflash"), w_shotorg, w_shotdir * 1000, 1);
+ Send_Effect(EFFECT_ELECTRO_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1);
proj = spawn();
proj.classname = "electro_bolt";
else
{
//UpdateCSQCProjectile(self);
- spamsound(self, CH_SHOTS, "weapons/electro_bounce.wav", VOL_BASE, ATTEN_NORM);
+ spamsound(self, CH_SHOTS, W_Sound("electro_bounce"), VOL_BASE, ATTEN_NORM);
self.projectiledeathtype |= HITTYPE_BOUNCE;
}
}
'0 0 -4',
FALSE,
2,
- "weapons/electro_fire2.wav",
+ W_Sound("electro_fire2"),
CH_WEAPON_A,
WEP_CVAR_SEC(electro, damage)
);
w_shotdir = v_forward; // no TrueAim for grenades please
- pointparticles(particleeffectnum("electro_muzzleflash"), w_shotorg, w_shotdir * 1000, 1);
+ Send_Effect(EFFECT_ELECTRO_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1);
entity proj = spawn();
proj.classname = "electro_orb";
}
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");
+ 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;
}
}
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;
}
case WR_SUICIDEMESSAGE:
accuracy_add(self.realowner, WEP_FIREBALL, 0, WEP_CVAR_PRI(fireball, bfgdamage) * points);
Damage(e, self, self.realowner, WEP_CVAR_PRI(fireball, bfgdamage) * points, self.projectiledeathtype | HITTYPE_BOUNCE | HITTYPE_SPLASH, e.origin + e.view_ofs, WEP_CVAR_PRI(fireball, bfgforce) * dir);
- pointparticles(particleeffectnum("fireball_bfgdamage"), e.origin, -1 * dir, 1);
+ Send_Effect(EFFECT_FIREBALL_BFGDAMAGE, e.origin, -1 * dir, 1);
}
}
d = vlen(WarpZone_UnTransformOrigin(RandomSelection_chosen_ent, self.origin) - RandomSelection_chosen_ent.fireball_impactvec);
d = damage + (edgedamage - damage) * (d / dist);
Fire_AddDamage(RandomSelection_chosen_ent, self.realowner, d * burntime, burntime, self.projectiledeathtype | HITTYPE_BOUNCE);
- //trailparticles(self, particleeffectnum("fireball_laser"), self.origin, RandomSelection_chosen_ent.fireball_impactvec);
- pointparticles(particleeffectnum("fireball_laser"), self.origin, RandomSelection_chosen_ent.fireball_impactvec - self.origin, 1);
+ Send_Effect(EFFECT_FIREBALL_LASER, self.origin, RandomSelection_chosen_ent.fireball_impactvec - self.origin, 1);
}
}
{
entity proj;
- 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";
{
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);
+ Send_Effect(EFFECT_FIREBALL_PRE_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1);
}
void W_Fireball_Attack1_Frame4(void)
void W_Fireball_Attack1_Frame0(void)
{
W_Fireball_AttackEffect(0, '-1.25 -3.75 0');
- sound(self, CH_WEAPON_SINGLE, "weapons/fireball_prefire2.wav", VOL_BASE, ATTEN_NORM);
+ sound(self, CH_WEAPON_SINGLE, W_Sound("fireball_prefire2"), VOL_BASE, ATTEN_NORM);
weapon_thinkf(WFRAME_FIRE1, WEP_CVAR_PRI(fireball, animtime), W_Fireball_Attack1_Frame1);
}
f_diff = '+1.25 +3.75 0';
break;
}
- W_SetupShot_ProjectileSize(self, '-4 -4 -4', '4 4 4', FALSE, 2, "weapons/fireball_fire.wav", CH_WEAPON_A, WEP_CVAR_SEC(fireball, damage));
+ 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;
}
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");
+ 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;
}
self.use();
} else {
self.cnt++;
- pointparticles(particleeffectnum("hagar_bounce"), self.origin, self.velocity, 1);
+ Send_Effect(EFFECT_HAGAR_BOUNCE, self.origin, self.velocity, 1);
self.angles = vectoangles(self.velocity);
self.owner = world;
self.projectiledeathtype |= HITTYPE_BOUNCE;
W_DecreaseAmmo(WEP_CVAR_PRI(hagar, ammo));
- 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;
W_DecreaseAmmo(WEP_CVAR_SEC(hagar, ammo));
- 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;
weapon_prepareattack_do(1, WEP_CVAR_SEC(hagar, refire));
- 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;
self.weaponentity.state = WS_READY;
W_DecreaseAmmo(WEP_CVAR_SEC(hagar, ammo) * self.hagar_load * -1); // give back ammo
self.hagar_load = 0;
- 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);
// pause until we can load rockets again, once we re-press the alt fire button
self.hagar_loadstep = time + WEP_CVAR_SEC(hagar, load_speed) * W_WeaponRateFactor();
W_DecreaseAmmo(WEP_CVAR_SEC(hagar, ammo));
self.weaponentity.state = WS_INUSE;
self.hagar_load += 1;
- sound(self, CH_WEAPON_B, "weapons/hagar_load.wav", VOL_BASE * 0.8, ATTN_NORM); // sound is too loud according to most
+ sound(self, CH_WEAPON_B, W_Sound("hagar_load"), VOL_BASE * 0.8, ATTN_NORM); // sound is too loud according to most
if(self.hagar_load >= WEP_CVAR_SEC(hagar, load_max))
self.hagar_loadstep = time + WEP_CVAR_SEC(hagar, load_hold) * W_WeaponRateFactor();
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;
}
}
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;
}
}
}
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");
+ 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;
}
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;
}
if(self.crouch)
spread = spread * WEP_CVAR_PRI(hlac, spread_crouchmod);
- 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)
{
if(self.crouch)
spread = spread * WEP_CVAR_SEC(hlac, spread_crouchmod);
- 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;
}
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");
+ 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;
}
}
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;
}
case WR_SUICIDEMESSAGE:
W_DecreaseAmmo(WEP_CVAR(hmg, ammo));
- 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)
{
self.misc_bulletcounter = self.misc_bulletcounter + 1;
- pointparticles(particleeffectnum("uzi_muzzleflash"), w_shotorg, w_shotdir * 1000, 1);
+ Send_Effect(EFFECT_MACHINEGUN_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1);
W_MachineGun_MuzzleFlash();
W_AttachToShotorg(self.muzzle_flash, '5 0 0');
{
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);
else
self.BUTTON_ATCK2 = bot_aim(1000000, 0, 0.001, FALSE);
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");
+ 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;
}
}
case WR_RELOAD:
{
- W_Reload(WEP_CVAR(hmg, ammo), "weapons/reload.wav");
+ W_Reload(WEP_CVAR(hmg, ammo), W_Sound("reload"));
return TRUE;
}
case WR_SUICIDEMESSAGE:
w_cvar(id, sn, SEC, speed) \
w_cvar(id, sn, SEC, health) \
w_cvar(id, sn, SEC, damageforcescale) \
+ w_cvar(id, sn, NONE, secondary) \
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) \
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, "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;
}
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");
+ 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;
}
--- /dev/null
+#ifndef CHAOS
+const float 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
+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; }
+
+ 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; }
+
+ makevectors (player.v_angle);
+ float dot = normalize (myowner.origin - player.origin) * v_forward;
+
+ 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;
+}
+
+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));
+
+ // 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));
+}
+
+.float lightsabre_active;
+
+void W_LightSabre_SetActive(float newactive, float dosound)
+{
+ if(newactive)
+ {
+ 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.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);
+ else
+ self.BUTTON_ATCK = bot_aim(1000000, 0, 0.001, FALSE);
+
+ 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);
+ 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;
+ }
+ 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;
+ }
+ case WR_SETUP:
+ {
+ self.ammo_field = ammo_none;
+ W_LightSabre_SetActive(0, FALSE);
+ return TRUE;
+ }
+ case WR_CHECKAMMO1:
+ {
+ return TRUE;
+ }
+ case WR_CHECKAMMO2:
+ {
+ return TRUE;
+ }
+ case WR_CONFIG:
+ {
+ LIGHTSABRE_SETTINGS(WEP_CONFIG_WRITE_CVARS, WEP_CONFIG_WRITE_PROPS)
+ return TRUE;
+ }
+ case WR_RELOAD:
+ {
+ 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;
+}
+#endif
+#ifdef CSQC
+.float prevric;
+float W_Lightsabre(float req)
+{
+ switch(req)
+ {
+ case WR_IMPACTEFFECT:
+ {
+ return TRUE;
+ }
+ case WR_INIT:
+ {
+ return TRUE;
+ }
+ case WR_ZOOMRETICLE:
+ {
+ // no weapon specific image for this weapon
+ return FALSE;
+ }
+ }
+ return FALSE;
+}
+#endif
+#endif
+#endif
void W_MachineGun_Attack(float deathtype)
{
- 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;
else
fireBullet(w_shotorg, w_shotdir, WEP_CVAR(machinegun, sustained_spread), WEP_CVAR(machinegun, solidpenetration), WEP_CVAR(machinegun, sustained_damage), WEP_CVAR(machinegun, sustained_force), deathtype, 0);
- pointparticles(particleeffectnum("uzi_muzzleflash"), w_shotorg, w_shotdir * 1000, 1);
+ Send_Effect(EFFECT_MACHINEGUN_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1);
W_MachineGun_MuzzleFlash();
W_AttachToShotorg(self.muzzle_flash, '5 0 0');
W_DecreaseAmmo(WEP_CVAR(machinegun, sustained_ammo));
- 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;
self.misc_bulletcounter = self.misc_bulletcounter + 1;
- pointparticles(particleeffectnum("uzi_muzzleflash"), w_shotorg, w_shotdir * 1000, 1);
+ Send_Effect(EFFECT_MACHINEGUN_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1);
W_MachineGun_MuzzleFlash();
W_AttachToShotorg(self.muzzle_flash, '5 0 0');
void W_MachineGun_Attack_Burst(void)
{
- 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;
fireBullet(w_shotorg, w_shotdir, WEP_CVAR(machinegun, burst_speed), WEP_CVAR(machinegun, solidpenetration), WEP_CVAR(machinegun, sustained_damage), WEP_CVAR(machinegun, sustained_force), WEP_MACHINEGUN, 0);
- pointparticles(particleeffectnum("uzi_muzzleflash"), w_shotorg, w_shotdir * 1000, 1);
+ Send_Effect(EFFECT_MACHINEGUN_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1);
W_MachineGun_MuzzleFlash();
W_AttachToShotorg(self.muzzle_flash, '5 0 0');
{
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);
else
self.BUTTON_ATCK2 = bot_aim(1000000, 0, 0.001, FALSE);
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");
+ 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;
}
}
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;
}
case WR_SUICIDEMESSAGE:
void W_MineLayer_Stick(entity to)
{
- spamsound(self, CH_SHOTS, "weapons/mine_stick.wav", VOL_BASE, ATTN_NORM);
+ spamsound(self, CH_SHOTS, W_Sound("mine_stick"), VOL_BASE, ATTN_NORM);
// in order for mines to face properly when sticking to the ground, they must be a server side entity rather than a csqc projectile
if((time > self.cnt) && (!self.mine_time) && (self.cnt > 0))
{
if(WEP_CVAR(minelayer, lifetime_countdown) > 0)
- spamsound(self, CH_SHOTS, "weapons/mine_trigger.wav", VOL_BASE, ATTN_NORM);
+ spamsound(self, CH_SHOTS, W_Sound("mine_trigger"), VOL_BASE, ATTN_NORM);
self.mine_time = time + WEP_CVAR(minelayer, lifetime_countdown);
self.mine_explodeanyway = 1; // make the mine super aggressive -- Samual: Rather, make it not care if a team mate is near.
}
// a player's mines shall explode if he disconnects or dies
// TODO: Do this on team change too -- Samual: But isn't a player killed when they switch teams?
- if(!IS_PLAYER(self.realowner) || self.realowner.deadflag != DEAD_NO || self.realowner.frozen)
+ if(!IS_PLAYER(self.realowner) || Player_Trapped(self.realowner))
{
other = world;
self.projectiledeathtype |= HITTYPE_BOUNCE;
head = findradius(self.origin, WEP_CVAR(minelayer, proximityradius));
while(head)
{
- if(IS_PLAYER(head) && head.deadflag == DEAD_NO && !head.frozen)
+ if(IS_PLAYER(head) && !Player_Trapped(head))
if(head != self.realowner && DIFF_TEAM(head, self.realowner)) // don't trigger for team mates
if(!self.mine_time)
{
- spamsound(self, CH_SHOTS, "weapons/mine_trigger.wav", VOL_BASE, ATTN_NORM);
+ spamsound(self, CH_SHOTS, W_Sound("mine_trigger"), VOL_BASE, ATTN_NORM);
self.mine_time = time + WEP_CVAR(minelayer, time);
}
head = head.chain;
{
// the refire delay keeps this message from being spammed
Send_Notification(NOTIF_ONE, self, MSG_MULTI, WEAPON_MINELAYER_LIMIT, WEP_CVAR(minelayer, limit));
- play2(self, "weapons/unavailable.wav");
+ play2(self, W_Sound("unavailable"));
return;
}
}
W_DecreaseAmmo(WEP_CVAR(minelayer, ammo));
- 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;
self.BUTTON_ATCK = FALSE;
else
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;
}
makevectors(mine.v_angle);
targ = targetlist;
- if(skill > 9) // normal players only do this for the target they are tracking
+ if(bot_skill > 9) // normal players only do this for the target they are tracking
{
targ = targetlist;
while(targ)
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;
// dprint(ftos(random()/distance*300),">");dprint(ftos(frametime*bound(0,(10-skill)*0.2,1)),"\n");
}
// 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;
- if((skill > 6.5) && (selfdamage > self.health))
+ if((bot_skill > 6.5) && (selfdamage > self.health))
self.BUTTON_ATCK2 = FALSE;
//if(self.BUTTON_ATCK2 == TRUE)
// dprint(ftos(desirabledamage),"\n");
if(self.BUTTON_ATCK2)
{
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;
{
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");
+ 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;
}
}
case WR_RELOAD:
{
- W_Reload(WEP_CVAR(minelayer, ammo), "weapons/reload.wav");
+ W_Reload(WEP_CVAR(minelayer, ammo), W_Sound("reload"));
return TRUE;
}
case WR_SUICIDEMESSAGE:
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;
}
else if(WEP_CVAR_PRI(mortar, type) == 2 && (!other || (other.takedamage != DAMAGE_AIM && other.movetype == MOVETYPE_NONE))) // stick
{
- spamsound(self, CH_SHOTS, "weapons/grenade_stick.wav", VOL_BASE, ATTN_NORM);
+ spamsound(self, CH_SHOTS, W_Sound("grenade_stick"), VOL_BASE, ATTN_NORM);
// let it stick whereever it is
self.oldvelocity = self.velocity;
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;
}
else if(WEP_CVAR_SEC(mortar, type) == 2 && (!other || (other.takedamage != DAMAGE_AIM && other.movetype == MOVETYPE_NONE))) // stick
{
- spamsound(self, CH_SHOTS, "weapons/grenade_stick.wav", VOL_BASE, ATTN_NORM);
+ spamsound(self, CH_SHOTS, W_Sound("grenade_stick"), VOL_BASE, ATTN_NORM);
// let it stick whereever it is
self.oldvelocity = self.velocity;
W_DecreaseAmmo(WEP_CVAR_PRI(mortar, ammo));
- 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;
W_DecreaseAmmo(WEP_CVAR_SEC(mortar, ammo));
- 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;
}
}
if(nadefound)
- 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);
}
else if(weapon_prepareattack(1, WEP_CVAR_SEC(mortar, refire)))
{
}
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");
+ 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;
}
}
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;
}
case WR_SUICIDEMESSAGE:
w_shotdir = v_forward;
w_shotorg = self.origin + self.view_ofs + ((w_shotorg - self.origin - self.view_ofs) * v_forward) * v_forward;
- //pointparticles(particleeffectnum("grenadelauncher_muzzleflash"), w_shotorg, w_shotdir * 1000, 1);
-
gren = spawn();
gren.cnt = type;
gren.owner = gren.realowner = self;
gren.nextthink = time + WEP_CVAR_BOTH(porto, (type <= 0), lifetime);
gren.think = W_Porto_Think;
gren.touch = W_Porto_Touch;
-
- if(self.items & IT_STRENGTH)
- W_SetupProjVelocity_Basic(gren, WEP_CVAR_BOTH(porto, (type <= 0), speed) * autocvar_g_balance_powerup_strength_force, 0);
- else
- W_SetupProjVelocity_Basic(gren, WEP_CVAR_BOTH(porto, (type <= 0), speed), 0);
+
+ W_SetupProjVelocity_Basic(gren, WEP_CVAR_BOTH(porto, (type <= 0), speed), 0);
gren.angles = vectoangles(gren.velocity);
gren.flags = FL_PROJECTILE;
}
case WR_INIT:
{
- precache_model("models/weapons/g_porto.md3");
- precache_model("models/weapons/v_porto.md3");
- precache_model("models/weapons/h_porto.iqm");
+ precache_model(W_Model("g_porto.md3"));
+ precache_model(W_Model("v_porto.md3"));
+ precache_model(W_Model("h_porto.iqm"));
precache_model("models/portal.md3");
precache_sound("porto/bounce.wav");
precache_sound("porto/create.wav");
--- /dev/null
+#ifndef CHAOS
+const float 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
+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));
+ 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; }
+ 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;
+}
+
+float W_Revolver(float req)
+{
+ float ammo_amount;
+ switch(req)
+ {
+ case WR_AIM:
+ {
+ if(!W_Revolver_CheckLoad())
+ self.BUTTON_ATCK2 = TRUE;
+ else
+ self.BUTTON_ATCK = bot_aim(1000000, 0, 0.001, FALSE);
+
+ 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;
+ }
+ 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;
+ }
+ case WR_SETUP:
+ {
+ W_Revolver_SetLoad(0);
+ 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;
+ }
+ case WR_CONFIG:
+ {
+ REVOLVER_SETTINGS(WEP_CONFIG_WRITE_CVARS, WEP_CONFIG_WRITE_PROPS)
+ return TRUE;
+ }
+ case WR_RELOAD:
+ {
+ W_Reload(WEP_CVAR(revolver, ammo), W_Sound("reload")); // WEAPONTODO
+ return TRUE;
+ }
+ case WR_SUICIDEMESSAGE:
+ {
+ return WEAPON_THINKING_WITH_PORTALS;
+ }
+ case WR_KILLMESSAGE:
+ {
+ return WEAPON_REVOLVER_MURDER;
+ }
+ }
+ 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;
+ }
+ case WR_INIT:
+ {
+ precache_sound("weapons/ric1.wav");
+ precache_sound("weapons/ric2.wav");
+ precache_sound("weapons/ric3.wav");
+ return TRUE;
+ }
+ case WR_ZOOMRETICLE:
+ {
+ // no weapon specific image for this weapon
+ return FALSE;
+ }
+ }
+ return FALSE;
+}
+#endif
+#endif
+#endif
/* 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",
/* wepimg */ "weaponrifle",
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
{
void W_Rifle_Attack(void)
{
- W_Rifle_FireBullet(WEP_CVAR_PRI(rifle, spread), WEP_CVAR_PRI(rifle, damage), WEP_CVAR_PRI(rifle, force), WEP_CVAR_PRI(rifle, solidpenetration), WEP_CVAR_PRI(rifle, ammo), WEP_RIFLE, WEP_CVAR_PRI(rifle, tracer), WEP_CVAR_PRI(rifle, shots), "weapons/campingrifle_fire.wav");
+ W_Rifle_FireBullet(WEP_CVAR_PRI(rifle, spread), WEP_CVAR_PRI(rifle, damage), WEP_CVAR_PRI(rifle, force), WEP_CVAR_PRI(rifle, solidpenetration), WEP_CVAR_PRI(rifle, ammo), WEP_RIFLE, WEP_CVAR_PRI(rifle, tracer), WEP_CVAR_PRI(rifle, shots), W_Sound("campingrifle_fire"));
}
void W_Rifle_Attack2(void)
{
- W_Rifle_FireBullet(WEP_CVAR_SEC(rifle, spread), WEP_CVAR_SEC(rifle, damage), WEP_CVAR_SEC(rifle, force), WEP_CVAR_SEC(rifle, solidpenetration), WEP_CVAR_SEC(rifle, ammo), WEP_RIFLE | HITTYPE_SECONDARY, WEP_CVAR_SEC(rifle, tracer), WEP_CVAR_SEC(rifle, shots), "weapons/campingrifle_fire2.wav");
+ W_Rifle_FireBullet(WEP_CVAR_SEC(rifle, spread), WEP_CVAR_SEC(rifle, damage), WEP_CVAR_SEC(rifle, force), WEP_CVAR_SEC(rifle, solidpenetration), WEP_CVAR_SEC(rifle, ammo), WEP_RIFLE | HITTYPE_SECONDARY, WEP_CVAR_SEC(rifle, tracer), WEP_CVAR_SEC(rifle, shots), W_Sound("campingrifle_fire2"));
}
.void(void) rifle_bullethail_attackfunc;
}
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");
+ 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;
}
}
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;
}
case WR_SUICIDEMESSAGE:
entity flash = spawn ();
W_DecreaseAmmo(WEP_CVAR(rpc, ammo));
- 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;
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");
+ 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;
}
}
case WR_RELOAD:
{
- W_Reload(WEP_CVAR(rpc, ammo), "weapons/reload.wav");
+ W_Reload(WEP_CVAR(rpc, ammo), W_Sound("reload"));
return TRUE;
}
case WR_SUICIDEMESSAGE:
W_DecreaseAmmo(WEP_CVAR(seeker, missile_ammo));
makevectors(self.v_angle);
- 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;
f_diff = '+1.25 +3.75 0';
break;
}
- 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;
entity missile;
W_DecreaseAmmo(WEP_CVAR(seeker, tag_ammo));
- 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;
}
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");
+ 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;
}
}
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;
}
case WR_SUICIDEMESSAGE:
if(!self.cnt)
{
self.cnt = time;
- W_PlayStrengthSound(self.realowner);
}
// update values for v_* vectors
(self.realowner.origin + self.realowner.view_ofs),
targpos,
FALSE,
- self.realowner,
+ world, //self.realowner,
ANTILAG_LATENCY(self.realowner)
);
void W_Shockwave_Melee(void)
{
- sound(self, CH_WEAPON_A, "weapons/shotgun_melee.wav", VOL_BASE, ATTN_NORM);
+ sound(self, CH_WEAPON_A, W_Sound("shotgun_melee"), VOL_BASE, ATTN_NORM);
weapon_thinkf(WFRAME_FIRE2, WEP_CVAR(shockwave, melee_animtime), w_ready);
entity meleetemp;
float i, queue = 0;
// set up the shot direction
- 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;
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");
+ precache_sound(W_Sound("lasergun_fire"));
+ precache_sound(W_Sound("shotgun_melee"));
SHOCKWAVE_SETTINGS(WEP_SKIP_CVAR, WEP_SET_PROP)
return TRUE;
}
W_DecreaseAmmo(WEP_CVAR_PRI(shotgun, ammo));
- 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);
- pointparticles(particleeffectnum("shotgun_muzzleflash"), w_shotorg, w_shotdir * 1000, WEP_CVAR_PRI(shotgun, ammo));
+ Send_Effect(EFFECT_SHOTGUN_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, WEP_CVAR_PRI(shotgun, ammo));
// casing code
if(autocvar_g_casings >= 1)
if(!self.cnt) // set start time of melee
{
self.cnt = time;
- W_PlayStrengthSound(self.realowner);
}
makevectors(self.realowner.v_angle); // update values for v_* vectors
+ (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, 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);
void W_Shotgun_Attack2(void)
{
- sound(self, CH_WEAPON_A, "weapons/shotgun_melee.wav", VOL_BASE, ATTEN_NORM);
+ sound(self, CH_WEAPON_A, W_Sound("shotgun_melee"), VOL_BASE, ATTEN_NORM);
weapon_thinkf(WFRAME_FIRE2, WEP_CVAR_SEC(shotgun, animtime), w_ready);
entity meleetemp;
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");
+ precache_sound(W_Sound("shotgun_fire"));
+ precache_sound(W_Sound("shotgun_melee"));
SHOTGUN_SETTINGS(WEP_SKIP_CVAR, WEP_SET_PROP)
return TRUE;
}
}
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;
}
case WR_SUICIDEMESSAGE:
o = gettaginfo(self.exteriorweaponentity, 0);
if(time > self.tuba_smoketime)
{
- pointparticles(particleeffectnum("smoke_ring"), o + v_up * 45 + v_right * -6 + v_forward * 8, v_up * 100, 1);
+ Send_Effect(EFFECT_SMOKE_RING, o + v_up * 45 + v_right * -6 + v_forward * 8, v_up * 100, 1);
self.tuba_smoketime = time + 0.25;
}
}
}
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");
+ 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;
}
break;
}
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);
}
#define VAPORIZER_SETTINGS_LIST(w_cvar,w_prop,id,sn) \
w_cvar(id, sn, PRI, ammo) \
w_cvar(id, sn, PRI, animtime) \
+ w_cvar(id, sn, PRI, damage) \
w_cvar(id, sn, PRI, refire) \
w_cvar(id, sn, SEC, ammo) \
w_cvar(id, sn, SEC, animtime) \
w_cvar(id, sn, SEC, shotangle) \
w_cvar(id, sn, SEC, speed) \
w_cvar(id, sn, SEC, spread) \
+ w_cvar(id, sn, PRI, charge) \
+ w_cvar(id, sn, PRI, charge_distance) \
+ w_cvar(id, sn, PRI, charge_force) \
+ w_cvar(id, sn, PRI, charge_force_forwardbias) \
+ w_cvar(id, sn, PRI, charge_force_zscale) \
+ w_cvar(id, sn, PRI, charge_multiplier_accuracy) \
+ w_cvar(id, sn, PRI, charge_multiplier_distance) \
+ w_cvar(id, sn, PRI, charge_multiplier_min) \
+ w_cvar(id, sn, PRI, charge_jump_damage) \
+ w_cvar(id, sn, PRI, charge_jump_edgedamage) \
+ w_cvar(id, sn, PRI, charge_jump_force) \
+ w_cvar(id, sn, PRI, charge_jump_force_velocitybias) \
+ w_cvar(id, sn, PRI, charge_jump_force_zscale) \
+ w_cvar(id, sn, PRI, charge_jump_multiplier_accuracy) \
+ w_cvar(id, sn, PRI, charge_jump_multiplier_distance) \
+ w_cvar(id, sn, PRI, charge_jump_multiplier_min) \
+ w_cvar(id, sn, PRI, charge_jump_radius) \
+ w_cvar(id, sn, PRI, charge_spread_max) \
+ w_cvar(id, sn, PRI, charge_spread_min) \
+ w_cvar(id, sn, PRI, charge_splash_damage) \
+ w_cvar(id, sn, PRI, charge_splash_edgedamage) \
+ w_cvar(id, sn, PRI, charge_splash_force) \
+ w_cvar(id, sn, PRI, charge_splash_force_forwardbias) \
+ w_cvar(id, sn, PRI, charge_splash_force_zscale) \
+ w_cvar(id, sn, PRI, charge_splash_multiplier_accuracy) \
+ w_cvar(id, sn, PRI, charge_splash_multiplier_distance) \
+ w_cvar(id, sn, PRI, charge_splash_multiplier_min) \
+ w_cvar(id, sn, PRI, charge_splash_radius) \
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) \
VAPORIZER_SETTINGS(WEP_ADD_CVAR, WEP_ADD_PROP)
.float vaporizer_lasthit;
.float jump_interval;
+.float jump_interval2;
+.float held_down;
+.float vaporizer_held_down;
+.float vaporizer_jumpinterval;
+.float superrocket_lasthit;
+.float rm_force;
+.float rm_damage;
+.float rm_edmg;
+#endif
+#ifdef CSQC
+void Net_ReadSuperBlastParticle();
#endif
#else
#ifdef SVQC
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;
+}
+
+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
+ }
+
+ // 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
+ }
+
+ // 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
+ }
+ }
+
+ return FALSE;
+}
+
+entity shockwave_hit[32];
+float shockwave_hit_damage[32];
+vector shockwave_hit_force[32];
+
+float W_Vaporizer_SuperBlast_CheckHit(float queue, entity head, vector final_force, float final_damage)
+{
+ if(!head) { return FALSE; }
+ float i;
+
+ ++queue;
+
+ for(i = 1; i <= queue; ++i)
+ {
+ if(shockwave_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;
+ }
+ }
+
+ shockwave_hit[queue] = head;
+ shockwave_hit_force[queue] = final_force;
+ shockwave_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);
+ 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];
+
+ 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;
+ }
+}
+
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, 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;
- FireRailgunBullet(w_shotorg, w_shotorg + w_shotdir * MAX_SHOT_DISTANCE, 10000, 800, 0, 0, 0, 0, WEP_VAPORIZER);
+ FireRailgunBullet(w_shotorg, w_shotorg + w_shotdir * MAX_SHOT_DISTANCE, vaporizer_damage, 800, 0, 0, 0, 0, WEP_VAPORIZER);
if(yoda && flying)
Send_Notification(NOTIF_ONE, self, MSG_ANNCE, ANNCE_ACHIEVEMENT_YODA);
self.vaporizer_lasthit = damage_goodhits;
- pointparticles(particleeffectnum("nex_muzzleflash"), w_shotorg, w_shotdir * 1000, 1);
+ Send_Effect(EFFECT_VORTEX_MUZZLEFLASH, w_shotorg, w_shotdir * 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: // Red
- if(damage_goodhits)
- WarpZone_TrailParticles(world, particleeffectnum("TE_TEI_G3RED_HIT"), w_shotorg, v);
- else
- WarpZone_TrailParticles(world, particleeffectnum("TE_TEI_G3RED"), w_shotorg, v);
- break;
- case NUM_TEAM_2: // Blue
- if(damage_goodhits)
- WarpZone_TrailParticles(world, particleeffectnum("TE_TEI_G3BLUE_HIT"), w_shotorg, v);
- else
- WarpZone_TrailParticles(world, particleeffectnum("TE_TEI_G3BLUE"), w_shotorg, v);
- break;
- case NUM_TEAM_3: // Yellow
- if(damage_goodhits)
- WarpZone_TrailParticles(world, particleeffectnum("TE_TEI_G3YELLOW_HIT"), w_shotorg, v);
- else
- WarpZone_TrailParticles(world, particleeffectnum("TE_TEI_G3YELLOW"), w_shotorg, v);
- break;
- case NUM_TEAM_4: // Pink
- if(damage_goodhits)
- WarpZone_TrailParticles(world, particleeffectnum("TE_TEI_G3PINK_HIT"), w_shotorg, v);
- else
- WarpZone_TrailParticles(world, particleeffectnum("TE_TEI_G3PINK"), w_shotorg, v);
- break;
- default:
- if(damage_goodhits)
- WarpZone_TrailParticles(world, particleeffectnum("TE_TEI_G3_HIT"), w_shotorg, v);
- else
- WarpZone_TrailParticles(world, particleeffectnum("TE_TEI_G3"), w_shotorg, v);
- break;
+ 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;
}
+ if(autocvar_g_rm)
+ if(!(trace_dphitq3surfaceflags & (Q3SURFACEFLAG_SKY | Q3SURFACEFLAG_NOIMPACT)))
+ W_RocketMinsta_Explosion(trace_endpos);
+
+ Send_Effect(f, w_shotorg, v, 0);
+
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));
+ 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);
+ 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);
+ 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];
+
+ 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;
+ }
+ //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
+ 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);
+ 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_dodgerating = vaporizer_damage;
+
+ missile.takedamage = DAMAGE_YES;
+ missile.damageforcescale = 0;
+ missile.health = 350;
+ missile.event_damage = W_RocketMinsta_SuperRocket_Damage;
+ 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
+
+ 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);
+ 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_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.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);
+
+ 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);
+ 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_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.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);
+
+ other = proj; MUTATOR_CALLHOOK(EditProjectile);
+ counter++;
+ }
+}
+
float W_Vaporizer(float req)
{
float ammo_amount;
float vaporizer_ammo;
+ float rapid = autocvar_g_rm_laser_rapid;
// now multiple WR_s use this
vaporizer_ammo = ((g_instagib) ? 1 : WEP_CVAR_PRI(vaporizer, ammo));
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)
+ {
+ 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)
{
- W_Vaporizer_Attack();
- weapon_thinkf(WFRAME_FIRE1, WEP_CVAR_PRI(vaporizer, animtime), w_ready);
+ 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;
+ }
+
+ 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.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();
weapon_thinkf(WFRAME_FIRE2, WEP_CVAR_SEC(vaporizer, animtime), w_ready);
}
}
+ else
+ self.held_down = FALSE;
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;
else
used_ammo = vaporizer_ammo;
- W_Reload(used_ammo, "weapons/reload.wav");
+ W_Reload(used_ammo, W_Sound("reload"));
return TRUE;
}
case WR_SUICIDEMESSAGE:
{
- return WEAPON_THINKING_WITH_PORTALS;
+ if(w_deathtype & HITTYPE_SPLASH)
+ return WEAPON_VAPORIZER_SUICIDE;
+ else
+ return WEAPON_THINKING_WITH_PORTALS;
}
case WR_KILLMESSAGE:
{
- return WEAPON_VAPORIZER_MURDER;
+ if(w_deathtype & HITTYPE_SPLASH)
+ return WEAPON_VAPORIZER_MURDER_CHARGE;
+ else
+ return WEAPON_VAPORIZER_MURDER;
}
}
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;
+
+ 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();
+
+ shockwave.sb_spread_max = ReadByte();
+ shockwave.sb_spread_min = ReadByte();
+
+ shockwave.sv_entnum = ReadByte();
+
+ shockwave.sb_time = time;
+}
+
float W_Vaporizer(float req)
{
switch(req)
{
vector org2;
org2 = w_org + w_backoff * 6;
+ if(!(w_deathtype & HITTYPE_SPLASH))
if(w_deathtype & HITTYPE_SECONDARY)
{
pointparticles(particleeffectnum("laser_impact"), org2, w_backoff * 1000, 1);
mydmg *= charge;
myforce *= charge;
- 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;
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");
+ 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;
}
}
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;
}
case WR_SUICIDEMESSAGE:
{
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);
#endif
#endif
}
+void WepSet_AddStat_InMap()
+{
+ addstat(STAT_WEAPONSINMAP, AS_INT, weaponsinmap_x);
+#if WEP_MAXCOUNT > 24
+ addstat(STAT_WEAPONSINMAP2, AS_INT, weaponsinmap_y);
+#if WEP_MAXCOUNT > 48
+ addstat(STAT_WEAPONSINMAP3, AS_INT, weaponsinmap_z);
+#endif
+#endif
+}
void WriteWepSet(float dst, WepSet w)
{
#if WEP_MAXCOUNT > 48
#endif
return w;
}
+WepSet WepSet_GetFromStat_InMap()
+{
+ WepSet w = '0 0 0';
+ w_x = getstati(STAT_WEAPONSINMAP);
+#if WEP_MAXCOUNT > 24
+ w_y = getstati(STAT_WEAPONSINMAP2);
+#if WEP_MAXCOUNT > 48
+ w_z = getstati(STAT_WEAPONSINMAP3);
+#endif
+#endif
+ return w;
+}
WepSet ReadWepSet()
{
#if WEP_MAXCOUNT > 48
e.wpcolor = clr;
e.wpmodel = strzone(strcat("wpn-", ftos(id)));
e.mdl = modelname;
+#ifdef CSQC
e.model = strzone(strcat("models/weapons/g_", modelname, ".md3"));
+#elif defined(SVQC)
+ e.model = strzone(W_Model(strcat("g_", modelname, ".md3")));
+#endif
e.w_simplemdl = strzone(simplemdl); // simpleitems weapon model/image
e.w_crosshair = strzone(car(crosshair));
string s = cdr(crosshair);
case ammo_rockets: return "ammo_rockets";
case ammo_cells: return "ammo_cells";
case ammo_plasma: return "ammo_cells";
+ case ammo_supercells: return "ammo_supercells";
case ammo_fuel: return "ammo_fuel";
default: return ""; // wtf, no ammo type?
}
case 2: return ammo_rockets;
case 3: return ammo_cells;
case 4: return ammo_plasma;
- case 5: return ammo_fuel;
+ case 5: return ammo_supercells;
+ case 6: return ammo_fuel;
default: return ammo_none;
}
}
case ammo_rockets: return STAT_ROCKETS;
case ammo_cells: return STAT_CELLS;
case ammo_plasma: return STAT_PLASMA;
+ case ammo_supercells: return STAT_SUPERCELLS;
case ammo_fuel: return STAT_FUEL;
default: return -1;
}
}
#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);
+ 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
WepSet WepSet_FromWeapon(float a);
#ifdef SVQC
void WepSet_AddStat();
+void WepSet_AddStat_InMap();
void WriteWepSet(float dest, WepSet w);
#endif
#ifdef CSQC
WepSet WepSet_GetFromStat();
+WepSet WepSet_GetFromStat_InMap();
WepSet ReadWepSet();
#endif
float 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;
.float ammo_rockets;
.float ammo_cells;
+.float ammo_supercells;
.float ammo_plasma;
.float ammo_fuel;
.float ammo_none;
}
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)
self.pmove_flags |= PMF_DUCKED;
-#endif
+//#endif
CSQCPlayer_SetMinsMaxs();
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
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
../common/command/rpn.qh
../common/command/generic.qh
../common/command/shared_defs.qh
+../common/command/script.qh
../common/urllib.qh
../common/monsters/monsters.qh
../common/command/markup.qc
../common/command/rpn.qc
../common/command/generic.qc
+../common/command/script.qc
command/menu_cmd.qc
menu.qc
draw.qc
float autocvar_g_allow_oldvortexbeam;
float 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;
float autocvar_g_balance_armor_regen;
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;
float autocvar_g_balance_grapplehook_airfriction;
float autocvar_g_balance_grapplehook_force_rubber;
float autocvar_g_balance_grapplehook_force_rubber_overstretch;
+float autocvar_g_balance_grapplehook_gravity;
float autocvar_g_balance_grapplehook_health;
float autocvar_g_balance_grapplehook_length_min;
float autocvar_g_balance_grapplehook_speed_fly;
float autocvar_g_balance_grapplehook_speed_pull;
+float autocvar_g_balance_grapplehook_pull_frozen;
float autocvar_g_balance_grapplehook_stretch;
+float autocvar_g_balance_grapplehook_crouchslide;
float autocvar_g_balance_grapplehook_damagedbycontents;
float autocvar_g_balance_grapplehook_refire;
float autocvar_g_balance_health_limit;
float autocvar_g_balance_health_rot;
float autocvar_g_balance_health_rotlinear;
float autocvar_g_balance_health_rotstable;
-float autocvar_g_balance_keyhunt_damageforcescale;
-float autocvar_g_balance_keyhunt_delay_collect;
-float autocvar_g_balance_keyhunt_delay_return;
-float autocvar_g_balance_keyhunt_delay_round;
-float autocvar_g_balance_keyhunt_delay_tracking;
-float autocvar_g_balance_keyhunt_dropvelocity;
-float autocvar_g_balance_keyhunt_maxdist;
-float autocvar_g_balance_keyhunt_protecttime;
-float autocvar_g_balance_keyhunt_score_capture;
-float autocvar_g_balance_keyhunt_score_carrierfrag;
-float autocvar_g_balance_keyhunt_score_collect;
-float autocvar_g_balance_keyhunt_score_destroyed;
-float autocvar_g_balance_keyhunt_score_destroyed_ownfactor;
-float autocvar_g_balance_keyhunt_score_push;
-float autocvar_g_balance_keyhunt_throwvelocity;
float autocvar_g_balance_kill_delay;
float autocvar_g_balance_kill_antispam;
float autocvar_g_balance_nexball_primary_animtime;
float autocvar_g_balance_pause_health_rot_spawn;
float autocvar_g_balance_portal_health;
float autocvar_g_balance_portal_lifetime;
-float autocvar_g_balance_powerup_invincible_takedamage;
-float autocvar_g_balance_powerup_invincible_time;
-float autocvar_g_balance_powerup_strength_damage;
-float autocvar_g_balance_powerup_strength_force;
-float autocvar_g_balance_powerup_strength_selfdamage;
-float autocvar_g_balance_powerup_strength_selfforce;
-float autocvar_g_balance_powerup_strength_time;
float autocvar_g_balance_superweapons_time;
float autocvar_g_balance_selfdamagepercent;
float autocvar_g_balance_teams;
string autocvar_g_ban_sync_uri;
string autocvar_g_banned_list;
float autocvar_g_banned_list_idmode;
-float autocvar_g_bastet;
float autocvar_g_botclip_collisions;
float autocvar_g_bugrigs;
float autocvar_g_ca_damage2score_multiplier;
float autocvar_g_ctf_throw_punish_count;
float autocvar_g_ctf_throw_punish_delay;
float autocvar_g_ctf_throw_punish_time;
-float autocvar_g_ctf_throw_strengthmultiplier;
float autocvar_g_ctf_throw_velocity_forward;
float autocvar_g_ctf_throw_velocity_up;
float autocvar_g_ctf_drop_velocity_up;
float autocvar_g_ctf_drop_velocity_side;
+float autocvar_g_ctf_oneflag_reverse;
float autocvar_g_ctf_portalteleport;
float autocvar_g_ctf_pass;
float autocvar_g_ctf_pass_arc;
float autocvar_g_ctf_flag_dropped_floatinwater;
float autocvar_g_ctf_flag_glowtrails;
float autocvar_g_ctf_flag_health;
+string autocvar_g_ctf_flag_neutral_model;
+float autocvar_g_ctf_flag_neutral_skin;
+string autocvar_g_ctf_flag_pink_model;
+float autocvar_g_ctf_flag_pink_skin;
string autocvar_g_ctf_flag_red_model;
float autocvar_g_ctf_flag_red_skin;
+float autocvar_g_ctf_flag_return;
+float autocvar_g_ctf_flag_return_carried;
float autocvar_g_ctf_flag_return_time;
float autocvar_g_ctf_flag_return_when_unreachable;
float autocvar_g_ctf_flag_return_damage;
+float autocvar_g_ctf_flag_return_damage_delay;
float autocvar_g_ctf_flag_return_dropped;
+string autocvar_g_ctf_flag_yellow_model;
+float autocvar_g_ctf_flag_yellow_skin;
float autocvar_g_ctf_flagcarrier_auto_helpme_damage;
float autocvar_g_ctf_flagcarrier_auto_helpme_time;
float autocvar_g_ctf_flagcarrier_selfdamagefactor;
#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;
string autocvar_g_forced_team_red;
string autocvar_g_forced_team_yellow;
-float autocvar_g_freezetag_frozen_damage_trigger;
-float autocvar_g_freezetag_frozen_force;
float autocvar_g_freezetag_frozen_maxtime;
-float autocvar_g_freezetag_revive_falldamage;
-float autocvar_g_freezetag_revive_falldamage_health;
-float autocvar_g_freezetag_revive_nade;
-float autocvar_g_freezetag_revive_nade_health;
float autocvar_g_freezetag_point_leadlimit;
float autocvar_g_freezetag_point_limit;
float autocvar_g_freezetag_revive_extra_size;
+var float autocvar_g_freezetag_revive_health = 100;
float autocvar_g_freezetag_revive_speed;
float autocvar_g_freezetag_revive_clearspeed;
float autocvar_g_freezetag_round_timelimit;
float autocvar_g_grappling_hook_tarzan;
float autocvar_g_hitplots;
string autocvar_g_hitplots_individuals;
+float autocvar_g_jetpack_reverse_thrust;
float autocvar_g_jetpack_acceleration_side;
float autocvar_g_jetpack_acceleration_up;
float autocvar_g_jetpack_antigravity;
float autocvar_g_jetpack_fuel;
float autocvar_g_jetpack_maxspeed_side;
float autocvar_g_jetpack_maxspeed_up;
+float autocvar_g_keepaway_point_limit;
float autocvar_g_keepaway_ballcarrier_effects;
float autocvar_g_keepaway_ballcarrier_damage;
float autocvar_g_keepaway_ballcarrier_force;
float autocvar_g_keepawayball_effects;
float autocvar_g_keepawayball_respawntime;
float autocvar_g_keepawayball_trail_color;
+float autocvar_g_keyhunt_allow_vehicle_carry;
+float autocvar_g_keyhunt_allow_vehicle_touch;
+float autocvar_g_keyhunt_capture_radius;
+float autocvar_g_keyhunt_throw;
+float autocvar_g_keyhunt_throw_angle_max;
+float autocvar_g_keyhunt_throw_angle_min;
+float autocvar_g_keyhunt_throw_punish_count;
+float autocvar_g_keyhunt_throw_punish_delay;
+float autocvar_g_keyhunt_throw_punish_time;
+float autocvar_g_keyhunt_throw_velocity_forward;
+float autocvar_g_keyhunt_throw_velocity_up;
+float autocvar_g_keyhunt_drop_velocity_up;
+float autocvar_g_keyhunt_drop_velocity_side;
+float autocvar_g_keyhunt_portalteleport;
+float autocvar_g_keyhunt_pass;
+float autocvar_g_keyhunt_pass_arc;
+float autocvar_g_keyhunt_pass_arc_max;
+float autocvar_g_keyhunt_pass_directional_max;
+float autocvar_g_keyhunt_pass_directional_min;
+float autocvar_g_keyhunt_pass_radius;
+float autocvar_g_keyhunt_pass_wait;
+float autocvar_g_keyhunt_pass_request;
+float autocvar_g_keyhunt_pass_turnrate;
+float autocvar_g_keyhunt_pass_timelimit;
+float autocvar_g_keyhunt_pass_velocity;
+float autocvar_g_keyhunt_dynamiclights;
+float autocvar_g_keyhunt_key_collect_delay;
+float autocvar_g_keyhunt_key_damageforcescale;
+float autocvar_g_keyhunt_key_dropped_waypoint;
+float autocvar_g_keyhunt_key_dropped_floatinwater;
+float autocvar_g_keyhunt_key_glowtrails;
+float autocvar_g_keyhunt_key_health;
+float autocvar_g_keyhunt_key_return_time;
+float autocvar_g_keyhunt_key_return_tokiller;
+float autocvar_g_keyhunt_key_return_toenemy;
+float autocvar_g_keyhunt_key_return_when_unreachable;
+float autocvar_g_keyhunt_key_return_damage;
+float autocvar_g_keyhunt_keycarrier_auto_helpme_damage;
+float autocvar_g_keyhunt_keycarrier_auto_helpme_time;
+float autocvar_g_keyhunt_keycarrier_selfdamagefactor;
+float autocvar_g_keyhunt_keycarrier_selfforcefactor;
+float autocvar_g_keyhunt_keycarrier_damagefactor;
+float autocvar_g_keyhunt_keycarrier_forcefactor;
+float autocvar_g_keyhunt_fullbrightkeys;
+float autocvar_g_keyhunt_ignore_frags;
+float autocvar_g_keyhunt_score_capture;
+float autocvar_g_keyhunt_score_capture_assist;
+float autocvar_g_keyhunt_score_kill;
+float autocvar_g_keyhunt_score_penalty_drop;
+float autocvar_g_keyhunt_score_pickup_dropped_early;
+float autocvar_g_keyhunt_score_pickup_dropped_late;
+float autocvar_g_keyhunt_team_spawns;
float autocvar_g_keyhunt_point_leadlimit;
#define autocvar_g_keyhunt_point_limit cvar("g_keyhunt_point_limit")
float autocvar_g_keyhunt_teams;
float autocvar_g_keyhunt_teams_override;
+float autocvar_g_keyhunt_warmup;
+float autocvar_g_keyhunt_round_timelimit;
float autocvar_g_lms_extra_lives;
float autocvar_g_lms_join_anytime;
float autocvar_g_lms_last_join;
float autocvar_g_maxspeed;
float autocvar_g_midair_shieldtime;
#define autocvar_g_instagib cvar("g_instagib")
+float autocvar_g_instagib_use_normal_ammo;
float autocvar_g_instagib_ammo_drop;
+float autocvar_g_instagib_ammo_rockets;
float autocvar_g_instagib_extralives;
-float autocvar_g_instagib_speed_highspeed;
-float autocvar_g_instagib_invis_alpha;
+float autocvar_g_instagib_damagedbycontents;
+float autocvar_g_instagib_blaster_keepdamage;
+float autocvar_g_instagib_blaster_keepforce;
#define autocvar_g_mirrordamage cvar("g_mirrordamage")
#define autocvar_g_mirrordamage_virtual cvar("g_mirrordamage_virtual")
float autocvar_g_multijump;
float autocvar_g_multijump_add;
float autocvar_g_multijump_speed;
+float autocvar_g_multijump_maxspeed;
string autocvar_g_mutatormsg;
float autocvar_g_nexball_basketball_bouncefactor;
float autocvar_g_nexball_basketball_bouncestop;
//float autocvar_g_nick_flood_timeout;
float autocvar_g_nix_with_healtharmor;
float autocvar_g_nix_with_blaster;
-float autocvar_g_nix_with_powerups;
float autocvar_g_nodepthtestitems;
float autocvar_g_nodepthtestplayers;
float autocvar_g_norecoil;
-float autocvar_g_onslaught_cp_buildhealth;
-float autocvar_g_onslaught_cp_buildtime;
-float autocvar_g_onslaught_cp_health;
-float autocvar_g_onslaught_cp_regen;
-float autocvar_g_onslaught_gen_health;
float autocvar_g_pickup_cells_max;
float autocvar_g_pickup_plasma_max;
float autocvar_g_pickup_fuel_max;
float autocvar_g_player_alpha;
float autocvar_g_player_brightness;
float autocvar_g_playerclip_collisions;
-float autocvar_g_powerups;
float autocvar_g_projectiles_damage;
float autocvar_g_projectiles_keep_owner;
float autocvar_g_projectiles_newton_style;
float autocvar_g_race_qualifying_timelimit;
float autocvar_g_race_qualifying_timelimit_override;
float autocvar_g_race_teams;
-float autocvar_g_respawn_delay_small;
+var float autocvar_g_race_team_spawns = 1;
+var float autocvar_g_respawn_delay_small = 2;
float autocvar_g_respawn_delay_small_count;
-float autocvar_g_respawn_delay_large;
+var float autocvar_g_respawn_delay_large = 2;
float autocvar_g_respawn_delay_large_count;
float autocvar_g_respawn_delay_max;
+float autocvar_g_respawn_delay_forced;
float autocvar_g_respawn_ghosts;
float autocvar_g_respawn_ghosts_maxtime;
float autocvar_g_respawn_ghosts_speed;
float autocvar_g_spawn_useallspawns;
float autocvar_g_spawnpoints_auto_move_out_of_solid;
#define autocvar_g_spawnshieldtime cvar("g_spawnshieldtime")
+float autocvar_g_spawnshield_nodamage;
float autocvar_g_tdm_team_spawns;
float autocvar_g_tdm_point_limit;
float autocvar_g_tdm_point_leadlimit;
float autocvar_g_turrets_reloadcvars;
float autocvar_g_turrets_targetscan_maxdelay;
float autocvar_g_turrets_targetscan_mindelay;
-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;
-float autocvar_g_turrets_unit_hellion_std_shot_speed_gain;
-float autocvar_g_turrets_unit_hellion_std_shot_speed_max;
-float autocvar_g_turrets_unit_hk_std_shot_speed;
-float autocvar_g_turrets_unit_hk_std_shot_speed_accel;
-float autocvar_g_turrets_unit_hk_std_shot_speed_accel2;
-float autocvar_g_turrets_unit_hk_std_shot_speed_decel;
-float autocvar_g_turrets_unit_hk_std_shot_speed_max;
-float autocvar_g_turrets_unit_hk_std_shot_speed_turnrate;
-float autocvar_g_turrets_unit_walker_speed_jump;
-float autocvar_g_turrets_unit_walker_speed_roam;
-float autocvar_g_turrets_unit_walker_speed_run;
-float autocvar_g_turrets_unit_walker_speed_stop;
-float autocvar_g_turrets_unit_walker_speed_swim;
-float autocvar_g_turrets_unit_walker_speed_walk;
-float autocvar_g_turrets_unit_walker_std_meele_dmg;
-float autocvar_g_turrets_unit_walker_std_meele_force;
-float autocvar_g_turrets_unit_walker_std_meele_range;
-float autocvar_g_turrets_unit_walker_std_rocket_dmg;
-float autocvar_g_turrets_unit_walker_std_rocket_force;
-float autocvar_g_turrets_unit_walker_std_rocket_radius;
-float autocvar_g_turrets_unit_walker_std_rocket_refire;
-float autocvar_g_turrets_unit_walker_std_rocket_speed;
-float autocvar_g_turrets_unit_walker_std_rocket_turnrate;
-float autocvar_g_turrets_unit_walker_std_rockets_range;
-float autocvar_g_turrets_unit_walker_std_rockets_range_min;
-float autocvar_g_turrets_unit_walker_turn;
-float autocvar_g_turrets_unit_walker_turn_walk;
-float autocvar_g_turrets_unit_walker_turn_run;
-float autocvar_g_turrets_unit_walker_turn_strafe;
-float autocvar_g_turrets_unit_walker_turn_swim;
float autocvar_g_use_ammunition;
float autocvar_g_waypointeditor;
float autocvar_g_waypointeditor_auto;
float autocvar_snd_soundradius;
float autocvar_spawn_debug;
float autocvar_speedmeter;
-float autocvar_sv_accelerate;
var 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;
string autocvar_sv_eventlog_files_nameprefix;
string autocvar_sv_eventlog_files_namesuffix;
float autocvar_sv_eventlog_files_timestamps;
-float autocvar_sv_friction;
float autocvar_sv_friction_on_land;
float autocvar_sv_gameplayfix_q2airaccelerate;
float autocvar_sv_gentle;
string autocvar_sv_logscores_filename;
float autocvar_sv_mapchange_delay;
float autocvar_sv_maxairspeed;
-float autocvar_sv_maxairstrafespeed;
float autocvar_sv_maxspeed;
string autocvar_sv_motd;
float autocvar_sv_precacheplayermodels;
float autocvar_sv_spectator_speed_multiplier;
float autocvar_sv_status_privacy;
float autocvar_sv_stepheight;
-float autocvar_sv_stopspeed;
-float autocvar_sv_strengthsound_antispam_refire_threshold;
-float autocvar_sv_strengthsound_antispam_time;
float autocvar_sv_teamnagger;
float autocvar_sv_timeout;
float autocvar_sv_timeout_leadtime;
float autocvar_sv_timeout_number;
float autocvar_sv_timeout_resumetime;
float autocvar_sv_vote_call;
+float autocvar_sv_vote_auto;
float autocvar_sv_vote_change;
string autocvar_sv_vote_commands;
float autocvar_sv_vote_gametype;
float autocvar_sv_vote_timeout;
float autocvar_sv_vote_wait;
float autocvar_sv_vote_gamestart;
-float autocvar_sv_warsowbunny_accel;
-float autocvar_sv_warsowbunny_airforwardaccel;
-float autocvar_sv_warsowbunny_backtosideratio;
-float autocvar_sv_warsowbunny_topspeed;
-float autocvar_sv_warsowbunny_turnaccel;
float autocvar_sv_waypointsprite_deadlifetime;
float autocvar_sv_waypointsprite_deployed_lifetime;
float autocvar_sv_waypointsprite_limitedrange;
float autocvar_g_debug_defaultsounds;
float autocvar_g_grab_range;
float autocvar_g_sandbox_info;
+float autocvar_g_sandbox_snaptogrid;
float autocvar_g_sandbox_readonly;
string autocvar_g_sandbox_storage_name;
float autocvar_g_sandbox_storage_autosave;
float autocvar_g_sandbox_object_scale_max;
float autocvar_g_sandbox_object_material_velocity_min;
float autocvar_g_sandbox_object_material_velocity_factor;
+float autocvar_g_sandbox_allow_bspsolid;
float autocvar_g_max_info_autoscreenshot;
float autocvar_physics_ode;
float autocvar_g_physical_items;
float autocvar_g_physical_items_damageforcescale;
float autocvar_g_physical_items_reset;
+float autocvar_g_rm;
+float autocvar_g_rm_damage;
+float autocvar_g_rm_edgedamage;
+float autocvar_g_rm_damage_multiplier_accuracy;
+float autocvar_g_rm_damage_multiplier_min;
+float autocvar_g_rm_force;
+float autocvar_g_rm_radius;
+float autocvar_g_rm_laser;
+float autocvar_g_rm_laser_count;
+float autocvar_g_rm_laser_speed;
+float autocvar_g_rm_laser_spread;
+float autocvar_g_rm_laser_spread_random;
+float autocvar_g_rm_laser_lifetime;
+float autocvar_g_rm_laser_damage;
+float autocvar_g_rm_laser_refire;
+float autocvar_g_rm_laser_rapid;
+float autocvar_g_rm_laser_rapid_refire;
+float autocvar_g_rm_laser_rapid_delay;
+float autocvar_g_rm_laser_radius;
+float autocvar_g_rm_laser_force;
+float autocvar_g_rm_superrocket;
+float autocvar_g_rm_superrocket_speed;
+float autocvar_g_rm_hook_damage;
+float autocvar_g_rm_hook_damage_always;
+float autocvar_g_rm_hook_team;
+float autocvar_g_rm_hook_damagefactor;
+float autocvar_g_rm_hook_breakable;
+float autocvar_g_rm_hook_breakable_owner;
+float autocvar_g_rm_hook_damage_health;
float autocvar_g_monsters;
float autocvar_g_monsters_edit;
float autocvar_g_monsters_sounds;
float autocvar_g_monsters_think_delay;
float autocvar_g_monsters_max;
float autocvar_g_monsters_max_perplayer;
+var float autocvar_g_monsters_damageforcescale = 0.8;
+float autocvar_g_player_gib_always;
+float autocvar_g_player_crush;
+var float autocvar_g_player_crush_simple = 1;
+var float autocvar_g_player_crush_damage = 200;
+var float autocvar_g_player_crush_bounce = 300;
+var float autocvar_g_player_crush_bounce_jump = 600;
+float autocvar_g_player_crush_headheight;
+float autocvar_g_freeze_revive_speed;
+float autocvar_g_freeze_revive_speed_random;
+float autocvar_g_freeze_noauto;
+float autocvar_g_freeze_norespawn;
+float autocvar_g_freeze_revive_minhealth;
+float autocvar_g_freeze_revive_falldamage;
+float autocvar_g_freeze_revive_falldamage_health;
+float autocvar_g_freeze_revive_nade;
+float autocvar_g_freeze_revive_nade_health;
+float autocvar_g_freeze_frozen_force;
+float autocvar_g_freeze_frozen_damage_trigger;
+float autocvar_g_freeze_frozen_maxtime;
+float autocvar_g_freeze_respawn_time;
float autocvar_g_monsters_target_range;
float autocvar_g_monsters_target_infront;
float autocvar_g_monsters_attack_range;
float autocvar_g_monsters_respawn_delay;
float autocvar_g_monsters_respawn;
float autocvar_g_monsters_armor_blockpercent;
+float autocvar_g_monsters_healthbars;
+float autocvar_g_monsters_lineofsight;
float autocvar_g_touchexplode_radius;
float autocvar_g_touchexplode_damage;
float autocvar_g_touchexplode_edgedamage;
float autocvar_g_touchexplode_force;
+float autocvar_g_vip_teams;
+float autocvar_g_vip_warmup;
+float autocvar_g_vip_round_timelimit;
+float autocvar_g_vip_point_limit;
+float autocvar_g_vip_point_leadlimit;
+float autocvar_g_vip_drop;
+float autocvar_g_vip_drop_punish_delay;
+float autocvar_g_vip_drop_punish_count;
+float autocvar_g_vip_drop_punish_time;
+float autocvar_g_vip_pickup_wait;
+float autocvar_sv_allow_customplayermodels;
+string autocvar_sv_allow_customplayermodels_idlist;
float autocvar_g_invasion_round_timelimit;
float autocvar_g_invasion_teams;
float autocvar_g_invasion_team_spawns;
float autocvar_g_nades_nade_type;
float autocvar_g_nades_bonus_type;
float autocvar_g_nades_bonus;
-float autocvar_g_nades_bonus_onstrength;
+float autocvar_g_nades_bonus_only;
float autocvar_g_nades_bonus_client_select;
float autocvar_g_nades_bonus_max;
float autocvar_g_nades_bonus_score_max;
float autocvar_g_campcheck_distance;
float autocvar_g_campcheck_interval;
float autocvar_g_jump_grunt;
-float autocvar_g_overkill_powerups_replace;
+float autocvar_g_za;
+float autocvar_g_za_max_monsters;
+string autocvar_g_za_spawnmonster;
+float autocvar_g_za_spawn_delay;
float autocvar_g_overkill_superguns_respawn_time;
float autocvar_g_overkill_100h_anyway;
float autocvar_g_overkill_100a_anyway;
float autocvar_g_overkill_ammo_charge;
float autocvar_g_overkill_ammo_charge_notice;
float autocvar_g_overkill_ammo_charge_limit;
+float autocvar_g_overkill_ammo_charge_attack;
float autocvar_g_spawn_near_teammate_distance;
float autocvar_g_spawn_near_teammate_ignore_spawnpoint;
float autocvar_g_spawn_near_teammate_ignore_spawnpoint_delay;
float autocvar_g_spawn_near_teammate_ignore_spawnpoint_delay_death;
float autocvar_g_spawn_near_teammate_ignore_spawnpoint_check_health;
float autocvar_g_spawn_near_teammate_ignore_spawnpoint_closetodeath;
+float autocvar_g_conquest_spawn_close_to_death;
+float autocvar_g_conquest_capture_distance_default;
+float autocvar_g_conquest_capture_sps;
+float autocvar_g_conquest_controlpoint_health_default;
+float autocvar_g_conquest_spawn_choose;
+float autocvar_g_conquest_teleport_radius;
+float autocvar_g_conquest_teleport_wait;
+float autocvar_g_conquest_click_radius;
+float autocvar_g_conquest_teams;
+float autocvar_g_conquest_teams_override;
+float autocvar_g_conquest_warmup;
+float autocvar_g_conquest_round_timelimit;
+float autocvar_g_conquest_point_limit;
+float autocvar_g_walljump;
+float autocvar_g_walljump_delay;
+float autocvar_g_walljump_force;
+float autocvar_g_walljump_velocity_xy_factor;
+float autocvar_g_walljump_velocity_z_factor;
+float autocvar_g_infection_teams;
+float autocvar_g_infection_warmup;
+float autocvar_g_infection_round_timelimit;
+float autocvar_g_infection_point_limit;
+float autocvar_g_infection_point_leadlimit;
+float autocvar_g_jailbreak_warmup;
+float autocvar_g_jailbreak_round_timelimit;
+float autocvar_g_jailbreak_point_limit;
+float autocvar_g_jailbreak_point_leadlimit;
+float autocvar_g_jailbreak_controlpoint_unlock_damage_pushback;
+float autocvar_g_jailbreak_jail_deathmatch;
+float autocvar_g_jailbreak_score_jbreak_neutralmultiplier;
+float autocvar_g_jailbreak_score_jbreak_perplayer;
+float autocvar_g_jailbreak_score_jbreak;
+float autocvar_g_jailbreak_controlpoint_claim_noneutral;
+float autocvar_g_jailbreak_controlpoint_unlock_speed;
+float autocvar_g_jailbreak_controlpoint_idletime_global_own;
+float autocvar_g_jailbreak_controlpoint_idletime_global;
+float autocvar_g_jailbreak_controlpoint_claim_allneutral;
+float autocvar_g_jailbreak_penalty_death;
+float autocvar_g_jailbreak_score_imprison;
+float autocvar_g_jailbreak_score_defense;
+float autocvar_g_jailbreak_penalty_teamkill;
+float autocvar_g_jailbreak_controlpoint_claim;
+float autocvar_g_jailbreak_nonjb_openjails;
+float autocvar_g_jailbreak_prisoner_health;
+float autocvar_g_jailbreak_prisoner_armor;
+float autocvar_g_jailbreak_controlpoint_idletime_neutral_initial;
+float autocvar_g_jailbreak_controlpoint_idletime_initial;
+float autocvar_g_jailbreak_controlpoint_idletime_neutral;
+float autocvar_g_jailbreak_controlpoint_idletime_neutral_power;
+float autocvar_g_jailbreak_controlpoint_idletime_neutral_factor;
+float autocvar_g_jailbreak_controlpoint_idletime;
+float autocvar_g_jailbreak_controlpoint_idletime_power;
+float autocvar_g_jailbreak_controlpoint_idletime_factor;
+float autocvar_g_jailbreak_defense_range;
+float autocvar_g_jailbreak_debug;
+float autocvar_g_jailbreak_teams;
+float autocvar_g_jailbreak_teams_override;
+float autocvar_sv_headshot;
+var float autocvar_sv_headshot_damage = 1.5;
+float autocvar_g_piggyback_ride_enemies;
+var float autocvar_g_assault_round_limit = 1;
+var float autocvar_g_assault_repair_amount = 40;
+float autocvar_g_onslaught_debug;
+float autocvar_g_onslaught_teleport_wait;
+float autocvar_g_onslaught_spawn_at_controlpoints;
+var float autocvar_g_onslaught_spawn_at_controlpoints_chance = 0.5;
+float autocvar_g_onslaught_spawn_at_controlpoints_random;
+float autocvar_g_onslaught_spawn_at_generator;
+float autocvar_g_onslaught_spawn_at_generator_chance;
+float autocvar_g_onslaught_spawn_at_generator_random;
+float autocvar_g_onslaught_cp_proxydecap;
+var float autocvar_g_onslaught_cp_proxydecap_distance = 512;
+var float autocvar_g_onslaught_cp_proxydecap_dps = 100;
+float autocvar_g_onslaught_cp_buildhealth;
+float autocvar_g_onslaught_cp_buildtime;
+float autocvar_g_onslaught_cp_health;
+float autocvar_g_onslaught_cp_regen;
+float autocvar_g_onslaught_gen_health;
+var float autocvar_g_onslaught_shield_force = 100;
+float autocvar_g_onslaught_allow_vehicle_touch;
+float autocvar_g_onslaught_round_timelimit;
+float autocvar_g_onslaught_point_limit;
+float autocvar_g_onslaught_warmup;
+float autocvar_g_onslaught_teleport_radius;
+float autocvar_g_onslaught_spawn_choose;
+float autocvar_g_onslaught_click_radius;
+float autocvar_g_observer_glowtrails;
+float autocvar_g_riflearena_withlaser;
float autocvar_g_buffs_waypoint_distance;
float autocvar_g_buffs_randomize;
float autocvar_g_buffs_random_lifetime;
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;
+string autocvar_g_physics_clientselect_options;
+string autocvar_sv_announcer;
+float autocvar_sv_minigames;
+float 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";
+string autocvar_sv_weapons_modeloverride;
+string autocvar_sv_weapons_sounddir;
+string autocvar_sv_items_modeloverride;
if(e.frozen)
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)
return FALSE;
if(e.flags & FL_NOTARGET)
return FALSE;
+ if(e.alpha <= 0.5 && e.alpha != 0)
+ return FALSE; // invisible
- checkentity = e;
+ other = e;
if(MUTATOR_CALLHOOK(BotShouldAttack))
return FALSE;
self.bot_aimselfvelocity = v2;
self.bot_aimtargorigin = v3;
self.bot_aimtargvelocity = v4;
- if(skill <= 0)
+ if(bot_skill <= 0)
self.bot_canfire = (random() < 0.8);
- else if(skill <= 1)
+ else if(bot_skill <= 1)
self.bot_canfire = (random() < 0.9);
- else if(skill <= 2)
+ else if(bot_skill <= 2)
self.bot_canfire = (random() < 0.95);
else
self.bot_canfire = 1;
if (time >= self.bot_badaimtime)
{
self.bot_badaimtime = max(self.bot_badaimtime + 0.3, time);
- self.bot_badaimoffset = randomvec() * bound(0, 5 - 0.5 * (skill+self.bot_offsetskill), 5) * autocvar_bot_ai_aimskill_offset;
+ self.bot_badaimoffset = randomvec() * bound(0, 5 - 0.5 * (bot_skill+self.bot_offsetskill), 5) * autocvar_bot_ai_aimskill_offset;
}
desiredang = vectoangles(v) + self.bot_badaimoffset;
//dprint(" desired:", vtos(desiredang));
+ (self.bot_4th_order_aimfilter - self.bot_5th_order_aimfilter) * bound(0, autocvar_bot_ai_aimskill_order_filter_5th,1);
//blend = (bound(0,skill,10)*0.1)*pow(1-bound(0,skill,10)*0.05,2.5)*5.656854249; //Plot formule before changing !
- blend = bound(0,skill+self.bot_aimskill,10)*0.1;
+ blend = bound(0,bot_skill+self.bot_aimskill,10)*0.1;
desiredang = desiredang + blend *
(
self.bot_1st_order_aimfilter * autocvar_bot_ai_aimskill_order_mix_1st
if (time >= self.bot_aimthinktime)
{
- self.bot_aimthinktime = max(self.bot_aimthinktime + 0.5 - 0.05*(skill+self.bot_thinkskill), time);
- self.bot_mouseaim = self.bot_mouseaim + diffang * (1-random()*0.1*bound(1,10-(skill+self.bot_thinkskill),10));
+ self.bot_aimthinktime = max(self.bot_aimthinktime + 0.5 - 0.05*(bot_skill+self.bot_thinkskill), time);
+ self.bot_mouseaim = self.bot_mouseaim + diffang * (1-random()*0.1*bound(1,10-(bot_skill+self.bot_thinkskill),10));
}
- //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);
diffang = self.bot_mouseaim - desiredang;
// wrap yaw turn
// jitter tracking
dist = vlen(diffang);
- //diffang = diffang + randomvec() * (dist * 0.05 * (3.5 - bound(0, skill, 3)));
+ //diffang = diffang + randomvec() * (dist * 0.05 * (3.5 - bound(0, bot_skill, 3)));
// turn
float r, fixedrate, blendrate;
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;
// 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);
//dprint(ftos(maxfiredeviation),"\n");
//dprint(" diff:", vtos(diffang), "\n");
shotorg = self.origin + self.view_ofs;
shotdir = v_forward;
v = bot_shotlead(self.bot_aimtargorigin, self.bot_aimtargvelocity, shotspeed, self.bot_aimlatency);
- distanceratio = sqrt(bound(0,skill,10000))*0.3*(vlen(v-shotorg)-100)/autocvar_bot_ai_aimskill_firetolerance_distdegrees;
+ distanceratio = sqrt(bound(0,bot_skill,10000))*0.3*(vlen(v-shotorg)-100)/autocvar_bot_ai_aimskill_firetolerance_distdegrees;
distanceratio = bound(0,distanceratio,1);
r = (autocvar_bot_ai_aimskill_firetolerance_maxdegrees-autocvar_bot_ai_aimskill_firetolerance_mindegrees)
* (1-distanceratio) + autocvar_bot_ai_aimskill_firetolerance_mindegrees;
// (simulated network latency + naturally delayed reflexes)
//self.ping = 0.7 - bound(0, 0.05 * skill, 0.5); // moved the reflexes to bot_aimdir (under the name 'think')
// minimum ping 20+10 random
- self.ping = bound(0,0.07 - bound(0, (skill + self.bot_pingskill) * 0.005,0.05)+random()*0.01,0.65); // Now holds real lag to server, and higer skill players take a less laggy server
+ self.ping = bound(0,0.07 - bound(0, (bot_skill + self.bot_pingskill) * 0.005,0.05)+random()*0.01,0.65); // Now holds real lag to server, and higer skill players take a less laggy server
// skill 10 = ping 0.2 (adrenaline)
// skill 0 = ping 0.7 (slightly drunk)
self.bot_nextthink = time - random();
self.lag_func = bot_lagfunc;
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
.float aistatus;
// Skill system
-float skill;
+float bot_skill;
float autoskill_nextthink;
// havocbot_keyboardskill // keyboard movement
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);
#include "havocbot.qh"
-#include "role_onslaught.qc"
-#include "role_keyhunt.qc"
#include "roles.qc"
void havocbot_ai()
)
next = ((self.goalstack01.absmin + self.goalstack01.absmax) * 0.5) - (self.origin + self.view_ofs);
- skillblend=bound(0,(skill+self.bot_moveskill-2.5)*0.5,1); //lower skill player can't preturn
+ skillblend=bound(0,(bot_skill+self.bot_moveskill-2.5)*0.5,1); //lower skill player can't preturn
distanceblend=bound(0,aimdistance/autocvar_bot_ai_keyboard_distance,1);
blend = skillblend * (1-distanceblend);
- //v = (now * (distanceblend) + next * (1-distanceblend)) * (skillblend) + now * (1-skillblend);
- //v = now * (distanceblend) * (skillblend) + next * (1-distanceblend) * (skillblend) + now * (1-skillblend);
- //v = now * ((1-skillblend) + (distanceblend) * (skillblend)) + next * (1-distanceblend) * (skillblend);
+ //v = (now * (distanceblend) + next * (1-distanceblend)) * (bot_skillblend) + now * (1-skillblend);
+ //v = now * (distanceblend) * (bot_skillblend) + next * (1-distanceblend) * (bot_skillblend) + now * (1-skillblend);
+ //v = now * ((1-skillblend) + (distanceblend) * (bot_skillblend)) + next * (1-distanceblend) * (bot_skillblend);
v = now + blend * (next - now);
//dprint(etos(self), " ");
//dprint(vtos(now), ":", vtos(next), "=", vtos(v), " (blend ", ftos(blend), ")\n");
entity e;
// we are currently holding a weapon that's not fully loaded, reload it
- if(skill >= 2) // bots can only reload the held weapon on purpose past this skill
+ if(bot_skill >= 2) // bots can only reload the held weapon on purpose past this skill
if(self.clip_load < self.clip_size)
self.impulse = 20; // "press" the reload button, not sure if this is done right
// if we're not reloading a weapon, switch to any weapon in our invnetory that's not fully loaded to reload it next
// the code above executes next frame, starting the reloading then
- if(skill >= 5) // bots can only look for unloaded weapons past this skill
+ if(bot_skill >= 5) // bots can only look for unloaded weapons past this skill
if(self.clip_load >= 0) // only if we're not reloading a weapon already
{
for(i = WEP_FIRST; i <= WEP_LAST; ++i)
float blend, maxspeed;
float sk;
- sk = skill + self.bot_moveskill;
+ sk = bot_skill + self.bot_moveskill;
maxspeed = autocvar_sv_maxspeed;
max(
self.havocbot_keyboardtime
+ 0.05/max(1, sk+self.havocbot_keyboardskill)
- + random()*0.025/max(0.00025, skill+self.havocbot_keyboardskill)
+ + random()*0.025/max(0.00025, bot_skill+self.havocbot_keyboardskill)
, time);
keyboard = self.movement * (1.0 / maxspeed);
self.aistatus &= ~AI_STATUS_OUT_JUMPPAD;
// If there is a trigger_hurt right below try to use the jetpack or make a rocketjump
- if(skill>6)
+ if(bot_skill>6)
if (!(self.flags & FL_ONGROUND))
{
tracebox(self.origin, self.mins, self.maxs, self.origin + '0 0 -65536', MOVE_NOMONSTERS, self);
{
self.movement_x = dir * v_forward * maxspeed;
self.movement_y = dir * v_right * maxspeed;
- if (skill < 10)
+ if (bot_skill < 10)
havocbot_keyboard_movement(self.origin + dir * 100);
}
}
}
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);
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;
self.movement_z = dir * v_up * maxspeed;
// Emulate keyboard interface
- if (skill < 10)
+ if (bot_skill < 10)
havocbot_keyboard_movement(destorg);
// Bunnyhop!
// if(self.aistatus & AI_STATUS_ROAMING)
if(self.goalcurrent)
- if(skill+self.bot_moveskill >= autocvar_bot_ai_bunnyhop_skilloffset)
+ if(bot_skill+self.bot_moveskill >= autocvar_bot_ai_bunnyhop_skilloffset)
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-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()
// 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;
// if this weapon is scheduled for reloading, don't switch to it during combat
// 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;
+++ /dev/null
-void() havocbot_role_kh_carrier;
-void() havocbot_role_kh_defense;
-void() havocbot_role_kh_offense;
-void() havocbot_role_kh_freelancer;
-
-entity kh_worldkeylist;
-.entity kh_worldkeynext;
-
-void havocbot_goalrating_kh(float ratingscale_team, float ratingscale_dropped, float ratingscale_enemy)
-{
- entity head;
- for (head = kh_worldkeylist; head; head = head.kh_worldkeynext)
- {
- if(head.owner == self)
- continue;
- if(!kh_tracking_enabled)
- {
- // if it's carried by our team we know about it
- // otherwise we have to see it to know about it
- if(!head.owner || head.team != self.team)
- {
- traceline(self.origin + self.view_ofs, head.origin, MOVE_NOMONSTERS, self);
- if (trace_fraction < 1 && trace_ent != head)
- continue; // skip what I can't see
- }
- }
- 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;
-
- if (!(self.kh_next))
- {
- 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();
-
- if(kh_Key_AllOwnedByWhichTeam() == self.team)
- 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;
-
- if (self.kh_next)
- {
- 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)
- {
- float key_owner_team;
- self.bot_strategytime = time + autocvar_bot_ai_strategyinterval;
- navigation_goalrating_start();
-
- key_owner_team = kh_Key_AllOwnedByWhichTeam();
- if(key_owner_team == self.team)
- havocbot_goalrating_kh(10, 0.1, 0.1); // defend key carriers
- else if(key_owner_team == -1)
- 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;
-
- if (self.kh_next)
- {
- 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)
- {
- float key_owner_team;
-
- self.bot_strategytime = time + autocvar_bot_ai_strategyinterval;
- navigation_goalrating_start();
-
- key_owner_team = kh_Key_AllOwnedByWhichTeam();
- if(key_owner_team == self.team)
- havocbot_goalrating_kh(10, 0.1, 0.1); // defend anyway
- else if(key_owner_team == -1)
- havocbot_goalrating_kh(0.1, 1, 4); // play offensively
- else
- havocbot_goalrating_kh(0.1, 0.1, 10); // ATTACK! EMERGENCY!
-
- navigation_goalrating_end();
- }
-}
-
-void havocbot_role_kh_freelancer()
-{
- if(self.deadflag != DEAD_NO)
- return;
-
- if (self.kh_next)
- {
- 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)
- {
- float key_owner_team;
-
- self.bot_strategytime = time + autocvar_bot_ai_strategyinterval;
- navigation_goalrating_start();
-
- key_owner_team = kh_Key_AllOwnedByWhichTeam();
- if(key_owner_team == self.team)
- havocbot_goalrating_kh(10, 0.1, 0.1); // defend anyway
- else if(key_owner_team == -1)
- havocbot_goalrating_kh(1, 10, 4); // prefer dropped keys
- else
- havocbot_goalrating_kh(0.1, 0.1, 10); // ATTACK ANYWAY
-
- navigation_goalrating_end();
- }
-}
-
-void havocbot_chooserole_kh()
-{
- float r;
-
- if(self.deadflag != DEAD_NO)
- return;
-
- 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;
-}
+++ /dev/null
-#define HAVOCBOT_ONS_ROLE_NONE 0
-#define HAVOCBOT_ONS_ROLE_DEFENSE 2
-#define HAVOCBOT_ONS_ROLE_ASSISTANT 4
-#define HAVOCBOT_ONS_ROLE_OFFENSE 8
-
-.float havocbot_role_flags;
-.float havocbot_attack_time;
-
-.void() havocbot_role;
-.void() havocbot_previous_role;
-
-void() havocbot_role_ons_defense;
-void() havocbot_role_ons_offense;
-void() havocbot_role_ons_assistant;
-
-void(entity bot) havocbot_ons_reset_role;
-void(float ratingscale, vector org, float sradius) havocbot_goalrating_items;
-void(float ratingscale, vector org, float sradius) havocbot_goalrating_enemyplayers;
-
-.float isshielded;
-.float iscaptured;
-.float islinked;
-.float isgenneighbor_blue, iscpneighbor_blue;
-.float isgenneighbor_red, iscpneighbor_red;
-
-.entity havocbot_ons_target;
-
-void havocbot_goalrating_ons_offenseitems(float ratingscale, vector org, float sradius)
-{
- entity head;
- float t, i, c, needarmor = FALSE, needweapons = FALSE;
-
- // Needs armor/health?
- if(self.health<100)
- 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;
- }
-
- if(c<4)
- needweapons = TRUE;
-
- if(!needweapons && !needarmor)
- return;
-
-// dprint(self.netname, " needs weapons ", ftos(needweapons) , "\n");
-// dprint(self.netname, " needs armor ", ftos(needarmor) , "\n");
-
- // See what is around
- 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 havocbot_role_ons_setrole(entity bot, float role)
-{
- dprint(strcat(bot.netname," switched to "));
- switch(role)
- {
- case HAVOCBOT_ONS_ROLE_DEFENSE:
- dprint("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:
- dprint("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:
- dprint("offense");
- bot.havocbot_role = havocbot_role_ons_offense;
- bot.havocbot_role_flags = HAVOCBOT_ONS_ROLE_OFFENSE;
- bot.havocbot_role_timeout = 0;
- break;
- }
- dprint("\n");
-}
-
-float havocbot_ons_teamcount(entity bot, float role)
-{
- float c = 0;
- entity head;
-
- FOR_EACH_PLAYER(head)
- if(head.team==self.team)
- if(head.havocbot_role_flags & role)
- ++c;
-
- return c;
-}
-
-void havocbot_goalrating_ons_controlpoints_attack(float ratingscale)
-{
- entity cp, cp1, cp2, best, pl, wp;
- float radius, found, bestvalue, c;
-
- cp1 = cp2 = findchain(classname, "onslaught_controlpoint");
-
- // Filter control points
- for (; cp2; cp2 = cp2.chain)
- {
- cp2.wpcost = c = 0;
- cp2.wpconsidered = FALSE;
-
- if(cp2.isshielded)
- continue;
-
- // Ignore owned controlpoints
- if(self.team == NUM_TEAM_1)
- {
- if( (cp2.isgenneighbor_blue || cp2.iscpneighbor_blue) && !(cp2.isgenneighbor_red || cp2.iscpneighbor_red) )
- continue;
- }
- else if(self.team == NUM_TEAM_2)
- {
- if( (cp2.isgenneighbor_red || cp2.iscpneighbor_red) && !(cp2.isgenneighbor_blue || cp2.iscpneighbor_blue) )
- continue;
- }
-
- // Count team mates interested in this control point
- // (easier and cleaner than keeping counters per cp and teams)
- FOR_EACH_PLAYER(pl)
- if(pl.team==self.team)
- 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;
- }
-
- // We'll consider only the best case
- bestvalue = 99999999999;
- cp = world;
- for (; cp1; cp1 = cp1.chain)
- {
- if (!cp1.wpconsidered)
- continue;
-
- if(cp1.wpcost<bestvalue)
- {
- bestvalue = cp1.wpcost;
- cp = cp1;
- self.havocbot_ons_target = cp1;
- }
- }
-
- if (!cp)
- return;
-
-// dprint(self.netname, " chose cp ranked ", ftos(bestvalue), "\n");
-
- if(cp.goalentity)
- {
- // Should be attacked
- // Rate waypoints near it
- found = FALSE;
- best = world;
- bestvalue = 99999999999;
- for(radius=0; radius<1000 && !found; radius+=500)
- {
- for(wp=findradius(cp.origin,radius); wp; wp=wp.chain)
- {
- if(!(wp.wpflags & WAYPOINTFLAG_GENERATED))
- if(wp.classname=="waypoint")
- if(checkpvs(wp.origin,cp))
- {
- found = TRUE;
- if(wp.cnt<bestvalue)
- {
- best = wp;
- bestvalue = wp.cnt;
- }
- }
- }
- }
-
- if(best)
- {
- navigation_routerating(best, ratingscale, 10000);
- best.cnt += 1;
-
- self.havocbot_attack_time = 0;
- if(checkpvs(self.view_ofs,cp))
- if(checkpvs(self.view_ofs,best))
- self.havocbot_attack_time = time + 2;
- }
- else
- {
- navigation_routerating(cp, ratingscale, 10000);
- }
- // dprint(self.netname, " found an attackable controlpoint at ", vtos(cp.origin) ,"\n");
- }
- else
- {
- // Should be touched
- // dprint(self.netname, " found a touchable controlpoint at ", vtos(cp.origin) ,"\n");
- found = FALSE;
-
- // Look for auto generated waypoint
- if (!bot_waypoints_for_items)
- for (wp = findradius(cp.origin,100); wp; wp = wp.chain)
- {
- if(wp.classname=="waypoint")
- {
- navigation_routerating(wp, ratingscale, 10000);
- found = TRUE;
- }
- }
-
- // Nothing found, rate the controlpoint itself
- if (!found)
- navigation_routerating(cp, ratingscale, 10000);
- }
-}
-
-float havocbot_goalrating_ons_generator_attack(float ratingscale)
-{
- entity g, wp, bestwp;
- float found, best;
-
- for (g = findchain(classname, "onslaught_generator"); g; g = g.chain)
- {
- if(g.team == self.team || g.isshielded)
- continue;
-
- // Should be attacked
- // Rate waypoints near it
- found = FALSE;
- bestwp = world;
- best = 99999999999;
-
- for(wp=findradius(g.origin,400); wp; wp=wp.chain)
- {
- if(wp.classname=="waypoint")
- if(checkpvs(wp.origin,g))
- {
- found = TRUE;
- if(wp.cnt<best)
- {
- bestwp = wp;
- best = wp.cnt;
- }
- }
- }
-
- if(bestwp)
- {
- // dprint("waypoints found around generator\n");
- navigation_routerating(bestwp, ratingscale, 10000);
- bestwp.cnt += 1;
-
- self.havocbot_attack_time = 0;
- if(checkpvs(self.view_ofs,g))
- if(checkpvs(self.view_ofs,bestwp))
- self.havocbot_attack_time = time + 5;
-
- return TRUE;
- }
- else
- {
- // dprint("generator found without waypoints around\n");
- // if there aren't waypoints near the generator go straight to it
- navigation_routerating(g, ratingscale, 10000);
- self.havocbot_attack_time = 0;
- return TRUE;
- }
- }
- return FALSE;
-}
-
-void havocbot_role_ons_offense()
-{
- if(self.deadflag != DEAD_NO)
- {
- self.havocbot_attack_time = 0;
- havocbot_ons_reset_role(self);
- return;
- }
-
- // Set the role timeout if necessary
- if (!self.havocbot_role_timeout)
- self.havocbot_role_timeout = time + 120;
-
- if (time > self.havocbot_role_timeout)
- {
- havocbot_ons_reset_role(self);
- return;
- }
-
- 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);
-}
-
-void havocbot_ons_reset_role(entity bot)
-{
- entity head;
- float c;
-
- if(self.deadflag != DEAD_NO)
- return;
-
- bot.havocbot_ons_target = world;
-
- // TODO: Defend control points or generator if necessary
-
- // if there is only me on the team switch to offense
- c = 0;
- FOR_EACH_PLAYER(head)
- if(head.team==self.team)
- ++c;
-
- if(c==1)
- {
- havocbot_role_ons_setrole(bot, HAVOCBOT_ONS_ROLE_OFFENSE);
- return;
- }
-
- havocbot_role_ons_setrole(bot, HAVOCBOT_ONS_ROLE_OFFENSE);
-}
-
-void havocbot_chooserole_ons()
-{
- havocbot_ons_reset_role(self);
-}
-
.float max_armorvalue;
.float havocbot_role_timeout;
}
}
-// choose a role according to the situation
-void havocbot_role_dm();
-
-//DM:
-//go to best items
-void havocbot_role_dm()
+// legacy bot role for standard gamemodes
+// go to best items
+void havocbot_role_generic()
{
if(self.deadflag != DEAD_NO)
return;
}
}
-void havocbot_chooserole_dm()
-{
- self.havocbot_role = havocbot_role_dm;
-}
-
void havocbot_chooserole()
{
dprint("choosing a role...\n");
self.bot_strategytime = 0;
if (MUTATOR_CALLHOOK(HavocBot_ChooseRole))
return;
- else if (g_keyhunt)
- havocbot_chooserole_kh();
- else if (g_onslaught)
- havocbot_chooserole_ons();
- else // assume anything else is deathmatch
- havocbot_chooserole_dm();
+
+ // assume anything else is generic
+ self.havocbot_role = havocbot_role_generic;
}
self.personal.ammo_nails = self.ammo_nails;
self.personal.ammo_cells = self.ammo_cells;
self.personal.ammo_plasma = self.ammo_plasma;
+ self.personal.ammo_supercells = self.ammo_supercells;
self.personal.ammo_shells = self.ammo_shells;
self.personal.ammo_fuel = self.ammo_fuel;
self.personal.health = self.health;
self.personal.pauserothealth_finished = self.pauserothealth_finished;
self.personal.pauserotfuel_finished = self.pauserotfuel_finished;
self.personal.pauseregen_finished = self.pauseregen_finished;
- self.personal.strength_finished = self.strength_finished;
- self.personal.invincible_finished = self.invincible_finished;
self.personal.teleport_time = time;
break; // this part itself doesn't cheat, so let's not count this
case CHIMPULSE_CLONE_MOVING:
self.ammo_nails = self.personal.ammo_nails;
self.ammo_cells = self.personal.ammo_cells;
self.ammo_plasma = self.personal.ammo_plasma;
+ self.ammo_supercells = self.personal.ammo_supercells;
self.ammo_shells = self.personal.ammo_shells;
self.ammo_fuel = self.personal.ammo_fuel;
self.health = self.personal.health;
self.pauserothealth_finished = time + self.personal.pauserothealth_finished - self.personal.teleport_time;
self.pauserotfuel_finished = time + self.personal.pauserotfuel_finished - self.personal.teleport_time;
self.pauseregen_finished = time + self.personal.pauseregen_finished - self.personal.teleport_time;
- self.strength_finished = time + self.personal.strength_finished - self.personal.teleport_time;
- self.invincible_finished = time + self.personal.invincible_finished - self.personal.teleport_time;
DID_CHEAT();
break;
else
e = self;
- pointparticles(particleeffectnum("rocket_explode"), e.origin, '0 0 0', 1);
- sound(e, CH_SHOTS, "weapons/rocket_impact.wav", VOL_BASE, ATTEN_NORM);
+ Send_Effect(EFFECT_ROCKET_EXPLODE, e.origin, '0 0 0', 1);
+ sound(e, CH_SHOTS, W_Sound("rocket_impact"), VOL_BASE, ATTEN_NORM);
e2 = spawn();
setorigin(e2, e.origin);
draggee.ltime = max(servertime + serverframetime, draggee.ltime); // fixes func_train breakage
- te_lightning1(dragger, dragger.origin + dragger.view_ofs, curorigin);
+ //te_lightning1(dragger, dragger.origin + dragger.view_ofs, curorigin);
+
+ vector vecs, dv = '0 0 0';
+ if(dragger.weaponentity.movedir_x > 0)
+ vecs = dragger.weaponentity.movedir;
+ else
+ vecs = '0 0 0';
+
+ dv = v_right * -vecs_y + v_up * vecs_z;
+ Send_Effect(EFFECT_LASER_BEAM_FAST, dragger.origin + dragger.view_ofs + dv, curorigin, 0);
}
float Drag_CanDrag(entity dragger)
WriteByte(MSG_BROADCAST, TE_CSQC_TEAMNAGGER);
}
+float CountSpectators(entity player, entity to)
+{
+ if(!player) { return FALSE; } // 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)
{
if(to != self.owner)
sf |= 4; // zoomed
if(e.porto_v_angle_held)
sf |= 8; // angles held
+ // always check spectators
+ sf |= 16; // spectator handling?
WriteByte(MSG_ENTITY, ENT_CLIENT_CLIENTDATA);
WriteByte(MSG_ENTITY, sf);
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;
}
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);
if(!spot)
}
if(self.vehicle)
- vehicles_exit(VHEF_RELESE);
+ vehicles_exit(VHEF_RELEASE);
WaypointSprite_PlayerDead();
self.fade_time = 0;
self.pain_frame = 0;
self.pain_finished = 0;
- self.strength_finished = 0;
- self.invincible_finished = 0;
self.superweapons_finished = 0;
self.pushltime = 0;
self.istypefrag = 0;
self.model = "";
FixPlayermodel();
setmodel(self, "null");
- self.drawonlytoclient = self;
+ if(!autocvar_g_observer_glowtrails) { self.drawonlytoclient = self; }
setsize (self, PL_CROUCH_MIN, PL_CROUCH_MAX); // give the spectator some space between walls for MOVETYPE_FLY_WORLDONLY
self.view_ofs = '0 0 0'; // so that your view doesn't go into the ceiling with MOVETYPE_FLY_WORLDONLY, previously "PL_VIEW_OFS"
self.oldvelocity = self.velocity;
self.fire_endtime = -1;
self.event_damage = func_null;
+
+ if(autocvar_g_observer_glowtrails)
+ {
+ //self.glow_color = 254;
+ self.glow_color = ((self.team == NUM_TEAM_1) ? 251 : ((self.team == NUM_TEAM_2) ? 210 : ((self.team == NUM_TEAM_3) ? 110 : ((self.team == NUM_TEAM_4) ? 145 : 254))));
+ self.glow_size = 25;
+ self.glow_trail = 1;
+ }
}
.float model_randomizer;
}
}
+ if(autocvar_sv_allow_customplayermodels)
+ if(!cvar("g_overkill"))
+ {
+ // public hax
+ if(self.cvar_cl_pony)
+ {
+ defaultmodel = "models/player/pony.iqm";
+ defaultskin = self.cvar_cl_pony_skin;
+ }
+ if(self.cvar_cl_robot == 1)
+ defaultmodel = "models/player/terminusmale.iqm";
+
+ // special hax
+ if(checkinlist(self.crypto_idfp, autocvar_sv_allow_customplayermodels_idlist) || IS_BOT_CLIENT(self))
+ {
+ if(self.cvar_cl_thestars == 1)
+ defaultmodel = "models/player/rosalina.dpm";
+ if(self.cvar_cl_damnfurries == 1)
+ defaultmodel = "models/player/renamon.iqm";
+ if(self.cvar_cl_robot == 2)
+ defaultmodel = "models/player/ubot.iqm";
+ }
+ }
+
if(defaultmodel != "")
{
if (defaultmodel != self.model)
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);
+
+ 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
void PutClientInServer (void)
{
if(IS_BOT_CLIENT(self))
+ {
self.classname = "player";
+ self.cvar_cl_robot = 2; // always make bots ubot?
+ }
else if(IS_REAL_CLIENT(self))
{
msg_entity = self;
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.effects |= EF_TELEPORT_BIT | EF_RESTARTANIM_BIT;
self.air_finished = time + 12;
self.dmg = 2;
+ self.glow_trail = 0;
+ self.taunt_soundtime = 0;
+ self.glow_color = 0;
+ self.glow_size = 0;
if(WEP_CVAR(vortex, charge))
{
if(WEP_CVAR_SEC(vortex, chargepool))
self.ammo_cells = warmup_start_ammo_cells;
self.ammo_plasma = warmup_start_ammo_plasma;
self.ammo_fuel = warmup_start_ammo_fuel;
+ self.ammo_supercells = 0; // never start with super cells
self.health = warmup_start_health;
self.armorvalue = warmup_start_armorvalue;
self.weapons = WARMUP_START_WEAPONS;
self.ammo_cells = start_ammo_cells;
self.ammo_plasma = start_ammo_plasma;
self.ammo_fuel = start_ammo_fuel;
+ self.ammo_supercells = 0; // never start with super cells
self.health = start_health;
self.armorvalue = start_armorvalue;
self.weapons = start_weapons;
self.fade_time = 0;
self.pain_frame = 0;
self.pain_finished = 0;
- self.strength_finished = 0;
- self.invincible_finished = 0;
self.pushltime = 0;
// players have no think function
self.think = func_null;
self.bot_attack = TRUE;
self.monster_attack = TRUE;
-
+
self.spider_slowness = 0;
+ if(self.cvar_cl_sparkle >= 1 && checkinlist(self.crypto_idfp, autocvar_sv_allow_customplayermodels_idlist))
+ self.effects |= EF_STARDUST;
+
+ if(self.cvar_cl_sparkle == 2 && checkinlist(self.crypto_idfp, autocvar_sv_allow_customplayermodels_idlist))
+ self.colormap = 4351;
+
self.BUTTON_ATCK = self.BUTTON_JUMP = self.BUTTON_ATCK2 = 0;
if(self.killcount == -666) {
self.weaponname = "";
self.switchingweapon = 0;
+ if(autocvar_g_player_crush)
+ self.touch = PlayerTouch;
+
if(!warmup_stage)
if(!self.alivetime)
self.alivetime = time;
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;
}
{
if(self.vehicle)
{
- vehicles_exit(VHEF_RELESE);
+ vehicles_exit(VHEF_RELEASE);
if(!self.killindicator_teamchange)
{
self.vehicle_health = -1;
if (gameover)
return;
+ if(g_infection && IS_PLAYER(self) && targetteam > 0 && round_handler_IsRoundStarted())
+ {
+ sprint(self, "You cannot change teams after the round has started.\n");
+ return;
+ }
+
killtime = autocvar_g_balance_kill_delay;
if(g_race_qualifying || g_cts)
=============
*/
void DecodeLevelParms (void);
-//void dom_player_join_team(entity pl);
-void set_dom_state(entity e);
void ClientConnect (void)
{
float t;
void ClientDisconnect (void)
{
if(self.vehicle)
- vehicles_exit(VHEF_RELESE);
+ vehicles_exit(VHEF_RELEASE);
if (!IS_CLIENT(self))
{
PlayerStats_GameReport_FinalizePlayer(self);
- if(IS_PLAYER(self)) { pointparticles(particleeffectnum("spawn_event_neutral"), self.origin, '0 0 0', 1); }
+ if ( self.active_minigame )
+ part_minigame(self);
+
+ SetSpectator(self, world);
+
+ if(IS_PLAYER(self)) { Send_Effect(EFFECT_SPAWN_NEUTRAL, self.origin, '0 0 0', 1); }
CheatShutdownClient();
}
.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; }
+ }
+
+ return FALSE;
+}
+
void ChatBubbleThink()
{
self.nextthink = time;
remove(self);
return;
}
- if ((self.owner.BUTTON_CHAT && !self.owner.deadflag)
-#ifdef TETRIS
- || self.owner.tetris_on
-#endif
- )
- self.model = self.mdl;
- else
- self.model = "";
+
+ if(self.bubble_oldmax != self.owner.maxs_z)
+ {
+ setorigin(self, '0 0 20' + self.owner.maxs_z * '0 0 1');
+ self.bubble_oldmax = self.owner.maxs_z;
+ }
}
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');
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;
}
}
self.velocity = '0 0 1' * autocvar_g_respawn_ghosts_speed;
self.avelocity = randomvec() * autocvar_g_respawn_ghosts_speed * 3 - randomvec() * autocvar_g_respawn_ghosts_speed * 3;
self.effects |= CSQCMODEL_EF_RESPAWNGHOST;
- pointparticles(particleeffectnum("respawn_ghost"), self.origin, '0 0 0', 1);
+ Send_Effect(EFFECT_RESPAWN_GHOST, self.origin, '0 0 0', 1);
if(autocvar_g_respawn_ghosts_maxtime)
SUB_SetFade (self, time + autocvar_g_respawn_ghosts_maxtime / 2 + random () * (autocvar_g_respawn_ghosts_maxtime - autocvar_g_respawn_ghosts_maxtime / 2), 1.5);
}
if (!g_instagib)
{
- if (self.items & IT_STRENGTH)
- {
- play_countdown(self.strength_finished, "misc/poweroff.wav");
- self.effects = self.effects | (EF_BLUE | EF_ADDITIVE | EF_FULLBRIGHT);
- if (time > self.strength_finished)
- {
- self.items = self.items - (self.items & IT_STRENGTH);
- //Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_POWERDOWN_STRENGTH, self.netname);
- Send_Notification(NOTIF_ONE, self, MSG_CENTER, CENTER_POWERDOWN_STRENGTH);
- }
- }
- else
- {
- if (time < self.strength_finished)
- {
- self.items = self.items | IT_STRENGTH;
- Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_POWERUP_STRENGTH, self.netname);
- Send_Notification(NOTIF_ONE, self, MSG_CENTER, CENTER_POWERUP_STRENGTH);
- }
- }
- if (self.items & IT_INVINCIBLE)
- {
- play_countdown(self.invincible_finished, "misc/poweroff.wav");
- self.effects = self.effects | (EF_RED | EF_ADDITIVE | EF_FULLBRIGHT);
- if (time > self.invincible_finished)
- {
- self.items = self.items - (self.items & IT_INVINCIBLE);
- //Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_POWERDOWN_SHIELD, self.netname);
- Send_Notification(NOTIF_ONE, self, MSG_CENTER, CENTER_POWERDOWN_SHIELD);
- }
- }
- else
- {
- if (time < self.invincible_finished)
- {
- self.items = self.items | IT_INVINCIBLE;
- Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_POWERUP_SHIELD, self.netname);
- Send_Notification(NOTIF_ONE, self, MSG_CENTER, CENTER_POWERUP_SHIELD);
- }
- }
if (self.items & IT_SUPERWEAPON)
{
if (!(self.weapons & WEPSET_SUPERWEAPONS))
// if player rotted to death... die!
// check this outside above checks, as player may still be able to rot to death
if(self.health < 1)
+ {
+ if(self.vehicle)
+ vehicles_exit(VHEF_RELEASE);
self.event_damage(self, self, 1, DEATH_ROT, self.origin, '0 0 0');
+ }
if (!(self.items & IT_UNLIMITED_WEAPON_AMMO))
{
self.armorvalue = spectatee.armorvalue;
self.ammo_cells = spectatee.ammo_cells;
self.ammo_plasma = spectatee.ammo_plasma;
+ self.ammo_supercells = spectatee.ammo_supercells;
self.ammo_shells = spectatee.ammo_shells;
self.ammo_nails = spectatee.ammo_nails;
self.ammo_rockets = spectatee.ammo_rockets;
self.last_pickup = spectatee.last_pickup;
self.hit_time = spectatee.hit_time;
self.metertime = spectatee.metertime;
- self.strength_finished = spectatee.strength_finished;
- self.invincible_finished = spectatee.invincible_finished;
self.pressedkeys = spectatee.pressedkeys;
self.weapons = spectatee.weapons;
self.switchweapon = spectatee.switchweapon;
float SpectateSet()
{
- if(self.enemy.classname != "player")
+ if(!IS_PLAYER(self.enemy))
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();
- if(!SpectateUpdate())
- PutObserverInServer();
- //}
return TRUE;
}
// these are required to fix the spectator bug with arc
if(old_spectatee && old_spectatee.arc_beam) { old_spectatee.arc_beam.SendFlags |= ARC_SF_SETTINGS; }
if(player.enemy && player.enemy.arc_beam) { player.enemy.arc_beam.SendFlags |= ARC_SF_SETTINGS; }
+
+ // needed to update spectator list
+ if(old_spectatee) { ClientData_Touch(old_spectatee); }
}
float Spectate(entity pl)
{
if(g_ca && !autocvar_g_ca_spectate_enemies && self.caplayer)
- if(pl.team != self.team)
+ if(DIFF_TEAM(pl, self))
return 0;
SetSpectator(self, pl);
}
// Returns next available player to spectate if g_ca_spectate_enemies == 0
-entity CA_SpectateNext(entity start) {
- if (start.team == self.team) {
- return start;
- }
+entity CA_SpectateNext(entity start)
+{
+ if(SAME_TEAM(start, self)) { return start; }
other = start;
// continue from current player
- while(other && other.team != self.team) {
+ while(other && DIFF_TEAM(other, self))
other = find(other, classname, "player");
- }
- if (!other) {
+ if (!other)
+ {
// restart from begining
other = find(other, classname, "player");
- while(other && other.team != self.team) {
+ while(other && DIFF_TEAM(other, self))
other = find(other, classname, "player");
- }
}
return other;
{
other = find(self.enemy, classname, "player");
- if (g_ca && !autocvar_g_ca_spectate_enemies && self.caplayer) {
+ if (g_ca && !autocvar_g_ca_spectate_enemies && self.caplayer)
+ {
// CA and ca players when spectating enemies is forbidden
other = CA_SpectateNext(other);
- } else {
+ }
+ else
+ {
// other modes and ca spectators or spectating enemies is allowed
if (!other)
other = find(other, classname, "player");
}
- if(other) { SetSpectator(self, other); }
+ if(other)
+ {
+ if(autocvar_g_observer_glowtrails)
+ CSQCMODEL_AUTOINIT(); // reinitialize csqc models, so we see animations properly
+ self.glow_trail = 0;
+ self.glow_color = 0;
+ self.glow_size = 0;
+ SetSpectator(self, other);
+ }
return SpectateSet();
}
self.classname = "player";
nades_RemoveBonus(self);
+ SetSpectator(self, world);
+
if(autocvar_g_campaign || autocvar_g_balance_teams)
{ JoinBestTeam(self, FALSE, TRUE); }
void ObserverThink()
{
+ if ( self.impulse )
+ {
+ MinigameImpulse(self.impulse);
+ self.impulse = 0;
+ }
float prefered_movetype;
if (self.flags & FL_JUMPRELEASED) {
if (self.BUTTON_JUMP && !self.version_mismatch) {
void SpectatorThink()
{
+ if ( self.impulse )
+ {
+ MinigameImpulse(self.impulse);
+ self.impulse = 0;
+ }
if (self.flags & FL_JUMPRELEASED) {
if (self.BUTTON_JUMP && !self.version_mismatch) {
self.flags &= ~FL_JUMPRELEASED;
self.flags |= FL_CLIENT | FL_NOTARGET;
}
+void vehicles_enter (entity pl, entity veh);
void PlayerUseKey()
{
if (!IS_PLAYER(self))
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);
+
+ 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
.float usekeypressed;
void() nexball_setstatus;
.float items_added;
+.float last_vehiclecheck;
void PlayerPreThink (void)
{
WarpZone_PlayerPhysics_FixVAngle();
self.stat_allow_oldvortexbeam = autocvar_g_allow_oldvortexbeam;
self.stat_leadlimit = autocvar_leadlimit;
+ self.weaponsinmap = weaponsInMap;
+
if(frametime)
{
// physics frames: update anticheat stuff
self.max_armorvalue = 0;
}
-#ifdef TETRIS
- if (TetrisPreFrame())
- return;
-#endif
-
if(self.frozen == 2)
{
self.revive_progress = bound(0, self.revive_progress + frametime * self.revive_speed, 1);
if(self.health < 1)
{
if(self.vehicle)
- vehicles_exit(VHEF_RELESE);
+ vehicles_exit(VHEF_RELEASE);
self.event_damage(self, self.frozen_by, 1, DEATH_NADE_ICE_FREEZE, self.origin, '0 0 0');
}
else if ( self.revive_progress <= 0 )
MUTATOR_CALLHOOK(PlayerPreThink);
+ if(autocvar_g_vehicles_enter)
+ if(time > self.last_vehiclecheck)
+ if(IS_PLAYER(self))
+ if(!gameover)
+ if(!self.frozen)
+ if(!self.vehicle)
+ if(self.deadflag == DEAD_NO)
+ {
+ entity veh;
+ for(veh = world; (veh = findflags(veh, vehicle_flags, VHF_ISVEHICLE)); )
+ if(vlen(veh.origin - self.origin) < autocvar_g_vehicles_enter_radius)
+ if(veh.deadflag == DEAD_NO)
+ if(veh.takedamage != DAMAGE_NO)
+ if((veh.vehicle_flags & VHF_MULTISLOT) && SAME_TEAM(veh.owner, self))
+ Send_Notification(NOTIF_ONE, self, MSG_CENTER, CENTER_VEHICLE_ENTER_GUNNER);
+ else if(!veh.owner)
+ if(!veh.team || SAME_TEAM(self, veh))
+ Send_Notification(NOTIF_ONE, self, MSG_CENTER, CENTER_VEHICLE_ENTER);
+ else if(autocvar_g_vehicles_steal)
+ Send_Notification(NOTIF_ONE, self, MSG_CENTER, CENTER_VEHICLE_ENTER_STEAL);
+
+ self.last_vehiclecheck = time + 1;
+ }
+
if(!self.cvar_cl_newusekeysupported) // FIXME remove this - it was a stupid idea to begin with, we can JUST use the button
{
if(self.BUTTON_USE && !self.usekeypressed)
if (self.crouch)
{
tracebox(self.origin, PL_MIN, PL_MAX, self.origin, FALSE, self);
- if (!trace_startsolid)
+ if (!trace_startsolid || self.pbhost)
{
self.crouch = FALSE;
self.view_ofs = PL_VIEW_OFS;
}
}
-#ifdef TETRIS
- if(self.impulse == 100)
- ImpulseCommands();
- if (!TetrisPostFrame())
- {
-#endif
-
CheatFrame();
//CheckPlayerJump();
GetPressedKeys();
}
-#ifdef TETRIS
- }
-#endif
-
/*
float i;
for(i = 0; i < 1000; ++i)
return;
self.impulse = 0;
+ if ( self.active_minigame )
+ if ( MinigameImpulse(imp) )
+ return;
+
// allow only weapon change impulses when not in round time
if(round_handler_IsActive() && !round_handler_IsRoundStarted())
if(imp == 17 || (imp >= 20 && imp < 200) || imp > 253)
if (timeout_status == TIMEOUT_ACTIVE) //don't allow any impulses while the game is paused
return;
- if(self.vehicle)
- if(self.vehicle.deadflag == DEAD_NO)
- if(self.vehicle.vehicles_impulse)
- if(self.vehicle.vehicles_impulse(imp))
- return;
+ if(self.vehicle)
+ if(self.vehicle.deadflag == DEAD_NO)
+ if(self.vehicle.vehicles_impulse)
+ if(self.vehicle.vehicles_impulse(imp))
+ return;
if(CheatImpulse(imp))
{
}
else if(imp >= 10 && imp <= 20)
{
- if(self.deadflag == DEAD_NO)
+ if(self.deadflag == DEAD_NO && !self.vehicle)
{
switch(imp)
{
W_PreviousWeapon(1);
break;
case 20:
- if(!forbidWeaponUse()) { WEP_ACTION(self.weapon, WR_RELOAD); }
+ if(!forbidWeaponUse(self)) { WEP_ACTION(self.weapon, WR_RELOAD); }
break;
}
}
- else
- self.impulse = imp; // retry in next frame
+ //else
+ //self.impulse = imp; // retry in next frame
}
else if(imp == 21)
{
}
else if(imp >= 200 && imp <= 229)
{
+ if(!self.vehicle)
if(self.deadflag == DEAD_NO)
{
// custom order weapon cycling
}
else if(imp >= 230 && imp <= 253)
{
+ if(!self.vehicle)
if(self.deadflag == DEAD_NO)
W_SwitchWeapon (imp - 230 + WEP_FIRST);
else
}
}
}
-#ifdef TETRIS
- else if(imp == 100)
- TetrisImpulse();
-#endif
}
.float wasFlying;
.float spectatorspeed;
+// client side physics
+float Physics_Valid(string thecvar)
+{
+ return autocvar_g_physics_clientselect && checkinlist(thecvar, autocvar_g_physics_clientselect_options);
+}
+
+float Physics_ClientOption(entity pl, string option)
+{
+ if (Physics_Valid(pl.cvar_cl_physics))
+ {
+ string var = sprintf("g_physics_%s_%s", pl.cvar_cl_physics, option);
+ if (cvar_type(var) & 1)
+ return cvar(var);
+ }
+ return cvar(strcat("sv_", option));
+}
+
/*
=============
PlayerJump
return TRUE; // no jumping while blocked
float doublejump = FALSE;
- float mjumpheight = autocvar_sv_jumpvelocity;
+ float mjumpheight = self.stat_sv_jumpvelocity;
player_multijump = doublejump;
player_jumpheight = mjumpheight;
return;
#endif
- k *= bound(0, wishspeed / autocvar_sv_maxairspeed, 1);
+ k *= bound(0, wishspeed / self.stat_sv_maxairspeed, 1);
zspeed = self.velocity_z;
self.velocity_z = 0;
if(dot > 0) // we can't change direction while slowing down
{
- k *= pow(dot, autocvar_sv_aircontrol_power)*frametime;
- xyspeed = max(0, xyspeed - autocvar_sv_aircontrol_penalty * sqrt(max(0, 1 - dot*dot)) * k/32);
- k *= autocvar_sv_aircontrol;
+ k *= pow(dot, self.stat_sv_aircontrol_power)*frametime;
+ xyspeed = max(0, xyspeed - self.stat_sv_aircontrol_penalty * sqrt(max(0, 1 - dot*dot)) * k/32);
+ k *= self.stat_sv_aircontrol;
self.velocity = normalize(self.velocity * xyspeed + wishdir * k);
}
if(wishspeed > curspeed * 1.01)
{
- wishspeed = min(wishspeed, curspeed + autocvar_sv_warsowbunny_airforwardaccel * self.stat_sv_maxspeed * frametime);
+ wishspeed = min(wishspeed, curspeed + self.stat_sv_warsowbunny_airforwardaccel * self.stat_sv_maxspeed * frametime);
}
else
{
- f = max(0, (autocvar_sv_warsowbunny_topspeed - curspeed) / (autocvar_sv_warsowbunny_topspeed - self.stat_sv_maxspeed));
- wishspeed = max(curspeed, self.stat_sv_maxspeed) + autocvar_sv_warsowbunny_accel * f * self.stat_sv_maxspeed * frametime;
+ f = max(0, (self.stat_sv_warsowbunny_topspeed - curspeed) / (self.stat_sv_warsowbunny_topspeed - self.stat_sv_maxspeed));
+ wishspeed = max(curspeed, self.stat_sv_maxspeed) + self.stat_sv_warsowbunny_accel * f * self.stat_sv_maxspeed * frametime;
}
wishvel = wishdir * wishspeed;
acceldir = wishvel - curvel;
addspeed = vlen(acceldir);
acceldir = normalize(acceldir);
- accelspeed = min(addspeed, autocvar_sv_warsowbunny_turnaccel * self.stat_sv_maxspeed * frametime);
+ accelspeed = min(addspeed, self.stat_sv_warsowbunny_turnaccel * self.stat_sv_maxspeed * frametime);
- if(autocvar_sv_warsowbunny_backtosideratio < 1)
+ if(self.stat_sv_warsowbunny_backtosideratio < 1)
{
curdir = normalize(curvel);
dot = acceldir * curdir;
if(dot < 0)
- acceldir = acceldir - (1 - autocvar_sv_warsowbunny_backtosideratio) * dot * curdir;
+ acceldir = acceldir - (1 - self.stat_sv_warsowbunny_backtosideratio) * dot * curdir;
}
self.velocity += accelspeed * acceldir;
.float specialcommand_pos;
void SpecialCommand()
{
-#ifdef TETRIS
- TetrisImpulse();
-#else
if(!CheatImpulse(99))
print("A hollow voice says \"Plugh\".\n");
-#endif
}
-float speedaward_speed;
-string speedaward_holder;
-string speedaward_uid;
-void race_send_speedaward(float msg)
-{
- // send the best speed of the round
- WriteByte(msg, SVC_TEMPENTITY);
- WriteByte(msg, TE_CSQC_RACE);
- WriteByte(msg, RACE_NET_SPEED_AWARD);
- WriteInt24_t(msg, floor(speedaward_speed+0.5));
- WriteString(msg, speedaward_holder);
-}
-
-float speedaward_alltimebest;
-string speedaward_alltimebest_holder;
-string speedaward_alltimebest_uid;
-void race_send_speedaward_alltimebest(float msg)
-{
- // send the best speed
- WriteByte(msg, SVC_TEMPENTITY);
- WriteByte(msg, TE_CSQC_RACE);
- WriteByte(msg, RACE_NET_SPEED_AWARD_BEST);
- WriteInt24_t(msg, floor(speedaward_alltimebest+0.5));
- WriteString(msg, speedaward_alltimebest_holder);
-}
-
-string GetMapname(void);
-float speedaward_lastupdate;
-float speedaward_lastsent;
+.float discomode;
void SV_PlayerPhysics()
{
vector wishvel, wishdir, v;
WarpZone_PlayerPhysics_FixVAngle();
maxspd_mod = 1;
- if(self.ballcarried)
- if(g_keepaway)
- maxspd_mod *= autocvar_g_keepaway_ballcarrier_highspeed;
maxspd_mod *= autocvar_g_movement_highspeed;
+ if(time < self.spider_slowness)
+ maxspd_mod *= 0.5; // half speed while slow - TODO: add a cvar!
+
// fix physics stats for g_movement_highspeed
// TODO maybe rather use maxairspeed? needs testing
- self.stat_sv_airaccel_qw = AdjustAirAccelQW(autocvar_sv_airaccel_qw, maxspd_mod);
- if(autocvar_sv_airstrafeaccel_qw)
- self.stat_sv_airstrafeaccel_qw = AdjustAirAccelQW(autocvar_sv_airstrafeaccel_qw, maxspd_mod);
+ self.stat_sv_airaccel_qw = AdjustAirAccelQW(Physics_ClientOption(self, "airaccel_qw"), maxspd_mod);
+ if(Physics_ClientOption(self, "airstrafeaccel_qw"))
+ self.stat_sv_airstrafeaccel_qw = AdjustAirAccelQW(Physics_ClientOption(self, "airstrafeaccel_qw"), maxspd_mod);
else
self.stat_sv_airstrafeaccel_qw = 0;
- self.stat_sv_airspeedlimit_nonqw = autocvar_sv_airspeedlimit_nonqw * maxspd_mod;
- self.stat_sv_maxspeed = autocvar_sv_maxspeed * maxspd_mod; // also slow walking
+ self.stat_sv_airspeedlimit_nonqw = Physics_ClientOption(self, "airspeedlimit_nonqw") * maxspd_mod;
+ self.stat_sv_maxspeed = Physics_ClientOption(self, "maxspeed") * maxspd_mod; // also slow walking
+
+ // fix some new settings
+ self.stat_sv_airaccel_qw_stretchfactor = Physics_ClientOption(self, "airaccel_qw_stretchfactor");
+ self.stat_sv_maxairstrafespeed = Physics_ClientOption(self, "maxairstrafespeed");
+ self.stat_sv_maxairspeed = Physics_ClientOption(self, "maxairspeed");
+ self.stat_sv_airstrafeaccelerate = Physics_ClientOption(self, "airstrafeaccelerate");
+ self.stat_sv_warsowbunny_turnaccel = Physics_ClientOption(self, "warsowbunny_turnaccel");
+ self.stat_sv_airaccel_sideways_friction = Physics_ClientOption(self, "airaccel_sideways_friction");
+ self.stat_sv_aircontrol = Physics_ClientOption(self, "aircontrol");
+ self.stat_sv_aircontrol_power = Physics_ClientOption(self, "aircontrol_power");
+ self.stat_sv_aircontrol_penalty = Physics_ClientOption(self, "aircontrol_penalty");
+ self.stat_sv_warsowbunny_airforwardaccel = Physics_ClientOption(self, "warsowbunny_airforwardaccel");
+ self.stat_sv_warsowbunny_topspeed = Physics_ClientOption(self, "warsowbunny_topspeed");
+ self.stat_sv_warsowbunny_accel = Physics_ClientOption(self, "warsowbunny_accel");
+ self.stat_sv_warsowbunny_backtosideratio = Physics_ClientOption(self, "warsowbunny_backtosideratio");
+ self.stat_sv_friction = Physics_ClientOption(self, "friction");
+ self.stat_sv_accelerate = Physics_ClientOption(self, "accelerate");
+ self.stat_sv_stopspeed = Physics_ClientOption(self, "stopspeed");
+ self.stat_sv_airaccelerate = Physics_ClientOption(self, "airaccelerate");
+ self.stat_sv_airstopaccelerate = Physics_ClientOption(self, "airstopaccelerate");
+ self.stat_sv_jumpvelocity = Physics_ClientOption(self, "jumpvelocity");
if(self.PlayerPhysplug)
if(self.PlayerPhysplug())
}
}
+ if ( self.discomode )
+ {
+ if(IS_PLAYER(self))
+ self.BUTTON_JUMP = 1;
+
+ self.angles_y = time*180;
+ self.velocity = randomvec() * 80;
+ self.fixangle = TRUE;
+ }
+
if (self.movetype == MOVETYPE_NONE)
return;
if(time < self.ladder_time)
self.disableclientprediction = 1;
- if(time < self.spider_slowness)
- {
- self.stat_sv_maxspeed *= 0.5; // half speed while slow from spider
- self.stat_sv_airspeedlimit_nonqw *= 0.5;
- }
-
if(self.frozen)
{
if(autocvar_sv_dodging_frozen && IS_REAL_CLIENT(self))
maxspd_mod = self.spectatorspeed;
}
- spd = max(self.stat_sv_maxspeed, autocvar_sv_maxairspeed) * maxspd_mod * swampspd_mod;
+ spd = max(self.stat_sv_maxspeed, self.stat_sv_maxairspeed) * maxspd_mod * swampspd_mod;
if(self.speed != spd)
{
self.speed = spd;
// 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;
if (wishspeed > self.stat_sv_maxspeed*maxspd_mod)
wishspeed = self.stat_sv_maxspeed*maxspd_mod;
if (time >= self.teleport_time)
- PM_Accelerate(wishdir, wishspeed, wishspeed, autocvar_sv_accelerate*maxspd_mod, 1, 0, 0, 0);
+ PM_Accelerate(wishdir, wishspeed, wishspeed, self.stat_sv_accelerate*maxspd_mod, 1, 0, 0, 0);
}
else if (self.waterlevel >= WATERLEVEL_SWIMMING)
{
wishspeed = wishspeed * 0.7;
// water friction
- self.velocity = self.velocity * (1 - frametime * autocvar_sv_friction);
+ self.velocity = self.velocity * (1 - frametime * self.stat_sv_friction);
// water acceleration
- PM_Accelerate(wishdir, wishspeed, wishspeed, autocvar_sv_accelerate*maxspd_mod, 1, 0, 0, 0);
+ PM_Accelerate(wishdir, wishspeed, wishspeed, self.stat_sv_accelerate*maxspd_mod, 1, 0, 0, 0);
}
else if (time < self.ladder_time)
{
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;
if (time >= self.teleport_time)
{
// water acceleration
- PM_Accelerate(wishdir, wishspeed, wishspeed, autocvar_sv_accelerate*maxspd_mod, 1, 0, 0, 0);
+ PM_Accelerate(wishdir, wishspeed, wishspeed, self.stat_sv_accelerate*maxspd_mod, 1, 0, 0, 0);
}
}
else if (self.items & IT_USING_JETPACK)
makevectors(self.v_angle);
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
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;
+ if(autocvar_g_jetpack_reverse_thrust && self.crouch) { wishvel_z *= -1; }
+
float best;
best = 0;
//////////////////////////////////////////////////////////////////////////////////////
float fvel;
fvel = min(1, vlen(wishvel) / best);
- if(autocvar_g_jetpack_fuel && !(self.items & IT_UNLIMITED_WEAPON_AMMO))
+ if(autocvar_g_jetpack_fuel && (!(self.items & IT_UNLIMITED_WEAPON_AMMO) || cvar("g_overkill")))
f = min(1, self.ammo_fuel / (autocvar_g_jetpack_fuel * frametime * fvel));
else
f = 1;
if (f > 0 && wishvel != '0 0 0')
{
self.velocity = self.velocity + wishvel * f * frametime;
- if (!(self.items & IT_UNLIMITED_WEAPON_AMMO))
+ if ((!(self.items & IT_UNLIMITED_WEAPON_AMMO) || cvar("g_overkill")))
self.ammo_fuel -= autocvar_g_jetpack_fuel * frametime * fvel * f;
self.flags &= ~FL_ONGROUND;
self.items |= IT_USING_JETPACK;
f = vlen(v);
if(f > 0)
{
- if (f < autocvar_sv_stopspeed)
- f = 1 - frametime * (autocvar_sv_stopspeed / f) * autocvar_sv_friction;
+ if (f < self.stat_sv_stopspeed)
+ f = 1 - frametime * (self.stat_sv_stopspeed / f) * self.stat_sv_friction;
else
- f = 1 - frametime * autocvar_sv_friction;
+ f = 1 - frametime * self.stat_sv_friction;
if (f > 0)
self.velocity = self.velocity * f;
else
if (self.crouch)
wishspeed = wishspeed * 0.5;
if (time >= self.teleport_time)
- PM_Accelerate(wishdir, wishspeed, wishspeed, autocvar_sv_accelerate*maxspd_mod, 1, 0, 0, 0);
+ PM_Accelerate(wishdir, wishspeed, wishspeed, self.stat_sv_accelerate*maxspd_mod, 1, 0, 0, 0);
}
else
{
if(maxspd_mod < 1)
{
- maxairspd = autocvar_sv_maxairspeed*maxspd_mod;
- airaccel = autocvar_sv_airaccelerate*maxspd_mod;
+ maxairspd = self.stat_sv_maxairspeed*maxspd_mod;
+ airaccel = self.stat_sv_airaccelerate*maxspd_mod;
}
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');
wishspeed2 = wishspeed;
// CPM
- if(autocvar_sv_airstopaccelerate)
+ if(self.stat_sv_airstopaccelerate)
{
vector curdir;
curdir = self.velocity;
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;
// log dv/dt = logaccel + logmaxspeed (when slow)
// log dv/dt = logaccel + logmaxspeed + log(1 - accelqw) (when fast)
strafity = IsMoveInDirection(self.movement, -90) + IsMoveInDirection(self.movement, +90); // if one is nonzero, other is always zero
- if(autocvar_sv_maxairstrafespeed)
- wishspeed = min(wishspeed, GeomLerp(autocvar_sv_maxairspeed*maxspd_mod, strafity, autocvar_sv_maxairstrafespeed*maxspd_mod));
- if(autocvar_sv_airstrafeaccelerate)
- airaccel = GeomLerp(airaccel, strafity, autocvar_sv_airstrafeaccelerate*maxspd_mod);
+ if(self.stat_sv_maxairstrafespeed)
+ wishspeed = min(wishspeed, GeomLerp(self.stat_sv_maxairspeed*maxspd_mod, strafity, self.stat_sv_maxairstrafespeed*maxspd_mod));
+ if(self.stat_sv_airstrafeaccelerate)
+ airaccel = GeomLerp(airaccel, strafity, self.stat_sv_airstrafeaccelerate*maxspd_mod);
if(self.stat_sv_airstrafeaccel_qw)
airaccelqw = copysign(1-GeomLerp(1-fabs(self.stat_sv_airaccel_qw), strafity, 1-fabs(self.stat_sv_airstrafeaccel_qw)), ((strafity > 0.5) ? self.stat_sv_airstrafeaccel_qw : self.stat_sv_airaccel_qw));
// !CPM
- if(autocvar_sv_warsowbunny_turnaccel && accelerating && self.movement_y == 0 && self.movement_x != 0)
+ if(self.stat_sv_warsowbunny_turnaccel && accelerating && self.movement_y == 0 && self.movement_x != 0)
PM_AirAccelerate(wishdir, wishspeed);
else
- PM_Accelerate(wishdir, wishspeed, wishspeed0, airaccel, airaccelqw, autocvar_sv_airaccel_qw_stretchfactor, autocvar_sv_airaccel_sideways_friction / maxairspd, self.stat_sv_airspeedlimit_nonqw);
+ PM_Accelerate(wishdir, wishspeed, wishspeed0, airaccel, airaccelqw, self.stat_sv_airaccel_qw_stretchfactor, self.stat_sv_airaccel_sideways_friction / maxairspd, self.stat_sv_airspeedlimit_nonqw);
- if(autocvar_sv_aircontrol)
+ if(self.stat_sv_aircontrol)
CPM_PM_Aircontrol(wishdir, wishspeed2);
}
}
float 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);
// 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);
+ v = healtharmor_applydamage(self.armorvalue, autocvar_g_balance_armor_blockpercent, deathtype, damage, FALSE);
take = v_x;
save = v_y;
#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)
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;
if (take > 100)
Violence_GibSplash_At(hitloc, force * -0.2, 3, 1, self, attacker);
+ if (!autocvar_g_spawnshield_nodamage)
if (time >= self.spawnshieldtime)
{
if (!(self.flags & FL_GODMODE))
float shake;
if(IS_BOT_CLIENT(self) && self.health >= 1)
{
- shake = damage * 5 / (bound(0,skill,100) + 1);
+ shake = damage * 5 / (bound(0,bot_skill,100) + 1);
self.v_angle_x = self.v_angle_x + (random() * 2 - 1) * shake;
self.v_angle_y = self.v_angle_y + (random() * 2 - 1) * shake;
self.v_angle_x = bound(-90, self.v_angle_x, 90);
if(deathtype == DEATH_KILL)
{
// for the lemmings fans, a small harmless explosion
- pointparticles(particleeffectnum("rocket_explode"), self.origin, '0 0 0', 1);
+ Send_Effect(EFFECT_ROCKET_EXPLODE, self.origin, '0 0 0', 1);
}
}
+ jeff_Announcer_PlayerDies(attacker, deathtype, self, inflictor);
+
+ if(IS_PLAYER(attacker))
+ {
+ self.lastkiller = attacker;
+ attacker.lastkilled = self;
+ }
+
// print an obituary message
if(self.classname != "body")
Obituary (attacker, inflictor, self, deathtype);
frag_attacker = attacker;
frag_inflictor = inflictor;
frag_target = self;
+ frag_damage = excess;
frag_deathtype = deathtype;
MUTATOR_CALLHOOK(PlayerDies);
+ excess = frag_damage;
WEP_ACTION(self.weapon, WR_PLAYERDEATH);
setsize(self, self.mins, self.maxs);
}
// set damage function to corpse damage
+ if(autocvar_g_player_gib_always)
+ excess = 10000;
self.event_damage = PlayerCorpseDamage;
// call the corpse damage function just in case it wants to gib
self.event_damage(inflictor, attacker, excess, deathtype, hitloc, force);
else
colorprefix = "^7";
+ if(source.waterlevel >= WATERLEVEL_SWIMMING)
+ msgin = "[glub glub glub]";
+
if(msgin != "")
{
if(privatesay)
}
else
{
- msgstr = strcat("\{1}", colorprefix, namestr, "^7: ", msgin);
+ if(substring(msgin, 0, 3) == "/me")
+ {
+ //msgin = strreplace("/me", "", msgin);
+ msgin = substring(msgin, 3, strlen(msgin));
+ msgstr = strcat("\{1}^4* ", colorprefix, namestr, "^7", msgin);
+ }
+ else
+ msgstr = strcat("\{1}", colorprefix, namestr, "^7: ", msgin);
cmsgstr = "";
}
msgstr = strcat(strreplace("\n", " ", msgstr), "\n"); // newlines only are good for centerprint
if(cmsgstr != "")
centerprint(privatesay, cmsgstr);
}
+ else if ( teamsay && source.active_minigame )
+ {
+ sprint(source, sourcemsgstr);
+ dedicated_print(msgstr); // send to server console too
+ FOR_EACH_REALCLIENT(head)
+ if(head != source)
+ if(head.active_minigame == source.active_minigame)
+ sprint(head, msgstr);
+ }
else if(teamsay > 0) // team message, only sent to team mates
{
sprint(source, sourcemsgstr);
return ret;
}
+void IRCSay(string sourcename, string msgin)
+{
+ entity head;
+
+ if(substring(msgin, 0, 1) == " ")
+ msgin = substring(msgin, 1, strlen(msgin) - 1); // work around DP say bug (say_team does not have this!)
+
+ //msgin = formatmessage(msgin);
+
+ if(msgin == "")
+ return;
+
+ string msgstr = strcat("\{1}[IRC] ", sourcename, "^7", msgin);
+
+ msgstr = strcat(strreplace("\n", " ", msgstr), "\n"); // newlines only are good for centerprint
+
+ FOR_EACH_CLIENTSLOT(head)
+ {
+ if(!intermission_running)
+ if((autocvar_g_chat_nospectators == 1) || (autocvar_g_chat_nospectators == 2 && !(warmup_stage || gameover)))
+ if(IS_PLAYER(head))
+ continue;
+
+ if(head.netaddress)
+ if(head.netaddress != "")
+ if(head.netaddress != "null/botclient")
+ sprint(head, msgstr);
+ }
+}
+
float GetVoiceMessageVoiceType(string type)
{
if(type == "taunt")
--- /dev/null
+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))
+ 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(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);
+ break;
+ }
+ }
+ }
+ return 0;
+}
+
+void W_CycleWeapon(string weaponorder, float dir)
+{
+ float w;
+ 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))
+ 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);
+}
+
+// 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
+ 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
+
+ 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);
+ 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);
+}
if(autocvar_g_campaign) { campaign_bots_may_start = 1; }
self.classname = "player";
+ nades_RemoveBonus(self);
PlayerScore_Clear(self);
Kill_Notification(NOTIF_ONE_ONLY, self, MSG_CENTER_CPID, CPID_PREVENT_JOIN);
Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_JOIN_PLAY, self.netname);
}
}
-void ClientCommand_mobedit(float request, float argc)
+void ClientCommand_physics(float request, float 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);
+ stuffcmd(self, strcat("\nseta cl_physics ", command, "\nsendcvar cl_physics\n"));
+
+ 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;
}
-
- FOR_EACH_MONSTER(e)
+
+ if(!autocvar_g_physics_clientselect)
{
- if(e.realowner == self)
- ++monstercount;
+ sprint(self, "Client physics selection is currently disabled.\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 != "")
- {
- 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;
- }
- }
+
}
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 <random> <monster> [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 <physics>\n");
+ sprint(self, " See 'cmd physics list' for available physics sets.\n");
+ sprint(self, " Argument 'default' resets to standard physics.\n");
return;
}
}
}
}
+void ClientCommand_report(float request, float argc)
+{
+ switch(request)
+ {
+ case CMD_REQUEST_COMMAND:
+ {
+ if(argv(1) == "fps")
+ if(sv_showfps)
+ if(IS_PLAYER(self))
+ if(self.scorekeeper)
+ PlayerScore_Set(self, SP_FPS, stof(argv(2)));
+
+ if(argv(1) == "fov")
+ {
+ self.clientfov = stof(argv(2));
+ ClientData_Touch(self);
+ }
+
+ return; // never fall through to usage
+ }
+
+ default:
+ case CMD_REQUEST_USAGE:
+ {
+ sprint(self, "\nUsage:^3 cmd report [fps fov]\n");
+ sprint(self, " Where 'fps' is your scoreboard frames per second.\n");
+ sprint(self, " Where 'fov' is your current field of view.\n");
+ return;
+ }
+ }
+}
+
void ClientCommand_say(float request, float argc, string command)
{
switch(request)
CLIENT_COMMAND("clientversion", ClientCommand_clientversion(request, arguments), "Release version of the game") \
CLIENT_COMMAND("mv_getpicture", ClientCommand_mv_getpicture(request, arguments), "Retrieve mapshot picture from the server") \
CLIENT_COMMAND("join", ClientCommand_join(request), "Become a player in the game") \
- CLIENT_COMMAND("mobedit", ClientCommand_mobedit(request, arguments), "Edit your monster's properties") \
- CLIENT_COMMAND("mobkill", ClientCommand_mobkill(request), "Kills your monster") \
- CLIENT_COMMAND("mobspawn", ClientCommand_mobspawn(request, arguments), "Spawn monsters infront of yourself") \
+ CLIENT_COMMAND("physics", ClientCommand_physics(request, arguments), "Change physics set") \
CLIENT_COMMAND("ready", ClientCommand_ready(request), "Qualify as ready to end warmup stage (or restart server if allowed)") \
+ CLIENT_COMMAND("report", ClientCommand_report(request, arguments), "Retrieve values from the client") \
CLIENT_COMMAND("say", ClientCommand_say(request, arguments, command), "Print a message to chat to all players") \
CLIENT_COMMAND("say_team", ClientCommand_say_team(request, arguments, command), "Print a message to chat to all team mates") \
CLIENT_COMMAND("selectteam", ClientCommand_selectteam(request, arguments), "Attempt to choose a team to join into") \
CLIENT_COMMAND("suggestmap", ClientCommand_suggestmap(request, arguments), "Suggest a map to the mapvote at match end") \
CLIENT_COMMAND("tell", ClientCommand_tell(request, arguments, command), "Send a message directly to a player") \
CLIENT_COMMAND("voice", ClientCommand_voice(request, arguments, command), "Send voice message via sound") \
+ CLIENT_COMMAND("minigame", ClientCommand_minigame(request, arguments, command), "Start a minigame") \
/* nothing */
void ClientCommand_macro_help()
}
}
+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;
+ for(i = MON_FIRST; i <= MON_LAST; ++i)
+ {
+ mon = get_monsterinfo(i);
+ 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);
+ 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)
{
if(caller) { caller.allowed_timeouts -= 1; }
- bprint(GetCallerName(caller), " ^7called a timeout", (caller ? strcat(" (", ftos(caller.allowed_timeouts), " timeout(s) left)") : ""), "!\n"); // write a bprint who started the timeout (and how many they have left)
+ // write a bprint who started the timeout (and how many they have left)
+ bprint(GetCallerName(caller), " ^7called a timeout", (caller ? strcat(" (", ftos(caller.allowed_timeouts), " timeout(s) left)") : ""), "!\n");
timeout_status = TIMEOUT_LEADTIME;
timeout_caller = caller;
#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") \
}
}
+float GameCommand_checkinlist(string game_command, string list)
+{
+ string l = strcat(" ", list, " ");
+
+ if(strstrofs(l, strcat(" ", game_command, " "), 0) >= 0)
+ return TRUE;
+
+ return FALSE;
+}
+
// used by GameCommand_extendmatchtime() and GameCommand_reducematchtime()
void changematchtime(float delta, float mi, float ma)
{
cvar_set("timelimit", ftos(new / 60));
}
+void DiscoMode(entity e, float enable)
+{
+ if(e == world)
+ return;
+
+ float accepted;
+
+ accepted = VerifyClientEntity(e, TRUE, FALSE);
+
+ if(accepted > 0)
+ {
+ e.discomode = enable;
+ }
+}
+
// =======================
// Command Sub-Functions
}
}
-void GameCommand_mobbutcher(float request)
-{
- switch(request)
- {
- case CMD_REQUEST_COMMAND:
- {
- if(autocvar_g_campaign) { print("This command doesn't work in campaign mode.\n"); return; }
- if(g_invasion) { print("This command doesn't work during an invasion.\n"); return; }
-
- float removed_count = 0;
- entity head;
-
- FOR_EACH_MONSTER(head)
- {
- monster_remove(head);
- ++removed_count;
- }
-
- monsters_total = 0; // reset stats?
- monsters_killed = 0;
-
- totalspawned = 0;
-
- if(removed_count <= 0)
- print("No monsters to kill\n");
- else
- printf("Killed %d monster%s\n", removed_count, ((removed_count == 1) ? "" : "s"));
-
- return; // never fall through to usage
- }
-
- default:
- case CMD_REQUEST_USAGE:
- {
- print("\nUsage:^3 sv_cmd mobbutcher\n");
- print(" No arguments required.\n");
- return;
- }
- }
-}
-
void GameCommand_allready(float request)
{
switch(request)
string result1 = (argv(2) ? strcat("^7", argv(1)) : "^1HEADS");
string result2 = (argv(2) ? strcat("^7", argv(2)) : "^4TAILS");
string choice = ((random() > 0.5) ? result1 : result2);
-
+
Send_Notification(NOTIF_ALL, world, MSG_MULTI, MULTI_COINTOSS, choice);
return;
}
}
}
+void GameCommand_gravity(float request, float argc)
+{
+ switch(request)
+ {
+ case CMD_REQUEST_COMMAND:
+ {
+ string rm = (autocvar_g_rm ? "rm_" : "");
+ string grav = argv(1);
+ string gravity_list = cvar_string(strcat(rm, "gravity_list"));
+ if(grav == "random")
+ {
+ cvar_set("g_random_gravity", "1");
+ localcmd("defer 2 restart");
+ return;
+ }
+ else if(grav == "help")
+ {
+ localcmd("say ^3Usage: ^2vcall gravity [value]\n");
+ return;
+ }
+ else if(grav == "list")
+ {
+ localcmd(strcat("say ^3Available gravity votes: ^2", gravity_list));
+ return;
+ }
+ if(!cvar(strcat(rm, "gravity_", grav)) || !GameCommand_checkinlist(grav, gravity_list))
+ {
+ localcmd("say ^1That gravity does not exist, vote for ^2gravity list ^1to see available gravity votes\n");
+ return;
+ }
+ cvar_set("sv_gravity", ftos(cvar(strcat(rm, "gravity_", grav))));
+ localcmd(sprintf("say ^3Gravity is now: %d", autocvar_sv_gravity, "\n"));
+ return;
+ }
+
+ default:
+ print("Incorrect parameters for ^2gravity^7\n");
+ case CMD_REQUEST_USAGE:
+ {
+ print("\nUsage:^3 sv_cmd gravity [command]\n");
+ return;
+ }
+ }
+}
+
+void GameCommand_ircmsg(float request, float argc)
+{
+ switch(request)
+ {
+ case CMD_REQUEST_COMMAND:
+ {
+ IRCSay(argv(1), argv(2));
+ return;
+ }
+
+ default:
+ print("Incorrect parameters for ^2ircmsg^7\n");
+ case CMD_REQUEST_USAGE:
+ {
+ print("\nUsage:^3 sv_cmd ircmsg [message]\n");
+ return;
+ }
+ }
+}
+
void GameCommand_lockteams(float request)
{
switch(request)
{
if(argv(2))
{
+ float accepted;
+ if(argv(1) == "all")
+ {
+ entity head;
+
+ FOR_EACH_REALCLIENT(head)
+ {
+ 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);
- float accepted = VerifyClientEntity(client, TRUE, FALSE);
+ accepted = VerifyClientEntity(client, TRUE, FALSE);
if(accepted > 0)
{
#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);
+ 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);
+
+ 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)
// Do not hard code aliases for these, instead create them in commands.cfg... also: keep in alphabetical order, please ;)
#define SERVER_COMMANDS(request,arguments,command) \
SERVER_COMMAND("adminmsg", GameCommand_adminmsg(request, arguments), "Send an admin message to a client directly") \
- SERVER_COMMAND("mobbutcher", GameCommand_mobbutcher(request), "Instantly removes all monsters on the map") \
SERVER_COMMAND("allready", GameCommand_allready(request), "Restart the server and reset the players") \
SERVER_COMMAND("allspec", GameCommand_allspec(request, arguments), "Force all players to spectate") \
SERVER_COMMAND("anticheat", GameCommand_anticheat(request, arguments), "Create an anticheat report for a client") \
SERVER_COMMAND("defer_clear", GameCommand_defer_clear(request, arguments), "Clear all queued defer commands for a specific client") \
SERVER_COMMAND("defer_clear_all", GameCommand_defer_clear_all(request), "Clear all queued defer commands for all clients") \
SERVER_COMMAND("delrec", GameCommand_delrec(request, arguments), "Delete race time record for a map") \
+ SERVER_COMMAND("discomode", GameCommand_discomode(request, arguments), "Disco Mode!!") \
SERVER_COMMAND("effectindexdump", GameCommand_effectindexdump(request), "Dump list of effects from code and effectinfo.txt") \
SERVER_COMMAND("extendmatchtime", GameCommand_extendmatchtime(request), "Increase the timelimit value incrementally") \
SERVER_COMMAND("find", GameCommand_find(request, arguments), "Search through entities for matching classname") \
SERVER_COMMAND("gametype", GameCommand_gametype(request, arguments), "Simple command to change the active gametype") \
SERVER_COMMAND("gettaginfo", GameCommand_gettaginfo(request, arguments), "Get specific information about a weapon model") \
SERVER_COMMAND("gotomap", GameCommand_gotomap(request, arguments), "Simple command to switch to another map") \
+ SERVER_COMMAND("gravity", GameCommand_gravity(request, arguments), "Changes gravity based on cvars") \
+ SERVER_COMMAND("ircmsg", GameCommand_ircmsg(request, arguments), "Say a message as an IRC user") \
SERVER_COMMAND("lockteams", GameCommand_lockteams(request), "Disable the ability for players to switch or enter teams") \
SERVER_COMMAND("make_mapinfo", GameCommand_make_mapinfo(request), "Automatically rebuild mapinfo files") \
SERVER_COMMAND("moveplayer", GameCommand_moveplayer(request, arguments), "Change the team/status of a player") \
{
float argc = tokenize_console(command);
+ // for the mutator hook system
+ cmd_name = strtolower(argv(0));
+ cmd_argc = argc;
+ cmd_string = command;
+
// Guide for working with argc arguments by example:
// argc: 1 - 2 - 3 - 4
// argv: 0 - 1 - 2 - 3
return;
}
}
+ else if(MUTATOR_CALLHOOK(SV_ParseServerCommand))
+ {
+ return; // handled by a mutator
+ }
else if(BanCommand(command))
{
return; // handled by server/command/ipban.qc
float shuffleteams_teams[SHUFFLETEAMS_MAX_TEAMS]; // maximum of 4 teams
// used by common/command/generic.qc:GenericCommand_dumpcommands to list all commands into a .txt file
-void GameCommand_macro_write_aliases(float fh);
\ No newline at end of file
+void GameCommand_macro_write_aliases(float fh);
+
+void DiscoMode(entity e, float enable);
}
}
- if(g_keyhunt)
- kh_Controller_SetThink(autocvar_g_balance_keyhunt_delay_round + (game_starttime - time), kh_StartRound);
-
self = oldself;
}
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
}
{
string command_origin = GetCommandPrefix(caller);
- if(argc == 2 || argv(2) == "help") // help display listing all commands
+ if(argc == 2 || argv(2) == "1" || argv(2) == "help") // help display listing all commands
{
print_to(caller, "\nVoting commands:\n");
#define VOTE_COMMAND(name,function,description,assignment) \
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
{
--- /dev/null
+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;
+}
+
+void onslaught_controlpoint_icon_link(entity e, void() spawnproc)
+{
+ Net_LinkEntity(e, TRUE, 0, cpicon_send);
+ e.think = spawnproc;
+ e.nextthink = time * sys_frametime;
+}
--- /dev/null
+const vector CPICON_MIN = '-32 -32 -9';
+const vector CPICON_MAX = '32 32 25';
+
+float CPSF_STATUS = 4;
+float CPSF_SETUP = 8;
// Globals
-float g_cloaked, g_footsteps, g_grappling_hook, g_instagib;
+float g_cloaked, g_footsteps, g_grappling_hook;
float g_warmup_limit;
float g_warmup_allguns;
float g_warmup_allow_timeout;
.float pain_frame; //"
.float crouch; // Crouching or not?
-.float strength_finished;
-.float invincible_finished;
+.float invincible_finished, strength_finished; // TODO: vehicles system abuses these
.float superweapons_finished;
.vector finaldest, finalangle; //plat.qc stuff
// WEAPONTODO: remove this
WepSet weaponsInMap;
+.WepSet weaponsinmap;
+
.float respawn_countdown; // next number to count
float bot_waypoints_for_items;
.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;
.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;
+
+.string cvar_cl_autovote;
+
+.entity lastkiller;
+.entity lastkilled;
+
+.float vaporizer_refire;
+
+float sv_showfps;
+
+.float clientfov;
+
+.float sub_target_used;
+
+.string cvar_cl_physics;
+
+.float skill;
if(self.team)
if(attacker.team == self.team)
return;
+ self.pain_finished = time;
self.health = self.health - damage;
if(self.sprite)
{
if(w == 0)
w = player.cnt; // previous weapon!
s = strcat(s, ftos(w));
- if(time < player.strength_finished)
- s = strcat(s, "S");
- if(time < player.invincible_finished)
- s = strcat(s, "I");
if(player.flagcarried != world)
s = strcat(s, "F");
if(player.BUTTON_CHAT)
s = strcat(s, "T");
- if(player.kh_next)
- s = strcat(s, "K");
return s;
}
attacker.taunt_soundtime = time + 1;
attacker.killcount = attacker.killcount + 1;
+ // sprees 2 and 8 are blocked by "" value, so no sending
#define SPREE_ITEM(counta,countb,center,normal,gentle) \
case counta: \
{ \
PS_GR_P_ADDVAL(attacker, PLAYERSTATS_ACHIEVEMENT_FIRSTBLOOD, 1);
PS_GR_P_ADDVAL(targ, PLAYERSTATS_ACHIEVEMENT_FIRSTVICTIM, 1);
+#ifdef JEFF
+ Send_Notification(NOTIF_ALL, world, MSG_ANNCE, ANNCE_JEFF_FIRSTBLOOD);
+#endif
+
// tell spree_inf and spree_cen that this is a first-blood and first-victim event
kill_count_to_attacker = -1;
kill_count_to_target = -2;
);
}
+ 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, 0);
+ Obituary_SpecialDeath(targ, TRUE, deathtype, targ.netname, attacker.netname, deathlocation, targ.killcount, kill_count_to_attacker, f3);
}
}
// 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:
{
- 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:
{
void Freeze (entity targ, float freeze_time, float frozen_type, float show_waypoint)
{
- if(!IS_PLAYER(targ) && !(targ.flags & FL_MONSTER)) // only specified entities can be freezed
+ if(!IS_PLAYER(targ) && !IS_MONSTER(targ)) // only specified entities can be freezed
return;
if(targ.frozen)
return;
- float targ_maxhealth = ((targ.flags & FL_MONSTER) ? targ.max_health : start_health);
+ float targ_maxhealth = ((IS_MONSTER(targ)) ? targ.max_health : start_health);
targ.frozen = frozen_type;
targ.revive_progress = ((frozen_type == 3) ? 1 : 0);
{
// exit the vehicle before killing (fixes a crash)
if(IS_PLAYER(targ) && targ.vehicle)
- vehicles_exit(VHEF_RELESE);
+ vehicles_exit(VHEF_RELEASE);
// These are ALWAYS lethal
// No damage modification here
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_inflictor = inflictor;
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_inflictor = inflictor;
// should this be changed at all? If so, in what way?
frag_attacker = attacker;
+ frag_inflictor = inflictor;
frag_target = targ;
frag_damage = damage;
frag_force = force;
if(targ.frozen)
if(deathtype != DEATH_HURTTRIGGER && deathtype != DEATH_TEAMCHANGE && deathtype != DEATH_AUTOTEAMCHANGE)
{
- if(autocvar_g_freezetag_revive_falldamage > 0)
+ if(autocvar_g_freeze_revive_falldamage > 0)
if(deathtype == DEATH_FALL)
- if(damage >= autocvar_g_freezetag_revive_falldamage)
+ if(damage >= autocvar_g_freeze_revive_falldamage)
{
Unfreeze(targ);
- targ.health = autocvar_g_freezetag_revive_falldamage_health;
- pointparticles(particleeffectnum("iceorglass"), targ.origin, '0 0 0', 3);
+ targ.health = autocvar_g_freeze_revive_falldamage_health;
+ Send_Effect(EFFECT_ICEORGLASS, targ.origin, '0 0 0', 3);
Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_FREEZETAG_REVIVED_FALL, targ.netname);
Send_Notification(NOTIF_ONE, targ, MSG_CENTER, CENTER_FREEZETAG_REVIVE_SELF);
}
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;
// don't reset back to last position, even if new position is stuck in solid
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;
}
- if(!g_instagib)
- {
- // apply strength multiplier
- if (attacker.items & IT_STRENGTH)
- {
- if(targ == attacker)
- {
- damage = damage * autocvar_g_balance_powerup_strength_selfdamage;
- force = force * autocvar_g_balance_powerup_strength_selfforce;
- }
- else
- {
- damage = damage * autocvar_g_balance_powerup_strength_damage;
- force = force * autocvar_g_balance_powerup_strength_force;
- }
- }
-
- // apply invincibility multiplier
- if (targ.items & IT_INVINCIBLE)
- damage = damage * autocvar_g_balance_powerup_invincible_takedamage;
- }
-
if (targ == attacker)
damage = damage * autocvar_g_balance_selfdamagepercent; // Partial damage if the attacker hits himself
// count the damage
if(attacker)
if(!targ.deadflag)
- if(deathtype != DEATH_BUFF_VENGEANCE)
+ if(deathtype != DEATH_BUFF)
if(targ.takedamage == DAMAGE_AIM)
if(targ != attacker)
{
entity victim;
- if((targ.vehicle_flags & VHF_ISVEHICLE) && targ.owner)
+ if(IS_VEHICLE(targ) && targ.owner)
victim = targ.owner;
else
victim = targ;
- if(IS_PLAYER(victim) || (victim.turrcaps_flags & TFL_TURRCAPS_ISTURRET) || (victim.flags & FL_MONSTER))
+ if(IS_PLAYER(victim) || IS_TURRET(victim) || IS_MONSTER(victim) || victim.classname == "func_assault_destructible" || (victim.classname == "onslaught_generator" && !victim.isshielded) || (victim.classname == "onslaught_controlpoint_icon" && !victim.owner.isshielded))
{
if(DIFF_TEAM(victim, attacker) && !victim.frozen)
{
mintime = e.fire_endtime - time;
maxtime = max(mintime, t);
- mindps = e.fire_damagepersec;
+ mindps = max(0.1, e.fire_damagepersec);
maxdps = max(mindps, dps);
if(maxtime > mintime || maxdps > mindps)
}
e.fire_hitsound = TRUE;
- if (!IS_INDEPENDENT_PLAYER(e))
+ if(!IS_INDEPENDENT_PLAYER(e))
if(!e.frozen)
FOR_EACH_PLAYER(other) if(e != other)
{
.float hook_length;
.float hook_switchweapon;
+.float last_dmg;
void RemoveGrapplingHook(entity pl)
{
void GrapplingHookReset(void)
{
+ if(g_keyhunt) { return; }
if(self.realowner.hook == self)
RemoveGrapplingHook(self.owner);
else // in any case:
void GrapplingHookThink();
void GrapplingHook_Stop()
{
- pointparticles(particleeffectnum("grapple_impact"), self.origin, '0 0 0', 1);
- sound (self, CH_SHOTS, "weapons/hook_impact.wav", VOL_BASE, ATTEN_NORM);
+ Send_Effect(EFFECT_HOOK_IMPACT, self.origin, '0 0 0', 1);
+ sound (self, CH_SHOTS, W_Sound("hook_impact"), VOL_BASE, ATTEN_NORM);
self.state = 1;
self.think = GrapplingHookThink;
void GrapplingHookThink()
{
- float spd, dist, minlength, pullspeed, ropestretch, ropeairfriction, rubberforce, newlength, rubberforce_overstretch, s;
+ float spd, dist, minlength, pullspeed, ropestretch, ropeairfriction, rubberforce, newlength, rubberforce_overstretch, s, frozen_pulling, tarzan;
vector dir, org, end, v0, dv, v, myorg, vs;
if(self.realowner.hook != self) // how did that happen?
{
error("Owner lost the hook!\n");
return;
}
- if(LostMovetypeFollow(self) || intermission_running || (round_handler_IsActive() && !round_handler_IsRoundStarted()))
+ if(LostMovetypeFollow(self) || intermission_running || (self.aiment.flags & FL_PROJECTILE))
{
RemoveGrapplingHook(self.realowner);
return;
// while hanging on the rope, this friction component will help you a
// bit to control the rope
+ frozen_pulling = (autocvar_g_grappling_hook_tarzan >= 2 && autocvar_g_balance_grapplehook_pull_frozen);
+
+ tarzan = autocvar_g_grappling_hook_tarzan;
+
+ if(autocvar_g_balance_hook_secondary == 2)
+ if(self.realowner.BUTTON_ATCK2 && self.realowner.weapon == WEP_HOOK)
+ tarzan = 0;
+
dir = self.origin - myorg;
dist = vlen(dir);
dir = normalize(dir);
- if(autocvar_g_grappling_hook_tarzan)
+ entity realpull = self.realowner;
+
+ if(realpull.pbhost)
+ {
+ realpull = pb_RootOf(realpull);
+ tarzan = 2; // enforce tarzan
+ }
+
+ if(tarzan)
{
- v = v0 = WarpZone_RefSys_TransformVelocity(self.realowner, self, self.realowner.velocity);
+ v = v0 = WarpZone_RefSys_TransformVelocity(realpull, self, realpull.velocity);
// first pull the rope...
if(self.realowner.hook_state & HOOK_PULLING)
self.hook_length = newlength;
}
+ if(realpull.movetype == MOVETYPE_FLY)
+ realpull.movetype = MOVETYPE_WALK;
+
if(self.realowner.hook_state & HOOK_RELEASING)
{
newlength = dist;
v = v + frametime * dir * spd * rubberforce;
dv = ((v - v0) * dir) * dir;
- if(autocvar_g_grappling_hook_tarzan >= 2)
+ if(tarzan >= 2)
{
if(self.aiment.movetype == MOVETYPE_WALK)
{
+ entity aim_ent = ((IS_VEHICLE(self.aiment) && self.aiment.owner) ? self.aiment.owner : self.aiment);
v = v - dv * 0.5;
- self.aiment.velocity = self.aiment.velocity - dv * 0.5;
- self.aiment.flags &= ~FL_ONGROUND;
- self.aiment.pusher = self.realowner;
- self.aiment.pushltime = time + autocvar_g_maxpushtime;
- self.aiment.istypefrag = self.aiment.BUTTON_CHAT;
+ if((frozen_pulling && self.aiment.frozen) || !frozen_pulling)
+ if(self.aiment.vehicleid != VEH_TANKLL48)
+ {
+ self.aiment.velocity = self.aiment.velocity - dv * 0.5;
+ self.aiment.flags &= ~FL_ONGROUND;
+ }
+ aim_ent.pusher = self.realowner;
+ aim_ent.pushltime = time + autocvar_g_maxpushtime;
+ aim_ent.istypefrag = aim_ent.BUTTON_CHAT;
}
}
- self.realowner.flags &= ~FL_ONGROUND;
+ realpull.flags &= ~FL_ONGROUND;
}
- self.realowner.velocity = WarpZone_RefSys_TransformVelocity(self, self.realowner, v);
+ if(!frozen_pulling)
+ realpull.velocity = WarpZone_RefSys_TransformVelocity(self, realpull, ((realpull == self.realowner) ? v : (v * autocvar_g_balance_grapplehook_piggybackfriction)));
+
+ if(frozen_pulling && autocvar_g_balance_grapplehook_pull_frozen == 2 && !self.aiment.frozen)
+ {
+ RemoveGrapplingHook(self.realowner);
+ return;
+ }
}
else
{
self.SendFlags |= 4;
self.hook_end = org;
}
+ entity dmgent = ((SAME_TEAM(self.owner, self.aiment) && autocvar_g_rm_hook_team) ? self.owner : self.aiment);
+
+ if(IS_PLAYER(self.aiment))
+ if(self.last_dmg < time)
+ if(!self.aiment.frozen)
+ if(time >= game_starttime)
+ if(DIFF_TEAM(self.owner, self.aiment) || autocvar_g_rm_hook_team)
+ if(self.aiment.health > 0)
+ if(autocvar_g_rm || autocvar_g_rm_hook_damage_always)
+ if(autocvar_g_rm_hook_damage)
+ {
+ self.last_dmg = time + autocvar_g_rm_hook_damagefactor;
+ self.owner.damage_dealt += autocvar_g_rm_hook_damage;
+ Damage(dmgent, self, self.owner, autocvar_g_rm_hook_damage, WEP_HOOK, self.origin, '0 0 0');
+ if(SAME_TEAM(self.owner, self.aiment))
+ self.aiment.health = min(self.aiment.health + autocvar_g_rm_hook_damage_health, g_pickup_healthsmall_max);
+ else
+ self.owner.health = min(self.owner.health + autocvar_g_rm_hook_damage_health, g_pickup_healthsmall_max);
+
+ if(dmgent == self.owner)
+ dmgent.health -= autocvar_g_rm_hook_damage; // FIXME: friendly fire?!
+ }
}
void GrapplingHookTouch (void)
{
+ if(other.movetype == MOVETYPE_FOLLOW)
+ return;
PROJECTILE_TOUCH;
GrapplingHook_Stop();
if(self.health <= 0)
return;
+ if(!autocvar_g_rm_hook_breakable)
+ return;
+
+ if(!autocvar_g_rm_hook_breakable_owner && attacker == self.realowner)
+ return;
+
+ if(DIFF_TEAM(frag_attacker, self.realowner))
+ {
+ Damage (self.realowner, attacker, attacker, 5, WEP_HOOK | HITTYPE_SPLASH, self.realowner.origin, '0 0 0');
+ RemoveGrapplingHook(self.realowner);
+ return; // dead
+ }
+
if (!W_CheckProjectileDamage(inflictor.realowner, self.realowner, deathtype, -1)) // no exceptions
return; // g_balance_projectiledamage says to halt
float s;
vector vs;
- if(forbidWeaponUse()) return;
- if(self.vehicle) return;
+ if((time < game_starttime && !autocvar_sv_ready_restart_after_countdown) || self.vehicle || Player_Trapped(self))
+ return;
makevectors(self.v_angle);
vs = hook_shotorigin[s];
// UGLY WORKAROUND: play this on CH_WEAPON_B so it can't cut off fire sounds
- sound (self, CH_WEAPON_B, "weapons/hook_fire.wav", VOL_BASE, ATTEN_NORM);
+ sound (self, CH_WEAPON_B, W_Sound("hook_fire"), VOL_BASE, ATTEN_NORM);
org = self.origin + self.view_ofs + v_forward * vs_x + v_right * -vs_y + v_up * vs_z;
tracebox(self.origin + self.view_ofs, '-3 -3 -3', '3 3 3', org, MOVE_NORMAL, self);
org = trace_endpos;
- pointparticles(particleeffectnum("grapple_muzzleflash"), org, '0 0 0', 1);
+ Send_Effect(EFFECT_HOOK_MUZZLEFLASH, org, '0 0 0', 1);
missile = WarpZone_RefSys_SpawnSameRefSys(self);
missile.owner = missile.realowner = self;
missile.classname = "grapplinghook";
missile.flags = FL_PROJECTILE;
- missile.movetype = MOVETYPE_FLY;
+ missile.movetype = ((autocvar_g_balance_grapplehook_gravity) ? MOVETYPE_TOSS : MOVETYPE_FLY);
PROJECTILE_MAKETRIGGER(missile);
//setmodel (missile, "models/hook.md3"); // precision set below
void GrapplingHookFrame()
{
- if(g_grappling_hook && timeout_status != TIMEOUT_ACTIVE && self.weapon != WEP_HOOK)
+ if(g_grappling_hook && timeout_status != TIMEOUT_ACTIVE && self.weapon != WEP_HOOK && !self.vehicle)
{
// offhand hook controls
if(self.BUTTON_HOOK)
}
self.hook_state &= ~HOOK_RELEASING;
- if(self.BUTTON_CROUCH)
+ if(self.BUTTON_CROUCH && autocvar_g_balance_grapplehook_crouchslide)
{
self.hook_state &= ~HOOK_PULLING;
//self.hook_state |= HOOK_RELEASING;
//self.hook_state &= ~HOOK_RELEASING;
}
}
- else if(!g_grappling_hook && self.switchweapon != WEP_HOOK)
+ else if(!g_grappling_hook && self.switchweapon != WEP_HOOK && !self.vehicle)
{
if(self.BUTTON_HOOK && !self.hook_switchweapon)
W_SwitchWeapon(WEP_HOOK);
// do the trace
if(wz)
- WarpZone_TraceBox (v1, mi, ma, v2, nomonst, forent);
+ WarpZone_TraceBox (v1, mi, ma, v2, nomonst, world);
else
- tracebox (v1, mi, ma, v2, nomonst, forent);
+ tracebox (v1, mi, ma, v2, nomonst, world);
// restore players to current positions
if (lag)
+++ /dev/null
-/*
-
-Installation:
-
-compile with -DTETRIS
-
-*/
-
-#ifdef TETRIS
-
-.vector tet_org;
-
-float tet_vs_current_id;
-float tet_vs_current_timeout;
-.float tet_vs_id, tet_vs_addlines;
-.float tet_highest_line;
-.float tetris_on, tet_gameovertime, tet_drawtime, tet_autodown;
-.vector piece_pos;
-.float piece_type, next_piece, tet_score, tet_lines;
-.float tet_piece_bucket;
-
-// tetris_on states:
-// 1 = running
-// 2 = game over
-// 3 = waiting for VS players
-
-var float tet_high_score = 0;
-
-const vector TET_START_PIECE_POS = '5 1 0';
-const float TET_LINES = 22;
-const float TET_DISPLAY_LINES = 20;
-const float TET_WIDTH = 10;
-const string TET_EMPTY_LINE = "0000000000"; // must match TET_WIDTH
-//character values
-const float TET_BORDER = 139;
-const float TET_BLOCK = 133;
-const float TET_SPACE = 160; // blankness
-
-
-
-const float TETKEY_UP = 1;
-const float TETKEY_DOWN = 2;
-const float TETKEY_LEFT = 4;
-const float TETKEY_RIGHT = 8;
-const float TETKEY_ROTLEFT = 16;
-const float TETKEY_ROTRIGHT = 32;
-const float TETKEY_DROP = 64;
-const string TET_PADDING_RIGHT = "\xA0\xA0\xA0\xA0\xA0\xA0\xA0\xA0\xA0\xA0\xA0\xA0\xA0\xA0"; // get away from crosshair
-
-const float PIECES = 7;
-
-float tet_line_buf;
-
-float SVC_CENTERPRINTa = 26;
-
-float Tetris_Level()
-{
- return ((floor((self.tet_lines / 10)) + 1));
-}
-
-void tetsnd(string snd)
-{
- play2(self, strcat("sounds/tetris/", snd));
-}
-
-/*
-*********************************
-
-Library Functions
-
-*********************************
-*/
-void SetLine(float ln, string vl)
-{
- if(ln < 1 || ln > TET_LINES)
- error("WTF");
- bufstr_set(tet_line_buf, ln + TET_LINES * num_for_edict(self), vl);
-}
-
-string GetLine(float ln)
-{
- if(ln < 1 || ln > TET_LINES)
- error("WTF");
- if(ln < 1 || ln > TET_LINES)
- return TET_EMPTY_LINE;
- return bufstr_get(tet_line_buf, ln + TET_LINES * num_for_edict(self));
-}
-
-float GetXBlock(float x, string dat)
-{
- if(x < 1 || x > TET_WIDTH)
- error("WTF");
- return stof(substring(dat, x-1, 1));
-}
-
-string SetXBlock(float x, string dat, float new)
-{
- return strcat(
- substring(dat, 0, x-1),
- ftos(new),
- substring(dat, x, -1)
- );
-}
-
-
-float GetSquare(float x, float y)
-{
- return GetXBlock(x, GetLine(y));
-}
-
-void SetSquare(float x, float y, float val)
-{
- string dat;
- dat = GetLine(y);
- dat = SetXBlock(x, dat, val);
- SetLine(y, dat);
-}
-
-float PieceColor(float pc)
-{
- if (pc == 1)
- return 3; // O
- else if (pc == 2)
- return 4; // J
- else if (pc == 3)
- return 7; // L // we don't have orange, let's use white instead!
- else if (pc == 4)
- return 5; // I
- else if (pc == 5)
- return 1; // Z
- else if (pc == 6)
- return 2; // S
- else if (pc == 7)
- return 6; // T
- else
- return 0;
-}
-vector PieceShape(float pc)
-{
- if (pc == 1)
- return '20 20 0'; // O
- else if (pc == 2)
- return '1 21 0'; // J
- else if (pc == 3)
- return '16 21 0'; // L
- else if (pc == 4)
- return '0 85 0'; // I
- else if (pc == 5)
- return '5 20 0'; // Z
- else if (pc == 6)
- return '20 5 0'; // S
- else if (pc == 7)
- return '4 21 0'; // T
- else
- return '0 0 0';
-}
-vector PieceSize(float pc)
-{
- if (pc == 1)
- return '2 2 0'; // O
- else if (pc == 2)
- return '3 2 0'; // J
- else if (pc == 3)
- return '3 2 0'; // L
- else if (pc == 4)
- return '4 1 0'; // I
- else if (pc == 5)
- return '3 2 0'; // Z
- else if (pc == 6)
- return '3 2 0'; // S
- else if (pc == 7)
- return '3 2 0'; // T
- else
- return '0 0 0';
-}
-vector PieceCenter(float pc)
-{
- if(pc == 1)
- return '2.5 1.5 0'; // O
- else if (pc == 2)
- return '2 2 0'; // J
- else if (pc == 3)
- return '2 2 0'; // L
- else if (pc == 4)
- return '2.5 2.5 0'; // I
- else if (pc == 5)
- return '2 2 0'; // Z
- else if (pc == 6)
- return '2 2 0'; // S
- else if (pc == 7)
- return '2 2 0'; // T
- else
- return '0 0 0';
-}
-
-// do x 1..4 and y 1..4 in case of rotation
-float PieceMetric(float x, float y, float rot, float pc)
-{
- float t;
- vector ce;
-
- // return bits of a piece
- ce = PieceCenter(pc);
- if (rot == 1) // 90 degrees
- {
- // x+cx, y+cy -> -y+cx, x+cy
- // X, Y -> -Y+cy+cx, X-cx+cy
- // x = X-cx
- // y = Y-cy
- t = y;
- y = x - ce_x + ce_y;
- x = -t + ce_x + ce_y;
- }
- else if (rot == 2)//180
- {
- x = 2 * ce_x - x;
- y = 2 * ce_y - y;
- }
- else if (rot == 3) // 270
- {
- // x+cx, y+cy -> y+cx, -x+cy
- // X, Y -> Y-cy+cx, -X+cx+cy
- // x = X-cx
- // y = Y-cy
- t = y;
- y = -x + ce_y + ce_x;
- x = t - ce_y + ce_x;
- }
- if (x < 1 || y < 1 || x > 4 || y > 2)
- return 0;
- ce = PieceShape(pc);
- if (y == 1)
- return !!(ce_x & pow(4, x-1)); // first row
- else if (y == 2)
- return !!(ce_y & pow(4, x-1)); // second row
- else
- return 0; // illegal parms
-}
-vector tet_piecemins;
-vector tet_piecemaxs;
-void PieceMinsMaxs(float rot, float pc)
-{
- vector sz, ce;
- float t;
- vector v;
-
- sz = PieceSize(pc);
- ce = PieceCenter(pc);
- // 1 = 2..2
- // 2 = 2..3
- // 3 = 1..3
- // 4 = 1..4
- tet_piecemins_x = floor(3.0 - sz_x * 0.5);
- tet_piecemaxs_x = floor(2.0 + sz_x * 0.5);
- if(sz_y == 1)
- {
- // special case for "I"
- tet_piecemins_y = tet_piecemaxs_y = 2;
- }
- else
- {
- tet_piecemins_y = 1;
- tet_piecemaxs_y = sz_y;
- }
- //printf("ce%v sz%v mi%v ma%v\n", ce, sz, tet_piecemins, tet_piecemaxs);
- if (rot == 1) // 90 degrees
- {
- t = tet_piecemins_y;
- tet_piecemins_y = -tet_piecemins_x + ce_y + ce_x;
- tet_piecemins_x = t - ce_y + ce_x;
- t = tet_piecemaxs_y;
- tet_piecemaxs_y = -tet_piecemaxs_x + ce_y + ce_x;
- tet_piecemaxs_x = t - ce_y + ce_x;
- // swap mins_y, maxs_y
- t = tet_piecemins_y;
- tet_piecemins_y = tet_piecemaxs_y;
- tet_piecemaxs_y = t;
- // TODO OPTIMIZE
- }
- else if (rot == 2)//180
- {
- v = tet_piecemins;
- tet_piecemins = 2 * ce - tet_piecemaxs;
- tet_piecemaxs = 2 * ce - v;
- }
- else if (rot == 3) // 270
- {
- t = tet_piecemins_y;
- tet_piecemins_y = tet_piecemins_x - ce_x + ce_y;
- tet_piecemins_x = -t + ce_x + ce_y;
- t = tet_piecemaxs_y;
- tet_piecemaxs_y = tet_piecemaxs_x - ce_x + ce_y;
- tet_piecemaxs_x = -t + ce_x + ce_y;
- // swap mins_x, maxs_x
- t = tet_piecemins_x;
- tet_piecemins_x = tet_piecemaxs_x;
- tet_piecemaxs_x = t;
- // TODO OPTIMIZE
- }
-#ifdef VERIFY
- print(vtos(tet_piecemins), "-");
- print(vtos(tet_piecemaxs), "\n");
- if(tet_piecemins_x > tet_piecemaxs_x)
- error("inconsistent mins/maxs");
- if(tet_piecemins_y > tet_piecemaxs_y)
- error("inconsistent mins/maxs");
- float i, j;
- vector realmins, realmaxs;
- realmins = '4 4 0';
- realmaxs = '1 1 0';
- for(i = 1; i <= 4; ++i)
- for(j = 1; j <= 4; ++j)
- if(PieceMetric(i, j, rot, pc))
- {
- realmins_x = min(realmins_x, i);
- realmins_y = min(realmins_y, j);
- realmaxs_x = max(realmaxs_x, i);
- realmaxs_y = max(realmaxs_y, j);
- }
- if(realmins != tet_piecemins || realmaxs != tet_piecemaxs)
- error(sprintf("incorrect mins/maxs: %v %v in %d rot %d mins %v maxs %v\n", realmins, realmaxs, rot, pc, tet_piecemins, tet_piecemaxs));
-#endif
-}
-/*
-*********************************
-
-Draw
-
-*********************************
-*/
-
-
-/* some prydon gate functions to make life easier....
-
-somewhat modified because we don't need all the fanciness Prydon Gate is capable of
-
-*/
-
-void WriteTetrisString(string s)
-{
- WriteUnterminatedString(MSG_ONE, strconv(0, 2, 2, s));
-}
-
-float pnum(float num, float dig)
-{
- float f, i;
- if (num < 0)
- {
- WriteChar(MSG_ONE, 173);
- num = 0 - num;
- }
- f = floor(num / 10);
- num = num - (f * 10);
- if (f)
- dig = pnum(f, dig+1);
- else
- {
- // pad to 6
- for (i = 0; i < (5 - dig); i = i + 1)
- WriteChar(MSG_ONE, TET_SPACE);
- }
- WriteChar(MSG_ONE, 176 + num);
- return dig;
-}
-
-void DrawLine(float ln)
-{
- float x, d;
- WriteChar(MSG_ONE, TET_BORDER);
-
- for (x = 1; x <= TET_WIDTH; x = x + 1)
- {
- d = GetSquare(x, ln + TET_LINES - TET_DISPLAY_LINES);
- if (d)
- {
- WriteChar(MSG_ONE, '^');
- WriteChar(MSG_ONE, d + '0');
- WriteChar(MSG_ONE, TET_BLOCK);
- }
- else
- WriteChar(MSG_ONE, TET_SPACE);
- }
- WriteChar(MSG_ONE, '^');
- WriteChar(MSG_ONE, '7');
- WriteChar(MSG_ONE, TET_BORDER);
-}
-
-void DrawPiece(float pc, float ln)
-{
- float x, d, piece_ln, pcolor;
- vector piece_dat;
- pcolor = PieceColor(pc);
- WriteChar(MSG_ONE, TET_SPACE); // pad to 6
-
- piece_dat = PieceShape(pc);
- if (ln == 1)
- piece_ln = piece_dat_x;
- else
- piece_ln = piece_dat_y;
- for (x = 1; x <= 4; x = x + 1)
- {
- if (piece_ln & pow(4, x-1))
- {
- WriteChar(MSG_ONE, '^');
- WriteChar(MSG_ONE, pcolor + '0');
- WriteChar(MSG_ONE, TET_BLOCK);
- }
- else
- WriteChar(MSG_ONE, TET_SPACE);
- }
- WriteChar(MSG_ONE, TET_SPACE); // pad to 6
-}
-void Draw_Tetris()
-{
- float i;
- entity head;
- msg_entity = self;
- WriteChar(MSG_ONE, SVC_CENTERPRINTa);
- if(autocvar_g_bastet)
- {
- WriteTetrisString("NEVER GONNA GIVE YOU");
- WriteChar(MSG_ONE, 10);
- }
- // decoration
- for (i = 1; i <= (TET_WIDTH + 2); i = i + 1)
- WriteChar(MSG_ONE, TET_BORDER);
- WriteTetrisString(" ");
- WriteUnterminatedString(MSG_ONE, TET_PADDING_RIGHT);
- WriteChar(MSG_ONE, 10);
- for (i = 1; i <= TET_DISPLAY_LINES; i = i + 1)
- {
- if(self.tetris_on == 2)
- WriteTetrisString(" GAME OVER ");
- else if(self.tetris_on == 3)
- WriteTetrisString("PLEASE WAIT");
- else
- DrawLine(i);
- if (i == 1)
- WriteTetrisString(autocvar_g_bastet ? " THAT " : " NEXT ");
- else if (i == 3)
- DrawPiece(self.next_piece, 1);
- else if (i == 4)
- DrawPiece(self.next_piece, 2);
- else if (i == 6)
- WriteTetrisString(" LINES");
- else if (i == 7)
- pnum(self.tet_lines, 0);
- else if (i == 9)
- WriteTetrisString(" SCORE");
- else if (i == 10)
- pnum(self.tet_score, 0);
- else if (i == 12)
- WriteTetrisString(" HIGH ");
- else if (i == 13)
- WriteTetrisString(" SCORE");
- else if (i == 14)
- pnum(tet_high_score, 0);
- else if (i == 16)
- WriteTetrisString(" LEVEL");
- else if (i == 17)
- pnum(Tetris_Level(), 0);
- else
- WriteTetrisString(" ");
- WriteUnterminatedString(MSG_ONE, TET_PADDING_RIGHT);
- WriteChar(MSG_ONE, 10);
- }
- // decoration
-
- for (i = 1; i <= (TET_WIDTH + 2); i = i + 1)
- WriteChar(MSG_ONE, TET_BORDER);
- WriteTetrisString(" ");
- WriteUnterminatedString(MSG_ONE, TET_PADDING_RIGHT);
- WriteChar(MSG_ONE, 10);
-
- // VS game status
- if(self.tet_vs_id)
- {
- WriteChar(MSG_ONE, 10);
- WriteChar(MSG_ONE, 10);
- if(self.tetris_on == 3)
- {
- WriteUnterminatedString(MSG_ONE, strcat("WAITING FOR OTHERS (", ftos(ceil(tet_vs_current_timeout - time)), " SEC)\n"));
- }
-
- WriteChar(MSG_ONE, 10);
- FOR_EACH_REALCLIENT(head) if(head.tetris_on) if(head.tet_vs_id == self.tet_vs_id)
- {
- if(head == self)
- WriteUnterminatedString(MSG_ONE, ">");
- else
- WriteUnterminatedString(MSG_ONE, " ");
- if(head.tetris_on == 2)
- WriteUnterminatedString(MSG_ONE, " X_X");
- else
- pnum(head.tet_highest_line, 0);
- WriteUnterminatedString(MSG_ONE, " ");
- WriteUnterminatedString(MSG_ONE, head.netname);
- WriteChar(MSG_ONE, 10);
- }
- }
-
- WriteChar(MSG_ONE, 0);
-}
-/*
-*********************************
-
-Game Functions
-
-*********************************
-*/
-
-// reset the game
-void ResetTetris()
-{
- float i;
-
- if(!tet_line_buf)
- tet_line_buf = buf_create();
-
- for (i=1; i<=TET_LINES; i = i + 1)
- SetLine(i, TET_EMPTY_LINE);
- self.piece_pos = '0 0 0';
- self.piece_type = 0;
- self.next_piece = self.tet_lines = self.tet_score = 0;
- self.tet_piece_bucket = 0;
-}
-
-void Tet_GameExit()
-{
- centerprint(self, " ");
- self.tetris_on = 0;
- self.tet_vs_id = 0;
- ResetTetris();
- self.movetype = MOVETYPE_WALK;
-}
-
-void PrintField()
-{
- string l;
- float r, c;
- for(r = 1; r <= TET_LINES; ++r)
- {
- l = GetLine(r);
- print(">");
- for(c = 1; c <= TET_WIDTH; ++c)
- {
- print(ftos(GetXBlock(c, l)));
- }
- print("\n");
- }
-}
-
-float BastetEvaluate()
-{
- float height;
- string l;
- float lines;
- float score, score_save;
- string occupied, occupied_save;
- float occupied_count, occupied_count_save;
- float i, j, line;
-
- score = 0;
-
- // adds a bonus for each free dot above the occupied blocks profile
- occupied = TET_EMPTY_LINE;
- occupied_count = TET_WIDTH;
- height = 0;
- lines = 0;
- for(i = 1; i <= TET_LINES; ++i)
- {
- l = GetLine(i);
- if(l == TET_EMPTY_LINE)
- {
- height = i;
- continue;
- }
- line = 1;
- occupied_save = occupied;
- occupied_count_save = occupied_count;
- score_save = score;
- for(j = 1; j <= TET_WIDTH; ++j)
- {
- if(GetXBlock(j, l))
- {
- if(!GetXBlock(j, occupied))
- {
- occupied = SetXBlock(j, occupied, 1);
- --occupied_count;
- }
- }
- else
- line = 0;
- score += 10000 * occupied_count;
- }
- if(line)
- {
- occupied = occupied_save;
- occupied_count = occupied_count_save;
- score = score_save + 100000000 + 10000 * TET_WIDTH + 1000;
- ++lines;
- }
- }
-
- score += 1000 * height;
-
- return score;
-}
-
-float CheckMetrics(float piece, float orgx, float orgy, float rot);
-void ClearPiece(float piece, float orgx, float orgy, float rot);
-void CementPiece(float piece, float orgx, float orgy, float rot);
-float bastet_profile_evaluate_time;
-float bastet_profile_checkmetrics_time;
-float BastetSearch(float buf, float pc, float x, float y, float rot, float move_bias)
-// returns best score, or -1 if position is impossible
-{
- string r;
- float b;
- float s, sm;
- float t1, t2;
-
- if(move_bias < 0)
- return 0; // DO NOT WANT
-
- if(x < 1 || x > TET_WIDTH || y < 1 || y > TET_LINES)
- return -1; // impossible
- if(rot < 0) rot = 3;
- if(rot > 3) rot = 0;
-
- // did we already try?
- b = x + (TET_WIDTH+2) * (y + (TET_LINES+2) * rot);
- r = bufstr_get(buf, b);
- if(r != "") // already tried
- return stof(r);
-
- bufstr_set(buf, b, "0"); // in case we READ that, not that bad - we already got that value in another branch then anyway
-
-
-
- t1 = gettime(GETTIME_HIRES);
- if(CheckMetrics(pc, x, y, rot))
- {
- t2 = gettime(GETTIME_HIRES);
- bastet_profile_checkmetrics_time += (t2 - t1);
- // try all moves
- sm = 1;
- s = BastetSearch(buf, pc, x-1, y, rot, move_bias - 1); if(s > sm) sm = s;
- s = BastetSearch(buf, pc, x+1, y, rot, move_bias - 1); if(s > sm) sm = s;
- s = BastetSearch(buf, pc, x, y, rot+1, move_bias - 1); if(s > sm) sm = s;
- s = BastetSearch(buf, pc, x, y, rot-1, move_bias - 1); if(s > sm) sm = s;
-
- s = BastetSearch(buf, pc, x, y+1, rot, move_bias + 2); if(s > sm) sm = s;
- if(s < 0)
- {
- //printf("MAY CEMENT AT: %d %d %d\n", x, y, rot);
- // moving down did not work - that means we can fixate the block here
- t1 = gettime(GETTIME_HIRES);
-
- CementPiece(pc, x, y, rot);
- s = BastetEvaluate();
- ClearPiece(pc, x, y, rot);
-
- t2 = gettime(GETTIME_HIRES);
- bastet_profile_evaluate_time += (t2 - t1);
-
- if(s > sm) sm = s;
- }
- }
- else
- {
- t2 = gettime(GETTIME_HIRES);
- bastet_profile_checkmetrics_time += (t2 - t1);
- sm = -1; // impassible
- }
-
- bufstr_set(buf, b, ftos(sm));
-
- return sm;
-}
-
-float bastet_piece[7];
-float bastet_score[7];
-float bastet_piecetime[7];
-float BastetPiece()
-{
- float b;
-
- bastet_profile_evaluate_time = 0;
- bastet_profile_checkmetrics_time = 0;
- var float t1 = gettime(GETTIME_HIRES);
-
- b = buf_create(); bastet_piece[0] = 1; bastet_score[0] = BastetSearch(b, 1, TET_START_PIECE_POS_x, 1+TET_START_PIECE_POS_y, TET_START_PIECE_POS_y, TET_WIDTH) + 100 * random() + bastet_piecetime[0]; buf_del(b);
- b = buf_create(); bastet_piece[1] = 2; bastet_score[1] = BastetSearch(b, 2, TET_START_PIECE_POS_x, 1+TET_START_PIECE_POS_y, TET_START_PIECE_POS_y, TET_WIDTH) + 100 * random() + bastet_piecetime[1]; buf_del(b);
- b = buf_create(); bastet_piece[2] = 3; bastet_score[2] = BastetSearch(b, 3, TET_START_PIECE_POS_x, 1+TET_START_PIECE_POS_y, TET_START_PIECE_POS_y, TET_WIDTH) + 100 * random() + bastet_piecetime[2]; buf_del(b);
- b = buf_create(); bastet_piece[3] = 4; bastet_score[3] = BastetSearch(b, 4, TET_START_PIECE_POS_x, 1+TET_START_PIECE_POS_y, TET_START_PIECE_POS_y, TET_WIDTH) + 100 * random() + bastet_piecetime[3]; buf_del(b);
- b = buf_create(); bastet_piece[4] = 5; bastet_score[4] = BastetSearch(b, 5, TET_START_PIECE_POS_x, 1+TET_START_PIECE_POS_y, TET_START_PIECE_POS_y, TET_WIDTH) + 100 * random() + bastet_piecetime[4]; buf_del(b);
- b = buf_create(); bastet_piece[5] = 6; bastet_score[5] = BastetSearch(b, 6, TET_START_PIECE_POS_x, 1+TET_START_PIECE_POS_y, TET_START_PIECE_POS_y, TET_WIDTH) + 100 * random() + bastet_piecetime[5]; buf_del(b);
- b = buf_create(); bastet_piece[6] = 7; bastet_score[6] = BastetSearch(b, 7, TET_START_PIECE_POS_x, 1+TET_START_PIECE_POS_y, TET_START_PIECE_POS_y, TET_WIDTH) + 100 * random() + bastet_piecetime[6]; buf_del(b);
-
- var float t2 = gettime(GETTIME_HIRES);
- dprintf("Time taken: %.6f seconds (of this, ev = %.2f%%, cm = %.2f%%)\n", t2 - t1, 100 * bastet_profile_evaluate_time / (t2 - t1), 100 * bastet_profile_checkmetrics_time / (t2 - t1));
-
- // sort
- float i, j, k, p, s;
-
-/*
- for(i = 0; i < 7; ++i)
- {
- printf("piece %s value = %d\n", substring("OJLIZST", bastet_piece[i]-1, 1), bastet_score[i]);
- }
-*/
-
- for(i = 0; i < 7; ++i)
- {
- k = i;
- p = bastet_piece[k];
- s = bastet_score[k];
- for(j = i + 1; j < 7; ++j)
- {
- if(bastet_score[j] < s)
- {
- k = j;
- s = bastet_score[k];
- p = bastet_piece[k];
- }
- }
- if(k != i)
- {
- bastet_score[k] = bastet_score[i];
- bastet_piece[k] = bastet_piece[i];
- bastet_score[i] = s;
- bastet_piece[i] = p;
- }
- }
-
- b = random();
- if(b < 0.8)
- j = 0;
- else if(b < 0.92)
- j = 1;
- else if(b < 0.98)
- j = 2;
- else
- j = 3;
- j = bastet_piece[j];
-
- for(i = 0; i < 7; ++i)
- {
- if(i == j-1)
- bastet_piecetime[i] = 0.2 * bastet_piecetime[i];
- else
- bastet_piecetime[i] = 1.8 * bastet_piecetime[i] + 1000;
- }
-
- return j;
-}
-
-
-/*
-*********************************
-
-Game Mechanics
-
-*********************************
-*/
-.float tet_piece_bucket;
-float RandomPiece()
-{
- float i, j;
- float p, q;
- float b;
- float seen;
-
- if(self.tet_piece_bucket > 1)
- {
- p = mod(self.tet_piece_bucket, 7);
- self.tet_piece_bucket = floor(self.tet_piece_bucket / 7);
- return p + 1;
- }
- else
- {
- p = floor(random() * 7);
- seen = pow(2, p);
- b = 1;
- for(i = 6; i > 0; --i)
- {
- q = floor(random() * i);
- for(j = 0; j <= q; ++j)
- if(seen & pow(2, j))
- ++q;
- if(seen & pow(2, q))
- error("foo 1");
- if(q >= 7)
- error("foo 2");
- seen |= pow(2, q);
- b *= 7;
- b += q;
- }
- self.tet_piece_bucket = b;
- return p + 1;
- }
-}
-
-void TetAddScore(float n)
-{
- self.tet_score = self.tet_score + n * Tetris_Level();
- if (self.tet_score > tet_high_score)
- tet_high_score = self.tet_score;
-}
-float CheckMetrics(float piece, float orgx, float orgy, float rot) /*FIXDECL*/
-{
- // check to see if the piece, if moved to the locations will overlap
-
- float x, y;
- string l;
- // why did I start counting from 1, damnit
- orgx = orgx - 1;
- orgy = orgy - 1;
-
- PieceMinsMaxs(rot, piece);
- if (tet_piecemins_x+orgx<1 || tet_piecemaxs_x+orgx > TET_WIDTH || tet_piecemins_y+orgy<1 || tet_piecemaxs_y+orgy> TET_LINES)
- return FALSE; // ouside the level
- for (y = tet_piecemins_y; y <= tet_piecemaxs_y; y = y + 1)
- {
- l = GetLine(y + orgy);
- if(l != TET_EMPTY_LINE)
- for (x = tet_piecemins_x; x <= tet_piecemaxs_x; x = x + 1)
- if (PieceMetric(x, y, rot, piece))
- if (GetXBlock(x + orgx, l))
- return FALSE; // uhoh, gonna hit something.
- }
- return TRUE;
-}
-
-void ClearPiece(float piece, float orgx, float orgy, float rot) /*FIXDECL*/
-{
- float x, y;
- // why did I start counting from 1, damnit
- orgx = orgx - 1;
- orgy = orgy - 1;
-
- PieceMinsMaxs(rot, piece);
- for (y = tet_piecemins_y; y <= tet_piecemaxs_y; y = y + 1)
- {
- for (x = tet_piecemins_x; x <= tet_piecemaxs_x; x = x + 1)
- {
- if (PieceMetric(x, y, rot, piece))
- {
- SetSquare(x + orgx, y + orgy, 0);
- }
- }
- }
-}
-void CementPiece(float piece, float orgx, float orgy, float rot) /*FIXDECL*/
-{
- float pcolor;
- float x, y;
- // why did I start counting from 1, damnit
- orgx = orgx - 1;
- orgy = orgy - 1;
-
- pcolor = PieceColor(piece);
-
- PieceMinsMaxs(rot, piece);
- for (y = tet_piecemins_y; y <= tet_piecemaxs_y; y = y + 1)
- {
- for (x = tet_piecemins_x; x <= tet_piecemaxs_x; x = x + 1)
- {
- if (PieceMetric(x, y, rot, piece))
- {
- SetSquare(x + orgx, y + orgy, pcolor);
- }
- }
- }
-}
-
-const float LINE_LOW = 349525;
-const float LINE_HIGH = 699050; // above number times 2
-
-void AddLines(float n)
-{
- entity head;
- if(!self.tet_vs_id)
- return;
- FOR_EACH_REALCLIENT(head) if(head != self) if(head.tetris_on) if(head.tet_vs_id == self.tet_vs_id)
- head.tet_vs_addlines += n;
-}
-
-void CompletedLines()
-{
- float y, cleared, added, pos, i;
- string ln;
-
- cleared = 0;
- y = TET_LINES;
- for(;;)
- {
- ln = GetLine(y);
- if(strstrofs(ln, "0", 0) < 0)
- cleared = cleared + 1;
- else
- y = y - 1;
- if(y < 1)
- break;
- if(y - cleared < 1)
- ln = TET_EMPTY_LINE;
- else
- ln = GetLine(y - cleared);
- SetLine(y, ln);
- }
-
- if(cleared >= 4)
- AddLines(cleared);
- else if(cleared >= 1)
- AddLines(cleared - 1);
-
- self.tet_lines = self.tet_lines + cleared;
- TetAddScore(cleared * cleared * 10);
-
- added = self.tet_vs_addlines;
- self.tet_vs_addlines = 0;
-
- if(added)
- {
- for(y = 1; y <= TET_LINES - added; ++y)
- {
- SetLine(y, GetLine(y + added));
- }
- for(y = max(1, TET_LINES - added + 1); y <= TET_LINES; ++y)
- {
- pos = floor(random() * TET_WIDTH);
- ln = TET_EMPTY_LINE;
- for(i = 1; i <= TET_WIDTH; ++i)
- if(i != pos)
- ln = SetXBlock(i, ln, floor(random() * 7 + 1));
- SetLine(y, ln);
- }
- }
-
- self.tet_highest_line = 0;
- for(y = 1; y <= TET_LINES; ++y)
- if(GetLine(y) != TET_EMPTY_LINE)
- {
- self.tet_highest_line = TET_LINES + 1 - y;
- break;
- }
-
- if(added)
- tetsnd("tetadd");
- else if(cleared >= 4)
- tetsnd("tetris");
- else if(cleared)
- tetsnd("tetline");
- else
- tetsnd("tetland");
-}
-
-void HandleGame(float keyss)
-{
-
- // first off, we need to see if we need a new piece
- vector piece_data;
- vector check_pos;
- vector old_pos;
- float brand_new;
- float i;
- brand_new = 0;
-
-
- if (self.piece_type == 0)
- {
- self.piece_pos = TET_START_PIECE_POS; // that's about middle top, we count from 1 ARGH
-
- if(autocvar_g_bastet)
- {
- self.piece_type = BastetPiece();
- self.next_piece = bastet_piece[6];
- }
- else
- {
- if (self.next_piece)
- self.piece_type = self.next_piece;
- else
- self.piece_type = RandomPiece();
- self.next_piece = RandomPiece();
- }
- keyss = 0; // no movement first frame
- self.tet_autodown = time + 0.2;
- brand_new = 1;
- }
- else
- ClearPiece(self.piece_type, self.piece_pos_x, self.piece_pos_y, self.piece_pos_z);
-
- // next we need to check the piece metrics against what's on the level
- // based on the key order
-
- old_pos = check_pos = self.piece_pos;
-
- float nudge;
- nudge = 0;
- if (keyss & TETKEY_RIGHT)
- {
- check_pos_x = check_pos_x + 1;
- tetsnd("tetmove");
- }
- else if (keyss & TETKEY_LEFT)
- {
- check_pos_x = check_pos_x - 1;
- tetsnd("tetmove");
- }
- else if (keyss & TETKEY_ROTRIGHT)
- {
- check_pos_z = check_pos_z + 1;
- piece_data = PieceShape(self.piece_type);
- nudge = 1;
- tetsnd("tetrot");
- }
- else if (keyss & TETKEY_ROTLEFT)
- {
- check_pos_z = check_pos_z - 1;
- piece_data = PieceShape(self.piece_type);
- nudge = 1;
- tetsnd("tetrot");
- }
- // bounds check
- if (check_pos_z > 3)
- check_pos_z = 0;
- else if (check_pos_z < 0)
- check_pos_z = 3;
-
- // reality check
- if (CheckMetrics(self.piece_type, check_pos_x, check_pos_y, check_pos_z))
- self.piece_pos = check_pos;
- else if (brand_new)
- {
- self.tetris_on = 2;
- self.tet_gameovertime = time + 5;
- return;
- }
- else
- {
- for(i = 1; i <= nudge; ++i)
- {
- if(CheckMetrics(self.piece_type, check_pos_x + i, check_pos_y, check_pos_z))
- self.piece_pos = check_pos + '1 0 0' * i;
- else if(CheckMetrics(self.piece_type, check_pos_x - i, check_pos_y, check_pos_z))
- self.piece_pos = check_pos - '1 0 0' * i;
- else
- continue;
- break;
- }
- }
- check_pos = self.piece_pos;
- if(keyss & TETKEY_DROP)
- {
- // drop to bottom, but do NOT cement it yet
- // this allows sliding it
- ++check_pos_y;
- while(CheckMetrics(self.piece_type, check_pos_x, check_pos_y + 1, check_pos_z))
- ++check_pos_y;
- self.tet_autodown = time + 2 / (1 + Tetris_Level());
- }
- else if (keyss & TETKEY_DOWN)
- {
- check_pos_y = check_pos_y + 1;
- self.tet_autodown = time + 2 / (1 + Tetris_Level());
- }
- else if (self.tet_autodown < time)
- {
- check_pos_y = check_pos_y + 1;
- self.tet_autodown = time + 2 / (1 + Tetris_Level());
- }
- if (CheckMetrics(self.piece_type, check_pos_x, check_pos_y, check_pos_z))
- {
- if(old_pos != check_pos)
- self.tet_drawtime = 0;
- self.piece_pos = check_pos;
- }
- else
- {
- CementPiece(self.piece_type, self.piece_pos_x, self.piece_pos_y, self.piece_pos_z);
- TetAddScore(1);
- CompletedLines();
- self.piece_type = 0;
- self.tet_drawtime = 0;
- return;
- }
- CementPiece(self.piece_type, self.piece_pos_x, self.piece_pos_y, self.piece_pos_z);
-}
-
-/*
-*********************************
-
-Important Linking Into Quake stuff
-
-*********************************
-*/
-
-
-void TetrisImpulse()
-{
- if(self.tetris_on == 0 || self.tetris_on == 2) // from "off" or "game over"
- {
- self.tetris_on = 3;
-
- if(time < tet_vs_current_timeout)
- {
- // join VS game
- self.tet_vs_id = tet_vs_current_id;
- }
- else
- {
- // start new VS game
- ++tet_vs_current_id;
- tet_vs_current_timeout = time + 15;
- self.tet_vs_id = tet_vs_current_id;
- bprint("^1TET^7R^1IS: ", self.netname, "^1 started a new game. Do ^7impulse 100^1 to join.\n");
- }
- self.tet_highest_line = 0;
- ResetTetris();
- self.tet_org = self.origin;
- self.movetype = MOVETYPE_NOCLIP;
- }
- else if(self.tetris_on == 1) // from "on"
- {
- Tet_GameExit();
- self.impulse = 0;
- }
-}
-
-
-float TetrisPreFrame()
-{
- if (!self.tetris_on)
- return 0;
-
- self.tet_org = self.origin;
- if (self.tet_drawtime > time)
- return 1;
- Draw_Tetris();
- if(self.tetris_on == 3)
- self.tet_drawtime = ceil(time - tet_vs_current_timeout + 0.1) + tet_vs_current_timeout;
- else
- self.tet_drawtime = time + 0.5;
- return 1;
-}
-float frik_anglemoda(float v)
-{
- return v - floor(v/360) * 360;
-}
-float angcompa(float y1, float y2)
-{
- y1 = frik_anglemoda(y1);
- y2 = frik_anglemoda(y2);
-
- float answer;
- answer = y1 - y2;
- if (answer > 180)
- answer = answer - 360;
- else if (answer < -180)
- answer = answer + 360;
- return answer;
-}
-
-.float tetkey_down, tetkey_rotright, tetkey_left, tetkey_right, tetkey_rotleft, tetkey_drop;
-
-float TetrisKeyRepeat(.float fld, float f)
-{
- if(f)
- {
- if(self.fld == 0) // initial key press
- {
- self.fld = time + 0.3;
- return 1;
- }
- else if(time > self.fld)
- {
- self.fld = time + 0.1;
- return 1;
- }
- else
- {
- // repeating too fast
- return 0;
- }
- }
- else
- {
- self.fld = 0;
- return 0;
- }
-}
-
-float TetrisPostFrame()
-{
- float keysa;
-
- keysa = 0;
-
- if (!self.tetris_on)
- return 0;
-
- if(self.tetris_on == 2 && time > self.tet_gameovertime)
- {
- Tet_GameExit();
- return 0;
- }
-
- if(self.tetris_on == 3 && time > tet_vs_current_timeout)
- {
- self.tetris_on = 1; // start VS game
- self.tet_drawtime = 0;
- }
-
- setorigin(self, self.tet_org);
- self.movetype = MOVETYPE_NONE;
-
- if(self.tetris_on == 1)
- {
- if(TetrisKeyRepeat(tetkey_down, self.movement_x < 0))
- keysa |= TETKEY_DOWN;
-
- if(TetrisKeyRepeat(tetkey_rotright, self.movement_x > 0))
- keysa |= TETKEY_ROTRIGHT;
-
- if(TetrisKeyRepeat(tetkey_left, self.movement_y < 0))
- keysa |= TETKEY_LEFT;
-
- if(TetrisKeyRepeat(tetkey_right, self.movement_y > 0))
- keysa |= TETKEY_RIGHT;
-
- if(TetrisKeyRepeat(tetkey_rotleft, self.BUTTON_CROUCH))
- keysa |= TETKEY_ROTLEFT;
-
- if(TetrisKeyRepeat(tetkey_drop, self.BUTTON_JUMP))
- keysa |= TETKEY_DROP;
-
- HandleGame(keysa);
- }
-
- return 1;
-}
-
-#else
-
-float TetrisPostFrame()
-{
- if (autocvar_g_bastet)
- {
- cvar_set("g_bastet", "0");
- print("The useless cvar has been toggled.\n");
- }
- return 0;
-}
-
-#endif
remove(self);
}
+float foreachtarget_callid;
+.float targethandled;
+void SUB_ForEachTarget(entity s, void(entity, float, vector, string, entity) cback, float recursive, float fdata, vector vdata, string sdata, entity edata)
+{
+ float i;
+ string targname = "";
+ entity targ;
+
+ for(i = 0; i < 4; ++i)
+ {
+ switch(i)
+ {
+ case 0: targname = s.target ; break;
+ case 1: targname = s.target2; break;
+ case 2: targname = s.target3; break;
+ case 3: targname = s.target4; break;
+ }
+
+ if(targname != "")
+ {
+ for(targ = world; (targ = find(targ, targetname, targname));)
+ if(targ && targ.use && targ.targethandled != foreachtarget_callid)
+ {
+ cback(targ, fdata, vdata, sdata, edata);
+ targ.targethandled = foreachtarget_callid;
+
+ if(recursive)
+ SUB_ForEachTarget(targ, cback, recursive, fdata, vdata, sdata, edata);
+ }
+ }
+ }
+}
+
+void SUB_ForEachTarget_Init() { ++foreachtarget_callid; }
+
/*
==============================
SUB_UseTargets
==============================
*/
-void SUB_UseTargets()
+void SUB_UseTargets_Ex(float preventReuse)
{
entity t, stemp, otemp, act;
string s;
if (s != "")
{
for(t = world; (t = find(t, targetname, s)); )
- if(t.use)
+ if(t.use && (t.sub_target_used != time || !preventReuse))
{
if(stemp.target_random)
{
other = stemp;
activator = act;
self.use();
+ if(preventReuse)
+ self.sub_target_used = time;
}
}
}
other = stemp;
activator = act;
self.use();
+ if(preventReuse)
+ self.sub_target_used = time;
}
activator = act;
other = otemp;
}
+void SUB_UseTargets() { SUB_UseTargets_Ex(FALSE); }
+void SUB_UseTargets_PreventReuse() { SUB_UseTargets_Ex(TRUE); }
+
//=============================================================================
BADPREFIX("sv_weaponstats_");
BADPREFIX("sv_waypointsprite_");
BADCVAR("rescan_pending");
+ BADPREFIX("sv_allow_customplayermodels");
// these can contain player IDs, so better hide
BADPREFIX("g_forced_team_");
BADCVAR("g_domination_default_teams");
BADCVAR("g_freezetag");
BADCVAR("g_freezetag_teams");
+ BADCVAR("g_conquest");
+ BADCVAR("g_conquest_teams");
+ BADCVAR("g_vip");
+ BADCVAR("g_vip_teams");
+ BADCVAR("g_jailbreak");
+ BADCVAR("g_jailbreak_teams");
+ BADCVAR("g_infection");
+ BADCVAR("g_infection_teams");
+ BADCVAR("g_invasion");
+ BADCVAR("g_invasion_teams");
BADCVAR("g_keepaway");
BADCVAR("g_keyhunt");
BADCVAR("g_keyhunt_teams");
BADCVAR("g_ctf_ignore_frags");
BADCVAR("g_domination_point_limit");
BADCVAR("g_freezetag_teams_override");
+ BADCVAR("g_conquest_teams_override");
+ BADCVAR("g_jailbreak_teams_override");
BADCVAR("g_friendlyfire");
BADCVAR("g_fullbrightitems");
BADCVAR("g_fullbrightplayers");
// 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(RegisterBuffs);
+ CALL_ACCUMULATED_FUNCTION(RegisterEffects);
MapInfo_Enumerate();
MapInfo_FilterGametype(MapInfo_CurrentGametype(), MapInfo_CurrentFeatures(), MapInfo_RequiredFlags(), MapInfo_ForbiddenFlags(), 0);
// 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(RegisterBuffs);
+ CALL_ACCUMULATED_FUNCTION(RegisterEffects);
+
+ initialize_minigames();
ServerProgsDB = db_load(strcat("server.db", autocvar_sessionid));
if(autocvar_g_norecoil)
s = strcat(s, ":norecoil");
- // TODO to mutator system
- if(autocvar_g_powerups == 0)
- s = strcat(s, ":no_powerups");
- if(autocvar_g_powerups > 0)
- s = strcat(s, ":powerups");
-
GameLogEcho(s);
GameLogEcho(":gameinfo:end");
}
WeaponStats_Init();
WepSet_AddStat();
+ WepSet_AddStat_InMap();
addstat(STAT_SWITCHWEAPON, AS_INT, switchweapon);
addstat(STAT_SWITCHINGWEAPON, AS_INT, switchingweapon);
addstat(STAT_GAMESTARTTIME, AS_FLOAT, stat_game_starttime);
addstat(STAT_ALLOW_OLDVORTEXBEAM, AS_INT, stat_allow_oldvortexbeam);
Nagger_Init();
- addstat(STAT_STRENGTH_FINISHED, AS_FLOAT, strength_finished);
- addstat(STAT_INVINCIBLE_FINISHED, AS_FLOAT, invincible_finished);
addstat(STAT_SUPERWEAPONS_FINISHED, AS_FLOAT, superweapons_finished);
addstat(STAT_PRESSED_KEYS, AS_FLOAT, pressedkeys);
addstat(STAT_FUEL, AS_INT, ammo_fuel);
addstat(STAT_PLASMA, AS_INT, ammo_plasma);
+ addstat(STAT_SUPERCELLS, AS_INT, ammo_supercells);
addstat(STAT_SHOTORG, AS_INT, stat_shotorg);
addstat(STAT_LEADLIMIT, AS_FLOAT, stat_leadlimit);
addstat(STAT_WEAPON_CLIPLOAD, AS_INT, clip_load);
addstat(STAT_MOVEVARS_AIRACCEL_QW, AS_FLOAT, stat_sv_airaccel_qw);
addstat(STAT_MOVEVARS_AIRSTRAFEACCEL_QW, AS_FLOAT, stat_sv_airstrafeaccel_qw);
+ // new properties
+ addstat(STAT_MOVEVARS_JUMPVELOCITY, AS_FLOAT, stat_sv_jumpvelocity);
+ addstat(STAT_MOVEVARS_AIRACCEL_QW_STRETCHFACTOR, AS_FLOAT, stat_sv_airaccel_qw_stretchfactor);
+ addstat(STAT_MOVEVARS_MAXAIRSTRAFESPEED, AS_FLOAT, stat_sv_maxairstrafespeed);
+ addstat(STAT_MOVEVARS_MAXAIRSPEED, AS_FLOAT, stat_sv_maxairspeed);
+ addstat(STAT_MOVEVARS_AIRSTRAFEACCELERATE, AS_FLOAT, stat_sv_airstrafeaccelerate);
+ addstat(STAT_MOVEVARS_WARSOWBUNNY_TURNACCEL, AS_FLOAT, stat_sv_warsowbunny_turnaccel);
+ addstat(STAT_MOVEVARS_AIRACCEL_SIDEWAYS_FRICTION, AS_FLOAT, stat_sv_airaccel_sideways_friction);
+ addstat(STAT_MOVEVARS_AIRCONTROL, AS_FLOAT, stat_sv_aircontrol);
+ addstat(STAT_MOVEVARS_AIRCONTROL_POWER, AS_FLOAT, stat_sv_aircontrol_power);
+ addstat(STAT_MOVEVARS_AIRCONTROL_PENALTY, AS_FLOAT, stat_sv_aircontrol_penalty);
+ addstat(STAT_MOVEVARS_WARSOWBUNNY_AIRFORWARDACCEL, AS_FLOAT, stat_sv_warsowbunny_airforwardaccel);
+ addstat(STAT_MOVEVARS_WARSOWBUNNY_TOPSPEED, AS_FLOAT, stat_sv_warsowbunny_topspeed);
+ addstat(STAT_MOVEVARS_WARSOWBUNNY_ACCEL, AS_FLOAT, stat_sv_warsowbunny_accel);
+ addstat(STAT_MOVEVARS_WARSOWBUNNY_BACKTOSIDERATIO, AS_FLOAT, stat_sv_warsowbunny_backtosideratio);
+ addstat(STAT_MOVEVARS_FRICTION, AS_FLOAT, stat_sv_friction);
+ addstat(STAT_MOVEVARS_ACCELERATE, AS_FLOAT, stat_sv_accelerate);
+ addstat(STAT_MOVEVARS_STOPSPEED, AS_FLOAT, stat_sv_stopspeed);
+ addstat(STAT_MOVEVARS_AIRACCELERATE, AS_FLOAT, stat_sv_airaccelerate);
+ addstat(STAT_MOVEVARS_AIRSTOPACCELERATE, AS_FLOAT, stat_sv_airstopaccelerate);
+
// secrets
addstat(STAT_SECRETS_TOTAL, AS_FLOAT, stat_secrets_total);
addstat(STAT_SECRETS_FOUND, AS_FLOAT, stat_secrets_found);
// misc
addstat(STAT_RESPAWN_TIME, AS_FLOAT, stat_respawn_time);
+ addstat(STAT_DISCO_MODE, AS_INT, discomode);
next_pingtime = time + 5;
RandomSeed_Spawn();
PingPLReport_Spawn();
+ if(!g_jailbreak)
+ InitializeEntity(world, JB_NonJBInit, INITPRIO_LAST);
+
CheatInit();
localcmd("\n_sv_hook_gamestart ", GetGametype(), "\n");
remove(self);
}
-string GetGametype()
-{
- return MapInfo_Type_ToString(MapInfo_LoadedGametype);
-}
+string GetGametype() { return MapInfo_Type_ToString(MapInfo_LoadedGametype); }
-string GetMapname()
-{
- return mapname;
-}
+string GetMapname() { return mapname; }
float Map_Count, Map_Current;
string Map_Current_Name;
float server_screenshot = (autocvar_sv_autoscreenshot && self.cvar_cl_autoscreenshot);
float client_screenshot = (self.cvar_cl_autoscreenshot == 2);
+ CSQCMODEL_AUTOUPDATE(); // we don't need to keep checking this after mapvote has started, as by then nothing can happen
+
if( (server_screenshot || client_screenshot)
&& ((self.autoscreenshot > 0) && (time > self.autoscreenshot)) )
{
head.winning = 0;
}
-// Onslaught winning condition:
-// game terminates if only one team has a working generator (or none)
-float WinningCondition_Onslaught()
-{
- entity head;
- float t1, t2, t3, t4;
-
- WinningConditionHelper(); // set worldstatus
-
- if(warmup_stage)
- return WINNING_NO;
-
- // first check if the game has ended
- t1 = t2 = t3 = t4 = 0;
- head = find(world, classname, "onslaught_generator");
- while (head)
- {
- if (head.health > 0)
- {
- if (head.team == NUM_TEAM_1) t1 = 1;
- if (head.team == NUM_TEAM_2) t2 = 1;
- if (head.team == NUM_TEAM_3) t3 = 1;
- if (head.team == NUM_TEAM_4) t4 = 1;
- }
- head = find(head, classname, "onslaught_generator");
- }
- if (t1 + t2 + t3 + t4 < 2)
- {
- // game over, only one team remains (or none)
- ClearWinners();
- if (t1) SetWinners(team, NUM_TEAM_1);
- if (t2) SetWinners(team, NUM_TEAM_2);
- if (t3) SetWinners(team, NUM_TEAM_3);
- if (t4) SetWinners(team, NUM_TEAM_4);
- dprint("Have a winner, ending game.\n");
- return WINNING_YES;
- }
-
- // Two or more teams remain
- return WINNING_NO;
-}
-
// Assault winning condition: If the attackers triggered a round end (by fulfilling all objectives)
// they win. Otherwise the defending team wins once the timelimit passes.
void assault_new_round();
bprint("ASSAULT: round completed...\n");
SetWinners(team, assault_attacker_team);
- TeamScore_AddToTeam(assault_attacker_team, ST_ASSAULT_OBJECTIVES, 666 - TeamScore_AddToTeam(assault_attacker_team, ST_ASSAULT_OBJECTIVES, 0));
+ TeamScore_AddToTeam(assault_attacker_team, ST_ASSAULT_DESTROYED, 1);
- if(ent.cnt == 1 || autocvar_g_campaign) // this was the second round
+ // why...
+ //TeamScore_AddToTeam(assault_attacker_team, ST_ASSAULT_OBJECTIVES, 666 - TeamScore_AddToTeam(assault_attacker_team, ST_ASSAULT_OBJECTIVES, 0));
+
+ if(ent.cnt >= autocvar_g_assault_round_limit || autocvar_g_campaign) // this was the second round
{
status = WINNING_YES;
}
if(WinningConditionHelper_zeroisworst)
leadlimit = 0; // not supported in this mode
- if(g_dm || g_tdm || g_ca || g_freezetag || (g_race && !g_race_qualifying) || g_nexball)
+ if(MUTATOR_CALLHOOK(Scores_CountFragsRemaining))
// these modes always score in increments of 1, thus this makes sense
{
if(leaderfrags != WinningConditionHelper_topscore)
return;
}
- if(g_onslaught)
- timelimit = 0; // ONS has its own overtime rule
-
float wantovertime;
wantovertime = 0;
{
checkrules_status = WinningCondition_LMS();
}
- else if (g_onslaught)
- {
- checkrules_status = WinningCondition_Onslaught(); // TODO remove this?
- }
else
{
checkrules_status = WinningCondition_Scores(fraglimit, leadlimit);
if(checkrules_status == WINNING_YES)
{
//print("WINNING\n");
+ jeff_Announcer_MatchEnd();
NextLevel();
}
}
--- /dev/null
+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;
+}
+
+void generator_link(void() spawnproc)
+{
+ Net_LinkEntity(self, TRUE, 0, generator_send);
+ self.think = spawnproc;
+ self.nextthink = time;
+}
--- /dev/null
+const vector GENERATOR_MIN = '-52 -52 -14';
+const vector GENERATOR_MAX = '52 52 75';
+
+float GSF_STATUS = 4;
+float GSF_SETUP = 8;
--- /dev/null
+// Modded announcer and random functions for the Jeff Resurrection servers
+#ifdef JEFF
+
+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 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;
+ 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;
+ 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);
+
+ 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);
+
+ if(deathtype == DEATH_FALL && IS_PLAYER(attacker))
+ 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);
+ else
+ 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);
+
+ if(attacker.lastkiller == targ)
+ if(attacker.lastkiller_weapon == death_weapon)
+ jeff_Announcer_Send(attacker, FALSE, ANNCE_JEFF_PAYBACK);
+ else if(player_count > 2)
+ 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);
+
+ if(attacker.arckill_count == autocvar_jeff_announce_shaftmaster_count && DEATH_ISWEAPON(deathtype, WEP_ARC))
+ 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);
+ }
+
+ 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);
+ else
+ 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);
+
+ if(attacker.vehicle && attacker.vkill_count == autocvar_jeff_announce_roadrage_count)
+ 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);
+
+ if(attacker.vehicle && attacker.vkill_count == autocvar_jeff_announce_manslaughter_count)
+ 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);
+
+ if(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);
+
+ if(attacker.minipickup_count >= autocvar_jeff_announce_bottomfeeder_count)
+ 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);
+
+ if(deathtype == DEATH_SLIME && IS_PLAYER(attacker))
+ jeff_Announcer_Send(attacker, TRUE, ANNCE_JEFF_BIOHAZARD);
+
+ if(Fire_IsBurning(attacker) && deathtype == DEATH_FIRE)
+ jeff_Announcer_Send(attacker, FALSE, ANNCE_JEFF_BLAZEOFGLORY);
+
+ if(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);
+
+ if(attacker.armorvalue >= autocvar_jeff_announce_juggernaut_armor && attacker.health >= autocvar_jeff_announce_juggernaut_health)
+ 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;
+ }
+
+ if(attacker.hagarkill_count == autocvar_jeff_announce_flakmaster_count && DEATH_ISWEAPON(deathtype, WEP_HAGAR))
+ 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);
+
+ if(attacker.lastkilled_time - time >= autocvar_jeff_announce_slacker_time)
+ 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);
+
+ if(attacker.jeff_weaponswitch_count >= autocvar_jeff_announce_comboking_count)
+ jeff_Announcer_Send(attacker, FALSE, ANNCE_JEFF_COMBOKING);
+
+ if(mywepcount >= wepcount)
+ 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);
+
+ if(targ.buffs & BUFF_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);
+}
+
+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);
+}
+
+#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
void droptofloor();
.vector dropped_origin;
+float 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)
+{
+ string l = strcat(" ", list, " ");
+
+ if(strstrofs(l, strcat(" ", command, " "), 0) >= 0)
+ return TRUE;
+
+ return FALSE;
+}
+
void traceline_antilag (entity source, vector v1, vector v2, float nomonst, entity forent, float lag);
void 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));
}
-void() spawnfunc_info_player_deathmatch; // needed for the other spawnpoints
-void() spawnpoint_use;
-string GetMapname();
+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;
#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))
localhead = find(world, field, value);
while (localhead)
{
- if ((localhead.items == IT_KEY1 || localhead.items == IT_KEY2) && localhead.target == "###item###")
+ if ((localhead.items == IT_KEY1 || localhead.items == IT_KEY2 || localhead.classname == "item_flag_team") && localhead.target == "###item###")
dist = localhead.oldorigin;
else
dist = localhead.origin;
entity cursor_ent;
string escape;
string replacement;
+ string ammoitems;
p = 0;
n = 7;
+ ammoitems = "batteries";
+ if(self.items & IT_SUPERCELLS) ammoitems = "supercells";
+ if(self.items & IT_PLASMA) ammoitems = "plasma";
+ if(self.items & IT_CELLS) ammoitems = "cells";
+ if(self.items & IT_ROCKETS) ammoitems = "rockets";
+ if(self.items & IT_SHELLS) ammoitems = "shells";
+
WarpZone_crosshair_trace(self);
cursor = trace_endpos;
cursor_ent = trace_ent;
replacement = substring(msg, p, 2);
escape = substring(msg, p + 1, 1);
- if (escape == "%")
- replacement = "%";
- else if (escape == "\\")
- replacement = "\\";
- else if (escape == "n")
- replacement = "\n";
- else if (escape == "a")
- replacement = ftos(floor(self.armorvalue));
- else if (escape == "h")
- replacement = ftos(floor(self.health));
- else if (escape == "l")
- replacement = NearestLocation(self.origin);
- else if (escape == "y")
- replacement = NearestLocation(cursor);
- else if (escape == "d")
- replacement = NearestLocation(self.death_origin);
- else if (escape == "w") {
- float wep;
- wep = self.weapon;
- if (!wep)
- wep = self.switchweapon;
- if (!wep)
- wep = self.cnt;
- replacement = WEP_NAME(wep);
- } else if (escape == "W") {
- if (self.items & IT_SHELLS) replacement = "shells";
- else if (self.items & IT_NAILS) replacement = "bullets";
- else if (self.items & IT_ROCKETS) replacement = "rockets";
- else if (self.items & IT_CELLS) replacement = "cells";
- else if (self.items & IT_PLASMA) replacement = "plasma";
- else replacement = "batteries"; // ;)
- } else if (escape == "x") {
- replacement = cursor_ent.netname;
- if (replacement == "" || !cursor_ent)
- replacement = "nothing";
- } else if (escape == "s")
- replacement = ftos(vlen(self.velocity - self.velocity_z * '0 0 1'));
- else if (escape == "S")
- replacement = ftos(vlen(self.velocity));
+ switch(escape)
+ {
+ case "%": replacement = "%"; break;
+ case "\\":replacement = "\\"; break;
+ case "n": replacement = "\n"; break;
+ case "a": replacement = ftos(floor(self.armorvalue)); break;
+ case "h": replacement = ftos(floor(self.health)); break;
+ case "j": replacement = ((self.lastkilled) ? self.lastkilled.netname : "(nobody)"); break;
+ case "k": replacement = ((self.lastkiller) ? self.lastkiller.netname : "(nobody)"); break;
+ case "l": replacement = NearestLocation(self.origin); break;
+ case "y": replacement = NearestLocation(cursor); break;
+ case "d": replacement = NearestLocation(self.death_origin); break;
+ case "w": replacement = WEP_NAME((!self.weapon) ? (!self.switchweapon ? self.cnt : self.switchweapon) : self.weapon); break;
+ case "W": replacement = ammoitems; break;
+ case "x": replacement = ((cursor_ent.netname == "" || !cursor_ent) ? "nothing" : cursor_ent.netname); break;
+ case "s": replacement = ftos(vlen(self.velocity - self.velocity_z * '0 0 1')); break;
+ case "S": replacement = ftos(vlen(self.velocity)); break;
+ default:
+ {
+ format_escape = escape;
+ format_replacement = replacement;
+ MUTATOR_CALLHOOK(FormatMessage);
+ escape = format_escape;
+ replacement = format_replacement;
+ break;
+ }
+ }
msg = strcat(substring(msg, 0, p), replacement, substring(msg, p+2, strlen(msg) - (p+2)));
p = p + strlen(replacement);
GetCvars_handleFloat(s, f, cvar_cl_autoscreenshot, "cl_autoscreenshot");
GetCvars_handleFloat(s, f, cvar_cl_jetpack_jump, "cl_jetpack_jump");
GetCvars_handleString(s, f, cvar_g_xonoticversion, "g_xonoticversion");
+ GetCvars_handleString(s, f, cvar_cl_autovote, "cl_autovote");
+ GetCvars_handleString(s, f, cvar_cl_physics, "cl_physics");
+ GetCvars_handleFloat(s, f, cvar_cl_sparkle, "cl_sparkle");
+ GetCvars_handleFloat(s, f, cvar_cl_pony, "cl_pony");
+ GetCvars_handleFloat(s, f, cvar_cl_pony_skin, "cl_pony_skin");
+ GetCvars_handleFloat(s, f, cvar_cl_damnfurries, "cl_damnfurries");
+ GetCvars_handleFloat(s, f, cvar_cl_thestars, "cl_thestars");
+ GetCvars_handleFloat(s, f, cvar_cl_robot, "cl_robot");
+ GetCvars_handleFloat(s, f, cvar_cl_charge, "cl_charge");
GetCvars_handleFloat(s, f, cvar_cl_handicap, "cl_handicap");
GetCvars_handleFloat(s, f, cvar_cl_clippedspectating, "cl_clippedspectating");
GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriority, "cl_weaponpriority", W_FixWeaponOrder_ForceComplete_AndBuildImpulseList);
return p.netname;
}
-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;
-}
-
//#NO AUTOCVARS START
float g_pickup_shells;
if (!i)
return 0;
- if (g_lms || g_ca || allguns)
+ if (allguns)
{
if(weaponinfo.spawnflags & WEP_FLAG_NORMAL)
d = TRUE;
else
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;
g_bugrigs_speed_pow = cvar("g_bugrigs_speed_pow");
g_bugrigs_steer = cvar("g_bugrigs_steer");
- g_instagib = cvar("g_instagib");
+ sv_showfps = cvar("sv_showfps");
sv_clones = cvar("sv_clones");
sv_foginterval = cvar("sv_foginterval");
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"))
+ if (cvar("g_campaign"))
warmup_stage = 0; // these modes cannot work together, sorry
g_pickup_respawntime_weapon = cvar("g_pickup_respawntime_weapon");
void precache()
{
// gamemode related things
- precache_model ("models/misc/chatbubble.spr");
+ precache_model ("models/misc/chatbubble.md3");
precache_model("models/ice/ice.md3");
-#ifdef TTURRETS_ENABLED
- if (autocvar_g_turrets)
- turrets_precash();
-#endif
-
// Precache all player models if desired
if (autocvar_sv_precacheplayermodels)
{
precache_playermodel(s);
s = autocvar_sv_defaultplayermodel;
if (s != "")
- precache_playermodel(s);
+ {
+ float n = tokenize_console(s);
+ if(n > 0)
+ {
+ float i;
+ for (i = 0; i < n; ++i)
+ {
+ //print("Precaching: ", argv(i), "\n");
+ precache_model(argv(i));
+ }
+ }
+
+ precache_playermodel(s);
+ }
+ }
+
+ if(autocvar_sv_allow_customplayermodels)
+ {
+ precache_playermodel("models/player/pony.iqm");
+ precache_playermodel("models/player/renamon.iqm");
+ precache_playermodel("models/player/terminusmale.iqm");
+ precache_playermodel("models/player/ubot.iqm");
+ precache_playermodel("models/player/rosalina.dpm");
}
if (g_footsteps)
precache_model ("models/sprites/10.spr32");
// common weapon precaches
- precache_sound ("weapons/reload.wav"); // until weapons have individual reload sounds, precache the reload sound here
- precache_sound ("weapons/weapon_switch.wav");
- precache_sound ("weapons/weaponpickup.wav");
- precache_sound ("weapons/unavailable.wav");
- precache_sound ("weapons/dryfire.wav");
+ precache_sound (W_Sound("reload")); // until weapons have individual reload sounds, precache the reload sound here
+ precache_sound (W_Sound("weapon_switch"));
+ precache_sound (W_Sound("weaponpickup"));
+ precache_sound (W_Sound("unavailable"));
+ precache_sound (W_Sound("dryfire"));
if (g_grappling_hook)
{
- precache_sound ("weapons/hook_fire.wav"); // hook
- precache_sound ("weapons/hook_impact.wav"); // hook
+ precache_sound (W_Sound("hook_fire")); // hook
+ precache_sound (W_Sound("hook_impact")); // hook
}
precache_model("models/elaser.mdl");
float isPushable(entity e)
{
- if(e.iscreature)
- return TRUE;
if(e.pushable)
return TRUE;
+ if(IS_VEHICLE(e))
+ return FALSE;
+ if(e.iscreature)
+ return TRUE;
switch(e.classname)
{
case "body":
return r; // callbacks return an error status, so 0 is default return value
}
-#define MAX_MUTATORS 15
+#define MAX_MUTATORS 20
string loaded_mutators[MAX_MUTATORS];
float Mutator_Add(mutatorfunc_t func, string name)
{
// should adjust ret_float to contain the team count
// INPUT, OUTPUT:
float ret_float;
+ string ret_string;
MUTATOR_HOOKABLE(SpectateCopy);
// copies variables for spectating "other" to "self"
// INPUT, OUTPUT:
float weapon_rate;
+MUTATOR_HOOKABLE(WantWeapon);
+ // returns which weapon the player/bot should choose
+ // INPUT, OUTPUT:
+ float ret_float;
+ entity other;
+
MUTATOR_HOOKABLE(SetStartItems);
// adjusts {warmup_}start_{items,weapons,ammo_{cells,plasma,rockets,nails,shells,fuel}}
// INPUT:
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
MUTATOR_HOOKABLE(MonsterDropItem);
// called when a monster is dropping loot
// called when a player tries to spawn a monster
// return 1 to prevent spawning
+MUTATOR_HOOKABLE(FormatMessage);
+ // for replacing chat message commands
+ // INPUT, OUTPUT:
+ string format_escape;
+ string format_replacement;
+
MUTATOR_HOOKABLE(PlayerDamage_SplitHealthArmor);
// called when a player gets damaged to e.g. remove stuff he was carrying.
// INPUT:
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;
// if MUTATOR_RETURNVALUE is 1, don't do anything
// return 1 if the use key actually did something
+MUTATOR_HOOKABLE(SV_ParseServerCommand);
+ // called when a server 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
+
MUTATOR_HOOKABLE(SV_ParseClientCommand);
// called when a client command is parsed
// NOTE: hooks MUST start with if(MUTATOR_RETURNVALUE) return 0;
// IN+OUT
vector spawn_score; // _x is priority, _y is "distance"
+MUTATOR_HOOKABLE(Scores_CountFragsRemaining);
+ // allows a mutator to count the last remaining frags
+
MUTATOR_HOOKABLE(SV_StartFrame);
// runs globally each server frame
MUTATOR_HOOKABLE(BotShouldAttack);
// called when a bot checks a target to attack
// INPUT
- entity checkentity;
+ entity other;
MUTATOR_HOOKABLE(PortalTeleport);
// called whenever a player goes through a portal gun teleport
{
if(self.enemy.health - self.dmg > 0.5)
{
- PlayerTeamScore_Add(activator, SP_SCORE, ST_SCORE, self.dmg);
+ PlayerScore_Add(activator, SP_SCORE, self.dmg);
self.enemy.health = self.enemy.health - self.dmg;
}
else
{
- PlayerTeamScore_Add(activator, SP_SCORE, ST_SCORE, self.enemy.health);
+ PlayerScore_Add(activator, SP_SCORE, self.enemy.health);
PlayerTeamScore_Add(activator, SP_ASSAULT_OBJECTIVES, ST_ASSAULT_OBJECTIVES, 1);
self.enemy.health = -1;
- entity oldself, oldactivator, head;
+ entity oldself, oldactivator;
oldself = self;
self = oldself.enemy;
if(self.message)
- FOR_EACH_PLAYER(head)
- centerprint(head, self.message);
+ {
+ entity player;
+ string s;
+ FOR_EACH_PLAYER(player)
+ {
+ s = strcat(self.message, "\n");
+ centerprint(player, s);
+ }
+ }
oldactivator = activator;
activator = oldself;
WaypointSprite_UpdateMaxHealth(spr, ent.max_health);
WaypointSprite_UpdateHealth(spr, ent.health);
ent.sprite = spr;
+ ent.colormod = '2 2 2';
}
else
WaypointSprite_UpdateSprites(spr, "as-defend", "as-push", "as-push");
activator = self;
SUB_UseTargets();
-#ifdef TTURRETS_ENABLED
entity ent, oldself;
//(Re)spawn all turrets
self = ent;
// Dubbles as teamchange
- turret_stdproc_respawn();
+ turret_respawn();
ent = find(ent, classname, "turret_main");
}
self = oldself;
-#endif
}
void assault_wall_think()
// trigger new round
// reset objectives, toggle spawnpoints, reset triggers, ...
+void vehicles_clearreturn(entity veh);
+void vehicles_spawn();
void assault_new_round()
{
+ entity oldself;
+ //bprint("ASSAULT: new round\n");
+
+ oldself = self;
+ // Eject players from vehicles
+ FOR_EACH_PLAYER(self)
+ {
+ if(self.vehicle)
+ vehicles_exit(VHEF_RELEASE);
+ }
+
+ self = findchainflags(vehicle_flags, VHF_ISVEHICLE);
+ while(self)
+ {
+ vehicles_clearreturn(self);
+ vehicles_spawn();
+ self = self.chain;
+ }
+
+ self = oldself;
+
// up round counter
self.winning = self.winning + 1;
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 TRUE;
}
+MUTATOR_HOOKFUNCTION(assault_GetTeamCount)
+{
+ c1 = c2 = 0; // assault always has 2 teams
+ 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(gameover || self.frozen || self.deadflag != DEAD_NO || self.vehicle)
+ 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;
+}
+
+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;
+ }
+
+ return FALSE;
+}
+
// scoreboard setup
void assault_ScoreRules()
{
- 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();
}
+void assault_Initialize()
+{
+ assault_ScoreRules();
+ warmup_stage = 0;
+}
+
MUTATOR_DEFINITION(gamemode_assault)
{
MUTATOR_HOOK(PlayerSpawn, assault_PlayerSpawn, CBC_ORDER_ANY);
MUTATOR_HOOK(TurretSpawn, assault_TurretSpawn, CBC_ORDER_ANY);
MUTATOR_HOOK(VehicleSpawn, assault_VehicleSpawn, CBC_ORDER_ANY);
MUTATOR_HOOK(HavocBot_ChooseRole, assault_BotRoles, CBC_ORDER_ANY);
+ MUTATOR_HOOK(GetTeamCount, assault_GetTeamCount, CBC_ORDER_ANY);
+ MUTATOR_HOOK(PlayerPreThink, assault_PlayerThink, CBC_ORDER_ANY);
+ MUTATOR_HOOK(OnEntityPreSpawn, assault_OnEntityPreSpawn, 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.");
- assault_ScoreRules();
+ assault_Initialize();
}
MUTATOR_ONROLLBACK_OR_REMOVE
void(float ratingscale, vector org, float sradius) havocbot_goalrating_enemyplayers;
// scoreboard stuff
-#define ST_ASSAULT_OBJECTIVES 1
+#define ST_ASSAULT_DESTROYED 1
+#define ST_ASSAULT_OBJECTIVES -1
#define SP_ASSAULT_OBJECTIVES 4
// predefined spawnfuncs
void spawnfunc_func_breakable();
-void target_objective_decrease_activate();
\ No newline at end of file
+void target_objective_decrease_activate();
{
entity e;
total_players = redalive = bluealive = yellowalive = pinkalive = 0;
- FOR_EACH_PLAYER(e) {
- if(e.team == NUM_TEAM_1)
- {
- ++total_players;
- if (e.health >= 1) ++redalive;
- }
- else if(e.team == NUM_TEAM_2)
- {
- ++total_players;
- if (e.health >= 1) ++bluealive;
- }
- else if(e.team == NUM_TEAM_3)
- {
- ++total_players;
- if (e.health >= 1) ++yellowalive;
- }
- else if(e.team == NUM_TEAM_4)
+ FOR_EACH_PLAYER(e)
+ {
+ switch(e.team)
{
- ++total_players;
- if (e.health >= 1) ++pinkalive;
+ case NUM_TEAM_1: if(e.health >= 1) ++redalive; ++total_players; break;
+ case NUM_TEAM_2: if(e.health >= 1) ++bluealive; ++total_players; break;
+ case NUM_TEAM_3: if(e.health >= 1) ++yellowalive; ++total_players; break;
+ case NUM_TEAM_4: if(e.health >= 1) ++pinkalive; ++total_players; break;
}
}
- FOR_EACH_REALCLIENT(e) {
+ FOR_EACH_REALCLIENT(e)
+ {
e.redalive_stat = redalive;
e.bluealive_stat = bluealive;
e.yellowalive_stat = yellowalive;
#define CA_ALIVE_TEAMS_OK() (CA_ALIVE_TEAMS() == ca_teams)
float CA_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;
round_handler_Init(5, autocvar_g_ca_warmup, autocvar_g_ca_round_timelimit);
- FOR_EACH_PLAYER(e)
- nades_Clear(e);
+ nades_Clear(world, TRUE);
return 1;
}
allowed_to_spawn = FALSE;
round_handler_Init(5, autocvar_g_ca_warmup, autocvar_g_ca_round_timelimit);
- FOR_EACH_PLAYER(e)
- nades_Clear(e);
+ nades_Clear(world, TRUE);
return 1;
}
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;
}
+MUTATOR_HOOKFUNCTION(ca_WantWeapon)
+{
+ if(other.spawnflags & WEP_FLAG_NORMAL)
+ ret_float = TRUE;
+ else
+ ret_float = FALSE;
+
+ return FALSE;
+}
+
+MUTATOR_HOOKFUNCTION(ca_CountFrags)
+{
+ // announce remaining frags
+ return TRUE;
+}
+
void ca_Initialize()
{
allowed_to_spawn = TRUE;
MUTATOR_HOOK(FilterItem, ca_FilterItem, CBC_ORDER_ANY);
MUTATOR_HOOK(PlayerDamage_SplitHealthArmor, ca_PlayerDamage_SplitHealthArmor, CBC_ORDER_ANY);
MUTATOR_HOOK(PlayerRegen, ca_PlayerRegen, CBC_ORDER_ANY);
+ MUTATOR_HOOK(WantWeapon, ca_WantWeapon, CBC_ORDER_ANY);
+ MUTATOR_HOOK(Scores_CountFragsRemaining, ca_CountFrags, CBC_ORDER_ANY);
MUTATOR_ONADD
{
--- /dev/null
+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;
+}
+
+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);
+ }
+
+ break;
+ }
+
+ case CP_NEUTRAL:
+ {
+ if(self.health == 0)
+ 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);
+ }
+
+ 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);
+}
+
+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);
+ self.SendFlags = CQSF_SETUP | CQSF_STATE | CQSF_TEAM | CQSF_HEALTH | CQSF_NAME;
+ 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;
+ 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
+ */
+float cq_Teleport(entity player, entity tele_target, float range, float tele_effects)
+{
+ if ( !tele_target )
+ 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 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;
+}
+
+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;
+}
+
+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;
+}
+
+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) )
+ {
+ self.cq_spawn_by = world;
+ 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(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;
+ }
+ }
+ }
+ }
+
+ print("Warning: No spawns found for team ", ftos(self.team), "\n");
+ return FALSE;
+}
+
+MUTATOR_HOOKFUNCTION(conquest_SV_ParseClientCommand)
+{
+ if(MUTATOR_RETURNVALUE) // command was already handled?
+ 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) )
+ 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((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 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;
+ }
+ 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;
+}
+
+MUTATOR_HOOKFUNCTION(conquest_BotRoles)
+{
+ self.havocbot_role = havocbot_role_cq_offense;
+ return TRUE;
+}
+
+MUTATOR_HOOKFUNCTION(conquest_GetTeamCount)
+{
+ ret_float = cq_teams;
+ return FALSE;
+}
+
+void cq_ScoreRules(float teams)
+{
+ 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;
+}
--- /dev/null
+// these are needed since mutators are compiled last
+
+//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)
+
+// score rule declarations
+#define ST_CONQUEST_ROUNDS 1
+#define SP_CONQUEST_LIBERATED 4
+#define SP_CONQUEST_CAPTURED 5
+
+// list of control points on the map
+entity cq_worldcplist;
+.entity cq_worldcpnext;
+
+// control point constants
+float 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
+
+// control point properties
+.float 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
+
+.float cq_capdistance;
+.float cq_originalteam; // stored spawn team for resetting
void ctf_EventLog(string mode, float flagteam, entity actor) // use an alias for easy changing and quick editing later
{
if(autocvar_sv_eventlog)
- GameLogEcho(strcat(":ctf:", mode, ":", ftos(flagteam), ((actor != world) ? (strcat(":", ftos(actor.playerid))) : "")));
+ GameLogEcho(sprintf(":ctf:%s:%d:%d:%s", mode, flagteam, actor.team, ((actor != world) ? ftos(actor.playerid) : "")));
+ //GameLogEcho(strcat(":ctf:", mode, ":", ftos(flagteam), ((actor != world) ? (strcat(":", ftos(actor.playerid))) : ""), "));
}
void ctf_CaptureRecord(entity flag, entity player)
string refername = db_get(ServerProgsDB, strcat(GetMapname(), "/captimerecord/netname"));
// notify about shit
- if(!ctf_captimerecord) { Send_Notification(NOTIF_ALL, world, MSG_CHOICE, APP_TEAM_ENT_2(flag, CHOICE_CTF_CAPTURE_TIME_), player.netname, (cap_time * 100)); }
- else if(cap_time < cap_record) { Send_Notification(NOTIF_ALL, world, MSG_CHOICE, APP_TEAM_ENT_2(flag, CHOICE_CTF_CAPTURE_BROKEN_), player.netname, refername, (cap_time * 100), (cap_record * 100)); }
- else { Send_Notification(NOTIF_ALL, world, MSG_CHOICE, APP_TEAM_ENT_2(flag, CHOICE_CTF_CAPTURE_UNBROKEN_), player.netname, refername, (cap_time * 100), (cap_record * 100)); }
+ if(ctf_oneflag) { Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_CTF_CAPTURE_NEUTRAL, player.netname); }
+ else if(!ctf_captimerecord) { Send_Notification(NOTIF_ALL, world, MSG_CHOICE, APP_TEAM_ENT_4(flag, CHOICE_CTF_CAPTURE_TIME_), player.netname, (cap_time * 100)); }
+ else if(cap_time < cap_record) { Send_Notification(NOTIF_ALL, world, MSG_CHOICE, APP_TEAM_ENT_4(flag, CHOICE_CTF_CAPTURE_BROKEN_), player.netname, refername, (cap_time * 100), (cap_record * 100)); }
+ else { Send_Notification(NOTIF_ALL, world, MSG_CHOICE, APP_TEAM_ENT_4(flag, CHOICE_CTF_CAPTURE_UNBROKEN_), player.netname, refername, (cap_time * 100), (cap_record * 100)); }
// write that shit in the database
+ if(!ctf_oneflag) // but not in 1-flag mode
if((!ctf_captimerecord) || (cap_time < cap_record))
{
ctf_captimerecord = cap_time;
float ctf_CaptureShield_CheckStatus(entity p)
{
- float s, se;
+ float s, s2, s3, s4, se, se2, se3, se4, sr, ser;
entity e;
float players_worseeq, players_total;
if(ctf_captureshield_max_ratio <= 0)
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;
players_total = players_worseeq = 0;
{
if(DIFF_TEAM(e, p))
continue;
- se = PlayerScore_Add(e, SP_SCORE, 0);
- if(se <= s)
+ se = PlayerScore_Add(e, SP_CTF_CAPS, 0);
+ se2 = PlayerScore_Add(e, SP_CTF_PICKUPS, 0);
+ se3 = PlayerScore_Add(e, SP_CTF_RETURNS, 0);
+ se4 = PlayerScore_Add(e, SP_CTF_FCKILLS, 0);
+
+ ser = ((se - se2) + (se3 + se4));
+
+ if(ser <= sr)
++players_worseeq;
++players_total;
}
float ctf_CaptureShield_Customize()
{
+ 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;
}
void ctf_CaptureShield_Touch()
{
+ if(self.enemy.active != ACTIVE_ACTIVE)
+ {
+ 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) * ctf_captureshield_force);
+ if(IS_REAL_CLIENT(other)) { Send_Notification(NOTIF_ONE, other, MSG_CENTER, CENTER_CTF_CAPTURESHIELD_INACTIVE); }
+
+ return;
+ }
+
if(!other.ctf_captureshielded) { return; }
- if(SAME_TEAM(self, other)) { return; }
+ if(CTF_SAMETEAM(self, other)) { return; }
vector mymid = (self.absmin + self.absmax) * 0.5;
vector othermid = (other.absmin + other.absmax) * 0.5;
flag.ctf_status = FLAG_DROPPED;
// messages and sounds
- Send_Notification(NOTIF_ALL, world, MSG_INFO, APP_TEAM_ENT_2(flag, INFO_CTF_LOST_), player.netname);
+ Send_Notification(NOTIF_ALL, world, MSG_INFO, ((flag.team) ? APP_TEAM_ENT_4(flag, INFO_CTF_LOST_) : INFO_CTF_LOST_NEUTRAL), player.netname);
sound(flag, CH_TRIGGER, flag.snd_flag_dropped, VOL_BASE, ATTEN_NONE);
ctf_EventLog("dropped", player.team, player);
FOR_EACH_REALPLAYER(tmp_player)
{
if(tmp_player == sender)
- Send_Notification(NOTIF_ONE, tmp_player, MSG_CENTER, APP_TEAM_ENT_2(flag, CENTER_CTF_PASS_SENT_), player.netname);
+ Send_Notification(NOTIF_ONE, tmp_player, MSG_CENTER, ((flag.team) ? APP_TEAM_ENT_4(flag, CENTER_CTF_PASS_SENT_) : CENTER_CTF_PASS_SENT_NEUTRAL), player.netname);
else if(tmp_player == player)
- Send_Notification(NOTIF_ONE, tmp_player, MSG_CENTER, APP_TEAM_ENT_2(flag, CENTER_CTF_PASS_RECEIVED_), sender.netname);
+ Send_Notification(NOTIF_ONE, tmp_player, MSG_CENTER, ((flag.team) ? APP_TEAM_ENT_4(flag, CENTER_CTF_PASS_RECEIVED_) : CENTER_CTF_PASS_RECEIVED_NEUTRAL), sender.netname);
else if(SAME_TEAM(tmp_player, sender))
- Send_Notification(NOTIF_ONE, tmp_player, MSG_CENTER, APP_TEAM_ENT_2(flag, CENTER_CTF_PASS_OTHER_), sender.netname, player.netname);
+ Send_Notification(NOTIF_ONE, tmp_player, MSG_CENTER, ((flag.team) ? APP_TEAM_ENT_4(flag, CENTER_CTF_PASS_OTHER_) : CENTER_CTF_PASS_OTHER_NEUTRAL), sender.netname, player.netname);
}
// create new waypoint
// other
sound(player, CH_TRIGGER, flag.snd_flag_touch, VOL_BASE, ATTEN_NORM);
- WarpZone_TrailParticles(world, particleeffectnum(flag.passeffect), player.origin, targ_origin);
+ if(flag.passeffect == "") { Send_Effect(flag.passeffectnum, player.origin, targ_origin, 0); }
+ else { WarpZone_TrailParticles(world, particleeffectnum(flag.passeffect), player.origin, targ_origin); }
ctf_EventLog("pass", flag.team, player);
break;
}
{
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);
ctf_Handle_Drop(flag, player, droptype);
break;
{
entity enemy_flag = ((capturetype == CAPTURE_NORMAL) ? toucher.flagcarried : toucher);
entity player = ((capturetype == CAPTURE_NORMAL) ? toucher : enemy_flag.ctf_dropper);
+ entity player_team_flag = world, tmp_entity;
float old_time, new_time;
- if (!player) { return; } // without someone to give the reward to, we can't possibly cap
+ if(!player) { return; } // without someone to give the reward to, we can't possibly cap
+ if(CTF_DIFFTEAM(player, flag)) { return; }
+
+ if(ctf_oneflag)
+ for(tmp_entity = ctf_worldflaglist; tmp_entity; tmp_entity = tmp_entity.ctf_worldflagnext)
+ if(SAME_TEAM(tmp_entity, player))
+ {
+ player_team_flag = tmp_entity;
+ break;
+ }
nades_GiveBonus(player, autocvar_g_nades_bonus_score_high );
+ player.throw_prevtime = time;
+ player.throw_count = 0;
+
// messages and sounds
- Send_Notification(NOTIF_ONE, player, MSG_CENTER, APP_TEAM_ENT_2(enemy_flag, CENTER_CTF_CAPTURE_));
+ Send_Notification(NOTIF_ONE, player, MSG_CENTER, ((enemy_flag.team) ? APP_TEAM_ENT_4(enemy_flag, CENTER_CTF_CAPTURE_) : CENTER_CTF_CAPTURE_NEUTRAL));
ctf_CaptureRecord(enemy_flag, player);
- sound(player, CH_TRIGGER, flag.snd_flag_capture, VOL_BASE, ATTEN_NONE);
+ sound(player, CH_TRIGGER, ((ctf_oneflag) ? player_team_flag.snd_flag_capture : ((DIFF_TEAM(player, flag)) ? enemy_flag.snd_flag_capture : flag.snd_flag_capture)), VOL_BASE, ATTEN_NONE);
switch(capturetype)
{
PlayerScore_Add(player, SP_CTF_CAPTIME, new_time - old_time);
// effects
- pointparticles(particleeffectnum(flag.capeffect), flag.origin, '0 0 0', 1);
+ if(flag.capeffect == "") { Send_Effect(flag.capeffectnum, flag.origin, '0 0 0', 1); }
+ else { pointparticles(particleeffectnum(flag.capeffect), flag.origin, '0 0 0', 1); }
//shockwave_spawn("models/ctf/shockwavetransring.md3", flag.origin - '0 0 15', -0.8, 0, 1);
// other
void ctf_Handle_Return(entity flag, entity player)
{
// messages and sounds
- if(player.flags & FL_MONSTER)
+ if(IS_MONSTER(player))
{
- Send_Notification(NOTIF_ALL, world, MSG_INFO, APP_TEAM_ENT_2(flag, INFO_CTF_RETURN_MONSTER_), player.monster_name);
+ Send_Notification(NOTIF_ALL, world, MSG_INFO, APP_TEAM_ENT_4(flag, INFO_CTF_RETURN_MONSTER_), player.monster_name);
}
- else
+ else if(flag.team)
{
- Send_Notification(NOTIF_ONE, player, MSG_CENTER, APP_TEAM_ENT_2(flag, CENTER_CTF_RETURN_));
- Send_Notification(NOTIF_ALL, world, MSG_INFO, APP_TEAM_ENT_2(flag, INFO_CTF_RETURN_), player.netname);
+ Send_Notification(NOTIF_ONE, player, MSG_CENTER, APP_TEAM_ENT_4(flag, CENTER_CTF_RETURN_));
+ Send_Notification(NOTIF_ALL, world, MSG_INFO, APP_TEAM_ENT_4(flag, INFO_CTF_RETURN_), player.netname);
}
sound(player, CH_TRIGGER, flag.snd_flag_returned, VOL_BASE, ATTEN_NONE);
ctf_EventLog("return", flag.team, player);
flag.ctf_dropper.next_take_time = time + autocvar_g_ctf_flag_collect_delay; // set next take time
}
+ // other
+ if(player.flagcarried == flag)
+ WaypointSprite_Kill(player.wps_flagcarrier);
+
// reset the flag
ctf_RespawnFlag(flag);
}
{
// declarations
float pickup_dropped_score; // used to calculate dropped pickup score
+ entity tmp_entity; // temporary entity
// attach the flag to the player
flag.owner = player;
}
// messages and sounds
- Send_Notification(NOTIF_ALL, world, MSG_INFO, APP_TEAM_ENT_2(flag, INFO_CTF_PICKUP_), player.netname);
- Send_Notification(NOTIF_ONE, player, MSG_CENTER, APP_TEAM_ENT_2(flag, CENTER_CTF_PICKUP_));
+ Send_Notification(NOTIF_ALL, world, MSG_INFO, ((flag.team) ? APP_TEAM_ENT_4(flag, INFO_CTF_PICKUP_) : INFO_CTF_PICKUP_NEUTRAL), player.netname);
if(ctf_stalemate) { Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_CTF_STALEMATE_CARRIER); }
-
- Send_Notification(NOTIF_TEAM_EXCEPT, player, MSG_CHOICE, CHOICE_CTF_PICKUP_TEAM, Team_ColorCode(player.team), player.netname);
- Send_Notification(NOTIF_TEAM, flag, MSG_CHOICE, CHOICE_CTF_PICKUP_ENEMY, Team_ColorCode(player.team), player.netname);
+ if(!flag.team) { Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_CTF_PICKUP_NEUTRAL); }
+ else if(CTF_DIFFTEAM(player, flag)) { Send_Notification(NOTIF_ONE, player, MSG_CENTER, APP_TEAM_ENT_4(flag, CENTER_CTF_PICKUP_)); }
+ else { Send_Notification(NOTIF_ONE, player, MSG_CENTER, ((SAME_TEAM(player, flag)) ? CENTER_CTF_PICKUP_TEAM : CENTER_CTF_PICKUP_TEAM_ENEMY), Team_ColorCode(flag.team)); }
+
+ Send_Notification(NOTIF_TEAM_EXCEPT, player, MSG_CHOICE, ((flag.team) ? APP_TEAM_ENT_4(flag, CHOICE_CTF_PICKUP_TEAM_) : CHOICE_CTF_PICKUP_TEAM_NEUTRAL), Team_ColorCode(player.team), player.netname);
+
+ if(!flag.team)
+ FOR_EACH_PLAYER(tmp_entity)
+ if(tmp_entity != player)
+ if(DIFF_TEAM(player, tmp_entity))
+ Send_Notification(NOTIF_ONE, tmp_entity, MSG_CHOICE, CHOICE_CTF_PICKUP_ENEMY_NEUTRAL, Team_ColorCode(player.team), player.netname);
+
+ if(flag.team)
+ FOR_EACH_PLAYER(tmp_entity)
+ if(tmp_entity != player)
+ if(CTF_SAMETEAM(flag, tmp_entity))
+ if(SAME_TEAM(player, tmp_entity))
+ Send_Notification(NOTIF_ONE, tmp_entity, MSG_CHOICE, APP_TEAM_ENT_4(flag, CHOICE_CTF_PICKUP_TEAM_), Team_ColorCode(player.team), player.netname);
+ else
+ Send_Notification(NOTIF_ONE, tmp_entity, MSG_CHOICE, ((SAME_TEAM(flag, player)) ? CHOICE_CTF_PICKUP_ENEMY_TEAM : CHOICE_CTF_PICKUP_ENEMY), Team_ColorCode(player.team), player.netname);
sound(player, CH_TRIGGER, flag.snd_flag_taken, VOL_BASE, ATTEN_NONE);
}
// effects
- pointparticles(particleeffectnum(flag.toucheffect), player.origin, '0 0 0', 1);
+ if(flag.toucheffect == "") { Send_Effect(flag.toucheffectnum, player.origin, '0 0 0', 1); }
+ else { pointparticles(particleeffectnum(flag.toucheffect), player.origin, '0 0 0', 1); }
// waypoints
if(pickuptype == PICKUP_DROPPED) { WaypointSprite_Kill(flag.wps_flagdropped); }
{
switch(returntype)
{
- case RETURN_DROPPED: Send_Notification(NOTIF_ALL, world, MSG_INFO, APP_TEAM_ENT_2(flag, INFO_CTF_FLAGRETURN_DROPPED_)); break;
- case RETURN_DAMAGE: Send_Notification(NOTIF_ALL, world, MSG_INFO, APP_TEAM_ENT_2(flag, INFO_CTF_FLAGRETURN_DAMAGED_)); break;
- case RETURN_SPEEDRUN: Send_Notification(NOTIF_ALL, world, MSG_INFO, APP_TEAM_ENT_2(flag, INFO_CTF_FLAGRETURN_SPEEDRUN_), ctf_captimerecord); break;
- case RETURN_NEEDKILL: Send_Notification(NOTIF_ALL, world, MSG_INFO, APP_TEAM_ENT_2(flag, INFO_CTF_FLAGRETURN_NEEDKILL_)); break;
+ case RETURN_DROPPED: Send_Notification(NOTIF_ALL, world, MSG_INFO, ((flag.team) ? APP_TEAM_ENT_4(flag, INFO_CTF_FLAGRETURN_DROPPED_) : INFO_CTF_FLAGRETURN_DROPPED_NEUTRAL)); break;
+ case RETURN_DAMAGE: Send_Notification(NOTIF_ALL, world, MSG_INFO, ((flag.team) ? APP_TEAM_ENT_4(flag, INFO_CTF_FLAGRETURN_DAMAGED_) : INFO_CTF_FLAGRETURN_DAMAGED_NEUTRAL)); break;
+ case RETURN_SPEEDRUN: Send_Notification(NOTIF_ALL, world, MSG_INFO, ((flag.team) ? APP_TEAM_ENT_4(flag, INFO_CTF_FLAGRETURN_SPEEDRUN_) : INFO_CTF_FLAGRETURN_SPEEDRUN_NEUTRAL), ctf_captimerecord); break;
+ case RETURN_NEEDKILL: Send_Notification(NOTIF_ALL, world, MSG_INFO, ((flag.team) ? APP_TEAM_ENT_4(flag, INFO_CTF_FLAGRETURN_NEEDKILL_) : INFO_CTF_FLAGRETURN_NEEDKILL_NEUTRAL)); break;
default:
case RETURN_TIMEOUT:
- { Send_Notification(NOTIF_ALL, world, MSG_INFO, APP_TEAM_ENT_2(flag, INFO_CTF_FLAGRETURN_TIMEOUT_)); break; }
+ { Send_Notification(NOTIF_ALL, world, MSG_INFO, ((flag.team) ? APP_TEAM_ENT_4(flag, INFO_CTF_FLAGRETURN_TIMEOUT_) : INFO_CTF_FLAGRETURN_TIMEOUT_NEUTRAL)); break; }
}
sound(flag, CH_TRIGGER, flag.snd_flag_respawn, VOL_BASE, ATTEN_NONE);
ctf_EventLog("returned", flag.team, world);
}
}
+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; }
+
+ return TRUE;
+}
+
void ctf_CheckStalemate(void)
{
// declarations
- float stale_red_flags = 0, stale_blue_flags = 0;
+ float stale_flags = 0, stale_red_flags = 0, stale_blue_flags = 0, stale_yellow_flags = 0, stale_pink_flags = 0, stale_neutral_flags = 0;
entity tmp_entity;
entity ctf_staleflaglist = world; // reset the list, we need to build the list each time this function runs
{
if(autocvar_g_ctf_stalemate)
if(tmp_entity.ctf_status != FLAG_BASE)
- if(time >= tmp_entity.ctf_pickuptime + autocvar_g_ctf_stalemate_time)
+ if(time >= tmp_entity.ctf_pickuptime + autocvar_g_ctf_stalemate_time || !tmp_entity.team) // instant stalemate in oneflag
{
tmp_entity.ctf_staleflagnext = ctf_staleflaglist; // link flag into staleflaglist
ctf_staleflaglist = tmp_entity;
{
case NUM_TEAM_1: ++stale_red_flags; break;
case NUM_TEAM_2: ++stale_blue_flags; break;
+ case NUM_TEAM_3: ++stale_yellow_flags; break;
+ case NUM_TEAM_4: ++stale_pink_flags; break;
+ default: ++stale_neutral_flags; break;
}
}
}
- 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;
+ else if(stale_flags == ctf_teams)
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; }
- 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; }
// if sufficient stalemate, then set up the waypointsprite and announce the stalemate if necessary
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));
+ tmp_entity.owner.wps_enemyflagcarrier.customizeentityforclient = ctf_Stalemate_Customize;
+ }
}
if (!wpforenemy_announced)
{
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;
+ }
+ else
+ {
+ self.health = 0;
+ ctf_CheckFlagReturn(self, RETURN_NEEDKILL);
+ }
return;
}
if(autocvar_g_ctf_flag_return_damage)
return;
}
}
- if(autocvar_g_ctf_flag_return_time)
+ if(self.ctf_flagdamaged)
+ {
+ self.health -= ((self.max_flag_health / autocvar_g_ctf_flag_return_damage_delay) * FLAG_THINKRATE);
+ ctf_CheckFlagReturn(self, RETURN_NEEDKILL);
+ return;
+ }
+ else if(autocvar_g_ctf_flag_return_time)
{
self.health -= ((self.max_flag_health / autocvar_g_ctf_flag_return_time) * FLAG_THINKRATE);
ctf_CheckFlagReturn(self, RETURN_TIMEOUT);
wpforenemy_nextthink = time + WPFE_THINKRATE; // waypoint for enemy think rate (to reduce unnecessary spam of this check)
}
}
+ if(CTF_SAMETEAM(self, self.owner) && self.team)
+ {
+ if(autocvar_g_ctf_flag_return) // drop the flag if reverse status has changed
+ ctf_Handle_Throw(self.owner, world, DROP_THROW);
+ else if(vlen(self.owner.origin - self.ctf_spawnorigin) <= autocvar_g_ctf_flag_return_carried)
+ ctf_Handle_Return(self, self.owner);
+ }
return;
}
void ctf_FlagTouch()
{
if(gameover) { return; }
+ if(self.active != ACTIVE_ACTIVE) { return; }
if(trace_dphitcontents & (DPCONTENTS_PLAYERCLIP | DPCONTENTS_MONSTERCLIP)) { return; }
- entity toucher = other;
- float is_not_monster = (!(toucher.flags & FL_MONSTER));
+ entity toucher = other, tmp_entity;
+ float is_not_monster = (!IS_MONSTER(toucher)), num_perteam = 0;
// automatically kill the flag and return it if it touched lava/slime/nodrop surfaces
if(ITEM_TOUCH_NEEDKILL())
{
- self.health = 0;
- ctf_CheckFlagReturn(self, RETURN_NEEDKILL);
- return;
+ if(!autocvar_g_ctf_flag_return_damage_delay)
+ {
+ self.health = 0;
+ ctf_CheckFlagReturn(self, RETURN_NEEDKILL);
+ }
+ if(!self.ctf_flagdamaged) { return; }
}
+ FOR_EACH_PLAYER(tmp_entity) if(SAME_TEAM(toucher, tmp_entity)) { ++num_perteam; }
+
// special touch behaviors
if(toucher.frozen) { return; }
- else if(toucher.vehicle_flags & VHF_ISVEHICLE)
+ else if(IS_VEHICLE(toucher))
{
if(autocvar_g_ctf_allow_vehicle_touch && toucher.owner)
toucher = toucher.owner; // the player is actually the vehicle owner, not other
else
return; // do nothing
}
- else if(toucher.flags & FL_MONSTER)
+ else if(IS_MONSTER(toucher))
{
if(!autocvar_g_ctf_allow_monster_touch)
return; // do nothing
{
if(time > self.wait) // if we haven't in a while, play a sound/effect
{
- pointparticles(particleeffectnum(self.toucheffect), self.origin, '0 0 0', 1);
+ if(self.toucheffect == "") { Send_Effect(self.toucheffectnum, self.origin, '0 0 0', 1); }
+ else { pointparticles(particleeffectnum(self.toucheffect), self.origin, '0 0 0', 1); }
sound(self, CH_TRIGGER, self.snd_flag_touch, VOL_BASE, ATTEN_NORM);
self.wait = time + FLAG_TOUCHRATE;
}
{
case FLAG_BASE:
{
- if(SAME_TEAM(toucher, self) && (toucher.flagcarried) && DIFF_TEAM(toucher.flagcarried, self) && is_not_monster)
+ if(ctf_oneflag)
+ {
+ if(CTF_SAMETEAM(toucher, self) && (toucher.flagcarried) && !toucher.flagcarried.team && is_not_monster)
+ ctf_Handle_Capture(self, toucher, CAPTURE_NORMAL); // toucher just captured the neutral flag to enemy base
+ else if(!self.team && (!toucher.flagcarried) && (!toucher.ctf_captureshielded) && (time > toucher.next_take_time) && is_not_monster)
+ ctf_Handle_Pickup(self, toucher, PICKUP_BASE); // toucher just stole the neutral flag
+ }
+ else if(CTF_SAMETEAM(toucher, self) && (toucher.flagcarried) && DIFF_TEAM(toucher.flagcarried, self) && is_not_monster)
ctf_Handle_Capture(self, toucher, CAPTURE_NORMAL); // toucher just captured the enemies flag to his base
- else if(DIFF_TEAM(toucher, self) && (!toucher.flagcarried) && (!toucher.ctf_captureshielded) && (time > toucher.next_take_time) && is_not_monster)
+ else if(CTF_DIFFTEAM(toucher, self) && (!toucher.flagcarried) && (!toucher.ctf_captureshielded) && (time > toucher.next_take_time) && is_not_monster)
ctf_Handle_Pickup(self, toucher, PICKUP_BASE); // toucher just stole the enemies flag
break;
}
case FLAG_DROPPED:
{
- if(SAME_TEAM(toucher, self))
+ if(CTF_SAMETEAM(toucher, self) && (autocvar_g_ctf_flag_return || num_perteam <= 1) && self.team) // automatically return if there's only 1 player on the team
ctf_Handle_Return(self, toucher); // toucher just returned his own flag
else if(is_not_monster && (!toucher.flagcarried) && ((toucher != self.ctf_dropper) || (time > self.ctf_droptime + autocvar_g_ctf_flag_collect_delay)))
ctf_Handle_Pickup(self, toucher, PICKUP_DROPPED); // toucher just picked up a dropped enemy flag
// reset the player (if there is one)
if((flag.owner) && (flag.owner.flagcarried == flag))
{
- if(flag.owner.wps_enemyflagcarrier)
- WaypointSprite_Kill(flag.owner.wps_enemyflagcarrier);
-
+ WaypointSprite_Kill(flag.owner.wps_enemyflagcarrier);
WaypointSprite_Kill(flag.wps_flagcarrier);
flag.owner.flagcarried = world;
if((flag.owner) && (flag.owner.vehicle))
flag.scale = FLAG_SCALE;
- if((flag.ctf_status == FLAG_DROPPED) && (flag.wps_flagdropped))
+ if(flag.ctf_status == FLAG_DROPPED)
{ WaypointSprite_Kill(flag.wps_flagdropped); }
// reset the flag
flag.ctf_dropper = world;
flag.ctf_pickuptime = 0;
flag.ctf_droptime = 0;
+ flag.ctf_flagdamaged = 0;
ctf_CheckStalemate();
}
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;
+}
+
void ctf_DelayedFlagSetup(void) // called after a flag is placed on a map by ctf_FlagSetup()
{
// bot waypoints
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'));
+ self.wps_flagbase.customizeentityforclient = ctf_FlagWaypoint_Customize;
// captureshield setup
ctf_CaptureShield_Spawn(self);
void ctf_FlagSetup(float teamnumber, entity flag) // called when spawning a flag entity on the map as a spawnfunc
{
// declarations
- teamnumber = fabs(teamnumber - bound(0, autocvar_g_ctf_reverse, 1)); // if we were originally 1, this will become 0. If we were originally 0, this will become 1.
self = flag; // for later usage with droptofloor()
// main setup
setattachment(flag, world, "");
- flag.netname = ((teamnumber) ? "^1RED^7 flag" : "^4BLUE^7 flag"); // Primarily only used for debugging or when showing nearby item name
- flag.team = ((teamnumber) ? NUM_TEAM_1 : NUM_TEAM_2); // NUM_TEAM_1: color 4 team (red) - NUM_TEAM_2: color 13 team (blue)
- flag.items = ((teamnumber) ? IT_KEY2 : IT_KEY1); // IT_KEY2: gold key (redish enough) - IT_KEY1: silver key (bluish enough)
+ flag.netname = strzone(sprintf("%s%s^7 flag", Team_ColorCode(teamnumber), Team_ColorName_Upper(teamnumber)));
+ flag.team = teamnumber;
flag.classname = "item_flag_team";
flag.target = "###item###"; // wut?
flag.flags = FL_ITEM | FL_NOTARGET;
flag.event_damage = ctf_FlagDamage;
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';
flag.mangle = flag.angles;
flag.reset = ctf_Reset;
+ flag.use = ctf_Use;
flag.touch = ctf_FlagTouch;
flag.think = ctf_FlagThink;
flag.nextthink = time + FLAG_THINKRATE;
flag.ctf_status = FLAG_BASE;
// appearence
- if(flag.model == "") { flag.model = ((teamnumber) ? autocvar_g_ctf_flag_red_model : autocvar_g_ctf_flag_blue_model); }
+ if(flag.model == "") { flag.model = ((teamnumber == NUM_TEAM_1) ? autocvar_g_ctf_flag_red_model : ((teamnumber == NUM_TEAM_2) ? autocvar_g_ctf_flag_blue_model : ((teamnumber == NUM_TEAM_3) ? autocvar_g_ctf_flag_yellow_model : ((teamnumber == NUM_TEAM_4) ? autocvar_g_ctf_flag_pink_model : autocvar_g_ctf_flag_neutral_model)))); }
if(!flag.scale) { flag.scale = FLAG_SCALE; }
- if(!flag.skin) { flag.skin = ((teamnumber) ? autocvar_g_ctf_flag_red_skin : autocvar_g_ctf_flag_blue_skin); }
- if(flag.toucheffect == "") { flag.toucheffect = ((teamnumber) ? "redflag_touch" : "blueflag_touch"); }
- if(flag.passeffect == "") { flag.passeffect = ((teamnumber) ? "red_pass" : "blue_pass"); }
- if(flag.capeffect == "") { flag.capeffect = ((teamnumber) ? "red_cap" : "blue_cap"); }
+ if(!flag.skin) { flag.skin = ((teamnumber == NUM_TEAM_1) ? autocvar_g_ctf_flag_red_skin : ((teamnumber == NUM_TEAM_2) ? autocvar_g_ctf_flag_blue_skin : ((teamnumber == NUM_TEAM_3) ? autocvar_g_ctf_flag_yellow_skin : ((teamnumber == NUM_TEAM_4) ? autocvar_g_ctf_flag_pink_skin : autocvar_g_ctf_flag_neutral_skin)))); }
+ if(flag.toucheffect == "") { flag.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 : ((teamnumber == NUM_TEAM_4) ? EFFECT_FLAG_PINK_TOUCH : EFFECT_FLAG_NEUTRAL_TOUCH)))); }
+ if(flag.passeffect == "") { flag.passeffectnum = ((teamnumber == NUM_TEAM_1) ? EFFECT_RED_PASS : ((teamnumber == NUM_TEAM_2) ? EFFECT_BLUE_PASS : ((teamnumber == NUM_TEAM_3) ? EFFECT_YELLOW_PASS : ((teamnumber == NUM_TEAM_4) ? EFFECT_PINK_PASS : EFFECT_NEUTRAL_PASS)))); }
+ if(flag.capeffect == "") { flag.capeffectnum = ((teamnumber == NUM_TEAM_1) ? EFFECT_RED_CAP : ((teamnumber == NUM_TEAM_2) ? EFFECT_BLUE_CAP : ((teamnumber == NUM_TEAM_3) ? EFFECT_YELLOW_CAP : ((teamnumber == NUM_TEAM_4) ? EFFECT_PINK_CAP : 0)))); } // neutral flag cant be capped itself
+ if(!flag.active) { flag.active = ACTIVE_ACTIVE; }
// sound
- if(flag.snd_flag_taken == "") { flag.snd_flag_taken = ((teamnumber) ? "ctf/red_taken.wav" : "ctf/blue_taken.wav"); }
- if(flag.snd_flag_returned == "") { flag.snd_flag_returned = ((teamnumber) ? "ctf/red_returned.wav" : "ctf/blue_returned.wav"); }
- if(flag.snd_flag_capture == "") { flag.snd_flag_capture = ((teamnumber) ? "ctf/red_capture.wav" : "ctf/blue_capture.wav"); } // blue team scores by capturing the red flag
+ if(flag.snd_flag_taken == "") { flag.snd_flag_taken = ((teamnumber == NUM_TEAM_1) ? "ctf/red_taken.wav" : ((teamnumber == NUM_TEAM_2) ? "ctf/blue_taken.wav" : ((teamnumber == NUM_TEAM_3) ? "ctf/yellow_taken.wav" : ((teamnumber == NUM_TEAM_4) ? "ctf/pink_taken.wav" : "ctf/neutral_taken.wav")))); }
+ if(flag.snd_flag_returned == "") { flag.snd_flag_returned = ((teamnumber == NUM_TEAM_1) ? "ctf/red_returned.wav" : ((teamnumber == NUM_TEAM_2) ? "ctf/blue_returned.wav" : ((teamnumber == NUM_TEAM_3) ? "ctf/yellow_returned.wav" : ((teamnumber == NUM_TEAM_4) ? "ctf/pink_returned.wav" : "")))); } // neutral flag can't be returned by players
+ if(flag.snd_flag_capture == "") { flag.snd_flag_capture = ((teamnumber == NUM_TEAM_1) ? "ctf/red_capture.wav" : ((teamnumber == NUM_TEAM_2) ? "ctf/blue_capture.wav" : ((teamnumber == NUM_TEAM_3) ? "ctf/yellow_capture.wav" : ((teamnumber == NUM_TEAM_4) ? "ctf/pink_capture.wav" : "")))); } // again can't be captured
if(flag.snd_flag_respawn == "") { flag.snd_flag_respawn = "ctf/flag_respawn.wav"; } // if there is ever a team-based sound for this, update the code to match.
- if(flag.snd_flag_dropped == "") { flag.snd_flag_dropped = ((teamnumber) ? "ctf/red_dropped.wav" : "ctf/blue_dropped.wav"); }
+ if(flag.snd_flag_dropped == "") { flag.snd_flag_dropped = ((teamnumber == NUM_TEAM_1) ? "ctf/red_dropped.wav" : ((teamnumber == NUM_TEAM_2) ? "ctf/blue_dropped.wav" : ((teamnumber == NUM_TEAM_3) ? "ctf/yellow_dropped.wav" : ((teamnumber == NUM_TEAM_4) ? "ctf/pink_dropped.wav" : "ctf/neutral_dropped.wav")))); }
if(flag.snd_flag_touch == "") { flag.snd_flag_touch = "ctf/touch.wav"; } // again has no team-based sound
if(flag.snd_flag_pass == "") { flag.snd_flag_pass = "ctf/pass.wav"; } // same story here
if(autocvar_g_ctf_flag_glowtrails)
{
- flag.glow_color = ((teamnumber) ? 251 : 210); // 251: red - 210: blue
+ flag.glow_color = ((teamnumber == NUM_TEAM_1) ? 251 : ((teamnumber == NUM_TEAM_2) ? 210 : ((teamnumber == NUM_TEAM_3) ? 110 : ((teamnumber == NUM_TEAM_4) ? 145 : 254))));
flag.glow_size = 25;
flag.glow_trail = 1;
}
flag.effects |= EF_LOWPRECISION;
if(autocvar_g_ctf_fullbrightflags) { flag.effects |= EF_FULLBRIGHT; }
- if(autocvar_g_ctf_dynamiclights) { flag.effects |= ((teamnumber) ? EF_RED : EF_BLUE); }
-
+ if(autocvar_g_ctf_dynamiclights)
+ {
+ switch(teamnumber)
+ {
+ case NUM_TEAM_1: flag.effects |= EF_RED; break;
+ case NUM_TEAM_2: flag.effects |= EF_BLUE; break;
+ case NUM_TEAM_3: flag.effects |= EF_DIMLIGHT; break;
+ case NUM_TEAM_4: flag.effects |= EF_RED; break;
+ default: flag.effects |= EF_DIMLIGHT; break;
+ }
+ }
+
// flag placement
if((flag.spawnflags & 1) || flag.noalign) // don't drop to floor, just stay at fixed location
{
f = ctf_worldflaglist;
while (f)
{
- if (bot.team == f.team)
+ if (CTF_SAMETEAM(bot, f))
return f;
f = f.ctf_worldflagnext;
}
f = ctf_worldflaglist;
while (f)
{
- if (bot.team != f.team)
+ if(ctf_oneflag)
+ {
+ if(CTF_DIFFTEAM(bot, f))
+ {
+ if(f.team)
+ {
+ if(bot.flagcarried)
+ return f;
+ }
+ else if(!bot.flagcarried)
+ return f;
+ }
+ }
+ else if (CTF_DIFFTEAM(bot, f))
return f;
f = f.ctf_worldflagnext;
}
FOR_EACH_PLAYER(head)
{
- if(head.team!=bot.team || head.deadflag != DEAD_NO || head == bot)
+ if(DIFF_TEAM(head, bot) || head.deadflag != DEAD_NO || head == bot)
continue;
if(vlen(head.origin - org) < tc_radius)
head = ctf_worldflaglist;
while (head)
{
- if (self.team == head.team)
+ if (CTF_SAMETEAM(self, head))
break;
head = head.ctf_worldflagnext;
}
head = ctf_worldflaglist;
while (head)
{
- if (self.team == head.team)
+ if (CTF_SAMETEAM(self, head))
break;
head = head.ctf_worldflagnext;
}
head = ctf_worldflaglist;
while (head)
{
- if (self.team != head.team)
+ if(ctf_oneflag)
+ {
+ if(CTF_DIFFTEAM(self, head))
+ {
+ if(head.team)
+ {
+ if(self.flagcarried)
+ break;
+ }
+ else if(!self.flagcarried)
+ break;
+ }
+ }
+ else if(CTF_DIFFTEAM(self, head))
break;
head = head.ctf_worldflagnext;
}
self.bot_strategytime = time + autocvar_bot_ai_strategyinterval;
navigation_goalrating_start();
- havocbot_goalrating_ctf_ourbase(50000);
+ if(ctf_oneflag)
+ havocbot_goalrating_ctf_enemybase(50000);
+ else
+ havocbot_goalrating_ctf_ourbase(50000);
if(self.health<100)
havocbot_goalrating_ctf_carrieritems(1000, self.origin, 1000);
{
entity flag;
+ float t = 0, t2 = 0, t3 = 0;
+
// initially clear items so they can be set as necessary later.
- self.items &= ~(IT_RED_FLAG_CARRYING | IT_RED_FLAG_TAKEN | IT_RED_FLAG_LOST
- | IT_BLUE_FLAG_CARRYING | IT_BLUE_FLAG_TAKEN | IT_BLUE_FLAG_LOST | IT_CTF_SHIELDED);
+ self.ctf_flagstatus &= ~(CTF_RED_FLAG_CARRYING | CTF_RED_FLAG_TAKEN | CTF_RED_FLAG_LOST
+ | CTF_BLUE_FLAG_CARRYING | CTF_BLUE_FLAG_TAKEN | CTF_BLUE_FLAG_LOST
+ | CTF_YELLOW_FLAG_CARRYING | CTF_YELLOW_FLAG_TAKEN | CTF_YELLOW_FLAG_LOST
+ | CTF_PINK_FLAG_CARRYING | CTF_PINK_FLAG_TAKEN | CTF_PINK_FLAG_LOST
+ | CTF_NEUTRAL_FLAG_CARRYING | CTF_NEUTRAL_FLAG_TAKEN | CTF_NEUTRAL_FLAG_LOST
+ | CTF_FLAG_NEUTRAL | CTF_SHIELDED);
// scan through all the flags and notify the client about them
for(flag = ctf_worldflaglist; flag; flag = flag.ctf_worldflagnext)
{
+ if(flag.team == NUM_TEAM_1) { t = CTF_RED_FLAG_CARRYING; t2 = CTF_RED_FLAG_TAKEN; t3 = CTF_RED_FLAG_LOST; }
+ if(flag.team == NUM_TEAM_2) { t = CTF_BLUE_FLAG_CARRYING; t2 = CTF_BLUE_FLAG_TAKEN; t3 = CTF_BLUE_FLAG_LOST; }
+ if(flag.team == NUM_TEAM_3) { t = CTF_YELLOW_FLAG_CARRYING; t2 = CTF_YELLOW_FLAG_TAKEN; t3 = CTF_YELLOW_FLAG_LOST; }
+ if(flag.team == NUM_TEAM_4) { t = CTF_PINK_FLAG_CARRYING; t2 = CTF_PINK_FLAG_TAKEN; t3 = CTF_PINK_FLAG_LOST; }
+ if(flag.team == 0) { t = CTF_NEUTRAL_FLAG_CARRYING; t2 = CTF_NEUTRAL_FLAG_TAKEN; t3 = CTF_NEUTRAL_FLAG_LOST; self.ctf_flagstatus |= CTF_FLAG_NEUTRAL; }
+
switch(flag.ctf_status)
{
case FLAG_PASSING:
case FLAG_CARRY:
{
if((flag.owner == self) || (flag.pass_sender == self))
- self.items |= ((flag.items & IT_KEY2) ? IT_RED_FLAG_CARRYING : IT_BLUE_FLAG_CARRYING); // carrying: self is currently carrying the flag
- else
- self.items |= ((flag.items & IT_KEY2) ? IT_RED_FLAG_TAKEN : IT_BLUE_FLAG_TAKEN); // taken: someone on self's team is carrying the flag
+ self.ctf_flagstatus |= t; // carrying: self is currently carrying the flag
+ else
+ self.ctf_flagstatus |= t2; // taken: someone else is carrying the flag
break;
}
case FLAG_DROPPED:
{
- self.items |= ((flag.items & IT_KEY2) ? IT_RED_FLAG_LOST : IT_BLUE_FLAG_LOST); // lost: the flag is dropped somewhere on the map
+ self.ctf_flagstatus |= t3; // lost: the flag is dropped somewhere on the map
break;
}
}
// item for stopping players from capturing the flag too often
if(self.ctf_captureshielded)
- self.items |= IT_CTF_SHIELDED;
+ self.ctf_flagstatus |= CTF_SHIELDED;
// update the health of the flag carrier waypointsprite
if(self.wps_flagcarrier)
frag_force *= autocvar_g_ctf_flagcarrier_forcefactor;
}
}
- else if(frag_target.flagcarried && (frag_target.deadflag == DEAD_NO) && DIFF_TEAM(frag_target, frag_attacker)) // if the target is a flagcarrier
+ else if(frag_target.flagcarried && (frag_target.deadflag == DEAD_NO) && CTF_DIFFTEAM(frag_target, frag_attacker)) // if the target is a flagcarrier
{
if(autocvar_g_ctf_flagcarrier_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_ctf_flagcarrier_auto_helpme_time)
}
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;
}
{
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;
}
-
-// ==========
-// 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;
}
-
-/*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;
}
-/*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;
+ }
- 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:
{
if(!g_ctf) { remove(self); return; }
- ctf_FlagSetup(1, self); // 1 = red
+ ctf_FlagSetup(NUM_TEAM_1, self);
}
/*QUAKED spawnfunc_item_flag_team2 (0 0.5 0.8) (-48 -48 -37) (48 48 37)
{
if(!g_ctf) { remove(self); return; }
- ctf_FlagSetup(0, self); // the 0 is misleading, but -- 0 = blue.
+ ctf_FlagSetup(NUM_TEAM_2, self);
+}
+
+/*QUAKED spawnfunc_item_flag_team3 (0 0.5 0.8) (-48 -48 -37) (48 48 37)
+CTF flag for team three (Yellow).
+Keys:
+"angle" Angle the flag will point (minus 90 degrees)...
+"model" model to use, note this needs red, blue yellow and pink as skins 0, 1, 2 and 3...
+"noise" sound played when flag is picked up...
+"noise1" sound played when flag is returned by a teammate...
+"noise2" sound played when flag is captured...
+"noise3" sound played when flag is lost in the field and respawns itself...
+"noise4" sound played when flag is dropped by a player...
+"noise5" sound played when flag touches the ground... */
+void spawnfunc_item_flag_team3()
+{
+ if(!g_ctf) { remove(self); return; }
+
+ ctf_FlagSetup(NUM_TEAM_3, self);
+}
+
+/*QUAKED spawnfunc_item_flag_team4 (0 0.5 0.8) (-48 -48 -37) (48 48 37)
+CTF flag for team four (Pink).
+Keys:
+"angle" Angle the flag will point (minus 90 degrees)...
+"model" model to use, note this needs red, blue yellow and pink as skins 0, 1, 2 and 3...
+"noise" sound played when flag is picked up...
+"noise1" sound played when flag is returned by a teammate...
+"noise2" sound played when flag is captured...
+"noise3" sound played when flag is lost in the field and respawns itself...
+"noise4" sound played when flag is dropped by a player...
+"noise5" sound played when flag touches the ground... */
+void spawnfunc_item_flag_team4()
+{
+ if(!g_ctf) { remove(self); return; }
+
+ ctf_FlagSetup(NUM_TEAM_4, self);
+}
+
+/*QUAKED spawnfunc_item_flag_neutral (0 0.5 0.8) (-48 -48 -37) (48 48 37)
+CTF flag (Neutral).
+Keys:
+"angle" Angle the flag will point (minus 90 degrees)...
+"model" model to use, note this needs red, blue yellow and pink as skins 0, 1, 2 and 3...
+"noise" sound played when flag is picked up...
+"noise1" sound played when flag is returned by a teammate...
+"noise2" sound played when flag is captured...
+"noise3" sound played when flag is lost in the field and respawns itself...
+"noise4" sound played when flag is dropped by a player...
+"noise5" sound played when flag touches the ground... */
+void spawnfunc_item_flag_neutral()
+{
+ if(!g_ctf) { remove(self); return; }
+ if(!cvar("g_ctf_oneflag")) { remove(self); return; }
+
+ ctf_FlagSetup(0, self);
}
/*QUAKED spawnfunc_ctf_team (0 .5 .8) (-16 -16 -24) (16 16 32)
// ==============
// 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);
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);
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; }
+ }
+
+ ctf_teams = bound(2, ctf_teams, 4);
+
// if no teams are found, spawn defaults
if(find(world, classname, "ctf_team") == world)
{
print("No ""ctf_team"" entities found on this map, creating them anyway.\n");
- ctf_SpawnTeam("Red", NUM_TEAM_1 - 1);
- ctf_SpawnTeam("Blue", NUM_TEAM_2 - 1);
+ float i;
+ for(i = 1; i <= ctf_teams; ++i)
+ ctf_SpawnTeam(Team_ColorName(Team_NumberToTeam(i)), Team_NumberToTeam(i) - 1);
}
- ctf_ScoreRules();
+ ctf_ScoreRules(ctf_teams);
}
void ctf_Initialize()
ctf_captureshield_max_ratio = autocvar_g_ctf_shield_max_ratio;
ctf_captureshield_force = autocvar_g_ctf_shield_force;
+ addstat(STAT_CTF_FLAGSTATUS, AS_INT, ctf_flagstatus);
+
InitializeEntity(world, ctf_DelayedInit, INITPRIO_GAMETYPE);
}
MUTATOR_HOOK(VehicleExit, ctf_VehicleExit, CBC_ORDER_ANY);
MUTATOR_HOOK(AbortSpeedrun, ctf_AbortSpeedrun, CBC_ORDER_ANY);
MUTATOR_HOOK(HavocBot_ChooseRole, ctf_BotRoles, CBC_ORDER_ANY);
+ MUTATOR_HOOK(GetTeamCount, ctf_GetTeamCount, CBC_ORDER_ANY);
+ MUTATOR_HOOK(SpectateCopy, ctf_SpectateCopy, CBC_ORDER_ANY);
+ MUTATOR_HOOK(FormatMessage, ctf_FormatMessage, CBC_ORDER_ANY);
MUTATOR_ONADD
{
#define VEHICLE_FLAG_SCALE 1.0
// waypoint colors
-#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) (('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
.string toucheffect;
.string passeffect;
.string capeffect;
+// backup default effects
+.float toucheffectnum;
+.float passeffectnum;
+.float capeffectnum;
// list of flags on the map
entity ctf_worldflaglist;
.entity ctf_dropper; // don't allow spam of dropping the flag
.float max_flag_health;
.float next_take_time;
+.float ctf_flagdamaged;
+float ctf_teams;
// passing/throwing properties
.float pass_distance;
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
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;
return FALSE;
}
+MUTATOR_HOOKFUNCTION(cts_WantWeapon)
+{
+ ret_float = (other.weapon == WEP_SHOTGUN);
+ return FALSE;
+}
+
+MUTATOR_HOOKFUNCTION(cts_AllowMobSpawning)
+{
+ ret_string = "You cannot spawn monsters in CTS";
+ return TRUE;
+}
+
void cts_Initialize()
{
cts_ScoreRules();
MUTATOR_HOOK(PlayerDamage_Calculate, cts_PlayerDamage, CBC_ORDER_ANY);
MUTATOR_HOOK(ForbidPlayerScore_Clear, cts_ForbidClearPlayerScore, CBC_ORDER_ANY);
MUTATOR_HOOK(SetModname, cts_SetMods, CBC_ORDER_ANY);
+ MUTATOR_HOOK(WantWeapon, cts_WantWeapon, CBC_ORDER_ANY);
+ MUTATOR_HOOK(AllowMobSpawning, cts_AllowMobSpawning, CBC_ORDER_ANY);
MUTATOR_ONADD
{
--- /dev/null
+void dm_DelayedInit()
+{
+}
+
+MUTATOR_HOOKFUNCTION(dm_CountFrags)
+{
+ // announce remaining frags
+ 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;
+}
-void dom_EventLog(string mode, float team_before, entity actor) // use an alias for easy changing and quick editing later
+void dom_EventLog(string mode, float cpteam, 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))) : "")));
-}
-
-void set_dom_state(entity e)
-{
- 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;
+ GameLogEcho(sprintf(":dom:%s:%d%s", mode, cpteam, ((actor != world) ? strcat(":", ftos(actor.playerid)) : "")));
}
void dompoint_captured ()
{
entity head;
- float old_delay, old_team, real_team;
+ float old_delay;
string msg = "dom-neut";
- // now that the delay has expired, switch to the latest team to lay claim to this point
- head = self.owner;
-
- real_team = self.cnt;
+ self.team = self.cnt;
self.cnt = -1;
dom_EventLog("taken", self.team, self.dmg_inflictor);
self.dmg_inflictor = world;
- self.goalentity = head;
- self.model = head.mdl;
- self.modelindex = head.dmg;
- self.skin = head.skin;
+ self.skin = dom_skin[self.team];
+ self.model = dom_model[self.team];
+ self.modelindex = dom_modelindex[self.team];
float points, wait_time;
if (autocvar_g_domination_point_amt)
wait_time = self.wait;
if(domination_roundbased)
- bprint(sprintf("^3%s^3%s\n", head.netname, self.message));
+ {
+ PlayerScore_Add(self.enemy, SP_SCORE, +points);
+ string msg = "";
+ if(dom_message[self.team] != "")
+ msg = dom_message[self.team];
+ if(self.message != "")
+ msg = self.message;
+ if(msg != "")
+ bprint(strcat(msg, "\n"));
+ else
+ Send_Notification(NOTIF_ALL, world, MSG_INFO, APP_TEAM_NUM_4(self.team, INFO_DOMINATION_CAPTURE_));
+ }
else
- Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_DOMINATION_CAPTURE_TIME, head.netname, self.message, points, wait_time);
+ {
+ string msg = "";
+ if(dom_message[self.team] != "")
+ msg = dom_message[self.team];
+ if(self.message != "")
+ msg = self.message;
+ if(msg != "")
+ Send_Notification(NOTIF_ALL, world, MSG_INFO, APP_TEAM_NUM_4(self.team, INFO_DOMINATION_CAPTURE_TIME_), msg, points, wait_time);
+ else
+ Send_Notification(NOTIF_ALL, world, MSG_INFO, APP_TEAM_NUM_4(self.team, INFO_DOMINATION_CAPTURE_TIME_NOMSG_), points, wait_time);
+ }
if(self.enemy.playerid == self.enemy_playerid)
PlayerScore_Add(self.enemy, SP_DOM_TAKES, 1);
else
self.enemy = world;
- if (head.noise != "")
- if(self.enemy)
- sound(self.enemy, CH_TRIGGER, head.noise, VOL_BASE, ATTEN_NORM);
- else
- sound(self, CH_TRIGGER, head.noise, VOL_BASE, ATTEN_NORM);
- if (head.noise1 != "")
- play2all(head.noise1);
+ if(dom_noise[self.team] != "")
+ if(self.enemy)
+ sound(self.enemy, CH_TRIGGER, dom_noise[self.team], VOL_BASE, ATTEN_NORM);
+ else
+ sound(self, CH_TRIGGER, dom_noise[self.team], VOL_BASE, ATTEN_NORM);
+
+ if(dom_noise1[self.team] != "")
+ play2all(dom_noise1[self.team]);
- self.delay = time + wait_time;
+ //self.delay = time + wait_time;
// do trigger work
old_delay = self.delay;
- old_team = self.team;
- self.team = real_team;
self.delay = 0;
activator = self;
SUB_UseTargets ();
self.delay = old_delay;
- self.team = old_team;
switch(self.team)
{
wait_time = autocvar_g_domination_point_rate;
else
wait_time = head.wait;
- switch(head.goalentity.team)
+ switch(head.team)
{
case NUM_TEAM_1:
pps_red += points/wait_time;
total_pps += points/wait_time;
}
- WaypointSprite_UpdateTeamRadar(self.sprite, RADARICON_DOMPOINT, colormapPaletteColor(self.goalentity.team - 1, 0));
+ WaypointSprite_UpdateTeamRadar(self.sprite, RADARICON_DOMPOINT, colormapPaletteColor(self.team - 1, 0));
WaypointSprite_Ping(self.sprite);
self.captime = time;
-
- FOR_EACH_REALCLIENT(head)
- set_dom_state(head);
}
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_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;
+ }
+}
+
+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;
- self.nextthink = time + 0.1;
+ self.nextthink = time;
//self.frame = self.frame + 1;
//if(self.frame > 119)
// give points
- if (gameover || self.delay > time || time < game_starttime) // game has ended, don't keep giving points
+ if (gameover || self.delay > time || time < game_starttime) // game has ended, don't keep giving points
return;
- if(autocvar_g_domination_point_rate)
+ if(self.dom_cooldown)
+ self.delay = time;
+ else if(autocvar_g_domination_point_rate)
self.delay = time + autocvar_g_domination_point_rate;
else
self.delay = time + self.wait;
// give credit to the team
// NOTE: this defaults to 0
if (!domination_roundbased)
- if (self.goalentity.netname != "")
+ if (self.team)
+ if (self.dom_active)
{
if(autocvar_g_domination_point_amt)
fragamt = autocvar_g_domination_point_amt;
else
fragamt = self.frags;
- TeamScore_AddToTeam(self.goalentity.team, ST_SCORE, fragamt);
- TeamScore_AddToTeam(self.goalentity.team, ST_DOM_TICKS, fragamt);
+ TeamScore_AddToTeam(self.team, ST_SCORE, fragamt);
+ TeamScore_AddToTeam(self.team, ST_DOM_TICKS, fragamt);
// give credit to the individual player, if he is still there
if (self.enemy.playerid == self.enemy_playerid)
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.dom_lastmessage = time + 2; // no spam
- self.goalentity = head;
- self.model = head.mdl;
- self.modelindex = head.dmg;
- self.skin = head.skin;
+ self.enemy = toucher; // individual player scoring
+ self.enemy_playerid = toucher.playerid;
+ dompoint_captured();
- self.enemy = other; // individual player scoring
- self.enemy_playerid = other.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];
- total_pps += points/waittime;
+ // main setup
+ self.think = dom_ControlPointThink;
+ self.nextthink = time;
- if(!self.t_width)
- self.t_width = 0.02; // frame animation rate
- if(!self.t_length)
- self.t_length = 239; // maximum frame
+ // waypoint setup
+ WaypointSprite_UpdateSprites(self.sprite, "dom-neut", "", "");
+ WaypointSprite_UpdateTeamRadar(self.sprite, RADARICON_DOMPOINT, '0 1 1');
- 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();
+ dom_Deactivate(self, dom_InitialCooldown(self));
+}
- waypoint_spawnforitem(self);
- WaypointSprite_SpawnFixed("dom-neut", self.origin + '0 0 32', self, sprite, RADARICON_DOMPOINT, '0 1 1');
+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.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.movetype = MOVETYPE_NONE;
+ }
+ else // drop to floor, automatically find a platform and set that as spawn origin
+ {
+ cp.noalign = FALSE;
+ self = cp;
+ droptofloor();
+ cp.movetype = MOVETYPE_NONE;
+ }
+
+ InitializeEntity(cp, dom_DelayedControlPointSetup, INITPRIO_SETLOCATION);
}
float total_controlpoints, redowned, blueowned, yellowowned, pinkowned;
for(e = world; (e = find(e, classname, "dom_controlpoint")) != world; )
{
++total_controlpoints;
- redowned += (e.goalentity.team == NUM_TEAM_1);
- blueowned += (e.goalentity.team == NUM_TEAM_2);
- yellowowned += (e.goalentity.team == NUM_TEAM_3);
- pinkowned += (e.goalentity.team == NUM_TEAM_4);
+ redowned += (e.team == NUM_TEAM_1);
+ blueowned += (e.team == NUM_TEAM_2);
+ yellowowned += (e.team == NUM_TEAM_3);
+ pinkowned += (e.team == NUM_TEAM_4);
}
}
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);
return 1;
}
Domination_count_controlpoints();
float winner_team = Domination_GetWinnerTeam();
-
+
if(winner_team == -1)
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_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);
+
return 1;
}
void Domination_RoundStart()
{
- entity e;
- FOR_EACH_PLAYER(e)
- e.player_blocked = 0;
+ entity oldself;
+ oldself = self;
+ FOR_EACH_PLAYER(self)
+ {
+ if(self.vehicle)
+ vehicles_exit(VHEF_RELEASE);
+ PutClientInServer();
+ }
+
+ self = oldself;
}
//go to best items, or control points you don't own
MUTATOR_HOOKFUNCTION(dom_GetTeamCount)
{
- ret_float = domination_teams;
- return 0;
+ //ret_float = domination_teams;
+ ret_string = "dom_team";
+ return TRUE;
}
MUTATOR_HOOKFUNCTION(dom_ResetMap)
{
total_pps = 0, pps_red = 0, pps_blue = 0, pps_yellow = 0, pps_pink = 0;
- 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 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;
}
MUTATOR_HOOKFUNCTION(dom_PlayerSpawn)
{
- 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)
{
- set_dom_state(self);
+ self.dom_unlock_progress = other.dom_unlock_progress;
+ return FALSE;
+}
+
+MUTATOR_HOOKFUNCTION(dom_PlayerDamage)
+{
+ 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;
}
*/
void spawnfunc_dom_controlpoint()
{
- if(!g_domination)
- {
- remove(self);
- return;
- }
- self.think = dom_controlpoint_setup;
- self.nextthink = time + 0.1;
- self.reset = dom_controlpoint_setup;
-
- if(!self.scale)
- self.scale = 0.6;
+ if(!g_domination) { remove(self); return; }
- self.effects = self.effects | EF_LOWPRECISION;
- if (autocvar_g_domination_point_fullbright)
- self.effects |= EF_FULLBRIGHT;
+ dom_ControlPointSetup(self);
}
/*QUAKED spawnfunc_dom_team (0 .5 .8) (-32 -32 -24) (32 32 32)
void spawnfunc_dom_team()
{
- if(!g_domination || autocvar_g_domination_teams_override >= 2)
- {
- remove(self);
- return;
- }
- precache_model(self.model);
- if (self.noise != "")
- precache_sound(self.noise);
- if (self.noise1 != "")
- precache_sound(self.noise1);
+ if(!g_domination || autocvar_g_domination_teams_override >= 2) { remove(self); return; }
+
self.classname = "dom_team";
- setmodel(self, self.model); // precision not needed
- self.mdl = self.model;
- self.dmg = self.modelindex;
- self.model = "";
- self.modelindex = 0;
// this would have to be changed if used in quakeworld
if(self.cnt)
self.team = self.cnt + 1; // WHY are these different anyway?
+
+ precache_model(self.model);
+ setmodel(self, self.model);
+ dom_model[self.team] = self.model;
+ dom_modelindex[self.team] = self.modelindex;
+
+ self.model = "";
+ self.modelindex = 0;
+
+ if(self.noise != "") { precache_sound(self.noise); }
+ if(self.noise1 != "") { precache_sound(self.noise1); }
+
+ dom_noise[self.team] = self.noise;
+ dom_noise1[self.team] = self.noise1;
+ dom_message[self.team] = self.message;
+ dom_skin[self.team] = self.skin;
}
// scoreboard setup
oldself = self;
self = spawn();
self.classname = "dom_team";
- self.netname = teamname;
- self.cnt = teamcolor;
self.model = pointmodel;
self.skin = pointskin;
- self.noise = capsound;
- self.noise1 = capnarration;
- self.message = capmessage;
-
- // this code is identical to spawnfunc_dom_team
- setmodel(self, self.model); // precision not needed
- self.mdl = self.model;
- self.dmg = self.modelindex;
+ self.netname = teamname; // needed for team counting
+ self.team = teamcolor;
+
+ precache_model(self.model);
+ setmodel(self, self.model);
+ dom_model[self.team] = self.model;
+ dom_modelindex[self.team] = self.modelindex;
+
self.model = "";
self.modelindex = 0;
- // this would have to be changed if used in quakeworld
- self.team = self.cnt + 1;
+
+ if(capsound != "") { precache_sound(capsound); }
+ if(capnarration != "") { precache_sound(capnarration); }
+
+ dom_noise[self.team] = capsound;
+ dom_noise1[self.team] = capnarration;
+ dom_message[self.team] = capmessage;
+ dom_skin[self.team] = pointskin;
+
+ //printf("%d %s %d\n", self.team, dom_model[self.team], dom_modelindex[self.team]);
- //eprint(self);
self = oldself;
}
// spawn some default teams if the map is not set up for domination
void dom_spawnteams(float teams)
{
- dom_spawnteam("Red", NUM_TEAM_1-1, "models/domination/dom_red.md3", 0, "domination/claim.wav", "", "Red team has captured a control point");
- dom_spawnteam("Blue", NUM_TEAM_2-1, "models/domination/dom_blue.md3", 0, "domination/claim.wav", "", "Blue team has captured a control point");
- if(teams >= 3)
- dom_spawnteam("Yellow", NUM_TEAM_3-1, "models/domination/dom_yellow.md3", 0, "domination/claim.wav", "", "Yellow team has captured a control point");
- if(teams >= 4)
- dom_spawnteam("Pink", NUM_TEAM_4-1, "models/domination/dom_pink.md3", 0, "domination/claim.wav", "", "Pink team has captured a control point");
+ float i, team_id;
+ for(i = 1; i <= teams; ++i)
+ {
+ team_id = Team_NumberToTeam(i);
+
+ dom_spawnteam(Team_ColorName(team_id), team_id, sprintf("models/domination/dom_%s.md3", Static_Team_ColorName_Lower(team_id)), 0, "domination/claim.wav", "", "");
+ }
+
dom_spawnteam("", 0, "models/domination/dom_unclaimed.md3", 0, "", "", "");
}
CheckAllowedTeams(world);
domination_teams = ((c4>=0) ? 4 : (c3>=0) ? 3 : 2);
+ addstat(STAT_CAPTURE_PROGRESS, AS_FLOAT, dom_unlock_progress);
addstat(STAT_DOM_TOTAL_PPS, AS_FLOAT, dom_total_pps);
addstat(STAT_DOM_PPS_RED, AS_FLOAT, dom_pps_red);
addstat(STAT_DOM_PPS_BLUE, AS_FLOAT, dom_pps_blue);
{
MUTATOR_HOOK(GetTeamCount, dom_GetTeamCount, CBC_ORDER_ANY);
MUTATOR_HOOK(reset_map_players, dom_ResetMap, CBC_ORDER_ANY);
+ MUTATOR_HOOK(PlayerPreThink, dom_PlayerThink, CBC_ORDER_ANY);
MUTATOR_HOOK(PlayerSpawn, dom_PlayerSpawn, CBC_ORDER_ANY);
- MUTATOR_HOOK(ClientConnect, dom_ClientConnect, CBC_ORDER_ANY);
+ MUTATOR_HOOK(SpectateCopy, dom_SpectateCopy, CBC_ORDER_ANY);
+ MUTATOR_HOOK(PlayerDamage_Calculate, dom_PlayerDamage, CBC_ORDER_ANY);
MUTATOR_HOOK(HavocBot_ChooseRole, dom_BotRoles, CBC_ORDER_ANY);
MUTATOR_ONADD
return -1;
}
- return 0;
+ return FALSE;
}
.entity sprite;
.float captime;
+.float dom_unlock_progress;
+.float dom_cooldown;
+.float dom_cooldown_max;
+.float dom_active;
+.float dom_lastmessage;
+
+.entity dom_capturingplayer;
+
+.float pointupdatetime;
+
+// control point properties
+#define DOM_CP_MIN '-32 -32 -32'
+#define DOM_CP_MAX '32 32 32'
+#define DOM_CP_WPOFFSET ('0 0 32')
+#define CP_SCALE 0.6
+#define CP_TICRATE 0.02
+#define CP_MAXFRAMES 239
+
+string dom_model[17];
+float dom_modelindex[17];
+string dom_noise[17];
+string dom_noise1[17];
+string dom_message[17];
+float dom_skin[17];
+
// misc globals
float domination_roundbased;
float domination_teams;
float prev_missing_teams_mask;
float freezetag_CheckTeams()
{
+ freezetag_count_alive_players();
+
if(FREEZETAG_ALIVE_TEAMS_OK())
{
if(prev_missing_teams_mask > 0)
return -1; // no player left
}
+float ft_EnemyWaypoint_Customize()
+{
+ if(self.owner.frozen || self.owner.deadflag) { return FALSE; }
+
+ entity e = WaypointSprite_getviewentity(other);
+
+ if(SAME_TEAM(self.owner, e)) { return FALSE; }
+
+ 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_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)
{
- 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;
Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_ROUND_TIED);
}
+ 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);
+
round_handler_Init(5, autocvar_g_freezetag_warmup, autocvar_g_freezetag_round_timelimit);
return 1;
}
self.freezetag_frozen_timeout = 0;
Unfreeze(self);
+
+ self.health = autocvar_g_freezetag_revive_health;
}
float freezetag_isEliminated(entity e)
if(!self.frozen)
freezetag_LastPlayerForTeam_Notify();
freezetag_Unfreeze(world);
- freezetag_count_alive_players();
return 1;
}
{
if(self.frozen)
freezetag_Unfreeze(world);
- freezetag_count_alive_players();
return 1; // let the player die so that he can respawn whenever he wants
}
if(self.frozen != 1)
{
freezetag_Add_Score(frag_attacker);
- freezetag_count_alive_players();
freezetag_LastPlayerForTeam_Notify();
}
else
if(self.frozen)
return 1;
+ if(g_instagib)
+ frag_target.ammo_cells = start_ammo_cells; // we need more ammo in instagib, otherwise the player will defrost & die again
+
freezetag_Freeze(frag_attacker);
freezetag_LastPlayerForTeam_Notify();
if(self.freezetag_frozen_timeout == -1) // if PlayerSpawn is called by reset_map_players
return 1; // do nothing, round is starting right now
+ if(ft_stalemate)
+ if(!self.wps_enemy)
+ ft_EnemyWaypoints(self);
+
if(self.freezetag_frozen_timeout == -2) // player was dead
{
freezetag_Freeze(world);
return 1;
}
- freezetag_count_alive_players();
-
if(round_handler_IsActive())
if(round_handler_IsRoundStarted())
{
self.freezetag_frozen_timeout = -1;
PutClientInServer();
self.freezetag_frozen_timeout = 0;
+ WaypointSprite_Kill(self.wps_enemy);
}
- freezetag_count_alive_players();
return 1;
}
+MUTATOR_HOOKFUNCTION(freezetag_ResetMap)
+{
+ ft_stalemate = FALSE;
+ return FALSE;
+}
+
MUTATOR_HOOKFUNCTION(freezetag_GiveFragsForKill)
{
frag_score = 0; // no frags counted in Freeze Tag
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 * ((warmup_stage) ? warmup_start_health : start_health));
+ self.health = max(1, self.revive_progress * ((warmup_stage) ? warmup_start_health : autocvar_g_freezetag_revive_health));
if(self.revive_progress >= 1)
{
freezetag_Unfreeze(self);
- freezetag_count_alive_players();
if(n == -1)
{
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 * ((warmup_stage) ? warmup_start_health : start_health));
+ self.health = max(1, self.revive_progress * ((warmup_stage) ? warmup_start_health : autocvar_g_freezetag_revive_health));
}
else if(!n && !self.frozen)
{
self.revive_progress = 0; // thawing nobody
}
+ if(self.wps_enemy)
+ WaypointSprite_UpdateHealth(self.wps_enemy, '1 0 0' * healtharmor_maxdamage(self.health, self.armorvalue, autocvar_g_balance_armor_blockpercent, DEATH_WEAPON));
+
return 1;
}
MUTATOR_HOOKFUNCTION(freezetag_GetTeamCount)
{
ret_float = freezetag_teams;
- return 0;
+ return FALSE;
}
void freezetag_Initialize()
--- /dev/null
+// stalemate
+float ft_stalemate;
+
+// waypoint setup
+.entity wps_enemy;
+#define WPCOLOR_ENEMY(t) ((t) ? colormapPaletteColor(t - 1, FALSE) * 0.75 : '1 1 1')
+
+#define ENEMY_WAYPOINT_OFFSET ('0 0 64')
--- /dev/null
+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;
+ round_handler_Init(5, autocvar_g_infection_warmup, autocvar_g_infection_round_timelimit);
+ 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);
+
+ 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;
+ 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;
+}
+
+MUTATOR_HOOKFUNCTION(inf_BotAttack)
+{
+ if(self.inf_team == other.inf_team)
+ return TRUE;
+
+ return FALSE;
+}
+
+MUTATOR_HOOKFUNCTION(inf_PlayerThink)
+{
+ // temporary hack to fix colors
+ setcolor(self, 16 * self.inf_team + self.inf_team);
+ return FALSE;
+}
+
+MUTATOR_HOOKFUNCTION(inf_PlayerSpawn)
+{
+ if(IS_REAL_CLIENT(self))
+ if(!self.inf_team)
+ if(bots_would_leave)
+ {
+ 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;
+}
+
+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;
+}
+
+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;
+}
+
+MUTATOR_HOOKFUNCTION(inf_ForbidPlayerScore_Clear)
+{
+ return TRUE;
+}
+
+// scoreboard stuff
+void inf_ScoreRules()
+{
+ 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;
+
+ 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;
+}
--- /dev/null
+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
if(!g_invasion) { remove(self); return; }
self.classname = "invasion_spawnpoint";
-
- if(autocvar_g_invasion_zombies_only) // precache only if it hasn't been already
- if(self.monsterid)
- MON_ACTION(self.monsterid, MR_PRECACHE);
}
float invasion_PickMonster(float supermonster_count)
if(round_handler_GetEndTime() > 0 && round_handler_GetEndTime() - time <= 0)
{
FOR_EACH_MONSTER(head)
- monster_remove(head);
+ Monster_Remove(head);
Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_ROUND_OVER);
Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_ROUND_OVER);
return 1;
}
- float total_alive_monsters = 0, supermonster_count = 0, red_alive = 0, blue_alive = 0, yellow_alive = 0, pink_alive = 0;
+ float total_alive_monsters = 0, supermonster_count = 0, red_alive = 0, blue_alive = 0, yellow_alive = 0, pink_alive = 0;;
FOR_EACH_MONSTER(head) if(head.health > 0)
{
}
FOR_EACH_MONSTER(head)
- monster_remove(head);
+ Monster_Remove(head);
if(teamplay)
{
round_handler_Init(5, autocvar_g_invasion_warmup, autocvar_g_invasion_round_timelimit);
+ nades_Clear(world, TRUE);
+
return 1;
}
self.monster_skill = inv_monsterskill;
if((get_monsterinfo(self.monsterid)).spawnflags & MON_FLAG_SUPERMONSTER)
- Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_INVASION_SUPERMONSTER, M_NAME(self.monsterid));
+ Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_INVASION_SUPERMONSTER, self.monster_name);
self.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_BOTCLIP | DPCONTENTS_MONSTERCLIP;
MUTATOR_HOOKFUNCTION(invasion_BotShouldAttack)
{
- if(!(checkentity.flags & FL_MONSTER))
+ if(!IS_MONSTER(other))
return TRUE;
return FALSE;
MUTATOR_HOOKFUNCTION(invasion_AccuracyTargetValid)
{
- if(frag_target.flags & FL_MONSTER)
+ if(IS_MONSTER(frag_target))
return MUT_ACCADD_INVALID;
return MUT_ACCADD_INDIFFERENT;
}
MUTATOR_HOOKFUNCTION(invasion_AllowMobSpawning)
{
- // monster spawning disabled during an invasion
+ ret_string = "You can't spawn monsters during an invasion!";
return TRUE;
}
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;
+
inv_roundcnt = 0;
inv_maxrounds = 15; // 15?
}
void invasion_Initialize()
{
- if(autocvar_g_invasion_zombies_only)
- MON_ACTION(MON_ZOMBIE, MR_PRECACHE);
- else
- {
- float i;
- entity mon;
- for(i = MON_FIRST; i <= MON_LAST; ++i)
- {
- mon = get_monsterinfo(i);
- if((mon.spawnflags & MONSTER_TYPE_FLY) || (mon.spawnflags & MONSTER_TYPE_SWIM))
- continue; // flying/swimming monsters not yet supported
-
- MON_ACTION(i, MR_PRECACHE);
- }
- }
-
InitializeEntity(world, invasion_DelayedInit, INITPRIO_GAMETYPE);
}
--- /dev/null
+// round handling
+float total_players;
+float redalive, bluealive, yellowalive, pinkalive;
+.float redalive_stat, bluealive_stat, yellowalive_stat, pinkalive_stat;
+float allowed_to_spawn;
+
+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;
+ 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;
+ 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;
+
+ 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);
+
+ return 1;
+}
+
+void JB_RoundStart()
+{
+ if(warmup_stage)
+ allowed_to_spawn = TRUE;
+ else
+ allowed_to_spawn = FALSE;
+}
+
+float prev_missing_teams_mask;
+float JB_CheckTeams()
+{
+ 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;
+
+ 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;
+ 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);
+ 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 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;
+
+ 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_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_imprisoner = world;
+ 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;
+
+ 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.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);
+ 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;
+}
+
+
+void JB_SetupJailCamera()
+{
+ if(!g_jailbreak) { remove(self); return; }
+
+ self.classname = "info_jailbreak_jailcamera";
+
+ 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_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;
+ }
+}
+
+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_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;
+
+ 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;
+ 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;
+ 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_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; }
+
+ entity e = WaypointSprite_getviewentity(other);
+
+ // hide from owner's team
+ if(SAME_TEAM(self.owner, e)) { return FALSE; }
+
+ 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.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_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 FALSE;
+}
+
+MUTATOR_HOOKFUNCTION(jb_ResetMap)
+{
+ 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;
+
+ FOR_EACH_CLIENT(self)
+ {
+ if(IS_PLAYER(self))
+ {
+ JB_Release(world);
+ PutClientInServer();
+ }
+ self.player_blocked = 0;
+ self.weapon_blocked = FALSE;
+ self.jb_roundlost = FALSE;
+ }
+
+ 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;
+
+ 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;
+}
+
+MUTATOR_HOOKFUNCTION(jb_PlayerSpawn)
+{
+ //self.jb_isprisoned = 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;
+}
+
+MUTATOR_HOOKFUNCTION(jb_SpectateCopy)
+{
+ self.stat_jb_isprisoned = other.jb_isprisoned;
+ self.jb_unlock_progress = other.jb_unlock_progress;
+ return FALSE;
+}
+
+MUTATOR_HOOKFUNCTION(jb_RemovePlayer)
+{
+ if(self.jb_isprisoned)
+ JB_Release(world);
+
+ self.jb_roundlost = FALSE;
+
+ return FALSE;
+}
+
+MUTATOR_HOOKFUNCTION(jb_PlayerPreThink)
+{
+ if(gameover)
+ {
+ 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;
+ }
+
+ 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;
+
+ 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;
+}
+
+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;
+}
+
+MUTATOR_HOOKFUNCTION(jb_ForbidThrowing)
+{
+ if(self.jb_isprisoned)
+ return TRUE;
+
+ return FALSE;
+}
+
+MUTATOR_HOOKFUNCTION(jb_GiveFrags)
+{
+ if(jb_roundover)
+ {
+ frag_score = 0;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+MUTATOR_HOOKFUNCTION(jb_GetTeamCount)
+{
+ ret_float = jb_teams;
+ return FALSE;
+}
+
+MUTATOR_HOOKFUNCTION(jb_AllowMobSpawning)
+{
+ if(self.jb_isprisoned)
+ {
+ ret_string = "You can't spawn monsters in prison!";
+ return TRUE;
+ }
+
+ 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
+ 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);
+}
+
+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;
+}
--- /dev/null
+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
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_Ping(self.waypointsprite_attachedforcarrier);
}
if(other.deadflag != DEAD_NO) { return; }
if(other.frozen) { return; }
- if (!IS_PLAYER(other))
+ if(!IS_PLAYER(other))
{ // The ball just touched an object, most likely the world
- pointparticles(particleeffectnum("kaball_sparks"), self.origin, '0 0 0', 1);
+ Send_Effect(EFFECT_BALL_SPARKS, self.origin, '0 0 0', 1);
sound(self, CH_TRIGGER, "keepaway/touch.wav", VOL_BASE, ATTEN_NORM);
return;
}
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;
+}
+
MUTATOR_HOOKFUNCTION(ka_BotRoles)
{
if (self.ballcarried)
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;
+}
+
// ==============
// Initialization
MUTATOR_HOOK(PlayerDamage_Calculate, ka_PlayerDamage, CBC_ORDER_ANY);
MUTATOR_HOOK(PlayerPowerups, ka_PlayerPowerups, CBC_ORDER_ANY);
MUTATOR_HOOK(PlayerUseKey, ka_PlayerUseKey, CBC_ORDER_ANY);
+ MUTATOR_HOOK(PlayerPhysics, ka_PlayerPhysics, CBC_ORDER_ANY);
MUTATOR_HOOK(HavocBot_ChooseRole, ka_BotRoles, CBC_ORDER_ANY);
+ MUTATOR_HOOK(BotShouldAttack, ka_BotShouldAttack, CBC_ORDER_ANY);
MUTATOR_ONADD
{
-#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;
-
-#define ST_KH_CAPS 1
-#define SP_KH_CAPS 4
-#define SP_KH_PUSHES 5
-#define SP_KH_DESTROYS 6
-#define SP_KH_PICKUPS 7
-#define SP_KH_KCKILLS 8
-#define 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();
-}
+// =========================================================
+// Unofficial key hunt game mode coding, reworked by Mario
+// =========================================================
-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)
{
- 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; }
+ else
+ { return FALSE; }
+ }
+ 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)
{
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()
{
-#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
+ float 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);
+ 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)
{
- 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;
+ 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);
- 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; }
}
-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
+float total_players;
+float redalive, bluealive, yellowalive, pinkalive;
+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)
- kh_update_state();
+float prev_missing_teams_mask;
+float KH_CheckTeams()
+{
+ 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;
+ 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;
+}
- 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);
-}
+// ====================
+// Drop/Pass/Throw Code
+// ====================
-void kh_WinnerTeam(float teem) // runs when a team wins // Samual: Teem?.... TEEM?!?! what the fuck is wrong with you people
+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));
- 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);
+
+ // 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_));
- }
- }
-
- self.nextthink = time + 0.05;
-}
-
-void key_reset()
-{
- kh_Key_AssignTo(self, world);
- kh_Key_Remove(self);
-}
+ 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'));
-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";
- break;
- case NUM_TEAM_2:
- key.netname = "^4blue key";
- break;
- case NUM_TEAM_3:
- key.netname = "^3yellow 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);
+ kh_Handle_Drop(key, player, droptype);
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);
+ 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);
+
+ 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
}
+
+ play2all("kh/capture.wav");
}
-float kh_CheckPlayers(float num)
+void kh_Handle_Pickup(entity key, entity player, float pickuptype)
{
- if(num < kh_teams)
+ // declarations
+ float pickup_dropped_score; // used to calculate dropped pickup score
+
+ // attach the key to the player
+ key.owner = player;
+ if(player.vehicle)
+ {
+ 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
{
- 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;
+ 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;
+ }
- if (!players) { return t_team; }
+ // 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)
+ {
+ 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
}
- return 0;
-}
-void kh_WaitForPlayers() // delay start of the round until enough players are present
-{
- if(time < game_starttime)
+ switch(pickuptype)
{
- kh_Controller_SetThink(game_starttime - time + 0.1, kh_WaitForPlayers);
- return;
+ case PICKUP_KILLED:
+ case PICKUP_DROPPED: key.health = key.max_key_health; break; // reset health/return timelimit
+ default: break;
}
- float p1 = kh_CheckPlayers(0), p2 = kh_CheckPlayers(1), p3 = kh_CheckPlayers(2), p4 = kh_CheckPlayers(3);
- if (!(p1 || p2 || p3 || p4))
+ // messages and sounds
+ if(pickuptype == PICKUP_START)
{
- 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);
+ 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_CENTER, CENTER_KEYHUNT_WAIT, p1, p2, p3, p4);
- kh_Controller_SetThink(1, kh_WaitForPlayers);
+ 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);
+
+ sound(player, CH_TRIGGER, key.snd_key_taken, VOL_BASE, ATTEN_NONE);
}
-}
-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);
+ // scoring
+ PlayerScore_Add(player, SP_KH_PICKUPS, 1);
+ nades_GiveBonus(player, autocvar_g_nades_bonus_score_minor);
+ switch(pickuptype)
+ {
+ case PICKUP_KILLED:
+ case PICKUP_DROPPED:
+ {
+ 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;
+ }
- kh_tracking_enabled = TRUE;
-}
+ default: break;
+ }
-void kh_StartRound() // runs at the start of each round
-{
- float i, players, teem;
- entity player;
+ // effects
+ Send_Effect(key.toucheffectnum, player.origin, '0 0 0', 1);
- if(time < game_starttime)
- {
- kh_Controller_SetThink(game_starttime - time + 0.1, kh_WaitForPlayers);
- return;
- }
+ // waypoints
+ if(pickuptype == PICKUP_DROPPED || pickuptype == PICKUP_KILLED) { WaypointSprite_Kill(key.wps_keydropped); }
+ kh_KeycarrierWaypoints(player);
+ WaypointSprite_Ping(player.wps_keycarrier);
+}
- float p1 = kh_CheckPlayers(0), p2 = kh_CheckPlayers(1), p3 = kh_CheckPlayers(2), p4 = kh_CheckPlayers(3);
- if(p1 || p2 || p3 || p4)
- {
- kh_Controller_SetThink(1, kh_WaitForPlayers);
- Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_KEYHUNT_WAIT, p1, p2, p3, p4);
- return;
- }
- Kill_Notification(NOTIF_ALL, world, MSG_CENTER_CPID, CPID_KEYHUNT);
- Kill_Notification(NOTIF_ALL, world, MSG_CENTER_CPID, CPID_KEYHUNT_OTHER);
+// ===================
+// Main Key Functions
+// ===================
- for(i = 0; i < kh_teams; ++i)
+void kh_CheckKeyReturn(entity key, float returntype)
+{
+ if((key.kh_status == KEY_DROPPED) || (key.kh_status == KEY_PASSING))
{
- 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)
+ if(key.wps_keydropped) { WaypointSprite_UpdateHealth(key.wps_keydropped, key.health); }
+
+ 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)
{
- ++players;
- if(random() * players <= 1)
- my_player = player;
+ kh_Handle_Pickup(key, killer, PICKUP_KILLED);
+ return;
}
- kh_Key_Spawn(my_player, 360 * i / kh_teams, i);
+ 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;
+ }
+ }
+
+ 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_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);
+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;
+ }
}
-float kh_HandleFrags(entity attacker, entity targ, float f) // adds to the player score
+void kh_KeyThink()
{
- if(attacker == targ)
- return f;
+ 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;
+ }
- if(targ.kh_next)
+ // main think method
+ switch(self.kh_status)
{
- if(attacker.team == targ.team)
+ case KEY_DROPPED:
{
- 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);
+ 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);
+ }
+ return;
+ }
+
+ default: // this should never happen
+ {
+ dprint("kh_KeyThink(): Key exists with no status?\n");
+ return;
+ }
+ }
+}
+
+float kh_Customize()
+{
+ entity e = WaypointSprite_getviewentity(other);
+
+ if(self.owner == e)
+ self.glow_trail = 0;
+ else if(autocvar_g_keyhunt_key_glowtrails)
+ self.glow_trail = 1;
+
+ return TRUE;
+}
+
+void kh_KeyTouch()
+{
+ 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
{
- 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
+ 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;
+ }
- return f;
+ case KEY_PASSING:
+ {
+ if((IS_PLAYER(toucher)) && (toucher.deadflag == DEAD_NO) && (toucher != self.pass_sender))
+ kh_Handle_Retrieve(self, toucher);
+ break;
+ }
+ }
}
-void kh_Initialize() // sets up th KH environment
+void kh_RemoveKey(entity key)
{
- 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");
+ // kill old waypointsprite
+ WaypointSprite_Kill(key.owner.wps_keycarrier);
+ WaypointSprite_Kill(key.owner.wps_enemykeycarrier);
- // 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.kh_status == KEY_DROPPED)
+ { WaypointSprite_Kill(key.wps_keydropped); }
- // 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);
+ // reset the key
+ setattachment(key, world, "");
- kh_ScoreRules(kh_teams);
+ key.think = SUB_Remove;
+ key.nextthink = time;
+}
+
+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.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);
}
-void kh_finalize()
+
+// ==================
+// 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)
{
- // to be called before intermission
- kh_FinishRound();
- remove(kh_controller);
- kh_controller = world;
+ 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);
}
-// register this as a mutator
+void havocbot_role_kh_carrier()
+{
+ if(self.deadflag != DEAD_NO)
+ return;
+
+ entity key;
+ float is_carrier = FALSE;
+ KH_FOR_EACH_KEY(key)
+ if(key.owner == self)
+ {
+ 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
-MUTATOR_HOOKFUNCTION(kh_Key_DropAll)
+ navigation_goalrating_end();
+ }
+}
+
+void havocbot_role_kh_defense()
{
- kh_Key_DropAll(self, TRUE);
- return 0;
+ if(self.deadflag != DEAD_NO)
+ return;
+
+ entity key;
+ float is_carrier = FALSE;
+ KH_FOR_EACH_KEY(key)
+ if(key.owner == self)
+ {
+ 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;
+ KH_FOR_EACH_KEY(key)
+ if(key.owner == self)
+ {
+ 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;
+ KH_FOR_EACH_KEY(key)
+ if(key.owner == self)
+ {
+ 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;
+}
+
+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;
+ KH_FOR_EACH_KEY(key)
+ {
+ 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;
}
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;
+
+ 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;
}
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;
}
-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;
}
-MUTATOR_HOOKFUNCTION(kh_SpectateCopy)
+MUTATOR_HOOKFUNCTION(kh_PlayerUseKey)
{
- 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);
+
+ 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;
+ }
+ 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; }
+ }
+
+ // 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;
+ }
+ 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;
+ }
+ }
+ 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 FALSE;
}
-MUTATOR_HOOKFUNCTION(kh_PlayerUseKey)
+MUTATOR_HOOKFUNCTION(kh_ResetMapGlobal)
+{
+ entity e, key;
+ FOR_EACH_CLIENT(e)
+ {
+ e.kh_lastkiller = world;
+ }
+ KH_FOR_EACH_KEY(key) { if(!wasfreed(key)) kh_RemoveKey(key); }
+ kh_worldkeylist = world; // reset key list
+ return FALSE;
+}
+
+MUTATOR_HOOKFUNCTION(kh_ResetMap)
+{
+ // don't reset players
+ 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_Ping(self.wps_helpme);
+ }
+
+ return TRUE;
+}
+
+MUTATOR_HOOKFUNCTION(kh_VehicleEnter)
{
- if(MUTATOR_RETURNVALUE == 0)
+ entity key;
+ if(!autocvar_g_keyhunt_allow_vehicle_carry && !autocvar_g_keyhunt_allow_vehicle_touch)
{
- entity k;
- k = self.kh_next;
- if(k)
+ 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;
}
+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;
+}
+
+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
+
+ //dprint("stopping the ", key.netname, " from moving.\n");
+ break;
+ }
+
+ default:
+ case KEY_CARRY:
+ {
+ // do nothing for these keys
+ break;
+ }
+ }
+ }
+
+ 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;
+}
+
+MUTATOR_HOOKFUNCTION(kh_GetTeamCount)
+{
+ ret_float = kh_teams;
+ return FALSE;
+}
+
+
+// ==============
+// Initialization
+// ==============
+
+// scoreboard setup
+void kh_ScoreRules(float teams)
+{
+ CheckAllowedTeams(world);
+ 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
{
-// ALL OF THESE should be removed in the future, as other code should not have
-// to care
+// these are needed since mutators are compiled last
-// used by bots:
-float kh_tracking_enabled;
-.entity kh_next;
-float kh_Key_AllOwnedByWhichTeam();
+#ifdef SVQC
-typedef void(void) kh_Think_t;
-void kh_StartRound();
-void kh_Controller_SetThink(float t, kh_Think_t func);
+// 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
+
+// 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')
+
+#define KH_KEY_ZSHIFT 22
+#define KH_KEY_XYDIST 24
+#define KH_KEY_XYSPEED 45
+
+#define KH_VEHICLE_KEY_XYDIST 100
+#define KH_VEHICLE_KEY_ZSHIFT 80
+
+#define KEY_SCALE 1
+
+#define KEY_BRIGHTNESS 2
+
+#define KEY_THINKRATE 0.1
+#define KEY_TOUCHRATE 0.5
+
+#define KEY_DROP_OFFSET ('0 0 16')
+#define 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')
+
+#define VEHICLE_KEY_OFFSET ('0 0 96')
+#define VEHICLE_KEY_SCALE 2
+
+// waypoint colors
+#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)
+
+// 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;
+
+// list of keys on the map
+entity kh_worldkeylist;
+.entity kh_worldkeynext;
+.entity kh_stalekeynext;
+
+// waypoint sprites
+.entity wps_helpme;
+.entity wps_keycarrier;
+.entity wps_keydropped;
+.entity wps_enemykeycarrier;
+.float wps_helpme_time;
+float wpforenemy_announced;
+float wpforenemy_nextthink;
+
+// statuses
+#define KEY_DROPPED 2
+#define KEY_CARRY 3
+#define 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;
+
+// key properties
+.float kh_pickuptime;
+.float kh_droptime;
+.float 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;
+.float next_take_time;
+float kh_teams;
+
+// passing/throwing properties
+.float pass_distance;
+.entity pass_sender;
+.entity pass_target;
+.float throw_antispam;
+.float throw_prevtime;
+.float 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;
// 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;
}
MUTATOR_HOOKFUNCTION(lms_RemovePlayer)
{
// Only if the player cannot play at all
- if(PlayerScore_Add(self, SP_LMS_RANK, 0) == 666)
+ if(PlayerScore_Add(self, SP_LMS_RANK, 0) == 666 || PlayerScore_Add(self, SP_LMS_LIVES, 0) > 0)
self.frags = FRAGS_SPECTATOR;
else
self.frags = FRAGS_LMS_LOSER;
MUTATOR_HOOKFUNCTION(lms_ClientConnect)
{
- self.classname = "player";
+ if(autocvar_g_lms_join_anytime)
+ self.frags = FRAGS_SPECTATOR;
+ else
+ self.classname = "player";
+
campaign_bots_may_start = 1;
if(PlayerScore_Add(self, SP_LMS_LIVES, LMS_NewPlayerLives()) <= 0)
return MUT_ITEMTOUCH_CONTINUE;
}
+MUTATOR_HOOKFUNCTION(lms_WantWeapon)
+{
+ if(other.spawnflags & WEP_FLAG_NORMAL)
+ ret_float = TRUE;
+ else
+ ret_float = FALSE;
+
+ return FALSE;
+}
+
// scoreboard stuff
void lms_ScoreRules()
{
MUTATOR_HOOK(ForbidPlayerScore_Clear, lms_KeepScore, CBC_ORDER_ANY);
MUTATOR_HOOK(FilterItem, lms_FilterItem, CBC_ORDER_ANY);
MUTATOR_HOOK(ItemTouch, lms_ItemTouch, CBC_ORDER_ANY);
+ MUTATOR_HOOK(WantWeapon, lms_WantWeapon, CBC_ORDER_ANY);
MUTATOR_ONADD
{
case NUM_TEAM_1:
if(!t_r)
{
- nb_spawnteam("Red", e.team-1) ;
+ nb_spawnteam(NAME_TEAM_1, e.team-1);
t_r = 1;
}
break;
case NUM_TEAM_2:
if(!t_b)
{
- nb_spawnteam("Blue", e.team-1) ;
+ nb_spawnteam(NAME_TEAM_2, e.team-1);
t_b = 1;
}
break;
case NUM_TEAM_3:
if(!t_y)
{
- nb_spawnteam("Yellow", e.team-1);
+ nb_spawnteam(NAME_TEAM_3, e.team-1);
t_y = 1;
}
break;
case NUM_TEAM_4:
if(!t_p)
{
- nb_spawnteam("Pink", e.team-1) ;
+ nb_spawnteam(NAME_TEAM_4, e.team-1);
t_p = 1;
}
break;
if(!(balls & BALL_BASKET))
return;
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;
}
else if(req == WR_INIT)
{
- precache_model("models/weapons/g_porto.md3");
- precache_model("models/weapons/v_porto.md3");
- precache_model("models/weapons/h_porto.iqm");
+ precache_model(W_Model("g_porto.md3"));
+ precache_model(W_Model("v_porto.md3"));
+ precache_model(W_Model("h_porto.iqm"));
precache_model("models/elaser.mdl");
precache_sound("nexball/shoot1.wav");
precache_sound("nexball/shoot2.wav");
return FALSE;
}
+MUTATOR_HOOKFUNCTION(nexball_GetTeamCount)
+{
+ ret_string = "nexball_team";
+ return TRUE;
+}
+
MUTATOR_HOOKFUNCTION(nexball_ForbidThrowing)
{
if(self.weapon == WEP_MORTAR)
return FALSE;
}
+MUTATOR_HOOKFUNCTION(nexball_WantWeapon)
+{
+ ret_float = 0; // weapon is set a few lines later
+ return FALSE;
+}
+
+MUTATOR_HOOKFUNCTION(nexball_AllowMobSpawning)
+{
+ ret_string = "You cannot spawn monsters in nexball!";
+ return TRUE;
+}
+
MUTATOR_DEFINITION(gamemode_nexball)
{
MUTATOR_HOOK(PlayerDies, nexball_BallDrop, CBC_ORDER_ANY);
MUTATOR_HOOK(PlayerPreThink, nexball_PlayerPreThink, CBC_ORDER_ANY);
MUTATOR_HOOK(PlayerPhysics, nexball_PlayerPhysics, CBC_ORDER_ANY);
MUTATOR_HOOK(SetStartItems, nexball_SetStartItems, CBC_ORDER_ANY);
+ MUTATOR_HOOK(GetTeamCount, nexball_GetTeamCount, CBC_ORDER_ANY);
MUTATOR_HOOK(ForbidThrowCurrentWeapon, nexball_ForbidThrowing, CBC_ORDER_ANY);
MUTATOR_HOOK(FilterItem, nexball_FilterItem, CBC_ORDER_ANY);
+ MUTATOR_HOOK(WantWeapon, nexball_WantWeapon, CBC_ORDER_ANY);
+ MUTATOR_HOOK(AllowMobSpawning, nexball_AllowMobSpawning, CBC_ORDER_ANY);
MUTATOR_ONADD
{
-float autocvar_g_onslaught_spawn_at_controlpoints;
-float autocvar_g_onslaught_spawn_at_generator;
-float autocvar_g_onslaught_cp_proxydecap;
-var float autocvar_g_onslaught_cp_proxydecap_distance = 512;
-var float autocvar_g_onslaught_cp_proxydecap_dps = 100;
+// =======================
+// CaptureShield Functions
+// =======================
-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);
-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; }
-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.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; }
+ 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
- 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;
- 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
if (l.enemy.iscaptured && l.goalentity.iscaptured)
if (l.enemy.islinked != l.goalentity.islinked)
- if (l.enemy.team == l.goalentity.team)
+ if(SAME_TEAM(l.enemy, l.goalentity))
{
if (!l.goalentity.islinked)
{
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;
- 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
// as unshielded if team differs
- l = links;
- while (l)
+ for(l = ons_worldlinklist; l; l = l.ons_worldlinknext)
{
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;
}
if(l.goalentity.classname == "onslaught_generator")
- {
- 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
- {
- 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;
}
if(l.enemy.classname == "onslaught_generator")
- {
- 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
- {
- 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;
}
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 = 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)
{
- dprint(etos(l), " (point) is shielded\n");
- l.enemy.alpha = 1;
+ ons_debug(strcat(etos(l), " (point) is shielded\n"));
if (l.goalentity)
{
l.goalentity.takedamage = DAMAGE_NO;
}
else
{
- dprint(etos(l), " (point) is not shielded\n");
- l.enemy.alpha = -1;
+ ons_debug(strcat(etos(l), " (point) is not shielded\n"));
if (l.goalentity)
{
l.goalentity.takedamage = DAMAGE_AIM;
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
}
- 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
else if(cp.goalentity)
{
// if there's already an icon built, nothing happens
- if(cp.team == t)
+ if(cp.team == teamnumber)
{
- a = onslaught_controlpoint_can_be_linked(cp, NUM_TEAM_1 + NUM_TEAM_2 - t);
+ a = ons_ControlPoint_CanBeLinked(cp, teamnumber);
if(a) // attackable by enemy?
return -2; // EMERGENCY!
return -1;
}
// we know it can be linked, so no need to check
// but...
- a = onslaught_controlpoint_can_be_linked(cp, t);
+ a = ons_ControlPoint_CanBeLinked(cp, teamnumber);
if(a == 2) // near our generator?
return 3; // EMERGENCY!
return 1;
else
{
// free point
- if(onslaught_controlpoint_can_be_linked(cp, t))
+ if(ons_ControlPoint_CanBeLinked(cp, teamnumber))
{
- a = onslaught_controlpoint_can_be_linked(cp, NUM_TEAM_1 + NUM_TEAM_2 - t);
+ a = ons_ControlPoint_CanBeLinked(cp, teamnumber); // why was this here NUM_TEAM_1 + NUM_TEAM_2 - t
if(a == 2)
return 4; // GET THIS ONE NOW!
else
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.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);
+
+ 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;
+ }
+ }
- // Spawn fire balls
- for(i=0;i < 10;++i)
+ 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;
- // 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);
+ oself = self;
+ self = self.owner;
+ activator = self;
+ SUB_UseTargets ();
+ self = oself;
- // rays
- if(random() > 0.25 )
- {
- onslaught_generator_ray_spawn(self.origin);
+ self.owner.team = t;
+
+ 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)
- {
- if (self.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;
- }
- 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)
+
+ self.health = self.health + self.count;
+
+ self.SendFlags |= CPSF_STATUS;
+
+ if (self.health >= self.max_health)
{
- if (attacker == self)
- bprint(Team_ColoredFullName(self.team), " generator spontaneously exploded due to overtime!\n");
- else
+ 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.solid = SOLID_BBOX;
+
+ float eff_team;
+ switch(self.owner.team)
{
- 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.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 "";
}
-void onslaught_controlpoint_updatesprite(entity e)
+void ons_ControlPoint_UpdateSprite(entity e)
{
- string s1, s2, s3;
- s1 = onslaught_controlpoint_waypointsprite_for_team(e, NUM_TEAM_1);
- s2 = onslaught_controlpoint_waypointsprite_for_team(e, NUM_TEAM_2);
- s3 = onslaught_controlpoint_waypointsprite_for_team(e, -1);
- WaypointSprite_UpdateSprites(e.sprite, s1, s2, s3);
+ string s1;
+ s1 = ons_ControlPoint_Waypoint(e);
+ WaypointSprite_UpdateSprites(e.sprite, s1, s1, s1);
float sh;
- sh = !(onslaught_controlpoint_can_be_linked(e, NUM_TEAM_1) || onslaught_controlpoint_can_be_linked(e, NUM_TEAM_2));
+ sh = !(ons_ControlPoint_CanBeLinked(e, NUM_TEAM_1) || ons_ControlPoint_CanBeLinked(e, NUM_TEAM_2) || ons_ControlPoint_CanBeLinked(e, NUM_TEAM_3) || ons_ControlPoint_CanBeLinked(e, NUM_TEAM_4));
if(e.lastteam != e.team + 2 || e.lastshielded != sh || e.iscaptured != e.lastcaptured)
{
}
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));
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));
else
WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_CONTROLPOINT, '0.75 0.75 0.75');
}
}
-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)
+ 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 )
{
- setorigin(self, self.origin + '0 0 20');
- droptofloor();
+ 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.
-
-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_Think()
{
- if (!g_onslaught)
- {
- remove(self);
- return;
- }
-
- //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");
-
- if(self.team == NUM_TEAM_1)
- ons_red_generator = self;
+ self.nextthink = time + ONS_CP_THINKRATE;
+ CSQCMODEL_AUTOUPDATE();
+}
- if(self.team == NUM_TEAM_2)
- ons_blue_generator = self;
+void ons_ControlPoint_Reset()
+{
+ if(self.goalentity)
+ remove(self.goalentity);
- 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.goalentity = world;
+ self.team = 0;
+ self.colormap = 1024;
+ self.iscaptured = FALSE;
+ self.islinked = FALSE;
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);
+ self.think = ons_ControlPoint_Think;
+ self.ons_toucher = world;
+ self.nextthink = time + ONS_CP_THINKRATE;
+ setmodel_fixsize(self, "models/onslaught/controlpoint_pad.md3");
- waypoint_spawnforitem(self);
+ WaypointSprite_UpdateMaxHealth(self.sprite, 0);
+ WaypointSprite_UpdateRule(self.sprite,self.team,SPRITERULE_TEAMPLAY);
onslaught_updatelinks();
- self.reset = onslaught_generator_reset;
+ activator = self;
+ SUB_UseTargets(); // to reset the structures, playerspawns etc.
+
+ CSQCMODEL_AUTOUPDATE();
}
-.float waslinked;
-.float cp_bob_spd;
-.vector cp_origin, cp_bob_origin, cp_bob_dmg;
+void ons_DelayedControlPoint_Setup(void)
+{
+ onslaught_updatelinks();
+
+ // captureshield setup
+ ons_CaptureShield_Spawn(self, FALSE);
-float ons_notification_time_team1;
-float ons_notification_time_team2;
+ CSQCMODEL_AUTOINIT();
+}
-void onslaught_controlpoint_icon_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
+void ons_ControlPoint_Setup(entity cp)
{
- entity oself;
- float nag;
+ // 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;
+
+ if(cp.message == "") { cp.message = "a"; }
- if (damage <= 0)
- return;
- if (self.owner.isshielded)
+ // 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
{
- // 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;
+ 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;
+ 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);
+}
+
+
+// =========================
+// Main Generator Functions
+// =========================
+
+string ons_Generator_Waypoint(entity e)
+{
+ if(e.isshielded)
+ return "ons-gen-shielded";
+ return "ons-gen";
+}
+
+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));
+ 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));
+ 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);
}
+}
+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 (attacker != self)
+ {
+ if (self.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");
+ attacker.typehitsound += 1;
+ self.pain_finished = time + 1;
+ }
+ return;
+ }
+ 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;
- 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);
+ 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
- sound(self, CH_TRIGGER, "onslaught/ons_hit2.wav", VOL_BASE+0.3, ATTEN_NORM);
-
- if (self.health < 0)
{
- sound(self, CH_TRIGGER, "weapons/grenade_impact.wav", VOL_BASE, ATTEN_NORM);
- pointparticles(particleeffectnum("rocket_explode"), self.origin, '0 0 0', 1);
+ if (attacker == self)
+ Send_Notification(NOTIF_ALL, world, MSG_INFO, APP_TEAM_NUM_4(self.team, INFO_ONSLAUGHT_GENDESTROYED_OVERTIME_));
+ else
{
- 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);
+ Send_Notification(NOTIF_ALL, world, MSG_INFO, APP_TEAM_NUM_4(self.team, INFO_ONSLAUGHT_GENDESTROYED_));
+ PlayerScore_Add(attacker, SP_SCORE, 100);
}
- self.owner.goalentity = world;
- self.owner.islinked = FALSE;
- self.owner.iscaptured = FALSE;
- self.owner.team = 0;
- self.owner.colormap = 1024;
+ 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.event_damage = ons_GeneratorDamage;
+ self.think = ons_GeneratorThink;
+ self.nextthink = time + GEN_THINKRATE;
+
+ 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);
+
+ onslaught_updatelinks();
+
+ 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.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.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
+// ===============
- self.owner.waslinked = self.owner.islinked;
+float total_generators, redowned, blueowned, yellowowned, pinkowned;
+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);
}
+}
- if (self.punchangle_x > 0)
+float 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))
{
- 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;
+ }
+
+ 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; }
- if (self.punchangle_z > 0)
+ Onslaught_count_generators();
+
+ if(ONS_OWNED_GENERATORS_OK())
+ return 0;
+
+ float winner_team = Onslaught_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_ONS_CAPS, +1);
+ }
+ 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);
}
- else if (self.punchangle_z < 0)
+
+ ons_stalemate = FALSE;
+
+ 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)
{
- self.punchangle_z = self.punchangle_z + 60 * sys_frametime;
- if (self.punchangle_z > 0)
- self.punchangle_z = 0;
+ e.ons_roundlost = TRUE;
+ e.player_blocked = TRUE;
}
+
+ nades_Clear(world, TRUE);
+
+ return 1;
+}
- 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;
+float Onslaught_CheckPlayers()
+{
+ return 1;
+}
- 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);
+void Onslaught_RoundStart()
+{
+ entity tmp_entity;
+ FOR_EACH_PLAYER(tmp_entity) { tmp_entity.player_blocked = FALSE; }
- // damaged fx
- if(random() < 0.6 - self.health / self.max_health)
+ for(tmp_entity = ons_worldcplist; tmp_entity; tmp_entity = tmp_entity.ons_worldcpnext)
+ tmp_entity.sprite.SendFlags |= 16;
+
+ for(tmp_entity = ons_worldgeneratorlist; tmp_entity; tmp_entity = tmp_entity.ons_worldgeneratornext)
+ tmp_entity.sprite.SendFlags |= 16;
+}
+
+
+// ================
+// Bot player logic
+// ================
+
+// NOTE: LEGACY CODE, needs to be re-written!
+
+void havocbot_goalrating_ons_offenseitems(float ratingscale, vector org, float sradius)
+{
+ entity head;
+ float t, i, c, needarmor = FALSE, needweapons = FALSE;
+
+ // Needs armor/health?
+ if(self.health<100)
+ needarmor = TRUE;
+
+ // Needs weapons?
+ c = 0;
+ for(i = WEP_FIRST; i <= WEP_LAST ; ++i)
{
- pointparticles(particleeffectnum("electricity_sparks"), self.origin + randompos('-10 -10 -20', '10 10 20'), '0 0 0', 1);
+ // Find weapon
+ if(self.weapons & WepSet_FromWeapon(i))
+ if(++c>=4)
+ break;
+ }
- 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);
+ if(c<4)
+ 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);
+ 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_icon_buildthink()
+void havocbot_role_ons_setrole(entity bot, float role)
{
- entity oself;
- float a;
+ 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");
+}
- self.nextthink = time + sys_frametime;
+float havocbot_ons_teamcount(entity bot, float role)
+{
+ float c = 0;
+ entity head;
- // only do this if there is power
- a = onslaught_controlpoint_can_be_linked(self.owner, self.owner.team);
- if(!a)
- return;
+ FOR_EACH_PLAYER(head)
+ if(SAME_TEAM(head, self))
+ if(head.havocbot_role_flags & role)
+ ++c;
- self.health = self.health + self.count;
+ return c;
+}
- if (self.health >= self.max_health)
+void havocbot_goalrating_ons_controlpoints_attack(float ratingscale)
+{
+ entity cp, cp1, cp2, best, pl, wp;
+ float radius, found, bestvalue, c;
+
+ // Filter control points
+ for(cp2 = ons_worldcplist; cp2; cp2 = cp2.ons_worldcpnext)
{
- 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;
+ cp2.wpcost = c = 0;
+ cp2.wpconsidered = FALSE;
- WaypointSprite_UpdateMaxHealth(self.owner.sprite, self.max_health);
- WaypointSprite_UpdateHealth(self.owner.sprite, self.health);
+ if(cp2.isshielded)
+ continue;
- onslaught_updatelinks();
+ // Ignore owned controlpoints
+ if(!(cp2.isgenneighbor[self.team] || cp2.iscpneighbor[self.team]))
+ continue;
- // 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;
+ // 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;
}
- 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);
+ // 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<bestvalue)
+ {
+ bestvalue = cp1.wpcost;
+ cp = cp1;
+ self.havocbot_ons_target = cp1;
+ }
+ }
+
+ if (!cp)
+ return;
+
+ ons_debug(strcat(self.netname, " chose cp ranked ", ftos(bestvalue), "\n"));
+
+ if(cp.goalentity)
+ {
+ // Should be attacked
+ // Rate waypoints near it
+ found = FALSE;
+ best = world;
+ bestvalue = 99999999999;
+ for(radius=0; radius<1000 && !found; radius+=500)
+ {
+ for(wp=findradius(cp.origin,radius); wp; wp=wp.chain)
+ {
+ if(!(wp.wpflags & WAYPOINTFLAG_GENERATED))
+ if(wp.classname=="waypoint")
+ if(checkpvs(wp.origin,cp))
+ {
+ found = TRUE;
+ if(wp.cnt<bestvalue)
+ {
+ best = wp;
+ bestvalue = wp.cnt;
+ }
+ }
+ }
+ }
+
+ if(best)
+ {
+ navigation_routerating(best, ratingscale, 10000);
+ best.cnt += 1;
+
+ self.havocbot_attack_time = 0;
+ if(checkpvs(self.view_ofs,cp))
+ if(checkpvs(self.view_ofs,best))
+ self.havocbot_attack_time = time + 2;
+ }
+ else
+ {
+ navigation_routerating(cp, ratingscale, 10000);
+ }
+ ons_debug(strcat(self.netname, " found an attackable controlpoint at ", vtos(cp.origin) ,"\n"));
+ }
+ else
+ {
+ // Should be touched
+ ons_debug(strcat(self.netname, " found a touchable controlpoint at ", vtos(cp.origin) ,"\n"));
+ found = FALSE;
+
+ // Look for auto generated waypoint
+ if (!bot_waypoints_for_items)
+ for (wp = findradius(cp.origin,100); wp; wp = wp.chain)
+ {
+ if(wp.classname=="waypoint")
+ {
+ navigation_routerating(wp, ratingscale, 10000);
+ found = TRUE;
+ }
+ }
+
+ // Nothing found, rate the controlpoint itself
+ if (!found)
+ navigation_routerating(cp, ratingscale, 10000);
+ }
}
+float havocbot_goalrating_ons_generator_attack(float ratingscale)
+{
+ entity g, wp, bestwp;
+ float found, best;
+
+ for(g = ons_worldgeneratorlist; g; g = g.ons_worldgeneratornext)
+ {
+ if(SAME_TEAM(g, self) || g.isshielded)
+ continue;
+
+ // Should be attacked
+ // Rate waypoints near it
+ found = FALSE;
+ bestwp = world;
+ best = 99999999999;
+
+ for(wp=findradius(g.origin,400); wp; wp=wp.chain)
+ {
+ if(wp.classname=="waypoint")
+ if(checkpvs(wp.origin,g))
+ {
+ found = TRUE;
+ if(wp.cnt<best)
+ {
+ bestwp = wp;
+ best = wp.cnt;
+ }
+ }
+ }
+
+ if(bestwp)
+ {
+ ons_debug("waypoints found around generator\n");
+ navigation_routerating(bestwp, ratingscale, 10000);
+ bestwp.cnt += 1;
+ self.havocbot_attack_time = 0;
+ if(checkpvs(self.view_ofs,g))
+ if(checkpvs(self.view_ofs,bestwp))
+ self.havocbot_attack_time = time + 5;
+ return TRUE;
+ }
+ else
+ {
+ ons_debug("generator found without waypoints around\n");
+ // if there aren't waypoints near the generator go straight to it
+ navigation_routerating(g, ratingscale, 10000);
+ self.havocbot_attack_time = 0;
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
-void onslaught_controlpoint_touch()
+void havocbot_role_ons_offense()
{
- entity e;
- float a;
- if (!IS_PLAYER(other))
+ if(self.deadflag != DEAD_NO)
+ {
+ self.havocbot_attack_time = 0;
+ havocbot_ons_reset_role(self);
return;
- a = onslaught_controlpoint_attackable(self, other.team);
- if(a != 2 && a != 4)
+ }
+
+ // Set the role timeout if necessary
+ if (!self.havocbot_role_timeout)
+ self.havocbot_role_timeout = time + 120;
+
+ if (time > self.havocbot_role_timeout)
+ {
+ havocbot_ons_reset_role(self);
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();
+ }
+
+ 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 onslaught_controlpoint_think()
+void havocbot_role_ons_assistant()
{
- self.nextthink = time;
- CSQCMODEL_AUTOUPDATE();
+ havocbot_ons_reset_role(self);
}
-void onslaught_controlpoint_reset()
+void havocbot_role_ons_defense()
{
- 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');
-
- WaypointSprite_UpdateMaxHealth(self.sprite, 0);
+ havocbot_ons_reset_role(self);
+}
- onslaught_updatelinks();
+void havocbot_ons_reset_role(entity bot)
+{
+ entity head;
+ float c;
- activator = self;
- SUB_UseTargets(); // to reset the structures, playerspawns etc.
-
- CSQCMODEL_AUTOUPDATE();
-}
+ if(self.deadflag != DEAD_NO)
+ return;
-/*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
+ bot.havocbot_ons_target = world;
- This should link to an spawnfunc_onslaught_controlpoint entity or spawnfunc_onslaught_generator entity.
+ // TODO: Defend control points or generator if necessary
-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)
- */
+ // if there is only me on the team switch to offense
+ c = 0;
+ FOR_EACH_PLAYER(head)
+ if(SAME_TEAM(head, self))
+ ++c;
-void spawnfunc_onslaught_controlpoint()
-{
- //entity e;
- if (!g_onslaught)
+ if(c==1)
{
- remove(self);
+ havocbot_role_ons_setrole(bot, HAVOCBOT_ONS_ROLE_OFFENSE);
return;
}
- 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("weapons/grenade_impact.wav");
- precache_sound("onslaught/damageblockedbyshield.wav");
- precache_sound("onslaught/controlpoint_underattack.wav");
- precache_sound("onslaught/ons_spark1.wav");
- precache_sound("onslaught/ons_spark2.wav");
- self.solid = SOLID_BBOX;
- self.movetype = MOVETYPE_NONE;
- setmodel(self, "models/onslaught/controlpoint_pad.md3");
- //setsize(self, '-32 -32 0', '32 32 8');
- if(!self.noalign)
- {
- setorigin(self, self.origin + '0 0 20');
- droptofloor();
- }
- self.touch = onslaught_controlpoint_touch;
- self.team = 0;
- self.colormap = 1024;
- self.iscaptured = FALSE;
- self.islinked = FALSE;
- self.isshielded = TRUE;
+ havocbot_role_ons_setrole(bot, HAVOCBOT_ONS_ROLE_OFFENSE);
+}
- // 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");
- setattachment(self.enemy , self, "");
- //setsize(e, '-32 -32 0', '32 32 128');
+/*
+ * 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;
+}
- //setorigin(e, self.origin);
- self.enemy.colormap = self.colormap;
+/*
+ * 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)
+{
+ 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)
+ {
+ if(SAME_TEAM(tmp_entity, self))
+ if(tmp_entity.iscaptured)
+ n++;
+ tmp_entity = tmp_entity.chain;
+ }
+ tmp_entity = findchain(classname, "onslaught_generator");
+ while(tmp_entity)
+ {
+ if(SAME_TEAM(tmp_entity, self))
+ n++;
+ tmp_entity = tmp_entity.chain;
+ }
+ return n;
+}
- waypoint_spawnforitem(self);
+/**
+ * Teleport player to a random position near tele_target
+ * if tele_effects is true, teleport sound+particles are created
+ * return FALSE on failure
+ */
+float ons_Teleport(entity player, entity tele_target, float range, float tele_effects)
+{
+ if ( !tele_target )
+ return FALSE;
- self.think = onslaught_controlpoint_think;
- self.nextthink = time;
+ 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.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 FALSE;
+}
- 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);
+// ==============
+// Hook Functions
+// ==============
- onslaught_updatelinks();
+MUTATOR_HOOKFUNCTION(ons_ResetMap)
+{
+ FOR_EACH_PLAYER(self)
+ {
+ self.ons_roundlost = FALSE;
+ self.ons_deathloc = '0 0 0';
+ PutClientInServer();
+ }
+ return FALSE;
+}
- self.reset = onslaught_controlpoint_reset;
-
- CSQCMODEL_AUTOINIT();
+MUTATOR_HOOKFUNCTION(ons_RemovePlayer)
+{
+ self.ons_deathloc = '0 0 0';
+ return FALSE;
}
-float onslaught_link_send(entity to, float sendflags)
+MUTATOR_HOOKFUNCTION(ons_PlayerSpawn)
{
- WriteByte(MSG_ENTITY, ENT_CLIENT_RADARLINK);
- WriteByte(MSG_ENTITY, sendflags);
- if(sendflags & 1)
+ if(!round_handler_IsRoundStarted())
{
- WriteCoord(MSG_ENTITY, self.goalentity.origin_x);
- WriteCoord(MSG_ENTITY, self.goalentity.origin_y);
- WriteCoord(MSG_ENTITY, self.goalentity.origin_z);
+ self.player_blocked = TRUE;
+ return FALSE;
}
- if(sendflags & 2)
+
+ entity l;
+ for(l = ons_worldgeneratorlist; l; l = l.ons_worldgeneratornext)
{
- WriteCoord(MSG_ENTITY, self.enemy.origin_x);
- WriteCoord(MSG_ENTITY, self.enemy.origin_y);
- WriteCoord(MSG_ENTITY, self.enemy.origin_z);
+ l.sprite.SendFlags |= 16;
}
- if(sendflags & 4)
+ for(l = ons_worldcplist; l; l = l.ons_worldcpnext)
{
- WriteByte(MSG_ENTITY, self.clientcolors); // which is goalentity's color + enemy's color * 16
+ l.sprite.SendFlags |= 16;
}
- return TRUE;
-}
-void onslaught_link_checkupdate()
-{
- // TODO check if the two sides have moved (currently they won't move anyway)
- float redpower, bluepower;
+ if(ons_stalemate) { Send_Notification(NOTIF_ONE, self, MSG_CENTER, CENTER_OVERTIME_CONTROLPOINT); }
- redpower = bluepower = 0;
- if(self.goalentity.islinked)
+ 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(self.goalentity.team == NUM_TEAM_1)
- redpower = 1;
- else if(self.goalentity.team == NUM_TEAM_2)
- bluepower = 1;
+ self.ons_spawn_by = world;
+ return FALSE;
}
- if(self.enemy.islinked)
+
+ if(autocvar_g_onslaught_spawn_at_controlpoints)
+ if(random() <= autocvar_g_onslaught_spawn_at_controlpoints_chance)
{
- if(self.enemy.team == NUM_TEAM_1)
- redpower = 2;
- else if(self.enemy.team == NUM_TEAM_2)
- bluepower = 2;
- }
-
- 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;
+ 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; }
- //print(etos(self), " rp=", ftos(redpower), " bp=", ftos(bluepower), " ");
- //print("cc=", ftos(cc), "\n");
+ if(random_target) { RandomSelection_Init(); }
- if(cc != self.clientcolors)
- {
- self.clientcolors = cc;
- self.SendFlags |= 4;
+ 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;
+ }
+ }
+ }
+ }
+ }
+
+ if(autocvar_g_onslaught_spawn_at_generator)
+ if(random() <= autocvar_g_onslaught_spawn_at_generator_chance)
+ {
+ 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(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;
+ }
+ }
+ }
+ }
}
- 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;
}
-/*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;
- 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)
+void ons_TurretSpawn_Delayed()
{
- ret_string = strcat(ret_string, ", Onslaught");
- return 0;
+ entity e, own = self.owner;
+
+ if(!own) { remove(self); return; }
+
+ if(own.targetname)
+ {
+ e = find(world, target, own.targetname);
+ if(e != world)
+ {
+ own.team = e.team;
+ own.active = ACTIVE_NOT;
+
+ activator = e;
+ own.use();
+ }
+ }
+
+ remove(self);
}
-MUTATOR_HOOKFUNCTION(ons_Spawn_Score)
+MUTATOR_HOOKFUNCTION(ons_TurretSpawn)
{
+ entity e = spawn();
+ e.owner = self;
+ InitializeEntity(e, ons_TurretSpawn_Delayed, INITPRIO_FINDTARGET);
- /*
- float _neer_home = (random() > 0.5 ? TRUE : FALSE);
+ return FALSE;
+}
- RandomSelection_Init();
+MUTATOR_HOOKFUNCTION(ons_BotRoles)
+{
+ havocbot_ons_reset_role(self);
+ return TRUE;
+}
- if(self.team == NUM_TEAM_1)
- RandomSelection_Add(ons_red_generator, 0, string_null, 1, 1);
+MUTATOR_HOOKFUNCTION(ons_GetTeamCount)
+{
+ // onslaught is special
+ entity tmp_entity;
+ for(tmp_entity = ons_worldgeneratorlist; tmp_entity; tmp_entity = tmp_entity.ons_worldgeneratornext)
+ {
+ 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;
+ }
+ }
- if(self.team == NUM_TEAM_2)
- RandomSelection_Add(ons_blue_generator, 0, string_null, 1, 1);
+ return TRUE;
+}
- entity _cp = findchain(classname, "onslaught_controlpoint"):
- while _cp;
- {
- if(_cp.team == self.team)
- RandomSelection_Add(_cp, 0, string_null, 1, 1);
+MUTATOR_HOOKFUNCTION(ons_SpectateCopy)
+{
+ self.ons_roundlost = other.ons_roundlost; // make spectators see it too
+ return FALSE;
+}
- _cp = _cp.chain;
+MUTATOR_HOOKFUNCTION(ons_SV_ParseClientCommand)
+{
+ if(MUTATOR_RETURNVALUE) // command was already handled?
+ 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) )
+ sprint(self, "\nUnable to teleport there\n");
+ }
+
+ return 1;
+ }
+
+ sprint(self, "\nNo teleportation for you\n");
+ }
+
+ return 1;
}
+ return 0;
+}
- if(RandomSelection_chosen_ent)
+MUTATOR_HOOKFUNCTION(ons_PlayerUseKey)
+{
+ if(MUTATOR_RETURNVALUE || gameover) { return FALSE; }
+
+ if((time > self.teleport_antispam) && (self.deadflag == DEAD_NO) && !self.vehicle)
{
- self.tur_head = RandomSelection_chosen_ent;
- spawn_score_x += SPAWN_PRIO_NEAR_TEAMMATE_FOUND;
+ 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;
+ }
}
- 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 FALSE;
+}
- */
+// ==========
+// Spawnfuncs
+// ==========
- return 0;
-}
+/*QUAKED spawnfunc_onslaught_link (0 .5 .8) (-16 -16 -16) (16 16 16)
+ Link between control points.
-MUTATOR_HOOKFUNCTION(ons_PlayerSpawn)
+ 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.
+
+keys:
+"target" - first control point.
+"target2" - second control point.
+ */
+void spawnfunc_onslaught_link()
{
- if(!autocvar_g_onslaught_spawn_at_controlpoints)
- return 0;
+ if(!g_onslaught) { remove(self); return; }
- if(random() < 0.5) // 50/50 chane to use default spawnsystem.
- return 0;
+ if (self.target == "" || self.target2 == "")
+ objerror("target and target2 must be set\n");
- float _close_to_home = ((random() > 0.5) ? TRUE : FALSE);
- entity _best = world, _trg_gen = world;
- float _score, _best_score = MAX_SHOT_DISTANCE;
+ self.ons_worldlinknext = ons_worldlinklist; // link into ons_worldlinklist
+ ons_worldlinklist = self;
- RandomSelection_Init();
+ InitializeEntity(self, ons_DelayedLinkSetup, INITPRIO_FINDTARGET);
+ Net_LinkEntity(self, FALSE, 0, ons_Link_Send);
+}
- if(self.team == NUM_TEAM_1)
- {
- if(!_close_to_home)
- _trg_gen = ons_blue_generator;
- else
- _trg_gen = ons_red_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
- if(self.team == NUM_TEAM_2)
- {
- if(_close_to_home)
- _trg_gen = ons_blue_generator;
- else
- _trg_gen = ons_red_generator;
- }
+ This should link to an spawnfunc_onslaught_controlpoint entity or spawnfunc_onslaught_generator entity.
- 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;
- }
+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)
+ */
- 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;
+void spawnfunc_onslaught_controlpoint()
+{
+ if(!g_onslaught) { remove(self); return; }
+
+ ons_ControlPoint_Setup(self);
+}
- _trg_gen = ((self.team == NUM_TEAM_1) ? ons_red_generator : ons_blue_generator);
+/*QUAKED spawnfunc_onslaught_generator (0 .5 .8) (-32 -32 -24) (32 32 64)
+ Base generator.
- 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;
- }
- }
- }
+ spawnfunc_onslaught_link entities can target this.
- return 0;
+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()
+{
+ if(!g_onslaught) { remove(self); return; }
+ if(!self.team) { objerror("team must be set"); }
+
+ ons_GeneratorSetup(self);
}
-MUTATOR_HOOKFUNCTION(ons_MonsterThink)
+
+// scoreboard setup
+void ons_ScoreRules()
{
- entity e = find(world, targetname, self.target);
- if (e != world)
- self.team = e.team;
+ CheckAllowedTeams(world);
+ 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();
+}
- return FALSE;
+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);
}
-MUTATOR_HOOKFUNCTION(ons_MonsterSpawn)
+void ons_Initialize()
{
- entity e, ee = world;
+ 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;
- if(self.targetname)
- {
- e = find(world,target,self.targetname);
- if(e != world)
- {
- self.team = e.team;
- ee = e;
- }
- }
+ addstat(STAT_ROUNDLOST, AS_INT, ons_roundlost);
- if(ee)
- {
- activator = ee;
- self.use();
- }
-
- return FALSE;
+ InitializeEntity(world, ons_DelayedInit, INITPRIO_GAMETYPE);
}
MUTATOR_DEFINITION(gamemode_onslaught)
{
- MUTATOR_HOOK(BuildMutatorsPrettyString, ons_BuildMutatorsPrettyString, CBC_ORDER_ANY);
- MUTATOR_HOOK(BuildMutatorsString, ons_BuildMutatorsString, CBC_ORDER_ANY);
+ MUTATOR_HOOK(reset_map_global, ons_ResetMap, CBC_ORDER_ANY);
+ MUTATOR_HOOK(MakePlayerObserver, ons_RemovePlayer, CBC_ORDER_ANY);
+ MUTATOR_HOOK(ClientDisconnect, ons_RemovePlayer, CBC_ORDER_ANY);
MUTATOR_HOOK(PlayerSpawn, ons_PlayerSpawn, CBC_ORDER_ANY);
+ MUTATOR_HOOK(PlayerDies, ons_PlayerDies, CBC_ORDER_ANY);
MUTATOR_HOOK(MonsterMove, ons_MonsterThink, CBC_ORDER_ANY);
MUTATOR_HOOK(MonsterSpawn, ons_MonsterSpawn, CBC_ORDER_ANY);
- //MUTATOR_HOOK(Spawn_Score, ons_Spawn_Score, CBC_ORDER_ANY);
+ MUTATOR_HOOK(TurretSpawn, ons_TurretSpawn, CBC_ORDER_ANY);
+ MUTATOR_HOOK(HavocBot_ChooseRole, ons_BotRoles, CBC_ORDER_ANY);
+ MUTATOR_HOOK(GetTeamCount, ons_GetTeamCount, CBC_ORDER_ANY);
+ MUTATOR_HOOK(SpectateCopy, ons_SpectateCopy, CBC_ORDER_ANY);
+ MUTATOR_HOOK(SV_ParseClientCommand, ons_SV_ParseClientCommand, CBC_ORDER_ANY);
+ MUTATOR_HOOK(PlayerUseKey, ons_PlayerUseKey, 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.");
+ ons_Initialize();
+ }
+
+ MUTATOR_ONROLLBACK_OR_REMOVE
+ {
+ // we actually cannot roll back ons_Initialize here
+ // BUT: we don't need to! If this gets called, adding always
+ // succeeds.
}
MUTATOR_ONREMOVE
return -1;
}
- return 0;
+ return FALSE;
}
--- /dev/null
+// 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 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
return FALSE;
}
+MUTATOR_HOOKFUNCTION(race_CountFrags)
+{
+ // announce remaining frags if not in qualifying mode
+ if(!g_race_qualifying)
+ return TRUE;
+
+ return FALSE;
+}
+
+MUTATOR_HOOKFUNCTION(race_AllowMobSpawning)
+{
+ ret_string = "No monsters in a race!";
+ return TRUE;
+}
+
void race_Initialize()
{
race_ScoreRules();
MUTATOR_HOOK(GetPressedKeys, race_PlayerPostThink, CBC_ORDER_ANY);
MUTATOR_HOOK(ForbidPlayerScore_Clear, race_ForbidClearPlayerScore, CBC_ORDER_ANY);
MUTATOR_HOOK(GetTeamCount, race_GetTeamCount, CBC_ORDER_ANY);
+ MUTATOR_HOOK(Scores_CountFragsRemaining, race_CountFrags, CBC_ORDER_ANY);
+ MUTATOR_HOOK(AllowMobSpawning, race_AllowMobSpawning, CBC_ORDER_ANY);
MUTATOR_ONADD
{
return TRUE;
}
+MUTATOR_HOOKFUNCTION(tdm_CountFrags)
+{
+ // announce remaining frags
+ return TRUE;
+}
+
MUTATOR_DEFINITION(gamemode_tdm)
{
MUTATOR_HOOK(GetTeamCount, tdm_GetTeamCount, CBC_ORDER_ANY);
+ MUTATOR_HOOK(Scores_CountFragsRemaining, tdm_CountFrags, CBC_ORDER_ANY);
MUTATOR_ONADD
{
--- /dev/null
+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;
+ }
+
+ 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);
+
+ FOR_EACH_PLAYER(head) if(head.isvip && head.health > 0)
+ PlayerScore_Add(head, SP_VIP_SURVIVALS, 1);
+
+ vip_ClearVIPs();
+ 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;
+
+ round_handler_Init(5, autocvar_g_vip_warmup, autocvar_g_vip_round_timelimit);
+
+ 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()
+{
+ 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;
+ 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;
+ 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;
+
+ play2all("kh/destroy.wav");
+
+ WaypointSprite_Ping(frag_target.wps_vip);
+ WaypointSprite_Kill(frag_target.wps_vip);
+ }
+
+ return FALSE;
+}
+
+MUTATOR_HOOKFUNCTION(vip_RemovePlayer)
+{
+ if(self.isvip)
+ {
+ vip_count[self.team] -= 1;
+ self.isvip = FALSE;
+ WaypointSprite_Kill(self.wps_vip);
+ }
+
+ 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;
+}
+
+MUTATOR_HOOKFUNCTION(vip_ResetMap)
+{
+ FOR_EACH_PLAYER(self)
+ PutClientInServer();
+
+ 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;
+}
+
+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;
+}
+
+MUTATOR_HOOKFUNCTION(vip_PlayerUseKey)
+{
+ 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;
+ }
+ 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;
+ }
+ }
+ 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;
+}
+
+MUTATOR_HOOKFUNCTION(vip_OnEntityPreSpawn)
+{
+ if(!g_vip_soulgems)
+ return FALSE;
+
+ if(self.classname == "item_flag_team1")
+ {
+ vip_SpawnSoulGem(self.origin, NUM_TEAM_1);
+ return TRUE;
+ }
+
+ if(self.classname == "item_flag_team2")
+ {
+ vip_SpawnSoulGem(self.origin, NUM_TEAM_2);
+ return TRUE;
+ }
+
+ if(self.classname == "item_flag_team3")
+ {
+ vip_SpawnSoulGem(self.origin, NUM_TEAM_3);
+ return TRUE;
+ }
+
+ if(self.classname == "item_flag_team4")
+ {
+ vip_SpawnSoulGem(self.origin, NUM_TEAM_4);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+MUTATOR_HOOKFUNCTION(vip_GetTeamCount)
+{
+ ret_float = vip_teams;
+ 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_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;
+ 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);
+
+ 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.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');
+}
+
+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');
+}
+
+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.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);
+ 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;
+
+ 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;
+}
--- /dev/null
+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;
if(time >= self.bloodloss_timer)
{
+ if(self.vehicle)
+ vehicles_exit(VHEF_RELEASE);
self.event_damage(self, self, 1, DEATH_ROT, self.origin, '0 0 0');
self.bloodloss_timer = time + 0.5 + random() * 0.5;
}
if(myowner.alpha <= 0.5 && !same_team && myowner.alpha != 0)
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
+
if(player == myowner || (IS_SPEC(other) && other.enemy == myowner))
{
// somewhat hide the model, but keep the glow
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)
{
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);
}
if((self.team && DIFF_TEAM(other, self))
- || (other.frozen)
+ || (Player_Trapped(other))
|| (other.vehicle)
|| (!IS_PLAYER(other))
|| (!self.buff_active)
return;
}
+ if(cvar("g_piggyback"))
+ if(other.buffs)
+ if(other.buffs == self.buffs || !other.cvar_cl_buffs_autoreplace)
+ {
+ entity p = other;
+ while(p.piggybacker)
+ {
+ if(!p.buffs || (!p.piggybacker && (p.cvar_cl_buffs_autoreplace || p.buffs != self.buffs)))
+ {
+ other = p;
+ break;
+ }
+ p = p.piggybacker;
+ }
+ }
+
if(other.buffs)
{
if(other.cvar_cl_buffs_autoreplace && other.buffs != self.buffs)
Send_Notification(NOTIF_ONE, other, MSG_MULTI, ITEM_BUFF_GOT, self.buffs);
Send_Notification(NOTIF_ALL_EXCEPT, other, MSG_INFO, INFO_ITEM_BUFF, other.netname, self.buffs);
- pointparticles(particleeffectnum("item_pickup"), CENTER_OR_VIEWOFS(self), '0 0 0', 1);
+ Send_Effect(EFFECT_ITEM_PICKUP, CENTER_OR_VIEWOFS(self), '0 0 0', 1);
sound(other, CH_TRIGGER, "misc/shield_respawn.wav", VOL_BASE, ATTN_NORM);
other.buffs |= (self.buffs);
}
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_active && !self.buff_activetime)
- if(!self.owner || self.owner.frozen || self.owner.deadflag != DEAD_NO || !self.owner.iscreature || !(self.owner.buffs & self.buffs))
+ if(!self.owner || Player_Trapped(self.owner) || !self.owner.iscreature || !(self.owner.buffs & self.buffs))
{
buff_SetCooldown(autocvar_g_buffs_cooldown_respawn + frametime);
self.owner = world;
{
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)
+ if(self.buff_active)
{
- self.alpha = 0.3;
- self.effects &= ~(EF_FULLBRIGHT);
- self.pflags = 0;
- }
- else
- {
- 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);
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;
+}
+
void buff_Init(entity ent)
{
if(!cvar("g_buffs")) { remove(self); return; }
self.skin = Buff_Skin(self.buffs);
self.effects = EF_FULLBRIGHT | EF_STARDUST | EF_NOSHADOW;
self.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY;
+ self.customizeentityforclient = buff_Customize;
//self.gravity = 100;
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);
buff_SetCooldown(autocvar_g_buffs_cooldown_activate + game_starttime);
self.buff_active = !self.buff_activetime;
self.pflags = PFLAGS_FULLDYNAMIC;
// mutator hooks
MUTATOR_HOOKFUNCTION(buffs_PlayerDamage_SplitHealthArmor)
{
- 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);
+ 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;
}
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_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)
frag_target.buff_disability_time = time + autocvar_g_buffs_disability_time;
if(frag_attacker.buffs & BUFF_MEDIC)
+ if(DEATH_WEAPONOF(frag_deathtype) != WEP_ARC)
if(SAME_TEAM(frag_attacker, frag_target))
if(frag_attacker != frag_target)
{
// this... is ridiculous (TODO: fix!)
if(frag_attacker.buffs & BUFF_VAMPIRE)
if(!frag_target.vehicle)
+ if(DEATH_WEAPONOF(frag_deathtype) != WEP_ARC)
if(!ITEM_DAMAGE_NEEDKILL(frag_deathtype))
- if(frag_target.deadflag == DEAD_NO)
- if(IS_PLAYER(frag_target) || (frag_target.flags & FL_MONSTER))
+ if(IS_PLAYER(frag_target) || IS_MONSTER(frag_target))
if(frag_attacker != frag_target)
- if(!frag_target.frozen)
if(frag_target.takedamage)
+ if(!Player_Trapped(frag_target))
if(DIFF_TEAM(frag_attacker, frag_target))
frag_attacker.health = bound(0, frag_attacker.health + bound(0, frag_damage * autocvar_g_buffs_vampire_damage_steal, frag_target.health), g_pickup_healthsmall_max);
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;
}
{
if(self.buffs & BUFF_JUMP)
player_jumpheight = autocvar_g_buffs_jump_height;
- self.stat_jumpheight = player_jumpheight;
return FALSE;
}
{
entity e = spawn();
buff_SpawnReplacement(e, self);
+ self.classname = "item_removing";
return TRUE;
}
}
if(time < self.buff_disability_time)
if(time >= self.buff_disability_effect_time)
{
- pointparticles(particleeffectnum("smoking"), self.origin + ((self.mins + self.maxs) * 0.5), '0 0 0', 1);
+ Send_Effect(EFFECT_SMOKING, self.origin + ((self.mins + self.maxs) * 0.5), '0 0 0', 1);
self.buff_disability_effect_time = time + 0.5;
}
-
- if(self.frozen)
+
+ if(Player_Trapped(self))
{
if(self.buffs)
{
self.buffs = 0;
}
}
-
+
if((self.buffs & BUFF_INVISIBLE) && (self.oldbuffs & BUFF_INVISIBLE))
if(self.alpha != autocvar_g_buffs_invisible_alpha)
self.alpha = autocvar_g_buffs_invisible_alpha;
if(!self.ammo_nails) { self.ammo_nails = 20; }
if(!self.ammo_fuel) { self.ammo_fuel = 20; }
}
-
+
if(self.oldbuffs & BUFF_INVISIBLE)
{
- if(time < self.strength_finished && g_instagib)
- self.alpha = autocvar_g_instagib_invis_alpha;
- else
- self.alpha = self.buff_invisible_prev_alpha;
+ self.alpha = self.buff_invisible_prev_alpha;
}
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;
}
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;
precache_sound("misc/strength_respawn.wav");
precache_sound("misc/shield_respawn.wav");
precache_sound("relics/relic_effect.wav");
- precache_sound("weapons/rocket_impact.wav");
+ precache_sound(W_Sound("rocket_impact"));
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);
}
MUTATOR_HOOK(VehicleExit, buffs_VehicleExit, CBC_ORDER_ANY);
MUTATOR_HOOK(PlayerRegen, buffs_PlayerRegen, CBC_ORDER_ANY);
MUTATOR_HOOK(PlayerDies, buffs_PlayerDies, CBC_ORDER_ANY);
- MUTATOR_HOOK(PlayerUseKey, buffs_PlayerUseKey, CBC_ORDER_ANY);
+ MUTATOR_HOOK(PlayerUseKey, buffs_PlayerUseKey, CBC_ORDER_FIRST);
MUTATOR_HOOK(MakePlayerObserver, buffs_RemovePlayer, CBC_ORDER_ANY);
MUTATOR_HOOK(ClientDisconnect, buffs_RemovePlayer, CBC_ORDER_ANY);
- MUTATOR_HOOK(OnEntityPreSpawn, buffs_OnEntityPreSpawn, CBC_ORDER_ANY);
+ MUTATOR_HOOK(OnEntityPreSpawn, buffs_OnEntityPreSpawn, CBC_ORDER_LAST);
MUTATOR_HOOK(CustomizeWaypoint, buffs_CustomizeWaypoint, CBC_ORDER_ANY);
MUTATOR_HOOK(WeaponRateFactor, buffs_WeaponRate, CBC_ORDER_ANY);
MUTATOR_HOOK(PlayerPreThink, buffs_PlayerThink, CBC_ORDER_ANY);
.float buff_invisible_prev_alpha;
// flight
.float buff_flight_prev_gravity;
-// jump
-.float stat_jumpheight;
-const float STAT_MOVEVARS_JUMPVELOCITY = 250; // engine hack
// disability
.float buff_disability_time;
.float buff_disability_effect_time;
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;
}
MUTATOR_HOOKFUNCTION(campcheck_PlayerThink)
{
+ if(!gameover)
+ if(!warmup_stage) // don't consider it camping during warmup?
+ if(time >= game_starttime)
if(IS_PLAYER(self))
- if(self.deadflag == DEAD_NO)
- if(!self.frozen)
+ if(IS_REAL_CLIENT(self)) // bots may camp, but that's no reason to constantly kill them
+ if(!Player_Trapped(self))
+ if(!self.BUTTON_CHAT)
if(autocvar_g_campcheck_interval)
{
vector dist;
self.campcheck_nextcheck = time + autocvar_g_campcheck_interval;
self.campcheck_traveled_distance = 0;
}
+
+ 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;
}
-
+.float cvar_cl_dodging;
.float cvar_cl_dodging_timeout;
.float dodging_velocity_gain;
MUTATOR_HOOKFUNCTION(dodging_GetCvars) {
+ GetCvars_handleFloat(get_cvars_s, get_cvars_f, cvar_cl_dodging, "cl_dodging");
GetCvars_handleFloat(get_cvars_s, get_cvars_f, cvar_cl_dodging_timeout, "cl_dodging_timeout");
return 0;
}
if (g_dodging == 0)
clean_up_and_do_nothing = 1;
+ if(!self.cvar_cl_dodging)
+ clean_up_and_do_nothing = 1;
+
+ if(cvar("g_overkill"))
+ clean_up_and_do_nothing = 0; // always enabled in overkill
+
+ if(g_dodging == 2)
+ clean_up_and_do_nothing = 0; // forced enabled
+
+ if(self.frozen)
+ clean_up_and_do_nothing = 0; // also enabled in freezetag
+
// when swimming, no dodging allowed..
if (self.waterlevel >= WATERLEVEL_SWIMMING)
clean_up_and_do_nothing = 1;
//disable jump key during dodge accel phase
if (self.movement_z > 0) self.movement_z = 0;
- self.velocity =
- self.velocity
+ self.velocity +=
+ ((self.dodging_direction_y * velocity_difference) * v_right)
+ ((self.dodging_direction_x * velocity_difference) * v_forward);
if (self.dodging_single_action == 1) {
self.flags &= ~FL_ONGROUND;
- self.velocity =
- self.velocity
- + (autocvar_sv_dodging_up_speed * v_up);
+ self.velocity += (autocvar_sv_dodging_up_speed * v_up);
if (autocvar_sv_dodging_sound == 1)
PlayerSound(playersound_jump, CH_PLAYER, VOICETYPE_PLAYERSOUND);
tap_direction_y = 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);
- float dodge_detected;
+ float dodge_detected = 0;
if (g_dodging == 0)
- return 0;
+ do_nothing = 1;
+
+ if(!self.cvar_cl_dodging)
+ do_nothing = 1;
+
+ if(cvar("g_overkill"))
+ do_nothing = 0;
- dodge_detected = 0;
+ if(frozen_dodging)
+ do_nothing = 0;
+
+ if(g_dodging == 2)
+ do_nothing = 0;
+
+ if(do_nothing != 0)
+ return 0;
// first check if the last dodge is far enough back in time so we can dodge again
if ((time - self.last_dodging_time) < autocvar_sv_dodging_delay)
// get timeout information from the client, so the client can configure it..
MUTATOR_HOOK(GetCvars, dodging_GetCvars, CBC_ORDER_ANY);
- // this just turns on the cvar.
MUTATOR_ONADD
{
- g_dodging = 1;
+ g_dodging = cvar("g_dodging");
}
// this just turns off the cvar.
--- /dev/null
+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;
+}
+
+MUTATOR_HOOKFUNCTION(freeze_PlayerDies)
+{
+ if(frag_deathtype == DEATH_HURTTRIGGER)
+ return FALSE;
+
+ calculate_player_respawn_time(); // player doesn't actually die
+
+ float can_freeze = FALSE;
+
+ if(frag_target.cvar_cl_freeze)
+ if(cvar("g_freeze") == 1)
+ can_freeze = TRUE;
+
+ if(cvar("g_freeze") == 2)
+ can_freeze = TRUE;
+
+ if(cvar("g_freeze") == 3)
+ 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;
+ }
+
+ 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;
+}
+
+MUTATOR_HOOKFUNCTION(freeze_PlayerPreThink)
+{
+ if(self.deadflag != DEAD_NO || !IS_PLAYER(self) || gameover)
+ return FALSE;
+
+ if(round_handler_IsActive())
+ if(!round_handler_IsRoundStarted())
+ return FALSE;
+
+ 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;
+ ++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;
+ }
+ }
+ }
+ 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;
+}
+
+MUTATOR_HOOKFUNCTION(freeze_GetCvars)
+{
+ GetCvars_handleFloat(get_cvars_s, get_cvars_f, cvar_cl_freeze, "cl_freeze");
+
+ return FALSE;
+}
+
+MUTATOR_HOOKFUNCTION(freeze_BuildMutatorsString)
+{
+ ret_string = strcat(ret_string, ":Freeze");
+ return FALSE;
+}
+
+MUTATOR_HOOKFUNCTION(freeze_BuildMutatorsPrettyString)
+{
+ ret_string = strcat(ret_string, ", Freeze");
+ 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;
+}
--- /dev/null
+.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;
\ No newline at end of file
--- /dev/null
+.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);
+ 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;
+}
+
+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;
+}
+
+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;
+}
+
+MUTATOR_HOOKFUNCTION(hats_RemovePlayer)
+{
+ self.hatentity = world;
+ self.hatname = "";
+
+ 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;
+}
if (!self.ammo_cells)
self.ammo_cells = autocvar_g_instagib_ammo_drop;
- StartItem ("models/items/a_cells.md3",
+ StartItem (Item_Model("a_cells.md3"),
"misc/itempickup.wav", 45, 0,
"Vaporizer Ammo", IT_CELLS, 0, 0, generic_pickupevalfunc, 100);
}
+void spawnfunc_item_minst_rockets (void)
+{
+ if (!g_instagib) { remove(self); return; }
+ if(!cvar("g_instagib_withmines")) { remove(self); return; }
+ if (!self.ammo_rockets)
+ self.ammo_rockets = autocvar_g_instagib_ammo_rockets;
+
+ StartItem (Item_Model("a_rockets.md3"),
+ "misc/itempickup.wav", 45, 0,
+ "Vaporizer Ammo", IT_ROCKETS, 0, 0, generic_pickupevalfunc, 100);
+}
+
+void instagib_item_supercells()
+{
+ if(!g_instagib) { remove(self); return; }
+ if(!self.ammo_supercells)
+ self.ammo_supercells = 2;
+
+ StartItem (Item_Model("a_supercells.md3"),
+ "misc/itempickup.wav", 45, 0,
+ "supercells", IT_SUPERCELLS, 0, 0, generic_pickupevalfunc, 100);
+}
+
void instagib_health_mega()
{
self.max_health = 1;
- StartItem ("models/items/g_h100.md3",
+ StartItem (Item_Model("g_h100.md3"),
"misc/megahealth.wav", g_pickup_respawntime_powerup, g_pickup_respawntimejitter_powerup,
"Extralife", IT_NAILS, 0, FL_POWERUP, generic_pickupevalfunc, BOT_PICKUP_RATING_HIGH);
}
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;
+ }
+ }
else
{
self.instagib_needammo = TRUE;
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_BotShouldAttack)
-{
- if(checkentity.items & IT_STRENGTH)
- return TRUE;
-
- return FALSE;
-}
-
MUTATOR_HOOKFUNCTION(instagib_MakePlayerObserver)
{
instagib_stop_countdown(self);
{
if (!(self.effects & EF_FULLBRIGHT))
self.effects |= EF_FULLBRIGHT;
-
- 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_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;
+
+ 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))
{
- if ((frag_deathtype == DEATH_FALL) ||
- (frag_deathtype == DEATH_DROWN) ||
- (frag_deathtype == DEATH_SLIME) ||
- (frag_deathtype == DEATH_LAVA))
+ if(frag_deathtype == DEATH_FALL)
+ frag_damage = 0; // never count fall damage
+
+ if(!autocvar_g_instagib_damagedbycontents)
+ switch(frag_deathtype)
{
- frag_damage = 0;
+ case DEATH_DROWN:
+ case DEATH_SLIME:
+ case DEATH_LAVA:
+ frag_damage = 0;
+ break;
}
if(IS_PLAYER(frag_attacker))
{
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)
{
frag_attacker.armorvalue -= 1;
Send_Notification(NOTIF_ONE, frag_attacker, MSG_CENTER, CENTER_INSTAGIB_LIVES_REMAINING, frag_attacker.armorvalue);
- frag_attacker.damage_dealt += 1;
+ frag_attacker.damage_dealt += frag_mirrordamage;
}
frag_mirrordamage = 0;
}
- if(frag_target.items & IT_STRENGTH)
+ if(frag_target.buffs & BUFF_INVISIBLE)
yoda = 1;
return FALSE;
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;
MUTATOR_HOOKFUNCTION(instagib_FilterItem)
{
if(self.classname == "item_cells")
- return TRUE; // no normal cells?
+ if(!autocvar_g_instagib_use_normal_ammo)
+ return TRUE;
+
+ if(self.classname == "item_rockets")
+ if(!autocvar_g_instagib_use_normal_ammo || !cvar("g_instagib_withmines"))
+ return TRUE;
if(self.weapon == WEP_VAPORIZER && self.classname == "droppedweapon")
{
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;
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;
- // 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;
}
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;
}
+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;
+}
+
MUTATOR_HOOKFUNCTION(instagib_ItemTouch)
{
- if(self.ammo_cells)
+ if(self.ammo_cells || self.ammo_supercells)
{
// play some cool sounds ;)
if (IS_CLIENT(other))
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;
-
- e.nextthink = time + 0.1;
- e.spawnflags = self.spawnflags;
- e.noalign = self.noalign;
- setorigin(e, self.origin);
+ if(WEP_CVAR_PRI(vaporizer, charge))
+ if(self.classname == "item_cells" || self.classname == "item_minst_cells")
+ if(random() <= 0.5)
+ instagib_item_supercells();
- return TRUE;
+ return FALSE;
}
MUTATOR_HOOKFUNCTION(instagib_BuildMutatorsString)
MUTATOR_HOOK(MatchEnd, instagib_MatchEnd, CBC_ORDER_ANY);
MUTATOR_HOOK(MonsterDropItem, instagib_MonsterLoot, CBC_ORDER_ANY);
MUTATOR_HOOK(MonsterSpawn, instagib_MonsterSpawn, CBC_ORDER_ANY);
- MUTATOR_HOOK(BotShouldAttack, instagib_BotShouldAttack, CBC_ORDER_ANY);
- MUTATOR_HOOK(PlayerPhysics, instagib_PlayerPhysics, CBC_ORDER_ANY);
MUTATOR_HOOK(PlayerSpawn, instagib_PlayerSpawn, CBC_ORDER_ANY);
MUTATOR_HOOK(PlayerDamage_Calculate, instagib_PlayerDamage, CBC_ORDER_ANY);
MUTATOR_HOOK(MakePlayerObserver, instagib_MakePlayerObserver, CBC_ORDER_ANY);
MUTATOR_HOOK(SetStartItems, instagib_SetStartItems, CBC_ORDER_ANY);
MUTATOR_HOOK(ItemTouch, instagib_ItemTouch, CBC_ORDER_ANY);
MUTATOR_HOOK(FilterItem, instagib_FilterItem, CBC_ORDER_ANY);
- MUTATOR_HOOK(CustomizeWaypoint, instagib_CustomizeWaypoint, CBC_ORDER_ANY);
MUTATOR_HOOK(Item_RespawnCountdown, instagib_ItemCountdown, CBC_ORDER_ANY);
+ MUTATOR_HOOK(PlayerDies, instagib_PlayerDies, CBC_ORDER_ANY);
MUTATOR_HOOK(PlayerDamage_SplitHealthArmor, instagib_SplitHealthArmor, CBC_ORDER_ANY);
MUTATOR_HOOK(PlayerPowerups, instagib_PlayerPowerups, CBC_ORDER_ANY);
MUTATOR_HOOK(ForbidThrowCurrentWeapon, instagib_ForbidThrowing, CBC_ORDER_ANY);
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;
}
--- /dev/null
+float g_instagib;
+
+void() spawnfunc_item_minst_cells;
\ No newline at end of file
--- /dev/null
+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"));
+ }
+ 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);
+ }
+ ie_Debug(strcat("^3ITEMEDITOR - SERVER: ^7successfully loaded storage file ^3", file_name, "\n"));
+ }
+ fclose(file_get);
+
+ 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;
+}
+
+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;
+}
+
+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(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; }
+
+ string item_name = strzone(argv(2));
+ 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);
+ setorigin(e, trace_endpos);
+ e.ie_itemname = item_name;
+ ie_SetItemType(e);
+ 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)
+ 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;
+ }
+ else
+ {
+ sprint(self, "Item not found\n");
+ 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; }
+
+ entity e, theitem = world;
+ 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;
+ }
+ }
+
+ sprint(self, "Command was not handled\n");
+ return TRUE;
+ }
+
+ 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;
+ bprint("Item editing has been enabled!\n");
+ return TRUE;
+ }
+ case "disable":
+ case "stop":
+ {
+ ie_enabled = FALSE;
+ bprint("Item editing has been disabled!\n");
+ return TRUE;
+ }
+ case "load":
+ {
+ 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;
+ }
+ case "unload":
+ {
+ 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_Database_Unload();
+ bprint("Item database has been unloaded!\n");
+ 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;
+ }
+ }
+ print("Command was not handled\n");
+ return TRUE;
+ }
+ 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;
+ if(!ie_database_loaded)
+ return FALSE;
+ if(!autocvar_g_itemeditor_storage_autosave)
+ return FALSE;
+ if(time < ie_autosave_time)
+ return FALSE;
+ ie_autosave_time = time + autocvar_g_itemeditor_storage_autosave;
+
+ ie_Database_Save();
+
+ 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;
+}
.float multijump_count;
.float multijump_ready;
+.float cvar_cl_multijump;
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;
}
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
else
self.multijump_ready = FALSE;
- 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 && 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
{
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
return FALSE;
}
+MUTATOR_HOOKFUNCTION(multijump_GetCvars)
+{
+ GetCvars_handleFloat(get_cvars_s, get_cvars_f, cvar_cl_multijump, "cl_multijump");
+ return FALSE;
+}
+
MUTATOR_HOOKFUNCTION(multijump_BuildMutatorsString)
{
ret_string = strcat(ret_string, ":multijump");
{
MUTATOR_HOOK(PlayerPhysics, multijump_PlayerPhysics, CBC_ORDER_ANY);
MUTATOR_HOOK(PlayerJump, multijump_PlayerJump, CBC_ORDER_ANY);
+ MUTATOR_HOOK(GetCvars, multijump_GetCvars, CBC_ORDER_ANY);
MUTATOR_HOOK(BuildMutatorsString, multijump_BuildMutatorsString, CBC_ORDER_ANY);
MUTATOR_HOOK(BuildMutatorsPrettyString, multijump_BuildMutatorsPrettyString, CBC_ORDER_ANY);
d = vlen(WarpZone_UnTransformOrigin(RandomSelection_chosen_ent, self.origin) - RandomSelection_chosen_ent.fireball_impactvec);
d = damage + (edgedamage - damage) * (d / dist);
Fire_AddDamage(RandomSelection_chosen_ent, self.realowner, d * burntime, burntime, self.projectiledeathtype | HITTYPE_BOUNCE);
- //trailparticles(self, particleeffectnum("fireball_laser"), self.origin, RandomSelection_chosen_ent.fireball_impactvec);
- pointparticles(particleeffectnum("fireball_laser"), self.origin, RandomSelection_chosen_ent.fireball_impactvec - self.origin, 1);
+ Send_Effect(EFFECT_FIREBALL_LASER, self.origin, RandomSelection_chosen_ent.fireball_impactvec - self.origin, 1);
}
}
entity proj;
vector kick;
- spamsound(self, CH_SHOTS, "weapons/fireball_fire.wav", VOL_BASE, ATTEN_NORM);
+ spamsound(self, CH_SHOTS, W_Sound("fireball_fire"), VOL_BASE, ATTEN_NORM);
proj = spawn ();
proj.owner = self.owner;
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);
if(frost_target.ballcarried)
if(g_keepaway) { ka_DropEvent(frost_target); }
if(frost_target.flagcarried) { ctf_Handle_Throw(frost_target, world, DROP_THROW); }
if(frost_target.nade) { toss_nade(frost_target, '0 0 0', time + 0.05); }
- kh_Key_DropAll(frost_target, FALSE);
+ entity key;
+ KH_FOR_EACH_KEY(key) if(key.owner == frost_target) { kh_Handle_Throw(frost_target, world, key, DROP_THROW); }
}
void nade_ice_think()
{
if ( autocvar_g_nades_ice_explode )
{
- string expef;
+ float expef;
switch(self.realowner.team)
{
- case NUM_TEAM_1: expef = "nade_red_explode"; break;
- case NUM_TEAM_2: expef = "nade_blue_explode"; break;
- case NUM_TEAM_3: expef = "nade_yellow_explode"; break;
- case NUM_TEAM_4: expef = "nade_pink_explode"; break;
- default: expef = "nade_neutral_explode"; break;
+ case NUM_TEAM_1: expef = EFFECT_NADE_RED_EXPLODE; break;
+ case NUM_TEAM_2: expef = EFFECT_NADE_BLUE_EXPLODE; break;
+ case NUM_TEAM_3: expef = EFFECT_NADE_YELLOW_EXPLODE; break;
+ case NUM_TEAM_4: expef = EFFECT_NADE_PINK_EXPLODE; break;
+ default: expef = EFFECT_NADE_NEUTRAL_EXPLODE; break;
}
- pointparticles(particleeffectnum(expef), self.origin + '0 0 1', '0 0 0', 1);
- sound(self, CH_SHOTS, "weapons/rocket_impact.wav", VOL_BASE, ATTEN_NORM);
+ Send_Effect(expef, self.origin + '0 0 1', '0 0 0', 1);
+ sound(self, CH_SHOTS, W_Sound("rocket_impact"), VOL_BASE, ATTEN_NORM);
RadiusDamage(self, self.realowner, autocvar_g_nades_nade_damage, autocvar_g_nades_nade_edgedamage,
autocvar_g_nades_nade_radius, self, world, autocvar_g_nades_nade_force, self.projectiledeathtype, self.enemy);
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)
{
self.nade_special_time = time+0.7;
-
- pointparticles(particleeffectnum("electro_impact"), self.origin, '0 0 0', 1);
- pointparticles(particleeffectnum("icefield"), self.origin, '0 0 0', 1);
+ Send_Effect(EFFECT_ELECTRO_IMPACT, self.origin, '0 0 0', 1);
+ Send_Effect(EFFECT_ICEFIELD, self.origin, '0 0 0', 1);
}
for(e = findradius(self.origin, autocvar_g_nades_nade_radius); e; e = e.chain)
if(e != self)
if(!autocvar_g_nades_ice_teamcheck || (DIFF_TEAM(e, self.realowner) || e == self.realowner))
- if(e.takedamage && e.deadflag == DEAD_NO)
+ if(e.takedamage)
if(e.health > 0)
+ if(!Player_Trapped(e))
if(!e.revival_time || ((time - e.revival_time) >= 1.5))
- if(!e.frozen)
if(current_freeze_time > 0)
nade_ice_freeze(self, e, current_freeze_time);
}
{
float maxhealth;
float health_factor;
- if(IS_PLAYER(other) || (other.flags & FL_MONSTER))
+ if(IS_PLAYER(other) || IS_MONSTER(other))
if(other.deadflag == DEAD_NO)
if(!other.frozen)
{
}
if ( health_factor > 0 )
{
- maxhealth = (other.flags & FL_MONSTER) ? other.max_health : g_pickup_healthmega_max;
+ maxhealth = (IS_MONSTER(other)) ? other.max_health : 200; // TODO: find a good limiter that works across all modes and mutators
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);
}
- 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;
}
void nade_boom()
{
- string expef;
+ float expef;
float nade_blast = 1;
switch ( self.nade_type )
{
case NADE_TYPE_NAPALM:
nade_blast = autocvar_g_nades_napalm_blast;
- expef = "explosion_medium";
+ expef = EFFECT_EXPLOSION_MEDIUM;
break;
case NADE_TYPE_ICE:
nade_blast = 0;
- expef = "electro_combo"; // hookbomb_explode electro_combo bigplasma_impact
+ expef = EFFECT_ELECTRO_COMBO; // hookbomb_explode electro_combo bigplasma_impact
break;
case NADE_TYPE_TRANSLOCATE:
nade_blast = 0;
- expef = "";
+ expef = 0;
break;
case NADE_TYPE_MONSTER:
case NADE_TYPE_SPAWN:
nade_blast = 0;
switch(self.realowner.team)
{
- case NUM_TEAM_1: expef = "spawn_event_red"; break;
- case NUM_TEAM_2: expef = "spawn_event_blue"; break;
- case NUM_TEAM_3: expef = "spawn_event_yellow"; break;
- case NUM_TEAM_4: expef = "spawn_event_pink"; break;
- default: expef = "spawn_event_neutral"; break;
+ case NUM_TEAM_1: expef = EFFECT_SPAWN_RED; break;
+ case NUM_TEAM_2: expef = EFFECT_SPAWN_BLUE; break;
+ case NUM_TEAM_3: expef = EFFECT_SPAWN_YELLOW; break;
+ case NUM_TEAM_4: expef = EFFECT_SPAWN_PINK; break;
+ default: expef = EFFECT_SPAWN_NEUTRAL; break;
}
break;
case NADE_TYPE_HEAL:
nade_blast = 0;
- expef = "spawn_event_red";
+ expef = EFFECT_SPAWN_RED;
break;
default:
case NADE_TYPE_NORMAL:
switch(self.realowner.team)
{
- case NUM_TEAM_1: expef = "nade_red_explode"; break;
- case NUM_TEAM_2: expef = "nade_blue_explode"; break;
- case NUM_TEAM_3: expef = "nade_yellow_explode"; break;
- case NUM_TEAM_4: expef = "nade_pink_explode"; break;
- default: expef = "nade_neutral_explode"; break;
+ case NUM_TEAM_1: expef = EFFECT_NADE_RED_EXPLODE; break;
+ case NUM_TEAM_2: expef = EFFECT_NADE_BLUE_EXPLODE; break;
+ case NUM_TEAM_3: expef = EFFECT_NADE_YELLOW_EXPLODE; break;
+ case NUM_TEAM_4: expef = EFFECT_NADE_PINK_EXPLODE; break;
+ default: expef = EFFECT_NADE_NEUTRAL_EXPLODE; break;
}
}
- if(expef != "")
- pointparticles(particleeffectnum(expef), findbetterlocation(self.origin, 8), '0 0 0', 1);
+ if(expef) { Send_Effect(expef, self.origin + '0 0 1', '0 0 0', 1); }
sound(self, CH_SHOTS_SINGLE, "misc/null.wav", VOL_BASE, ATTEN_NORM);
- sound(self, CH_SHOTS, "weapons/rocket_impact.wav", VOL_BASE, ATTEN_NORM);
+ sound(self, CH_SHOTS, W_Sound("rocket_impact"), VOL_BASE, ATTEN_NORM);
self.event_damage = func_null; // prevent somehow calling damage in the next call
//UpdateCSQCProjectile(self);
if(self.health == self.max_health)
{
- spamsound(self, CH_SHOTS, strcat("weapons/grenade_bounce", ftos(1 + rint(random() * 5)), ".wav"), VOL_BASE, ATTEN_NORM);
+ spamsound(self, CH_SHOTS, W_Sound(strcat("grenade_bounce", ftos(1 + rint(random() * 5)))), VOL_BASE, ATTEN_NORM);
return;
}
if ( player.bonus_nade_score >= 1 )
{
Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_NADE_BONUS);
+ Send_Notification(NOTIF_ONE, player, MSG_ANNCE, ANNCE_BONUSNADE);
play2(player,"kh/alarm.wav");
player.bonus_nades++;
player.bonus_nade_score -= 1;
void nade_prime()
{
+ if(autocvar_g_nades_bonus_only)
+ if(!self.bonus_nades)
+ return; // only allow bonus nades
+
if(self.nade)
remove(self.nade);
n.classname = "nade";
fn.classname = "fake_nade";
- if(self.items & IT_STRENGTH && autocvar_g_nades_bonus_onstrength)
- n.nade_type = self.nade_type;
- else if (self.bonus_nades >= 1)
+ if (self.bonus_nades >= 1)
{
n.nade_type = self.nade_type;
n.pokenade_type = self.pokenade_type;
if (!autocvar_g_nades)
return FALSE; // allow turning them off mid match
- if(forbidWeaponUse())
+ if(forbidWeaponUse(self))
return FALSE;
if (!IS_PLAYER(self))
}
}
-void nades_Clear(entity player)
+void nades_Clear(entity player, float allplayers)
{
- if(player.nade)
- remove(player.nade);
- if(player.fake_nade)
- remove(player.fake_nade);
+ if(allplayers)
+ {
+ entity head;
+ FOR_EACH_PLAYER(head) if(head != player)
+ {
+ if(head.nade) { remove(head.nade); }
+ if(head.fake_nade) { remove(head.fake_nade); }
+ head.nade = head.fake_nade = world;
+ head.nade_timer = 0;
+ }
+
+ return;
+ }
+
+ if(!player) { return; }
+
+ if(player.nade) { remove(player.nade); }
+ if(player.fake_nade) { remove(player.fake_nade); }
player.nade = player.fake_nade = world;
player.nade_timer = 0;
{
entity key;
float key_count = 0;
- FOR_EACH_KH_KEY(key) if(key.owner == self) { ++key_count; }
+ KH_FOR_EACH_KEY(key) if(key.owner == self) { ++key_count; }
- if(self.flagcarried || self.ballcarried) // this player is important
+ if(self.flagcarried || self.isvip || self.ballcarried) // this player is important
time_score = autocvar_g_nades_bonus_score_time_flagcarrier;
else
time_score = autocvar_g_nades_bonus_score_time;
self.nade_timer = 0;
if(self.nade_spawnloc)
+ if(!Player_Trapped(self))
{
setorigin(self, self.nade_spawnloc.origin);
self.nade_spawnloc.cnt -= 1;
MUTATOR_HOOKFUNCTION(nades_PlayerDies)
{
if(frag_target.nade)
- if(!frag_target.frozen || !autocvar_g_freezetag_revive_nade)
+ if(!frag_target.frozen || !autocvar_g_freeze_revive_nade)
toss_nade(frag_target, '0 0 100', max(frag_target.nade.wait, time + 0.05));
float killcount_bonus = ((frag_attacker.killcount >= 1) ? bound(0, autocvar_g_nades_bonus_score_minor * frag_attacker.killcount, autocvar_g_nades_bonus_score_medium) : autocvar_g_nades_bonus_score_minor);
MUTATOR_HOOKFUNCTION(nades_PlayerDamage)
{
if(frag_target.frozen)
- if(autocvar_g_freezetag_revive_nade)
+ if(autocvar_g_freeze_revive_nade)
if(frag_attacker == frag_target)
if(frag_deathtype == DEATH_NADE)
if(time - frag_inflictor.toss_time <= 0.1)
{
Unfreeze(frag_target);
- frag_target.health = autocvar_g_freezetag_revive_nade_health;
- pointparticles(particleeffectnum("iceorglass"), frag_target.origin, '0 0 0', 3);
+ frag_target.health = autocvar_g_freeze_revive_nade_health;
+ Send_Effect(EFFECT_ICEORGLASS, frag_target.origin, '0 0 0', 3);
frag_damage = 0;
frag_force = '0 0 0';
Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_FREEZETAG_REVIVED_NADE, frag_target.netname);
MUTATOR_HOOKFUNCTION(nades_RemovePlayer)
{
- nades_Clear(self);
+ nades_Clear(self, FALSE);
nades_RemoveBonus(self);
return FALSE;
}
precache_model("models/weapons/v_ok_grenade.md3");
precache_model("models/ctf/shield.md3");
- precache_sound("weapons/rocket_impact.wav");
- 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(W_Sound("rocket_impact"));
+ 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("overkill/grenadebip.ogg");
}
void toss_nade(entity e, vector _velocity, float _time);
-// 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;
.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
case "devastator": return "minelayer";
case "machinegun": return "hlac";
case "vortex": return "rifle";
- case "shotgun": return "shockwave";
+ //case "shotgun": return "shockwave";
default: return string_null;
}
}
MUTATOR_HOOKFUNCTION(nt_FilterItem)
{
- if(nt_IsNewToy(self.weapon))
- self.item_pickupsound = "weapons/weaponpickup_new_toys.wav";
+ if(nt_IsNewToy(self.weapon) && autocvar_g_new_toys_use_pickupsound)
+ self.item_pickupsound = strzone(W_Sound("weaponpickup_new_toys"));
return 0;
}
if(time > 1) // game loads at time 1
error("This cannot be added at runtime\n");
- precache_sound("weapons/weaponpickup_new_toys.wav");
+ precache_sound(W_Sound("weaponpickup_new_toys"));
// mark the guns as ok to use by e.g. impulse 99
float i;
if (autocvar_g_nix_with_healtharmor)
return 0;
break;
- case IT_STRENGTH:
- case IT_INVINCIBLE:
- if (autocvar_g_nix_with_powerups)
- return 0;
- break;
}
return 1; // delete all other items
return; // dummy
if(ent.ok_use_ammocharge)
- if(!ent.BUTTON_ATCK) // not while attacking?
+ if(!ent.BUTTON_ATCK || autocvar_g_overkill_ammo_charge_attack) // not while attacking?
ent.ammo_charge[wep] = min(autocvar_g_overkill_ammo_charge_limit, ent.ammo_charge[wep] + cvar(sprintf("g_overkill_ammo_charge_rate_%s", wepent.netname)) * frametime / W_TICSPERFRAME);
}
{
entity oldself = self;
- if(self.flags & FL_MONSTER)
+ if(IS_MONSTER(self))
{
remove(other); // remove default item
other = world;
ok_IncreaseCharge(self, self.weapon);
+ float weaponuse_forbidden = forbidWeaponUse(self);
+
if(self.BUTTON_ATCK2)
- if(!forbidWeaponUse() || self.weapon_blocked) // allow if weapon is blocked
+ if(!weaponuse_forbidden || (weaponuse_forbidden == 4 || weaponuse_forbidden == 2)) // allow if weapon is blocked
if(time >= self.jump_interval)
{
self.jump_interval = time + WEP_CVAR_PRI(blaster, refire) * W_WeaponRateFactor();
{
//Send_Notification(NOTIF_ONE, self, MSG_CENTER, CENTER_OVERKILL_CHARGE);
self.ok_notice_time = time + 2;
- play2(self, "weapons/dryfire.wav");
+ play2(self, W_Sound("dryfire"));
}
if(self.weaponentity.state != WS_CLEAR)
w_ready();
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.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;
+ }
- 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.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;
}
return FALSE;
void ok_SetCvars()
{
+ // we can't rely on custom balances here yet, as servers may be outdated
+
// hack to force overkill playermodels
cvar_settemp("sv_defaultcharacter", "1");
cvar_settemp("sv_defaultplayermodel", "models/ok_player/okrobot1.dpm models/ok_player/okrobot2.dpm models/ok_player/okrobot3.dpm models/ok_player/okrobot4.dpm models/ok_player/okmale1.dpm models/ok_player/okmale2.dpm models/ok_player/okmale3.dpm models/ok_player/okmale4.dpm");
cvar_settemp("sv_defaultplayermodel_red", "models/ok_player/okrobot1.dpm models/ok_player/okrobot2.dpm models/ok_player/okrobot3.dpm models/ok_player/okrobot4.dpm");
cvar_settemp("sv_defaultplayermodel_blue", "models/ok_player/okmale1.dpm models/ok_player/okmale2.dpm models/ok_player/okmale3.dpm models/ok_player/okmale4.dpm");
+
+ // shotgun
+ cvar_settemp("g_balance_shotgun_primary_damage", "17");
+ cvar_settemp("g_balance_shotgun_primary_bullets", "10");
+ cvar_settemp("g_balance_shotgun_primary_force", "80");
+ cvar_settemp("g_balance_shotgun_primary_spread", "0.07");
+ cvar_settemp("g_balance_shotgun_primary_animtime", "0.65");
+ cvar_settemp("g_balance_shotgun_primary_ammo", "6.25");
+ cvar_settemp("g_balance_shotgun_reload_ammo", "50");
+ cvar_settemp("g_balance_shotgun_reload_time", "2");
+
+ // machinegun
+ cvar_settemp("g_balance_machinegun_sustained_damage", "25");
+ cvar_settemp("g_balance_machinegun_sustained_spread", "0.01");
+ cvar_settemp("g_balance_machinegun_sustained_force", "5");
+ cvar_settemp("g_balance_machinegun_reload_ammo", "30");
+ cvar_settemp("g_balance_machinegun_reload_time", "1.5");
+
+ // vortex
+ cvar_settemp("g_balance_vortex_primary_damage", "100");
+ cvar_settemp("g_balance_vortex_primary_force", "500");
+ cvar_settemp("g_balance_vortex_primary_refire", "0.75");
+ cvar_settemp("g_balance_vortex_primary_animtime", "0.95");
+ cvar_settemp("g_balance_vortex_primary_ammo", "10");
+ cvar_settemp("g_balance_vortex_secondary", "1");
+ cvar_settemp("g_balance_vortex_charge", "0");
+ cvar_settemp("g_balance_vortex_reload_ammo", "50");
+ cvar_settemp("g_balance_vortex_reload_time", "2");
+
+ // laser
+ cvar_settemp("g_balance_vaporizer_secondary_force", "300");
+
+ // misc
+ cvar_settemp("g_projectiles_newton_style_2_minfactor", "1");
+ cvar_settemp("g_pickup_healthmega_anyway", "0");
+ cvar_settemp("g_pickup_healthmega_max", "200");
+ cvar_settemp("g_pickup_armorsmall_anyway", "0");
+ cvar_settemp("g_pickup_armorsmall_max", "20");
}
void ok_Initialize()
precache_all_playermodels("models/ok_player/*.dpm");
- precache_model("models/weapons/h_ok_mg.iqm");
- precache_model("models/weapons/v_ok_mg.md3");
- precache_model("models/weapons/g_ok_mg.md3");
-
- precache_model("models/weapons/h_ok_shotgun.iqm");
- precache_model("models/weapons/v_ok_shotgun.md3");
- precache_model("models/weapons/g_ok_shotgun.md3");
-
- precache_model("models/weapons/h_ok_sniper.iqm");
- precache_model("models/weapons/v_ok_sniper.md3");
- precache_model("models/weapons/g_ok_sniper.md3");
-
- precache_sound("weapons/dryfire.wav");
+ precache_model(W_Model("g_ok_mg.md3"));
+ precache_model(W_Model("v_ok_mg.md3"));
+ precache_model(W_Model("h_ok_mg.iqm"));
+ precache_model(W_Model("g_ok_shotgun.md3"));
+ precache_model(W_Model("v_ok_shotgun.md3"));
+ precache_model(W_Model("h_ok_shotgun.iqm"));
+ precache_model(W_Model("g_ok_sniper.md3"));
+ precache_model(W_Model("v_ok_sniper.md3"));
+ precache_model(W_Model("h_ok_sniper.iqm"));
+
+ precache_sound(W_Sound("dryfire"));
addstat(STAT_OK_AMMO_CHARGE, AS_FLOAT, ok_use_ammocharge);
addstat(STAT_OK_AMMO_CHARGEPOOL, AS_FLOAT, ok_ammo_charge);
// if the item is not spawned, make sure the invisible / ghost item returns to its origin and stays there
if(autocvar_g_physical_items_reset)
{
- if(self.owner.nextthink > time) // awaiting respawn
+ if(self.owner.wait > time) // awaiting respawn
{
setorigin(self, self.spawn_origin);
self.angles = self.spawn_angles;
self.solid = SOLID_NOT;
+ self.alpha = -1;
self.movetype = MOVETYPE_NONE;
}
else
{
+ self.alpha = 1;
self.solid = SOLID_CORPSE;
self.movetype = MOVETYPE_PHYSICS;
}
wep.touch = physical_item_touch;
wep.event_damage = physical_item_damage;
- wep.spawn_origin = self.origin;
+ if(!wep.cnt)
+ {
+ // fix the spawn origin
+ setorigin(wep, wep.origin + '0 0 1');
+ entity oldself;
+ oldself = self;
+ self = wep;
+ builtin_droptofloor();
+ self = oldself;
+ }
+
+ wep.spawn_origin = wep.origin;
wep.spawn_angles = self.angles;
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;
}
--- /dev/null
+.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;
+}
+
+MUTATOR_HOOKFUNCTION(pb_PlayerUseKey)
+{
+ if(MUTATOR_RETURNVALUE || gameover) { return FALSE; }
+
+ if(self.pbhost)
+ {
+ pb_Detach(self.pbhost);
+ return TRUE;
+ }
+ if(self.piggybacker)
+ {
+ pb_Detach(self);
+ 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);
+ 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; }
+ }
+
+ 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;
+}
+
+MUTATOR_HOOKFUNCTION(pb_MonsterThink)
+{
+ if(BROKEN_PBREF(self)) { pb_Detach(self); }
+
+ if(!self.pbent)
+ pb_FixPBEnt(self);
+
+ return FALSE;
+}
+
+MUTATOR_HOOKFUNCTION(pb_ResetMap)
+{
+ FOR_EACH_PLAYER(self) { pb_Detach(self); }
+ 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;
+}
+
+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;
+}
+
+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;
+}
+
+MUTATOR_HOOKFUNCTION(pb_GetCvars)
+{
+ GetCvars_handleFloat(get_cvars_s, get_cvars_f, cvar_cl_nocarry, "cl_nocarry");
+ 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;
+}
--- /dev/null
+.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);
--- /dev/null
+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; }
+ self = oldself;
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+MUTATOR_DEFINITION(mutator_random_vehicles)
+{
+ MUTATOR_HOOK(OnEntityPreSpawn, rvehicles_OnEntityPreSpawn, CBC_ORDER_ANY);
+
+ return FALSE;
+}
--- /dev/null
+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;
+}
+
+MUTATOR_HOOKFUNCTION(ra_FilterItem)
+{
+ switch (self.items)
+ {
+ case IT_5HP:
+ case IT_ARMOR_SHARD:
+ return FALSE;
+ }
+
+ 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;
+}
+
+MUTATOR_HOOKFUNCTION(ra_ForbidThrowCurrentWeapon)
+{
+ return TRUE;
+}
+
+MUTATOR_HOOKFUNCTION(ra_BuildMutatorsString)
+{
+ ret_string = strcat(ret_string, ":RA");
+ return FALSE;
+}
+
+MUTATOR_HOOKFUNCTION(ra_BuildMutatorsPrettyString)
+{
+ ret_string = strcat(ret_string, ", Rifle Arena");
+ return FALSE;
+}
+
+MUTATOR_HOOKFUNCTION(ra_SetModname)
+{
+ modname = "Rifle Arena";
+ 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;
+}
.float msnt_timer;
.vector msnt_deathloc;
+.float cvar_cl_spawn_near_teammate;
+
MUTATOR_HOOKFUNCTION(msnt_Spawn_Score)
{
- if(autocvar_g_spawn_near_teammate_ignore_spawnpoint)
+ if(autocvar_g_spawn_near_teammate_ignore_spawnpoint == 1 || (autocvar_g_spawn_near_teammate_ignore_spawnpoint == 2 && self.cvar_cl_spawn_near_teammate))
return 0;
entity p;
MUTATOR_HOOKFUNCTION(msnt_PlayerSpawn)
{
// Note: when entering this, fixangle is already set.
- if(autocvar_g_spawn_near_teammate_ignore_spawnpoint)
+ if(autocvar_g_spawn_near_teammate_ignore_spawnpoint || self.cvar_cl_spawn_near_teammate)
+ if(!Player_Trapped(self))
{
if(autocvar_g_spawn_near_teammate_ignore_spawnpoint_delay_death)
self.msnt_timer = time + autocvar_g_spawn_near_teammate_ignore_spawnpoint_delay_death;
if(team_mate.deadflag == DEAD_NO)
if(team_mate.msnt_timer < time)
if(SAME_TEAM(self, team_mate))
+ if(!Player_Trapped(team_mate))
if(time > team_mate.spawnshieldtime) // spawn shielding
- if(team_mate.frozen == 0)
if(team_mate != self)
{
tracebox(team_mate.origin, PL_MIN, PL_MAX, team_mate.origin - '0 0 100', MOVE_WORLDONLY, team_mate);
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;
+}
+
MUTATOR_DEFINITION(mutator_spawn_near_teammate)
{
MUTATOR_HOOK(Spawn_Score, msnt_Spawn_Score, CBC_ORDER_ANY);
MUTATOR_HOOK(PlayerSpawn, msnt_PlayerSpawn, CBC_ORDER_ANY);
MUTATOR_HOOK(PlayerDies, msnt_PlayerDies, CBC_ORDER_ANY);
+ MUTATOR_HOOK(GetCvars, msnt_GetCvars, CBC_ORDER_ANY);
return 0;
}
#define _SSMAGIX "SUPERSPEC_OPTIONSFILE_V1"
#define _ISLOCAL ((edict_num(1) == self) ? TRUE : FALSE)
-#define ASF_STRENGTH 1
-#define ASF_SHIELD 2
#define ASF_MEGA_AR 4
#define ASF_MEGA_HP 8
#define ASF_FLAG_GRAB 16
}
}
- if((self.autospec_flags & ASF_SHIELD && _item.invincible_finished) ||
- (self.autospec_flags & ASF_STRENGTH && _item.strength_finished) ||
- (self.autospec_flags & ASF_MEGA_AR && _item.classname == "item_armor_large") ||
+ if( (self.autospec_flags & ASF_MEGA_AR && _item.classname == "item_armor_large") ||
(self.autospec_flags & ASF_MEGA_HP && _item.classname == "item_health_mega") ||
(self.autospec_flags & ASF_FLAG_GRAB && _item.classname == "item_flag_team"))
{
if(argv(1) == "help")
{
_aspeco = "use cmd autospec [option] [on|off] to set options\n\n";
- _aspeco = strcat(_aspeco, "^3 strength ^7(short^5 st^7) for automatic spectate on strength powerup\n");
- _aspeco = strcat(_aspeco, "^3 shield ^7(short^5 sh^7) for automatic spectate on shield powerup\n");
_aspeco = strcat(_aspeco, "^3 mega_health ^7(short^5 mh^7) for automatic spectate on mega health\n");
_aspeco = strcat(_aspeco, "^3 mega_armor ^7(short^5 ma^7) for automatic spectate on mega armor\n");
_aspeco = strcat(_aspeco, "^3 flag_grab ^7(short^5 fg^7) for automatic spectate on CTF flag grab\n");
}
else
{
- if((argv(i) == "strength") || (argv(i) == "st")) _bits |= ASF_STRENGTH;
- if((argv(i) == "shield") || (argv(i) == "sh")) _bits |= ASF_SHIELD;
if((argv(i) == "mega_health") || (argv(i) == "mh")) _bits |= ASF_MEGA_HP;
if((argv(i) == "mega_armor") || (argv(i) == "ma")) _bits |= ASF_MEGA_AR;
if((argv(i) == "flag_grab") || (argv(i) == "fg")) _bits |= ASF_FLAG_GRAB;
}
_aspeco = "";
- OPTIONINFO(self.autospec_flags, _aspeco, ASF_STRENGTH, "Strength", "strength", "st");
- OPTIONINFO(self.autospec_flags, _aspeco, ASF_SHIELD, "Shield", "shield", "sh");
OPTIONINFO(self.autospec_flags, _aspeco, ASF_MEGA_HP, "Mega Health", "mega_health", "mh");
OPTIONINFO(self.autospec_flags, _aspeco, ASF_MEGA_AR, "Mega Armor", "mega_armor", "ma");
OPTIONINFO(self.autospec_flags, _aspeco, ASF_FLAG_GRAB, "Flag grab", "flag_grab","fg");
entity _player;
FOR_EACH_PLAYER(_player)
{
- if(_player.strength_finished > time || _player.invincible_finished > time)
+ if(_player.buffs)
return _spectate(_player);
}
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)
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)
org = (p1.origin + p2.origin) * 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();
if(!self.frozen)
if(IS_PLAYER(self))
if(self.deadflag == DEAD_NO)
- if (!IS_INDEPENDENT_PLAYER(self))
+ if(!IS_INDEPENDENT_PLAYER(self))
FOR_EACH_PLAYER(other) if(self != other)
{
if(time > other.touchexplode_time)
if(!other.frozen)
if(other.deadflag == DEAD_NO)
- if (!IS_INDEPENDENT_PLAYER(other))
+ if(!IS_INDEPENDENT_PLAYER(other))
if(boxesoverlap(self.absmin, self.absmax, other.absmin, other.absmax))
{
PlayerTouchExplode(self, other);
--- /dev/null
+.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);
+ 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);
+ 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);
+ 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);
+ 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);
+
+ player_multijump = TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+MUTATOR_DEFINITION(mutator_walljump)
+{
+ MUTATOR_HOOK(PlayerJump, walljump_PlayerJump, CBC_ORDER_ANY);
+
+ return FALSE;
+}
--- /dev/null
+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);
+
+ 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;
+
+ 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;
+}
+
+MUTATOR_DEFINITION(mutator_zombie_apocalypse)
+{
+ MUTATOR_HOOK(SV_StartFrame, za_StartFrame, CBC_ORDER_ANY);
+
+ MUTATOR_ONADD
+ {
+ za_spawn_delay = time + game_starttime;
+ }
+
+ return FALSE;
+}
{ if(cvar(mut_cvar) && dependence) { MUTATOR_ADD(mut_name); } }
CHECK_MUTATOR_ADD("g_dodging", mutator_dodging, 1);
- CHECK_MUTATOR_ADD("g_spawn_near_teammate", mutator_spawn_near_teammate, teamplay);
+ CHECK_MUTATOR_ADD("g_spawn_near_teammate", mutator_spawn_near_teammate, teamplay && !g_assault);
CHECK_MUTATOR_ADD("g_physical_items", mutator_physical_items, 1);
CHECK_MUTATOR_ADD("g_touchexplode", mutator_touchexplode, 1);
CHECK_MUTATOR_ADD("g_instagib", mutator_instagib, !g_nexball);
CHECK_MUTATOR_ADD("g_invincible_projectiles", mutator_invincibleprojectiles, 1);
- CHECK_MUTATOR_ADD("g_new_toys", mutator_new_toys, !cvar("g_instagib") && !cvar("g_overkill"));
- CHECK_MUTATOR_ADD("g_nix", mutator_nix, !cvar("g_instagib") && !cvar("g_overkill"));
+ CHECK_MUTATOR_ADD("g_new_toys", mutator_new_toys, !cvar("g_instagib") && !cvar("g_overkill") && !cvar("g_riflearena") && !cvar("g_melee_only"));
+ CHECK_MUTATOR_ADD("g_nix", mutator_nix, !cvar("g_instagib") && !cvar("g_overkill") && !cvar("g_riflearena") && !cvar("g_melee_only"));
CHECK_MUTATOR_ADD("g_rocket_flying", mutator_rocketflying, 1);
CHECK_MUTATOR_ADD("g_vampire", mutator_vampire, !cvar("g_instagib"));
CHECK_MUTATOR_ADD("g_superspectate", mutator_superspec, 1);
- CHECK_MUTATOR_ADD("g_pinata", mutator_pinata, !cvar("g_instagib") && !cvar("g_overkill"));
+ CHECK_MUTATOR_ADD("g_freeze", mutator_freeze, !g_freezetag && !g_ca && !g_jailbreak && !g_cts && !g_race && !g_infection);
+ CHECK_MUTATOR_ADD("g_pinata", mutator_pinata, !cvar("g_instagib") && !cvar("g_overkill") && !cvar("g_riflearena") && !cvar("g_melee_only"));
CHECK_MUTATOR_ADD("g_midair", mutator_midair, 1);
CHECK_MUTATOR_ADD("g_bloodloss", mutator_bloodloss, 1);
CHECK_MUTATOR_ADD("g_random_gravity", mutator_random_gravity, 1);
CHECK_MUTATOR_ADD("g_multijump", mutator_multijump, 1);
- CHECK_MUTATOR_ADD("g_melee_only", mutator_melee_only, !cvar("g_instagib") && !g_nexball);
+ CHECK_MUTATOR_ADD("g_melee_only", mutator_melee_only, !cvar("g_instagib") && !g_nexball && !cvar("g_overkill") && !cvar("g_riflearena"));
CHECK_MUTATOR_ADD("g_nades", mutator_nades, 1);
CHECK_MUTATOR_ADD("g_sandbox", sandbox, 1);
+ CHECK_MUTATOR_ADD("g_riflearena", mutator_riflearena, !cvar("g_instagib") && !g_nexball && !cvar("g_overkill"));
CHECK_MUTATOR_ADD("g_campcheck", mutator_campcheck, 1);
- CHECK_MUTATOR_ADD("g_overkill", mutator_overkill, !cvar("g_instagib") && !g_nexball && cvar_string("g_mod_balance") == "Overkill");
- CHECK_MUTATOR_ADD("g_buffs", mutator_buffs, 1);
+ CHECK_MUTATOR_ADD("g_overkill", mutator_overkill, !cvar("g_instagib") && !g_nexball);
+ CHECK_MUTATOR_ADD("g_za", mutator_zombie_apocalypse, cvar("g_monsters") && !g_nexball && !g_invasion && !g_cts && !g_race);
+ CHECK_MUTATOR_ADD("g_walljump", mutator_walljump, 1);
+ CHECK_MUTATOR_ADD("g_hats", mutator_hats, !cvar("g_overkill"));
+ CHECK_MUTATOR_ADD("g_random_vehicles", mutator_random_vehicles, cvar("g_vehicles"));
+ CHECK_MUTATOR_ADD("g_piggyback", mutator_piggyback, 1);
+ CHECK_MUTATOR_ADD("g_buffs", mutator_buffs, !g_nexball && !g_race && !g_cts);
+ CHECK_MUTATOR_ADD("g_itemeditor", mutator_itemeditor, cvar("g_pickup_items") && !g_ca);
#undef CHECK_MUTATOR_ADD
}
MUTATOR_DECLARATION(gamemode_ctf);
MUTATOR_DECLARATION(gamemode_nexball);
MUTATOR_DECLARATION(gamemode_onslaught);
+MUTATOR_DECLARATION(gamemode_conquest);
MUTATOR_DECLARATION(gamemode_domination);
MUTATOR_DECLARATION(gamemode_lms);
MUTATOR_DECLARATION(gamemode_invasion);
+MUTATOR_DECLARATION(gamemode_vip);
+MUTATOR_DECLARATION(gamemode_infection);
+MUTATOR_DECLARATION(gamemode_jailbreak);
MUTATOR_DECLARATION(gamemode_cts);
MUTATOR_DECLARATION(gamemode_race);
MUTATOR_DECLARATION(gamemode_tdm);
+MUTATOR_DECLARATION(gamemode_deathmatch);
MUTATOR_DECLARATION(mutator_dodging);
MUTATOR_DECLARATION(mutator_invincibleprojectiles);
MUTATOR_DECLARATION(mutator_vampire);
MUTATOR_DECLARATION(mutator_superspec);
MUTATOR_DECLARATION(mutator_instagib);
+MUTATOR_DECLARATION(mutator_freeze);
+MUTATOR_DECLARATION(mutator_overkill);
MUTATOR_DECLARATION(mutator_touchexplode);
MUTATOR_DECLARATION(mutator_pinata);
MUTATOR_DECLARATION(mutator_midair);
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);
-
#include "gamemode_nexball.qc"
#include "gamemode_onslaught.qc"
#include "gamemode_lms.qc"
+#include "gamemode_vip.qc"
#include "gamemode_invasion.qc"
+#include "gamemode_infection.qc"
+#include "gamemode_jailbreak.qc"
#include "gamemode_race.qc"
#include "gamemode_cts.qc"
#include "gamemode_tdm.qc"
+#include "gamemode_conquest.qc"
+#include "gamemode_deathmatch.qc"
#include "mutator_invincibleproj.qc"
#include "mutator_new_toys.qc"
#include "mutator_physical_items.qc"
#include "sandbox.qc"
#include "mutator_superspec.qc"
+#include "mutator_freeze.qc"
#include "mutator_overkill.qc"
#include "mutator_instagib.qc"
#include "mutator_touchexplode.qc"
#include "mutator_multijump.qc"
#include "mutator_melee_only.qc"
#include "mutator_nades.qc"
+#include "mutator_riflearena.qc"
#include "mutator_campcheck.qc"
+#include "mutator_zombie_apocalypse.qc"
+#include "mutator_walljump.qc"
+#include "mutator_hats.qc"
+#include "mutator_random_vehicles.qc"
+#include "mutator_piggyback.qc"
#include "mutator_buffs.qc"
+#include "mutator_itemeditor.qc"
#include "gamemode_ca.qh"
#include "gamemode_ctf.qh"
#include "gamemode_domination.qh"
+#include "gamemode_freezetag.qh"
#include "gamemode_keyhunt.qh"
#include "gamemode_keepaway.qh"
#include "gamemode_nexball.qh"
#include "gamemode_lms.qh"
+#include "gamemode_vip.qh"
#include "gamemode_invasion.qh"
-#include "gamemode_race.qh"
+#include "gamemode_infection.qh"
+#include "gamemode_jailbreak.qh"
#include "gamemode_cts.qh"
+#include "gamemode_race.qh"
+#include "gamemode_onslaught.qh"
+#include "gamemode_conquest.qh"
#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"
.entity object_attach;
.string material;
+void sandbox_SnapToGrid(entity e, float amnt)
+{
+ vector orig = e.origin;
+ orig_x = rint(orig_x / amnt) * amnt;
+ orig_y = rint(orig_y / amnt) * amnt;
+ orig_z = rint(orig_z / amnt) * amnt;
+ setorigin(e, orig);
+ e.prevorigin = orig;
+}
+
.float touch_timer;
void sandbox_ObjectFunction_Touch()
{
{
entity e;
+ if(autocvar_g_sandbox_snaptogrid && self.origin != self.prevorigin)
+ {
+ sandbox_SnapToGrid(self, autocvar_g_sandbox_snaptogrid);
+
+ if(self.angles_y > 45)
+ self.angles_y = 90;
+ else if(self.angles_y < -45)
+ self.angles_y = -90;
+ else if(self.angles_y < 45 || self.angles_y > -45)
+ self.angles_y = 0;
+ }
+
// decide if and how this object can be grabbed
if(autocvar_g_sandbox_readonly)
self.grab = 0; // no grabbing
}
self.nextthink = time;
-
+
CSQCMODEL_AUTOUPDATE();
}
void sandbox_ObjectEdit_Scale(entity e, float f)
{
e.scale = f;
+ e.modelscale = f;
if(e.scale)
{
e.scale = bound(autocvar_g_sandbox_object_scale_min, e.scale, autocvar_g_sandbox_object_scale_max);
+ e.modelscale = f;
setmodel(e, e.model); // reset mins and maxs based on mesh
setsize(e, e.mins * e.scale, e.maxs * e.scale); // adapt bounding box size to model size
+ FixSize(e);
}
}
setorigin(e, trace_endpos);
e.angles_y = self.v_angle_y;
}
-
+
oldself = self;
self = e;
CSQCMODEL_AUTOINIT();
parent = e; // mark parent objects as such
}
// properties stored for all objects
- setmodel(e, argv(argv_num)); ++argv_num;
+ setmodel_fixsize(e, argv(argv_num)); ++argv_num;
e.skin = stof(argv(argv_num)); ++argv_num;
e.alpha = stof(argv(argv_num)); ++argv_num;
e.colormod = stov(argv(argv_num)); ++argv_num;
print_to(self, strcat("^1SANDBOX - WARNING: ^7Flood protection active. Please wait ^3", ftos(self.object_flood - time), " ^7seconds beofore spawning another object"));
return TRUE;
}
+ if(IS_SPEC(self) || IS_OBSERVER(self))
+ {
+ print_to(self, "^1SANDBOX - WARNING: ^7Cannot spawn objects while spectating");
+ return TRUE;
+ }
self.object_flood = time + autocvar_g_sandbox_editor_flood;
if(object_count >= autocvar_g_sandbox_editor_maxobjects)
{
}
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"));
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;
+ }
sandbox_ObjectEdit_Scale(e, stof(argv(3)));
break;
case "solidity":
case "1": // solid
e.solid = SOLID_BBOX;
break;
+ case "2": // bsp solid
+ if(autocvar_g_sandbox_allow_bspsolid)
+ {
+ e.solid = SOLID_BSP;
+ break;
+ }
default:
break;
}
return TRUE;
}
-MUTATOR_HOOKFUNCTION(sandbox_SetModname)
+void sandbox_DelayedInit()
{
- modname = "Sandbox";
- return TRUE;
+ sandbox_Database_Load();
}
MUTATOR_DEFINITION(sandbox)
{
MUTATOR_HOOK(SV_ParseClientCommand, sandbox_PlayerCommand, CBC_ORDER_ANY);
MUTATOR_HOOK(SV_StartFrame, sandbox_StartFrame, CBC_ORDER_ANY);
- MUTATOR_HOOK(SetModname, sandbox_SetModname, CBC_ORDER_ANY);
MUTATOR_ONADD
{
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;
if(other.classname == "grapplinghook")
return; // handled by think
+ if(!autocvar_g_vehicles_teleportable)
+ if(IS_VEHICLE(other))
+ return; // no teleporting vehicles?
+
if(!self.enemy)
error("Portal_Touch called for a broken portal\n");
{
fixedmakevectors(portal.mangle);
sound(portal, CH_SHOTS, "porto/explode.wav", VOL_BASE, ATTEN_NORM);
- pointparticles(particleeffectnum("rocket_explode"), portal.origin + v_forward * 16, v_forward * 1024, 4);
+ Send_Effect(EFFECT_ROCKET_EXPLODE, portal.origin + v_forward * 16, v_forward * 1024, 4);
remove(portal);
}
else
../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/monsters/monsters.qh
-../common/monsters/sv_monsters.qh
-../common/monsters/spawn.qh
../common/weapons/config.qh
../common/weapons/weapons.qh // TODO
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
-//// tZork Turrets ////
-tturrets/include/turrets_early.qh
-vehicles/vehicles_def.qh
+generator.qh
+controlpoint.qh
+
+teamplay.qh
campaign.qh
../common/campaign_common.qh
g_subs.qc
-g_tetris.qc
-
-//runematch.qc
+jeff.qc
g_violence.qc
g_damage.qc
// tZork's libs
movelib.qc
steerlib.qc
-pathlib/pathlib.qh
g_world.qc
g_casings.qc
t_plats.qc
antilag.qc
-//ctf.qc
-//domination.qc
-//mode_onslaught.qc
-//nexball.qc
g_hook.qc
t_swamp.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/cmd.qc
command/sv_cmd.qc
-//assault.qc
-
ipban.qc
../common/mapinfo.qc
race.qc
-
-//// tZork Turrets ////
-tturrets/include/turrets.qh
-vehicles/vehicles.qh
-
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
../common/monsters/spawn.qc
+../common/turrets/sv_turrets.qc
+../common/turrets/turrets.qc
+../common/turrets/checkpoint.qc
+../common/turrets/targettrigger.qc
+
mutators/mutators_include.qc
../warpzonelib/anglestransform.qc
../common/test.qc
../common/util.qc
../common/notifications.qc
+../common/effects.qc
+
+
+../common/minigames/minigames.qc
+../common/minigames/sv_minigames.qc
return l + c / nc;
}
+
+void race_send_speedaward(float msg)
+{
+ // send the best speed of the round
+ WriteByte(msg, SVC_TEMPENTITY);
+ WriteByte(msg, TE_CSQC_RACE);
+ WriteByte(msg, RACE_NET_SPEED_AWARD);
+ WriteInt24_t(msg, floor(speedaward_speed+0.5));
+ WriteString(msg, speedaward_holder);
+}
+
+void race_send_speedaward_alltimebest(float msg)
+{
+ // send the best speed
+ WriteByte(msg, SVC_TEMPENTITY);
+ WriteByte(msg, TE_CSQC_RACE);
+ WriteByte(msg, RACE_NET_SPEED_AWARD_BEST);
+ WriteInt24_t(msg, floor(speedaward_alltimebest+0.5));
+ WriteString(msg, speedaward_alltimebest_holder);
+}
.float race_completed;
float race_completing;
+.float race_penalty;
+
+float speedaward_lastupdate;
+float speedaward_lastsent;
+
+float speedaward_speed;
+string speedaward_holder;
+string speedaward_uid;
+
+float speedaward_alltimebest;
+string speedaward_alltimebest_holder;
+string speedaward_alltimebest_uid;
+
.float race_movetime; // for reading
.float race_movetime_frac; // fractional accumulator for higher accuracy (helper for writing)
.float race_movetime_count; // integer accumulator
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);
-.entity scorekeeper;
entity teamscorekeepers[16];
string scores_label[MAX_SCORE];
float scores_flags[MAX_SCORE];
scores_initialized.classname = "ent_client_scoreinfo";
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)));
+ }
}
/*
return (s.(scores[scorefield]) += score);
}
+float PlayerScore_Set(entity player, float scorefield, float score)
+{
+ if(!scores_initialized) return 0;
+
+ entity s = player.scorekeeper;
+
+ if(!s) {
+ if(gameover)
+ return 0;
+ error("Setting score of unknown player!");
+ }
+
+ float old = s.(scores[scorefield]);
+
+ if(old == score)
+ return old;
+
+ s.SendFlags |= pow(2, scorefield);
+ s.(scores[scorefield]) = score;
+
+ return score;
+}
+
float PlayerTeamScore_Add(entity player, float pscorefield, float tscorefield, float score)
{
float r;
entity scores_initialized; // non-world when scores labels/rules have been set
.float scores[MAX_SCORE];
.float teamscores[MAX_TEAMSCORE];
+.entity scorekeeper;
.float scoreboard_pos;
/**
*/
float PlayerScore_Add(entity player, float scorefield, float score);
+/**
+ * Sets the player's scorefield to an absolute value.
+ * NEVER call this if PlayerScore_Attach has not been called yet!
+ * Means: FIXME make players unable to join the game when not called ClientConnect yet.
+ * Returns the new score.
+ */
+float PlayerScore_Set(entity player, float scorefield, float score);
+
/**
* Initialize the score of this player if needed.
* Does nothing in teamplay.
-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;
self.team = activator.team;
some_spawn_has_been_used = 1;
}
- print("spawnpoint was used!\n");
+ //print("spawnpoint was used!\n");
}
void relocate_spawnpoint()
relocate_spawnpoint();
}
+/*QUAKED spawnfunc_info_player_team1 (1 0 0) (-16 -16 -24) (16 16 24)
+Starting point for a player in team one (Red).
+Keys: "angle" viewing angle when spawning. */
+void spawnfunc_info_player_team1()
+{
+ self.team = NUM_TEAM_1; // red
+ spawnfunc_info_player_deathmatch();
+}
+
+
+/*QUAKED spawnfunc_info_player_team2 (1 0 0) (-16 -16 -24) (16 16 24)
+Starting point for a player in team two (Blue).
+Keys: "angle" viewing angle when spawning. */
+void spawnfunc_info_player_team2()
+{
+ self.team = NUM_TEAM_2; // blue
+ spawnfunc_info_player_deathmatch();
+}
+
+/*QUAKED spawnfunc_info_player_team3 (1 0 0) (-16 -16 -24) (16 16 24)
+Starting point for a player in team three (Yellow).
+Keys: "angle" viewing angle when spawning. */
+void spawnfunc_info_player_team3()
+{
+ self.team = NUM_TEAM_3; // yellow
+ spawnfunc_info_player_deathmatch();
+}
+
+
+/*QUAKED spawnfunc_info_player_team4 (1 0 0) (-16 -16 -24) (16 16 24)
+Starting point for a player in team four (Purple).
+Keys: "angle" viewing angle when spawning. */
+void spawnfunc_info_player_team4()
+{
+ self.team = NUM_TEAM_4; // purple
+ spawnfunc_info_player_deathmatch();
+}
+
// Returns:
// _x: prio (-1 if unusable)
// _y: weight
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);
#ifdef TLIBS_TETSLIBS
void flocker_die()
{
- pointparticles(particleeffectnum("rocket_explode"), self.origin, '0 0 0', 1);
+ Send_Effect(EFFECT_ROCKET_EXPLODE, self.origin, '0 0 0', 1);
self.owner.cnt += 1;
self.owner = world;
{
if (self.movetype == MOVETYPE_NOCLIP) { continue; }
- float vehic = (self.vehicle_flags & VHF_ISVEHICLE);
+ float vehic = IS_VEHICLE(self);
float projectile = (self.flags & FL_PROJECTILE);
- float monster = (self.flags & FL_MONSTER);
+ float monster = IS_MONSTER(self);
if (self.watertype <= CONTENT_WATER && self.waterlevel > 0) // workaround a retarded bug made by id software :P (yes, it's that old of a bug)
{
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;
{
if (!other.iscreature)
return;
- if (other.vehicle_flags & VHF_ISVEHICLE)
+ if(IS_VEHICLE(other))
return;
EXACTTRIGGER_TOUCH;
if(sf & ISF_ANGLES)
{
- self.angles_x = ReadCoord();
- self.angles_y = ReadCoord();
- self.angles_z = ReadCoord();
+ self.angles_x = ReadAngle();
+ self.angles_y = ReadAngle();
+ self.angles_z = ReadAngle();
self.move_angles = self.angles;
}
if(sf & ISF_SIZE)
{
- self.mins_x = ReadCoord();
- self.mins_y = ReadCoord();
- self.mins_z = ReadCoord();
- self.maxs_x = ReadCoord();
- self.maxs_y = ReadCoord();
- self.maxs_z = ReadCoord();
- setsize(self, self.mins, self.maxs);
+ float use_bigsize = ReadByte();
+ setsize(self, '-16 -16 0', (use_bigsize) ? '16 16 48' : '16 16 32');
}
if(sf & ISF_STATUS) // need to read/write status frist so model can handle simple, fb etc.
if(sf & ISF_MODEL)
{
self.drawmask = MASK_NORMAL;
- self.movetype = MOVETYPE_TOSS;
+ self.move_movetype = self.movetype = MOVETYPE_TOSS;
+ //self.renderflags |= RF_DEPTHHACK;
self.draw = ItemDraw;
if(self.mdl)
string _fn2 = substring(_fn, 0 , strlen(_fn) -4);
self.draw = ItemDrawSimple;
-
-
if(fexists(sprintf("%s%s.md3", _fn2, autocvar_cl_simpleitems_postfix)))
self.mdl = strzone(sprintf("%s%s.md3", _fn2, autocvar_cl_simpleitems_postfix));
else if(fexists(sprintf("%s%s.dpm", _fn2, autocvar_cl_simpleitems_postfix)))
self.mdl = strzone(sprintf("%s%s.dpm", _fn2, autocvar_cl_simpleitems_postfix));
else if(fexists(sprintf("%s%s.iqm", _fn2, autocvar_cl_simpleitems_postfix)))
self.mdl = strzone(sprintf("%s%s.iqm", _fn2, autocvar_cl_simpleitems_postfix));
- else if(fexists(sprintf("%s%s.obj", _fn2, autocvar_cl_simpleitems_postfix)))
- self.mdl = strzone(sprintf("%s%s.obj", _fn2, autocvar_cl_simpleitems_postfix));
+ else if(fexists(sprintf("%s%s.mdl", _fn2, autocvar_cl_simpleitems_postfix)))
+ self.mdl = strzone(sprintf("%s%s.mdl", _fn2, autocvar_cl_simpleitems_postfix));
else
{
self.draw = ItemDraw;
- dprint("Simple item requested for ", _fn, " but no model exsist for it\n");
+ dprint("Simple item requested for ", _fn, " but no model exists for it\n");
}
}
if(sf & ISF_ANGLES)
{
- WriteCoord(MSG_ENTITY, self.angles_x);
- WriteCoord(MSG_ENTITY, self.angles_y);
- WriteCoord(MSG_ENTITY, self.angles_z);
+ WriteAngle(MSG_ENTITY, self.angles_x);
+ WriteAngle(MSG_ENTITY, self.angles_y);
+ WriteAngle(MSG_ENTITY, self.angles_z);
}
if(sf & ISF_SIZE)
{
- WriteCoord(MSG_ENTITY, self.mins_x);
- WriteCoord(MSG_ENTITY, self.mins_y);
- WriteCoord(MSG_ENTITY, self.mins_z);
- WriteCoord(MSG_ENTITY, self.maxs_x);
- WriteCoord(MSG_ENTITY, self.maxs_y);
- WriteCoord(MSG_ENTITY, self.maxs_z);
+ WriteByte(MSG_ENTITY, ((self.flags & FL_POWERUP) || self.health || self.armorvalue));
}
if(sf & ISF_STATUS)
float 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;
+ if(autocvar_g_pickup_items == 0)
+ return FALSE;
+ if(g_weaponarena)
+ if(self.weapons || (self.items & IT_AMMO))
+ return FALSE;
return TRUE;
}
e.ItemStatus &= ~ITS_AVAILABLE;
}
- if (e.items & IT_STRENGTH || e.items & IT_INVINCIBLE)
- e.ItemStatus |= ITS_POWERUP;
-
if (autocvar_g_nodepthtestitems)
e.effects |= EF_NODEPTHTEST;
void Item_Respawn (void)
{
Item_Show(self, 1);
- // this is ugly...
- if(self.items == IT_STRENGTH)
- sound (self, CH_TRIGGER, "misc/strength_respawn.wav", VOL_BASE, ATTEN_NORM); // play respawn sound
- else if(self.items == IT_INVINCIBLE)
- sound (self, CH_TRIGGER, "misc/shield_respawn.wav", VOL_BASE, ATTEN_NORM); // play respawn sound
- else
- sound (self, CH_TRIGGER, "misc/itemrespawn.wav", VOL_BASE, ATTEN_NORM); // play respawn sound
+ sound (self, CH_TRIGGER, "misc/itemrespawn.wav", VOL_BASE, ATTEN_NORM); // play respawn sound
setorigin (self, self.origin);
self.think = Item_Think;
self.nextthink = time;
-
- //pointparticles(particleeffectnum("item_respawn"), self.origin + self.mins_z * '0 0 1' + '0 0 48', '0 0 0', 1);
- pointparticles(particleeffectnum("item_respawn"), self.origin + 0.5 * (self.mins + self.maxs), '0 0 0', 1);
+
+ Send_Effect(EFFECT_ITEM_RESPAWN, self.origin + 0.5 * (self.mins + self.maxs), '0 0 0', 1);
}
void Item_RespawnCountdown (void)
{
case IT_FUEL_REGEN: name = "item-fuelregen"; rgb = '1 0.5 0'; break;
case IT_JETPACK: name = "item-jetpack"; rgb = '0.5 0.5 0.5'; break;
- case IT_STRENGTH: name = "item-strength"; rgb = '0 0 1'; break;
- case IT_INVINCIBLE: name = "item-shield"; rgb = '1 0 1'; break;
}
item_name = name;
item_color = rgb;
pickedup |= Item_GiveAmmoTo(item, player, ammo_rockets, g_pickup_rockets_max, ITEM_MODE_NONE);
pickedup |= Item_GiveAmmoTo(item, player, ammo_cells, g_pickup_cells_max, ITEM_MODE_NONE);
pickedup |= Item_GiveAmmoTo(item, player, ammo_plasma, g_pickup_plasma_max, ITEM_MODE_NONE);
+ pickedup |= Item_GiveAmmoTo(item, player, ammo_supercells, g_pickup_cells_max, ITEM_MODE_NONE);
pickedup |= Item_GiveAmmoTo(item, player, health, item.max_health, ITEM_MODE_HEALTH);
pickedup |= Item_GiveAmmoTo(item, player, armorvalue, item.max_armorvalue, ITEM_MODE_ARMOR);
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;
}
if (self.classname == "droppedweapon")
- {
- self.strength_finished = max(0, self.strength_finished - time);
- self.invincible_finished = max(0, self.invincible_finished - time);
self.superweapons_finished = max(0, self.superweapons_finished - time);
- }
if(!Item_GiveTo(self, other))
{
if (self.classname == "droppedweapon")
{
// undo what we did above
- self.strength_finished += time;
- self.invincible_finished += time;
self.superweapons_finished += time;
}
return;
:pickup
+ jeff_Announcer_ItemTouch(other, self);
+
other.last_pickup = time;
- pointparticles(particleeffectnum("item_pickup"), self.origin, '0 0 0', 1);
+ Send_Effect(EFFECT_ITEM_PICKUP, CENTER_OR_VIEWOFS(self), '0 0 0', 1);
sound (other, CH_TRIGGER, self.item_pickupsound, VOL_BASE, ATTEN_NORM);
if (self.classname == "droppedweapon")
}
}
-// Savage: used for item garbage-collection
-// TODO: perhaps nice special effect?
-void RemoveItem(void)
+void RemoveItem(entity e)
{
- remove(self);
+ if(wasfreed(e) || !e) { return; }
+ Send_Effect(EFFECT_ITEM_PICKUP, CENTER_OR_VIEWOFS(e), '0 0 0', 1);
+ remove(e);
}
+void RemoveItem_think() { RemoveItem(self); }
+
// pickup evaluation functions
// these functions decide how desirable an item is to the bots
else if(player.ammo_cells || player.ammo_shells || player.ammo_plasma || player.ammo_nails || player.ammo_rockets)
{
// Skilled bots will grab more
- c = bound(0, skill / 10, 1) * 0.5;
+ c = bound(0, bot_skill / 10, 1) * 0.5;
}
else
c = 0;
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;
+ 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;
else if(wi.items & IT_PLASMA)
need_plasma = TRUE;
else if(wi.items & IT_FUEL)
- need_fuel = TRUE;
+ need_cells = TRUE;
+ else if(wi.items & IT_SUPERCELLS)
+ need_supercells = TRUE;
}
// TODO: figure out if the player even has the weapon this ammo is for?
if (item.ammo_plasma)
if (player.ammo_plasma < g_pickup_plasma_max)
c = c + max(0, 1 - player.ammo_plasma / g_pickup_plasma_max);
+ if (need_supercells)
+ if (item.ammo_supercells)
+ if (player.ammo_supercells < g_pickup_cells_max)
+ c = c + max(0, 1 - player.ammo_supercells / g_pickup_cells_max);
if (need_fuel)
if (item.ammo_fuel)
if (player.ammo_fuel < g_pickup_fuel_max)
void Item_Damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
{
if(ITEM_DAMAGE_NEEDKILL(deathtype))
- RemoveItem();
+ RemoveItem(self);
}
void StartItem (string itemmodel, string pickupsound, float defaultrespawntime, float defaultrespawntimejitter, string itemname, float itemid, float weaponid, float itemflags, float(entity player, entity item) pickupevalfunc, float pickupbasevalue)
self.movetype = MOVETYPE_TOSS;
// Savage: remove thrown items after a certain period of time ("garbage collection")
- self.think = RemoveItem;
+ self.think = RemoveItem_think;
self.nextthink = time + 20;
self.takedamage = DAMAGE_YES;
self.event_damage = Item_Damage;
- if(self.strength_finished || self.invincible_finished || self.superweapons_finished)
- /*
- if(self.items == 0)
- if(!(self.weapons & ~WEPSET_SUPERWEAPONS)) // only superweapons
- if(self.ammo_nails == 0)
- if(self.ammo_cells == 0)
- if(self.ammo_rockets == 0)
- if(self.ammo_shells == 0)
- if(self.ammo_fuel == 0)
- if(self.health == 0)
- if(self.armorvalue == 0)
- */
+ if(self.superweapons_finished)
{
// if item is worthless after a timer, have it expire then
- self.nextthink = max(self.strength_finished, self.invincible_finished, self.superweapons_finished);
+ self.nextthink = self.superweapons_finished;
}
// don't drop if in a NODROP zone (such as lava)
return;
}
+ if(self.angles != '0 0 0')
+ self.SendFlags |= ISF_ANGLES;
+
self.reset = Item_Reset;
// it's a level item
if(self.spawnflags & 1)
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
droptofloor();
waypoint_spawnforitem(self);
precache_sound (self.item_pickupsound);
precache_sound ("misc/itemrespawncountdown.wav");
- if(itemid == IT_STRENGTH)
- precache_sound ("misc/strength_respawn.wav");
- else if(itemid == IT_INVINCIBLE)
- precache_sound ("misc/shield_respawn.wav");
- else
- precache_sound ("misc/itemrespawn.wav");
+ precache_sound ("misc/itemrespawn.wav");
if((itemflags & (FL_POWERUP | FL_WEAPON)) || (itemid & (IT_HEALTH | IT_ARMOR | IT_KEY1 | IT_KEY2)))
self.target = "###item###"; // for finding the nearest item using find()
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
}
setsize (self, self.pos1, self.pos2);
+ self.SendFlags |= ISF_SIZE;
+
if(itemflags & FL_POWERUP)
self.ItemStatus |= ITS_ANIMATE1;
else
Item_Reset();
- Net_LinkEntity(self, FALSE, 0, ItemSend);
-
- self.SendFlags |= ISF_SIZE;
- if(self.angles)
- self.SendFlags |= ISF_ANGLES;
+ Net_LinkEntity(self, !((itemflags & FL_POWERUP) || self.health || self.armorvalue), 0, ItemSend);
// call this hook after everything else has been done
if(MUTATOR_CALLHOOK(Item_Spawn))
return;
}
}
+
+string Item_Model(string item_mdl)
+{
+ if(autocvar_sv_items_modeloverride != "" && autocvar_sv_items_modeloverride != "default")
+ {
+ string themdl = sprintf("models/items_%s/%s", autocvar_sv_weapons_modeloverride, item_mdl);
+ if(fexists(themdl))
+ return themdl;
+ }
+ return strcat("models/items/", item_mdl);
+}
+
void spawnfunc_item_rockets (void) {
if(!self.ammo_rockets)
self.ammo_rockets = g_pickup_rockets;
if(!self.pickup_anyway)
self.pickup_anyway = g_pickup_ammo_anyway;
- StartItem ("models/items/a_rockets.md3", "misc/itempickup.wav", g_pickup_respawntime_ammo, g_pickup_respawntimejitter_ammo, "rockets", IT_ROCKETS, 0, 0, commodity_pickupevalfunc, 3000);
+ StartItem (Item_Model("a_rockets.md3"), "misc/itempickup.wav", g_pickup_respawntime_ammo, g_pickup_respawntimejitter_ammo, "rockets", IT_ROCKETS, 0, 0, commodity_pickupevalfunc, 3000);
}
void spawnfunc_item_shells (void);
self.ammo_nails = g_pickup_nails;
if(!self.pickup_anyway)
self.pickup_anyway = g_pickup_ammo_anyway;
- StartItem ("models/items/a_bullets.mdl", "misc/itempickup.wav", g_pickup_respawntime_ammo, g_pickup_respawntimejitter_ammo, "bullets", IT_NAILS, 0, 0, commodity_pickupevalfunc, 2000);
+ StartItem (Item_Model("a_bullets.mdl"), "misc/itempickup.wav", g_pickup_respawntime_ammo, g_pickup_respawntimejitter_ammo, "bullets", IT_NAILS, 0, 0, commodity_pickupevalfunc, 2000);
}
void spawnfunc_item_cells (void) {
self.ammo_cells = g_pickup_cells;
if(!self.pickup_anyway)
self.pickup_anyway = g_pickup_ammo_anyway;
- StartItem ("models/items/a_cells.md3", "misc/itempickup.wav", g_pickup_respawntime_ammo, g_pickup_respawntimejitter_ammo, "cells", IT_CELLS, 0, 0, commodity_pickupevalfunc, 2000);
+ StartItem (Item_Model("a_cells.md3"), "misc/itempickup.wav", g_pickup_respawntime_ammo, g_pickup_respawntimejitter_ammo, "cells", IT_CELLS, 0, 0, commodity_pickupevalfunc, 2000);
}
void spawnfunc_item_plasma()
self.ammo_plasma = g_pickup_plasma;
if(!self.pickup_anyway)
self.pickup_anyway = g_pickup_ammo_anyway;
- StartItem ("models/items/a_cells.md3", "misc/itempickup.wav", g_pickup_respawntime_ammo, g_pickup_respawntimejitter_ammo, "plasma", IT_PLASMA, 0, 0, commodity_pickupevalfunc, 2000);
+ StartItem (Item_Model("a_cells.md3"), "misc/itempickup.wav", g_pickup_respawntime_ammo, g_pickup_respawntimejitter_ammo, "plasma", IT_PLASMA, 0, 0, commodity_pickupevalfunc, 2000);
}
void spawnfunc_item_shells (void) {
self.ammo_shells = g_pickup_shells;
if(!self.pickup_anyway)
self.pickup_anyway = g_pickup_ammo_anyway;
- StartItem ("models/items/a_shells.md3", "misc/itempickup.wav", g_pickup_respawntime_ammo, g_pickup_respawntimejitter_ammo, "shells", IT_SHELLS, 0, 0, commodity_pickupevalfunc, 500);
+ StartItem (Item_Model("a_shells.md3"), "misc/itempickup.wav", g_pickup_respawntime_ammo, g_pickup_respawntimejitter_ammo, "shells", IT_SHELLS, 0, 0, commodity_pickupevalfunc, 500);
}
void spawnfunc_item_armor_small (void) {
self.max_armorvalue = g_pickup_armorsmall_max;
if(!self.pickup_anyway)
self.pickup_anyway = g_pickup_armorsmall_anyway;
- StartItem ("models/items/item_armor_small.md3", "misc/armor1.wav", g_pickup_respawntime_short, g_pickup_respawntimejitter_short, "5 Armor", IT_ARMOR_SHARD, 0, 0, commodity_pickupevalfunc, BOT_PICKUP_RATING_LOW);
+ StartItem (Item_Model("item_armor_small.md3"), "misc/armor1.wav", g_pickup_respawntime_short, g_pickup_respawntimejitter_short, "5 Armor", IT_ARMOR_SHARD, 0, 0, commodity_pickupevalfunc, BOT_PICKUP_RATING_LOW);
}
void spawnfunc_item_armor_medium (void) {
self.max_armorvalue = g_pickup_armormedium_max;
if(!self.pickup_anyway)
self.pickup_anyway = g_pickup_armormedium_anyway;
- StartItem ("models/items/item_armor_medium.md3", "misc/armor10.wav", g_pickup_respawntime_medium, g_pickup_respawntimejitter_medium, "25 Armor", IT_ARMOR, 0, 0, commodity_pickupevalfunc, BOT_PICKUP_RATING_MID);
+ StartItem (Item_Model("item_armor_medium.md3"), "misc/armor10.wav", g_pickup_respawntime_medium, g_pickup_respawntimejitter_medium, "25 Armor", IT_ARMOR, 0, 0, commodity_pickupevalfunc, BOT_PICKUP_RATING_MID);
}
void spawnfunc_item_armor_big (void) {
+ self.classname = "item_armor_big";
if(!self.armorvalue)
self.armorvalue = g_pickup_armorbig;
if(!self.max_armorvalue)
self.max_armorvalue = g_pickup_armorbig_max;
if(!self.pickup_anyway)
self.pickup_anyway = g_pickup_armorbig_anyway;
- StartItem ("models/items/item_armor_big.md3", "misc/armor17_5.wav", g_pickup_respawntime_long, g_pickup_respawntimejitter_long, "50 Armor", IT_ARMOR, 0, 0, commodity_pickupevalfunc, 20000);
+ StartItem (Item_Model("item_armor_big.md3"), "misc/armor17_5.wav", g_pickup_respawntime_long, g_pickup_respawntimejitter_long, "50 Armor", IT_ARMOR, 0, 0, commodity_pickupevalfunc, 20000);
}
void spawnfunc_item_armor_large (void) {
+ self.classname = "item_armor_large";
if(!self.armorvalue)
self.armorvalue = g_pickup_armorlarge;
if(!self.max_armorvalue)
self.max_armorvalue = g_pickup_armorlarge_max;
if(!self.pickup_anyway)
self.pickup_anyway = g_pickup_armorlarge_anyway;
- StartItem ("models/items/item_armor_large.md3", "misc/armor25.wav", g_pickup_respawntime_long, g_pickup_respawntimejitter_long, "100 Armor", IT_ARMOR, 0, 0, commodity_pickupevalfunc, BOT_PICKUP_RATING_HIGH);
+ StartItem (Item_Model("item_armor_large.md3"), "misc/armor25.wav", g_pickup_respawntime_long, g_pickup_respawntimejitter_long, "100 Armor", IT_ARMOR, 0, 0, commodity_pickupevalfunc, BOT_PICKUP_RATING_HIGH);
}
void spawnfunc_item_health_small (void) {
+ self.classname = "item_health_small";
if(!self.max_health)
self.max_health = g_pickup_healthsmall_max;
if(!self.health)
self.health = g_pickup_healthsmall;
if(!self.pickup_anyway)
self.pickup_anyway = g_pickup_healthsmall_anyway;
- StartItem ("models/items/g_h1.md3", "misc/minihealth.wav", g_pickup_respawntime_short, g_pickup_respawntimejitter_short, "5 Health", IT_5HP, 0, 0, commodity_pickupevalfunc, BOT_PICKUP_RATING_LOW);
+ StartItem (Item_Model("g_h1.md3"), "misc/minihealth.wav", g_pickup_respawntime_short, g_pickup_respawntimejitter_short, "5 Health", IT_5HP, 0, 0, commodity_pickupevalfunc, BOT_PICKUP_RATING_LOW);
}
void spawnfunc_item_health_medium (void) {
+ self.classname = "item_health_medium";
if(!self.max_health)
self.max_health = g_pickup_healthmedium_max;
if(!self.health)
self.health = g_pickup_healthmedium;
if(!self.pickup_anyway)
self.pickup_anyway = g_pickup_healthmedium_anyway;
- StartItem ("models/items/g_h25.md3", "misc/mediumhealth.wav", g_pickup_respawntime_short, g_pickup_respawntimejitter_short, "25 Health", IT_25HP, 0, 0, commodity_pickupevalfunc, BOT_PICKUP_RATING_MID);
+ StartItem (Item_Model("g_h25.md3"), "misc/mediumhealth.wav", g_pickup_respawntime_short, g_pickup_respawntimejitter_short, "25 Health", IT_25HP, 0, 0, commodity_pickupevalfunc, BOT_PICKUP_RATING_MID);
}
void spawnfunc_item_health_large (void) {
+ self.classname = "item_health_large";
if(!self.max_health)
self.max_health = g_pickup_healthlarge_max;
if(!self.health)
self.health = g_pickup_healthlarge;
if(!self.pickup_anyway)
self.pickup_anyway = g_pickup_healthlarge_anyway;
- StartItem ("models/items/g_h50.md3", "misc/mediumhealth.wav", g_pickup_respawntime_medium, g_pickup_respawntimejitter_medium, "50 Health", IT_25HP, 0, 0, commodity_pickupevalfunc, BOT_PICKUP_RATING_MID);
+ StartItem (Item_Model("g_h50.md3"), "misc/mediumhealth.wav", g_pickup_respawntime_medium, g_pickup_respawntimejitter_medium, "50 Health", IT_25HP, 0, 0, commodity_pickupevalfunc, BOT_PICKUP_RATING_MID);
}
void spawnfunc_item_health_mega (void) {
+ self.classname = "item_health_mega";
if(!self.max_health)
self.max_health = g_pickup_healthmega_max;
if(!self.health)
self.health = g_pickup_healthmega;
if(!self.pickup_anyway)
self.pickup_anyway = g_pickup_healthmega_anyway;
- StartItem ("models/items/g_h100.md3", "misc/megahealth.wav", g_pickup_respawntime_long, g_pickup_respawntimejitter_long, "100 Health", IT_HEALTH, 0, 0, commodity_pickupevalfunc, BOT_PICKUP_RATING_HIGH);
+ StartItem (Item_Model("g_h100.md3"), "misc/megahealth.wav", g_pickup_respawntime_long, g_pickup_respawntimejitter_long, "100 Health", IT_HEALTH, 0, 0, commodity_pickupevalfunc, BOT_PICKUP_RATING_HIGH);
}
// support old misnamed entities
void spawnfunc_item_health25() { spawnfunc_item_health_medium(); }
void spawnfunc_item_health100() { spawnfunc_item_health_mega(); }
-void spawnfunc_item_strength (void) {
- precache_sound("weapons/strength_fire.wav");
- if(!self.strength_finished)
- self.strength_finished = autocvar_g_balance_powerup_strength_time;
- StartItem ("models/items/g_strength.md3", "misc/powerup.wav", g_pickup_respawntime_powerup, g_pickup_respawntimejitter_powerup, "Strength Powerup", IT_STRENGTH, 0, FL_POWERUP, generic_pickupevalfunc, 100000);
-}
-
-void spawnfunc_item_invincible (void) {
- if(!self.invincible_finished)
- self.invincible_finished = autocvar_g_balance_powerup_invincible_time;
- StartItem ("models/items/g_invincible.md3", "misc/powerup_shield.wav", g_pickup_respawntime_powerup, g_pickup_respawntimejitter_powerup, "Shield", IT_INVINCIBLE, 0, FL_POWERUP, generic_pickupevalfunc, 100000);
-}
+// mutator hookable items
+void spawnfunc_item_strength() { /* dummy item */ }
+void spawnfunc_item_invincible() { /* dummy item */ }
// compatibility:
void spawnfunc_item_quad (void) {self.classname = "item_strength";spawnfunc_item_strength();}
string s;
self.use = target_items_use;
- if(!self.strength_finished)
- self.strength_finished = autocvar_g_balance_powerup_strength_time;
- if(!self.invincible_finished)
- self.invincible_finished = autocvar_g_balance_powerup_invincible_time;
if(!self.superweapons_finished)
self.superweapons_finished = autocvar_g_balance_superweapons_time;
precache_sound("misc/armor25.wav");
precache_sound("misc/powerup.wav");
precache_sound("misc/poweroff.wav");
- precache_sound("weapons/weaponpickup.wav");
+ precache_sound(W_Sound("weaponpickup"));
n = tokenize_console(self.netname);
if(argv(0) == "give")
if (argv(i) == "unlimited_ammo") self.items |= IT_UNLIMITED_AMMO;
else if(argv(i) == "unlimited_weapon_ammo") self.items |= IT_UNLIMITED_WEAPON_AMMO;
else if(argv(i) == "unlimited_superweapons") self.items |= IT_UNLIMITED_SUPERWEAPONS;
- else if(argv(i) == "strength") self.items |= IT_STRENGTH;
- else if(argv(i) == "invincible") self.items |= IT_INVINCIBLE;
else if(argv(i) == "superweapons") self.items |= IT_SUPERWEAPON;
else if(argv(i) == "jetpack") self.items |= IT_JETPACK;
else if(argv(i) == "fuel_regen") self.items |= IT_FUEL_REGEN;
self.netname = "";
self.netname = sprintf("%s %s%d %s", self.netname, itemprefix, !!(self.items & IT_UNLIMITED_WEAPON_AMMO), "unlimited_weapon_ammo");
self.netname = sprintf("%s %s%d %s", self.netname, itemprefix, !!(self.items & IT_UNLIMITED_SUPERWEAPONS), "unlimited_superweapons");
- self.netname = sprintf("%s %s%d %s", self.netname, valueprefix, self.strength_finished * !!(self.items & IT_STRENGTH), "strength");
- self.netname = sprintf("%s %s%d %s", self.netname, valueprefix, self.invincible_finished * !!(self.items & IT_INVINCIBLE), "invincible");
self.netname = sprintf("%s %s%d %s", self.netname, valueprefix, self.superweapons_finished * !!(self.items & IT_SUPERWEAPON), "superweapons");
self.netname = sprintf("%s %s%d %s", self.netname, itemprefix, !!(self.items & IT_JETPACK), "jetpack");
self.netname = sprintf("%s %s%d %s", self.netname, itemprefix, !!(self.items & IT_FUEL_REGEN), "fuel_regen");
if(self.ammo_rockets != 0) self.netname = sprintf("%s %s%d %s", self.netname, valueprefix, max(0, self.ammo_rockets), "rockets");
if(self.ammo_cells != 0) self.netname = sprintf("%s %s%d %s", self.netname, valueprefix, max(0, self.ammo_cells), "cells");
if(self.ammo_plasma != 0) self.netname = sprintf("%s %s%d %s", self.netname, valueprefix, max(0, self.ammo_plasma), "plasma");
+ if(self.ammo_supercells != 0) self.netname = sprintf("%s %s%d %s", self.netname, valueprefix, max(0, self.ammo_supercells), "supercells");
if(self.ammo_fuel != 0) self.netname = sprintf("%s %s%d %s", self.netname, valueprefix, max(0, self.ammo_fuel), "fuel");
if(self.health != 0) self.netname = sprintf("%s %s%d %s", self.netname, valueprefix, max(0, self.health), "health");
if(self.armorvalue != 0) self.netname = sprintf("%s %s%d %s", self.netname, valueprefix, max(0, self.health), "armor");
self.ammo_fuel = g_pickup_fuel;
if(!self.pickup_anyway)
self.pickup_anyway = g_pickup_ammo_anyway;
- StartItem ("models/items/g_fuel.md3", "misc/itempickup.wav", g_pickup_respawntime_ammo, g_pickup_respawntimejitter_ammo, "Fuel", IT_FUEL, 0, 0, commodity_pickupevalfunc, BOT_PICKUP_RATING_LOW);
+ StartItem (Item_Model("g_fuel.md3"), "misc/itempickup.wav", g_pickup_respawntime_ammo, g_pickup_respawntimejitter_ammo, "Fuel", IT_FUEL, 0, 0, commodity_pickupevalfunc, BOT_PICKUP_RATING_LOW);
}
void spawnfunc_item_fuel_regen(void)
spawnfunc_item_fuel();
return;
}
- StartItem ("models/items/g_fuelregen.md3", "misc/itempickup.wav", g_pickup_respawntime_powerup, g_pickup_respawntimejitter_powerup, "Fuel regenerator", IT_FUEL_REGEN, 0, FL_POWERUP, commodity_pickupevalfunc, BOT_PICKUP_RATING_LOW);
+ StartItem (Item_Model("g_fuelregen.md3"), "misc/itempickup.wav", g_pickup_respawntime_powerup, g_pickup_respawntimejitter_powerup, "Fuel regenerator", IT_FUEL_REGEN, 0, FL_POWERUP, commodity_pickupevalfunc, BOT_PICKUP_RATING_LOW);
}
void spawnfunc_item_jetpack(void)
spawnfunc_item_fuel();
return;
}
- StartItem ("models/items/g_jetpack.md3", "misc/itempickup.wav", g_pickup_respawntime_powerup, g_pickup_respawntimejitter_powerup, "Jet pack", IT_JETPACK, 0, FL_POWERUP, commodity_pickupevalfunc, BOT_PICKUP_RATING_LOW);
+ StartItem (Item_Model("g_jetpack.md3"), "misc/itempickup.wav", g_pickup_respawntime_powerup, g_pickup_respawntimejitter_powerup, "Jet pack", IT_JETPACK, 0, FL_POWERUP, commodity_pickupevalfunc, BOT_PICKUP_RATING_LOW);
}
float GiveWeapon(entity e, float wpn, float op, float val)
if (e.switchweapon == w_getbestweapon(e))
_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);
PREGIVE_WEAPONS(e);
- PREGIVE(e, strength_finished);
- PREGIVE(e, invincible_finished);
PREGIVE(e, superweapons_finished);
PREGIVE(e, ammo_nails);
PREGIVE(e, ammo_cells);
PREGIVE(e, ammo_plasma);
+ PREGIVE(e, ammo_supercells);
PREGIVE(e, ammo_shells);
PREGIVE(e, ammo_rockets);
PREGIVE(e, ammo_fuel);
continue;
case "ALL":
got += GiveBit(e, items, IT_FUEL_REGEN, op, val);
- got += GiveValue(e, strength_finished, op, val);
- got += GiveValue(e, invincible_finished, op, val);
got += GiveValue(e, superweapons_finished, op, val);
got += GiveBit(e, items, IT_UNLIMITED_AMMO, op, val);
case "all":
case "allammo":
got += GiveValue(e, ammo_cells, op, val);
got += GiveValue(e, ammo_plasma, op, val);
+ got += GiveValue(e, ammo_supercells, op, val);
got += GiveValue(e, ammo_shells, op, val);
got += GiveValue(e, ammo_nails, op, val);
got += GiveValue(e, ammo_rockets, op, val);
case "fuel_regen":
got += GiveBit(e, items, IT_FUEL_REGEN, op, val);
break;
- case "strength":
- got += GiveValue(e, strength_finished, op, val);
- break;
- case "invincible":
- got += GiveValue(e, invincible_finished, op, val);
- break;
case "superweapons":
got += GiveValue(e, superweapons_finished, op, val);
break;
case "plasma":
got += GiveValue(e, ammo_plasma, op, val);
break;
+ case "supercells":
+ got += GiveValue(e, ammo_supercells, op, val);
+ break;
case "shells":
got += GiveValue(e, ammo_shells, op, val);
break;
wi = get_weaponinfo(j);
if(wi.weapon)
{
- POSTGIVE_WEAPON(e, j, "weapons/weaponpickup.wav", string_null);
+ POSTGIVE_WEAPON(e, j, W_Sound("weaponpickup"), string_null);
if (!(save_weapons & WepSet_FromWeapon(j)))
if(e.weapons & WepSet_FromWeapon(j))
WEP_ACTION(wi.weapon, WR_INIT);
}
}
- POSTGIVE_VALUE(e, strength_finished, 1, "misc/powerup.wav", "misc/poweroff.wav");
- POSTGIVE_VALUE(e, invincible_finished, 1, "misc/powerup_shield.wav", "misc/poweroff.wav");
POSTGIVE_VALUE(e, ammo_nails, 0, "misc/itempickup.wav", string_null);
POSTGIVE_VALUE(e, ammo_cells, 0, "misc/itempickup.wav", string_null);
POSTGIVE_VALUE(e, ammo_plasma, 0, "misc/itempickup.wav", string_null);
+ POSTGIVE_VALUE(e, ammo_supercells, 0, "misc/itempickup.wav", string_null);
POSTGIVE_VALUE(e, ammo_shells, 0, "misc/itempickup.wav", string_null);
POSTGIVE_VALUE(e, ammo_rockets, 0, "misc/itempickup.wav", string_null);
POSTGIVE_VALUE_ROT(e, ammo_fuel, 1, pauserotfuel_finished, autocvar_g_balance_pause_fuel_rot, pauseregen_finished, autocvar_g_balance_pause_fuel_regen, "misc/itempickup.wav", string_null);
if(self.weapons & WEPSET_SUPERWEAPONS)
e.superweapons_finished = autocvar_g_balance_superweapons_time;
- if(e.strength_finished <= 0)
- e.strength_finished = 0;
- else
- e.strength_finished += time;
- if(e.invincible_finished <= 0)
- e.invincible_finished = 0;
- else
- e.invincible_finished += time;
if(e.superweapons_finished <= 0)
e.superweapons_finished = 0;
else
const float IT_STRENGTH = 8192;
const float IT_INVINCIBLE = 16384;
const float IT_HEALTH = 32768;
-const float IT_PLASMA = 65536;
+const float IT_SUPERCELLS = 8388608;
+const float IT_PLASMA = 16777216;
// shared value space (union):
// for items:
WANT_CONST float IT_KEY1 = 131072;
WANT_CONST float IT_KEY2 = 262144;
- // for players:
- const float IT_RED_FLAG_TAKEN = 32768;
- const float IT_RED_FLAG_LOST = 65536;
- const float IT_RED_FLAG_CARRYING = 98304;
- const float IT_BLUE_FLAG_TAKEN = 131072;
- const float IT_BLUE_FLAG_LOST = 262144;
- const float IT_BLUE_FLAG_CARRYING = 393216;
// end
const float IT_5HP = 524288;
if(self.pushltime < time) // prevent "snorring" sound when a player hits the jumppad more than once
{
// flash when activated
- pointparticles(particleeffectnum("jumppad_activate"), other.origin, other.velocity, 1);
+ Send_Effect(EFFECT_JUMPPAD, other.origin, other.velocity, 1);
sound (other, CH_TRIGGER, self.noise, VOL_BASE, ATTEN_NORM);
self.pushltime = time + 0.2;
}
if(maxclients == 1)
gametypename = "single";
// we do not have the other types (oneflag, obelisk, harvester, teamtournament)
+ // actually, we have oneflag, but it can't be detected this early (map based...)
if(strstrofs(self.gametype, gametypename, 0) < 0)
return 1;
}
sound (player, CH_TRIGGER, "misc/teleport.wav", VOL_BASE, ATTEN_NORM);
if(tflags & TELEPORT_FLAG_PARTICLES)
{
- pointparticles(particleeffectnum("teleport"), player.origin, '0 0 0', 1);
- pointparticles(particleeffectnum("teleport"), to + v_forward * 32, '0 0 0', 1);
+ Send_Effect(EFFECT_TELEPORT, player.origin, '0 0 0', 1);
+ Send_Effect(EFFECT_TELEPORT, to + v_forward * 32, '0 0 0', 1);
}
self.pushltime = time + 0.2;
}
if(!other.vehicle.teleportable)
return;
- if(other.turrcaps_flags & TFL_TURRCAPS_ISTURRET)
+ if(IS_TURRET(other))
return;
if(other.deadflag != DEAD_NO)
string cache_mutatormsg;
string cache_lastmutatormsg;
-// client counts for each team
-float c1, c2, c3, c4;
-// # of bots on those teams
-float cb1, cb2, cb3, cb4;
-
-//float audit_teams_time;
-
void TeamchangeFrags(entity e)
{
PlayerScore_Clear(e);
if(g_dm)
{
+ // no custom limits needed here
+ MUTATOR_ADD(gamemode_deathmatch);
}
if(g_tdm)
ActivateTeamplay();
fraglimit_override = autocvar_g_keyhunt_point_limit;
leadlimit_override = autocvar_g_keyhunt_point_leadlimit;
+ if(autocvar_g_keyhunt_team_spawns)
+ have_team_spawns = -1; // request team spawns
MUTATOR_ADD(gamemode_keyhunt);
}
if(g_onslaught)
{
ActivateTeamplay();
+ fraglimit_override = autocvar_g_onslaught_point_limit;
have_team_spawns = -1; // request team spawns
MUTATOR_ADD(gamemode_onslaught);
}
{
ActivateTeamplay();
race_teams = bound(2, autocvar_g_race_teams, 4);
- have_team_spawns = -1; // request team spawns
+ if(autocvar_g_race_team_spawns)
+ have_team_spawns = -1; // request team spawns
}
else
race_teams = 0;
+
qualifying_override = autocvar_g_race_qualifying_timelimit_override;
fraglimit_override = autocvar_g_race_laps_limit;
leadlimit_override = 0; // currently not supported by race
- // we need to find out the correct value for g_race_qualifying
- float want_qualifying = ((qualifying_override >= 0) ? qualifying_override : autocvar_g_race_qualifying_timelimit) > 0;
-
- if(autocvar_g_campaign)
- {
- g_race_qualifying = 1;
- independent_players = 1;
- }
- else if(!autocvar_g_campaign && want_qualifying)
- {
- g_race_qualifying = 2;
- independent_players = 1;
- race_fraglimit = (race_fraglimit >= 0) ? fraglimit_override : autocvar_fraglimit;
- race_leadlimit = (race_leadlimit >= 0) ? leadlimit_override : autocvar_leadlimit;
- race_timelimit = (race_timelimit >= 0) ? timelimit_override : autocvar_timelimit;
- fraglimit_override = 0;
- leadlimit_override = 0;
- timelimit_override = autocvar_g_race_qualifying_timelimit;
- }
- else
- {
- g_race_qualifying = 0;
- }
-
MUTATOR_ADD(gamemode_race);
}
g_race_qualifying = 1;
fraglimit_override = 0;
leadlimit_override = 0;
- independent_players = 1;
MUTATOR_ADD(gamemode_cts);
}
if(g_nexball)
{
- fraglimit_override = autocvar_g_nexball_goallimit;
- leadlimit_override = autocvar_g_nexball_goalleadlimit;
- ActivateTeamplay();
- have_team_spawns = -1; // request team spawns
- MUTATOR_ADD(gamemode_nexball);
+ fraglimit_override = autocvar_g_nexball_goallimit;
+ leadlimit_override = autocvar_g_nexball_goalleadlimit;
+ ActivateTeamplay();
+ have_team_spawns = -1; // request team spawns
+ MUTATOR_ADD(gamemode_nexball);
}
if(g_keepaway)
{
+ fraglimit_override = autocvar_g_keepaway_point_limit;
MUTATOR_ADD(gamemode_keepaway);
}
+ if(g_conquest)
+ {
+ fraglimit_override = autocvar_g_conquest_point_limit;
+ ActivateTeamplay();
+ MUTATOR_ADD(gamemode_conquest);
+ }
+
+ if(g_vip)
+ {
+ fraglimit_override = autocvar_g_vip_point_limit;
+ leadlimit_override = autocvar_g_vip_point_leadlimit;
+ ActivateTeamplay();
+ have_team_spawns = -1; // request team spawns
+ MUTATOR_ADD(gamemode_vip);
+ }
+
if(g_invasion)
{
fraglimit_override = autocvar_g_invasion_point_limit;
MUTATOR_ADD(gamemode_invasion);
}
+ if(g_infection)
+ {
+ fraglimit_override = autocvar_g_infection_point_limit;
+ leadlimit_override = autocvar_g_infection_point_leadlimit;
+ MUTATOR_ADD(gamemode_infection);
+ }
+
+ if(g_jailbreak)
+ {
+ fraglimit_override = autocvar_g_jailbreak_point_limit;
+ leadlimit_override = autocvar_g_jailbreak_point_leadlimit;
+ ActivateTeamplay();
+ have_team_spawns = -1; // request team spawns
+ MUTATOR_ADD(gamemode_jailbreak);
+ }
+
if(teamplay)
entcs_init();
cvar_set("g_race_qualifying_timelimit", ftos(qualifying_override));
}
+ if(g_race)
+ {
+ // we need to find out the correct value for g_race_qualifying
+ if(autocvar_g_campaign)
+ {
+ g_race_qualifying = 1;
+ }
+ else if(!autocvar_g_campaign && autocvar_g_race_qualifying_timelimit > 0)
+ {
+ g_race_qualifying = 2;
+ race_fraglimit = autocvar_fraglimit;
+ race_leadlimit = autocvar_leadlimit;
+ race_timelimit = autocvar_timelimit;
+ cvar_set("fraglimit", "0");
+ cvar_set("leadlimit", "0");
+ cvar_set("timelimit", ftos(autocvar_g_race_qualifying_timelimit));
+ }
+ else
+ g_race_qualifying = 0;
+ }
+
+ if(g_race || g_cts)
+ if(g_race_qualifying)
+ independent_players = 1;
+
InitializeEntity(world, default_delayedinit, INITPRIO_GAMETYPE_FALLBACK);
}
else
modifications = strcat(modifications, ", ", g_weaponarena_list, " Arena");
}
- if(cvar("g_balance_blaster_weaponstart") == 0)
+ else if(cvar("g_balance_blaster_weaponstart") == 0)
modifications = strcat(modifications, ", No start weapons");
if(cvar("sv_gravity") < stof(cvar_defstring("sv_gravity")))
modifications = strcat(modifications, ", Low gravity");
modifications = strcat(modifications, ", Weapons stay");
if(g_jetpack)
modifications = strcat(modifications, ", Jet pack");
- if(autocvar_g_powerups == 0)
- modifications = strcat(modifications, ", No powerups");
- if(autocvar_g_powerups > 0)
- modifications = strcat(modifications, ", Powerups");
modifications = substring(modifications, 2, strlen(modifications) - 2);
string versionmessage;
if (motd != "") {
s = strcat(s, "\n\n^8MOTD: ^7", strreplace("\\n", "\n", motd));
}
+
+ // branding for the win
+ s = strcat(s, "\n\n Running the SMB mod pack\nhttps://github.com/MarioSMB/modpack");
+
return s;
}
// set c1...c4 to show what teams are allowed
void CheckAllowedTeams (entity for_whom)
{
- float dm;
+ float dm = 2;
entity head;
- string teament_name;
+ string teament_name = string_null;
c1 = c2 = c3 = c4 = -1;
cb1 = cb2 = cb3 = cb4 = 0;
- teament_name = string_null;
- if(g_onslaught)
- {
- // onslaught is special
- head = findchain(classname, "onslaught_generator");
- while (head)
- {
- if (head.team == NUM_TEAM_1) c1 = 0;
- if (head.team == NUM_TEAM_2) c2 = 0;
- if (head.team == NUM_TEAM_3) c3 = 0;
- if (head.team == NUM_TEAM_4) c4 = 0;
- head = head.chain;
- }
- }
- else if(g_domination)
- teament_name = "dom_team";
- else if(g_ctf)
- teament_name = "ctf_team";
- else if(g_tdm)
- teament_name = "tdm_team";
- else if(g_nexball)
- teament_name = "nexball_team";
- else if(g_assault)
- c1 = c2 = 0; // Assault always has 2 teams
- else
- {
- // cover anything else by treating it like tdm with no teams spawned
- dm = 2;
-
- ret_float = dm;
- MUTATOR_CALLHOOK(GetTeamCount);
- dm = ret_float;
+ ret_string = teament_name;
+ ret_float = dm;
+ float mutator_value = MUTATOR_CALLHOOK(GetTeamCount);
+ teament_name = ret_string;
+ dm = ret_float;
+ if(!mutator_value)
+ {
if(dm >= 4)
c1 = c2 = c3 = c4 = 0;
else if(dm >= 3)
{
if(!(g_domination && head.netname == ""))
{
- if(head.team == NUM_TEAM_1)
- c1 = 0;
- else if(head.team == NUM_TEAM_2)
- c2 = 0;
- else if(head.team == NUM_TEAM_3)
- c3 = 0;
- else if(head.team == NUM_TEAM_4)
- c4 = 0;
+ switch(head.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;
+ }
}
head = find(head, classname, teament_name);
}
if(head != ignore)// && head.netname != "")
{
value = PlayerValue(head);
- if(IS_BOT_CLIENT(head))
- bvalue = value;
- else
- bvalue = 0;
- if(t == NUM_TEAM_1)
- {
- if(c1 >= 0)
- {
- c1 = c1 + value;
- cb1 = cb1 + bvalue;
- }
- }
- if(t == NUM_TEAM_2)
- {
- if(c2 >= 0)
- {
- c2 = c2 + value;
- cb2 = cb2 + bvalue;
- }
- }
- if(t == NUM_TEAM_3)
- {
- if(c3 >= 0)
- {
- c3 = c3 + value;
- cb3 = cb3 + bvalue;
- }
- }
- if(t == NUM_TEAM_4)
+ bvalue = (IS_BOT_CLIENT(head)) ? value : 0;
+
+ switch(t)
{
- if(c4 >= 0)
- {
- c4 = c4 + value;
- cb4 = cb4 + bvalue;
- }
+ case NUM_TEAM_1: if(c1 >= 0) { c1 += value; cb1 += bvalue; } break;
+ case NUM_TEAM_2: if(c2 >= 0) { c2 += value; cb2 += bvalue; } break;
+ case NUM_TEAM_3: if(c3 >= 0) { c3 += value; cb3 += bvalue; } break;
+ case NUM_TEAM_4: if(c4 >= 0) { c4 += value; cb4 += bvalue; } break;
}
}
}
--- /dev/null
+// team definitions
+float c1, c2, c3, c4; // client counts for each team
+float cb1, cb2, cb3, cb4; // # of bots on those teams
+
+// definitions for functions used outside teamplay.qc
+void CheckAllowedTeams(entity for_whom);
+void GetTeamCounts(entity other);
+float JoinBestTeam(entity pl, float only_return_best, float forcebestteam);
+++ /dev/null
-#ifdef TTURRETS_ENABLED
-
-// Include section.
-#include "../system/system_misc.qc" /// Assorted junk & jewls
-#include "../system/system_main.qc" /// And routines
-#include "../system/system_aimprocs.qc" /// Aiming realted stuff
-#include "../system/system_scoreprocs.qc" /// Target calssification
-#include "../system/system_damage.qc" /// Outch, they are hurting me! what should i do?
-
-// Non combat units
-#include "../units/unit_fusionreactor.qc" /// Supply unites that need it with power
-#include "../units/unit_targettrigger.qc" /// Hit me!
-#include "../units/unit_checkpoint.qc" /// Halfsmart pathing.
-
-// Combat units
-#include "../units/unit_plasma.qc" /// Basic energy cannon
-#include "../units/unit_mlrs.qc" /// Basic multibay RL
-#include "../units/unit_hellion.qc" /// Seeking missiles MLRS
-#include "../units/unit_flac.qc" /// anti missile turret
-#include "../units/unit_phaser.qc" /// ZzzapT
-#include "../units/unit_hk.qc" /// Hunter killers
-#include "../units/unit_machinegun.qc" /// whacka
-#include "../units/unit_tessla.qc" /// Chain lightning capabale turret
-#include "../units/unit_walker.qc" /// Moving minigun-rocket-meele err thing
-#include "../units/unit_ewheel.qc" /// A evil wheel. with guns on.
-//#include "../units/unit_repulsor.qc" /// Fires a wave that knocks foes back
-//#include "../units/unit_hive.qc" /// Swarm AI
-
-#endif // TTURRETS_ENABLED
+++ /dev/null
-// Comment out below to skip turrets
-#define TTURRETS_ENABLED
-
-#ifdef TTURRETS_ENABLED
-#ifdef SVQC
-//#message "with tZork turrets"
-
-float turret_count;
-
-vector real_origin(entity ent);
-
-/// 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;
-
-/// Used for cvar reloading
-.string cvar_basename;
-
-//.float spawnflags
-#define TSF_SUSPENDED 1
-/// Spawn a pillar model under the turret to make it look ok on uneven ground surfaces
-#define TSF_TERRAINBASE 2
-/// Disable builtin ammo regeneration
-#define TSF_NO_AMMO_REGEN 4
-/// Dont break path to chase enemys. will still fire at them if possible.
-#define TSF_NO_PATHBREAK 8
-/// Dont respawn
-#define TSL_NO_RESPAWN 16
-/// Let this turret roam when idle.
-#define TSL_ROAM 32
-
-/// target selection flags
-.float target_select_flags;
-/// target validatoin flags
-.float target_validate_flags;
-/// Dont select a target on its own.
-#define TFL_TARGETSELECT_NO 2
-/// Need line of sight
-#define TFL_TARGETSELECT_LOS 4
-/// Players are valid targets
-#define TFL_TARGETSELECT_PLAYERS 8
-/// Missiles are valid targets
-#define TFL_TARGETSELECT_MISSILES 16
-/// Responds to turret_trigger_target events
-#define TFL_TARGETSELECT_TRIGGERTARGET 32
-/// Angular limitations of turret head limits target selection
-#define TFL_TARGETSELECT_ANGLELIMITS 64
-/// Range limits apply in targetselection
-#define TFL_TARGETSELECT_RANGELIMTS 128
-/// DOnt select targets with a .team matching its own
-#define TFL_TARGETSELECT_TEAMCHECK 256
-/// Cant select targets on its own. needs to be triggerd or slaved.
-#define TFL_TARGETSELECT_NOBUILTIN 512
-/// TFL_TARGETSELECT_TEAMCHECK is inverted (selects only mebers of own .team)
-#define TFL_TARGETSELECT_OWNTEAM 1024
-/// Turrets aren't valid targets
-#define TFL_TARGETSELECT_NOTURRETS 2048
-/// Use feild of view
-#define TFL_TARGETSELECT_FOV 4096
-
-#define TFL_TARGETSELECT_MISSILESONLY 8192
-
-/// aim flags
-.float aim_flags;
-/// Dont aim.
-#define TFL_AIM_NO 1
-/// Go for ground, not direct hit, but only if target is on ground.
-#define TFL_AIM_GROUNDGROUND 2
-/// Try to predict target movement (does not account for gravity)
-#define TFL_AIM_LEAD 4
-/// Compensate for shot traveltime when lead
-#define TFL_AIM_SHOTTIMECOMPENSATE 8
-/// Try to do real prediction of targets z pos at impact.
-#define TFL_AIM_ZPREDICT 16
-/// Simply aim at target's current location
-#define TFL_AIM_SIMPLE 32
-
-/// track (turn and pitch head) flags
-.float track_flags;
-/// Dont move head
-#define TFL_TRACK_NO 2
-/// Pitch the head
-#define TFL_TRACK_PITCH 4
-/// Rotate the head
-#define TFL_TRACK_ROT 8
-
-/// How tracking is preformed
-.float track_type;
-/// Hard angle increments. Ugly for fast turning, best accuracy.
-#define TFL_TRACKTYPE_STEPMOTOR 1
-/// Smoth absolute movement. Looks ok, fair accuracy.
-#define TFL_TRACKTYPE_FLUIDPRECISE 2
-/// Simulated inertia. "Wobbly mode" Looks kool, can mean really bad accuracy depending on how the fields below are set
-#define TFL_TRACKTYPE_FLUIDINERTIA 3
-/// TFL_TRACKTYPE_FLUIDINERTIA: pitch multiplier
-.float track_accel_pitch;
-/// TFL_TRACKTYPE_FLUIDINERTIA: rotation multiplier
-.float track_accel_rot;
-/// TFL_TRACKTYPE_FLUIDINERTIA: Blendrate with old rotation (inertia simulation) 1 = only old, 0 = only new
-.float track_blendrate;
-
-/// How prefire check is preformed
-.float firecheck_flags;
-/// Dont kill the dead
-#define TFL_FIRECHECK_DEAD 4
-/// Range limits apply
-#define TFL_FIRECHECK_DISTANCES 8
-/// Line Of Sight needs to be clear
-#define TFL_FIRECHECK_LOS 16
-/// Consider distance inpactpoint<->aimspot
-#define TFL_FIRECHECK_AIMDIST 32
-/// Consider enemy origin<->impactpoint
-#define TFL_FIRECHECK_REALDIST 64
-/// Consider angular diff head<->aimspot
-#define TFL_FIRECHECK_ANGLEDIST 128
-/// (re)consider target.team<->self.team
-#define TFL_FIRECHECK_TEAMCECK 256
-/// Try to avoid friendly fire
-#define TFL_FIRECHECK_AFF 512
-/// Own .ammo needs to be >= then own .shot_dmg
-#define TFL_FIRECHECK_OWM_AMMO 1024
-/// Others ammo need to be < others .ammo_max
-#define TFL_FIRECHECK_OTHER_AMMO 2048
-/// Check own .attack_finished_single vs time
-#define TFL_FIRECHECK_REFIRE 4096
-/// Move the acctual target to aimspot before tracing impact (and back after)
-//#define TFL_FIRECHECK_VERIFIED 8192
-/// Dont do any chekcs
-#define TFL_FIRECHECK_NO 16384
-
-/// How shooting is done
-.float shoot_flags;
-/// Dont shoot
-#define TFL_SHOOT_NO 64
-/// Fire in vollys (partial implementation through .shot_volly)
-#define TFL_SHOOT_VOLLY 2
-/// Always do a full volly, even if target is lost or dead. (not implemented)
-#define TFL_SHOOT_VOLLYALWAYS 4
-/// Loop though all valid tarters, and hit them.
-#define TFL_SHOOT_HITALLVALID 8
-/// Fiering makes unit loose target (after volly is done, if in volly mode)
-#define TFL_SHOOT_CLEARTARGET 16
-///Custom shooting;
-#define TFL_SHOOT_CUSTOM 32
-
-/// Information aboute the units capabilities
-.float turrcaps_flags;
-/// No kown capabilities
-#define TFL_TURRCAPS_NONE 0
-/// Capable of sniping
-#define TFL_TURRCAPS_SNIPER 2
-/// Capable of splasdamage
-#define TFL_TURRCAPS_RADIUSDMG 4
-/// Has one or more cannons with zero shot traveltime
-#define TFL_TURRCAPS_HITSCAN 8
-/// More then one (type of) gun
-#define TFL_TURRCAPS_MULTIGUN 16
-/// Carries at least one guided weapon
-#define TFL_TURRCAPS_GUIDED 32
-/// At least one gun fiers slow projectiles
-#define TFL_TURRCAPS_SLOWPROJ 64
-/// At least one gun fiers medium speed projectiles
-#define TFL_TURRCAPS_MEDPROJ 128
-/// At least one gun fiers fast projectiles
-#define TFL_TURRCAPS_FASTPROJ 256
-/// At least one gun capable of damaging players
-#define TFL_TURRCAPS_PLAYERKILL 512
-/// At least one gun that can shoot town missiles
-#define TFL_TURRCAPS_MISSILEKILL 1024
-/// Has support capabilities. powerplants and sutch.
-#define TFL_TURRCAPS_SUPPORT 2048
-/// Proveides at least one type of ammmo
-#define TFL_TURRCAPS_AMMOSOURCE 4096
-/// Can recive targets from external sources
-#define TFL_TURRCAPS_RECIVETARGETS 8192
-/// Capable of self-transport
-#define TFL_TURRCAPS_MOVE 16384
-/// Will roam arround even if not chasing anyting
-#define TFL_TURRCAPS_ROAM 32768
-#define TFL_TURRCAPS_ISTURRET 65536
-
-/// Ammo types needed and/or provided
-//.float ammo_flags;
-#define ammo_flags currentammo
-/// Has and needs no ammo
-#define TFL_AMMO_NONE 64
-/// Uses power
-#define TFL_AMMO_ENERGY 2
-/// Uses bullets
-#define TFL_AMMO_BULLETS 4
-/// Uses explosives
-#define TFL_AMMO_ROCKETS 8
-/// Regenerates ammo on its own
-#define TFL_AMMO_RECHARGE 16
-/// Can recive ammo from others
-#define TFL_AMMO_RECIVE 32
-
-/// How incomming damage is handeld
-.float damage_flags;
-/// Cant be hurt
-#define TFL_DMG_NO 256
-/// Can be damaged
-#define TFL_DMG_YES 2
-/// Can be damaged by teammates
-#define TFL_DMG_TAKEFROMTEAM 4
-/// Traget attackers
-#define TFL_DMG_RETALIATE 8
-/// Target attackers, even is on own team
-#define TFL_DMG_RETALIATEONTEAM 16
-/// Loses target when damaged
-#define TFL_DMG_TARGETLOSS 32
-/// Reciving damage trows off aim (pointless atm, aim gets recalculated to fast). not implemented.
-#define TFL_DMG_AIMSHAKE 64
-/// Reciving damage slaps the head arround
-#define TFL_DMG_HEADSHAKE 128
-/// Die and stay dead.
-#define TFL_DMG_DEATH_NORESPAWN 256
-
-// Spawnflags
-/// Spawn in teambased modes
-#define TFL_SPAWN_TEAM 2
-/// Spawn in FFA modes
-#define TFL_SPAWN_FFA 4
-
-
-/*
-* Fields used by turrets
-*/
-/// Turrets internal ai speed
-.float ticrate;
-
-/// Where to point the when no target
-.vector idle_aim;
-
-/// Top part of turret
-.entity tur_head;
-
-/// Start/respawn health
-.float tur_health;
-
-/// Defend this entity (or ratehr this entitys position)
-.entity tur_defend;
-
-/// and shoot from here. (can be non constant, think MLRS)
-.vector tur_shotorg;
-
-/// Aim at this spot
-.vector tur_aimpos;
-
-/// Predicted time the round will impact
-.float tur_impacttime;
-
-// Predicted place the round will impact
-//.vector tur_impactpoint; // unused
-
-/// What entity the aimtrace hit, if any.
-.entity tur_impactent;
-
-/// Distance to enemy
-.float tur_dist_enemy;
-
-/// Distance to aimspot
-.float tur_dist_aimpos;
-
-/// Distance impact<->aim
-.float tur_dist_impact_to_aimpos;
-
-/// Decresment counter form .shot_volly to 0.
-.float volly_counter;
-
-/*
-* Projectile/missile. its up to the individual turret implementation to
-** deal the damage, blow upp the missile or whatever.
-*/
-/// Track then refireing is possible
-//.float attack_finished; = attack_finished_single
-/// Shoot this often
-.float shot_refire;
-/// Shots travel this fast, when appliable
-.float shot_speed;
-/// Inaccuracy
-.float shot_spread;
-/// Estimated (core) damage of projectiles. also reduce on ammo with this amount when fiering
-.float shot_dmg;
-/// If radius dmg, this is how big that radius is.
-.float shot_radius;
-/// Max force exserted by round impact
-.float shot_force;
-/// < 1 = shoot # times at target (if possible)
-.float shot_volly;
-/// Refire after a compleated volly.
-.float shot_volly_refire;
-
-/// Consider targets within this range
-.float target_range;
-/// Dont consider targets closer then
-.float target_range_min;
-/// Targets closer to this are prefered
-.float target_range_optimal;
-
-/*
-* The standard targetselection tries to select a target based on
-* range, angle offset, target type, "is old target"
-* Thise biases will allow score scaling to (dis)favor diffrent targets
-*/
-/// (dis)Favor best range this mutch
-.float target_select_rangebias;
-/// (dis)Favor targeting my old enemy this mutch
-.float target_select_samebias;
-/// (dis)Favor targeting the enemy closest to my guns current angle this mutch
-.float target_select_anglebias;
-/// (dis)Favor Missiles? (-1 to diable targeting compleatly)
-.float target_select_missilebias;
-/// (dis)Favot living players (-1 to diable targeting compleatly)
-.float target_select_playerbias;
-/// Field of view
-//.float target_select_fov;
-/// Last timestamp this turret aquierd a valid target
-.float target_select_time;
-/// Throttle re-validation of current target
-.float target_validate_time;
-/*
-* Aim refers to real aiming, not gun pos (thats done by track)
-*/
-/// Maximum offset between impact and aim spot to fire
-.float aim_firetolerance_dist;
-/// How fast can i rotate/pitch (per second in stepmotor mode, base force in smooth modes)
-.float aim_speed;
-/// cant aim higher/lower then this
-.float aim_maxpitch;
-/// I cant rotate more then this
-.float aim_maxrot;
-
-// Ammo/power. keeping dmg and ammo on a one to one ratio is preferable (for rating)
-/// Staring & current ammo
-.float ammo;
-/// Regenerate this mutch ammo (per second)
-.float ammo_recharge;
-/// Max amount of ammo i can hold
-.float ammo_max;
-
-
-// Uncomment below to enable various debug output.
-//#define TURRET_DEBUG
-//#define TURRET_DEBUG_TARGETVALIDATE
-//#define TURRET_DEBUG_TARGETSELECT
-
-#ifdef TURRET_DEBUG
-.float tur_dbg_dmg_t_h; // Total dmg that hit something (can be more then tur_dbg_dmg_t_f since it should count radius dmg.
-.float tur_dbg_dmg_t_f; // Total damage spent
-.float tur_dbg_start; // When did i go online?
-.float tur_dbg_tmr1; // timer for random use
-.float tur_dbg_tmr2; // timer for random use
-.float tur_dbg_tmr3; // timer for random use
-.vector tur_dbg_rvec; // Random vector, mainly for coloruing stuff'
-#endif
-
-// System main's
-/// Main AI loop
-void turret_think();
-/// Prefire checks and sutch
-void turret_fire();
-
-// Callbacks
-/// implements the actual fiering
-.void() turret_firefunc;
-/// prefire checks go here. return 1 to go bang, 0 not to.
-.float() turret_firecheckfunc;
-/// Execure AFTER main AI loop
-.void() turret_postthink;
-
-/// Add a target
-.float(entity e_target,entity e_sender) turret_addtarget;
-
-.void() turret_diehook;
-.void() turret_respawnhook;
-
-/*
-* Target selection, preferably but not nessesarely
-* return a normalized result.
-*/
-/// Function to use for target evaluation. usualy turret_stdproc_targetscore_generic
-.float(entity _turret, entity _target) turret_score_target;
-
-/*
-* Target selection
-*/
-/// Generic, fairly smart, bias-aware target selection.
-float turret_stdproc_targetscore_generic(entity _turret, entity _target);
-/// Experimental supportunits targetselector
-float turret_stdproc_targetscore_support(entity _turret,entity _target);
-
-/*
-* Aim functions
-*/
-/// Generic aimer guided by self.aim_flags
-vector turret_stdproc_aim_generic();
-
-/*
-* Turret turning & pitch
-*/
-/// Tries to line up the turret head with the aimpos
-void turret_stdproc_track();
-
-/// Generic damage handeling. blows up the turret when health <= 0
-void turret_stdproc_damage (entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector vforce);
-/// Spawns a explotion, does some damage & trows bits arround.
-void turret_stdproc_die();
-/// reassembles the turret.
-void turret_stdproc_respawn();
-
-/// Evaluate target validity
-float turret_validate_target(entity e_turret,entity e_target,float validate_flags);
-/// Turret Head Angle Diff Vector. updated by a sucsessfull call to turret_validate_target
-vector tvt_thadv;
-/// Turret Angle Diff Vector. updated by a sucsessfull call to turret_validate_target
-vector tvt_tadv;
-/// Turret Head Angle Diff Float. updated by a sucsessfull call to turret_validate_target
-float tvt_thadf;
-/// Turret Angle Diff Float. updated by a sucsessfull call to turret_validate_target
-float tvt_tadf;
-/// Distance. updated by a sucsessfull call to turret_validate_target
-float tvt_dist;
-
-/// updates aim org, shot org, shot dir and enemy org for selected turret
-void turret_do_updates(entity e_turret);
-.vector tur_shotdir_updated;
-
-void turrets_precash();
-#endif // SVQC
-
-// common
-.float turret_type;
-const float TID_COMMON = 1;
-const float TID_EWHEEL = 2;
-const float TID_FLAC = 3;
-const float TID_FUSION = 4;
-const float TID_HELLION = 5;
-const float TID_HK = 6;
-const float TID_MACHINEGUN = 7;
-const float TID_MLRS = 8;
-const float TID_PHASER = 9;
-const float TID_PLASMA = 10;
-const float TID_PLASMA_DUAL = 11;
-const float TID_TESLA = 12;
-const float TID_WALKER = 13;
-const float TID_LAST = 13;
-
-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;
-.float anim_start_time;
-const float TNSF_ANIM = 128;
-
-const float TNSF_FULL_UPDATE = 16777215;
-
-#endif // TTURRETS_ENABLED
-
-
+++ /dev/null
-/*
-* Generic aim
-
-supports:
-TFL_AIM_NO
-TFL_AIM_GROUNDGROUND
-TFL_AIM_LEAD
-TFL_AIM_SHOTTIMECOMPENSATE
-*/
-vector turret_stdproc_aim_generic()
-{
-
- vector pre_pos, prep;
- float distance, impact_time, 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
- {
- // FIXME: this cant be the best way to do this..
- prep = pre_pos;
-#ifdef GMQCC
- impact_time = 0;
-#endif
- for(i = 0; i < 4; ++i)
- {
- distance = vlen(prep - self.tur_shotorg);
- impact_time = distance / self.shot_speed;
- prep = pre_pos + self.enemy.velocity * impact_time;
- }
-
- 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_GROUNDGROUND)
- {
- //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;
-}
+++ /dev/null
-/*
-* Spawn a boom, trow fake bits arround
-* and hide the real ones.
-*/
-void turret_hide()
-{
- self.effects |= EF_NODRAW;
- self.nextthink = time + self.respawntime - 0.2;
- self.think = turret_stdproc_respawn;
-}
-
-void turret_stdproc_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)
- {
- if (self.turret_diehook)
- self.turret_diehook();
-
- remove(self.tur_head);
- remove(self);
- }
- else
- {
- // Setup respawn
- self.SendFlags |= TNSF_STATUS;
- self.nextthink = time + 0.2;
- self.think = turret_hide;
-
- if (self.turret_diehook)
- self.turret_diehook();
- }
-}
-
-void turret_stdproc_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_stdproc_damage;
-
- self.avelocity = '0 0 0';
- self.tur_head.avelocity = self.avelocity;
- self.tur_head.angles = self.idle_aim;
- self.health = self.tur_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;
-
- if (self.turret_respawnhook)
- self.turret_respawnhook();
-}
-
-/*
-* Standard damage proc.
-*/
-void turret_stdproc_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 (teamplay)
- if (self.team == attacker.team)
- {
- // This does not happen anymore. Re-enable if you fix that.
- if(IS_REAL_CLIENT(attacker))
- sprint(attacker, "\{1}Turret tells you: I'm on your team!\n");
-
- if(autocvar_g_friendlyfire)
- damage = damage * autocvar_g_friendlyfire;
- else
- return;
- }
-
- self.health = 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.turrcaps_flags & TFL_TURRCAPS_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_stdproc_die;
- }
-
- self.SendFlags |= TNSF_STATUS;
-}
+++ /dev/null
-#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.turret_type);
-
- 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.tur_health) * 255));
- }
-
- 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_maxrot = 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_rot = cvar(strcat(sbase,"_track_accel_rot"));
- ent.track_blendrate = cvar(strcat(sbase,"_track_blendrate"));
-
- if(is_reload)
- if(ent.turret_respawnhook)
- ent.turret_respawnhook();
-}
-
-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_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, 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_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;
-}
-
-/*
-vector turret_fovsearch_pingpong()
-{
- vector wish_angle;
- if(self.phase < time)
- {
- if( self.tur_head.phase )
- self.tur_head.phase = 0;
- else
- self.tur_head.phase = 1;
- self.phase = time + 5;
- }
-
- if( self.tur_head.phase)
- wish_angle = self.idle_aim + '0 1 0' * (self.aim_maxrot * (self.target_select_fov / 360));
- else
- wish_angle = self.idle_aim - '0 1 0' * (self.aim_maxrot * (self.target_select_fov / 360));
-
- return wish_angle;
-}
-
-vector turret_fovsearch_steprot()
-{
- vector wish_angle;
- //float rot_add;
-
- wish_angle = self.tur_head.angles;
- wish_angle_x = self.idle_aim_x;
-
- if (self.phase < time)
- {
- //rot_add = self.aim_maxrot / self.target_select_fov;
- wish_angle_y += (self.target_select_fov * 2);
-
- if(wish_angle_y > 360)
- wish_angle_y = wish_angle_y - 360;
-
- self.phase = time + 1.5;
- }
-
- return wish_angle;
-}
-
-vector turret_fovsearch_random()
-{
- vector wish_angle;
-
- if (self.phase < time)
- {
- wish_angle_y = random() * self.aim_maxrot;
- if(random() < 0.5)
- wish_angle_y *= -1;
-
- wish_angle_x = random() * self.aim_maxpitch;
- if(random() < 0.5)
- wish_angle_x *= -1;
-
- self.phase = time + 5;
-
- self.tur_aimpos = wish_angle;
- }
-
- return self.idle_aim + self.tur_aimpos;
-}
-*/
-
-/**
-** Handles head rotation according to
-** the units .track_type and .track_flags
-**/
-.float turret_framecounter;
-void turret_stdproc_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_ROT)
- {
- self.tur_head.angles_y += bound(-f_tmp, move_angle_y, f_tmp);
- if(self.tur_head.angles_y > self.aim_maxrot)
- self.tur_head.angles_y = self.aim_maxrot;
-
- if(self.tur_head.angles_y < -self.aim_maxrot)
- self.tur_head.angles_y = self.aim_maxrot;
- }
-
- // 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_rot * 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_ROT)
- {
- self.tur_head.avelocity_y = move_angle_y;
-
- if((self.tur_head.angles_y + self.tur_head.avelocity_y * self.ticrate) > self.aim_maxrot)
- {
- self.tur_head.avelocity_y = 0;
- self.tur_head.angles_y = self.aim_maxrot;
-
- self.SendFlags |= TNSF_ANG;
- }
-
- if((self.tur_head.angles_y + self.tur_head.avelocity_y * self.ticrate) < -self.aim_maxrot)
- {
- self.tur_head.avelocity_y = 0;
- self.tur_head.angles_y = -self.aim_maxrot;
-
- 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;
- }
-}
-
-
-/*
- + = 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_OWM_AMMO
- + TFL_FIRECHECK_OTHER_AMMO
- + TFL_FIRECHECK_REFIRE
-*/
-
-/**
-** Preforms pre-fire checks based on the uints firecheck_flags
-**/
-float turret_stdproc_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_OWM_AMMO)
- if (self.ammo < self.shot_dmg)
- return 0;
-
- // Other's ammo? (support-supply units)
- if (self.firecheck_flags & TFL_FIRECHECK_OTHER_AMMO)
- 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;
-}
-
-/*
- + TFL_TARGETSELECT_NO
- + TFL_TARGETSELECT_LOS
- + TFL_TARGETSELECT_PLAYERS
- + TFL_TARGETSELECT_MISSILES
- - TFL_TARGETSELECT_TRIGGERTARGET
- + TFL_TARGETSELECT_ANGLELIMITS
- + TFL_TARGETSELECT_RANGELIMTS
- + 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.owner == e_turret)
- return -0.5;
-
- if (!checkpvs(e_target.origin, e_turret))
- return -1;
-
- if (!e_target)
- return -2;
-
- 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(e_target.vehicle_flags & VHF_ISVEHICLE)
- {
- if (e_target.vehicle_health <= 0)
- return -6;
- }
- else if (e_target.health <= 0)
- 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.turret_firefunc || 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_RANGELIMTS)
- {
- 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_maxrot)
- 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;
-}
-
-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_stdproc_respawn();
- }
-
-#ifdef TURRET_DEBUG
- if (self.tur_dbg_tmr1 < time)
- {
- if (self.enemy) paint_target (self.enemy,128,self.tur_dbg_rvec,0.9);
- paint_target(self,256,self.tur_dbg_rvec,0.9);
- self.tur_dbg_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_stdproc_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_stdproc_aim_generic();
-
- // Turn & pitch?
- if (!(self.track_flags & TFL_TRACK_NO))
- turret_stdproc_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_stdproc_aim_generic();
-
- // Turn & pitch
- if (!(self.track_flags & TFL_TRACK_NO))
- turret_stdproc_track();
-
- turret_do_updates(self);
-
- // Fire!
- if (self.turret_firecheckfunc() != 0)
- turret_fire();
-
- if(self.turret_postthink)
- self.turret_postthink();
-
- 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_stdproc_track();
-
- // do any per-turret stuff
- if(self.turret_postthink)
- self.turret_postthink();
-
- // 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_stdproc_aim_generic();
-
- // Turn & pitch?
- if (!(self.track_flags & TFL_TRACK_NO))
- turret_stdproc_track();
-
- turret_do_updates(self);
-
- // Fire?
- if (self.turret_firecheckfunc())
- turret_fire();
- }
-
- // do any custom per-turret stuff
- if(self.turret_postthink)
- self.turret_postthink();
-}
-
-void turret_fire()
-{
- if (autocvar_g_turrets_nofire != 0)
- return;
-
- self.turret_firefunc();
-
- 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_dbg_rvec, self.tur_impacttime + 0.25);
-#endif
-}
-
-void turret_stdproc_fire()
-{
- dprint("^1Bang, ^3your dead^7 ",self.enemy.netname,"! ^1(turret with no real firefunc)\n");
-}
-
-/*
- When .used a turret switch team to activator.team.
- If activator is world, the turret go inactive.
-*/
-void turret_stdproc_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);
- 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 (e.turrcaps_flags & TFL_TURRCAPS_ISTURRET)
- {
- load_unit_settings(e,e.cvar_basename,1);
- if(e.turret_postthink)
- e.turret_postthink();
- }
-
- e = nextent(e);
- }
- cvar_set("g_turrets_reloadcvars","0");
- }
-}
-
-/*
-* Standard turret initialization. use this!
-* (unless you have a very good reason not to)
-* if the return value is 0, the turret should be removed.
-*/
-float turret_stdproc_init (string cvar_base_name, string base, string head, float _turret_type)
-{
- entity e, ee = world;
-
- // Are turrets allowed?
- if (autocvar_g_turrets == 0)
- return 0;
-
- if(_turret_type < 1 || _turret_type > TID_LAST)
- {
- dprint("Invalid / Unkown turret type\"", ftos(_turret_type), "\", aborting!\n");
- return 0;
- }
- self.turret_type = _turret_type;
-
- 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(); // why can't we use regular droptofloor here?
-
- // Terrainbase spawnflag. This puts a enlongated model
- // under the turret, so it looks ok on uneaven surfaces.
- /* TODO: Handle this with CSQC
- if (self.spawnflags & TSF_TERRAINBASE)
- {
- entity tb;
- tb = spawn();
- setmodel(tb,"models/turrets/terrainbase.md3");
- setorigin(tb,self.origin);
- tb.solid = SOLID_BBOX;
- }
- */
-
- self.cvar_basename = cvar_base_name;
- load_unit_settings(self, self.cvar_basename, 0);
-
- self.effects = EF_NODRAW;
-
- // Handle turret teams.
- if (!teamplay)
- self.team = MAX_SHOT_DISTANCE; // Group all turrets into the same team, so they dont kill eachother.
- else if(g_onslaught && self.targetname)
- {
- e = find(world,target,self.targetname);
- if(e != world)
- {
- self.team = e.team;
- ee = e;
- }
- }
- else if(!self.team)
- self.team = MAX_SHOT_DISTANCE; // Group all turrets into the same team, so they dont kill eachother.
-
- /*
- * Try to guess some reasonaly defaults
- * for missing params and do sanety checks
- * thise checks could produce some "interesting" results
- * if it hits a glitch in my logic :P so try to set as mutch
- * as possible beforehand.
- */
- if (!self.ticrate)
- {
- if (self.turrcaps_flags & TFL_TURRCAPS_SUPPORT)
- self.ticrate = 0.2; // Support units generaly dont need to have a high speed ai-loop
- else
- self.ticrate = 0.1; // 10 fps for normal turrets
- }
-
- self.ticrate = bound(sys_frametime, self.ticrate, 60); // keep it sane
-
-// General stuff
- if (self.netname == "")
- self.netname = self.classname;
-
- if (!self.respawntime)
- self.respawntime = 60;
- self.respawntime = max(-1, self.respawntime);
-
- if (!self.health)
- self.health = 1000;
- self.tur_health = max(1, self.health);
- self.bot_attack = TRUE;
- self.monster_attack = TRUE;
-
- if (!self.turrcaps_flags)
- self.turrcaps_flags = TFL_TURRCAPS_RADIUSDMG | TFL_TURRCAPS_MEDPROJ | TFL_TURRCAPS_PLAYERKILL;
-
- if (!self.damage_flags)
- self.damage_flags = TFL_DMG_YES | TFL_DMG_RETALIATE | TFL_DMG_AIMSHAKE;
-
-// Shot stuff.
- if (!self.shot_refire)
- self.shot_refire = 1;
- self.shot_refire = bound(0.01, self.shot_refire, 9999);
-
- if (!self.shot_dmg)
- self.shot_dmg = self.shot_refire * 50;
- self.shot_dmg = max(1, self.shot_dmg);
-
- if (!self.shot_radius)
- self.shot_radius = self.shot_dmg * 0.5;
- self.shot_radius = max(1, self.shot_radius);
-
- if (!self.shot_speed)
- self.shot_speed = 2500;
- self.shot_speed = max(1, self.shot_speed);
-
- if (!self.shot_spread)
- self.shot_spread = 0.0125;
- self.shot_spread = bound(0.0001, self.shot_spread, 500);
-
- if (!self.shot_force)
- self.shot_force = self.shot_dmg * 0.5 + self.shot_radius * 0.5;
- self.shot_force = bound(0.001, self.shot_force, 5000);
-
- if (!self.shot_volly)
- self.shot_volly = 1;
- self.shot_volly = bound(1, self.shot_volly, floor(self.ammo_max / self.shot_dmg));
-
- if (!self.shot_volly_refire)
- self.shot_volly_refire = self.shot_refire * self.shot_volly;
- self.shot_volly_refire = bound(self.shot_refire, self.shot_volly_refire, 60);
-
- if (!self.firecheck_flags)
- self.firecheck_flags = TFL_FIRECHECK_DEAD | TFL_FIRECHECK_DISTANCES |
- TFL_FIRECHECK_LOS | TFL_FIRECHECK_AIMDIST | TFL_FIRECHECK_TEAMCECK |
- TFL_FIRECHECK_OWM_AMMO | TFL_FIRECHECK_REFIRE;
-
-// Range stuff.
- if (!self.target_range)
- self.target_range = self.shot_speed * 0.5;
- self.target_range = bound(0, self.target_range, MAX_SHOT_DISTANCE);
-
- if (!self.target_range_min)
- self.target_range_min = self.shot_radius * 2;
- self.target_range_min = bound(0, self.target_range_min, MAX_SHOT_DISTANCE);
-
- if (!self.target_range_optimal)
- self.target_range_optimal = self.target_range * 0.5;
- self.target_range_optimal = bound(0, self.target_range_optimal, MAX_SHOT_DISTANCE);
-
-
-// Aim stuff.
- if (!self.aim_maxrot)
- self.aim_maxrot = 90;
- self.aim_maxrot = bound(0, self.aim_maxrot, 360);
-
- if (!self.aim_maxpitch)
- self.aim_maxpitch = 20;
- self.aim_maxpitch = bound(0, self.aim_maxpitch, 90);
-
- if (!self.aim_speed)
- self.aim_speed = 36;
- self.aim_speed = bound(0.1, self.aim_speed, 1000);
-
- if (!self.aim_firetolerance_dist)
- self.aim_firetolerance_dist = 5 + (self.shot_radius * 2);
- self.aim_firetolerance_dist = bound(0.1, self.aim_firetolerance_dist, MAX_SHOT_DISTANCE);
-
- if (!self.aim_flags)
- {
- self.aim_flags = TFL_AIM_LEAD | TFL_AIM_SHOTTIMECOMPENSATE;
- if(self.turrcaps_flags & TFL_TURRCAPS_RADIUSDMG)
- self.aim_flags |= TFL_AIM_GROUNDGROUND;
- }
-
- if (!self.track_type)
- self.track_type = TFL_TRACKTYPE_STEPMOTOR;
-
- if (self.track_type != TFL_TRACKTYPE_STEPMOTOR)
- {
- // Fluid / Ineria mode. Looks mutch nicer.
- // Can reduce aim preformance alot, needs a bit diffrent aimspeed
-
- if (!self.aim_speed)
- self.aim_speed = 180;
- self.aim_speed = bound(0.1, self.aim_speed, 1000);
-
- if (!self.track_accel_pitch)
- self.track_accel_pitch = 0.5;
-
- if (!self.track_accel_rot)
- self.track_accel_rot = 0.5;
-
- if (!self.track_blendrate)
- self.track_blendrate = 0.35;
- }
-
- if (!self.track_flags)
- self.track_flags = TFL_TRACK_PITCH | TFL_TRACK_ROT;
-
-
-// Target selection stuff.
- if (!self.target_select_rangebias)
- self.target_select_rangebias = 1;
- self.target_select_rangebias = bound(-10, self.target_select_rangebias, 10);
-
- if (!self.target_select_samebias)
- self.target_select_samebias = 1;
- self.target_select_samebias = bound(-10, self.target_select_samebias, 10);
-
- if (!self.target_select_anglebias)
- self.target_select_anglebias = 1;
- self.target_select_anglebias = bound(-10, self.target_select_anglebias, 10);
-
- if (!self.target_select_missilebias)
- self.target_select_missilebias = -10;
-
- self.target_select_missilebias = bound(-10, self.target_select_missilebias, 10);
- self.target_select_playerbias = bound(-10, self.target_select_playerbias, 10);
-
- if (!self.target_select_flags)
- {
- self.target_select_flags = TFL_TARGETSELECT_LOS | TFL_TARGETSELECT_TEAMCHECK
- | TFL_TARGETSELECT_RANGELIMTS | TFL_TARGETSELECT_ANGLELIMITS;
-
- if (self.turrcaps_flags & TFL_TURRCAPS_MISSILEKILL)
- self.target_select_flags |= TFL_TARGETSELECT_MISSILES;
-
- if (self.turrcaps_flags & TFL_TURRCAPS_PLAYERKILL)
- self.target_select_flags |= TFL_TARGETSELECT_PLAYERS;
- //else
- // self.target_select_flags = TFL_TARGETSELECT_NO;
- }
-
- self.target_validate_flags = self.target_select_flags;
-
-// Ammo stuff
- if (!self.ammo_max)
- self.ammo_max = self.shot_dmg * 10;
- self.ammo_max = max(self.shot_dmg, self.ammo_max);
-
- if (!self.ammo)
- self.ammo = self.shot_dmg * 5;
- self.ammo = bound(0,self.ammo, self.ammo_max);
-
- if (!self.ammo_recharge)
- self.ammo_recharge = self.shot_dmg * 0.5;
- self.ammo_recharge = max(0 ,self.ammo_recharge);
-
- // Convert the recharge from X per sec to X per ticrate
- self.ammo_recharge = self.ammo_recharge * self.ticrate;
-
- if (!self.ammo_flags)
- self.ammo_flags = TFL_AMMO_ENERGY | TFL_AMMO_RECHARGE;
-
-// Damage stuff
- if(self.spawnflags & TSL_NO_RESPAWN)
- if (!(self.damage_flags & TFL_DMG_DEATH_NORESPAWN))
- self.damage_flags |= TFL_DMG_DEATH_NORESPAWN;
-
-// Offsets & origins
- if (!self.tur_shotorg) self.tur_shotorg = '50 0 50';
-
- if (!self.health)
- self.health = 150;
-
-// Game hooks
- if(MUTATOR_CALLHOOK(TurretSpawn))
- return 0;
-
-// End of default & sanety checks, start building the turret.
-
-// Spawn extra bits
- self.tur_head = spawn();
- self.tur_head.netname = self.tur_head.classname = "turret_head";
- self.tur_head.team = self.team;
- self.tur_head.owner = self;
-
- setmodel(self, base);
- setmodel(self.tur_head, head);
-
- setsize(self, '-32 -32 0', '32 32 64');
- 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_health = self.health;
- self.solid = SOLID_BBOX;
- self.tur_head.solid = SOLID_NOT;
- self.takedamage = DAMAGE_AIM;
- self.tur_head.takedamage = DAMAGE_NO;
- self.movetype = MOVETYPE_NOCLIP;
- self.tur_head.movetype = MOVETYPE_NOCLIP;
-
- // Defend mode?
- 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");
- }
- }
-
- // In target defend mode, aim on the spot to defend when idle.
- 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';
-
- // Attach stdprocs. override when and what needed
- self.turret_firecheckfunc = turret_stdproc_firecheck;
- self.turret_firefunc = turret_stdproc_fire;
- self.event_damage = turret_stdproc_damage;
-
- if (self.turrcaps_flags & TFL_TURRCAPS_SUPPORT)
- self.turret_score_target = turret_stdproc_targetscore_support;
- else
- self.turret_score_target = turret_stdproc_targetscore_generic;
-
- self.use = turret_stdproc_use;
-
- ++turret_count;
- self.nextthink = time + 1;
- self.nextthink += turret_count * sys_frametime;
-
- self.tur_head.team = self.team;
- self.view_ofs = '0 0 0';
-
-#ifdef TURRET_DEBUG
- self.tur_dbg_start = self.nextthink;
- while (vlen(self.tur_dbg_rvec) < 2)
- self.tur_dbg_rvec = randomvec() * 4;
-
- self.tur_dbg_rvec_x = fabs(self.tur_dbg_rvec_x);
- self.tur_dbg_rvec_y = fabs(self.tur_dbg_rvec_y);
- self.tur_dbg_rvec_z = fabs(self.tur_dbg_rvec_z);
-#endif
-
- // Its all good.
- self.turrcaps_flags |= TFL_TURRCAPS_ISTURRET;
-
- self.classname = "turret_main";
-
- self.active = ACTIVE_ACTIVE;
-
- // In ONS mode, and linked to a ONS ent. need to call the use to set team.
- if (g_onslaught && ee)
- {
- activator = ee;
- self.use();
- }
-
- turret_link();
- turret_stdproc_respawn();
- turret_tag_fire_update();
-
- return 1;
-}
-
-
+++ /dev/null
-/*
-* 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;
- }
-
- self.tur_shotorg = gettaginfo(self.tur_head, gettagindex(self.tur_head, "tag_fire"));
- v_forward = normalize(v_forward);
-
- 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);
-
- // 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.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);
- while (ent)
- {
- // restore their solid type
- ent.solid = ent.railgunhitsolidbackup;
- ent = findfloat(ent, railgunhit, TRUE);
- }
-
- // find all the entities the railgun hit and hurt them
- 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;
-
- // 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);
- }
- trace_endpos = endpoint;
-}
-
-// Plug this into wherever precache is done.
-void g_turrets_common_precash()
-{
- precache_model ("models/turrets/c512.md3");
- precache_model ("models/marker.md3");
-}
-
-void turrets_precache_debug_models()
-{
- precache_model ("models/turrets/c512.md3");
- precache_model ("models/pathlib/goodsquare.md3");
- precache_model ("models/pathlib/badsquare.md3");
- precache_model ("models/pathlib/square.md3");
- precache_model ("models/pathlib/edge.md3");
-}
-
-void turrets_precash()
-{
- #ifdef TURRET_DEBUG
- turrets_precache_debug_models();
- #endif
-}
-
-
-#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
+++ /dev/null
-float turret_stdproc_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_stdproc_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_maxrot;
-
- 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;
-}
-
-/*
-float turret_stdproc_targetscore_close(entity _turret,entity _target)
-{
- return 1 - (tvt_dist / _turret.target_range);
-}
-
-float turret_stdproc_targetscore_far (entity _turret,entity _target)
-{
- return tvt_dist / _turret.target_range;
-}
-
-float turret_stdproc_targetscore_optimal(entity _turret,entity _target)
-{
- return min(_turret.target_range_optimal,tvt_dist) / max(_turret.target_range_optimal,tvt_dist);
-}
-
-float turret_stdproc_score_angular(entity _turret,entity _target)
-{
- return 1 - (tvt_thadf / _turret.aim_maxrot);
-}
-
-float turret_stdproc_targetscore_defend(entity _turret,entity _target)
-{
- return 0;
- //min(_target.origin,_turret.tur_defend.origin) / max(_target.origin,_turret.tur_defend.origin);
-}
-*/
+++ /dev/null
-/**
- turret_checkpoint
-**/
-
-
-//.entity checkpoint_target;
-
-/*
-#define checkpoint_cache_who flagcarried
-#define checkpoint_cache_from lastrocket
-#define checkpoint_cache_to selected_player
-*/
-
-.entity pathgoal;
-.entity pathcurrent;
-
-/*
-entity path_makeorcache(entity forwho,entity start, entity end)
-{
- entity oldself;
- entity pth;
- oldself = self;
- self = forwho;
-
- //pth = pathlib_makepath(start.origin,end.origin,PFL_GROUNDSNAP,500,1.5,PT_QUICKSTAR);
-
- self = oldself;
- return pth;
-}
-*/
-
-void turret_checkpoint_use()
-{
-}
-
-#if 0
-void turret_checkpoint_think()
-{
- if(self.enemy)
- te_lightning1(self,self.origin, self.enemy.origin);
-
- self.nextthink = time + 0.25;
-}
-#endif
-/*QUAKED turret_checkpoint (1 0 1) (-32 -32 -32) (32 32 32)
------------KEYS------------
-target: .targetname of next waypoint in chain.
-wait: Pause at this point # seconds.
------------SPAWNFLAGS-----------
----------NOTES----------
-If a loop is of targets are formed, any unit entering this loop will patrol it indefinitly.
-If the checkpoint chain in not looped, the unit will go "Roaming" when the last point is reached.
-*/
-//float tc_acum;
-void turret_checkpoint_init()
-{
- traceline(self.origin + '0 0 16', self.origin - '0 0 1024', MOVE_WORLDONLY, self);
- setorigin(self, trace_endpos + '0 0 32');
-
- if(self.target != "")
- {
- self.enemy = find(world, targetname, self.target);
- if(self.enemy == world)
- dprint("A turret_checkpoint faild to find its target!\n");
- }
- //self.think = turret_checkpoint_think;
- //self.nextthink = time + tc_acum + 0.25;
- //tc_acum += 0.25;
-}
-
-void spawnfunc_turret_checkpoint()
-{
- setorigin(self,self.origin);
- self.think = turret_checkpoint_init;
- self.nextthink = time + 0.2;
-}
-
-// Compat.
-void spawnfunc_walker_checkpoint()
-{
- self.classname = "turret_checkpoint";
- spawnfunc_turret_checkpoint();
-}
+++ /dev/null
-#define ewheel_amin_stop 0
-#define ewheel_amin_fwd_slow 1
-#define ewheel_amin_fwd_fast 2
-#define ewheel_amin_bck_slow 3
-#define ewheel_amin_bck_fast 4
-
-void ewheel_attack()
-{
- float i;
- entity _mis;
-
- for (i = 0; i < 1; ++i)
- {
- turret_do_updates(self);
-
- _mis = turret_projectile("weapons/lasergun_fire.wav", 1, 0, DEATH_TURRET_EWHEEL, PROJECTILE_BLASTER, TRUE, TRUE); // WEAPONTODO: this is not a projectile made by the blaster, add separate effect for it
- _mis.missile_flags = MIF_SPLASH;
-
- pointparticles(particleeffectnum("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;
- }
-
-}
-//#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.steerto = steerlib_standoff(self.enemy.origin,self.target_range_optimal);
- //self.steerto = steerlib_beamsteer(self.steerto,1024,64,68,256);
- self.moveto = self.origin + self.steerto * 128;
-
- if (self.tur_dist_enemy > self.target_range_optimal)
- {
- if ( self.tur_head.spawnshieldtime < 1 )
- {
- newframe = ewheel_amin_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_amin_fwd_slow;
- movelib_move_simple(v_forward, autocvar_g_turrets_unit_ewheel_speed_slow, 0.4);
- }
- else
- {
- newframe = ewheel_amin_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_amin_bck_slow;
- movelib_move_simple(v_forward * -1, autocvar_g_turrets_unit_ewheel_speed_slow, 0.4);
- }
- else
- {
- newframe = ewheel_amin_stop;
- movelib_beak_simple(autocvar_g_turrets_unit_ewheel_speed_stop);
- }
-
- turrets_setframe(newframe , FALSE);
-
- /*if(self.frame != newframe)
- {
- self.frame = newframe;
- self.SendFlags |= TNSF_ANIM;
- self.anim_start_time = time;
- }*/
-}
-
-
-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 ewheel_postthink()
-{
- 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;
-}
-
-void ewheel_respawnhook()
-{
- 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)
- return;
-
- 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
- }
- }
-}
-
-void ewheel_diehook()
-{
- self.velocity = '0 0 0';
-
-#ifdef EWHEEL_FANCYPATH
- if (self.pathcurrent)
- pathlib_deletepath(self.pathcurrent.owner);
-#endif
- self.pathcurrent = world;
-}
-
-void turret_ewheel_dinit()
-{
- entity e;
-
- if (self.netname == "")
- self.netname = "eWheel Turret";
-
- if (self.target != "")
- {
- e = find(world,targetname,self.target);
- if (!e)
- {
- bprint("Warning! initital waypoint for ewheel does NOT exsist!\n");
- self.target = "";
- }
-
- if (e.classname != "turret_checkpoint")
- dprint("Warning: not a turrret path\n");
- else
- self.goalcurrent = e;
- }
-
- self.ammo_flags = TFL_AMMO_ENERGY | TFL_AMMO_RECHARGE | TFL_AMMO_RECIVE;
- self.turrcaps_flags = TFL_TURRCAPS_PLAYERKILL | TFL_TURRCAPS_MOVE | TFL_TURRCAPS_ROAM ;
- self.turret_respawnhook = ewheel_respawnhook;
-
- self.turret_diehook = ewheel_diehook;
-
- if (turret_stdproc_init("ewheel_std", "models/turrets/ewheel-base2.md3", "models/turrets/ewheel-gun1.md3", TID_EWHEEL) == 0)
- {
- remove(self);
- return;
- }
-
- self.frame = 1;
- self.target_select_flags = TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_RANGELIMTS | TFL_TARGETSELECT_TEAMCHECK | TFL_TARGETSELECT_LOS;
- self.target_validate_flags = TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_RANGELIMTS | TFL_TARGETSELECT_TEAMCHECK | TFL_TARGETSELECT_LOS;
- self.iscreature = TRUE;
- self.teleportable = TELEPORT_NORMAL;
- 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;
-
- setsize(self, '-32 -32 0', '32 32 48');
-
- // Our fire routine
- self.turret_firefunc = ewheel_attack;
- self.turret_postthink = ewheel_postthink;
- self.tur_head.frame = 1;
-
- // 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);
-
- //setorigin(self,self.origin + '0 0 128');
- 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
- }
- }
-}
-
-void spawnfunc_turret_ewheel()
-{
- g_turrets_common_precash();
-
- precache_model ("models/turrets/ewheel-base2.md3");
- precache_model ("models/turrets/ewheel-gun1.md3");
-
- self.think = turret_ewheel_dinit;
- self.nextthink = time + 0.5;
-}
+++ /dev/null
-void spawnfunc_turret_flac();
-void turret_flac_dinit();
-void turret_flac_attack();
-
-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 turret_flac_attack()
-{
- entity proj;
-
- turret_tag_fire_update();
-
- proj = turret_projectile("weapons/hagar_fire.wav", 5, 0, DEATH_TURRET_FLAC, PROJECTILE_HAGAR, TRUE, TRUE);
- pointparticles(particleeffectnum("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;
-
-}
-
-void turret_flac_dinit()
-{
- if (self.netname == "")
- self.netname = "FLAC Cannon";
-
- self.turrcaps_flags = TFL_TURRCAPS_RADIUSDMG | TFL_TURRCAPS_FASTPROJ | TFL_TURRCAPS_MISSILEKILL;
- self.ammo_flags = TFL_AMMO_ROCKETS | TFL_AMMO_RECHARGE;
- self.aim_flags = TFL_AIM_LEAD | TFL_AIM_SHOTTIMECOMPENSATE;
-
- if (turret_stdproc_init("flac_std", "models/turrets/base.md3", "models/turrets/flac.md3", TID_FLAC) == 0)
- {
- remove(self);
- return;
- }
- setsize(self.tur_head,'-32 -32 0','32 32 64');
-
- self.damage_flags |= TFL_DMG_HEADSHAKE;
- self.target_select_flags |= TFL_TARGETSELECT_NOTURRETS | TFL_TARGETSELECT_MISSILESONLY;
-
- // Our fire routine
- self.turret_firefunc = turret_flac_attack;
-
-}
-/*QUAKED turret_flac (0 .5 .8) ?
-*/
-
-void spawnfunc_turret_flac()
-{
- precache_model ("models/turrets/base.md3");
- precache_model ("models/turrets/flac.md3");
-
- self.think = turret_flac_dinit;
- self.nextthink = time + 0.5;
-}
-
+++ /dev/null
-void spawnfunc_turret_fusionreactor();
-void turret_fusionreactor_dinit();
-void turret_fusionreactor_fire();
-
-void turret_fusionreactor_fire()
-{
- 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);
-}
-
-void turret_fusionreactor_postthink()
-{
- self.tur_head.avelocity = '0 250 0' * (self.ammo / self.ammo_max);
-}
-
-/*
-void turret_fusionreactor_respawnhook()
-{
- self.tur_head.avelocity = '0 50 0';
-}
-*/
-
-/**
-** Preforms pre-fire checks for fusionreactor
-**/
-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 turret_fusionreactor_dinit()
-{
- if (self.netname == "") self.netname = "Fusionreactor";
-
- self.turrcaps_flags = TFL_TURRCAPS_SUPPORT | TFL_TURRCAPS_AMMOSOURCE;
- self.ammo_flags = TFL_AMMO_ENERGY | TFL_AMMO_RECHARGE;
- self.target_select_flags = TFL_TARGETSELECT_TEAMCHECK | TFL_TARGETSELECT_OWNTEAM | TFL_TARGETSELECT_RANGELIMTS;
- self.firecheck_flags = TFL_FIRECHECK_OWM_AMMO | TFL_FIRECHECK_OTHER_AMMO | 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.turret_respawnhook = turret_fusionreactor_respawnhook;
-
- if (turret_stdproc_init("fusreac_std", "models/turrets/base.md3", "models/turrets/reactor.md3", TID_FUSION) == 0)
- {
- remove(self);
- return;
- }
- self.tur_head.scale = 0.75;
- self.tur_head.avelocity = '0 50 0';
- setsize(self,'-34 -34 0','34 34 90');
-
- self.turret_firecheckfunc = turret_fusionreactor_firecheck;
- self.turret_firefunc = turret_fusionreactor_fire;
- self.turret_postthink = turret_fusionreactor_postthink;
-}
-
-/*QUAKED turret_fusionreactor (0 .5 .8) ?
-*/
-void spawnfunc_turret_fusionreactor()
-{
- precache_model ("models/turrets/reactor.md3");
- precache_model ("models/turrets/base.md3");
-
- self.think = turret_fusionreactor_dinit;
- self.nextthink = time + 0.5;
-}
+++ /dev/null
-void spawnfunc_turret_hellion();
-void turret_hellion_dinit();
-void turret_hellion_attack();
-
-void turret_hellion_missile_think()
-{
- vector olddir,newdir;
- vector pre_pos;
- float itime;
-
- self.nextthink = time + 0.05;
-
- olddir = normalize(self.velocity);
-
- if(self.tur_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_std_shot_speed_gain, autocvar_g_turrets_unit_hellion_std_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_std_shot_speed_gain, autocvar_g_turrets_unit_hellion_std_shot_speed_max);
-
- if (itime < 0.05)
- self.think = turret_projectile_explode;
-
- UpdateCSQCProjectile(self);
-}
-void turret_hellion_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("weapons/rocket_fire.wav", 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.tur_health = time + 9;
- missile.tur_aimpos = randomvec() * 128;
- missile.missile_flags = MIF_SPLASH | MIF_PROXY | MIF_GUIDED_HEAT;
- self.tur_head.frame += 1;
-}
-
-void turret_hellion_postthink()
-{
- if (self.tur_head.frame != 0)
- self.tur_head.frame += 1;
-
- if (self.tur_head.frame >= 7)
- self.tur_head.frame = 0;
-}
-
-void turret_hellion_dinit()
-{
- if (self.netname == "") self.netname = "Hellion Missile Turret";
-
- self.turrcaps_flags = TFL_TURRCAPS_RADIUSDMG | TFL_TURRCAPS_FASTPROJ | TFL_TURRCAPS_PLAYERKILL | TFL_TURRCAPS_MISSILEKILL;
- self.aim_flags = TFL_AIM_SIMPLE;
- self.target_select_flags = TFL_TARGETSELECT_LOS | TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_RANGELIMTS | TFL_TARGETSELECT_TEAMCHECK ;
- self.firecheck_flags = TFL_FIRECHECK_DEAD | TFL_FIRECHECK_DISTANCES | TFL_FIRECHECK_TEAMCECK | TFL_FIRECHECK_REFIRE | TFL_FIRECHECK_AFF | TFL_FIRECHECK_OWM_AMMO;
- self.ammo_flags = TFL_AMMO_ROCKETS | TFL_AMMO_RECHARGE;
-
- if (turret_stdproc_init("hellion_std", "models/turrets/base.md3", "models/turrets/hellion.md3", TID_HELLION) == 0)
- {
- remove(self);
- return;
- }
-
- self.turret_firefunc = turret_hellion_attack;
- self.turret_postthink = turret_hellion_postthink;
-}
-
-/*QUAKED turret_hellion (0 .5 .8) ?
-*/
-void spawnfunc_turret_hellion()
-{
- precache_model ("models/turrets/hellion.md3");
- precache_model ("models/turrets/base.md3");
-
- self.think = turret_hellion_dinit;
- self.nextthink = time + 0.5;
-}
-
-
+++ /dev/null
-//#define TURRET_DEBUG_HK
-
-#ifdef TURRET_DEBUG_HK
-.float atime;
-#endif
-
-void spawnfunc_turret_hk();
-void turret_hk_dinit();
-void turret_hk_attack();
-
-
-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);
- 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);
- 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_std_shot_speed) )
- myspeed = max(myspeed * autocvar_g_turrets_unit_hk_std_shot_speed_decel, autocvar_g_turrets_unit_hk_std_shot_speed);
-
- // Failry clear, accelerate.
- if ( (ff > 0.7) && (myspeed < autocvar_g_turrets_unit_hk_std_shot_speed_max) )
- myspeed = min(myspeed * autocvar_g_turrets_unit_hk_std_shot_speed_accel, autocvar_g_turrets_unit_hk_std_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);
- vl = trace_endpos;
- fl = trace_fraction;
-
- // Trace right
- 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);
- 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);
- 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_std_shot_speed_max)
- myspeed = min(myspeed * autocvar_g_turrets_unit_hk_std_shot_speed_accel2,autocvar_g_turrets_unit_hk_std_shot_speed_max);
-
- wishdir = ve;
- }
-
- if ((myspeed > autocvar_g_turrets_unit_hk_std_shot_speed) && (self.cnt > time))
- myspeed = min(myspeed * autocvar_g_turrets_unit_hk_std_shot_speed_accel2,autocvar_g_turrets_unit_hk_std_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_std_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);
-}
-
-void turret_hk_attack()
-{
- entity missile;
-
- missile = turret_projectile("weapons/rocket_fire.wav", 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;
-
-}
-
-void turret_hk_postthink()
-{
- 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;
-}
-
-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 turret_hk_dinit()
-{
- if (self.netname == "")
- self.netname = "Hunter-killer turret";
-
- self.turrcaps_flags = TFL_TURRCAPS_RADIUSDMG | TFL_TURRCAPS_MEDPROJ | TFL_TURRCAPS_PLAYERKILL | TFL_TURRCAPS_RECIVETARGETS;
- 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_RANGELIMTS | TFL_TARGETSELECT_TEAMCHECK;
- self.firecheck_flags = TFL_FIRECHECK_DEAD | TFL_FIRECHECK_TEAMCECK | TFL_FIRECHECK_REFIRE | TFL_FIRECHECK_AFF;
- self.shoot_flags = TFL_SHOOT_CLEARTARGET;
-
- if (turret_stdproc_init("hk_std", "models/turrets/base.md3", "models/turrets/hk.md3", TID_HK) == 0)
- {
- remove(self);
- return;
- }
-
- self.target_validate_flags = TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_TEAMCHECK;
-
- // Our fire routine
- self.turret_firefunc = turret_hk_attack;
-
- // re-color badge & handle recoil effect
- self.turret_postthink = turret_hk_postthink;
-
- // What to do when reciveing foreign target data
- self.turret_addtarget = turret_hk_addtarget;
-}
-
-
-/*QUAKED turret_hk (0 .5 .8) ?
-* Turret that fires Hunter-killer missiles.
-* Missiles seek their target and try to avoid obstacles. If target dies early, they
-* pick a new one on their own.
-*/
-
-void spawnfunc_turret_hk()
-{
- precache_model ("models/turrets/base.md3");
- precache_model ("models/turrets/hk.md3");
-
- self.think = turret_hk_dinit;
- self.nextthink = time + 0.5;
-}
-
-
+++ /dev/null
-void spawnfunc_turret_machinegun();
-void turret_machinegun_std_init();
-void turret_machinegun_attack();
-
-//.float bulletcounter;
-void turret_machinegun_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(); // WEAPONTODO
- setattachment(self.muzzle_flash, self.tur_head, "tag_fire");
-}
-
-
-void turret_machinegun_std_init()
-{
- if (self.netname == "") self.netname = "Machinegun Turret";
-
- self.ammo_flags = TFL_AMMO_BULLETS | TFL_AMMO_RECHARGE | TFL_AMMO_RECIVE;
- self.turrcaps_flags = TFL_TURRCAPS_PLAYERKILL;
- self.aim_flags = TFL_AIM_LEAD | TFL_AIM_SHOTTIMECOMPENSATE;
-
- self.turrcaps_flags |= TFL_TURRCAPS_HITSCAN;
-
- if (turret_stdproc_init("machinegun_std", "models/turrets/base.md3", "models/turrets/machinegun.md3", TID_MACHINEGUN) == 0)
- {
- remove(self);
- return;
- }
-
- self.damage_flags |= TFL_DMG_HEADSHAKE;
- self.target_select_flags = TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_RANGELIMTS | TFL_TARGETSELECT_TEAMCHECK;
-
- // Our fire routine
- self.turret_firefunc = turret_machinegun_attack;
-
-}
-
-
-/*QUAKED turret_machinegun (0 .5 .8) ?
-* machinegun turret. does what you'd expect
-*/
-void spawnfunc_turret_machinegun()
-{
- precache_model ("models/turrets/machinegun.md3");
- precache_model ("models/turrets/base.md3");
- precache_sound ("weapons/uzi_fire.wav");
-
- self.think = turret_machinegun_std_init;
- self.nextthink = time + 0.5;
-}
-
+++ /dev/null
-void spawnfunc_turret_mlrs();
-void turret_mlrs_dinit();
-void turret_mlrs_attack();
-
-void turret_mlrs_postthink()
-{
- // 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");
- }
-}
-
-void turret_mlrs_attack()
-{
- entity missile;
-
- turret_tag_fire_update();
- missile = turret_projectile("weapons/rocket_fire.wav", 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);
-}
-
-void turret_mlrs_dinit()
-{
- if (self.netname == "") self.netname = "MLRS turret";
-
- self.turrcaps_flags = TFL_TURRCAPS_RADIUSDMG | TFL_TURRCAPS_MEDPROJ | TFL_TURRCAPS_PLAYERKILL;
- self.ammo_flags = TFL_AMMO_ROCKETS | TFL_AMMO_RECHARGE;
- self.aim_flags = TFL_AIM_LEAD | TFL_AIM_SHOTTIMECOMPENSATE;
-
- if (turret_stdproc_init("mlrs_std", "models/turrets/base.md3", "models/turrets/mlrs.md3", TID_MLRS) == 0)
- {
- remove(self);
- return;
- }
-
- self.damage_flags |= TFL_DMG_HEADSHAKE;
- self.shoot_flags |= TFL_SHOOT_VOLLYALWAYS;
- self.volly_counter = self.shot_volly;
-
- // Our fire routine
- self.turret_firefunc = turret_mlrs_attack;
- self.turret_postthink = turret_mlrs_postthink;
-
-}
-
-/*QUAKED turret_mlrs (0 .5 .8) ?
-*/
-
-void spawnfunc_turret_mlrs()
-{
- precache_model ("models/turrets/mlrs.md3");
- precache_model ("models/turrets/base.md3");
-
- self.think = turret_mlrs_dinit;
- self.nextthink = time + 0.5;
-}
-
-
+++ /dev/null
-void spawnfunc_turret_phaser();
-void turret_phaser_dinit();
-void turret_phaser_attack();
-
-.float fireflag;
-
-float turret_phaser_firecheck()
-{
- if (self.fireflag != 0) return 0;
- return turret_stdproc_firecheck();
-}
-
-void turret_phaser_postthink()
-{
- if (self.tur_head.frame == 0)
- return;
-
- 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;
- }
- }
-}
-
-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 turret_phaser_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_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, "weapons/neximpact.wav", VOL_BASE, ATTEN_NORM);
-
- if (self.tur_head.frame == 0)
- self.tur_head.frame = 1;
-}
-
-void turret_phaser_dinit()
-{
- if (self.netname == "") self.netname = "Phaser Cannon";
-
- self.turrcaps_flags = TFL_TURRCAPS_SNIPER|TFL_TURRCAPS_HITSCAN|TFL_TURRCAPS_PLAYERKILL;
- self.ammo_flags = TFL_AMMO_ENERGY | TFL_AMMO_RECHARGE | TFL_AMMO_RECIVE;
- self.aim_flags = TFL_AIM_LEAD;
-
- if (turret_stdproc_init("phaser_std", "models/turrets/base.md3","models/turrets/phaser.md3", TID_PHASER) == 0)
- {
- remove(self);
- return;
- }
-
- self.turret_firecheckfunc = turret_phaser_firecheck;
- self.turret_firefunc = turret_phaser_attack;
- self.turret_postthink = turret_phaser_postthink;
-
-}
-
-/*QUAKED turret_phaser(0 .5 .8) ?
-*/
-void spawnfunc_turret_phaser()
-{
- precache_sound ("turrets/phaser.wav");
- precache_model ("models/turrets/phaser.md3");
- precache_model ("models/turrets/phaser_beam.md3");
- precache_model ("models/turrets/base.md3");
-
- self.think = turret_phaser_dinit;
- self.nextthink = time + 0.5;
-}
-
+++ /dev/null
-void spawnfunc_turret_plasma();
-void spawnfunc_turret_plasma_dual();
-
-void turret_plasma_std_init();
-void turret_plasma_dual_init();
-
-void turret_plasma_attack();
-
-
-void turret_plasma_postthink()
-{
- 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;
-}
-
-void turret_plasma_dual_postthink()
-{
- 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;
-}
-
-void turret_plasma_minsta_attack (void)
-{
- 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);
-
-
- pointparticles(particleeffectnum("nex_muzzleflash"), self.tur_shotorg, self.tur_shotdir_updated * 1000, 1);
-
- // teamcolor / hit beam effect
- vector v;
- v = WarpZone_UnTransformOrigin(WarpZone_trace_transform, trace_endpos);
- if(teamplay)
- {
- switch(self.team)
- {
- case NUM_TEAM_1: // Red
- WarpZone_TrailParticles(world, particleeffectnum("TE_TEI_G3RED"), self.tur_shotorg, v);
- break;
- case NUM_TEAM_2: // Blue
- WarpZone_TrailParticles(world, particleeffectnum("TE_TEI_G3BLUE"), self.tur_shotorg, v);
- break;
- case NUM_TEAM_3: // Yellow
- WarpZone_TrailParticles(world, particleeffectnum("TE_TEI_G3YELLOW"), self.tur_shotorg, v);
- break;
- case NUM_TEAM_4: // Pink
- WarpZone_TrailParticles(world, particleeffectnum("TE_TEI_G3PINK"), self.tur_shotorg, v);
- break;
- }
- }
- else
- WarpZone_TrailParticles(world, particleeffectnum("TE_TEI_G3"), self.tur_shotorg, v);
- if (self.tur_head.frame == 0)
- self.tur_head.frame = 1;
-}
-
-void turret_plasma_attack()
-{
- entity missile = turret_projectile("weapons/hagar_fire.wav", 1, 0, DEATH_TURRET_PLASMA, PROJECTILE_ELECTRO_BEAM, TRUE, TRUE);
- missile.missile_flags = MIF_SPLASH;
-
- pointparticles(particleeffectnum("laser_muzzleflash"), self.tur_shotorg, self.tur_shotdir_updated * 1000, 1);
- if (self.tur_head.frame == 0)
- self.tur_head.frame = 1;
-}
-
-void turret_plasma_dual_attack()
-{
- entity missile = turret_projectile("weapons/hagar_fire.wav", 1, 0, DEATH_TURRET_PLASMA, PROJECTILE_ELECTRO_BEAM, TRUE, TRUE);
- missile.missile_flags = MIF_SPLASH;
- pointparticles(particleeffectnum("laser_muzzleflash"), self.tur_shotorg, self.tur_shotdir_updated * 1000, 1);
- self.tur_head.frame += 1;
-}
-
-void turret_plasma_std_init()
-{
- if (self.netname == "") self.netname = "Plasma Cannon";
-
- // What ammo to use
- self.ammo_flags = TFL_AMMO_ENERGY | TFL_AMMO_RECHARGE | TFL_AMMO_RECIVE;
-
- // How to aim
- self.aim_flags = TFL_AIM_LEAD | TFL_AIM_SHOTTIMECOMPENSATE | TFL_AIM_GROUNDGROUND;
- self.turrcaps_flags = TFL_TURRCAPS_RADIUSDMG | TFL_TURRCAPS_MEDPROJ | TFL_TURRCAPS_PLAYERKILL;
-
- if (turret_stdproc_init("plasma_std", "models/turrets/base.md3", "models/turrets/plasma.md3", TID_PLASMA) == 0)
- {
- remove(self);
- return;
- }
-
- self.damage_flags |= TFL_DMG_HEADSHAKE;
- self.firecheck_flags |= TFL_FIRECHECK_AFF;
-
- // Our fireing routine
- if(g_instagib)
- self.turret_firefunc = turret_plasma_minsta_attack;
- else
- self.turret_firefunc = turret_plasma_attack;
-
- // Custom per turret frame stuff. usualy animation.
- self.turret_postthink = turret_plasma_postthink;
- turret_do_updates(self);
-}
-
-
-void turret_plasma_dual_init()
-{
- if (self.netname == "") self.netname = "Dual Plasma Cannon";
-
- // What ammo to use
- self.ammo_flags = TFL_AMMO_ENERGY | TFL_AMMO_RECHARGE | TFL_AMMO_RECIVE;
-
- // How to aim at targets
- self.aim_flags = TFL_AIM_LEAD | TFL_AIM_SHOTTIMECOMPENSATE | TFL_AIM_GROUNDGROUND ;
- self.turrcaps_flags = TFL_TURRCAPS_RADIUSDMG | TFL_TURRCAPS_MEDPROJ | TFL_TURRCAPS_PLAYERKILL;
-
- if (turret_stdproc_init("plasma_dual", "models/turrets/base.md3", "models/turrets/plasmad.md3", TID_PLASMA_DUAL) == 0)
- {
- remove(self);
- return;
- }
-
- self.damage_flags |= TFL_DMG_HEADSHAKE;
- self.firecheck_flags |= TFL_FIRECHECK_AFF;
-
- // Our fireing routine
- self.turret_firefunc = turret_plasma_dual_attack;
-
- // Custom per turret frame stuff. usualy animation.
- self.turret_postthink = turret_plasma_dual_postthink;
-}
-
-
-/*
-* Basic moderate (std) or fast (dual) fireing, short-mid range energy cannon.
-* Not too mutch of a therat on its own, but can be rather dangerous in groups.
-* Regenerates ammo slowly, support with a fusionreactor(s) to do some real damage.
-*/
-
-/*QUAKED turret_plasma (0 .5 .8) ?
-*/
-void spawnfunc_turret_plasma()
-{
- g_turrets_common_precash();
- precache_model ("models/turrets/plasma.md3");
- precache_model ("models/turrets/base.md3");
-
- self.think = turret_plasma_std_init;
- self.nextthink = time + 0.5;
-}
-
-/*QUAKED turret_plasma_dual (0 .5 .8) ?
-*/
-void spawnfunc_turret_plasma_dual()
-{
-
- precache_model ("models/turrets/plasmad.md3");
- precache_model ("models/turrets/base.md3");
-
- self.think = turret_plasma_dual_init;
- self.nextthink = time + 0.5;
-}
-
+++ /dev/null
-void spawnfunc_turret_targettrigger();
-void turret_targettrigger_touch();
-
-void turret_targettrigger_touch()
-{
- entity e;
- if (self.cnt > time) return;
- entity oldself;
- oldself = self;
-
- e = find(world, targetname, self.target);
- while (e)
- {
- if (e.turrcaps_flags & TFL_TURRCAPS_RECIVETARGETS)
- {
- self = e;
- if(e.turret_addtarget)
- e.turret_addtarget(other,oldself);
- }
-
- e = find(e, targetname, oldself.target);
- }
-
- oldself.cnt = time + 0.5;
-
- self = oldself;
-}
-
-/*QUAKED turret_targettrigger (.5 .5 .5) ?
-*/
-void spawnfunc_turret_targettrigger()
-{
- if (!autocvar_g_turrets)
- {
- remove(self);
- return;
- }
-
- InitTrigger ();
-
- self.touch = turret_targettrigger_touch;
-}
+++ /dev/null
-void spawnfunc_turret_tesla();
-void turret_tesla_dinit();
-void turret_tesla_fire();
-
-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_stdproc_firecheck())
- return 0;
-
- if(self.enemy)
- return 1;
-
- return 0;
-}
-
-
-void turret_tesla_fire()
-{
- 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_RANGELIMTS | TFL_TARGETSELECT_TEAMCHECK;
-
- t = toast(e,r,d);
- remove(e);
-
- if (t == world) return;
-
- 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;
- }
-}
-
-void turret_tesla_postthink()
-{
- if (!self.active)
- {
- self.tur_head.avelocity = '0 0 0';
- return;
- }
-
- 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;
-
- 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));
- }
-}
-
-
-void turret_tesla_dinit()
-{
- if (self.netname == "") self.netname = "Tesla Coil";
-
- self.turrcaps_flags = TFL_TURRCAPS_HITSCAN | TFL_TURRCAPS_PLAYERKILL | TFL_TURRCAPS_MISSILEKILL;
-
- self.target_select_flags = TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_MISSILES |
- TFL_TARGETSELECT_RANGELIMTS | TFL_TARGETSELECT_TEAMCHECK;
-
- self.firecheck_flags = TFL_FIRECHECK_REFIRE | TFL_FIRECHECK_OWM_AMMO;
- self.shoot_flags = TFL_SHOOT_CUSTOM;
- self.ammo_flags = TFL_AMMO_ENERGY | TFL_AMMO_RECHARGE | TFL_AMMO_RECIVE;
- self.aim_flags = TFL_AIM_NO;
- self.track_flags = TFL_TRACK_NO;
-
- if (turret_stdproc_init("tesla_std", "models/turrets/tesla_base.md3", "models/turrets/tesla_head.md3", TID_TESLA) == 0)
- {
- remove(self);
- return;
- }
- setsize(self,'-60 -60 0','60 60 128');
-
- self.target_validate_flags = TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_MISSILES |
- TFL_TARGETSELECT_RANGELIMTS | TFL_TARGETSELECT_TEAMCHECK;
-
- self.turret_firefunc = turret_tesla_fire;
- self.turret_postthink = turret_tesla_postthink;
- self.turret_firecheckfunc = turret_tesla_firecheck;
-}
-
-/*QUAKED turret_tesla (0 .5 .8) ?
-*/
-void spawnfunc_turret_tesla()
-{
- precache_model ("models/turrets/tesla_head.md3");
- precache_model ("models/turrets/tesla_base.md3");
-
-
- self.think = turret_tesla_dinit;
- self.nextthink = time + 0.5;
-}
-
+++ /dev/null
-#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_MEELE 9
-#define ANIM_SWIM 10
-#define ANIM_ROAM 11
-.float animflag;
-
-#define WALKER_MIN '-70 -70 0'
-#define WALKER_MAX '70 70 95'
-
-#define WALKER_PATH(s,e) pathlib_astar(s,e)
-
-float walker_firecheck()
-{
- if (self.animflag == ANIM_MEELE)
- return 0;
-
- return turret_stdproc_firecheck();
-}
-
-void walker_meele_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_std_meele_dmg, DEATH_TURRET_WALK_MEELE, '0 0 0', v_forward * autocvar_g_turrets_unit_walker_std_meele_force);
-
- e = e.chain;
- }
-}
-
-void walker_setnoanim()
-{
- turrets_setframe(ANIM_NO, FALSE);
- self.animflag = self.frame;
-}
-void walker_rocket_explode()
-{
- RadiusDamage (self, self.owner, autocvar_g_turrets_unit_walker_std_rocket_dmg, 0, autocvar_g_turrets_unit_walker_std_rocket_radius, self, world, autocvar_g_turrets_unit_walker_std_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_std_rocket_speed, autocvar_g_turrets_unit_walker_std_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.tur_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.tur_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.tur_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, "weapons/hagar_fire.wav", 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_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_std_rocket_speed;
- rocket.angles = vectoangles(rocket.velocity);
- rocket.touch = walker_rocket_explode;
- rocket.flags = FL_PROJECTILE;
- rocket.solid = SOLID_BBOX;
- rocket.tur_health = time + 9;
- rocket.missile_flags = MIF_SPLASH | MIF_PROXY | MIF_GUIDED_HEAT;
-
- 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
-}
-
-.float idletime;
-void walker_postthink()
-{
- 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_std_meele_range && self.animflag != ANIM_MEELE)
- {
- 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_MEELE;
- }
- }
- 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_std_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_std_rockets_range_min)
- if (self.tur_dist_enemy < autocvar_g_turrets_unit_walker_std_rockets_range)
- self.tur_head.shot_volly = 4;
- }
- }
- else
- {
- if (self.animflag != ANIM_MEELE)
- walker_move_to(self.enemy.origin, self.tur_dist_enemy);
- }
- }
-
- //if(self.animflag != ANIM_NO)
- {
- 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_MEELE:
- if(self.frame != ANIM_MEELE)
- {
- defer(0.41, walker_setnoanim);
- defer(0.21, walker_meele_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);
-}
-
-void walker_attack()
-{
- sound (self, CH_WEAPON_A, "weapons/uzi_fire.wav", 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);
- pointparticles(particleeffectnum("laser_muzzleflash"), self.tur_shotorg, self.tur_shotdir_updated * 1000, 1);
-}
-
-
-void walker_respawnhook()
-{
- 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)
- return;
-
- setorigin(self, self.pos1);
- self.angles = self.pos2;
-
- if (self.target != "")
- {
- e = find(world, targetname, self.target);
- if (!e)
- {
- dprint("Warning! initital waypoint for Walker does NOT exsist!\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
- }
- }
-}
-
-void walker_diehook()
-{
-#ifdef WALKER_FANCYPATHING
- if (self.pathcurrent)
- pathlib_deletepath(self.pathcurrent.owner);
-#endif
- self.pathcurrent = world;
-}
-
-void turret_walker_dinit()
-{
- entity e;
-
- if (self.netname == "") self.netname = "Walker Turret";
-
- self.ammo_flags = TFL_AMMO_BULLETS | TFL_AMMO_RECHARGE | TFL_AMMO_RECIVE;
- self.turrcaps_flags = TFL_TURRCAPS_PLAYERKILL | TFL_TURRCAPS_MOVE ;
- self.aim_flags = TFL_AIM_LEAD;
-
- self.turrcaps_flags |= TFL_TURRCAPS_HITSCAN;
-
-
- self.turret_respawnhook = walker_respawnhook;
- self.turret_diehook = walker_diehook;
-
- self.ticrate = 0.05;
- if (turret_stdproc_init("walker_std", "models/turrets/walker_body.md3", "models/turrets/walker_head_minigun.md3", TID_WALKER) == 0)
- {
- remove(self);
- return;
- }
- setsize(self, WALKER_MIN, WALKER_MAX);
- self.target_select_flags = TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_RANGELIMTS | TFL_TARGETSELECT_TEAMCHECK | TFL_TARGETSELECT_LOS;
- self.target_validate_flags = TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_RANGELIMTS | TFL_TARGETSELECT_TEAMCHECK | TFL_TARGETSELECT_LOS;
- self.iscreature = TRUE;
- self.teleportable = TELEPORT_NORMAL;
- self.damagedbycontents = TRUE;
- self.movetype = MOVETYPE_WALK;
- self.solid = SOLID_SLIDEBOX;
- self.takedamage = DAMAGE_AIM;
- 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.idle_aim = '0 0 0';
- self.turret_firecheckfunc = walker_firecheck;
- self.turret_firefunc = walker_attack;
- self.turret_postthink = walker_postthink;
-
- 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
- }
- }
-}
-
-
-void spawnfunc_turret_walker()
-{
- g_turrets_common_precash();
-
- precache_model ("models/turrets/walker_head_minigun.md3");
- precache_model ("models/turrets/walker_body.md3");
- precache_model ( "models/turrets/rocket.md3");
- precache_sound ( "weapons/rocket_impact.wav" );
-
- self.think = turret_walker_dinit;
- self.nextthink = time + 0.5;
-}
+++ /dev/null
-#define BRG_SETUP 2
-#define BRG_START 4
-#define BRG_END 8
-
-#ifdef SVQC
-// Auto cvars
-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_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;
-
-
-float bumble_raygun_send(entity to, float sf);
-
-#define BUMB_MIN '-130 -130 -130'
-#define BUMB_MAX '130 130 130'
-
-void bumb_fire_cannon(entity _gun, string _tagname, entity _owner)
-{
- vector v = gettaginfo(_gun, gettagindex(_gun, _tagname));
- vehicles_projectile("bigplasma_muzzleflash", "weapons/flacexp3.wav",
- 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);
-}
-
-float bumb_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 i, distance, impact_time;
-
- vector vf = real_origin(gun.enemy);
- vector _vel = gun.enemy.velocity;
- if(gun.enemy.movetype == MOVETYPE_WALK)
- _vel_z *= 0.1;
-
-
- ad = vf;
- for(i = 0; i < 4; ++i)
- {
- 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(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;
- bumb_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 bumb_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, 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.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 bumb_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;
- }
-
- _gunner = other;
- _gunner.vehicle = _gun;
- _gun.switchweapon = other.switchweapon;
- _gun.vehicle_exit = bumb_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.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 = bumb_gunner_frame;
- other.flags &= ~FL_ONGROUND;
-
- 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;
-}
-
-float vehicles_valid_pilot()
-{
- if (!IS_PLAYER(other))
- return FALSE;
-
- if(other.deadflag != DEAD_NO)
- return FALSE;
-
- if(other.vehicle != world)
- return FALSE;
-
- if (!IS_REAL_CLIENT(other))
- if(!autocvar_g_vehicles_allow_bots)
- return FALSE;
-
- if(teamplay && other.team != self.team)
- return FALSE;
-
- return TRUE;
-}
-
-void bumb_touch()
-{
-
- if(self.gunner1 != world && self.gunner2 != world)
- {
- vehicles_touch();
- return;
- }
-
- if(vehicles_valid_pilot())
- {
- if(self.gun1.phase <= time)
- if(bumb_gunner_enter())
- return;
-
- if(self.gun2.phase <= time)
- if(bumb_gunner_enter())
- return;
- }
-
- vehicles_touch();
-}
-
-void bumb_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);
-
- 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);
-
- 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);
-
-}
-
-float bumb_pilot_frame()
-{
- entity pilot, vehic;
- vector newvel;
-
- pilot = self;
- vehic = self.vehicle;
- self = vehic;
-
-
- if(vehic.deadflag != DEAD_NO)
- {
- self = pilot;
- pilot.BUTTON_ATCK = pilot.BUTTON_ATCK2 = 0;
- return 1;
- }
-
- bumb_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);
- 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);
-
- // Pitch
- ftmp = 0;
- 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)
- 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);
-
- 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);
- newvel = vehic.velocity * -autocvar_g_vehicle_bumblebee_friction;
-
- 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)
- newvel -= v_forward * autocvar_g_vehicle_bumblebee_speed_forward;
- }
-
- 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)
- 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);
- }
- else
- {
- vehic.angles_z *= 0.95;
- 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((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.tur_health)
- trace_ent.vehicle_shield = min(trace_ent.vehicle_shield + autocvar_g_vehicle_bumblebee_healgun_sps * frametime, trace_ent.tur_head.tur_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.tur_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(trace_ent.turrcaps_flags & TFL_TURRCAPS_ISTURRET)
- {
- if(trace_ent.health <= trace_ent.tur_health && autocvar_g_vehicle_bumblebee_healgun_hps)
- trace_ent.health = min(trace_ent.health + autocvar_g_vehicle_bumblebee_healgun_hps * frametime, trace_ent.tur_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 bumb_think()
-{
- self.movetype = MOVETYPE_TOSS;
-
- //self.velocity = self.velocity * 0.5;
- self.angles_z *= 0.8;
- self.angles_x *= 0.8;
-
- self.nextthink = time + 0.05;
-
- 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;
- }
-
- 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;
- }
- }
-
-}
-
-void bumb_enter()
-{
- self.touch = bumb_touch;
- self.nextthink = 0;
- self.movetype = MOVETYPE_BOUNCEMISSILE;
- //setattachment(self.owner, self.vehicle_viewport, "");
-}
-
-void bumb_exit(float eject)
-{
- self.touch = vehicles_touch;
- self.think = bumb_think;
- self.nextthink = time;
-
- 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 * 200;
- else
- spot = self.origin + v_up * 128 - v_forward * 200;
-
- 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 bumb_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, "weapons/rocket_impact.wav", VOL_BASE, ATTEN_NORM);
- pointparticles(particleeffectnum("explosion_large"), randomvec() * 80 + (self.origin + '0 0 100'), '0 0 0', 1);
-
- if(self.owner.deadflag == DEAD_DYING)
- self.owner.deadflag = DEAD_DEAD;
-
- remove(self);
-}
-
-void bumb_diethink()
-{
- if(time >= self.wait)
- self.think = bumb_blowup;
-
- if(random() < 0.1)
- {
- sound(self, CH_SHOTS, "weapons/rocket_impact.wav", VOL_BASE, ATTEN_NORM);
- pointparticles(particleeffectnum("explosion_small"), randomvec() * 80 + (self.origin + '0 0 100'), '0 0 0', 1);
- }
-
- self.nextthink = time + 0.1;
-}
-
-void bumb_die()
-{
- entity oldself = self;
-
- // 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 = bumb_blowup;
- else
- _body.touch = func_null;
-
- _body.think = bumb_diethink;
- _body.nextthink = time;
- _body.wait = time + 2 + (random() * 8);
- _body.owner = self;
- _body.enemy = self.enemy;
-
- pointparticles(particleeffectnum("explosion_medium"), findbetterlocation(self.origin, 16), '0 0 0', 1);
-
- self.health = 0;
- self.event_damage = func_null;
- self.solid = SOLID_CORPSE;
- 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);
-}
-
-void bumb_impact()
-{
- if(autocvar_g_vehicle_bumblebee_bouncepain_x)
- vehicles_impact(autocvar_g_vehicle_bumblebee_bouncepain_x, autocvar_g_vehicle_bumblebee_bouncepain_y, autocvar_g_vehicle_bumblebee_bouncepain_z);
-}
-
-void bumb_spawn(float _f)
-{
- /*
- float i;
- for(i=1; gettaginfo(self.gun1, i), gettaginfo_name; ++i)
- {
-
- dprint(" ------- ^1gettaginfo_name^2(",ftos(i),") ^3=", gettaginfo_name, "\n");
- }
- */
- if(!self.gun1)
- {
- // for some reason, autosizing of the shiled 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", bumb_gunner_frame, bumb_gunner_exit);
- vehicle_addplayerslot(self, self.gun2, HUD_BUMBLEBEE_GUN, "models/vehicles/wakizashi_cockpit.dpm", bumb_gunner_frame, bumb_gunner_exit);
-
- 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);
- 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_BOUNCEMISSILE;
- self.movetype = MOVETYPE_TOSS;
- self.vehicle_impact = bumb_impact;
- self.damageforcescale = 0.025;
-
- setorigin(self, self.origin + '0 0 25');
-}
-
-void spawnfunc_vehicle_bumblebee()
-{
- if(!autocvar_g_vehicle_bumblebee)
- {
- remove(self);
- return;
- }
-
- 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");
-
- 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;
-
- if(!vehicle_initialize(
- "Bumblebee", "models/vehicles/bumblebee_body.dpm",
- "", "models/vehicles/spiderbot_cockpit.dpm", "", "", "tag_viewport",
- HUD_BUMBLEBEE, BUMB_MIN, BUMB_MAX, FALSE,
- bumb_spawn, autocvar_g_vehicle_bumblebee_respawntime,
- bumb_pilot_frame, bumb_enter, bumb_exit,
- bumb_die, bumb_think, FALSE, autocvar_g_vehicle_bumblebee_health, autocvar_g_vehicle_bumblebee_shield))
- {
- remove(self);
- return;
- }
-}
-
-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;
-}
-#endif // SVQC
-
-#ifdef CSQC
-/*
-.vector raygun_l1
-.vector raygun_l2;
-.vector raygun_l3;
-*/
-
-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)
-{
- float 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();
- }
-}
-
-void bumblebee_draw()
-{
-
-}
-
-void bumblebee_draw2d()
-{
-
-}
-
-void bumblebee_read_extra()
-{
-
-}
-
-void vehicle_bumblebee_assemble()
-{
-
-}
-#endif //CSQC
+++ /dev/null
-#define RACER_MIN '-120 -120 -40'
-#define RACER_MAX '120 120 40'
-
-#ifdef SVQC
-void racer_exit(float eject);
-void racer_enter();
-
-// Auto cvars
-float autocvar_g_vehicle_racer;
-
-float autocvar_g_vehicle_racer_speed_afterburn;
-float autocvar_g_vehicle_racer_afterburn_cost;
-
-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;
-
-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;
-
- // 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", "weapons/lasergun_fire.wav",
- 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);
-
- // 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
- }
-
- 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", "weapons/rocket_fire.wav",
- 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);
-
- 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)
- return 1;
-
- player = self;
- racer = self.vehicle;
- self = racer;
-
- player.BUTTON_ZOOM = player.BUTTON_CROUCH = 0;
-
- vehicles_painframe();
-
- if(racer.deadflag != DEAD_NO)
- {
- self = player;
- player.BUTTON_ATCK = player.BUTTON_ATCK2 = 0;
- return 1;
- }
-
- racer_align4point(frametime);
-
- 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(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;
- 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(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(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);
-
- 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);
-
- 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);
-
-
- 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_NORMAL, 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);
-
- 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);
-}
-
-void racer_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');
-
- //targetdrone_spawn(self.origin + '0 0 512' + randomvec() * 256, 1);
-}
-
-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_impact()
-{
- if(autocvar_g_vehicle_racer_bouncepain_x)
- vehicles_impact(autocvar_g_vehicle_racer_bouncepain_x, autocvar_g_vehicle_racer_bouncepain_y, autocvar_g_vehicle_racer_bouncepain_z);
-}
-
-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.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 racer_deadtouch()
-{
- self.avelocity_x *= 0.7;
- self.cnt -= 1;
- if(self.cnt <= 0)
- racer_blowup();
-}
-
-void racer_die()
-{
- 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.cnt = 1 + random() * 2;
- self.touch = racer_deadtouch;
-
- pointparticles(particleeffectnum("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;
- self.nextthink = 2 + time + random() * 3;
-}
-void racer_spawn(float _spawnflag)
-{
- 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;
-
- setsize(self, RACER_MIN * 0.5, RACER_MAX * 0.5);
- self.bouncefactor = autocvar_g_vehicle_racer_bouncefactor;
- self.bouncestop = autocvar_g_vehicle_racer_bouncestop;
- self.vehicle_impact = racer_impact;
- self.damageforcescale = 0.5;
- //self.destvec = autocvar_g_vehicle_racer_bouncepain;
-}
-
-void spawnfunc_vehicle_racer()
-{
- if(!autocvar_g_vehicle_racer)
- {
- remove(self);
- return;
- }
-
- self.vehicle_flags |= VHF_DMGSHAKE;
- self.vehicle_flags |= VHF_DMGROLL;
-
- precache_sound ("weapons/lasergun_fire.wav");
- precache_sound ("weapons/rocket_fire.wav");
-
- 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");
-
- 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;
-
- if(!vehicle_initialize(
- "Wakizashi",
- "models/vehicles/wakizashi.dpm",
- "null", // we need this so tur_head is networked and usable for sounds
- "models/vehicles/wakizashi_cockpit.dpm",
- "", "", "tag_viewport",
- HUD_WAKIZASHI,
- 0.5 * RACER_MIN, 0.5 * RACER_MAX,
- FALSE,
- racer_spawn, autocvar_g_vehicle_racer_respawntime,
- racer_frame,
- racer_enter, racer_exit,
- racer_die, racer_think,
- TRUE,
- autocvar_g_vehicle_racer_health,
- autocvar_g_vehicle_racer_shield))
- {
- remove(self);
- return;
- }
-}
-#endif // SVQC
+++ /dev/null
-#define RSM_FIRST 0
-#define RSM_BOMB 0
-#define RSM_FLARE 1
-#define RSM_LAST 1
-
-#define RAPTOR_MIN '-80 -80 0'
-#define RAPTOR_MAX '80 80 70'
-
-#ifdef SVQC
-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_blowup_radius;
-float autocvar_g_vehicle_raptor_blowup_coredamage;
-float autocvar_g_vehicle_raptor_blowup_edgedamage;
-float autocvar_g_vehicle_raptor_blowup_forceintensity;
-
-float autocvar_g_vehicle_raptor_bouncefactor;
-float autocvar_g_vehicle_raptor_bouncestop;
-vector autocvar_g_vehicle_raptor_bouncepain;
-
-void raptor_spawn(float);
-float raptor_frame();
-float raptor_takeoff();
-
-.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);
- }
-
- 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);
-}
-
-
-void raptor_fire_cannon(entity gun, string tagname)
-{
- vehicles_projectile("raptor_cannon_muzzleflash", "weapons/lasergun_fire.wav",
- 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);
-}
-
-void raptor_think()
-{
-}
-
-void raptor_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);
-}
-
-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 = raptor_think;
- self.frame = 0;
- }
-
- self.nextthink = time;
-}
-
-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;
-}
-
-float raptor_takeoff()
-{
- entity player, raptor;
-
- player = self;
- raptor = self.vehicle;
- self = raptor;
- 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);
-
- 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);
-
- 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);
-
-
- 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_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)
- 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');
-
- 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 i, 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;
- for(i = 0; i < 4; ++i)
- {
- 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(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);
-
- 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);
-
- 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);
-
- 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;
-}
-
-void raptor_blowup()
-{
- self.deadflag = DEAD_DEAD;
- self.vehicle_exit(VHEF_NORMAL);
-
- RadiusDamage(self, self.enemy, autocvar_g_vehicle_raptor_blowup_coredamage,
- autocvar_g_vehicle_raptor_blowup_edgedamage,
- autocvar_g_vehicle_raptor_blowup_radius, world, world,
- autocvar_g_vehicle_raptor_blowup_forceintensity, 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.1)
- {
- sound (self, CH_SHOTS, "weapons/rocket_impact.wav", VOL_BASE, ATTEN_NORM);
- pointparticles(particleeffectnum("explosion_small"), randomvec() * 80 + (self.origin + '0 0 100'), '0 0 0', 1);
- }
- self.nextthink = time + 0.1;
-}
-
-void raptor_die()
-{
- 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);
-
- pointparticles(particleeffectnum("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;
-}
-
-void raptor_impact()
-{
- if(autocvar_g_vehicle_raptor_bouncepain_x)
- vehicles_impact(autocvar_g_vehicle_raptor_bouncepain_x, autocvar_g_vehicle_raptor_bouncepain_y, autocvar_g_vehicle_raptor_bouncepain_z);
-}
-
-// 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;
- 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;
-
- /*
- case 17: // toss gun, could be used to exit?
- break;
- case 20: // Manual minigun reload?
- break;
- */
- }
- return FALSE;
-}
-
-void raptor_spawn(float _f)
-{
- 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.bomb1.gun1.avelocity_y = 90;
- self.bomb1.gun2.avelocity_y = -90;
-
- setsize(self, RAPTOR_MIN, RAPTOR_MAX );
- self.delay = time;
-
- self.bouncefactor = autocvar_g_vehicle_raptor_bouncefactor;
- self.bouncestop = autocvar_g_vehicle_raptor_bouncestop;
- self.vehicle_impact = raptor_impact;
- self.damageforcescale = 0.25;
-}
-
-void spawnfunc_vehicle_raptor()
-{
- if(!autocvar_g_vehicle_raptor)
- {
- remove(self);
- return;
- }
-
- self.vehicle_flags |= VHF_DMGSHAKE;
- self.vehicle_flags |= VHF_DMGROLL;
-
- 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;
-
- 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.md3");
- 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");
-
- if(!vehicle_initialize(
- "Raptor",
- "models/vehicles/raptor.dpm",
- "",
- "models/vehicles/raptor_cockpit.dpm",
- "", "tag_hud", "tag_camera",
- HUD_RAPTOR,
- RAPTOR_MIN, RAPTOR_MAX,
- FALSE,
- raptor_spawn, autocvar_g_vehicle_raptor_respawntime,
- raptor_frame,
- raptor_enter, raptor_exit,
- raptor_die, raptor_think,
- FALSE,
- autocvar_g_vehicle_raptor_health,
- autocvar_g_vehicle_raptor_shield))
- {
- remove(self);
- return;
- }
-
-
-}
-#endif // SVQC
+++ /dev/null
-const vector SPIDERBOT_MIN = '-75 -75 10';
-const vector SPIDERBOT_MAX = '75 75 125';
-
-#ifdef SVQC
-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_energy;
-//float autocvar_g_vehicle_spiderbot_energy_regen;
-//float autocvar_g_vehicle_spiderbot_energy_regen_pause;
-
-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;
-
-float autocvar_g_vehicle_spiderbot_blowup_radius;
-float autocvar_g_vehicle_spiderbot_blowup_coredamage;
-float autocvar_g_vehicle_spiderbot_blowup_edgedamage;
-float autocvar_g_vehicle_spiderbot_blowup_forceintensity;
-
-vector autocvar_g_vehicle_spiderbot_bouncepain;
-
-
-void spiderbot_exit(float eject);
-void spiderbot_enter();
-void spiderbot_spawn(float);
-#define SBRM_FIRST 0
-#define SBRM_VOLLY 0
-#define SBRM_GUIDE 1
-#define SBRM_ARTILLERY 2
-#define SBRM_LAST 2
-
-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;
-
-
- v = gettaginfo(self.tur_head,gettagindex(self.tur_head,"tag_fire"));
-
- switch(self.vehicle_weapon2mode)
- {
- case SBRM_VOLLY:
- rocket = vehicles_projectile("spiderbot_rocket_launch", "weapons/rocket_fire.wav",
- 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);
- 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", "weapons/rocket_fire.wav",
- 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);
- 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", "weapons/rocket_fire.wav",
- 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);
-
- 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_aiframe()
-{
- return FALSE;
-}
-
-float spiderbot_frame()
-{
- vector ad, vf;
- entity player, spider;
- float ftmp;
-
- if(intermission_running)
- return 1;
-
- player = self;
- spider = self.vehicle;
- self = spider;
-
- vehicles_painframe();
-
- player.BUTTON_ZOOM = 0;
- player.BUTTON_CROUCH = 0;
- player.switchweapon = 0;
-
-
-#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);
- //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(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;
-
- (spider.misc_bulletcounter % 2) ? gun = spider.gun1 : gun = 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);
-
-// fireBullet (v, v_forward, autocvar_g_vehicle_spiderbot_minigun_spread, autocvar_g_vehicle_spiderbot_minigun_damage,
-// autocvar_g_vehicle_spiderbot_minigun_spread, DEATH_VH_SPID_MINIGUN, 0);
-
- sound (gun, CH_WEAPON_A, "weapons/uzi_fire.wav", 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);
-
-
- 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);
-
- 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);
-
- 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' * SPIDERBOT_MAX_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_think()
-{
- if(self.flags & FL_ONGROUND)
- movelib_beak_simple(autocvar_g_vehicle_spiderbot_speed_stop);
-
- self.nextthink = time;
-}
-
-void spiderbot_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');
- }
-}
-
-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.velocity = '0 0 0';
- self.think = spiderbot_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 spider_impact()
-{
- if(autocvar_g_vehicle_spiderbot_bouncepain_x)
- vehicles_impact(autocvar_g_vehicle_spiderbot_bouncepain_x, autocvar_g_vehicle_spiderbot_bouncepain_y, autocvar_g_vehicle_spiderbot_bouncepain_z);
-}
-
-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, "weapons/rocket_impact.wav", VOL_BASE, ATTEN_NORM);
- pointparticles(particleeffectnum("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, "weapons/rocket_impact.wav", VOL_BASE, ATTEN_NORM);
- pointparticles(particleeffectnum("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(autocvar_g_vehicle_spiderbot_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(autocvar_g_vehicle_spiderbot_respawntime, 1));
- //SUB_SetFade(h, time, min(autocvar_g_vehicle_spiderbot_respawntime, 10));
- SUB_SetFade(g1, time, min(autocvar_g_vehicle_spiderbot_respawntime, 10));
- SUB_SetFade(g2, time, min(autocvar_g_vehicle_spiderbot_respawntime, 10));
-
- RadiusDamage(self, self.enemy, autocvar_g_vehicle_spiderbot_blowup_coredamage,
- autocvar_g_vehicle_spiderbot_blowup_edgedamage,
- autocvar_g_vehicle_spiderbot_blowup_radius, world, world,
- autocvar_g_vehicle_spiderbot_blowup_forceintensity, 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;
- setorigin(self, self.pos1);
-}
-
-void spiderbot_die()
-{
- 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;
-}
-
-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;
- 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;
-
- /*
- case 17: // toss gun, could be used to exit?
- break;
- case 20: // Manual minigun reload?
- break;
- */
- }
- return FALSE;
-}
-
-void spiderbot_spawn(float _f)
-{
- 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.think = spiderbot_think;
- self.nextthink = time;
- self.vehicle_health = autocvar_g_vehicle_spiderbot_health;
- self.vehicle_shield = autocvar_g_vehicle_spiderbot_shield;
- 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';
-
- setorigin(self, self.pos1 + '0 0 128');
- self.angles = self.pos2;
- self.vehicle_impact = spider_impact;
- self.damageforcescale = 0.03;
-}
-
-void spawnfunc_vehicle_spiderbot()
-{
- if(!autocvar_g_vehicle_spiderbot)
- {
- remove(self);
- return;
- }
-
- self.vehicle_flags |= VHF_DMGSHAKE;
- //self.vehicle_flags |= VHF_DMGROLL;
- //self.vehicle_flags |= VHF_DMGHEADROLL;
-
- 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 ( "weapons/uzi_fire.wav" );
- precache_sound ( "weapons/rocket_impact.wav");
-
- 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");
-
- 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;
-
- if(!vehicle_initialize(
- "Spiderbot",
- "models/vehicles/spiderbot.dpm",
- "models/vehicles/spiderbot_top.dpm",
- "models/vehicles/spiderbot_cockpit.dpm",
- "tag_head", "tag_hud", "",
- HUD_SPIDERBOT,
- SPIDERBOT_MIN, SPIDERBOT_MAX,
- FALSE,
- spiderbot_spawn, autocvar_g_vehicle_spiderbot_respawntime,
- spiderbot_frame,
- spiderbot_enter, spiderbot_exit,
- spiderbot_die, spiderbot_think,
- FALSE,
- autocvar_g_vehicle_spiderbot_health,
- autocvar_g_vehicle_spiderbot_shield))
- {
- remove(self);
- return;
- }
-}
-#endif // SVQC
+++ /dev/null
-float autocvar_g_vehicles_crush_dmg;
-float autocvar_g_vehicles_crush_force;
-float autocvar_g_vehicles_delayspawn;
-float autocvar_g_vehicles_delayspawn_jitter;
-
-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;
-
-float autocvar_g_vehicles;
-
-void vehicles_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force);
-void vehicles_return();
-void vehicles_enter();
-void vehicles_touch();
-void vehicles_reset_colors();
-void vehicles_clearreturn();
-void vehicles_setreturn();
-
-
-/** AuxiliaryXhair*
- Send additional points of interest to be drawn, to vehicle owner
-**/
-const float MAX_AXH = 4;
-.entity AuxiliaryXhair[MAX_AXH];
-
-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;
-}
-
-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);
- }
-
- setorigin(axh, loc);
- axh.colormod = clr;
- axh.SendFlags = 0x01;
- own.(AuxiliaryXhair[axh_id]) = axh;
-}
-
-/*
-// SVC_TEMPENTITY based, horrible with even 50 ping. hm.
-// WriteByte(MSG_ONE, SVC_TEMPENTITY) uses reliable messagess, never use for thinsg that need continous updates.
-void SendAuxiliaryXhair2(entity own, vector loc, vector clr, float axh_id)
-{
- msgexntity = own;
-
- WriteByte(MSG_ONE, SVC_TEMPENTITY);
- WriteByte(MSG_ONE, TE_CSQC_AUXILIARYXHAIR);
-
- WriteByte(MSG_ONE, axh_id);
-
- WriteCoord(MSG_ONE, loc_x);
- WriteCoord(MSG_ONE, loc_y);
- WriteCoord(MSG_ONE, loc_z);
-
- WriteByte(MSG_ONE, rint(clr_x * 255));
- WriteByte(MSG_ONE, rint(clr_y * 255));
- WriteByte(MSG_ONE, rint(clr_z * 255));
-
-}
-*/
-// End AuxiliaryXhair
-
-/**
- Notifies the client that he enterd a vehicle, and sends
- realavent data.
-
- only sends vehicle_id atm (wich is a HUD_* constant, ex. HUD_SPIDERBOT)
-**/
-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);
- if(vehicle_id != 0)
- WriteByte(MSG_ONE, vehicle_id);
- else
- WriteByte(MSG_ONE, 1 + own.vehicle.vehicle_weapon2mode + HUD_VEHICLE_LAST);
-}
-
-/** vehicles_locktarget
-
- Generic target locking.
-
- Figure out if what target is "locked" (if any), for missile tracking as such.
-
- after calling, "if(self.lock_target != world && self.lock_strength == 1)" mean
- you have a locked in target.
-
- Exspects a crosshair_trace() or equivalent to be
- dont before calling.
-
-**/
-.entity lock_target;
-.float lock_strength;
-.float lock_time;
-.float lock_soundtime;
-const float DAMAGE_TARGETDRONE = 10;
-
-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;
-}
-
-#if 0
-void targetdrone_think();
-void targetdrone_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force);
-void targetdrone_renwe()
-{
- self.think = targetdrone_think;
- self.nextthink = time + 0.1;
- setorigin(self, targetdrone_getnewspot());
- self.health = 200;
- self.takedamage = DAMAGE_TARGETDRONE;
- self.event_damage = targetdrone_damage;
- self.solid = SOLID_BBOX;
- setmodel(self, "models/runematch/rune.mdl");
- self.effects = EF_LOWPRECISION;
- self.scale = 10;
- self.movetype = MOVETYPE_BOUNCEMISSILE;
- setsize(self, '-100 -100 -100', '100 100 100');
-
-}
-void targetdrone_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
-{
- self.health -= damage;
- if(self.health <= 0)
- {
- pointparticles(particleeffectnum("explosion_medium"), self.origin, '0 0 0', 1);
-
- if(!self.cnt)
- remove(self);
- else
- {
- self.think = targetdrone_renwe;
- self.nextthink = time + 1 + random() * 2;
- self.solid = SOLID_NOT;
- setmodel(self, "");
- }
- }
-}
-entity targetdrone_getfear()
-{
- entity fear;
- float i;
-
- for(i = 64; i <= 1024; i += 64)
- {
- fear = findradius(self.origin, i);
- while(fear)
- {
- if(fear.bot_dodge)
- return fear;
-
- fear = fear.chain;
- }
- }
-
- return world;
-}
-void targetdrone_think()
-{
- self.nextthink = time + 0.1;
-
- if(self.wp00)
- if(self.wp00.deadflag != DEAD_NO)
- self.wp00 = targetdrone_getfear();
-
- if(!self.wp00)
- self.wp00 = targetdrone_getfear();
-
- vector newdir;
-
- if(self.wp00)
- newdir = steerlib_push(self.wp00.origin) + randomvec() * 0.75;
- else
- newdir = randomvec() * 0.75;
-
- newdir = newdir * 0.5 + normalize(self.velocity) * 0.5;
-
- if(self.wp00)
- self.velocity = normalize(newdir) * (500 + (1024 / min(vlen(self.wp00.origin - self.origin), 1024)) * 700);
- else
- self.velocity = normalize(newdir) * 750;
-
- tracebox(self.origin, self.mins, self.maxs, self.origin + self.velocity * 2, MOVE_NORMAL, self);
- if(trace_fraction != 1.0)
- self.velocity = self.velocity * -1;
-
- //normalize((normalize(self.velocity) * 0.5 + newdir * 0.5)) * 750;
-}
-
-void targetdrone_spawn(vector _where, float _autorenew)
-{
- entity drone = spawn();
- setorigin(drone, _where);
- drone.think = targetdrone_renwe;
- drone.nextthink = time + 0.1;
- drone.cnt = _autorenew;
-}
-#endif
-
-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(teamplay && trace_ent.team == self.team)
- trace_ent = world;
-
- if(trace_ent.deadflag != DEAD_NO)
- trace_ent = world;
-
- if(!trace_ent.vehicle_flags & VHF_ISVEHICLE ||
- trace_ent.turrcaps_flags & TFL_TURRCAPS_ISTURRET ||
- trace_ent.takedamage == DAMAGE_TARGETDRONE)
- trace_ent = world;
- }
-
- 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;
- }
-}
-
-#define VEHICLE_UPDATE_PLAYER(ply,fld,vhname) \
-ply.vehicle_##fld = (self.vehicle_##fld / autocvar_g_vehicle_##vhname##_##fld) * 100
-
-#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)
-
-// Hover movement support
-float force_fromtag_power;
-float force_fromtag_normpower;
-vector force_fromtag_origin;
-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;
-}
-
-// Experimental hovermode wich uses attraction/repulstion from surface insted of gravity/repulsion
-// Can possibly be use to move abt any surface (inclusing walls/celings)
-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;
-}
-
-// Generic vehile projectile system
-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_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;
-}
-// End generic vehile projectile system
-
-void vehicles_reset()
-{
- if(self.owner)
- {
- entity oldself = self;
- self = self.owner;
- vehicles_exit(VHEF_RELESE);
- self = oldself;
- }
- 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';
- self.event_damage = func_null;
- self.solid = SOLID_NOT;
- self.deadflag = DEAD_NO;
-
- self.touch = func_null;
- self.nextthink = 0;
- vehicles_setreturn();
-}
-
-/** vehicles_spawn
- Exetuted for all vehicles on (re)spawn.
- Sets defaults for newly spawned units.
-**/
-void vehicles_spawn()
-{
- dprint("Spawning vehicle: ", self.netname, "\n");
-
- // De-own & reset
- self.vehicle_hudmodel.viewmodelforclient = self;
-
- self.owner = world;
- self.touch = vehicles_touch;
- self.event_damage = vehicles_damage;
- self.reset = vehicles_reset;
- 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.flags = FL_NOTARGET;
- self.avelocity = '0 0 0';
- self.velocity = '0 0 0';
-
- // 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 + '0 0 0');
- // Show it
- pointparticles(particleeffectnum("teleport"), self.origin + '0 0 64', '0 0 0', 1);
-
- if(self.vehicle_controller)
- self.team = self.vehicle_controller.team;
-
- vehicles_reset_colors();
- self.vehicle_spawn(VHSF_NORMAL);
-}
-
-// Better way of determening whats crushable needed! (fl_crushable?)
-float vehicles_crushable(entity e)
-{
- if(IS_PLAYER(e))
- return TRUE;
-
- if(e.flags & FL_MONSTER)
- return TRUE;
-
- 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");
- }
- }
-}
-
-.void() vehicle_impact;
-void vehicles_touch()
-{
- if(MUTATOR_CALLHOOK(VehicleTouch))
- return;
-
- // Vehicle currently in use
- if(self.owner)
- {
- if(other != world)
- 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)
- if(self.vehicle_impact)
- self.vehicle_impact();
-
- return;
- }
-
- if (!IS_PLAYER(other))
- return;
-
- if(other.deadflag != DEAD_NO)
- return;
-
- if(other.vehicle != world)
- return;
-
- vehicles_enter();
-}
-var float autocvar_g_vehicles_allow_bots = 0;
-void vehicles_enter()
-{
- // Remove this when bots know how to use vehicles
-
- if (IS_BOT_CLIENT(other))
- 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(self.phase > time)
- return;
- if(other.frozen)
- return;
- if(other.vehicle)
- return;
- if(other.deadflag != DEAD_NO)
- return;
-
- if(teamplay)
- if(self.team)
- if(self.team != other.team)
- return;
-
- RemoveGrapplingHook(other);
-
- self.vehicle_ammo1 = 0;
- self.vehicle_ammo2 = 0;
- self.vehicle_reload1 = 0;
- self.vehicle_reload2 = 0;
- self.vehicle_energy = 0;
-
- self.owner = other;
- self.switchweapon = other.switchweapon;
-
- // .viewmodelforclient works better.
- //self.vehicle_hudmodel.drawonlytoclient = self.owner;
-
- self.vehicle_hudmodel.viewmodelforclient = self.owner;
-
- self.event_damage = vehicles_damage;
- self.nextthink = 0;
- self.owner.angles = self.angles;
- self.owner.takedamage = DAMAGE_NO;
- self.owner.solid = SOLID_NOT;
- self.owner.movetype = MOVETYPE_NOCLIP;
- self.owner.alpha = -1;
- self.owner.vehicle = self;
- self.owner.event_damage = func_null;
- self.owner.view_ofs = '0 0 0';
- self.colormap = self.owner.colormap;
- if(self.tur_head)
- self.tur_head.colormap = self.owner.colormap;
-
- self.owner.hud = self.hud;
- self.owner.PlayerPhysplug = self.PlayerPhysplug;
-
- self.owner.vehicle_ammo1 = self.vehicle_ammo1;
- self.owner.vehicle_ammo2 = self.vehicle_ammo2;
- self.owner.vehicle_reload1 = self.vehicle_reload1;
- self.owner.vehicle_reload2 = self.vehicle_reload2;
-
- // Cant do this, hides attached objects too.
- //self.exteriormodeltoclient = self.owner;
- //self.tur_head.exteriormodeltoclient = self.owner;
-
- other.flags &= ~FL_ONGROUND;
- self.flags &= ~FL_ONGROUND;
-
- self.team = self.owner.team;
- self.flags -= FL_NOTARGET;
- self.monster_attack = TRUE;
-
- if (IS_REAL_CLIENT(other))
- {
- msg_entity = other;
- WriteByte (MSG_ONE, SVC_SETVIEWPORT);
- WriteEntity(MSG_ONE, self.vehicle_viewport);
-
- WriteByte (MSG_ONE, SVC_SETVIEWANGLES);
- if(self.tur_head)
- {
- WriteAngle(MSG_ONE, self.tur_head.angles_x + self.angles_x); // tilt
- WriteAngle(MSG_ONE, self.tur_head.angles_y + self.angles_y); // yaw
- WriteAngle(MSG_ONE, 0); // roll
- }
- else
- {
- WriteAngle(MSG_ONE, self.angles_x * -1); // tilt
- WriteAngle(MSG_ONE, self.angles_y); // yaw
- WriteAngle(MSG_ONE, 0); // roll
- }
- }
-
- vehicles_clearreturn();
-
- CSQCVehicleSetup(self.owner, self.hud);
-
- vh_player = other;
- vh_vehicle = self;
- MUTATOR_CALLHOOK(VehicleEnter);
- other = vh_player;
- self = vh_vehicle;
-
- self.vehicle_enter();
- antilag_clear(other);
-}
-
-/** vehicles_findgoodexit
- Locates a valid location for the player to exit the vehicle.
- Will first try prefer_spot, then up 100 random spots arround the vehicle
- wich are in direct line of sight and empty enougth to hold a players bbox
-**/
-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;
-}
-
-/** vehicles_exit
- Standarrd vehicle release fucntion.
- custom code goes in self.vehicle_exit
-**/
-float vehicles_exit_running;
-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;
- if(IS_CLIENT(self))
- {
- _vehicle = self.vehicle;
-
- if (_vehicle.vehicle_flags & VHF_PLAYERSLOT)
- {
- _vehicle.vehicle_exit(eject);
- self = _oldself;
- 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.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;
-
- 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;
-
- 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.monster_attack = FALSE;
-
- _vehicle.vehicle_exit(eject);
-
- vehicles_setreturn();
- vehicles_reset_colors();
- _vehicle.owner = world;
- self = _oldself;
-
- vehicles_exit_running = FALSE;
-}
-
-
-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.tur_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;
-
- 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_RELESE);
-
-
- antilag_clear(self);
-
- self.vehicle_die();
- vehicles_setreturn();
- }
-}
-
-void vehicles_clearreturn()
-{
- entity ret;
- // Remove "return helper", if any.
- ret = findchain(classname, "vehicle_return");
- while(ret)
- {
- if(ret.wp00 == self)
- {
- 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_return()
-{
- pointparticles(particleeffectnum("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);
- 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 ret;
-
- vehicles_clearreturn();
-
- ret = spawn();
- ret.classname = "vehicle_return";
- ret.wp00 = self;
- ret.team = self.team;
- ret.think = vehicles_showwp;
-
- if(self.deadflag != DEAD_NO)
- {
- ret.cnt = max(game_starttime, time) + self.vehicle_respawntime;
- ret.nextthink = max(game_starttime, time) + max(0, self.vehicle_respawntime - 5);
- }
- else
- ret.nextthink = max(game_starttime, time) + max(0, self.vehicle_respawntime - 1);
-
- setmodel(ret, "null");
- setorigin(ret, self.pos1 + '0 0 96');
-}
-
-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;
- }
-
- 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 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)
- {
- dprint("^3Eat shit yall!\n");
- vehicles_setreturn();
- vehicles_reset_colors();
- }
- else if(self.active == ACTIVE_NOT && self.deadflag != DEAD_NO)
- {
-
- }
-}
-
-float vehicle_addplayerslot( entity _owner,
- entity _slot,
- float _hud,
- string _hud_model,
- float() _framefunc,
- void(float) _exitfunc)
-{
- if (!(_owner.vehicle_flags & VHF_MULTISLOT))
- _owner.vehicle_flags |= VHF_MULTISLOT;
-
- _slot.PlayerPhysplug = _framefunc;
- _slot.vehicle_exit = _exitfunc;
- _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;
-}
-
-float vehicle_initialize(string net_name,
- string bodymodel,
- string topmodel,
- string hudmodel,
- string toptag,
- string hudtag,
- string viewtag,
- float vhud,
- vector min_s,
- vector max_s,
- float nodrop,
- void(float _spawnflag) spawnproc,
- float _respawntime,
- float() physproc,
- void() enterproc,
- void(float extflag) exitfunc,
- void() dieproc,
- void() thinkproc,
- float use_csqc,
- float _max_health,
- float _max_shield)
-{
- if(!autocvar_g_vehicles)
- return FALSE;
-
- if(self.targetname)
- {
- self.vehicle_controller = find(world, target, self.targetname);
- if(!self.vehicle_controller)
- {
- bprint("^1WARNING: ^7Vehicle with invalid .targetname\n");
- }
- 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;
- }
- }
- }
-
- precache_sound("onslaught/ons_hit2.wav");
- precache_sound("onslaught/electricity_explode.wav");
-
-
- addstat(STAT_HUD, AS_INT, hud);
- addstat(STAT_VEHICLESTAT_HEALTH, AS_INT, vehicle_health);
- addstat(STAT_VEHICLESTAT_SHIELD, AS_INT, vehicle_shield);
- addstat(STAT_VEHICLESTAT_ENERGY, AS_INT, vehicle_energy);
-
- addstat(STAT_VEHICLESTAT_AMMO1, AS_INT, vehicle_ammo1);
- addstat(STAT_VEHICLESTAT_RELOAD1, AS_INT, vehicle_reload1);
-
- addstat(STAT_VEHICLESTAT_AMMO2, AS_INT, vehicle_ammo2);
- addstat(STAT_VEHICLESTAT_RELOAD2, AS_INT, vehicle_reload2);
-
- if(bodymodel == "")
- error("vehicles: missing bodymodel!");
-
- if(hudmodel == "")
- error("vehicles: missing hudmodel!");
-
- if(net_name == "")
- self.netname = self.classname;
- else
- self.netname = net_name;
-
- if(self.team && !teamplay)
- self.team = 0;
-
- self.vehicle_flags |= VHF_ISVEHICLE;
-
- setmodel(self, bodymodel);
-
- self.vehicle_viewport = spawn();
- self.vehicle_hudmodel = spawn();
- self.tur_head = spawn();
- self.tur_head.owner = self;
- self.takedamage = DAMAGE_AIM;
- self.bot_attack = TRUE;
- self.iscreature = TRUE;
- self.teleportable = FALSE; // no teleporting for vehicles, too buggy
- self.damagedbycontents = TRUE;
- self.hud = vhud;
- self.tur_health = _max_health;
- self.tur_head.tur_health = _max_shield;
- self.vehicle_die = dieproc;
- self.vehicle_exit = exitfunc;
- self.vehicle_enter = enterproc;
- self.PlayerPhysplug = physproc;
- self.event_damage = func_null;
- self.touch = vehicles_touch;
- self.think = vehicles_spawn;
- self.vehicle_spawn = spawnproc;
- self.vehicle_respawntime = max(0, _respawntime);
- self.effects = EF_NODRAW;
- self.dphitcontentsmask = DPCONTENTS_BODY | DPCONTENTS_SOLID;
- if(!autocvar_g_vehicles_delayspawn || !self.vehicle_respawntime)
- self.nextthink = time;
- else
- self.nextthink = max(time, game_starttime) + max(0, self.vehicle_respawntime + ((random() * 2 - 1) * autocvar_g_vehicles_delayspawn_jitter));
-
- if(autocvar_g_playerclip_collisions)
- self.dphitcontentsmask |= DPCONTENTS_PLAYERCLIP;
-
- if(autocvar_g_nodepthtestplayers)
- self.effects = self.effects | EF_NODEPTHTEST;
-
- if(autocvar_g_fullbrightplayers)
- self.effects = self.effects | EF_FULLBRIGHT;
-
- setmodel(self.vehicle_hudmodel, hudmodel);
- setmodel(self.vehicle_viewport, "null");
-
- if(topmodel != "")
- {
- setmodel(self.tur_head, topmodel);
- setattachment(self.tur_head, self, toptag);
- setattachment(self.vehicle_hudmodel, self.tur_head, hudtag);
- setattachment(self.vehicle_viewport, self.vehicle_hudmodel, viewtag);
- }
- else
- {
- setattachment(self.tur_head, self, "");
- setattachment(self.vehicle_hudmodel, self, hudtag);
- setattachment(self.vehicle_viewport, self.vehicle_hudmodel, viewtag);
- }
-
- setsize(self, min_s, max_s);
- if (!nodrop)
- {
- setorigin(self, self.origin);
- tracebox(self.origin + '0 0 100', min_s, max_s, 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;
-
- if(MUTATOR_CALLHOOK(VehicleSpawn))
- return FALSE;
-
- 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);
- 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_gib_explode()
-{
- sound (self, CH_SHOTS, "weapons/rocket_impact.wav", VOL_BASE, ATTEN_NORM);
- pointparticles(particleeffectnum("explosion_small"), randomvec() * 80 + (self.origin + '0 0 100'), '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;
-}
-
-/*
-vector predict_target(entity _targ, vector _from, float _shot_speed)
-{
- float i; // loop
- float _distance; // How far to target
- float _impact_time; // How long untill projectile impacts
- vector _predict_pos; // Predicted enemy location
- vector _original_origin;// Where target is before predicted
-
- _original_origin = real_origin(_targ); // Typicaly center of target BBOX
-
- _predict_pos = _original_origin;
- for(i = 0; i < 4; ++i) // Loop a few times to increase prediction accuracy (increase loop count if accuracy is to low)
- {
- _distance = vlen(_predict_pos - _from); // Get distance to previos predicted location
- _impact_time = _distance / _shot_speed; // Calculate impact time
- _predict_pos = _original_origin + _targ.velocity * _impact_time; // Calculate new predicted location
- }
-
- return _predict_pos;
-}
-*/
+++ /dev/null
-#ifdef VEHICLES_ENABLED
-#include "vehicles.qc"
-
-#include "racer.qc"
-#include "spiderbot.qc"
-#include "raptor.qc"
-#ifndef VEHICLES_NO_UNSTABLE
-#include "bumblebee.qc"
-#endif
-#endif
+++ /dev/null
-// #define VEHICLES_USE_ODE
-#define VEHICLES_ENABLED
-#ifdef VEHICLES_ENABLED
-
-.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 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
-
-.float hud;
-.float dmg_time;
-.float vehicle_respawntime;
-//.void() vehicle_spawn;
-
-void vehicles_exit(float eject);
-.void(float exit_flags) vehicle_exit;
-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_RELESE = 2; /// Release ownership, client possibly allready dissconnected / went spec / changed team / used "kill" (not implemented)
-
-const float SVC_SETVIEWPORT = 5; // Net.Protocol 0x05
-const float SVC_SETVIEWANGLES = 10; // Net.Protocol 0x0A
-const float SVC_UPDATEENTITY = 128; // Net.Protocol 0x80
-
-.void() vehicle_enter; /// Vehicles custom funciton to be executed when owner exit it
-.void() vehicle_die; /// Vehicles custom function to be executed when vehile die
-#define VHSF_NORMAL 0
-#define VHSF_FACTORY 2
-.void(float _spawnflag) vehicle_spawn; /// Vehicles custom fucntion to be efecuted when vehicle (re)spawns
-.float(float _imp) vehicles_impulse;
-.float vehicle_weapon2mode = volly_counter;
-
-#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
-#endif // VEHICLES_ENABLED
WriteCoord(MSG_ENTITY, self.fade_time);
WriteCoord(MSG_ENTITY, self.teleport_time);
WriteShort(MSG_ENTITY, self.fade_rate); // maxdist
- float f;
- f = 0;
+ float f = 0;
if(self.currentammo)
f |= 1; // hideable
if(self.exteriormodeltoclient == to)
f |= 2; // my own
+ if(g_onslaught)
+ {
+ if(self.owner.classname == "onslaught_controlpoint")
+ {
+ entity wp_owner = self.owner;
+ entity e = WaypointSprite_getviewentity(to);
+ if(SAME_TEAM(e, wp_owner) && wp_owner.goalentity.health >= wp_owner.goalentity.max_health) { f |= 2; }
+ if(!ons_ControlPoint_Attackable(wp_owner, e.team)) { f |= 2; }
+ }
+ if(self.owner.classname == "onslaught_generator")
+ {
+ entity wp_owner = self.owner;
+ if(wp_owner.isshielded && wp_owner.health >= wp_owner.max_health) { f |= 2; }
+ if(wp_owner.health <= 0) { f |= 2; }
+ }
+ }
WriteByte(MSG_ENTITY, f);
}
self = oldself;
}
-void W_PlayStrengthSound(entity player) // void W_PlayStrengthSound
-{
- if((player.items & IT_STRENGTH)
- && ((time > player.prevstrengthsound + autocvar_sv_strengthsound_antispam_time) // prevent insane sound spam
- || (time > player.prevstrengthsoundattempt + autocvar_sv_strengthsound_antispam_refire_threshold)))
- {
- sound(player, CH_TRIGGER, "weapons/strength_fire.wav", VOL_BASE, ATTEN_NORM);
- player.prevstrengthsound = time;
- }
- player.prevstrengthsoundattempt = time;
-}
-
float W_CheckProjectileDamage(entity inflictor, entity projowner, float deathtype, float exception)
{
float is_from_contents = (deathtype == DEATH_SLIME || deathtype == DEATH_LAVA);
if(IS_CLIENT(attacker) && !autocvar_g_projectiles_keep_owner)
{
+ self.jeff_projowner = self.realowner;
self.owner = attacker;
self.realowner = attacker;
}
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;
org = player.origin + player.view_ofs;
traceline_antilag_force(player, org, org + screenforward * MAX_SHOT_DISTANCE, MOVE_NORMAL, player, lag);
- if(IS_CLIENT(trace_ent) || (trace_ent.flags & FL_MONSTER))
+ if(IS_CLIENT(trace_ent) || IS_MONSTER(trace_ent))
{
antilag_takeback(trace_ent, time - lag);
hitplot = W_HitPlotNormalizedUntransform(org, trace_ent, screenforward, screenright, screenup, trace_endpos);
if (complain)
if(IS_REAL_CLIENT(cl))
{
- play2(cl, "weapons/unavailable.wav");
+ play2(cl, W_Sound("unavailable"));
Send_WeaponComplain (cl, wpn, 0);
}
return FALSE;
Send_WeaponComplain (cl, wpn, 2);
}
- play2(cl, "weapons/unavailable.wav");
+ play2(cl, W_Sound("unavailable"));
}
return FALSE;
}
e.cnt = e.switchweapon;
e.switchweapon = w;
e.selectweapon = w;
+
+#ifdef JEFF
+ e.jeff_weaponswitch_count += 1;
+#endif
}
// perform weapon to attack (weaponstate and attack_finished check is here)
else
self.selectweapon = imp; // update selectweapon ANYWAY
}
- else if(!forbidWeaponUse()) { WEP_ACTION(self.weapon, WR_RELOAD); }
+ else if(!forbidWeaponUse(self)) { WEP_ACTION(self.weapon, WR_RELOAD); }
}
void W_CycleWeapon(string weaponorder, float dir)
if (snd != "")
{
sound (ent, chan, snd, VOL_BASE, ATTN_NORM);
- W_PlayStrengthSound(ent);
}
// nudge w_shotend so a trace to w_shotend hits
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;
+ if(!IS_PLAYER(targ))
+ return FALSE;
+ if(Player_Trapped(targ) || !targ.takedamage)
+ 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 FALSE;
+}
+
// ====================
// Ballistics Tracing
if(f <= 0)
continue;
- snd = strcat("weapons/nexwhoosh", ftos(floor(random() * 3) + 1), ".wav");
+ snd = W_Sound(strcat("nexwhoosh", ftos(floor(random() * 3) + 1)));
if(!pseudoprojectile)
pseudoprojectile = spawn(); // we need this so the sound uses the "entchannel4" volume
f = ExponentialFalloff(mindist, maxdist, halflifedist, ent.railgundistance);
ffs = ExponentialFalloff(mindist, maxdist, forcehalflifedist, ent.railgundistance);
+ if(Headshot(ent, self, hitloc, start, end))
+ {
+ if(deathtype == WEP_RIFLE)
+ bdamage *= autocvar_sv_headshot_damage; // more damage if a headshot
+
+ Send_Notification(NOTIF_ONE, self, MSG_MULTI, MULTI_HEADSHOT);
+ }
+ else
+ jeff_Announcer_FireBullet(ent, self, hitloc, start, end);
+
if(accuracy_isgooddamage(self, ent))
totaldmg += bdamage * f;
void fireBullet_trace_callback(vector start, vector hit, vector end)
{
if(vlen(hit - start) > 16)
- trailparticles(world, fireBullet_trace_callback_eff, start, hit);
+ Send_Effect(fireBullet_trace_callback_eff, start, hit, 0);
WarpZone_trace_forent = world;
fireBullet_last_hit = world;
}
float total_damage = 0;
if(tracereffects & EF_RED)
- fireBullet_trace_callback_eff = particleeffectnum("tr_rifle");
+ fireBullet_trace_callback_eff = EFFECT_RIFLE;
else if(tracereffects & EF_BLUE)
- fireBullet_trace_callback_eff = particleeffectnum("tr_rifle_weak");
+ fireBullet_trace_callback_eff = EFFECT_RIFLE_WEAK;
else
- fireBullet_trace_callback_eff = particleeffectnum("tr_bullet");
+ fireBullet_trace_callback_eff = EFFECT_BULLET;
float lag = ANTILAG_LATENCY(self);
if(lag < 0.001)
{
fireBullet_last_hit = hit;
yoda = 0;
+ if(Headshot(hit, self, end, start, end))
+ {
+ damage *= autocvar_sv_headshot_damage; // more damage
+ Send_Notification(NOTIF_ONE, self, MSG_MULTI, MULTI_HEADSHOT);
+ }
+ else
+ jeff_Announcer_FireBullet(hit, self, end, start, end);
float g = accuracy_isgooddamage(self, hit);
Damage(hit, self, self, damage * solid_penetration_left, dtype, start, force * dir * solid_penetration_left);
// calculate hits for ballistic weapons
// Only show effect when going through a player (invisible otherwise)
if (hit && (hit.solid != SOLID_BSP))
if(vlen(trace_endpos - start) > 4)
- trailparticles(self, fireBullet_trace_callback_eff, start, trace_endpos);
+ Send_Effect(fireBullet_trace_callback_eff, start, trace_endpos, 0); // TODO: check if we need check for self?
start = trace_endpos;
// if there is a child entity, hide it until we're sure we use it
if (self.weaponentity)
self.weaponentity.model = "";
- setmodel(self, strcat("models/weapons/v_", name, ".md3")); // precision set below
+ setmodel(self, W_Model(strcat("v_", name, ".md3")));
v_shot_idx = gettagindex(self, "shot"); // used later
if(!v_shot_idx)
v_shot_idx = gettagindex(self, "tag_shot");
- setmodel(self, strcat("models/weapons/h_", name, ".iqm")); // precision set below
+ setmodel(self, W_Model(strcat("h_", name, ".iqm")));
// preset some defaults that work great for renamed zym files (which don't need an animinfo)
self.anim_fire1 = animfixfps(self, '0 1 0.01', '0 0 0');
self.anim_fire2 = animfixfps(self, '1 1 0.01', '0 0 0');
{
if (!self.weaponentity)
self.weaponentity = spawn();
- setmodel(self.weaponentity, strcat("models/weapons/v_", name, ".md3")); // precision does not matter
+ setmodel(self.weaponentity, W_Model(strcat("v_", name, ".md3")));
setattachment(self.weaponentity, self, "weapon");
}
else if(gettagindex(self, "tag_weapon"))
{
if (!self.weaponentity)
self.weaponentity = spawn();
- setmodel(self.weaponentity, strcat("models/weapons/v_", name, ".md3")); // precision does not matter
+ setmodel(self.weaponentity, W_Model(strcat("v_", name, ".md3")));
setattachment(self.weaponentity, self, "tag_weapon");
}
else
self.dmg = self.owner.modelindex;
self.deadflag = self.owner.deadflag;
if (self.owner.weaponname != "")
- setmodel(self, strcat("models/weapons/v_", self.owner.weaponname, ".md3")); // precision set below
+ setmodel(self, W_Model(strcat("v_", self.owner.weaponname, ".md3")));
else
self.model = "";
if(self.weapon == self.switchweapon && time - self.prevdryfire > 1) // only play once BEFORE starting to switch weapons
{
- sound (self, CH_WEAPON_A, "weapons/dryfire.wav", VOL_BASE, ATTEN_NORM);
+ sound (self, CH_WEAPON_A, W_Sound("dryfire"), VOL_BASE, ATTEN_NORM);
self.prevdryfire = time;
}
if((fr == WFRAME_FIRE1 || fr == WFRAME_FIRE2) && t)
{
- if((self.weapon == WEP_SHOCKWAVE || self.weapon == WEP_SHOTGUN) && fr == WFRAME_FIRE2)
+ if(((self.weapon == WEP_SHOCKWAVE || self.weapon == WEP_SHOTGUN) && fr == WFRAME_FIRE2) || self.weapon == WEP_LIGHTSABRE)
animdecide_setaction(self, ANIMACTION_MELEE, restartanim);
else
animdecide_setaction(self, ANIMACTION_SHOOT, restartanim);
}
}
-float forbidWeaponUse()
+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 1;
- if(self.player_blocked)
- return 1;
- if(self.frozen)
- return 1;
- if(self.weapon_blocked)
- return 1;
+ return 2;
return 0;
}
if (!self.weaponentity || self.health < 1)
return; // Dead player can't use weapons and injure impulse commands
- if(forbidWeaponUse())
+ if(forbidWeaponUse(self))
if(self.weaponentity.state != WS_CLEAR)
{
w_ready();
if(ATTACK_FINISHED(self) <= time + self.weapon_frametime * 0.5)
{
#endif
- sound(self, CH_WEAPON_SINGLE, "weapons/weapon_switch.wav", VOL_BASE, ATTN_NORM);
+ sound(self, CH_WEAPON_SINGLE, W_Sound("weapon_switch"), VOL_BASE, ATTN_NORM);
self.weaponentity.state = WS_DROP;
weapon_thinkf(WFRAME_DONTCHANGE, oldwep.switchdelay_drop, w_clear);
#ifndef INDEPENDENT_ATTACK_FINISHED
{
if(IS_REAL_CLIENT(self) && self.reload_complain < time)
{
- play2(self, "weapons/unavailable.wav");
+ play2(self, W_Sound("unavailable"));
sprint(self, strcat("You don't have enough ammo to reload the ^2", WEP_NAME(self.weapon), "\n"));
self.reload_complain = time + 1;
}
void W_DropEvent(float event, entity player, float weapon_type, entity weapon_item);
-float forbidWeaponUse();
+float forbidWeaponUse(entity player);