From: Mircea Kitsune Date: Mon, 16 Jan 2012 19:54:53 +0000 (+0200) Subject: Merge branch 'master' into mirceakitsune/damage_effects X-Git-Tag: xonotic-v0.6.0~110^2^2~9 X-Git-Url: http://git.xonotic.org/?a=commitdiff_plain;h=48a9eb74e54b8e703797522a2591c65d19596915;hp=e2cdd292127becabc2bbc805d83695713c553781;p=xonotic%2Fxonotic-data.pk3dir.git Merge branch 'master' into mirceakitsune/damage_effects --- diff --git a/commands.cfg b/commands.cfg index 1f7e68458..b23a1e847 100644 --- a/commands.cfg +++ b/commands.cfg @@ -53,6 +53,7 @@ alias who "qc_cmd_svcmd who ${* ?}" // Displa alias addtolist "qc_cmd_svmenu addtolist ${* ?}" // Add a string to a cvar alias dumpcommands "qc_cmd_svmenu dumpcommands ${* ?}" // Dump all commands on the program to *_cmd_dump.txt alias maplist "qc_cmd_svmenu maplist ${* ?}" // Automatic control of maplist +alias nextframe "qc_cmd_svmenu nextframe ${* ?}" // do something next frame alias removefromlist "qc_cmd_svmenu removefromlist ${* ?}" // Remove a string from a cvar alias rpn "qc_cmd_svmenu rpn ${* ?}" // RPN calculator //alias settemp "qc_cmd_svmenu settemp ${* ?}" // Temporarily set a value to a cvar which is restored later @@ -289,4 +290,4 @@ alias vdoend "vdo endmatch" // rcon server commands // ====================== rcon_secure 1 -set rcon_restricted_commands "restart fraglimit chmap gotomap endmatch reducematchtime extendmatchtime allready kick kickban \"sv_cmd bans\" \"sv_cmd unban *\" status \"sv_cmd teamstatus\" movetoauto movetored movetoblue movetoyellow movetopink" \ No newline at end of file +set rcon_restricted_commands "restart fraglimit chmap gotomap endmatch reducematchtime extendmatchtime allready kick kickban \"sv_cmd bans\" \"sv_cmd unban *\" status \"sv_cmd teamstatus\" movetoauto movetored movetoblue movetoyellow movetopink" diff --git a/defaultXonotic.cfg b/defaultXonotic.cfg index 66eedac17..b76b602b4 100644 --- a/defaultXonotic.cfg +++ b/defaultXonotic.cfg @@ -1784,6 +1784,7 @@ seta cl_forceplayercolors 0 "make everyone look like your own color (requires se seta cl_forcemyplayermodel "" "set to the model file name you want to show yourself as (requires server to have sv_use_csqc_players 1; does not affect how enemies look with cl_forceplayermodels)" seta cl_forcemyplayerskin 0 "set to the skin number you want to show yourself as (requires server to have sv_use_csqc_players 1; does not affect how enemies look with cl_forceplayermodels)" seta cl_forcemyplayercolors 0 "set to the color value (encoding is same as _cl_color) for your own player model (requires server to have sv_use_csqc_players 1, and is ignored in teamplay; does not affect how enemies look with cl_forceplayermodels)" +seta cl_predictionerrorcompensation 0 "try to compensate for prediction errors and reduce preceived lag (requires server to have sv_use_csqc_players 1)" // debug cvars for keyhunt attaching set _angles "0 0 0" diff --git a/qcsrc/client/Main.qc b/qcsrc/client/Main.qc index 8facbf500..81c1369a0 100644 --- a/qcsrc/client/Main.qc +++ b/qcsrc/client/Main.qc @@ -787,7 +787,8 @@ void Gamemode_Init() { if not(isdemo()) { - localcmd("\n_cl_hook_gamestart ", MapInfo_Type_ToString(gametype), "\n"); + if(!(calledhooks & HOOK_START)) + localcmd("\n_cl_hook_gamestart ", MapInfo_Type_ToString(gametype), "\n"); calledhooks |= HOOK_START; } } diff --git a/qcsrc/client/View.qc b/qcsrc/client/View.qc index 5ab52d5f1..afccaf45d 100644 --- a/qcsrc/client/View.qc +++ b/qcsrc/client/View.qc @@ -372,6 +372,8 @@ void CSQC_UpdateView(float w, float h) vector vf_size, vf_min; float a; + execute_next_frame(); + ++framecount; hud = getstati(STAT_HUD); diff --git a/qcsrc/client/command/cl_cmd.qc b/qcsrc/client/command/cl_cmd.qc index 95032dce7..638b7f996 100644 --- a/qcsrc/client/command/cl_cmd.qc +++ b/qcsrc/client/command/cl_cmd.qc @@ -524,4 +524,4 @@ float CSQC_ConsoleCommand(string command) // Return value should be 1 if CSQC handled the command, otherwise return 0 to have the engine handle it. return FALSE; -} \ No newline at end of file +} diff --git a/qcsrc/common/command/generic.qc b/qcsrc/common/command/generic.qc index 8cf7f5aa5..d0cd7a304 100644 --- a/qcsrc/common/command/generic.qc +++ b/qcsrc/common/command/generic.qc @@ -208,6 +208,26 @@ void GenericCommand_maplist(float request, float argc) } } +void GenericCommand_nextframe(float request, float arguments, string command) +{ + switch(request) + { + case CMD_REQUEST_COMMAND: + { + queue_to_execute_next_frame(substring(command, argv_start_index(1), argv_end_index(-1) - argv_start_index(1))); + return; + } + + default: + case CMD_REQUEST_USAGE: + { + print(strcat("\nUsage:^3 ", GetProgramCommandPrefix(), " nextframe command...")); + print(" Where command will be executed next frame of this VM\n"); + return; + } + } +} + void GenericCommand_removefromlist(float request, float argc) { switch(request) @@ -334,6 +354,7 @@ void GenericCommand_(float request) GENERIC_COMMAND("addtolist", GenericCommand_addtolist(request, arguments), "Add a string to a cvar") \ GENERIC_COMMAND("dumpcommands", GenericCommand_dumpcommands(request), "Dump all commands on the program to *_cmd_dump.txt") \ GENERIC_COMMAND("maplist", GenericCommand_maplist(request, arguments), "Automatic control of maplist") \ + GENERIC_COMMAND("nextframe", GenericCommand_nextframe(request, arguments, command), "Execute the given command next frame of this VM") \ GENERIC_COMMAND("removefromlist", GenericCommand_removefromlist(request, arguments), "Remove a string from a cvar") \ GENERIC_COMMAND("rpn", GenericCommand_rpn(request, arguments, command), "RPN calculator") \ GENERIC_COMMAND("settemp", GenericCommand_settemp(request, arguments), "Temporarily set a value to a cvar which is restored later") \ diff --git a/qcsrc/common/command/generic.qh b/qcsrc/common/command/generic.qh index 3078a0f05..6fa7d3822 100644 --- a/qcsrc/common/command/generic.qh +++ b/qcsrc/common/command/generic.qh @@ -14,4 +14,4 @@ string GetProgramCommandPrefix(void); // used by common/command/generic.qc:GenericCommand_dumpcommands to list all commands into a .txt file #define CMD_Write(s) fputs(fh, s) #define CMD_Write_Alias(execute,command,description) CMD_Write(sprintf("alias %-20s \"%-13s %-20s ${* ?}\" // %s\n", command, execute, command, description)) -void GenericCommand_macro_write_aliases(float fh); \ No newline at end of file +void GenericCommand_macro_write_aliases(float fh); diff --git a/qcsrc/common/util.qc b/qcsrc/common/util.qc index 9e4bdf7e7..1930d7c6f 100644 --- a/qcsrc/common/util.qc +++ b/qcsrc/common/util.qc @@ -2228,3 +2228,23 @@ void Skeleton_SetBones(entity e) e.skeleton_bones_index = e.modelindex; } #endif + +string to_execute_next_frame; +void execute_next_frame() +{ + if(to_execute_next_frame) + { + localcmd("\n", to_execute_next_frame, "\n"); + strunzone(to_execute_next_frame); + to_execute_next_frame = string_null; + } +} +void queue_to_execute_next_frame(string s) +{ + if(to_execute_next_frame) + { + s = strcat(s, "\n", to_execute_next_frame); + strunzone(to_execute_next_frame); + } + to_execute_next_frame = strzone(s); +} diff --git a/qcsrc/common/util.qh b/qcsrc/common/util.qh index db075ec2a..5685f4db2 100644 --- a/qcsrc/common/util.qh +++ b/qcsrc/common/util.qh @@ -303,3 +303,7 @@ void WriteApproxPastTime(float dst, float t); #ifdef CSQC float ReadApproxPastTime(); #endif + +// execute-stuff-next-frame subsystem +void execute_next_frame(); +void queue_to_execute_next_frame(string s); diff --git a/qcsrc/csqcmodellib/cl_player.qc b/qcsrc/csqcmodellib/cl_player.qc index dc9627d9e..40ba3f7a9 100644 --- a/qcsrc/csqcmodellib/cl_player.qc +++ b/qcsrc/csqcmodellib/cl_player.qc @@ -35,24 +35,43 @@ entity csqcplayer; vector csqcplayer_origin, csqcplayer_velocity; float csqcplayer_sequence, player_pmflags; float csqcplayer_moveframe; -vector csqcplayer_predictionerror; +vector csqcplayer_predictionerroro; +vector csqcplayer_predictionerrorv; float csqcplayer_predictionerrortime; +float csqcplayer_predictionerrorfactor; -vector CSQCPlayer_GetPredictionError() +vector CSQCPlayer_GetPredictionErrorO() { - if(!autocvar_cl_predictionerrorcompensation) + if(time >= csqcplayer_predictionerrortime) + return '0 0 0'; + return csqcplayer_predictionerroro * (csqcplayer_predictionerrortime - time) * csqcplayer_predictionerrorfactor; +} + +vector CSQCPlayer_GetPredictionErrorV() +{ + if(time >= csqcplayer_predictionerrortime) return '0 0 0'; - if(time < csqcplayer_predictionerrortime) - return csqcplayer_predictionerror * (csqcplayer_predictionerrortime - time) * autocvar_cl_predictionerrorcompensation; - return '0 0 0'; + return csqcplayer_predictionerrorv * (csqcplayer_predictionerrortime - time) * csqcplayer_predictionerrorfactor; } -void CSQCPlayer_SetPredictionError(vector v) +void CSQCPlayer_SetPredictionError(vector o, vector v) { if(!autocvar_cl_predictionerrorcompensation) + { + csqcplayer_predictionerrorfactor = 0; return; - csqcplayer_predictionerror = (csqcplayer_predictionerrortime - time) * autocvar_cl_predictionerrorcompensation * csqcplayer_predictionerror + v; - csqcplayer_predictionerrortime = time + 1.0 / autocvar_cl_predictionerrorcompensation; + } + + // error too big to compensate, we LIKELY hit a teleport or a + // jumppad, or it's a jump time disagreement that'll get fixed + // next frame + if(vlen(o) > 32 || vlen(v) > 128) + return; + + csqcplayer_predictionerroro = CSQCPlayer_GetPredictionErrorO() + o; + csqcplayer_predictionerrorv = CSQCPlayer_GetPredictionErrorV() + v; + csqcplayer_predictionerrorfactor = autocvar_cl_predictionerrorcompensation / ticrate; + csqcplayer_predictionerrortime = time + 1.0 / csqcplayer_predictionerrorfactor; } void CSQCPlayer_Unpredict() @@ -92,9 +111,14 @@ void CSQCPlayer_SavePrediction() csqcplayer_status = CSQCPLAYERSTATUS_PREDICTED; } -void CSQCPlayer_PredictTo(float endframe) +void CSQCPlayer_PredictTo(float endframe, float apply_error) { CSQCPlayer_Unpredict(); + if(apply_error) + { + self.origin += CSQCPlayer_GetPredictionErrorO(); + self.velocity += CSQCPlayer_GetPredictionErrorV(); + } CSQCPlayer_SetMinsMaxs(); csqcplayer_status = CSQCPLAYERSTATUS_PREDICTED; @@ -183,11 +207,12 @@ void CSQCPlayer_SetCamera() { vector o, v; o = self.origin; + v = v0; csqcplayer_status = CSQCPLAYERSTATUS_PREDICTED; - CSQCPlayer_PredictTo(servercommandframe + 1); - CSQCPlayer_SetPredictionError(o - self.origin); + CSQCPlayer_PredictTo(servercommandframe + 1, FALSE); + CSQCPlayer_SetPredictionError(self.origin - o, self.velocity - v); self.origin = o; - self.velocity = v0; + self.velocity = v; // get crouch state from the server if(getstati(STAT_VIEWHEIGHT) == PL_VIEW_OFS_z) @@ -203,7 +228,7 @@ void CSQCPlayer_SetCamera() CSQCPlayer_SavePrediction(); } - CSQCPlayer_PredictTo(clientcommandframe + 1); + CSQCPlayer_PredictTo(clientcommandframe + 1, TRUE); CSQCPlayer_SetMinsMaxs(); diff --git a/qcsrc/menu/menu.qc b/qcsrc/menu/menu.qc index 536295fc2..c097f99b9 100644 --- a/qcsrc/menu/menu.qc +++ b/qcsrc/menu/menu.qc @@ -616,6 +616,8 @@ void m_draw() float t; float realFrametime; + execute_next_frame(); + menuMouseMode = cvar("menu_mouse_absolute"); if (anim) diff --git a/qcsrc/server/cheats.qc b/qcsrc/server/cheats.qc index 57a09fe38..e7154b7f5 100644 --- a/qcsrc/server/cheats.qc +++ b/qcsrc/server/cheats.qc @@ -92,6 +92,11 @@ float CheatsAllowed(float i, float argc, float fr) // the cheat gets passed as a if((++attempting, !CheatsAllowed(i,argc,fr))) \ break +void spawnfunc_info_autoscreenshot() +{ + // empty spawnfunc just so this entity can exist +} + float CheatImpulse(float i) { BEGIN_CHEAT_FUNCTION(); @@ -214,8 +219,25 @@ float CheatImpulse(float i) break; case CHIMPULSE_TELEPORT: IS_CHEAT(i, 0, 0); + if(self.movetype == MOVETYPE_NOCLIP) + { + e = find(world, classname, "info_autoscreenshot"); + if(e) + { + sprint(self, "Emergency teleport used info_autoscreenshot location\n"); + setorigin(self, e.origin); + self.angles = e.angles; + remove(e); + // should we? self.angles_x = -self.angles_x; + self.fixangle = TRUE; + self.velocity = '0 0 0'; + DID_CHEAT(); + break; + } + } if(MoveToRandomMapLocation(self, DPCONTENTS_SOLID | DPCONTENTS_CORPSE | DPCONTENTS_PLAYERCLIP, DPCONTENTS_SLIME | DPCONTENTS_LAVA | DPCONTENTS_SKY | DPCONTENTS_BODY | DPCONTENTS_DONOTENTER, Q3SURFACEFLAG_SKY, ((gamestart_sv_cheats >= 2) ? 100000 : 100), 1024, 256)) { + sprint(self, "Emergency teleport used random location\n"); self.angles_x = -self.angles_x; self.fixangle = TRUE; self.velocity = '0 0 0'; diff --git a/qcsrc/server/cl_weapons.qc b/qcsrc/server/cl_weapons.qc index 8c0cb96e0..4a12ce185 100644 --- a/qcsrc/server/cl_weapons.qc +++ b/qcsrc/server/cl_weapons.qc @@ -173,11 +173,17 @@ float W_AmmoItemCode(float wpn) return (get_weaponinfo(wpn)).items & IT_AMMO; } +.float savenextthink; void thrown_wep_think() { - self.solid = SOLID_TRIGGER; self.owner = world; - SUB_SetFade(self, time + 20, 1); + 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 @@ -197,6 +203,33 @@ string W_ThrowNewWeapon(entity own, float wpn, float doreduce, vector org, vecto wep.flags |= FL_TOSSED; wep.colormap = own.colormap; + if(W_WeaponBit(wpn) & WEPBIT_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(own.weapons & WEPBIT_SUPERWEAPONS & W_WeaponBit(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) { @@ -208,7 +241,9 @@ string W_ThrowNewWeapon(entity own, float wpn, float doreduce, vector org, vecto return string_null; wep.glowmod = own.weaponentity_glowmod; wep.think = thrown_wep_think; - wep.nextthink = time + 0.5; + wep.savenextthink = wep.nextthink; + wep.nextthink = min(wep.nextthink, time + 0.5); + wep.pickup_anyway = TRUE; // these are ALWAYS pickable return ""; } else @@ -262,7 +297,8 @@ string W_ThrowNewWeapon(entity own, float wpn, float doreduce, vector org, vecto } wep.glowmod = own.weaponentity_glowmod; wep.think = thrown_wep_think; - wep.nextthink = time + 0.5; + wep.savenextthink = wep.nextthink; + wep.nextthink = min(wep.nextthink, time + 0.5); wep.pickup_anyway = TRUE; // these are ALWAYS pickable return s; } @@ -288,8 +324,6 @@ float W_IsWeaponThrowable(float w) wb = W_WeaponBit(w); if(!wb) return 0; - if(wb & WEPBIT_SUPERWEAPONS) // can't throw a superweapon, they don't work - return 0; wa = W_AmmoItemCode(w); if(start_weapons & wb) { diff --git a/qcsrc/server/command/cmd.qc b/qcsrc/server/command/cmd.qc index e8a620731..b901aca6b 100644 --- a/qcsrc/server/command/cmd.qc +++ b/qcsrc/server/command/cmd.qc @@ -660,6 +660,11 @@ void SV_ParseClientCommand(string command) { 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 diff --git a/qcsrc/server/playerstats.qc b/qcsrc/server/playerstats.qc index 672b031c1..2d0487671 100644 --- a/qcsrc/server/playerstats.qc +++ b/qcsrc/server/playerstats.qc @@ -184,6 +184,7 @@ void PlayerStats_TeamScore(float t, string event_id, float value) // TODO: doesn S: "hostname" of the server C: number of "unpure" cvar changes U: UDP port number of the server + D: duration of the match P: player ID of an existing player; this also sets the owner for all following "n", "e" and "t" lines (lower case!) n: nickname of the player (optional) t: team ID @@ -226,6 +227,7 @@ void PlayerStats_ready(entity fh, entity pass, float status) url_fputs(fh, sprintf("S %s\n", cvar_string("hostname"))); url_fputs(fh, sprintf("C %d\n", cvar_purechanges_count)); url_fputs(fh, sprintf("U %d\n", cvar("port"))); + url_fputs(fh, sprintf("D %f\n", max(0, time - game_starttime))); for(p = playerstats_last; (pn = db_get(playerstats_db, sprintf("%s:*", p))) != ""; p = pn) { url_fputs(fh, sprintf("P %s\n", p)); diff --git a/qcsrc/server/sv_main.qc b/qcsrc/server/sv_main.qc index 73c444afe..66579bc9c 100644 --- a/qcsrc/server/sv_main.qc +++ b/qcsrc/server/sv_main.qc @@ -151,6 +151,8 @@ float RedirectionThink(); entity SelectSpawnPoint (float anypoint); void StartFrame (void) { + execute_next_frame(); + remove = remove_unsafely; // not during spawning! serverprevtime = servertime; servertime = time; diff --git a/qcsrc/server/t_items.qc b/qcsrc/server/t_items.qc index 11688ae36..cd4ab6ceb 100644 --- a/qcsrc/server/t_items.qc +++ b/qcsrc/server/t_items.qc @@ -168,7 +168,7 @@ void Item_Show (entity e, float mode) e.spawnshieldtime = 1; } - if (e.strength_finished || e.invincible_finished) + if (e.items & (IT_STRENGTH | IT_INVINCIBLE)) e.effects |= EF_ADDITIVE | EF_FULLBRIGHT; if (autocvar_g_nodepthtestitems) e.effects |= EF_NODEPTHTEST; @@ -510,8 +510,24 @@ void Item_Touch (void) if (self.owner == other) return; + 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)) - return; + { + if (self.classname == "droppedweapon") + { + // undo what we did above + self.strength_finished += time; + self.invincible_finished += time; + self.superweapons_finished += time; + return; + } + } other.last_pickup = time; @@ -546,11 +562,15 @@ void Item_Reset() { Item_Show(self, !self.state); setorigin (self, self.origin); - self.think = SUB_Null; - self.nextthink = 0; - if((self.flags & FL_POWERUP) | (self.weapons & WEPBIT_SUPERWEAPONS)) // do not spawn powerups initially! - Item_ScheduleInitialRespawn(self); + if(self.classname != "droppedweapon") + { + self.think = SUB_Null; + self.nextthink = 0; + + if((self.flags & FL_POWERUP) | (self.weapons & WEPBIT_SUPERWEAPONS)) // do not spawn powerups initially! + Item_ScheduleInitialRespawn(self); + } } void Item_FindTeam() @@ -654,7 +674,7 @@ float weapon_pickupevalfunc(entity player, entity item) float commodity_pickupevalfunc(entity player, entity item) { - float c, i, need_shells, need_nails, need_rockets, need_cells; + float c, i, need_shells, need_nails, need_rockets, need_cells, need_fuel; entity wi; c = 0; @@ -674,6 +694,8 @@ float commodity_pickupevalfunc(entity player, entity item) need_rockets = TRUE; else if(wi.items & IT_CELLS) need_cells = TRUE; + else if(wi.items & IT_FUEL) + need_cells = TRUE; } // TODO: figure out if the player even has the weapon this ammo is for? @@ -695,6 +717,10 @@ float commodity_pickupevalfunc(entity player, entity item) if (item.ammo_cells) if (player.ammo_cells < g_pickup_cells_max) c = c + max(0, 1 - player.ammo_cells / g_pickup_cells_max); + if (need_fuel) + if (item.ammo_fuel) + if (player.ammo_fuel < g_pickup_fuel_max) + c = c + max(0, 1 - player.ammo_fuel / g_pickup_fuel_max); if (item.armorvalue) if (player.armorvalue < item.max_armorvalue) c = c + max(0, 1 - player.armorvalue / item.max_armorvalue); @@ -721,9 +747,28 @@ void StartItem (string itemmodel, string pickupsound, float defaultrespawntime, self.reset = SUB_Remove; // it's a dropped weapon self.movetype = MOVETYPE_TOSS; + // Savage: remove thrown items after a certain period of time ("garbage collection") self.think = RemoveItem; - self.nextthink = time + 60; + self.nextthink = time + 20; + + if(self.strength_finished || self.invincible_finished || self.superweapons_finished) + /* + if(self.items == 0) + if(self.weapons == (self.weapons & WEPBIT_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 item is worthless after a timer, have it expire then + self.nextthink = max(self.strength_finished, self.invincible_finished, self.superweapons_finished); + } + // don't drop if in a NODROP zone (such as lava) traceline(self.origin, self.origin, MOVE_NORMAL, self); if (trace_dpstartcontents & DPCONTENTS_NODROP) @@ -856,7 +901,6 @@ void StartItem (string itemmodel, string pickupsound, float defaultrespawntime, self.colormap = 1024; // color shirt=0 pants=0 grey } - Item_Show(self, 1); self.state = 0; if(self.team) { diff --git a/qcsrc/server/w_rocketlauncher.qc b/qcsrc/server/w_rocketlauncher.qc index 723389e71..42ae90d74 100644 --- a/qcsrc/server/w_rocketlauncher.qc +++ b/qcsrc/server/w_rocketlauncher.qc @@ -63,58 +63,6 @@ void W_Rocket_DoRemoteExplode () remove (self); } -entity FindLaserTarget(entity e, float dist_variance, float dot_variance) -{ - entity head, selected; - vector dir; - float dist, maxdist,// bestdist, - dot,// bestdot, - points, bestpoints; - //bestdist = 9999; - //bestdot = -2; - bestpoints = 0; - maxdist = 800; - selected = world; - - makevectors(e.angles); - - head = find(world, classname, "laser_target"); - while(head) - { - points = 0; - dir = normalize(head.origin - self.origin); - dot = dir * v_forward; - dist = vlen(head.origin - self.origin); - if(dist > maxdist) - dist = maxdist; - - // gain points for being in front - points = points + ((dot+1)*0.5) * 500 - * (1 + crandom()*dot_variance); - // gain points for being close away - points = points + (1 - dist/maxdist) * 1000 - * (1 + crandom()*dot_variance); - - traceline(e.origin, head.origin, TRUE, self); - if(trace_fraction < 1) - { - points = 0; - } - - if(points > bestpoints)//random() > 0.5)// - { - bestpoints = points; - selected = head; - } - - head = find(head, classname, "laser_target"); - } - - //bprint(selected.realowner.netname); - //bprint("\n"); - return selected; -} - void W_Rocket_RemoteExplode() { if(self.realowner.deadflag == DEAD_NO) @@ -134,6 +82,8 @@ vector rocket_steerto(vector thisdir, vector goaldir, float maxturn_cos) { if(thisdir * goaldir > maxturn_cos) return goaldir; + if(thisdir * goaldir < -0.9998) // less than 1 degree and opposite + return thisdir; // refuse to guide (better than letting a numerical error happen) float f, m2; vector v; // solve: @@ -151,6 +101,15 @@ vector rocket_steerto(vector thisdir, vector goaldir, float maxturn_cos) v = solve_quadratic(m2 - f * f, 2 * f * (m2 - 1), m2 - 1); return normalize(thisdir + goaldir * v_y); // the larger solution! } +// assume thisdir == -goaldir: +// f == -1 +// v = solve_qadratic(m2 - 1, -2 * (m2 - 1), m2 - 1) +// (m2 - 1) x^2 - 2 * (m2 - 1) * x + (m2 - 1) = 0 +// x^2 - 2 * x + 1 = 0 +// (x - 1)^2 = 0 +// x = 1 +// normalize(thisdir + goaldir) +// normalize(0) void W_Rocket_Think (void) {