]> git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/commitdiff
Merge branch 'master' into mirceakitsune/damage_effects
authorMircea Kitsune <sonichedgehog_hyperblast00@yahoo.com>
Mon, 16 Jan 2012 19:54:53 +0000 (21:54 +0200)
committerMircea Kitsune <sonichedgehog_hyperblast00@yahoo.com>
Mon, 16 Jan 2012 19:54:53 +0000 (21:54 +0200)
18 files changed:
commands.cfg
defaultXonotic.cfg
qcsrc/client/Main.qc
qcsrc/client/View.qc
qcsrc/client/command/cl_cmd.qc
qcsrc/common/command/generic.qc
qcsrc/common/command/generic.qh
qcsrc/common/util.qc
qcsrc/common/util.qh
qcsrc/csqcmodellib/cl_player.qc
qcsrc/menu/menu.qc
qcsrc/server/cheats.qc
qcsrc/server/cl_weapons.qc
qcsrc/server/command/cmd.qc
qcsrc/server/playerstats.qc
qcsrc/server/sv_main.qc
qcsrc/server/t_items.qc
qcsrc/server/w_rocketlauncher.qc

index 1f7e684588770a6e8554d6c9cc4162daabfe2899..b23a1e8474575e2f3c4a11222bac087a5e16aa5f 100644 (file)
@@ -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"
index 66eedac17663451302383757f0a7afcbe139df67..b76b602b4c3e42a1dfb24a2ad0c024a24f378801 100644 (file)
@@ -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"
index 8facbf500ff9a8e2b28567b7b96bd85c88c85ebf..81c1369a0e61e45b660d73dd82130eeaf33388c7 100644 (file)
@@ -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;
        }
 }
index 5ab52d5f19ae51c602e624d10609e13f4067df32..afccaf45dc6ab6f217fb0503381ed1ca2a17148b 100644 (file)
@@ -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);
index 95032dce7bd4b013ab22701cda09ebf3521486aa..638b7f996ce042c578b8dabaa6ff370a0ae4afeb 100644 (file)
@@ -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
+}
index 8cf7f5aa52b5777e4533396e25f9688070714b9c..d0cd7a3042cd1e993522c1377ff52864bdbbeaeb 100644 (file)
@@ -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") \
index 3078a0f05439af3fcad58b2ac96a808f08b6e64e..6fa7d382209bd66f10c5e12e667fb59d941ac80e 100644 (file)
@@ -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);
index 9e4bdf7e74c90fcb0df4f4fcf062bfee62d691ea..1930d7c6fc4a04f2c7d33d029631f79bae308c29 100644 (file)
@@ -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);
+}
index db075ec2a0576efc3cbec05f3b1a433d54fbebe8..5685f4db27d42ec177e03e666a6939c2eb8f8489 100644 (file)
@@ -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);
index dc9627d9e8173fcc86a9c3a45408d71e101f0b6d..40ba3f7a9f73c4d0a283dc6578b54a396211f00e 100644 (file)
@@ -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();
 
index 536295fc2f479f66dbdd08641c10a4a49330bac6..c097f99b963833366276363fffc9aebebcf8d3c5 100644 (file)
@@ -616,6 +616,8 @@ void m_draw()
        float t;
        float realFrametime;
 
+       execute_next_frame();
+
        menuMouseMode = cvar("menu_mouse_absolute");
 
        if (anim)
index 57a09fe3808ac8997b2a557010fb442e24827dc8..e7154b7f52f17617b9a79c7560aba4e551f99161 100644 (file)
@@ -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';
index 8c0cb96e0eb4f0627867800f7691eb979aa53af2..4a12ce1850323e10f7b11282a76874cb0f426e66 100644 (file)
@@ -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)
        {
index e8a6207314af6c8b23a0152e88b1ab9ae1d9cf93..b901aca6bc01947c0d6279ab20db5473ecf1cdee 100644 (file)
@@ -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 
index 672b031c119bccaedca248dbce5fd408b31f44ca..2d04876710daa9a0d9de807d60bd0c6d0f86b822 100644 (file)
@@ -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));
index 73c444afec7b8637e0481ecb18ba133d01f471a1..66579bc9c92807ef2cfc11cbf3164b47ee20c2e1 100644 (file)
@@ -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;
index 11688ae36a67cbdca3117edb78df76df93f94786..cd4ab6ceb21d29f956d3ce8af4cab46bdc66a1cb 100644 (file)
@@ -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)
        {
index 723389e7121e5d7536f5f7bf07aec65847548092..42ae90d7466e84c0a46740c9ec2cef614ce873ef 100644 (file)
@@ -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)
 {